Commit 105935d4 authored by addyosmani's avatar addyosmani

Adding re-write of the Ember application.

parent 50ed86e1
/* Helpers */
.hidden {
display: none
}
/* ==== 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"] { margin-right: 5px;
vertical-align: middle; }
.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; }
}
\ No newline at end of file
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 {
}
.sc-view {
position: relative;
overflow: visible; }
/*new additions*/
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px;
line-height: 1.4em;
background: #eeeeee;
color: #333333;
}
#todoapp {
width: 480px;
margin: 0 auto 40px;
background: white;
padding: 20px;
-moz-box-shadow: rgba(0, 0, 0, 0.2) 0 5px 6px 0;
-webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 5px 6px 0;
-o-box-shadow: rgba(0, 0, 0, 0.2) 0 5px 6px 0;
box-shadow: rgba(0, 0, 0, 0.2) 0 5px 6px 0;
-moz-border-radius-bottomleft: 5px;
-webkit-border-bottom-left-radius: 5px;
-o-border-bottom-left-radius: 5px;
-ms-border-bottom-left-radius: 5px;
-khtml-border-bottom-left-radius: 5px;
border-bottom-left-radius: 5px;
-moz-border-radius-bottomright: 5px;
-webkit-border-bottom-right-radius: 5px;
-o-border-bottom-right-radius: 5px;
-ms-border-bottom-right-radius: 5px;
-khtml-border-bottom-right-radius: 5px;
border-bottom-right-radius: 5px;
}
#todoapp h1 {
font-size: 36px;
font-weight: bold;
text-align: center;
padding: 20px 0 30px 0;
line-height: 1;
}
#create-todo {
position: relative;
}
#create-todo input {
width: 466px;
font-size: 24px;
font-family: inherit;
line-height: 1.4em;
border: 0;
outline: none;
padding: 6px;
border: 1px solid #999999;
-moz-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset;
-webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset;
-o-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset;
box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset;
}
#create-todo input::-webkit-input-placeholder {
font-style: italic;
}
#create-todo span {
position: absolute;
z-index: 999;
width: 170px;
left: 50%;
margin-left: -85px;
}
#todo-list {
margin-top: 10px;
}
#todo-list li {
padding: 12px 20px 11px 0;
position: relative;
font-size: 24px;
line-height: 1.1em;
border-bottom: 1px solid #cccccc;
}
#todo-list li:after {
content: "\0020";
display: block;
height: 0;
clear: both;
overflow: hidden;
visibility: hidden;
}
#todo-list li.editing {
padding: 0;
border-bottom: 0;
}
#todo-list .editing .display,
#todo-list .edit {
display: none;
}
#todo-list .editing .edit {
display: block;
}
#todo-list .editing input {
width: 444px;
font-size: 24px;
font-family: inherit;
margin: 0;
line-height: 1.6em;
border: 0;
outline: none;
padding: 10px 7px 0px 27px;
border: 1px solid #999999;
-moz-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset;
-webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset;
-o-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset;
box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset;
}
#todo-list .check {
position: relative;
top: 9px;
margin: 0 10px 0 7px;
float: left;
}
#todo-list .is-done label {
text-decoration: line-through;
color: #777777;
}
#todo-list .todo-destroy {
position: absolute;
right: 5px;
top: 14px;
display: none;
cursor: pointer;
width: 20px;
height: 20px;
background: url(destroy.png) no-repeat 0 0;
}
#todo-list li:hover .todo-destroy {
display: block;
}
#todo-list .todo-destroy:hover {
background-position: 0 -20px;
}
#todo-stats {
*zoom: 1;
margin-top: 10px;
color: #777777;
}
#todo-stats:after {
content: "\0020";
display: block;
height: 0;
clear: both;
overflow: hidden;
visibility: hidden;
}
#todo-stats .todo-count {
float: left;
}
#todo-stats .todo-count .number {
font-weight: bold;
color: #333333;
}
#todo-stats .todo-clear {
float: right;
}
#todo-stats .todo-clear a {
color: #777777;
font-size: 12px;
}
#todo-stats .todo-clear a:visited {
color: #777777;
}
#todo-stats .todo-clear a:hover {
color: 336699;
}
#instructions {
width: 520px;
margin: 10px auto;
color: #777777;
text-shadow: rgba(255, 255, 255, 0.8) 0 1px 0;
text-align: center;
}
#instructions a {
color: #336699;
}
#credits {
width: 520px;
margin: 30px auto;
color: #999;
text-shadow: rgba(255, 255, 255, 0.8) 0 1px 0;
text-align: center;
}
#credits a {
color: #888;
}
/* line 109 */
#todoapp #todo-stats {
*zoom: 1;
margin-top: 10px;
color: #555555;
-moz-border-radius-bottomleft: 5px;
-webkit-border-bottom-left-radius: 5px;
-o-border-bottom-left-radius: 5px;
-ms-border-bottom-left-radius: 5px;
-khtml-border-bottom-left-radius: 5px;
border-bottom-left-radius: 5px;
-moz-border-radius-bottomright: 5px;
-webkit-border-bottom-right-radius: 5px;
-o-border-bottom-right-radius: 5px;
-ms-border-bottom-right-radius: 5px;
-khtml-border-bottom-right-radius: 5px;
border-bottom-right-radius: 5px;
background: #f4fce8;
border-top: 1px solid #ededed;
padding: 0 20px;
line-height: 36px;
}
/* line 22, /opt/ree/lib/ruby/gems/1.8/gems/compass-0.10.5/frameworks/compass/stylesheets/compass/utilities/general/_clearfix.scss */
#todoapp #todo-stats:after {
content: "\0020";
display: block;
height: 0;
clear: both;
overflow: hidden;
visibility: hidden;
}
/* line 118 */
#todoapp #todo-stats .todo-count {
float: left;
}
/* line 120 */
#todoapp #todo-stats .todo-count .number {
font-weight: bold;
color: #555555;
}
/* line 123 */
#todoapp #todo-stats .todo-clear {
float: right;
}
/* line 125 */
#todoapp #todo-stats button {
display: block;
line-height: 20px;
text-decoration: none;
-moz-border-radius: 12px;
-webkit-border-radius: 12px;
-o-border-radius: 12px;
-ms-border-radius: 12px;
-khtml-border-radius: 12px;
border-radius: 12px;
background: rgba(0, 0, 0, 0.1);
color: #555555;
font-size: 11px;
margin-top: 8px;
padding: 0 10px 1px;
-moz-box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0 0;
-webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0 0;
-o-box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0 0;
box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0 0;
float:right;
border:0px;
}
/* line 136 */
#todoapp #todo-stats button:hover, #todoapp #todo-stats button:focus {
background: rgba(0, 0, 0, 0.15);
-moz-box-shadow: rgba(0, 0, 0, 0.3) 0 -1px 0 0;
-webkit-box-shadow: rgba(0, 0, 0, 0.3) 0 -1px 0 0;
-o-box-shadow: rgba(0, 0, 0, 0.3) 0 -1px 0 0;
box-shadow: rgba(0, 0, 0, 0.3) 0 -1px 0 0;
}
#todos { display:block}
@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;
}
}
}
......@@ -4,52 +4,77 @@
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>ember.js • TodoMVC</title>
<link rel="stylesheet" href="css/style.css?v=2">
<link rel="stylesheet" href="css/todos.css">
<link rel="stylesheet" href="../../assets/base.css">
<link rel="stylesheet" href="css/app.css">
<!--[if IE]>
<script src="../../assets/ie.js"></script>
<![endif]-->
</head>
<body>
<div id="todoapp">
<div class="title">
<h1>Todos</h1>
</div>
<div class="content">
<script type="text/x-handlebars">
{{#view id="create-todo"}}
{{view Todos.CreateTodoView id="new-todo" placeholder="What needs to be done?"}}
{{/view}}
{{#view id="stats-area"}}
{{view Ember.Checkbox class="mark-all-done"
title="Mark all as complete"
valueBinding="Todos.todosController.allAreDone"}}
{{#view id="todos"}}
{{#collection id="todo-list" contentBinding="Todos.todosController" tagName="ul" itemClassBinding="content.isDone"}}
{{view Ember.Checkbox titleBinding="content.title" valueBinding="content.isDone"}}
{{/collection}}
{{/view}}
<!-- Insert this after the CreateTodoView and before the collection. -->
{{#view Todos.StatsView id="todo-stats" content=this}}
{{#view Todos.ClearCompletedButtonView target="Todos.todosController" action="clearCompletedTodos" classNameBindings="completedButtonClass"}}
Clear {{completedString}}
{{/view}}
{{remainingString}} left
{{/view}}
{{/view}}
<section id="todoapp"></section>
<footer id="info">
<p>Double-click to edit a todo</p>
<p>
Created by
<a href="http://github.com/tomdale">Tom Dale</a>,
<a href="http://github.com/addyosmani">Addy Osmani</a>
</p>
<p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
</footer>
<!-- /* Handlebars templates start */ -->
<script id="statsTemplate" type="text/x-handlebars">
{{#with view}}
{{#if oneLeft }}
<strong>{{entries.remaining}}</strong> item left
{{else}}
<strong>{{entries.remaining}}</strong> items left
{{/if}}
{{/with}}
</script>
</div>
</div>
<div id="credits">
Credits: Tom Dale, Addy Osmani
</div>
<script id="filtersTemplate" type="text/x-handlebars">
<ul id="filters">
<li>
<a {{action showAll href=true}}>All </a>
</li>
<li>
<a {{action showActive href=true}}>Active</a>
</li>
<li>
<a {{action showCompleted href=true}}>Completed</a>
</li>
</ul>
</script>
<script id="clearBtnTemplate" type="text/x-handlebars">
{{#with view}}
<button {{action "clearCompleted" target="entries"}} {{bindAttr class="buttonClass:hidden"}} >
Clear completed ({{entries.completed}})
</button>
{{/with}}
</script>
<script id="todosTemplate" type="text/x-handlebars">
{{#unless view.content.editing}}
{{view Ember.Checkbox checkedBinding="view.content.completed" class="toggle"}}
<label>{{view.content.title}}</label>
<button {{action removeItem target="this"}} class="destroy" ></button>
{{else}}
{{view view.ItemEditorView contentBinding="view.content"}}
{{/unless}}
</script>
<!-- /* Handlebars templates end */ -->
<script src="../../assets/base.js"></script>
<script src="../../assets/jquery.min.js"></script>
<script src="js/libs/ember-0.9.min.js"></script>
<script src="js/libs/handlebars-1.0.0.beta.6.js"></script>
<script src="js/libs/ember-latest.js"></script>
<script src="js/app.js"></script>
<script src="js/router.js"></script>
<script src="js/models/todo.js"></script>
<script src="js/models/store.js"></script>
<script src="js/controllers/entries.js"></script>
<script src="js/controllers/todos.js"></script>
<script src="js/views/application.js"></script>
<script src="js/views/todos.js"></script>
<script type="text/javascript">
Todos.initialize();
</script>
</body>
</html>
\ No newline at end of file
</html>
Todos = Ember.Application.create();
Todos.Todo = Ember.Object.extend({
id: null,
title: null,
isDone: false,
todoChanged: function () {
Todos.TodoStore.update(this);
}.observes('title', 'isDone')
});
Todos.todosController = Ember.ArrayProxy.create({
content: [],
createTodo: function(title) {
var todo = Todos.Todo.create({ title: title }),
stats = document.getElementById('stats-area');
this.pushObject(todo);
Todos.TodoStore.create(todo);
(stats.style.display=='block')? stats.style.display = 'inline' : stats.style.display = 'block';
},
pushObject: function (item, ignoreStorage) {
if (!ignoreStorage)
Todos.TodoStore.create(item);
return this._super(item);
},
removeObject: function (item) {
Todos.TodoStore.remove(item);
return this._super(item);
},
clearCompletedTodos: function() {
this.filterProperty('isDone', true).forEach(this.removeObject, this);
},
remaining: function() {
return this.filterProperty('isDone', false).get('length');
}.property('@each.isDone'),
completed: function() {
return this.filterProperty('isDone', true).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'),
completeClass: function () {
return this.get('completed') < 1 ? 'none-completed' : 'some-completed';
}.property('@each.isDone')
});
Todos.StatsView = Ember.View.extend({
remainingBinding: 'Todos.todosController.remaining',
remainingString: function() {
var remaining = this.get('remaining');
return remaining + (remaining === 1 ? " item" : " items");
}.property('remaining')
});
Todos.CreateTodoView = Ember.TextField.extend({
insertNewline: function() {
var value = this.get('value');
if (value) {
Todos.todosController.createTodo(value);
this.set('value', '');
}
}
});
Todos.ClearCompletedButtonView = Ember.Button.extend({
completedBinding: 'Todos.todosController.completed',
completedString: function() {
var completed = this.get('completed');
return completed + " completed" + (completed === 1 ? " item" : " items");
}.property('completed'),
completedButtonClass: function () {
if (this.get('completed') < 1)
return 'hidden';
else
return '';
}.property('completed')
});
Todos.TodoStore = (function () {
// Generate four random hex digits.
var S4 = function () {
return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
};
// Generate a pseudo-GUID by concatenating random hexadecimal.
var guid = function () {
return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
};
// Our Store is represented by a single JS object in *localStorage*. Create it
// with a meaningful name, like the name you'd give a table.
var Store = function(name) {
this.name = name;
var store = localStorage.getItem(this.name);
this.data = (store && JSON.parse(store)) || {};
// Save the current state of the **Store** to *localStorage*.
this.save = function() {
localStorage.setItem(this.name, JSON.stringify(this.data));
};
// Add a model, giving it a (hopefully)-unique GUID, if it doesn't already
// have an id of it's own.
this.create = function (model) {
if (!model.get('id')) model.set('id', guid());
return this.update(model);
};
// Update a model by replacing its copy in `this.data`.
this.update = function(model) {
this.data[model.get('id')] = model.getProperties('id', 'title', 'isDone');
this.save();
return model;
};
// Retrieve a model from `this.data` by id.
this.find = function(model) {
return Todos.Todo.create(this.data[model.get('id')]);
};
// Return the array of all models currently in storage.
this.findAll = function() {
var result = [];
for (var key in this.data)
{
var todo = Todos.Todo.create(this.data[key]);
result.push(todo);
}
return result;
};
// Delete a model from `this.data`, returning it.
this.remove = function(model) {
delete this.data[model.get('id')];
this.save();
return model;
};
};
return new Store('todos-emberjs');
})();
(function () {
var items = Todos.TodoStore.findAll();
if(items.length > 1){
Todos.todosController.set('[]', items);
}
})();
(function( win ) {
'use strict';
win.Todos = Ember.Application.create({
VERSION: '0.2',
rootElement: '#todoapp',
storeNamespace: 'todos-emberjs',
// Extend to inherit outlet support
ApplicationController: Ember.Controller.extend(),
});
})( window );
(function( app ) {
'use strict';
var Entries = Ember.ArrayProxy.extend({
store: new app.Store( app.storeNamespace ),
content: [],
createNew: function( value ) {
if ( !value.trim() )
return;
var todo = this.get( 'store' ).createFromTitle( value );
this.pushObject( todo );
},
pushObject: function( item, ignoreStorage) {
if ( !ignoreStorage )
this.get( 'store' ).create( item );
return this._super( item );
},
removeObject: function( item ) {
this.get( 'store' ).remove( item );
return this._super( item );
},
clearCompleted: function() {
this.filterProperty(
'completed', true
).forEach( this.removeObject, this );
},
total: function() {
return this.get( 'length' );
}.property( '@each.length' ),
remaining: function() {
return this.filterProperty( 'completed', false ).get( 'length' );
}.property( '@each.completed' ),
completed: function() {
return this.filterProperty( 'completed', true ).get( 'length' );
}.property( '@each.completed' ),
noneLeft: function() {
return this.get( 'total' ) === 0;
}.property( 'total' ),
allAreDone: function( key, value ) {
if ( value !== undefined ) {
this.setEach( 'completed', value );
return value;
} else {
return !!this.get( 'length' ) &&
this.everyProperty( 'completed', true );
}
}.property( '@each.completed' ),
init: function() {
this._super();
// Load items if any upon initialization
var items = this.get( 'store' ).findAll();
if ( items.get( 'length' ) ) {
this.set( '[]', items );
};
}
});
app.EntriesController = Entries;
app.entriesController = Entries.create();
})( window.Todos );
(function( app ) {
'use strict';
var TodosController = Ember.Controller.extend({
entries: function() {
var filter = this.getPath( 'content.filterBy' );
if ( Ember.empty( filter ) ) {
return this.get( 'content' );
}
if ( !Ember.compare( filter, 'completed' ) ) {
return this.get( 'content' ).filterProperty( 'completed', true );
}
if ( !Ember.compare( filter, 'active' ) ) {
return this.get( 'content' ).filterProperty( 'completed', false );
}
}.property( 'content.remaining', 'content.filterBy' )
});
app.TodosController = TodosController;
})( window.Todos );
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
(function( app ) {
'use strict';
var Store = function( name ) {
this.name = name;
var store = localStorage.getItem( this.name );
this.data = ( store && JSON.parse( store ) ) || {};
// Save the current state of the **Store** to *localStorage*.
this.save = function() {
localStorage.setItem( this.name, JSON.stringify( this.data ) );
};
// Wrapper around `this.create`
// Creates a `Todo` model object out of the title
this.createFromTitle = function( title ) {
var todo = app.Todo.create({
title: title,
store: this
});
this.create( todo );
return todo;
};
// Store the model inside the `Store`
this.create = function ( model ) {
if ( !model.get( 'id' ) )
model.set( 'id', Date.now() );
return this.update( model );
};
// Update a model by replacing its copy in `this.data`.
this.update = function( model ) {
this.data[ model.get( 'id' ) ] = model.getProperties(
'id', 'title', 'completed'
);
this.save();
return model;
};
// Retrieve a model from `this.data` by id.
this.find = function( model ) {
var todo = app.Todo.create( this.data[ model.get( 'id' ) ] );
todo.set( 'store', this );
return todo;
};
// Return the array of all models currently in storage.
this.findAll = function() {
var result = [],
key;
for ( key in this.data ) {
var todo = app.Todo.create( this.data[ key ] );
todo.set( 'store', this );
result.push( todo );
}
return result;
};
// Delete a model from `this.data`, returning it.
this.remove = function( model ) {
delete this.data[ model.get( 'id' ) ];
this.save();
return model;
};
};
app.Store = Store;
})( window.Todos );
(function( app ) {
'use strict';
app.Todo = Ember.Object.extend({
id: null,
title: null,
completed: false,
// set store reference upon creation instead of creating static bindings
store: null,
// Observer that will react on item change and will update the storage
todoChanged: function() {
this.get( 'store' ).update( this );
}.observes( 'title', 'completed' )
});
})( window.Todos);
(function( app ) {
'use strict';
var Router = Ember.Router.extend({
root: Ember.Route.extend({
showAll: Ember.Route.transitionTo( 'index' ),
showActive: Ember.Route.transitionTo( 'active' ),
showCompleted: Ember.Route.transitionTo( 'completed' ),
index: Ember.Route.extend({
route: '/',
connectOutlets: function( router ) {
var controller = router.get( 'applicationController' );
var context = app.entriesController;
context.set( 'filterBy', '' );
controller.connectOutlet( 'todos', context )
}
}),
active: Ember.Route.extend({
route: '/active',
connectOutlets: function( router ) {
var controller = router.get( 'applicationController' );
var context = app.entriesController;
context.set( 'filterBy', 'active' );
controller.connectOutlet( 'todos', context )
}
}),
completed: Ember.Route.extend({
route: '/completed',
connectOutlets: function( router ) {
var controller = router.get( 'applicationController' );
var context = app.entriesController;
context.set( 'filterBy', 'completed' );
controller.connectOutlet( 'todos', context )
}
}),
specs: Ember.Route.extend({
route: '/specs',
connectOutlets: function() {
// TODO: Write them
}
})
})
});
app.Router = Router;
})( window.Todos );
(function( app ) {
'use strict';
var ApplicationView = Ember.ContainerView.extend({
childViews: [ 'headerView', 'mainView', 'footerView' ],
headerView: Ember.ContainerView.create({
childViews: [ 'titleView', 'createTodoView' ],
elementId: 'header',
tagName: 'header',
titleView: Ember.View.create({
tagName: 'h1',
template: function() {
return 'todos';
}
}),
createTodoView: Ember.TextField.create({
entriesBinding: 'controller.namespace.entriesController',
placeholder: 'What needs to be done?',
elementId: 'new-todo',
insertNewline: function() {
var value = this.get( 'value' );
if ( value ) {
this.get( 'entries' ).createNew( value );
this.set( 'value', '' );
}
}
}),
}),
mainView: Em.ContainerView.create({
elementId: 'main',
tagName: 'section',
visibilityBinding: 'controller.namespace.entriesController.noneLeft',
classNameBindings: [ 'visibility:hidden' ],
childViews: [ 'outletView', 'markAllChkbox' ],
outletView: Ember.View.create({
template: Ember.Handlebars.compile( '{{outlet}}' ),
}),
markAllChkbox: Ember.Checkbox.create({
entriesBinding: 'controller.namespace.entriesController',
elementId: 'toggle-all',
checkedBinding: 'entries.allAreDone'
})
}),
footerView: Ember.ContainerView.create({
elementId: 'footer',
tagName: 'footer',
visibilityBinding: 'controller.namespace.entriesController.noneLeft',
classNameBindings: [ 'visibility:hidden' ],
childViews: [ 'statsView', 'filtersView', 'clearBtnView' ],
statsView: Ember.View.create({
entriesBinding: 'controller.namespace.entriesController',
elementId: 'todo-count',
tagName: 'span',
templateName: 'statsTemplate',
oneLeft: function() {
return this.getPath( 'entries.remaining' ) === 1;
}.property( 'entries.remaining' )
}),
filtersView: Ember.View.create({
templateName: 'filtersTemplate',
}),
clearBtnView: Ember.View.create({
entriesBinding: 'controller.namespace.entriesController',
templateName: 'clearBtnTemplate',
elementId: 'clear-completed',
buttonClass: function () {
return !this.getPath( 'entries.completed' );
}.property( 'entries.completed' )
})
})
});
app.ApplicationView = ApplicationView;
})( window.Todos);
(function( app ) {
'use strict';
var TodosView = Ember.CollectionView.extend({
contentBinding: 'controller.entries',
tagName: 'ul',
elementId: 'todo-list',
itemViewClass: Ember.View.extend({
templateName: 'todosTemplate',
classNames: [ 'view' ],
classNameBindings: ['content.completed', 'content.editing'],
doubleClick: function() {
this.get( 'content' ).set( 'editing', true );
},
removeItem: function() {
this.getPath( 'controller.content' ).removeObject(
this.get( 'content' )
);
},
ItemEditorView: Ember.TextField.extend({
valueBinding: 'content.title',
classNames: [ 'edit' ],
change: function() {
if ( Ember.empty( this.getPath( 'content.title' ) ) ) {
this.getPath( 'controller.content' ).removeObject(
this.get( 'content' )
);
}else{
this.get('content').set('title', this.getPath('content.title').trim());
}
},
whenDone: function() {
this.get( 'content' ).set( 'editing', false );
},
focusOut: function() {
this.whenDone();
},
didInsertElement: function() {
this.$().focus();
},
insertNewline: function() {
this.whenDone();
}
})
})
});
app.TodosView = TodosView;
})( window.Todos);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment