Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

initial commit of example applications

  • Loading branch information...
commit ba9c415c2baaf528f95ea31d520f7f6ecc7121b9 0 parents
Tom Dale authored
246 contacts/app.js
@@ -0,0 +1,246 @@
+App = SC.Application.create();
+
+/*
+
+ Model
+
+*/
+
+var names = ["Adam", "Bert", "Charlie", "Dave", "Ernie", "Frances",
+ "Gary", "Isabelle", "John", "Kyle", "Lyla", "Matt", "Nancy", "Ophelia",
+ "Peter", "Quentin", "Rachel", "Stan", "Tom", "Uma", "Veronica", "Wilson",
+ "Xander", "Yehuda", "Zora"];
+
+App.Contact = SC.Object.extend({
+ firstName: '',
+ lastName: '',
+
+ hasName: function() {
+ var firstName = this.get('firstName'),
+ lastName = this.get('lastName');
+
+ return firstName !== '' || lastName !== '';
+ }.property('firstName', 'lastName'),
+
+ // This value is used to determine how the contact
+ // should be sorted in the contacts list. By default
+ // we sort by last name, but we use the first name if
+ // no last name is provided.
+ sortValue: function() {
+ return this.get('lastName') || this.get('firstName');
+ }.property('firstName', 'lastName')
+});
+
+/*
+
+ Controllers
+
+*/
+
+App.contactsController = SC.ArrayController.create({
+ // The array of Contact objects that backs the array controller.
+ content: [],
+
+ // Adds a new contact to the list and ensures it is
+ // sorted correctly.
+ add: function(contact) {
+ var length = this.get('length'), idx;
+
+ idx = this.binarySearch(contact.get('sortValue'), 0, length);
+
+ this.insertAt(idx, contact);
+
+ // If the value by which we've sorted the contact
+ // changes, we need to re-insert it at the correct
+ // location in the list.
+ contact.addObserver('sortValue', this, 'contactSortValueDidChange');
+ },
+
+ // Binary search implementation that finds the index
+ // where a contact should be inserted.
+ binarySearch: function(value, low, high) {
+ var mid, midValue;
+
+ if (low === high) {
+ return low;
+ }
+
+ mid = low + Math.floor((high - low) / 2);
+ midValue = this.objectAt(mid).get('sortValue');
+
+ if (value > midValue) {
+ return this.binarySearch(value, mid+1, high);
+ } else if (value < midValue) {
+ return this.binarySearch(value, low, mid);
+ }
+
+ return mid;
+ },
+
+ remove: function(contact) {
+ this.removeObject(contact);
+ contact.removeObserver('sortValue', this, 'contactSortValueDidChange');
+ },
+
+ contactSortValueDidChange: function(contact) {
+ this.remove(contact);
+ this.add(contact);
+ },
+
+ // Creates a new, empty Contact object and adds it to the
+ // array controller.
+ newContact: function() {
+ var firstName = Math.floor(Math.random()*names.length),
+ lastName = Math.floor(Math.random()*names.length),
+ hasLastName = Math.random();
+
+ this.add(App.Contact.create({
+ firstName: names[firstName],
+ lastName: hasLastName < 0.9 ? names[lastName] : null,
+ phoneNumbers: []
+ }));
+ },
+
+ loadContacts: function() {
+ var self = this;
+ $.ajax({
+ url: '/contacts.json',
+ dataType: 'json',
+ success: function(data) {
+ var contacts = data.contacts;
+
+ // Turn JSON objects into bindable Ember
+ // objects.
+ contacts = contacts.map(function(item) {
+ return self.createContactFromJSON(item);
+ });
+
+ self.set('content', contacts);
+ },
+
+ error: function() {
+ self.pushObject(self.createContactFromJSON({
+ firstName: 'Peter',
+ lastName: 'Wagenet',
+ phoneNumbers: ['(415) 555-2381']
+ }));
+
+ self.set('isFromFixtures', true);
+ }
+ });
+ },
+
+ createContactFromJSON: function(json) {
+ json.phoneNumbers = json.phoneNumbers.map(function(number) {
+ return { number: number };
+ });
+
+ return App.Contact.create(json);
+ }
+});
+
+App.contactsController.loadContacts();
+
+App.selectedContactController = SC.Object.create({
+ content: null
+});
+
+App.DeleteNumberView = SC.View.extend({
+ classNames: ['delete-number-view'],
+ click: function() {
+ var phoneNumber = this.get('content');
+ var contact = this.getPath('contentView.content');
+
+ contact.get('phoneNumbers').removeObject(phoneNumber);
+ },
+
+ touchEnd: function() {
+ this.click();
+ }
+});
+
+App.EditField = SC.View.extend({
+ tagName: 'span',
+ templateName: 'edit-field',
+
+ doubleClick: function() {
+ this.set('isEditing', true);
+ return false;
+ },
+
+ touchEnd: function() {
+ // Rudimentary double tap support, could be improved
+ var touchTime = new Date();
+ if (this._lastTouchTime && touchTime - this._lastTouchTime < 250) {
+ this.doubleClick();
+ this._lastTouchTime = null;
+ } else {
+ this._lastTouchTime = touchTime;
+ }
+
+ // Prevent zooming
+ return false;
+ },
+
+ focusOut: function() {
+ this.set('isEditing', false);
+ },
+
+ keyUp: function(evt) {
+ if (evt.keyCode === 13) {
+ this.set('isEditing', false);
+ }
+ }
+});
+
+App.TextField = SC.TextField.extend({
+ didInsertElement: function() {
+ this.$().focus();
+ }
+});
+
+SC.Handlebars.registerHelper('editable', function(path, options) {
+ options.hash.valueBinding = path;
+ return SC.Handlebars.helpers.view.call(this, App.EditField, options);
+});
+
+SC.Handlebars.registerHelper('button', function(options) {
+ var hash = options.hash;
+
+ if (!hash.target) {
+ hash.target = "App.contactsController";
+ }
+ return SC.Handlebars.helpers.view.call(this, SC.Button, options);
+});
+
+App.ContactListView = SC.View.extend({
+ classNameBindings: ['isSelected'],
+
+ click: function() {
+ var content = this.get('content');
+
+ App.selectedContactController.set('content', content);
+ },
+
+ touchEnd: function() {
+ this.click();
+ },
+
+ isSelected: function() {
+ var selectedItem = App.selectedContactController.get('content'),
+ content = this.get('content');
+
+ if (content === selectedItem) { return true; }
+ }.property('App.selectedContactController.content')
+});
+
+App.CardView = SC.View.extend({
+ contentBinding: 'App.selectedContactController.content',
+ classNames: ['card'],
+
+ addPhoneNumber: function() {
+ var phoneNumbers = this.getPath('content.phoneNumbers');
+ phoneNumbers.pushObject({ number: '' });
+ }
+});
+
BIN  contacts/images/broken_noise.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  contacts/images/delete.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  contacts/images/ricepaper2.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
77 contacts/index.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Ember.js Contacts</title>
+ <link rel="stylesheet" href="style.css" type="text/css" media="screen" charset="utf-8">
+ <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
+ <script type="text/javascript" src="../lib/ember.min.js"></script>
+ <script type="text/javascript" src="app.js"></script>
+ </head>
+ <body>
+ <div id="container">
+ <div id="contact-list">
+ <script type="text/x-handlebars">
+ <h2>My Contacts</h2>
+ <ul>
+ {{#each App.contactsController}}
+ {{#view App.ContactListView contentBinding="this"}}
+ {{#with content}}
+ <li>
+ {{#if hasName}}
+ {{#if lastName}}
+ {{firstName}} <b>{{lastName}}</b>
+ {{else}}
+ <b>{{firstName}}</b>
+ {{/if}}
+ {{else}}
+ <span class="no-name">no name</span>
+ {{/if}}
+ {{/with}}
+ {{/view}}
+ {{/each}}
+ </ul>
+ {{#button action="newContact"}}New Contact{{/button}}
+ </script>
+ </div>
+
+ <div id="detail">
+ <script type="text/x-handlebars">
+ {{#if App.selectedContactController.content}}
+ {{#view App.CardView}}
+ <div class="name">
+ {{editable "content.firstName"}} {{editable "content.lastName"}}
+ </div>
+
+ <table class="phone-numbers">
+ {{#each content.phoneNumbers}}
+ <tr>
+ <td>{{editable number}}</td>
+ <td>{{view App.DeleteNumberView contentBinding="this"}}</td>
+ </tr>
+ {{/each}}
+ </table>
+ {{#view SC.Button target="contentView" action="addPhoneNumber"}}
+ Add Phone Number
+ {{/view}}
+ {{/view}}
+ {{else}}
+ <div class="no-contact-selected">No contact selected.</div>
+ {{/if}}
+
+ </script>
+ </div>
+
+ <script type="text/x-handlebars" data-template-name="edit-field">
+ {{#if isEditing}}
+ {{view App.TextField valueBinding="value" propagatesEvents=true}}
+ {{else}}
+ {{#if value}}
+ {{value}}
+ {{else}}
+ <span class="no-name">empty</span>
+ {{/if}}
+ {{/if}}
+ </script>
+ </body>
+</html>
+
67 contacts/server.js
@@ -0,0 +1,67 @@
+var express = require('express'),
+ connect = require('connect');
+
+var app = express.createServer(
+ connect.logger(),
+ connect.static(__dirname),
+ connect.static('..')
+);
+
+var guid = 0;
+
+var contacts = [
+ {
+ guid: ++guid,
+ firstName: 'Peter',
+ lastName: 'Wagenet',
+ phoneNumbers: ['(415) 555-2380']
+ },
+
+ {
+ guid: ++guid,
+ firstName: 'Yehuda',
+ lastName: 'Katz',
+ phoneNumbers: ['(415) 555-6666']
+ },
+
+ {
+ guid: ++guid,
+ firstName: 'Erik',
+ lastName: 'Bryn',
+ phoneNumbers: ['(415) 555-2380', '(614) 555-8127']
+ },
+
+ {
+ guid: ++guid,
+ firstName: 'James',
+ lastName: 'Rosen',
+ phoneNumbers: ['(415) 555-2380']
+ },
+
+ {
+ guid: ++guid,
+ firstName: 'Kara',
+ lastName: 'Gates',
+ phoneNumbers: ['(207) 555-3141']
+ },
+
+ {
+ guid: ++guid,
+ firstName: 'Dudley',
+ lastName: 'Flanders',
+ phoneNumbers: ['(415) 555-6789']
+ },
+
+ {
+ guid: ++guid,
+ firstName: 'Tom',
+ lastName: 'Dale',
+ phoneNumbers: ['(808) 800-8135']
+ }
+];
+
+app.get('/contacts.json', function(req, res) {
+ res.send({contacts: contacts});
+});
+
+app.listen(3000);
132 contacts/style.css
@@ -0,0 +1,132 @@
+html, body {
+ background: url(images/broken_noise.png);
+ color: #333;
+ margin: 0;
+ padding: 0;
+ height: 100%;
+}
+
+body {
+ font-family: Helvetica, Arial, "sans-serif";
+}
+
+#container {
+ position: absolute;
+ left: 50%;
+ margin-left: -450px;
+ width: 900px;
+ top: 10px;
+ bottom: 10px;
+ padding: 0;
+ background: url(images/ricepaper2.png);
+ border-radius: 12px;
+}
+
+#contact-list {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 300px;
+ bottom: 0;
+ box-shadow: rgba(0,0,0,0.8) 0 0 5px;
+}
+
+#contact-list h2 {
+ font-family: "HelveticaNeue-UltraLight", Helvetica, Arial;
+ font-weight: 100;
+ text-align: center;
+ position: absolute;
+ top: 10px;
+ right: 0;
+ left: 0;
+ height: 30px;
+ margin: 0;
+}
+
+#contact-list button {
+ position: absolute;
+ bottom: 5px;
+ left: 13px;
+}
+
+#contact-list ul {
+ position: absolute;
+ top: 45px;
+ right: 13px;
+ bottom: 30px;
+ left: 13px;
+ padding: 3px;
+ border: 1px solid #bbb;
+ background-color: white;
+ overflow: auto;
+ margin: 0;
+ list-style-type: none;
+}
+
+#contact-list ul li {
+ padding: 3px;
+ cursor: default;
+}
+
+#contact-list .is-selected {
+ background-color: #ecf3ff;
+ color: #274bf1;
+}
+
+#contact-list .is-selected .no-name {
+ color: #274bf1;
+}
+
+.no-name {
+ color: #aaa;
+ font-style: italic;
+}
+
+#detail {
+ position: absolute;
+ width: 600px;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ text-shadow: #fff 0 -1px 0;
+}
+
+#detail .card {
+ position: absolute;
+ background-color: rgba(255,255,255,0.4);
+ padding: 7px;
+ top: 46px;
+ left: 13px;
+ right: 13px;
+ bottom: 46px;
+ overflow: auto;
+}
+
+#detail .card .name,
+#detail .card .name input {
+ font-size: 32px;
+ font-weight: bold;
+}
+
+#detail .card table {
+ margin-top: 35px;
+}
+
+#detail .card button {
+ margin-top: 13px;
+}
+
+#detail .delete-number-view {
+ width: 16px;
+ height: 16px;
+ background-image: url(images/delete.png);
+}
+
+#detail .no-contact-selected {
+ position: absolute;
+ top: 50%;
+ margin-top: -10px;
+ height: 20px;
+ width: 100%;
+ text-align: center;
+}
35 index.html.md
@@ -0,0 +1,35 @@
+---
+title: Examples
+---
+
+## Todos
+
+As required by law, our JavaScript framework includes a Todos demo.
+
+<a href="/examples/todos/">Visit Todos</a> |
+<a href="https://github.com/emberjs/todos">Source</a>
+<a href="/examples/todos/">
+<img src="/images/screenshots/todos.png" class="screenshot">
+</a>
+
+## Simple Bindings
+This demo contains an example a simple view bound to a controller.
+
+<a href="/examples/simple_bindings/">Visit Simple Bindings</a>
+<a href="/examples/simple_bindings/">
+<img src="/images/screenshots/simple_bindings.png" class="screenshot">
+</a>
+
+## Contacts
+
+A more sophisticated demo application. Add new contacts, change their names, and watch as they are updated
+and sorted in real time. Click on a contact and double-click a field to edit.
+
+If you check out this code from GitHub, it includes a node.js server that illustrates how to load data
+into a controller.
+
+
+<a href="/examples/contacts/">Visit Contacts</a>
+<a href="/examples/contacts/">
+<img src="/images/screenshots/contacts.png" class="screenshot">
+</a>
13 lib/ember.min.js
13 additions, 0 deletions not shown
68 lib/examples.js
@@ -0,0 +1,68 @@
+Examples = SC.Application.create();
+Examples.SelectPopupView = SC.View.extend({
+ didInsertElement: function() {
+ this.change();
+ },
+
+ change: function() {
+ var input = this.$('select');
+ var exampleToLoad = input.val();
+
+ Examples.exampleController.set('example', exampleToLoad);
+ }
+});
+
+Examples.exampleController = SC.Object.create({
+ example: '',
+
+ exampleDidChange: function() {
+ var example = this.get('example');
+ this.updateExample(example);
+ }.observes('example'),
+
+ updateExample: function(example) {
+ var iframe;
+
+ iframe = this.get('iframe');
+ if (iframe) {
+ document.body.removeChild(iframe);
+ }
+
+ iframe = document.createElement('iframe');
+ document.body.appendChild(iframe);
+
+ var iframeDocument = iframe.contentDocument,
+ iframeBody = iframeDocument.body,
+ iframeWindow = iframe.contentWindow;
+
+ var libraries = ['http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.js', 'lib/sproutcore.js'];
+ var script;
+
+ libraries.forEach(function(url) {
+ script = this.createScriptElement(iframeDocument, url);
+ iframeDocument.head.appendChild(script);
+ }, this);
+
+
+ script = this.createScriptElement(iframeDocument, "/examples/"+example+"/source.js");
+ iframeBody.appendChild(script);
+
+ $.ajax('/examples/'+example+'/template.handlebars', {
+ success: function(template) {
+ var view = iframeWindow.SC.View.create({
+ template: iframeWindow.SC.Handlebars.compile(template)
+ });
+ view.append();
+ }
+ });
+
+ this.set('iframe', iframe);
+ },
+
+ createScriptElement: function(document, url) {
+ var elem = document.createElement('script');
+ elem.src = url;
+ return elem;
+ }
+});
+
BIN  simple_bindings/images/ricepaper2.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
79 simple_bindings/index.html
@@ -0,0 +1,79 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Simple Bindings</title>
+ <style>
+ body {
+ background: url(images/ricepaper2.png);
+ font-family: Helvetica, Arial, "sans-serif";
+ }
+
+ body > div > div {
+ background-color: #fff;
+ -webkit-border-radius: 6px;
+ box-shadow: rgba(0,0,0,0.3) 0px 0px 2px;
+
+ margin: 0 auto 35px auto;
+ padding: 15px;
+ width: 600px;
+ }
+
+ .invite {
+ font-family: "Zapfino";
+ }
+
+ input[type="checkbox"] {
+ margin-right: 5px;
+ }
+
+ h2 {
+ margin-top: 0;
+ }
+ </style>
+ <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
+ <script type="text/javascript" src="../lib/ember.min.js"></script>
+ <script type="text/javascript">
+ App = SC.Application.create();
+ App.personController = SC.Object.create({
+ firstName: 'Alan',
+ lastName: 'Turing',
+
+ fullName: function() {
+ return this.get('firstName') + ' ' + this.get('lastName');
+ }.property('firstName', 'lastName'),
+
+ didRSVP: false
+ });
+ </script>
+
+
+ </head>
+ <body>
+ <script type="text/x-handlebars">
+ <div class="editor">
+ <div class="invite">You are cordially invited to the introduction of Ember.js.</div>
+ <p>
+ First Name: {{view SC.TextField valueBinding="App.personController.firstName"}}
+ </p>
+ <p>
+ Last Name: {{view SC.TextField valueBinding="App.personController.lastName"}}
+ </p>
+ {{#view SC.Checkbox valueBinding="App.personController.didRSVP"}}
+ <input type="checkbox">Click here to RSVP
+ {{/view}}
+ </div>
+
+ <div class="viewer">
+ <h2>Guest List</h2>
+ {{#with App.personController}}
+ {{#if didRSVP}}
+ {{fullName}}
+ {{else}}
+ No one has RSVPed yet.
+ {{/if}}
+ {{/with}}
+ </div>
+ </script>
+ </body>
+</html>
+
5 todos/README
@@ -0,0 +1,5 @@
+This application shows how you can write SproutCore 2.0 apps with an astonishingly small amount of code.
+
+For more information about SproutCore, visit http://www.sproutcore.com.
+
+By Yehuda Katz and Tom Dale
BIN  todos/apple-touch-icon-114x114-precomposed.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  todos/apple-touch-icon-57x57-precomposed.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  todos/apple-touch-icon-72x72-precomposed.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  todos/apple-touch-icon-precomposed.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  todos/apple-touch-icon.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
141 todos/css/style.css
@@ -0,0 +1,141 @@
+
+/* ==== Scroll down to find where to put your styles :) ==== */
+
+/* HTML5 ✰ Boilerplate */
+
+html, body, div, span, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+abbr, address, cite, code, del, dfn, em, img, ins, kbd, q, samp,
+small, strong, sub, sup, var, b, i, dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td,
+article, aside, canvas, details, figcaption, figure,
+footer, header, hgroup, menu, nav, section, summary,
+time, mark, audio, video {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ font-size: 100%;
+ font: inherit;
+ vertical-align: baseline;
+}
+
+article, aside, details, figcaption, figure,
+footer, header, hgroup, menu, nav, section {
+ display: block;
+}
+
+blockquote, q { quotes: none; }
+blockquote:before, blockquote:after,
+q:before, q:after { content: ''; content: none; }
+ins { background-color: #ff9; color: #000; text-decoration: none; }
+mark { background-color: #ff9; color: #000; font-style: italic; font-weight: bold; }
+del { text-decoration: line-through; }
+abbr[title], dfn[title] { border-bottom: 1px dotted; cursor: help; }
+table { border-collapse: collapse; border-spacing: 0; }
+hr { display: block; height: 1px; border: 0; border-top: 1px solid #ccc; margin: 1em 0; padding: 0; }
+input, select { vertical-align: middle; }
+
+body { font:13px/1.231 sans-serif; *font-size:small; }
+select, input, textarea, button { font:99% sans-serif; }
+pre, code, kbd, samp { font-family: monospace, sans-serif; }
+
+html { overflow-y: scroll; }
+a:hover, a:active { outline: none; }
+ul, ol { margin-left: 2em; }
+ol { list-style-type: decimal; }
+nav ul, nav li { margin: 0; list-style:none; list-style-image: none; }
+small { font-size: 85%; }
+strong, th { font-weight: bold; }
+td { vertical-align: top; }
+
+sub, sup { font-size: 75%; line-height: 0; position: relative; }
+sup { top: -0.5em; }
+sub { bottom: -0.25em; }
+
+pre { white-space: pre; white-space: pre-wrap; word-wrap: break-word; padding: 15px; }
+textarea { overflow: auto; }
+.ie6 legend, .ie7 legend { margin-left: -7px; }
+input[type="radio"] { vertical-align: text-bottom; }
+input[type="checkbox"] { vertical-align: bottom; }
+.ie7 input[type="checkbox"] { vertical-align: baseline; }
+.ie6 input { vertical-align: text-bottom; }
+label, input[type="button"], input[type="submit"], input[type="image"], button { cursor: pointer; }
+button, input, select, textarea { margin: 0; }
+input:valid, textarea:valid { }
+input:invalid, textarea:invalid { border-radius: 1px; -moz-box-shadow: 0px 0px 5px red; -webkit-box-shadow: 0px 0px 5px red; box-shadow: 0px 0px 5px red; }
+.no-boxshadow input:invalid, .no-boxshadow textarea:invalid { background-color: #f0dddd; }
+
+::-moz-selection{ background: #FF5E99; color:#fff; text-shadow: none; }
+::selection { background:#FF5E99; color:#fff; text-shadow: none; }
+a:link { -webkit-tap-highlight-color: #FF5E99; }
+
+button { width: auto; overflow: visible; }
+.ie7 img { -ms-interpolation-mode: bicubic; }
+
+body, select, input, textarea { color: #444; }
+h1, h2, h3, h4, h5, h6 { font-weight: bold; }
+a, a:active, a:visited { color: #607890; }
+a:hover { color: #036; }
+
+/*
+ // ========================================== \\
+ || ||
+ || Your styles ! ||
+ || ||
+ \\ ========================================== //
+*/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+.ir { display: block; text-indent: -999em; overflow: hidden; background-repeat: no-repeat; text-align: left; direction: ltr; }
+.hidden { display: none; visibility: hidden; }
+.visuallyhidden { border: 0; clip: rect(0 0 0 0); height: 1px; margin: -1px; overflow: hidden; padding: 0; position: absolute; width: 1px; }
+.visuallyhidden.focusable:active,
+.visuallyhidden.focusable:focus { clip: auto; height: auto; margin: 0; overflow: visible; position: static; width: auto; }
+.invisible { visibility: hidden; }
+.clearfix:before, .clearfix:after { content: "\0020"; display: block; height: 0; overflow: hidden; }
+.clearfix:after { clear: both; }
+.clearfix { zoom: 1; }
+
+
+@media all and (orientation:portrait) {
+
+}
+
+@media all and (orientation:landscape) {
+
+}
+
+@media screen and (max-device-width: 480px) {
+
+ /* html { -webkit-text-size-adjust:none; -ms-text-size-adjust:none; } */
+}
+
+
+@media print {
+ * { background: transparent !important; color: black !important; text-shadow: none !important; filter:none !important;
+ -ms-filter: none !important; }
+ a, a:visited { color: #444 !important; text-decoration: underline; }
+ a[href]:after { content: " (" attr(href) ")"; }
+ abbr[title]:after { content: " (" attr(title) ")"; }
+ .ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after { content: ""; }
+ pre, blockquote { border: 1px solid #999; page-break-inside: avoid; }
+ thead { display: table-header-group; }
+ tr, img { page-break-inside: avoid; }
+ @page { margin: 0.5cm; }
+ p, h2, h3 { orphans: 3; widows: 3; }
+ h2, h3{ page-break-after: avoid; }
+}
250 todos/css/todos.css
@@ -0,0 +1,250 @@
+html, body, div, span, applet, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+a, abbr, acronym, address, big, cite, code,
+del, dfn, em, img, ins, kbd, q, s, samp,
+small, strike, strong, sub, sup, tt, var,
+b, u, i, center,
+dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td,
+article, aside, canvas, details, embed,
+figure, figcaption, footer, header, hgroup,
+menu, nav, output, ruby, section, summary,
+time, mark, audio, video {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ font-size: 100%;
+ font: inherit;
+ vertical-align: baseline; }
+
+body {
+ line-height: 1; }
+
+ol, ul {
+ list-style: none; }
+
+table {
+ border-collapse: collapse;
+ border-spacing: 0; }
+
+caption, th, td {
+ text-align: left;
+ font-weight: normal;
+ vertical-align: middle; }
+
+q, blockquote {
+ quotes: none; }
+ q:before, q:after, blockquote:before, blockquote:after {
+ content: "";
+ content: none; }
+
+a img {
+ border: none; }
+
+article, aside, details, figcaption, figure,
+footer, header, hgroup, menu, nav, section {
+ display: block; }
+
+/* CSS Reset */
+html, body, div, span, applet, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+a, abbr, acronym, address, big, cite, code,
+del, dfn, em, img, ins, kbd, q, s, samp,
+small, strike, strong, sub, sup, tt, var,
+b, u, i, center,
+dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td,
+article, aside, canvas, details, embed,
+figure, figcaption, footer, header, hgroup,
+menu, nav, output, ruby, section, summary,
+time, mark, audio, video {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ font-size: 100%;
+ font: inherit;
+ vertical-align: baseline; }
+
+body {
+ line-height: 1; }
+
+ol, ul {
+ list-style: none; }
+
+table {
+ border-collapse: collapse;
+ border-spacing: 0; }
+
+caption, th, td {
+ text-align: left;
+ font-weight: normal;
+ vertical-align: middle; }
+
+q, blockquote {
+ quotes: none; }
+ q:before, q:after, blockquote:before, blockquote:after {
+ content: "";
+ content: none; }
+
+a img {
+ border: none; }
+
+article, aside, details, figcaption, figure,
+footer, header, hgroup, menu, nav, section {
+ display: block; }
+
+body {
+ line-height: 1;
+ font-family: "Lucida Grande", sans-serif;
+ font-size: 13px; }
+
+ol, ul {
+ list-style: none; }
+
+blockquote, q {
+ quotes: none; }
+
+blockquote:before, blockquote:after,
+q:before, q:after {
+ content: '';
+ content: none; }
+
+table {
+ border-collapse: collapse;
+ border-spacing: 0; }
+
+/* App CSS */
+body, html {
+ color: #777;
+ background-color: #F2F4F5; }
+
+.sc-view {
+ position: relative;
+ overflow: visible; }
+
+body {
+ -moz-box-shadow: rgba(0, 0, 0, 0.6) 0 0 1px;
+ -webkit-box-shadow: rgba(0, 0, 0, 0.6) 0 0 1px;
+ -o-box-shadow: rgba(0, 0, 0, 0.6) 0 0 1px;
+ box-shadow: rgba(0, 0, 0, 0.6) 0 0 1px;
+ -moz-border-radius: 8px;
+ -webkit-border-radius: 8px;
+ -o-border-radius: 8px;
+ -ms-border-radius: 8px;
+ -khtml-border-radius: 8px;
+ border-radius: 8px;
+ position: absolute;
+ width: 600px;
+ left: 50%;
+ margin-top: 38px;
+ border: 1px solid #bbbbbb;
+ margin-left: -300px;
+ background-color: #fff;
+ padding: 40px 10px 10px; }
+ body .mark-all-done label {
+ margin-left: 5px;
+ font-weight: bold; }
+ body #stats {
+ overflow: hidden;
+ width: 100%;
+ padding: 5px 10px;
+ margin: 10px -10px;
+ background-color: #eee;
+ border-top: 1px solid #aaa;
+ border-bottom: 1px solid #aaa;
+ line-height: 25px; }
+ body #stats .remaining {
+ float: left; }
+ body #stats .sc-button {
+ background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(1%, #f9f9f9), color-stop(34%, #dddddd), color-stop(67%, #f2f2f2), color-stop(100%, #f7f7f7));
+ background-image: -webkit-linear-gradient(#f9f9f9 1%, #dddddd, #f2f2f2, #f7f7f7);
+ background-image: -moz-linear-gradient(#f9f9f9 1%, #dddddd, #f2f2f2, #f7f7f7);
+ background-image: -o-linear-gradient(#f9f9f9 1%, #dddddd, #f2f2f2, #f7f7f7);
+ background-image: linear-gradient(#f9f9f9 1%, #dddddd, #f2f2f2, #f7f7f7);
+ border: 1px solid #828282;
+ color: #000;
+ float: right;
+ padding: 5px; }
+ body #stats .sc-button:hover {
+ background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(1%, #ffffff), color-stop(34%, #e2e2e2), color-stop(67%, #f7f7f7), color-stop(100%, #fcfcfc));
+ background-image: -webkit-linear-gradient(#ffffff 1%, #e2e2e2, #f7f7f7, #fcfcfc);
+ background-image: -moz-linear-gradient(#ffffff 1%, #e2e2e2, #f7f7f7, #fcfcfc);
+ background-image: -o-linear-gradient(#ffffff 1%, #e2e2e2, #f7f7f7, #fcfcfc);
+ background-image: linear-gradient(#ffffff 1%, #e2e2e2, #f7f7f7, #fcfcfc); }
+ body #stats .sc-button.is-active {
+ background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(1%, #efefef), color-stop(34%, #d3d3d3), color-stop(67%, #e8e8e8), color-stop(100%, #ededed));
+ background-image: -webkit-linear-gradient(#efefef 1%, #d3d3d3, #e8e8e8, #ededed);
+ background-image: -moz-linear-gradient(#efefef 1%, #d3d3d3, #e8e8e8, #ededed);
+ background-image: -o-linear-gradient(#efefef 1%, #d3d3d3, #e8e8e8, #ededed);
+ background-image: linear-gradient(#efefef 1%, #d3d3d3, #e8e8e8, #ededed); }
+ body input[type='text'] {
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
+ -o-border-radius: 5px;
+ -ms-border-radius: 5px;
+ -khtml-border-radius: 5px;
+ border-radius: 5px;
+ -moz-box-shadow: 0 0 10px -2px rgba(0, 0, 0, 0.6);
+ -webkit-box-shadow: 0 0 10px -2px rgba(0, 0, 0, 0.6);
+ -o-box-shadow: 0 0 10px -2px rgba(0, 0, 0, 0.6);
+ box-shadow: 0 0 10px -2px rgba(0, 0, 0, 0.6);
+ color: #999;
+ background-color: #f0f0f0;
+ width: 588px;
+ font-size: 30px;
+ font-family: Helvetica, sans-serif;
+ padding: 5px;
+ border: 1px solid #bbbbbb;
+ font-weight: 500; }
+ body input[type='text']::-webkit-input-placeholder {
+ color: #aaa; }
+ body h1 {
+ -moz-border-radius-topleft: 8px;
+ -webkit-border-top-left-radius: 8px;
+ -o-border-top-left-radius: 8px;
+ -ms-border-top-left-radius: 8px;
+ -khtml-border-top-left-radius: 8px;
+ border-top-left-radius: 8px;
+ -moz-border-radius-topright: 8px;
+ -webkit-border-top-right-radius: 8px;
+ -o-border-top-right-radius: 8px;
+ -ms-border-top-right-radius: 8px;
+ -khtml-border-top-right-radius: 8px;
+ border-top-right-radius: 8px;
+ background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(49%, #f4f4f4), color-stop(51%, #ededed), color-stop(100%, #dedede));
+ background-image: -webkit-linear-gradient(#ffffff, #f4f4f4 49%, #ededed 51%, #dedede);
+ background-image: -moz-linear-gradient(#ffffff, #f4f4f4 49%, #ededed 51%, #dedede);
+ background-image: -o-linear-gradient(#ffffff, #f4f4f4 49%, #ededed 51%, #dedede);
+ background-image: linear-gradient(#ffffff, #f4f4f4 49%, #ededed 51%, #dedede);
+ text-shadow: white 0 1px 1px;
+ font-size: 15px;
+ position: absolute;
+ width: 600px;
+ height: 20px;
+ color: #53565e;
+ top: 0;
+ left: 0;
+ padding: 5px 10px;
+ border-bottom: 1px solid #bbbbbb; }
+ body .sc-checkbox input[type="checkbox"] {
+ margin-right: 7px; }
+ body ul {
+ margin: 10px 0 2px 0; }
+ body ul li {
+ padding: 5px; }
+ body ul li.is-done {
+ color: #B7B7B7;
+ text-decoration: line-through; }
+ body ul li:nth-child(odd) {
+ background-color: #F7F7F7; }
+
+label > input[type="checkbox"] {
+ margin-right: 8px;
+}
+
+.ember-button {
+ padding: 8px;
+ margin: 5px 5px 5px 0;
+}
150 todos/css/todos.scss
@@ -0,0 +1,150 @@
+@import "compass/css3";
+@import "compass/reset";
+
+/* CSS Reset */
+@include global-reset;
+
+body {
+ line-height: 1;
+ font-family: "Lucida Grande", sans-serif;
+ font-size: 13px;
+}
+ol, ul {
+ list-style: none;
+}
+blockquote, q {
+ quotes: none;
+}
+blockquote:before, blockquote:after,
+q:before, q:after {
+ content: '';
+ content: none;
+}
+table {
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+
+/* App CSS */
+body, html {
+ color: #777;
+ background-color: #F2F4F5;
+}
+
+.sc-view {
+ position: relative;
+ overflow: visible;
+}
+
+$width: 600px;
+$border: 1px solid #bbb;
+
+body {
+ @include box-shadow(rgba(0,0,0,0.6) 0 0 1px);
+ @include border-radius(8px);
+
+ $padding: 10px;
+ $header-height: 20px;
+
+ position: absolute;
+ width: $width;
+ left: 50%;
+ margin-top: 38px;
+ border: $border;
+ margin-left: -300px;
+ background-color: #fff;
+ padding: ($header-height + $padding * 2) $padding $padding;
+
+ .mark-all-done label {
+ margin-left: 5px;
+ font-weight: bold;
+ }
+
+ #stats {
+ overflow: hidden;
+ width: 100%;
+ padding: 5px $padding;
+ margin: $padding ($padding * -1);
+ background-color: #eee;
+ border-top: 1px solid #aaa;
+ border-bottom: 1px solid #aaa;
+ line-height: 25px;
+
+ .remaining {
+ float: left;
+ }
+
+ .sc-button {
+ @include background-image(linear-gradient(#F9F9F9 1%, #DDD, #F2F2F2, #F7F7F7));
+ border: 1px solid #828282;
+ color: #000;
+ float: right;
+ padding: 5px;
+
+ &:hover {
+ @include background-image(linear-gradient(#FFF 1%, #E2E2E2, #F7F7F7, #FCFCFC));
+ }
+
+ &.is-active {
+ @include background-image(linear-gradient(#EFEFEF 1%, #D3D3D3, #E8E8E8, #EDEDED));
+ }
+ }
+ }
+
+ input[type='text'] {
+ @include border-radius(5px);
+ @include single-box-shadow(rgba(0,0,0,0.6), 0, 0, 10px, -2px);
+ color: #999;
+ background-color: rgb(240,240,240);
+ width: $width - ($padding) - 2px;
+ font-size: 30px;
+ font-family: Helvetica, sans-serif;
+ padding: 5px;
+ border: $border;
+ font-weight: 500;
+
+ &::-webkit-input-placeholder {
+ color: #aaa;
+ }
+ }
+
+ h1 {
+ @include border-top-radius(8px);
+ @include background-image(linear-gradient(color-stops(white, rgb(244,244,244) 49%, rgb(237,237,237) 51%, #dedede)));
+ @include single-text-shadow(white, 0, 1px, 1px);
+
+ font-size: 15px;
+ position: absolute;
+ width: $width;
+ height: $header-height;
+ color: rgb(83,86,94);
+ top: 0;
+ left: 0;
+ padding: ($padding / 2) $padding;
+ border-bottom: $border;
+ }
+
+ .sc-checkbox {
+ input[type="checkbox"] {
+ margin-right: 7px;
+ }
+ }
+
+ ul {
+ margin: 10px 0 2px 0;
+
+ li {
+ padding: 5px;
+
+ &.is-done {
+ color: #B7B7B7;
+ text-decoration: line-through;
+ }
+ }
+
+ li:nth-child(odd) {
+ background-color: #F7F7F7;
+ }
+ }
+}
+
BIN  todos/favicon.ico
Binary file not shown
53 todos/index.html
@@ -0,0 +1,53 @@
+<!doctype html>
+<!--[if lt IE 7 ]> <html lang="en" class="no-js ie6"> <![endif]--> <!--[if IE 7 ]> <html lang="en" class="no-js ie7"> <![endif]--> <!--[if IE 8 ]> <html lang="en" class="no-js ie8"> <![endif]--> <!--[if IE 9 ]> <html lang="en" class="no-js ie9"> <![endif]-->
+<!--[if (gt IE 9)|!(IE)]><!--> <html lang="en" class="no-js"> <!--<![endif]-->
+<head>
+ <meta charset="UTF-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+
+ <title>Ember Todos</title>
+ <meta name="description" content="">
+ <meta name="author" content="">
+
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+
+ <link rel="shortcut icon" href="/favicon.ico">
+ <link rel="apple-touch-icon" href="/apple-touch-icon.png">
+ <link rel="stylesheet" href="css/style.css?v=2">
+ <link rel="stylesheet" href="css/todos.css">
+
+ <!--[if lt IE 9]>
+ <script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
+ <![endif]-->
+
+ <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
+ <script type="text/javascript" src="../lib/ember.min.js"></script>
+</head>
+<body>
+ <h1>Todos</h1>
+ <script type="text/x-handlebars">
+ {{view Todos.CreateTodoView id="new-todo" placeholder="What needs to be done?"}}
+
+ <!-- Insert this after the CreateTodoView and before the collection. -->
+ {{#view Todos.StatsView id="stats"}}
+ {{#view Em.Button classBinding="isActive"
+ target="Todos.todosController"
+ action="clearCompletedTodos"}}
+ Clear Completed Todos
+ {{/view}}
+ {{remainingString}} remaining
+ {{/view}}
+
+ {{view Em.Checkbox class="mark-all-done"
+ title="Mark All as Done"
+ valueBinding="Todos.todosController.allAreDone"}}
+
+ {{#collection contentBinding="Todos.todosController" tagName="ul" itemClassBinding="content.isDone"}}
+ {{view Em.Checkbox titleBinding="content.title"
+ valueBinding="content.isDone"}}
+ {{/collection}}
+ </script>
+
+ <script src="js/app.js"></script>
+</body>
+</html>
54 todos/js/app.js
@@ -0,0 +1,54 @@
+Todos = Ember.Application.create();
+
+Todos.Todo = Em.Object.extend({
+ title: null,
+ isDone: false
+});
+
+Todos.todosController = Em.ArrayProxy.create({
+ content: [],
+
+ createTodo: function(title) {
+ var todo = Todos.Todo.create({ title: title });
+ this.pushObject(todo);
+ },
+
+ clearCompletedTodos: function() {
+ this.filterProperty('isDone', true).forEach(this.removeObject, this);
+ },
+
+ remaining: function() {
+ return this.filterProperty('isDone', false).get('length');
+ }.property('@each.isDone'),
+
+ allAreDone: function(key, value) {
+ if (value !== undefined) {
+ this.setEach('isDone', value);
+
+ return value;
+ } else {
+ return !!this.get('length') && this.everyProperty('isDone', true);
+ }
+ }.property('@each.isDone')
+});
+
+Todos.StatsView = Em.View.extend({
+ remainingBinding: 'Todos.todosController.remaining',
+
+ remainingString: function() {
+ var remaining = this.get('remaining');
+ return remaining + (remaining === 1 ? " item" : " items");
+ }.property('remaining')
+});
+
+Todos.CreateTodoView = Em.TextField.extend({
+ insertNewline: function() {
+ var value = this.get('value');
+
+ if (value) {
+ Todos.todosController.createTodo(value);
+ this.set('value', '');
+ }
+ }
+});
+
Please sign in to comment.
Something went wrong with that request. Please try again.