Skip to content
This repository
Browse code

first commit

  • Loading branch information...
commit 652036b256c5f015b13ad44cde0f6bd04b8c03f7 0 parents
Evan You authored
23  css/style.css
... ...
@@ -0,0 +1,23 @@
  1
+body{width:100%;padding:0;margin:0;background-color:#000;color:#fff;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;}
  2
+ul{margin:0;padding:0;list-style-type:none;}
  3
+a{color:#fff;text-decoration:none;}
  4
+#wrapper{overflow-x:hidden;overflow-y:auto;}
  5
+#check,#cross{display:block;width:55px;height:60px;position:absolute;-webkit-transition:opacity 0 cubic-bezier(0, 0, 0.2, 1),-webkit-transform 0 cubic-bezier(0, 0, 0.2, 1);-webkit-perspective:0;-webkit-backface-visibility:hidden;display:none;}
  6
+#check{left:0;}
  7
+#cross{right:0;}
  8
+#home,#listview{position:absolute;width:100%;height:100%;top:0;left:0;-webkit-transition:all .15s ease;-webkit-perspective:0;-webkit-backface-visibility:hidden;}
  9
+#home{z-index:2;}
  10
+#listview{z-index:1;}
  11
+.drag{z-index:999 !important;-webkit-transition:all 0 cubic-bezier(0, 0, 0.2, 1) !important;}
  12
+.shadow{box-shadow:0 1px 6px rgba(0, 0, 0, 0.25);}
  13
+.slow{-webkit-transition:all 0.35s ease-in-out !important;}
  14
+.medium{-webkit-transition:all 0.25s ease-in-out !important;}
  15
+.fast{-webkit-transition:all .15s ease !important;}
  16
+.list,.todo{height:60px;-webkit-user-select:none;-webkit-transition:all .1s ease;-webkit-perspective:0;-webkit-backface-visibility:hidden;position:relative;}.list .inner,.todo .inner{height:58px;padding:0 10px;font-size:18px;font-weight:bold;line-height:58px;border-top:1px solid rgba(255, 255, 255, 0.1);border-bottom:1px solid rgba(0, 0, 0, 0.1);position:relative;}
  17
+.list .name,.todo .name{display:inline-block;position:relative;}.list .name span,.todo .name span{position:absolute;z-index:2;display:block;width:100%;height:2px;background-color:#fff;left:0;top:52%;-webkit-perspective:0;-webkit-backface-visibility:hidden;-webkit-transform-origin:0%;-webkit-transition:-webkit-transform 0 cubic-bezier(0, 0, 0.2, 1);-webkit-transform:scalex(0);}
  18
+.list input[type="text"],.todo input[type="text"]{border:none;background:transparent;color:#fff;font-size:18px;font-weight:bold;padding:0;}
  19
+.list{background-color:#0086F3;}.list .count{position:absolute;right:0;top:-1px;width:60px;height:60px;text-align:center;line-height:60px;background-color:rgba(255, 255, 255, 0.15);}
  20
+.list.empty{color:rgba(255, 255, 255, 0.5);}
  21
+.todo{background-color:#f5001d;}.todo.green{background-color:#00AA33 !important;}
  22
+.todo.done{background-color:#252525 !important;color:#434343;}.todo.done .name span{-webkit-transform:scalex(1) !important;background-color:#434343 !important;}
  23
+.todo.done a{color:#434343 !important;}
164  css/style.less
... ...
@@ -0,0 +1,164 @@
  1
+body {
  2
+	width: 100%;
  3
+	padding: 0;
  4
+	margin: 0;
  5
+	background-color: #000;
  6
+	color: #fff;
  7
+	font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
  8
+}
  9
+
  10
+ul {
  11
+	margin: 0;
  12
+	padding: 0;
  13
+	list-style-type: none;
  14
+}
  15
+
  16
+a {
  17
+	color: #fff;
  18
+	text-decoration: none;
  19
+}
  20
+
  21
+#wrapper {
  22
+	overflow-x: hidden;
  23
+	overflow-y: auto;
  24
+}
  25
+
  26
+#check, #cross {
  27
+	display: block;
  28
+	width: 55px;
  29
+	height: 60px;
  30
+	position: absolute;
  31
+	-webkit-transition: opacity 0 cubic-bezier(0,0,0.2,1), -webkit-transform 0 cubic-bezier(0,0,0.2,1);
  32
+	-webkit-perspective: 0;
  33
+	-webkit-backface-visibility: hidden;
  34
+	display: none;
  35
+}
  36
+
  37
+#check {
  38
+	left: 0;
  39
+}
  40
+
  41
+#cross {
  42
+	right: 0;
  43
+}
  44
+
  45
+#home, #listview {
  46
+	position: absolute;
  47
+	width: 100%;
  48
+	height: 100%;
  49
+	top: 0;
  50
+	left: 0;
  51
+	-webkit-transition: all .15s ease;
  52
+	-webkit-perspective: 0;
  53
+	-webkit-backface-visibility: hidden;
  54
+}
  55
+
  56
+#home {
  57
+	z-index: 2;
  58
+}
  59
+
  60
+#listview {
  61
+	z-index: 1;
  62
+}
  63
+
  64
+.drag {
  65
+	z-index: 999 !important;
  66
+	-webkit-transition: all 0 cubic-bezier(0,0,0.2,1) !important;
  67
+}
  68
+
  69
+.shadow {
  70
+	box-shadow: 0 1px 6px rgba(0,0,0,.25);
  71
+}
  72
+
  73
+.slow {
  74
+	-webkit-transition: all .35s ease-in-out !important;
  75
+}
  76
+
  77
+.medium {
  78
+	-webkit-transition: all .25s ease-in-out !important;
  79
+}
  80
+
  81
+.fast {
  82
+	-webkit-transition: all .15s ease !important;
  83
+}
  84
+
  85
+.list, .todo {
  86
+	height: 60px;
  87
+	-webkit-user-select: none;
  88
+	-webkit-transition: all .1s ease;
  89
+	-webkit-perspective: 0;
  90
+	-webkit-backface-visibility: hidden;
  91
+	position: relative;
  92
+	.inner {
  93
+		height: 58px;
  94
+		padding: 0 10px;
  95
+		font-size: 18px;
  96
+		font-weight: bold;
  97
+		line-height: 58px;
  98
+		border-top: 1px solid rgba(255,255,255,.1);
  99
+		border-bottom: 1px solid rgba(0,0,0,.1);
  100
+		position: relative;
  101
+	}
  102
+	.name {
  103
+		display: inline-block;
  104
+		position: relative;
  105
+		span {
  106
+			position: absolute;
  107
+			z-index: 2;
  108
+			display: block;
  109
+			width: 100%;
  110
+			height: 2px;
  111
+			background-color: #fff;
  112
+			left: 0;
  113
+			top: 52%;
  114
+			-webkit-perspective: 0;
  115
+			-webkit-backface-visibility: hidden;
  116
+			-webkit-transform-origin: 0%;
  117
+			-webkit-transition: -webkit-transform 0 cubic-bezier(0,0,0.2,1); 
  118
+			-webkit-transform: scaleX(0);
  119
+		}
  120
+	}
  121
+	input[type="text"] {
  122
+		border: none;
  123
+		background: transparent;
  124
+		color: #fff;
  125
+		font-size: 18px;
  126
+		font-weight: bold;
  127
+		padding: 0;
  128
+	}
  129
+}
  130
+
  131
+.list {
  132
+	background-color: #0086F3;
  133
+	.count {
  134
+		position: absolute;
  135
+		right: 0;
  136
+		top: -1px;
  137
+		width: 60px;
  138
+		height: 60px;
  139
+		text-align: center;
  140
+		line-height: 60px;
  141
+		background-color: rgba(255,255,255,.15);
  142
+	}
  143
+	&.empty {
  144
+		color: rgba(255,255,255,.5);
  145
+	}
  146
+}
  147
+
  148
+.todo {
  149
+	background-color: hsl(353, 100%, 48%);
  150
+	&.green {
  151
+		background-color: #00AA33 !important;
  152
+	}
  153
+	&.done {
  154
+		background-color: #252525 !important;
  155
+		color: #434343;
  156
+		.name span {
  157
+			-webkit-transform: scaleX(1) !important;
  158
+			background-color: #434343 !important;
  159
+		}
  160
+		a {
  161
+			color: #434343 !important;
  162
+		}
  163
+	}
  164
+}
BIN  img/check.png
BIN  img/cross.png
20  index.html
... ...
@@ -0,0 +1,20 @@
  1
+<!DOCTYPE html>
  2
+<html>
  3
+	<head>
  4
+		<title>HTML5 Clear</title>
  5
+		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  6
+		<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
  7
+		<link rel="stylesheet" href="css/style.css" type="text/css" media="screen" charset="utf-8">
  8
+		<script src="js/zepto.js" type="text/javascript" charset="utf-8"></script>
  9
+		<!--<script src="js/touch-scroll.js" type="text/javascript" charset="utf-8"></script>-->
  10
+		<script src="js/shiv.js" type="text/javascript" charset="utf-8"></script>
  11
+		<script src="js/app.js" type="text/javascript" charset="utf-8"></script>
  12
+		<!--<script src="../live.js" type="text/javascript" charset="utf-8"></script>-->
  13
+	</head>
  14
+	<body>
  15
+		<img id="check" src="img/check.png"/>
  16
+		<img id="cross" src="img/cross.png"/>
  17
+		<div id="wrapper">
  18
+		</div>
  19
+	</body>
  20
+</html>
471  js/app.js
... ...
@@ -0,0 +1,471 @@
  1
+//Templates
  2
+var templates = {
  3
+	Home: function () {
  4
+		return '<ul id="home"></ul>';
  5
+	},
  6
+	List: function (list) {
  7
+		var count = list.count();
  8
+		return '<li class="list ' + (count == 0 ? 'empty' : '') + '" data-index="'+ list.index +'">'
  9
+		+ '<div class="inner">'
  10
+		+ '<div class="name">' + list.name + '</div>'
  11
+		+ '<div class="count">' + count + '</div>'
  12
+		+ '</div></li>';
  13
+	},
  14
+	ListView: function () {
  15
+		return '<ul id="listview"></ul>';
  16
+	},
  17
+	Todo: function (todo) {
  18
+		return '<li class="todo ' + (todo.done ? 'done' : '') + '" data-index="' + todo.index + '">'
  19
+		+ '<div class="inner">'
  20
+		+ '<div class="name">' + todo.name + '<span></span></div>'
  21
+		+ '</div></li>';
  22
+	}
  23
+};
  24
+
  25
+//Todo View
  26
+var Todo = function (name) {
  27
+	this.name = name;
  28
+	this.done = false;
  29
+};
  30
+Todo.prototype = {
  31
+	render: function (list, index) {
  32
+		this.list = list;
  33
+		this.index = index;
  34
+		var todo = this,
  35
+			dragging = false,
  36
+			swiping = false,
  37
+			touch = {},
  38
+			w = 55,
  39
+			dx = dy = 0;
  40
+		todo.el = $(templates.Todo(todo));
  41
+		var line = todo.el.find('span');
  42
+		todo.el.bind('touchstart', function(e){
  43
+			if (e.touches.length == 1) {
  44
+				touch.x1 = e.touches[0].pageX;
  45
+				touch.y1 = e.touches[0].pageY;
  46
+			}
  47
+		})
  48
+		.bind('touchmove', function(e) {
  49
+			if (!window.swipeLock && e.touches.length == 1) {
  50
+				dx = e.touches[0].pageX - touch.x1;
  51
+				dy = e.touches[0].pageY - touch.y1;
  52
+				if (Math.abs(dy) < 6 && !swiping) {
  53
+					swiping = true;
  54
+					window.nodrag = true;
  55
+					todo.el.addClass('drag');
  56
+					list.home.resetIcons(todo.el);
  57
+				}
  58
+				if (swiping) {
  59
+					if (dx > 0 && dx < w) {
  60
+						var pct = dx/w;
  61
+						if (pct < 0.03) pct = 0;
  62
+						$('#check').css('opacity', pct);
  63
+						if (!todo.done) line.css('-webkit-transform', 'scaleX(' + pct + ')');
  64
+					} else if (dx < 0 && dx > -w){
  65
+						$('#cross').css('opacity', -dx/w);
  66
+					} else if (dx >= w) {
  67
+						dx = w + (dx - w) * .25;
  68
+						$('#check').css({
  69
+							'opacity': 1,
  70
+							'-webkit-transform': 'translate3d(' + (dx-w) + 'px, 0, 0)'
  71
+						});
  72
+					} else if (dx <= -w) {
  73
+						dx = -w + (dx + w) * .25;
  74
+						$('#cross').css({
  75
+							'opacity': 1,
  76
+							'-webkit-transform': 'translate3d(' + (dx+w) + 'px, 0, 0)'
  77
+						});
  78
+					}
  79
+					if (dx >= w-1) {
  80
+						if (todo.done) {
  81
+							todo.el.removeClass('done');
  82
+						} else {
  83
+							todo.el.addClass('green');
  84
+						}
  85
+					} else {
  86
+						if (todo.done) {
  87
+							todo.el.addClass('done');
  88
+						} else {
  89
+							todo.el.removeClass('green');
  90
+						}
  91
+					}
  92
+					if (dx <= 0 || list.todos.length > 0) {
  93
+						todo.el.css('-webkit-transform', 'translate3d(' + dx + 'px, 0, 0)');
  94
+					}
  95
+				}
  96
+			}
  97
+		})
  98
+		.swipeLeft(function(e){ //DELETE
  99
+			if (!window.swipeLock) {
  100
+				todo.destroy();
  101
+			}
  102
+		})
  103
+		.swipeRight(function(e){ //DONE
  104
+			if (!window.swipeLock) {
  105
+				if (!todo.done) {
  106
+					todo.done = true;
  107
+					var dy = (todo.list.todos.length - 1) * 60 - todo.el.get(0).offsetTop;
  108
+					setTimeout(function(){
  109
+						todo.el.addClass('medium').addClass('done').css({
  110
+							'z-index': '999',
  111
+							'margin-bottom':'-60px',
  112
+							'-webkit-transform':'translate3d(0,'+ dy +'px,0)'
  113
+						});
  114
+						setTimeout(function(){ //reset
  115
+							todo.list.todos.splice(todo.index, 1);
  116
+							todo.list.todos.push(todo);
  117
+							todo.list.resetView();
  118
+						}, 250);
  119
+					}, 150);
  120
+				} else {
  121
+					todo.done = false;
  122
+					var dy = -todo.el.get(0).offsetTop;
  123
+					setTimeout(function(){
  124
+						todo.el.addClass('medium').removeClass('done').css({
  125
+							'z-index': '999',
  126
+							'margin-bottom':'-60px',
  127
+							'-webkit-transform':'translate3d(0,'+ dy +'px,0)'
  128
+						});
  129
+						todo.el.siblings().addClass('medium').css({
  130
+							'-webkit-transform':'translate3d(0,60px,0)'
  131
+						});
  132
+						setTimeout(function(){
  133
+							todo.list.todos.splice(todo.index, 1);
  134
+							todo.list.todos.unshift(todo);
  135
+							todo.list.resetView();
  136
+						}, 250);
  137
+					}, 150);
  138
+				}
  139
+			}
  140
+		})
  141
+		.longTap(function(e){
  142
+			//st
  143
+		})
  144
+		.bind('touchend touchcancel', function(e){
  145
+			if (e.touches.length == 0) {
  146
+				window.nodrag = false;
  147
+				dragging = false;
  148
+				swiping = false;
  149
+				todo.el.removeClass('drag').css('-webkit-transform', 'translate3d(0,0,0)');
  150
+				touch = {};
  151
+				$('#cross, #check').css('opacity', 0);
  152
+				if (!todo.done) {
  153
+					line.addClass('fast').css('-webkit-transform', 'scaleX(0)');
  154
+					setTimeout(function(){
  155
+						line.removeClass('fast');
  156
+					}, 150);
  157
+				}
  158
+			}
  159
+		});
  160
+		
  161
+		todo.el.find('.name').tap(function(e){
  162
+			e.stopPropagation();
  163
+			todo.el.siblings().css('opacity', .3);
  164
+			var oname = $(this).text();
  165
+			$(this).html($('<input type="text" value="' + oname + '"/>').blur(function(){
  166
+				todo.el.siblings().css('opacity', 1);
  167
+				todo.name = this.value;
  168
+				$(this).replaceWith(this.value);
  169
+			}));
  170
+		});
  171
+		return todo.el;
  172
+	},
  173
+	destroy: function () {
  174
+		var todo = this;
  175
+		todo.el.addClass('medium').css('-webkit-transform', 'translate3d(' + (-window.innerWidth) + 'px, 0, 0)');
  176
+		setTimeout(function(){ todo.el.css('height', 0); }, 250);
  177
+		setTimeout(function(){
  178
+			todo.el.remove();
  179
+			todo.list.todos.splice(todo.index, 1);
  180
+			todo.list.resetView();
  181
+		}, 500);
  182
+	}
  183
+};
  184
+
  185
+//List View
  186
+var List = function (name, todos) {
  187
+	this.name = name;
  188
+	this.todos = todos || [];
  189
+};
  190
+List.prototype = {
  191
+	renderSelf: function (home, index) {
  192
+		this.home = home;
  193
+		this.index = index;
  194
+		var list = this,
  195
+			dragging = false,
  196
+			swiping = false,
  197
+			touch = {},
  198
+			w = 55;
  199
+		list.el = $(templates.List(list))
  200
+		.bind('touchstart', function(e){
  201
+			if (e.touches.length == 1) {
  202
+				touch.x1 = e.touches[0].pageX;
  203
+				touch.y1 = e.touches[0].pageY;
  204
+			}
  205
+		})
  206
+		.bind('touchmove', function(e) {
  207
+			if (!window.swipeLock && e.touches.length == 1) {
  208
+				var dx = e.touches[0].pageX - touch.x1,
  209
+					dy = e.touches[0].pageY - touch.y1;
  210
+				if (Math.abs(dy) < 6 && !swiping) {
  211
+					swiping = true;
  212
+					window.nodrag = true;
  213
+					list.el.addClass('drag');
  214
+					home.resetIcons(list.el);
  215
+				}
  216
+				if (swiping) {
  217
+					if (dx > 0 && dx < w) {
  218
+						$('#check').css('opacity', dx/w);
  219
+					} else if (dx < 0 && dx > -w){
  220
+						$('#cross').css('opacity', -dx/w);
  221
+					} else if (dx >= w) {
  222
+						dx = w + (dx - w) * .25;
  223
+						$('#check').css({
  224
+							'opacity': 1,
  225
+							'-webkit-transform': 'translate3d(' + (dx-w) + 'px, 0, 0)'
  226
+						});
  227
+					} else if (dx <= -w) {
  228
+						dx = -w + (dx + w) * .25;
  229
+						$('#cross').css({
  230
+							'opacity': 1,
  231
+							'-webkit-transform': 'translate3d(' + (dx+w) + 'px, 0, 0)'
  232
+						});
  233
+					}
  234
+					if (dx <= 0 || list.todos.length > 0) {
  235
+						list.el.css('-webkit-transform', 'translate3d(' + dx + 'px, 0, 0)');
  236
+					}
  237
+				}
  238
+			}
  239
+		})
  240
+		.bind('touchend touchcancel', function(e){
  241
+			window.nodrag = false;
  242
+			dragging = false;
  243
+			swiping = false;
  244
+			list.el.removeClass('drag').css('-webkit-transform', 'translate3d(0,0,0)');
  245
+			touch = {};
  246
+			$('#cross, #check').css('opacity', 0);
  247
+		})
  248
+		.swipeLeft(function(e){
  249
+			if (!window.swipeLock) {
  250
+				var l = list.count();
  251
+				if ((l > 0 && confirm('This list contains ' + l + ' items. Are you sure you want to delete it?')) || l == 0) {
  252
+					list.destroy();
  253
+				}
  254
+			}
  255
+		})
  256
+		.swipeRight(function(e){
  257
+			if (!window.swipeLock && list.count() > 0 && confirm('Are you sure you want to complete all your items in this list?')) {
  258
+				$.each(list.todos, function(i, t) {
  259
+					t.done = true;
  260
+				});
  261
+				list.refreshSelf();
  262
+			}
  263
+		})
  264
+		.longTap(function(e){
  265
+			//start vertical dragging
  266
+		})
  267
+		.tap(function(e){
  268
+			if (list.todos.length > 0) list.renderView().appendTo('#wrapper');
  269
+		});
  270
+		list.el.find('.name').tap(function(e){
  271
+			e.stopPropagation();
  272
+			list.el.siblings().css('opacity', .3);
  273
+			var oname = $(this).text();
  274
+			$(this).html($('<input type="text" value="' + oname + '"/>').blur(function(){
  275
+				list.el.siblings().css('opacity', 1);
  276
+				list.name = this.value;
  277
+				$(this).replaceWith(this.value);
  278
+			}));
  279
+		});
  280
+		return list.el;
  281
+	},
  282
+	renderView: function () {
  283
+		
  284
+		//Handling the pinches
  285
+		
  286
+		var list = this,
  287
+			odist = 0,
  288
+			triggered = false;
  289
+		
  290
+		list.view = $(templates.ListView())
  291
+		.bind('touchstart', function (e) {
  292
+			if (e.touches.length == 2) {
  293
+				window.nodrag = true;
  294
+				var dx = e.touches[0].pageX - e.touches[1].pageX,
  295
+					dy = e.touches[0].pageY - e.touches[1].pageY;
  296
+				odist = dx*dx + dy*dy;
  297
+			}
  298
+		})
  299
+		.bind('touchmove', function (e) {
  300
+			if (!triggered && e.touches.length == 2) {
  301
+				var dx = e.touches[0].pageX - e.touches[1].pageX,
  302
+					dy = e.touches[0].pageY - e.touches[1].pageY,
  303
+					dist = dx*dx + dy*dy;
  304
+				if (odist - dist > 50) {                           //PINCH IN
  305
+					list.home.el.addClass('slow').css({
  306
+						'-webkit-transform': 'translate3d(0,0,0)',
  307
+						'opacity': 1
  308
+					});
  309
+					list.view.find('.todo').addClass('slow').each(function (i, t) {
  310
+						$(this).css({
  311
+							'-webkit-transform': 'translate3d(0,-'+ this.offsetTop +'px,0)',
  312
+							'opacity': 0
  313
+						});
  314
+					});
  315
+					setTimeout(function(){
  316
+						list.home.el.removeClass('slow');
  317
+						list.home.reset();
  318
+						list.view.remove();
  319
+					}, 350);
  320
+					triggered = true;
  321
+				} else if (odist - dist < -50) {                   //PINCH OUT
  322
+					
  323
+					triggered = true;
  324
+				}
  325
+			}
  326
+		})
  327
+		.bind('touchend touchcancel', function (e) {
  328
+			if (e.touches.length == 0) {
  329
+				window.nodrag = false;
  330
+				odist = 0;
  331
+				triggered = false;
  332
+			}
  333
+		});
  334
+		
  335
+		$.each(list.todos, function(i,todo){
  336
+			var t = todo.render(list, i).css({
  337
+				'z-index':99-i,
  338
+				'-webkit-transform':'translate3d(0,' + (list.el.offset().top - 60 * i) + 'px,0)'
  339
+			});
  340
+			list.view.append(t);
  341
+		});
  342
+		
  343
+		setTimeout(function(){
  344
+			list.home.el.addClass('slow').css({
  345
+				'-webkit-transform': 'translate3d(0,' + (-list.home.el.height()) + 'px,0)',
  346
+				'opacity': 0
  347
+			});
  348
+			list.view.find('.todo').addClass('slow').css('-webkit-transform','translate3d(0,0,0)');
  349
+		}, 30);
  350
+		setTimeout(function(){
  351
+			list.home.el.removeClass('slow');
  352
+			list.resetView();
  353
+		}, 380);
  354
+		list.refreshView();
  355
+		return list.view;
  356
+	},
  357
+	resetView: function () {
  358
+		var list = this;
  359
+		list.view.empty();
  360
+		$.each(list.todos, function(i,todo){
  361
+			var t = todo.render(list, i).css({
  362
+				'z-index':99-i
  363
+			});
  364
+			list.view.append(t);
  365
+		});
  366
+		list.refreshView();
  367
+	},
  368
+	refreshView: function () {
  369
+		var list = this;
  370
+		list.view.find('.todo:not(.done)').each(function(i){
  371
+			$(this).css('background-color','hsl(' + (353+i*10)%360 + ',100%,' + (i==0 ? '48%':'53%') + ')');
  372
+		});
  373
+	},
  374
+	refreshSelf: function () {
  375
+		var list = this;
  376
+		list.el.find('.count').html(list.count());
  377
+		list.el.addClass('empty');
  378
+	},
  379
+	destroy: function () {
  380
+		var list = this;
  381
+		list.el.addClass('medium').css('-webkit-transform', 'translate3d(' + (-window.innerWidth) + 'px, 0, 0)');
  382
+		setTimeout(function(){ list.el.css('height', 0); }, 250);
  383
+		setTimeout(function(){
  384
+			list.el.remove();
  385
+			list.home.lists.splice(list.index, 1);
  386
+			list.home.refresh();
  387
+		}, 500);
  388
+	},
  389
+	count: function () {
  390
+		var list = this,
  391
+			todo = 0;
  392
+		for (var i = 0; i < list.todos.length; i++) {
  393
+			if (!list.todos[i].done) todo++;
  394
+		}
  395
+		return todo;
  396
+	}
  397
+};
  398
+
  399
+//Home View
  400
+var Home = function (lists) {
  401
+	this.lists = lists || [];
  402
+};
  403
+Home.prototype = {
  404
+	render: function () {
  405
+		var home = this;
  406
+		home.el = $(templates.Home());
  407
+		home.reset();
  408
+		return home.el;
  409
+	},
  410
+	reset: function () {
  411
+		var home = this;
  412
+		home.el.empty();
  413
+		$.each(home.lists, function(i, list) {
  414
+			home.el.append(list.renderSelf(home, i));
  415
+		});
  416
+		home.refresh();
  417
+	},
  418
+	refresh: function () {
  419
+		var home = this;
  420
+		home.el.find('.list').each(function(i){
  421
+			$(this).css('background-color','hsl(' + (212-i*3)%360 + ', 100%, 53%)');
  422
+		});
  423
+	},
  424
+	resetIcons: function(el) {
  425
+		$('#cross, #check').show().css({
  426
+			'top': el.offset().top + 'px',
  427
+			'opacity': 0,
  428
+			'-webkit-transform': 'none'
  429
+		});
  430
+	}
  431
+};
  432
+
  433
+$(function(){
  434
+	
  435
+	//setup scrolling
  436
+	$(document)
  437
+	.bind('touchmove', function(e){
  438
+		if (window.nodrag) {
  439
+			e.preventDefault();
  440
+		} else {
  441
+			window.swipeLock = true;
  442
+		}
  443
+	})
  444
+	.bind('touchend touchcancel', function(e){
  445
+		window.swipeLock = false;
  446
+	});
  447
+	
  448
+	var home = new Home([
  449
+		new List('Hello', [
  450
+			new Todo('Swipe right to complete'),
  451
+			new Todo('Swipe left to delete'),
  452
+			new Todo('Tap on text to edit'),
  453
+			new Todo('Drag down to add new'),
  454
+			new Todo('Pinch to go back.')
  455
+		]),
  456
+		new List('This is a demo', [
  457
+			new Todo('Built with HTML5'),
  458
+			new Todo('CSS3'),
  459
+			new Todo('and Zepto.js')
  460
+		]),
  461
+		new List('by Evan You', [
  462
+			new Todo('@youyuxi'),
  463
+			new Todo('By the way'),
  464
+			new Todo('I\'m looking for a job!'),
  465
+			new Todo('youyuxi.com')
  466
+		])
  467
+	]);
  468
+	
  469
+	home.render().appendTo('#wrapper');
  470
+	
  471
+});
10  js/shiv.js
... ...
@@ -0,0 +1,10 @@
  1
+Array.prototype.move = function (old_index, new_index) {
  2
+    if (new_index >= this.length) {
  3
+        var k = new_index - this.length;
  4
+        while ((k--) + 1) {
  5
+            this.push(undefined);
  6
+        }
  7
+    }
  8
+    this.splice(new_index, 0, this.splice(old_index, 1)[0]);
  9
+    return this; // for testing purposes
  10
+};
378  js/touch-scroll.js
... ...
@@ -0,0 +1,378 @@
  1
+(function($) {
  2
+	
  3
+	// Define default scroll settings
  4
+	var defaults = {
  5
+		y: 0,
  6
+		scrollHeight: 0,
  7
+		elastic: !navigator.userAgent.match(/android/i),
  8
+		momentum: true,
  9
+		elasticDamp: 0.6,
  10
+		elasticTime: 50,
  11
+		reboundTime: 400,
  12
+		momentumDamp: 0.9,
  13
+		momentumTime: 300,
  14
+		iPadMomentumDamp: 0.95,
  15
+		iPadMomentumTime: 1200,
  16
+		touchTags: ['select', 'input', 'textarea']
  17
+	};
  18
+	
  19
+	// Define methods
  20
+	var methods = {
  21
+		
  22
+		init: function(options) {
  23
+			return this.each(function() {
  24
+				
  25
+				var o = $.extend(defaults, options);
  26
+				
  27
+				// Prevent double-init, just update instead
  28
+				if (!!this._init) {
  29
+					return this.update();
  30
+				}
  31
+				this._init = true;
  32
+				
  33
+				// Define element variables
  34
+				var $this = $(this),
  35
+					scrollY = -o.y,
  36
+					touchY = 0,
  37
+					movedY = 0,
  38
+					pollY = 0,
  39
+					height = 0,
  40
+					maxHeight = 0,
  41
+					scrollHeight = 0,
  42
+					scrolling = false,
  43
+					bouncing = false,
  44
+					moved = false,
  45
+					timeoutID,
  46
+					isiPad = !!navigator.platform.match(/ipad/i),
  47
+					hasMatrix = 'WebKitCSSMatrix' in window,
  48
+					has3d = hasMatrix && 'm11' in new WebKitCSSMatrix();
  49
+				
  50
+				// Keep bottom of scroll area at the bottom on resize
  51
+				var update = this.update = function() {
  52
+					height = $this.height();
  53
+					if (o.scrollHeight) {
  54
+						scrollHeight = o.scrollHeight;
  55
+					} else if ($this.prop) {
  56
+						scrollHeight = $this.prop('scrollHeight'); // jQuery 1.6 uses .prop(), older versions use .attr()
  57
+					} else {
  58
+						scrollHeight = $this.attr('scrollHeight');
  59
+					}
  60
+					if (scrollHeight < height) {
  61
+						scrollHeight = height;
  62
+					}
  63
+					maxHeight = height - scrollHeight;
  64
+					clearTimeout(timeoutID);
  65
+					clampScroll(false);
  66
+				};
  67
+				
  68
+				// Set up initial variables
  69
+				update();
  70
+				
  71
+				// Set up transform CSS
  72
+				$this.css({'-webkit-transition-property': '-webkit-transform',
  73
+					'-webkit-transition-timing-function': 'cubic-bezier(0,0,0.2,1)',
  74
+					'-webkit-transition-duration': '0',
  75
+					'-webkit-transform': cssTranslate(scrollY)});
  76
+				
  77
+				// Listen for screen size change event
  78
+				window.addEventListener('onorientationchange' in window ? 'orientationchange' : 'resize', update, false);
  79
+				
  80
+				// Listen for touch events
  81
+				$this.bind('touchstart.touchScroll', touchStart);
  82
+				$this.bind('touchmove.touchScroll', touchMove);
  83
+				$this.bind('touchend.touchScroll touchcancel.touchScroll', touchEnd);
  84
+				$this.bind('webkitTransitionEnd.touchScroll', transitionEnd);
  85
+				
  86
+				// Set the position of the scroll area using transform CSS
  87
+				var setPosition = this.setPosition = function(y) {
  88
+					scrollY = y;
  89
+					$this.css('-webkit-transform', cssTranslate(scrollY));
  90
+				};
  91
+				
  92
+				// Transform using a 3D translate if available
  93
+				function cssTranslate(y) {
  94
+					return 'translate' + (has3d ? '3d(0,' : '(0,') + y + 'px' + (has3d ? ',0)' : ')');
  95
+				}
  96
+				
  97
+				// Set CSS transition time
  98
+				function setTransitionTime(time) {
  99
+					time = time || '0';
  100
+					$this.css('-webkit-transition-duration', time + 'ms');
  101
+				}
  102
+
  103
+				// Get the actual pixel position made by transform CSS
  104
+				function getPosition() {
  105
+					if (hasMatrix) {
  106
+						var transform = window.getComputedStyle($this[0]).webkitTransform;
  107
+						if (!!transform && transform !== 'none') {
  108
+							var matrix = new WebKitCSSMatrix(transform);
  109
+							return matrix.f;
  110
+						}
  111
+					}
  112
+					return scrollY;
  113
+				}
  114
+				
  115
+				// Expose getPosition API
  116
+				this.getPosition = function() {
  117
+					return getPosition();
  118
+				};
  119
+
  120
+				// Bounce back to the bounds after momentum scrolling
  121
+				function reboundScroll() {
  122
+					if (scrollY > 0) {
  123
+						scrollTo(0, o.reboundTime);
  124
+					} else if (scrollY < maxHeight) {
  125
+						scrollTo(maxHeight, o.reboundTime);
  126
+					}
  127
+				}
  128
+
  129
+				// Stop everything once the CSS transition in complete
  130
+				function transitionEnd() {
  131
+					if (bouncing) {
  132
+						bouncing = false;
  133
+						reboundScroll();
  134
+					}
  135
+
  136
+					clearTimeout(timeoutID);
  137
+				}
  138
+				
  139
+				// Limit the scrolling to within the bounds
  140
+				function clampScroll(poll) {
  141
+					if (!hasMatrix || bouncing) {
  142
+						return;
  143
+					}
  144
+
  145
+					var oldY = pollY;
  146
+					pollY = getPosition();
  147
+					
  148
+					if (pollY > 0) {
  149
+						if (o.elastic) {
  150
+							// Slow down outside top bound
  151
+							bouncing = true;
  152
+							scrollY = 0;
  153
+							momentumScroll(pollY - oldY, o.elasticDamp, 1, height, o.elasticTime);
  154
+						} else {
  155
+							// Stop outside top bound
  156
+							setTransitionTime(0);
  157
+							setPosition(0);
  158
+						}
  159
+					} else if (pollY < maxHeight) {
  160
+						if (o.elastic) {
  161
+							// Slow down outside bottom bound
  162
+							bouncing = true;
  163
+							scrollY = maxHeight;
  164
+							momentumScroll(pollY - oldY, o.elasticDamp, 1, height, o.elasticTime);
  165
+						} else {
  166
+							// Stop outside bottom bound
  167
+							setTransitionTime(0);
  168
+							setPosition(maxHeight);
  169
+						}
  170
+					} else if (poll) {
  171
+						// Poll the computed position to check if element is out of bounds
  172
+						timeoutID = setTimeout(clampScroll, 20, true);
  173
+					}
  174
+				}
  175
+				
  176
+				// Animate to a position using CSS
  177
+				function scrollTo(destY, time) {
  178
+					if (destY === scrollY) {
  179
+						return;
  180
+					}
  181
+					
  182
+					moved = true;
  183
+					setTransitionTime(time);
  184
+					setPosition(destY);
  185
+				}
  186
+				
  187
+				// Perform a momentum-based scroll using CSS
  188
+				function momentumScroll(d, k, minDist, maxDist, t) {
  189
+					var ad = Math.abs(d),
  190
+						dy = 0;
  191
+					
  192
+					// Calculate the total distance
  193
+					while (ad > 0.1) {
  194
+						ad *= k;
  195
+						dy += ad;
  196
+					}
  197
+					
  198
+					// Limit to within min and max distances
  199
+					if (dy > maxDist) {
  200
+						dy = maxDist;
  201
+					}
  202
+					if (dy > minDist) {
  203
+						if (d < 0) {
  204
+							dy = -dy;
  205
+						}
  206
+						
  207
+						dy += scrollY;
  208
+						
  209
+						// If outside the bounds, don't go too far
  210
+						if (height > 0) {
  211
+							if (dy > height * 2) {
  212
+								var ody = dy;
  213
+								dy = height * 2;
  214
+							} else if (dy < maxHeight - height * 2) {
  215
+								dy = maxHeight - height * 2;
  216
+							}
  217
+						}
  218
+					
  219
+						// Perform scroll
  220
+						scrollTo(Math.round(dy), t);
  221
+					}
  222
+					
  223
+					clampScroll(true);
  224
+				}
  225
+				
  226
+				// Get the touch points from this event
  227
+				function getTouches(e) {
  228
+					if (e.originalEvent) {
  229
+						if (e.originalEvent.touches && e.originalEvent.touches.length) {
  230
+							return e.originalEvent.touches;
  231
+						} else if (e.originalEvent.changedTouches && e.originalEvent.changedTouches.length) {
  232
+							return e.originalEvent.changedTouches;
  233
+						}
  234
+					}
  235
+					return e.touches;
  236
+				}
  237
+				
  238
+				// Dispatches a fake mouse event from a touch event
  239
+				function dispatchMouseEvent(name, touch, target) {
  240
+					var e = document.createEvent('MouseEvent');
  241
+					e.initMouseEvent(name, true, true, touch.view, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null);
  242
+					target.dispatchEvent(e);
  243
+				}
  244
+				
  245
+				// Find the root node of this target
  246
+				function getRootNode(target) {
  247
+					while (target.nodeType !== 1) {
  248
+						target = target.parentNode;
  249
+					}
  250
+					return target;
  251
+				}
  252
+				
  253
+				// Perform a touch start event
  254
+				function touchStart(e) {
  255
+					// Allow certain HTML tags to receive touch events
  256
+					if ($.inArray(e.target.tagName.toLowerCase(), o.touchTags) !== -1) {
  257
+						return;
  258
+					}
  259
+					
  260
+					// Stop the default touches
  261
+					e.preventDefault();
  262
+					e.stopPropagation();
  263
+					
  264
+					var touch = getTouches(e)[0];
  265
+					
  266
+					// Dispatch a fake mouse down event		
  267
+					dispatchMouseEvent('mousedown', touch, getRootNode(touch.target));
  268
+					
  269
+					scrolling = true;
  270
+					moved = false;
  271
+					movedY = 0;
  272
+					
  273
+					clearTimeout(timeoutID);
  274
+					setTransitionTime(0);
  275
+					
  276
+					// Check scroll position
  277
+					if (o.momentum) {
  278
+						var y = getPosition();
  279
+						if (y !== scrollY) {
  280
+							setPosition(y);
  281
+							moved = true;
  282
+						}
  283
+					}
  284
+
  285
+					touchY = touch.pageY - scrollY;
  286
+				}
  287
+				
  288
+				// Perform a touch move event
  289
+				function touchMove(e) {
  290
+					if (!scrolling) {
  291
+						return;
  292
+					}
  293
+					
  294
+					var dy = getTouches(e)[0].pageY - touchY;
  295
+					
  296
+					// Elastic-drag or stop when moving outside of boundaries
  297
+					if (dy > 0) {
  298
+						if (o.elastic) {
  299
+							dy /= 2;
  300
+						} else {
  301
+							dy = 0;
  302
+						}
  303
+					} else if (dy < maxHeight) {
  304
+						if (o.elastic) {
  305
+							dy = (dy + maxHeight) / 2;
  306
+						} else {
  307
+							dy = maxHeight;
  308
+						}
  309
+					}
  310
+					
  311
+					movedY = dy - scrollY;
  312
+					moved = true;
  313
+					setPosition(dy);
  314
+				}
  315
+				
  316
+				// Perform a touch end event
  317
+				function touchEnd(e) {
  318
+					if (!scrolling) {
  319
+						return;
  320
+					}
  321
+					
  322
+					scrolling = false;
  323
+					
  324
+					if (moved) {
  325
+						// Ease back to within boundaries
  326
+						if (scrollY > 0 || scrollY < maxHeight) {
  327
+							reboundScroll();
  328
+						} else if (o.momentum) {
  329
+							// Free scroll with momentum
  330
+							momentumScroll(movedY, isiPad ? o.iPadMomentumDamp : o.momentumDamp, 40, 2000, isiPad ? o.iPadMomentumTime : o.momentumTime);
  331
+						}			
  332
+					} else {
  333
+						var touch = getTouches(e)[0],
  334
+							target = getRootNode(touch.target);
  335
+						
  336
+						// Dispatch fake mouse up and click events if this touch event did not move
  337
+						dispatchMouseEvent('mouseup', touch, target);
  338
+						dispatchMouseEvent('click', touch, target);
  339
+					}
  340
+				}
  341
+			
  342
+			});
  343
+		},
  344
+		
  345
+		update: function() {
  346
+			return this.each(function() {
  347
+				this.update();
  348
+			});
  349
+		},
  350
+		
  351
+		getPosition: function() {
  352
+			var a = [];
  353
+			this.each(function() {
  354
+				a.push(-this.getPosition());
  355
+			});
  356
+			return a;
  357
+		},
  358
+		
  359
+		setPosition: function(y) {
  360
+			return this.each(function() {
  361
+				this.setPosition(-y);
  362
+			});
  363
+		}
  364
+		
  365
+	};
  366
+	
  367
+	// Public method for touchScroll
  368
+	$.fn.touchScroll = function(method) {
  369
+	    if (methods[method]) {
  370
+			return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
  371
+		} else if (typeof method === 'object' || !method) {
  372
+			return methods.init.apply(this, arguments);
  373
+		} else {
  374
+			$.error('Method ' +  method + ' does not exist on jQuery.touchScroll');
  375
+		}
  376
+	};
  377
+
  378
+})(Zepto);
1,412  js/zepto.js
... ...
@@ -0,0 +1,1412 @@
  1
+//     Zepto.js
  2
+//     (c) 2010, 2011 Thomas Fuchs
  3
+//     Zepto.js may be freely distributed under the MIT license.
  4
+
  5
+(function(undefined){
  6
+  if (String.prototype.trim === undefined) // fix for iOS 3.2
  7
+    String.prototype.trim = function(){ return this.replace(/^\s+/, '').replace(/\s+$/, '') };
  8
+
  9
+  // For iOS 3.x
  10
+  // from https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/reduce
  11
+  if (Array.prototype.reduce === undefined)
  12
+    Array.prototype.reduce = function(fun){
  13
+      if(this === void 0 || this === null) throw new TypeError();
  14
+      var t = Object(this), len = t.length >>> 0, k = 0, accumulator;
  15
+      if(typeof fun != 'function') throw new TypeError();
  16
+      if(len == 0 && arguments.length == 1) throw new TypeError();
  17
+
  18
+      if(arguments.length >= 2)
  19
+       accumulator = arguments[1];
  20
+      else
  21
+        do{
  22
+          if(k in t){
  23
+            accumulator = t[k++];
  24
+            break;
  25
+          }
  26
+          if(++k >= len) throw new TypeError();
  27
+        } while (true);
  28
+
  29
+      while (k < len){
  30
+        if(k in t) accumulator = fun.call(undefined, accumulator, t[k], k, t);
  31
+        k++;
  32
+      }
  33
+      return accumulator;
  34
+    };
  35
+
  36
+})();
  37
+//     Zepto.js
  38
+//     (c) 2010, 2011 Thomas Fuchs
  39
+//     Zepto.js may be freely distributed under the MIT license.
  40
+
  41
+var Zepto = (function() {
  42
+  var undefined, key, $$, classList, emptyArray = [], slice = emptyArray.slice,
  43
+    document = window.document,
  44
+    elementDisplay = {}, classCache = {},
  45
+    getComputedStyle = document.defaultView.getComputedStyle,
  46
+    cssNumber = { 'column-count': 1, 'columns': 1, 'font-weight': 1, 'line-height': 1,'opacity': 1, 'z-index': 1, 'zoom': 1 },
  47
+    fragmentRE = /^\s*<(\w+)[^>]*>/,
  48
+    elementTypes = [1, 9, 11],
  49
+    adjacencyOperators = [ 'after', 'prepend', 'before', 'append' ],
  50
+    table = document.createElement('table'),
  51
+    tableRow = document.createElement('tr'),
  52
+    containers = {
  53
+      'tr': document.createElement('tbody'),
  54
+      'tbody': table, 'thead': table, 'tfoot': table,
  55
+      'td': tableRow, 'th': tableRow,
  56
+      '*': document.createElement('div')
  57
+    },
  58
+    readyRE = /complete|loaded|interactive/,
  59
+    classSelectorRE = /^\.([\w-]+)$/,
  60
+    idSelectorRE = /^#([\w-]+)$/,
  61
+    tagSelectorRE = /^[\w-]+$/;
  62
+
  63
+  function isF(value) { return ({}).toString.call(value) == "[object Function]" }
  64
+  function isO(value) { return value instanceof Object }
  65
+  function isA(value) { return value instanceof Array }
  66
+  function likeArray(obj) { return typeof obj.length == 'number' }
  67
+
  68
+  function compact(array) { return array.filter(function(item){ return item !== undefined && item !== null }) }
  69
+  function flatten(array) { return array.length > 0 ? [].concat.apply([], array) : array }
  70
+  function camelize(str)  { return str.replace(/-+(.)?/g, function(match, chr){ return chr ? chr.toUpperCase() : '' }) }
  71
+  function dasherize(str){
  72
+    return str.replace(/::/g, '/')
  73
+           .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
  74
+           .replace(/([a-z\d])([A-Z])/g, '$1_$2')
  75
+           .replace(/_/g, '-')
  76
+           .toLowerCase();
  77
+  }
  78
+  function uniq(array)    { return array.filter(function(item,index,array){ return array.indexOf(item) == index }) }
  79
+
  80
+  function classRE(name){
  81
+    return name in classCache ?
  82
+      classCache[name] : (classCache[name] = new RegExp('(^|\\s)' + name + '(\\s|$)'));
  83
+  }
  84
+
  85
+  function maybeAddPx(name, value) { return (typeof value == "number" && !cssNumber[dasherize(name)]) ? value + "px" : value; }
  86
+
  87
+  function defaultDisplay(nodeName) {
  88
+    var element, display;
  89
+    if (!elementDisplay[nodeName]) {
  90
+      element = document.createElement(nodeName);
  91
+      document.body.appendChild(element);
  92
+      display = getComputedStyle(element, '').getPropertyValue("display");
  93
+      element.parentNode.removeChild(element);
  94
+      display == "none" && (display = "block");
  95
+      elementDisplay[nodeName] = display;
  96
+    }
  97
+    return elementDisplay[nodeName];
  98
+  }
  99
+
  100
+  function fragment(html, name) {
  101
+    if (name === undefined) fragmentRE.test(html) && RegExp.$1;
  102
+    if (!(name in containers)) name = '*';
  103
+    var container = containers[name];
  104
+    container.innerHTML = '' + html;
  105
+    return slice.call(container.childNodes);
  106
+  }
  107
+
  108
+  function Z(dom, selector){
  109
+    dom = dom || emptyArray;
  110
+    dom.__proto__ = Z.prototype;
  111
+    dom.selector = selector || '';
  112
+    return dom;
  113
+  }
  114
+
  115
+  function $(selector, context){
  116
+    if (!selector) return Z();
  117
+    if (context !== undefined) return $(context).find(selector);
  118
+    else if (isF(selector)) return $(document).ready(selector);
  119
+    else if (selector instanceof Z) return selector;
  120
+    else {
  121
+      var dom;
  122
+      if (isA(selector)) dom = compact(selector);
  123
+      else if (elementTypes.indexOf(selector.nodeType) >= 0 || selector === window)
  124
+        dom = [selector], selector = null;
  125
+      else if (fragmentRE.test(selector))
  126
+        dom = fragment(selector.trim(), RegExp.$1), selector = null;
  127
+      else if (selector.nodeType && selector.nodeType == 3) dom = [selector];
  128
+      else dom = $$(document, selector);
  129
+      return Z(dom, selector);
  130
+    }
  131
+  }
  132
+
  133
+  $.extend = function(target){
  134
+    slice.call(arguments, 1).forEach(function(source) {
  135
+      for (key in source) target[key] = source[key];
  136
+    })
  137
+    return target;
  138
+  }
  139
+
  140
+  $.qsa = $$ = function(element, selector){
  141
+    var found;
  142
+    return (element === document && idSelectorRE.test(selector)) ?