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">
<title>AngularJS - TodoMVC</title> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<link rel="stylesheet" href="css/base.css"> <title>AngularJS - TodoMVC</title>
<link rel="stylesheet" href="css/app.css"> <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="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>
</footer> </li>
</div> <li>
<div id="instructions"> <a ng-class="{selected: location.path() == '/active'}" href="#/active">Active</a>
Double-click to edit a todo. </li>
</div> <li>
<div id="credits"> <a ng-class="{selected: location.path() == '/completed'}" href="#/completed">Completed</a>
Credits: <a href="http://twitter.com/cburgdorf">Christoph Burgdorf</a>, <a href="http://ericbidelman.com">Eric Bidelman</a> </li>
</div> </ul>
<script src="../../assets/base.js"></script> <button id="clear-completed" ng-click="clearDoneTodos()" ng-show="completedTodos().length" ng-bind-template="Clear completed ({{completedTodos().length}})"></button>
<script src="js/libs/angular/angular.min.js"></script> </footer>
<script src="js/controllers.js"></script> </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>
<script src="../../assets/base.js"></script>
<script src="js/libs/angular/angular.min.js"></script>
<script src="js/controllers/todo.js"></script>
<script src="js/directives/todo-directives.js"></script>
</body> </body>
</html> </html>
\ No newline at end of file
/* 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> </form>
<div class="content"> </header>
<div id="todo-form"> <section id="main" ng-show="todos.length">
</div> <input id="toggle-all" type="checkbox" ng-model="allChecked" ng-change="markAllDone()" ng-checked="remainingTodos().length == 0">
<form id="todo-form" ng:submit="addTodo()"> <label for="toggle-all">Mark all as complete</label>
<input id="new-todo" name="newTodo" my:blur="addTodo()" placeholder="What needs to be done?" type="text"> <ul id="todo-list">
<span class="ui-tooltip-top" ng:show="showHitEnterHint"> <li ng-repeat="todo in todos | filter:statusFilter" ng-dblclick="editTodo(todo)" ng-class="{done: todo.completed, editing: todo.editing}">
Press Enter to save this task <div class="view">
</span> <input class="toggle" type="checkbox" name="todo.completed" ng-model="todo.completed" ng-change="toggleDone(todo)">
</form> <label ng-bind="todo.title">Loading...</label>
<div id="todos"> <button class="destroy" ng-click="removeTodo(todo)"></button>
<ul id="todo-list"> </div>
<li class="todo" ng:class="'editing-' + todo.editing + ' done-' + todo.done" ng:repeat="todo in todos"> <form ng-submit="finishEditing(todo)">
<div class="display"> <input class="edit" type="text" name="todo.title" ng-model="todo.title" todo-blur="finishEditing(todo)" todo-focus="todo.editing">
<input ng:change="changeStatus(todo)" class="check" type="checkbox" name="todo.done" / > </form>
<div ng:click="editTodo(todo)" class="todo-content"> {{ todo.content }} </div> </li>
<span class="todo-destroy" ng:click="removeTodo(todo)"></span> </ul>
</div> </section>
<div class="edit"> <footer id="footer" ng-show="todos.length">
<form ng:submit="finishEditing(todo)"> <span id="todo-count"><strong>{{remainingTodos().length}}</strong> <ng-pluralize count="remainingTodos().length" when="todoForms"></ng-pluralize></span>
<input class="todo-input" my:focus="todo.editing" my:blur="finishEditing(todo)" name="todo.content" type="text"> <ul id="filters">
</form> <li>
</div> <a ng-class="{selected: location.path() == '/'} " href="#/">All</a>
</li> </li>
</ul> <li>
</div> <a ng-class="{selected: location.path() == '/active'}" href="#/active">Active</a>
<div id="todo-stats"> </li>
<span class="todo-count" ng:show="hasTodos()"> <li>
<ng:pluralize count="remainingTodos()" when="{'0' : 'No items left.', '1': '1 item left.', 'other' : '{} items left.' }"> <a ng-class="{selected: location.path() == '/completed'}" href="#/completed">Completed</a>
</ng:pluralize> </li>
</span> </ul>
<span class="todo-clear" ng:show="hasFinishedTodos()"> <button id="clear-completed" ng-click="clearDoneTodos()" ng-show="doneTodos().length" ng-bind-template="Clear completed ({{doneTodos().length}})"></button>
<a ng:click="clearCompletedItems()"> </footer>
Clear <ng:pluralize count="finishedTodos()" when="{'1': '1 completed item', 'other' : '{} completed items' }"> </section>
</ng:pluralize> <footer id="info">
</a> <p>Double-click to edit a todo.</p>
</span> <p>Created by <a href="http://twitter.com/cburgdorf">Christoph Burgdorf</a>.</p>
</div> <p>Extended to use PersistenceJS for local storage by <a href="http://jacobmumm.com">Jacob Mumm</a>.</p>
</div> </footer>
</div>
<ul id="instructions">
<li>Click to edit a todo.</li>
</ul>
<div id="credits">
<p>
Originally Created by
<br>
<a href="http://jgn.me/">Jérôme Gravel-Niquet</a>
</p>
<p>
Rewritten to use <a href="http://angularjs.org">AngularJS </a> by
<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>
\ No newline at end of file
/* 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') {
return new Date(value * 1000); if (value > 1000000000000) {
// it's in milliseconds
return new Date(value);
} else {
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) {
......
...@@ -6,7 +6,7 @@ var defaultTypeMapper = { ...@@ -6,7 +6,7 @@ var defaultTypeMapper = {
* SQL type for ids * SQL type for ids
*/ */
idType: "VARCHAR(32)", idType: "VARCHAR(32)",
/** /**
* SQL type for class names (used by mixins) * SQL type for class names (used by mixins)
*/ */
...@@ -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
return new Date(parseInt(val, 10) * 1000); 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);
}
case 'BOOL': case 'BOOL':
return val === 1 || val === '1'; return val === 1 || val === '1';
break; break;
...@@ -124,7 +129,7 @@ function config(persistence, dialect) { ...@@ -124,7 +129,7 @@ function config(persistence, dialect) {
/** /**
* Synchronize the data model with the database, creates table that had not * Synchronize the data model with the database, creates table that had not
* been defined before * been defined before
* *
* @param tx * @param tx
* transaction object to use (optional) * transaction object to use (optional)
* @param callback * @param callback
...@@ -147,7 +152,7 @@ function config(persistence, dialect) { ...@@ -147,7 +152,7 @@ function config(persistence, dialect) {
return; return;
} }
var queries = [], meta, colDefs, otherMeta, tableName; var queries = [], meta, colDefs, otherMeta, tableName;
var tm = persistence.typeMapper; var tm = persistence.typeMapper;
var entityMeta = persistence.getEntityMeta(); var entityMeta = persistence.getEntityMeta();
for (var entityName in entityMeta) { for (var entityName in entityMeta) {
...@@ -178,10 +183,10 @@ function config(persistence, dialect) { ...@@ -178,10 +183,10 @@ function config(persistence, dialect) {
var otherMeta = meta.hasMany[rel].type.meta; var otherMeta = meta.hasMany[rel].type.meta;
var inv = meta.hasMany[rel].inverseProperty; var inv = meta.hasMany[rel].inverseProperty;
// following test ensures that mixin mtm tables get created with the mixin itself // following test ensures that mixin mtm tables get created with the mixin itself
// it seems superfluous because mixin will be processed before entitites that use it // it seems superfluous because mixin will be processed before entitites that use it
// but better be safe than sorry. // but better be safe than sorry.
if (otherMeta.hasMany[inv].type.meta != meta) if (otherMeta.hasMany[inv].type.meta != meta)
continue; continue;
var p1 = meta.name + "_" + rel; var p1 = meta.name + "_" + rel;
var p2 = otherMeta.name + "_" + inv; var p2 = otherMeta.name + "_" + inv;
queries.push([dialect.createIndex(tableName, [p1]), null]); queries.push([dialect.createIndex(tableName, [p1]), null]);
...@@ -219,7 +224,7 @@ function config(persistence, dialect) { ...@@ -219,7 +224,7 @@ function config(persistence, dialect) {
/** /**
* Persists all changes to the database transaction * Persists all changes to the database transaction
* *
* @param tx * @param tx
* transaction to use * transaction to use
* @param callback * @param callback
...@@ -276,7 +281,7 @@ function config(persistence, dialect) { ...@@ -276,7 +281,7 @@ function config(persistence, dialect) {
} }
}); });
}; };
/** /**
* Remove all tables in the database (as defined by the model) * Remove all tables in the database (as defined by the model)
*/ */
...@@ -316,7 +321,7 @@ function config(persistence, dialect) { ...@@ -316,7 +321,7 @@ function config(persistence, dialect) {
} else { } else {
cb(); cb();
} }
function cb(result, err) { function cb(result, err) {
session.clean(); session.clean();
persistence.generatedTables = {}; persistence.generatedTables = {};
...@@ -372,7 +377,7 @@ function config(persistence, dialect) { ...@@ -372,7 +377,7 @@ function config(persistence, dialect) {
obj._dirtyProperties[p] = true; obj._dirtyProperties[p] = true;
} }
} }
} }
for ( var p in obj._dirtyProperties) { for ( var p in obj._dirtyProperties) {
if (obj._dirtyProperties.hasOwnProperty(p)) { if (obj._dirtyProperties.hasOwnProperty(p)) {
properties.push("`" + p + "`"); properties.push("`" + p + "`");
...@@ -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;
} }
...@@ -455,7 +460,7 @@ function config(persistence, dialect) { ...@@ -455,7 +460,7 @@ function config(persistence, dialect) {
/////////////////////////// QueryCollection patches to work in SQL environment /////////////////////////// QueryCollection patches to work in SQL environment
/** /**
* Function called when session is flushed, returns list of SQL queries to execute * Function called when session is flushed, returns list of SQL queries to execute
* (as [query, arg] tuples) * (as [query, arg] tuples)
*/ */
persistence.QueryCollection.prototype.persistQueries = function() { return []; }; persistence.QueryCollection.prototype.persistQueries = function() { return []; };
...@@ -568,7 +573,7 @@ function config(persistence, dialect) { ...@@ -568,7 +573,7 @@ function config(persistence, dialect) {
/** /**
* Asynchronous call to actually fetch the items in the collection * Asynchronous call to actually fetch the items in the collection
* @param tx transaction to use * @param tx transaction to use
* @param callback function to be called taking an array with * @param callback function to be called taking an array with
* result objects as argument * result objects as argument
*/ */
persistence.DbQueryCollection.prototype.list = function (tx, callback) { persistence.DbQueryCollection.prototype.list = function (tx, callback) {
...@@ -590,7 +595,7 @@ function config(persistence, dialect) { ...@@ -590,7 +595,7 @@ function config(persistence, dialect) {
var entityName = this._entityName; var entityName = this._entityName;
var meta = persistence.getMeta(entityName); var meta = persistence.getMeta(entityName);
var tm = persistence.typeMapper; var tm = persistence.typeMapper;
// handles mixin case -- this logic is generic and could be in persistence. // handles mixin case -- this logic is generic and could be in persistence.
if (meta.isMixin) { if (meta.isMixin) {
var result = []; var result = [];
...@@ -609,7 +614,7 @@ function config(persistence, dialect) { ...@@ -609,7 +614,7 @@ function config(persistence, dialect) {
query.list(null, callback); query.list(null, callback);
}); });
return; return;
} }
function selectAll (meta, tableAlias, prefix) { function selectAll (meta, tableAlias, prefix) {
var selectFields = [ tm.inIdVar("`" + tableAlias + "`.id") + " AS " + prefix + "id" ]; var selectFields = [ tm.inIdVar("`" + tableAlias + "`.id") + " AS " + prefix + "id" ];
...@@ -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(", ");
} }
...@@ -705,7 +710,7 @@ function config(persistence, dialect) { ...@@ -705,7 +710,7 @@ function config(persistence, dialect) {
}; };
/** /**
* Asynchronous call to remove all the items in the collection. * Asynchronous call to remove all the items in the collection.
* Note: does not only remove the items from the collection, but * Note: does not only remove the items from the collection, but
* the items themselves. * the items themselves.
* @param tx transaction to use * @param tx transaction to use
...@@ -726,7 +731,7 @@ function config(persistence, dialect) { ...@@ -726,7 +731,7 @@ function config(persistence, dialect) {
that.destroyAll(tx, callback); that.destroyAll(tx, callback);
}); });
return; return;
} }
var entityName = this._entityName; var entityName = this._entityName;
var meta = persistence.getMeta(entityName); var meta = persistence.getMeta(entityName);
var tm = persistence.typeMapper; var tm = persistence.typeMapper;
...@@ -739,7 +744,7 @@ function config(persistence, dialect) { ...@@ -739,7 +744,7 @@ function config(persistence, dialect) {
query.destroyAll(tx, callback); query.destroyAll(tx, callback);
}, callback); }, callback);
return; return;
} }
var joinSql = ''; var joinSql = '';
var additionalWhereSqls = this._additionalWhereSqls.slice(0); var additionalWhereSqls = this._additionalWhereSqls.slice(0);
...@@ -789,13 +794,13 @@ function config(persistence, dialect) { ...@@ -789,13 +794,13 @@ function config(persistence, dialect) {
if(tx && !tx.executeSql) { // provided callback as first argument if(tx && !tx.executeSql) { // provided callback as first argument
callback = tx; callback = tx;
tx = null; tx = null;
} }
if(!tx) { // no transaction supplied if(!tx) { // no transaction supplied
session.transaction(function(tx) { session.transaction(function(tx) {
that.count(tx, callback); that.count(tx, callback);
}); });
return; return;
} }
var entityName = this._entityName; var entityName = this._entityName;
var meta = persistence.getMeta(entityName); var meta = persistence.getMeta(entityName);
var tm = persistence.typeMapper; var tm = persistence.typeMapper;
...@@ -814,7 +819,7 @@ function config(persistence, dialect) { ...@@ -814,7 +819,7 @@ function config(persistence, dialect) {
callback(result); callback(result);
}); });
return; return;
} }
var joinSql = ''; var joinSql = '';
var additionalWhereSqls = this._additionalWhereSqls.slice(0); var additionalWhereSqls = this._additionalWhereSqls.slice(0);
...@@ -862,14 +867,14 @@ function config(persistence, dialect) { ...@@ -862,14 +867,14 @@ function config(persistence, dialect) {
vars.push("?"); vars.push("?");
args.push(inverseMeta.name); args.push(inverseMeta.name);
} }
queries.push(["INSERT INTO " + rel.tableName + queries.push(["INSERT INTO " + rel.tableName +
" (`" + columns.join("`, `") + "`) VALUES (" + vars.join(",") + ")", args]); " (`" + columns.join("`, `") + "`) VALUES (" + vars.join(",") + ")", args]);
} }
this._localAdded = []; this._localAdded = [];
// Removed // Removed
for(var i = 0; i < this._localRemoved.length; i++) { for(var i = 0; i < this._localRemoved.length; i++) {
queries.push(["DELETE FROM " + rel.tableName + queries.push(["DELETE FROM " + rel.tableName +
" WHERE `" + direct + "_" + this._coll + "` = " + tm.outIdVar("?") + " AND `" + " WHERE `" + direct + "_" + this._coll + "` = " + tm.outIdVar("?") + " AND `" +
inverse + '_' + rel.inverseProperty + inverse + '_' + rel.inverseProperty +
"` = " + tm.outIdVar("?"), [tm.entityIdToDbId(this._obj.id), tm.entityIdToDbId(this._localRemoved[i].id)]]); "` = " + tm.outIdVar("?"), [tm.entityIdToDbId(this._obj.id), tm.entityIdToDbId(this._localRemoved[i].id)]]);
} }
......
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){
persistence.flush(); item.title = newTitle;
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