Commit 6ed23dfd authored by Jacob Mumm's avatar Jacob Mumm Committed by Sindre Sorhus

Close GH-161: Updating Angular, Angular/Persistence examples. Fixes #153, Fixes #154

parent 5cc9baa7
/*this doesn't seem to be used in the jquery example at all. Its getting in the way */
#todo-count span {
font-weight: inherit;
}
\ No newline at end of file
html,
body {
margin: 0;
padding: 0;
}
body {
font: 14px "Helvetica Neue", Helvetica, Arial, sans-serif;
line-height: 1.4em;
background: #eeeeee;
color: #333333;
width: 520px;
margin: 0 auto;
-webkit-font-smoothing: antialiased;
}
#todoapp {
background: #fff;
padding: 20px;
margin-bottom: 40px;
-webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 2px 6px 0;
-moz-box-shadow: rgba(0, 0, 0, 0.2) 0 2px 6px 0;
-ms-box-shadow: rgba(0, 0, 0, 0.2) 0 2px 6px 0;
-o-box-shadow: rgba(0, 0, 0, 0.2) 0 2px 6px 0;
box-shadow: rgba(0, 0, 0, 0.2) 0 2px 6px 0;
-webkit-border-radius: 0 0 5px 5px;
-moz-border-radius: 0 0 5px 5px;
-ms-border-radius: 0 0 5px 5px;
-o-border-radius: 0 0 5px 5px;
border-radius: 0 0 5px 5px;
}
#todoapp h1 {
font-size: 36px;
font-weight: bold;
text-align: center;
padding: 0 0 10px 0;
}
#todoapp input[type="text"] {
width: 466px;
font-size: 24px;
font-family: inherit;
line-height: 1.4em;
border: 0;
outline: none;
padding: 6px;
border: 1px solid #999999;
-webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset;
-moz-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset;
-ms-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;
}
#todoapp input::-webkit-input-placeholder {
font-style: italic;
}
#todo-list {
margin: 10px 0;
padding: 0;
list-style: none;
}
#todo-list li {
padding: 18px 20px 18px 0;
position: relative;
font-size: 24px;
border-bottom: 1px solid #cccccc;
}
#todo-list li:last-child {
border-bottom: none;
}
#todo-list li.done label {
color: #777777;
text-decoration: line-through;
}
#todo-list li .destroy {
display: none;
position: absolute;
top: 20px;
right: 10px;
cursor: pointer;
width: 20px;
height: 20px;
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUBAMAAAB/pwA+AAAABGdBTUEAALGPC/xhBQAAACdQTFRFzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMAAAA////zMzMhnu0WAAAAAt0Uk5T5u3pqtV3jFQEKAC0bVelAAAAfUlEQVQI12NYtWpFsc8R865VqxhWrZpyBgg8QcylZ8AgCsjMgTCPrWJYfgYKqhjWwJgaDDVnzpw+c2bPmTPHGWzOnNm95/TuM2cOM/AARXfvBooeZAAp270bRCIz4QoOIGtDMqwJZoUEQzvCYrhzuhhWtUKYEahOX7UK6iEA3A6NUGwCTZIAAAAASUVORK5CYII=') no-repeat center center;
}
#todo-list li:hover .destroy {
display: block;
}
#todo-list li.editing {
border-bottom: none;
margin-top: -1px;
padding: 0;
}
#todo-list li.editing:last-child {
margin-bottom: -1px;
}
#todo-list li.editing .edit {
display: block;
width: 444px;
padding: 13px 15px 14px 20px;
margin: 0;
}
#todo-list li.editing .view {
display: none;
}
#todo-list li .view label {
word-break: break-word;
}
#todo-list li .edit {
display: none;
}
#todoapp footer {
margin: 0 -20px -20px -20px;
overflow: hidden;
color: #555555;
background: #f4fce8;
border-top: 1px solid #ededed;
padding: 0 20px;
line-height: 37px;
-webkit-border-radius: 0 0 5px 5px;
-moz-border-radius: 0 0 5px 5px;
-ms-border-radius: 0 0 5px 5px;
-o-border-radius: 0 0 5px 5px;
border-radius: 0 0 5px 5px;
}
#clear-completed {
float: right;
line-height: 20px;
text-decoration: none;
background: rgba(0, 0, 0, 0.1);
color: #555555;
font-size: 11px;
margin-top: 8px;
margin-bottom: 8px;
padding: 0 10px 1px;
cursor: pointer;
-webkit-border-radius: 12px;
-moz-border-radius: 12px;
-ms-border-radius: 12px;
-o-border-radius: 12px;
border-radius: 12px;
-webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0 0;
-moz-box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0 0;
-ms-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;
}
#clear-completed:hover {
background: rgba(0, 0, 0, 0.15);
-webkit-box-shadow: rgba(0, 0, 0, 0.3) 0 -1px 0 0;
-moz-box-shadow: rgba(0, 0, 0, 0.3) 0 -1px 0 0;
-ms-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;
}
#clear-completed:active {
position: relative;
top: 1px;
}
#todo-count span {
font-weight: bold;
}
#instructions {
margin: 10px auto;
color: #777777;
text-shadow: rgba(255, 255, 255, 0.8) 0 1px 0;
text-align: center;
}
#instructions a {
color: #336699;
}
#credits {
margin: 30px auto;
color: #999;
text-shadow: rgba(255, 255, 255, 0.8) 0 1px 0;
text-align: center;
}
#credits a {
color: #888;
}
\ No newline at end of file
<!doctype html> <!doctype html>
<html ng-app="todomvc"> <html lang="en" ng-app="todomvc">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>AngularJS - TodoMVC</title> <title>AngularJS - TodoMVC</title>
<link rel="stylesheet" href="css/base.css"> <link rel="stylesheet" href="../../assets/base.css">
<link rel="stylesheet" href="css/app.css">
<!--[if IE]> <!--[if IE]>
<script src="../../assets/ie.js"></script> <script src="../../assets/ie.js"></script>
<![endif]--> <![endif]-->
</head> </head>
<body> <body>
<div ng-controller="TodoController" id="todoapp"> <section id="todoapp" ng-controller="TodoController">
<header> <header id="header">
<h1>Todos</h1> <h1>todos</h1>
<form id="todo-form" ng-submit="addTodo()"> <form id="todo-form" ng-submit="addTodo()">
<input type="text" id="new-todo" name="newTodo" ng-model="newTodo" placeholder="What needs to be done?"> <input id="new-todo" placeholder="What needs to be done?" type="text" name="newTodo" ng-model="newTodo">
</form> </form>
</header> </header>
<section id="main" ng-show="todos.length"> <section id="main" ng-show="todos.length">
<input type="checkbox" id="toggle-all" ng-click="markAllDone()" ng-checked="remainingTodos().length == 0"> <input id="toggle-all" type="checkbox" ng-model="allChecked" ng-change="markAllDone()" ng-checked="remainingTodos().length == 0">
<label for="toggle-all">Mark all as complete</label> <label for="toggle-all">Mark all as complete</label>
<ul id="todo-list"> <ul id="todo-list">
<li ng-repeat="todo in todos" ng-dblclick="editTodo(todo)" ng-class="{done: todo.done, editing: todo.editing}"> <li ng-repeat="todo in todos | filter:statusFilter" ng-dblclick="editTodo(todo)" ng-class="{completed: todo.completed, editing: todo.editing}">
<div class="view"> <div class="view">
<input type="checkbox" class="toggle" name="todo.done" ng-model="todo.done"> <input class="toggle" type="checkbox" name="todo.completed" ng-model="todo.completed">
<label ng-bind="todo.title">Loading...</label> <label ng-bind="todo.title">Loading...</label>
<a class="destroy" ng-click="removeTodo(todo)"></a> <button class="destroy" ng-click="removeTodo(todo)"></button>
</div> </div>
<form ng-submit="finishEditing(todo)"> <form ng-submit="finishEditing(todo)">
<input type="text" class="edit" name="todo.title" ng-model="todo.title" my:blur="finishEditing(todo)"> <input class="edit" type="text" name="todo.title" ng-model="todo.title" todo-blur="finishEditing(todo)" todo-focus="todo.editing">
</form> </form>
</li> </li>
</ul> </ul>
</section> </section>
<footer ng-show="todos.length"> <footer id="footer" ng-show="todos.length">
<a id="clear-completed" ng-click="clearDoneTodos()" ng-show="doneTodos().length" ng-bind-template="Clear {{doneTodos().length}} items"></a> <span id="todo-count"><strong>{{remainingTodos().length}}</strong> <ng-pluralize count="remainingTodos().length" when="todoForms"></ng-pluralize></span>
<div id="todo-count"> <ul id="filters">
<ng-pluralize count="remainingTodos().length" when="todoForms"></ng-pluralize> <li>
</div> <a ng-class="{selected: location.path() == '/'} " href="#/">All</a>
</li>
<li>
<a ng-class="{selected: location.path() == '/active'}" href="#/active">Active</a>
</li>
<li>
<a ng-class="{selected: location.path() == '/completed'}" href="#/completed">Completed</a>
</li>
</ul>
<button id="clear-completed" ng-click="clearDoneTodos()" ng-show="completedTodos().length" ng-bind-template="Clear completed ({{completedTodos().length}})"></button>
</footer>
</section>
<footer id="info">
<p>Double-click to edit a todo.</p>
<p>Credits: <a href="http://twitter.com/cburgdorf">Christoph Burgdorf</a>, <a href="http://ericbidelman.com">Eric Bidelman</a>, <a href="http://jacobmumm.com">Jacob Mumm</a></p>
</footer> </footer>
</div>
<div id="instructions">
Double-click to edit a todo.
</div>
<div id="credits">
Credits: <a href="http://twitter.com/cburgdorf">Christoph Burgdorf</a>, <a href="http://ericbidelman.com">Eric Bidelman</a>
</div>
<script src="../../assets/base.js"></script> <script src="../../assets/base.js"></script>
<script src="js/libs/angular/angular.min.js"></script> <script src="js/libs/angular/angular.min.js"></script>
<script src="js/controllers.js"></script> <script src="js/controllers/todo.js"></script>
<script src="js/directives/todo-directives.js"></script>
</body> </body>
</html> </html>
/* App Controllers */
var todomvc = angular.module('todomvc', []);
todomvc.controller('TodoController',['$scope',function ($scope) {
$scope.todos = retrieveStore();
// Call updateStore() whenever the todos array changes.
$scope.$watch('todos', updateStore, true);
$scope.todoForms = {
0: "You're done!",
one: '{} item left',
other: '{} items left'
};
function retrieveStore() {
var store = localStorage.getItem('todo-angularjs');
return (store && JSON.parse(store)) || [];
};
function updateStore() {
var isEditing = $scope.todos.filter(function(val) {
return val.editing;
}).length;
if (!isEditing) {
localStorage.setItem('todo-angularjs', JSON.stringify($scope.todos));
}
};
$scope.addTodo = function() {
if (this.newTodo.trim().length === 0) {
return;
}
$scope.todos.push({
title: this.newTodo,
done: false,
editing: false
});
this.newTodo = '';
};
$scope.editTodo = function(todo) {
//cancel any active editing operation
$scope.todos.forEach(function(val) {
val.editing = false;
});
todo.editing = true;
};
$scope.finishEditing = function(todo) {
if (todo.title.trim().length === 0) {
$scope.removeTodo(todo);
} else {
todo.editing = false;
}
};
$scope.removeTodo = function(todo) {
for (var i = 0, len = $scope.todos.length; i < len; ++i) {
if (todo === $scope.todos[i]) {
$scope.todos.splice(i, 1);
}
}
};
$scope.remainingTodos = function() {
return $scope.todos.filter(function(val) {
return !val.done;
});
};
$scope.doneTodos = function() {
return $scope.todos.filter(function(val) {
return val.done;
});
}
$scope.clearDoneTodos = function() {
$scope.todos = $scope.remainingTodos();
};
$scope.markAllDone = function() {
var markDone = true;
if (!$scope.remainingTodos().length) {
markDone = false;
}
$scope.todos.forEach(function(todo) {
todo.done = markDone;
});
};
}]);
/* App Controllers */
var todomvc = angular.module('todomvc', []);
function TodoController($scope, $location) {
$scope.todos = retrieveStore();
$scope.newTodo = "";
if($location.path()=='') $location.path('/');
$scope.location = $location;
// Call updateStore() whenever the todos array changes.
$scope.$watch('todos', updateStore, true);
$scope.$watch(function() {return $location.path(); }, function(path) {
$scope.statusFilter = path == '/active' ?
{ completed: false } : path == '/completed' ?
{ completed: true } : null;
});
$scope.todoForms = {
one: 'item left',
other: 'items left'
};
function retrieveStore() {
var store = localStorage.getItem('todos-angularjs');
return (store && JSON.parse(store)) || [];
};
function updateStore() {
var isEditing = $scope.todos.filter(function(val) {
return val.editing;
}).length;
if (!isEditing) {
localStorage.setItem('todos-angularjs', JSON.stringify($scope.todos));
}
};
$scope.addTodo = function() {
if (this.newTodo.trim().length === 0) {
return;
}
$scope.todos.push({
title: this.newTodo,
completed: false,
editing: false
});
this.newTodo = '';
};
$scope.editTodo = function(todo) {
//cancel any active editing operation
$scope.todos.forEach(function(val) {
val.editing = false;
});
todo.editing = true;
};
$scope.finishEditing = function(todo) {
if (todo.title.trim().length === 0) {
$scope.removeTodo(todo);
} else {
todo.editing = false;
}
};
$scope.removeTodo = function(todo) {
for (var i = 0, len = $scope.todos.length; i < len; ++i) {
if (todo === $scope.todos[i]) {
$scope.todos.splice(i, 1);
}
}
};
$scope.remainingTodos = function() {
return $scope.todos.filter(function(val) {
return !val.completed;
});
};
$scope.completedTodos = function() {
return $scope.todos.filter(function(val) {
return val.completed;
});
}
$scope.clearDoneTodos = function() {
$scope.todos = $scope.remainingTodos();
};
$scope.markAllDone = function() {
var markDone = true;
if (!$scope.remainingTodos().length) {
markDone = false;
}
$scope.todos.forEach(function(todo) {
todo.completed = markDone;
});
};
};
todomvc.directive('todoBlur', function() {
return function( scope, elem, attrs ) {
elem.bind('blur', function() {
scope.$apply( attrs.todoBlur );
});
};
});
todomvc.directive('todoFocus', function( $timeout ) {
return function( scope, elem, attrs ) {
scope.$watch( attrs.todoFocus, function( newval ) {
if ( newval ) {
$timeout(function() {
elem[0].focus();
elem[0].select();
}, 0 );
}
});
};
});
This source diff could not be displayed because it is too large. You can view the blob instead.
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, font, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td {
margin: 0;
padding: 0;
border: 0;
outline: 0;
font-weight: inherit;
font-style: inherit;
font-size: 100%;
font-family: inherit;
vertical-align: baseline;
}
body {
line-height: 1;
color: black;
background: white;
}
ol, ul {
list-style: none;
}
a img {
border: none;
}
html {
background: #eeeeee;
}
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px;
line-height: 1.4em;
background: #eeeeee;
color: #333333;
}
.clickable{
cursor:pointer;
}
#todoapp {
background: none repeat scroll 0 0 white;
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.2);
background: none repeat scroll 0 0 white;
margin: 0 auto 40px;
padding: 20px;
width: 480px;
}
#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;
}
#todoapp h1 {
font-size: 36px;
font-weight: bold;
text-align: center;
padding: 20px 0 30px 0;
line-height: 1;
}
#create-todo {
position: relative;
}
#todo-form 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-true {
padding: 0;
border-bottom: 0;
}
#todo-list .editing-true .display,
#todo-list .edit {
display: none;
}
#todo-list .editing-true .edit {
display: block;
}
#todo-list .editing-true 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 .done-true .todo-content {
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(../img/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;
background: none repeat scroll 0 0 #F4FCE8;
border-radius: 0 0 5px 5px;
border-top: 1px solid #EDEDED;
color: #555555;
display: block;
line-height: 36px;
margin: 20px -20px -20px;
overflow: hidden;
padding: 0 20px;
}
#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;
}
#todoapp #todo-stats {
background: none repeat scroll 0 0 #F4FCE8;
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
border-top: 1px solid #EDEDED;
color: #555555;
line-height: 36px;
margin-top: 10px;
padding: 0 20px;
}
#todoapp #todo-stats .todo-clear a {
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;
}
/* line 136 */
#todoapp #todo-stats .todo-clear a:hover, #todoapp #todo-stats .todo-clear a: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;
}
#todo-stats .todo-clear a {
color: #777777;
font-size: 12px;
}
#todo-stats .todo-clear a:visited {
color: #777777;
}
#todo-stats .todo-clear a:hover, #todo-stats .todo-clear a:focus {
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;
}
/*
* Fran�ois 'cahnory' Germain
*/
.ui-tooltip, .ui-tooltip-top, .ui-tooltip-right, .ui-tooltip-bottom, .ui-tooltip-left {
color:#ffffff;
cursor:normal;
display:-moz-inline-stack;
display:inline-block;
font-size:12px;
font-family:arial;
padding:.5em 1em;
position:relative;
text-align:center;
text-shadow:0 -1px 1px #111111;
-webkit-border-top-left-radius:4px ;
-webkit-border-top-right-radius:4px ;
-webkit-border-bottom-right-radius:4px ;
-webkit-border-bottom-left-radius:4px ;
-khtml-border-top-left-radius:4px ;
-khtml-border-top-right-radius:4px ;
-khtml-border-bottom-right-radius:4px ;
-khtml-border-bottom-left-radius:4px ;
-moz-border-radius-topleft:4px ;
-moz-border-radius-topright:4px ;
-moz-border-radius-bottomright:4px ;
-moz-border-radius-bottomleft:4px ;
border-top-left-radius:4px ;
border-top-right-radius:4px ;
border-bottom-right-radius:4px ;
border-bottom-left-radius:4px ;
-o-box-shadow:0 1px 2px #000000, inset 0 0 0 1px #222222, inset 0 2px #666666, inset 0 -2px 2px #444444;
-moz-box-shadow:0 1px 2px #000000, inset 0 0 0 1px #222222, inset 0 2px #666666, inset 0 -2px 2px #444444;
-khtml-box-shadow:0 1px 2px #000000, inset 0 0 0 1px #222222, inset 0 2px #666666, inset 0 -2px 2px #444444;
-webkit-box-shadow:0 1px 2px #000000, inset 0 0 0 1px #222222, inset 0 2px #666666, inset 0 -2px 2px #444444;
box-shadow:0 1px 2px #000000, inset 0 0 0 1px #222222, inset 0 2px #666666, inset 0 -2px 2px #444444;
background-color:#3b3b3b;
background-image:-moz-linear-gradient(top,#555555,#222222);
background-image:-webkit-gradient(linear,left top,left bottom,color-stop(0,#555555),color-stop(1,#222222));
filter:progid:DXImageTransform.Microsoft.gradient(startColorStr=#555555,EndColorStr=#222222);
-ms-filter:progid:DXImageTransform.Microsoft.gradient(startColorStr=#555555,EndColorStr=#222222);
}
.ui-tooltip:after, .ui-tooltip-top:after, .ui-tooltip-right:after, .ui-tooltip-bottom:after, .ui-tooltip-left:after {
content:"\25B8";
display:block;
font-size:2em;
height:0;
line-height:0;
position:absolute;
}
.ui-tooltip:after, .ui-tooltip-bottom:after {
color:#2a2a2a;
bottom:0;
left:1px;
text-align:center;
text-shadow:1px 0 2px #000000;
-o-transform:rotate(90deg);
-moz-transform:rotate(90deg);
-khtml-transform:rotate(90deg);
-webkit-transform:rotate(90deg);
width:100%;
}
.ui-tooltip-top:after {
bottom:auto;
color:#4f4f4f;
left:-2px;
top:0;
text-align:center;
text-shadow:none;
-o-transform:rotate(-90deg);
-moz-transform:rotate(-90deg);
-khtml-transform:rotate(-90deg);
-webkit-transform:rotate(-90deg);
width:100%;
}
.ui-tooltip-right:after {
color:#222222;
right:-0.375em;
top:50%;
margin-top:-.05em;
text-shadow:0 1px 2px #000000;
-o-transform:rotate(0);
-moz-transform:rotate(0);
-khtml-transform:rotate(0);
-webkit-transform:rotate(0);
}
.ui-tooltip-left:after {
color:#222222;
left:-0.375em;
top:50%;
margin-top:.1em;
text-shadow:0 -1px 2px #000000;
-o-transform:rotate(180deg);
-moz-transform:rotate(180deg);
-khtml-transform:rotate(180deg);
-webkit-transform:rotate(180deg);
}
<!doctype html> <!doctype html>
<html xmlns:ng="http://angularjs.org/" xmlns:my="http://rx.org"> <html lang="en" ng-app="todomvc">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>AngularJS with PersistenceJS Storage Todo App</title> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<link rel="stylesheet" href="css/app.css"> <title>AngularJS - TodoMVC</title>
<link rel="stylesheet" href="../../assets/base.css">
<!--[if IE]> <!--[if IE]>
<script src="../../assets/ie.js"></script> <script src="../../assets/ie.js"></script>
<![endif]--> <![endif]-->
</head> </head>
<body> <body>
<div ng:controller="App.Controllers.TodoController" id="todoapp"> <section id="todoapp" ng-controller="TodoController">
<div class="title"> <header id="header">
<h1> <h1>todos</h1>
Todos <form id="todo-form" ng-submit="addTodo()">
</h1> <input id="new-todo" placeholder="What needs to be done?" type="text" name="newTodo" ng-model="newTodo">
</div>
<div class="content">
<div id="todo-form">
</div>
<form id="todo-form" ng:submit="addTodo()">
<input id="new-todo" name="newTodo" my:blur="addTodo()" placeholder="What needs to be done?" type="text">
<span class="ui-tooltip-top" ng:show="showHitEnterHint">
Press Enter to save this task
</span>
</form> </form>
<div id="todos"> </header>
<section id="main" ng-show="todos.length">
<input id="toggle-all" type="checkbox" ng-model="allChecked" ng-change="markAllDone()" ng-checked="remainingTodos().length == 0">
<label for="toggle-all">Mark all as complete</label>
<ul id="todo-list"> <ul id="todo-list">
<li class="todo" ng:class="'editing-' + todo.editing + ' done-' + todo.done" ng:repeat="todo in todos"> <li ng-repeat="todo in todos | filter:statusFilter" ng-dblclick="editTodo(todo)" ng-class="{done: todo.completed, editing: todo.editing}">
<div class="display"> <div class="view">
<input ng:change="changeStatus(todo)" class="check" type="checkbox" name="todo.done" / > <input class="toggle" type="checkbox" name="todo.completed" ng-model="todo.completed" ng-change="toggleDone(todo)">
<div ng:click="editTodo(todo)" class="todo-content"> {{ todo.content }} </div> <label ng-bind="todo.title">Loading...</label>
<span class="todo-destroy" ng:click="removeTodo(todo)"></span> <button class="destroy" ng-click="removeTodo(todo)"></button>
</div> </div>
<div class="edit"> <form ng-submit="finishEditing(todo)">
<form ng:submit="finishEditing(todo)"> <input class="edit" type="text" name="todo.title" ng-model="todo.title" todo-blur="finishEditing(todo)" todo-focus="todo.editing">
<input class="todo-input" my:focus="todo.editing" my:blur="finishEditing(todo)" name="todo.content" type="text">
</form> </form>
</div>
</li> </li>
</ul> </ul>
</div> </section>
<div id="todo-stats"> <footer id="footer" ng-show="todos.length">
<span class="todo-count" ng:show="hasTodos()"> <span id="todo-count"><strong>{{remainingTodos().length}}</strong> <ng-pluralize count="remainingTodos().length" when="todoForms"></ng-pluralize></span>
<ng:pluralize count="remainingTodos()" when="{'0' : 'No items left.', '1': '1 item left.', 'other' : '{} items left.' }"> <ul id="filters">
</ng:pluralize> <li>
</span> <a ng-class="{selected: location.path() == '/'} " href="#/">All</a>
<span class="todo-clear" ng:show="hasFinishedTodos()"> </li>
<a ng:click="clearCompletedItems()"> <li>
Clear <ng:pluralize count="finishedTodos()" when="{'1': '1 completed item', 'other' : '{} completed items' }"> <a ng-class="{selected: location.path() == '/active'}" href="#/active">Active</a>
</ng:pluralize> </li>
</a> <li>
</span> <a ng-class="{selected: location.path() == '/completed'}" href="#/completed">Completed</a>
</div> </li>
</div>
</div>
<ul id="instructions">
<li>Click to edit a todo.</li>
</ul> </ul>
<div id="credits"> <button id="clear-completed" ng-click="clearDoneTodos()" ng-show="doneTodos().length" ng-bind-template="Clear completed ({{doneTodos().length}})"></button>
<p> </footer>
Originally Created by </section>
<br> <footer id="info">
<a href="http://jgn.me/">Jérôme Gravel-Niquet</a> <p>Double-click to edit a todo.</p>
</p> <p>Created by <a href="http://twitter.com/cburgdorf">Christoph Burgdorf</a>.</p>
<p> <p>Extended to use PersistenceJS for local storage by <a href="http://jacobmumm.com">Jacob Mumm</a>.</p>
Rewritten to use <a href="http://angularjs.org">AngularJS </a> by </footer>
<br>
<a href="http://cburgdorf.wordpress.com/">Christoph Burgdorf</a>
<br>Cleanup, edits: <a href="http://www.linkedin.com/pub/dan-doyon/2/1b0/a83">Dan Doyon</a>
</p>
<p>
Extended for persistent WebSQL storage by <br/>
<a href="http://jacobmumm.com">Jacob Mumm</a><br/>
Using <a href="http://persistencejs.org">PersistenceJS</a>
</p>
</div>
<script src="../../assets/base.js"></script> <script src="../../assets/base.js"></script>
<script src="js/booter.js"></script> <script src="js/libs/angular/angular.min.js"></script>
<script src="lib/angular/angular.min.js" ng:autobind></script> <script src="js/libs/persistencejs/persistence.js"></script>
<script src="lib/rx/rx.js"></script> <script src="js/libs/persistencejs/persistence.store.sql.js"></script>
<script src="lib/rx/rx.angular.js"></script> <script src="js/libs/persistencejs/persistence.store.websql.js"></script>
<script src="lib/persistence/persistence.js"></script> <script src="js/controllers/todo.js"></script>
<script src="lib/persistence/persistence.store.sql.js"></script> <script src="js/modules/persistence-service.js"></script>
<script src="lib/persistence/persistence.store.websql.js"></script> <script src="js/directives/todo-directives.js"></script>
<script src="js/controllers.js"></script>
<script src="js/directive.js"></script>
<script src="js/services.js"></script>
</body> </body>
</html> </html>
/* App Controllers */
App.Controllers.TodoController = function (persistencejs) {
var self = this;
self.newTodo = "";
self.editTodoStartContent = "";
self.addTodo = function() {
if (self.newTodo.length === 0) return;
self.todos.push({
content: self.newTodo,
done: false,
editing: false
});
persistencejs.add(self.newTodo);
self.newTodo = "";
};
self.editTodo = function(todo) {
angular.forEach(self.todos, function(value) {
value.editing = false;
});
todo.editing = true;
self.editTodoStartContent = todo.content;
};
self.changeStatus = function(todo){
persistencejs.changeStatus(todo);
};
self.finishEditing = function(todo) {
todo.editing = false;
persistencejs.edit(self.editTodoStartContent, todo.content);
};
self.removeTodo = function(todo) {
angular.Array.remove(self.todos, todo);
persistencejs.remove(todo);
};
self.todos = [];
var countTodos = function(done) {
return function() {
return angular.Array.count(self.todos, function(x) {
return x.done === (done === "done");
});
}
};
self.remainingTodos = countTodos("undone");
self.finishedTodos = countTodos("done");
self.clearCompletedItems = function() {
var oldTodos = self.todos;
self.todos = [];
angular.forEach(oldTodos, function(todo) {
if (!todo.done) self.todos.push(todo);
});
persistencejs.clearCompletedItems();
};
self.hasFinishedTodos = function() {
return self.finishedTodos() > 0;
};
self.hasTodos = function() {
return self.todos.length > 0;
};
self.loadTodos = function(){
persistencejs.fetchAll(self);
}
self.refresh = function(){ self.$apply(); }
self.loadTodos();
/*
The following code deals with hiding the hint *while* you are typing,
showing it once you did *finish* typing (aka 500 ms since you hit the last key)
*in case* the result is a non empty string
*/
Rx.Observable.FromAngularScope(self, "newTodo")
.Do(function() {
self.showHitEnterHint = false;
})
.Throttle(500)
.Select(function(x) {
return x.length > 0;
})
.ToOutputProperty(self, "showHitEnterHint");
};
App.Controllers.TodoController.$inject = ['persistencejs'];
\ No newline at end of file
/* App Controllers */
var todomvc = angular.module('todomvc', []);
function TodoController($scope, $location, persistencejs) {
$scope.todos = []; loadTodos();
$scope.newTodo = "";
$scope.editTodoStartContent = "";
if($location.path()=='') $location.path('/');
$scope.location = $location;
$scope.$watch(function() {return $location.path(); }, function(path) {
$scope.statusFilter = path == '/active' ?
{ completed: false } : path == '/completed' ?
{ completed: true } : null;
});
function loadTodos() {
persistencejs.fetchAll($scope);
};
$scope.refresh = function(){ $scope.$apply(); }
$scope.todoForms = {
one: 'item left',
other: 'items left'
};
$scope.addTodo = function() {
if (this.newTodo.trim().length === 0) {
return;
}
$scope.todos.push({
title: this.newTodo,
completed: false,
editing: false
});
persistencejs.add(this.newTodo);
this.newTodo = '';
};
$scope.editTodo = function(todo) {
$scope.todos.forEach(function(val) {
val.editing = false;
});
todo.editing = true;
$scope.editTodoStartContent = todo.title;
};
$scope.finishEditing = function(todo) {
if (todo.title.trim().length === 0) {
$scope.removeTodo(todo);
persistencejs.remove(todo);
} else {
todo.editing = false;
persistencejs.edit($scope.editTodoStartContent, todo.title);
}
};
$scope.removeTodo = function(todo) {
for (var i = 0, len = $scope.todos.length; i < len; ++i) {
if (todo === $scope.todos[i]) {
$scope.todos.splice(i, 1);
}
}
persistencejs.remove(todo);
};
$scope.remainingTodos = function() {
return $scope.todos.filter(function(val) {
return !val.completed;
});
};
$scope.completedTodos = function() {
return $scope.todos.filter(function(val) {
return val.completed;
});
}
$scope.clearDoneTodos = function() {
$scope.todos = $scope.remainingTodos();
persistencejs.clearCompletedItems();
};
$scope.toggleDone = function(todo){
persistencejs.changeStatus(todo);
}
$scope.markAllDone = function() {
var markDone = true;
if (!$scope.remainingTodos().length) {
markDone = false;
}
$scope.todos.forEach(function(todo) {
if(todo.completed !== markDone){
persistencejs.changeStatus(todo);
}
todo.completed = markDone;
});
};
};
angular.directive('my:blur', function(expression, compiledElement) {
var compiler = this;
return function(linkElement) {
var scope = this;
linkElement.bind('blur', function(event) {
scope.$apply(expression, linkElement);
event.stopPropagation();
});
};
});
angular.directive("my:focus", function(expression, compiledElement){
return function(element){
this.$watch(expression, function(){
if(angular.formatter.boolean.parse(expression)){
element[0].focus();
}
}, element);
};
});
todomvc.directive('todoBlur', function() {
return function( scope, elem, attrs ) {
elem.bind('blur', function() {
scope.$apply( attrs.todoBlur );
});
};
});
todomvc.directive('todoFocus', function( $timeout ) {
return function( scope, elem, attrs ) {
scope.$watch( attrs.todoFocus, function( newval ) {
if ( newval ) {
$timeout(function() {
elem[0].focus();
elem[0].select();
}, 0 );
}
});
};
});
...@@ -459,7 +459,7 @@ persistence.get = function(arg1, arg2) { ...@@ -459,7 +459,7 @@ persistence.get = function(arg1, arg2) {
that._data_obj[ref] = session.trackedObjects[that._data[ref]]; that._data_obj[ref] = session.trackedObjects[that._data[ref]];
return that._data_obj[ref]; return that._data_obj[ref];
} else { } else {
throw new Error("Property '" + ref + "' with id: " + that._data[ref] + " not fetched, either prefetch it or fetch it manually."); throw new Error("Property '" + ref + "' of '" + meta.name + "' with id: " + that._data[ref] + " not fetched, either prefetch it or fetch it manually.");
} }
}); });
}()); }());
...@@ -1016,7 +1016,12 @@ persistence.get = function(arg1, arg2) { ...@@ -1016,7 +1016,12 @@ persistence.get = function(arg1, arg2) {
switch(type) { switch(type) {
case 'DATE': case 'DATE':
if(typeof value === 'number') { if(typeof value === 'number') {
if (value > 1000000000000) {
// it's in milliseconds
return new Date(value);
} else {
return new Date(value * 1000); return new Date(value * 1000);
}
} else { } else {
return null; return null;
} }
...@@ -1281,8 +1286,10 @@ persistence.get = function(arg1, arg2) { ...@@ -1281,8 +1286,10 @@ persistence.get = function(arg1, arg2) {
var el = ar[i]; var el = ar[i];
if(el.equals && el.equals(item)) { if(el.equals && el.equals(item)) {
ar.splice(i, 1); ar.splice(i, 1);
return;
} else if(el === item) { } else if(el === item) {
ar.splice(i, 1); ar.splice(i, 1);
return;
} }
} }
} }
...@@ -1627,7 +1634,7 @@ persistence.get = function(arg1, arg2) { ...@@ -1627,7 +1634,7 @@ persistence.get = function(arg1, arg2) {
s += '|Order:'; s += '|Order:';
for(var i = 0; i < this._orderColumns.length; i++) { for(var i = 0; i < this._orderColumns.length; i++) {
var col = this._orderColumns[i]; var col = this._orderColumns[i];
s += col[0] + ", " + col[1]; s += col[0] + ", " + col[1] + ", " + col[2];
} }
s += '|Prefetch:'; s += '|Prefetch:';
for(var i = 0; i < this._prefetchFields.length; i++) { for(var i = 0; i < this._prefetchFields.length; i++) {
...@@ -1714,12 +1721,16 @@ persistence.get = function(arg1, arg2) { ...@@ -1714,12 +1721,16 @@ persistence.get = function(arg1, arg2) {
* Returns a new query collection with an ordering imposed on the collection * Returns a new query collection with an ordering imposed on the collection
* @param property the property to sort on * @param property the property to sort on
* @param ascending should the order be ascending (= true) or descending (= false) * @param ascending should the order be ascending (= true) or descending (= false)
* @param caseSensitive should the order be case sensitive (= true) or case insensitive (= false)
* note: using case insensitive ordering for anything other than TEXT fields yields
* undefinded behavior
* @return the query collection with imposed ordering * @return the query collection with imposed ordering
*/ */
QueryCollection.prototype.order = function (property, ascending) { QueryCollection.prototype.order = function (property, ascending, caseSensitive) {
ascending = ascending === undefined ? true : ascending; ascending = ascending === undefined ? true : ascending;
caseSensitive = caseSensitive === undefined ? true : caseSensitive;
var c = this.clone(); var c = this.clone();
c._orderColumns.push( [ property, ascending ]); c._orderColumns.push( [ property, ascending, caseSensitive ]);
return this._session.uniqueQueryCollection(c); return this._session.uniqueQueryCollection(c);
}; };
...@@ -2055,8 +2066,13 @@ persistence.get = function(arg1, arg2) { ...@@ -2055,8 +2066,13 @@ persistence.get = function(arg1, arg2) {
for(var i = 0; i < that._orderColumns.length; i++) { for(var i = 0; i < that._orderColumns.length; i++) {
var col = that._orderColumns[i][0]; var col = that._orderColumns[i][0];
var asc = that._orderColumns[i][1]; var asc = that._orderColumns[i][1];
var sens = that._orderColumns[i][2];
var aVal = persistence.get(a, col); var aVal = persistence.get(a, col);
var bVal = persistence.get(b, col); var bVal = persistence.get(b, col);
if (!sens) {
aVal = aVal.toLowerCase();
bVal = bVal.toLowerCase();
}
if(aVal < bVal) { if(aVal < bVal) {
return asc ? -1 : 1; return asc ? -1 : 1;
} else if(aVal > bVal) { } else if(aVal > bVal) {
......
...@@ -44,7 +44,12 @@ var defaultTypeMapper = { ...@@ -44,7 +44,12 @@ var defaultTypeMapper = {
switch (type) { switch (type) {
case 'DATE': case 'DATE':
// SQL is in seconds and JS in miliseconds // SQL is in seconds and JS in miliseconds
if (val > 1000000000000) {
// usually in seconds, but sometimes it's milliseconds
return new Date(parseInt(val, 10));
} else {
return new Date(parseInt(val, 10) * 1000); return new Date(parseInt(val, 10) * 1000);
}
case 'BOOL': case 'BOOL':
return val === 1 || val === '1'; return val === 1 || val === '1';
break; break;
...@@ -389,7 +394,7 @@ function config(persistence, dialect) { ...@@ -389,7 +394,7 @@ function config(persistence, dialect) {
} }
} }
executeQueriesSeq(tx, additionalQueries, function() { executeQueriesSeq(tx, additionalQueries, function() {
if (properties.length === 0) { // Nothing changed if (!obj._new && properties.length === 0) { // Nothing changed and not new
if(callback) callback(); if(callback) callback();
return; return;
} }
...@@ -670,7 +675,7 @@ function config(persistence, dialect) { ...@@ -670,7 +675,7 @@ function config(persistence, dialect) {
sql += " ORDER BY " sql += " ORDER BY "
+ this._orderColumns.map( + this._orderColumns.map(
function (c) { function (c) {
return "`" + mainPrefix + c[0] + "` " return (c[2] ? "`" : "LOWER(`") + mainPrefix + c[0] + (c[2] ? "` " : "`) ")
+ (c[1] ? "ASC" : "DESC"); + (c[1] ? "ASC" : "DESC");
}).join(", "); }).join(", ");
} }
......
angular.service('persistencejs', function() { todomvc.factory('persistencejs', function(){
persistence.store.websql.config(persistence, 'todo-angular-persistence', 'todo database', 5*1024*1024); persistence.store.websql.config(persistence, 'todos-angularjs', 'todo database', 5*1024*1024);
var Todo = persistence.define('todo', { var Todo = persistence.define('todo', {
content: 'TEXT', title: 'TEXT',
done: 'BOOL' completed: 'BOOL'
}); });
persistence.schemaSync(); persistence.schemaSync();
return { return {
add: function(item){ add: function(item){
var t = new Todo(); var t = new Todo();
t.content = item; t.title = item;
t.done = false; t.completed = false;
persistence.add(t); persistence.add(t);
persistence.flush(); persistence.flush();
}, },
edit: function(startContent, endContent){ edit: function(oldTitle, newTitle){
Todo.all().filter('content','=',startContent).one(function(item){ Todo.all().filter('title','=',oldTitle).one(function(item){
item.content = endContent; if(item){
item.title = newTitle;
persistence.flush(); persistence.flush();
}
}); });
}, },
changeStatus: function(item){ changeStatus: function(item){
Todo.all().filter('content','=',item.content).one(function(todo){ Todo.all().filter('title','=',item.title).one(function(todo){
todo.done = item.done; todo.completed = item.completed;
persistence.flush(); persistence.flush();
}); });
}, },
clearCompletedItems: function(){ clearCompletedItems: function(){
Todo.all().filter('done','=',true).destroyAll(); Todo.all().filter('completed','=',true).destroyAll();
}, },
remove: function(item){ remove: function(item){
Todo.all().filter('content','=',item.content).destroyAll(); Todo.all().filter('title','=',item.title).destroyAll();
}, },
fetchAll: function(controller){ fetchAll: function(controller){
...@@ -42,8 +44,8 @@ angular.service('persistencejs', function() { ...@@ -42,8 +44,8 @@ angular.service('persistencejs', function() {
var todos = []; var todos = [];
items.forEach(function(item){ items.forEach(function(item){
todos.push({ todos.push({
content: item.content, title: item.title,
done: item.done, completed: item.completed,
editing: false editing: false
}); });
if(--itemCount == 0){ if(--itemCount == 0){
......
(function () {
var global = this,
root = (typeof ProvideCustomRxRootObject == "undefined") ? global.Rx : ProvideCustomRxRootObject();
var observable = root.Observable;
var observableCreate = observable.Create;
observable.FromAngularScope = function (angularScope, propertyName) {
return observableCreate(function (observer) {
var unwatch = angularScope.$watch(function(){
return angularScope[propertyName];
},
function(){
observer.OnNext(angularScope[propertyName]);
});
return function () {
unwatch();
};
})
.Skip(1); //In AngularJS 0.10.x There is no way to avoid initial evaluation. So we take care about it!
};
observable.prototype.ToOutputProperty = function (scope, propertyName) {
var disposable = this.Subscribe(function (data) {
scope[propertyName] = data;
scope.$apply();
});
scope.$on('$destroy', function(event){
//we need to asure that we only dispose the observable when it's our scope that
//was destroyed.
//TODO: Figure out if thats enough to asure the above (e.g what happens when
//a child scope will be destroyed but ours won't be affected. Or the other way around,
//if a higher scope will be destroyed (and therefore ours as well) does it mean that $destroy()
//will be also called on our scope or will our scope get destroyed without actually
//calling $destroy() on it?
if (event.targetScope === scope){
disposable.Dispose();
}
});
};
})();
\ No newline at end of file
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