Commit fc47dac9 authored by David Posin's avatar David Posin Committed by Pascal Hartig

Adding Enyo + Backbone.js

Added code for new task

Working on repeater list

Have model synced to UI for create

Working on model sync for deletion, and completion

Continue Development

Finished

almost fully cleaned up

Reduced to only needed files
Minified 3rd part files only

Squash commits

Added comments

Clean up code style

Fix breaks from cleanup

clean up quotes and add space to comments

Moved all braces to command lines

Removed erroneous merge file

Fix extra character, change error message

Rearranged files to better match contribution guidelines

Removed all css except the bundled css
Cleaned index.html to look like the base todo index.html

Fixed blog url in footer

Changed Enter functionality

Enter now reverts the text change in task itema
Asitiom

Escape now aborts edit, Enter resets edit content

Modified enter to save edit, and esc to cancel edit

standardized to tabs; added jshint directives

Added directives to each file for jshint so that expected issues (like
no strict, and a global enyo object) are not flagged.   Ran everything
through jshint and jsbeautifier.org

More clean up

Found a few more + ones mentioned in the diff

Changed JSBeautifier settings and re-ran

Code cleanup

Fixed spelling

Added code to deselect text when double clicking

Removed unneeded focus code

Fix IE10 bug

Remove unnecessary file and routing bug on IE10

Modified fix for IE10 that broke view switching

Added base.js into the project

IE 10 changes

Added code to fix IE view routing
Added type to script tags to keep IE  happy

Missed a quote

Doh! seriously

Fixed routing, added component.json,

Also explicitly set app to be at the window.
Many thanks to @stephenplusplus

Minor changes

Fixed clear field error
Changed routing url for 'all' to avoid browser anchor behavior
Renamed models/models to models/model to avoid confusion

Improved file structure to avoid confusion

Fixed mark all complete bug

also clean up jshint and comments structure

Fixed version number in page title

removed redundant .jshint config file

Updated comments

Fixed edit field having strikethrough

on completed rows

Comment updates

Missed one comment

Fix opening curlies

Fix index

Fixed line through edit

Was adding unnecessary style.  Removed command

Removed error handler and type attributes

Fixing style

Fixing }, { and }]

Fixed array positioning
parent 9a9a0995
{
"name": "todomvc-enyo-backbone",
"version": "0.0.0",
"dependencies": {
"todomvc-common": "~0.1.4"
}
}
\ No newline at end of file
html,
body {
margin: 0;
padding: 0;
}
button {
margin: 0;
padding: 0;
border: 0;
background: none;
font-size: 100%;
vertical-align: baseline;
font-family: inherit;
color: inherit;
-webkit-appearance: none;
/*-moz-appearance: none;*/
-ms-appearance: none;
-o-appearance: none;
appearance: none;
}
body {
font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif;
line-height: 1.4em;
background: #eaeaea url('bg.png');
color: #4d4d4d;
width: 550px;
margin: 0 auto;
-webkit-font-smoothing: antialiased;
-moz-font-smoothing: antialiased;
-ms-font-smoothing: antialiased;
-o-font-smoothing: antialiased;
font-smoothing: antialiased;
}
#todoapp {
background: #fff;
background: rgba(255, 255, 255, 0.9);
margin: 130px 0 40px 0;
border: 1px solid #ccc;
position: relative;
border-top-left-radius: 2px;
border-top-right-radius: 2px;
box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.2),
0 25px 50px 0 rgba(0, 0, 0, 0.15);
}
#todoapp:before {
content: '';
border-left: 1px solid #f5d6d6;
border-right: 1px solid #f5d6d6;
width: 2px;
position: absolute;
top: 0;
left: 40px;
height: 100%;
}
#todoapp input::-webkit-input-placeholder {
font-style: italic;
}
#todoapp input:-moz-placeholder {
font-style: italic;
color: #a9a9a9;
}
#todoapp h1 {
position: absolute;
top: -120px;
width: 100%;
font-size: 70px;
font-weight: bold;
text-align: center;
color: #b3b3b3;
color: rgba(255, 255, 255, 0.3);
text-shadow: -1px -1px rgba(0, 0, 0, 0.2);
-webkit-text-rendering: optimizeLegibility;
-moz-text-rendering: optimizeLegibility;
-ms-text-rendering: optimizeLegibility;
-o-text-rendering: optimizeLegibility;
text-rendering: optimizeLegibility;
}
#header {
padding-top: 15px;
border-radius: inherit;
}
#header:before {
content: '';
position: absolute;
top: 0;
right: 0;
left: 0;
height: 15px;
z-index: 2;
border-bottom: 1px solid #6c615c;
background: #8d7d77;
background: -webkit-gradient(linear, left top, left bottom, from(rgba(132, 110, 100, 0.8)),to(rgba(101, 84, 76, 0.8)));
background: -webkit-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8));
background: -moz-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8));
background: -o-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8));
background: -ms-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8));
background: linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8));
filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#9d8b83', EndColorStr='#847670');
border-top-left-radius: 1px;
border-top-right-radius: 1px;
}
#new-todo,
.edit {
position: relative;
margin: 0;
width: 100%;
font-size: 24px;
font-family: inherit;
line-height: 1.4em;
border: 0;
outline: none;
color: inherit;
padding: 6px;
border: 1px solid #999;
box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2);
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
-ms-box-sizing: border-box;
-o-box-sizing: border-box;
box-sizing: border-box;
-webkit-font-smoothing: antialiased;
-moz-font-smoothing: antialiased;
-ms-font-smoothing: antialiased;
-o-font-smoothing: antialiased;
font-smoothing: antialiased;
}
#new-todo {
padding: 16px 16px 16px 60px;
border: none;
background: rgba(0, 0, 0, 0.02);
z-index: 2;
box-shadow: none;
}
#main {
position: relative;
z-index: 2;
border-top: 1px dotted #adadad;
}
label[for=toggle-all] {
display: none;
}
#toggle-all {
position: absolute;
top: -42px;
left: -4px;
width: 40px;
text-align: center;
border: none; /* Mobile Safari */
}
#toggle-all:before {
content: '»';
font-size: 28px;
color: #d9d9d9;
padding: 0 25px 7px;
}
#toggle-all:checked:before {
color: #737373;
}
#todo-list {
margin: 0;
padding: 0;
list-style: none;
}
#todo-list li {
position: relative;
font-size: 24px;
border-bottom: 1px dotted #ccc;
}
#todo-list li:last-child {
border-bottom: none;
}
#todo-list li.editing {
border-bottom: none;
padding: 0;
}
#todo-list li.editing .edit {
display: block;
width: 506px;
padding: 13px 17px 12px 17px;
margin: 0 0 0 43px;
}
#todo-list li.editing .view {
display: none;
}
#todo-list li .toggle {
text-align: center;
width: 40px;
/* auto, since non-WebKit browsers doesn't support input styling */
height: auto;
position: absolute;
top: 0;
bottom: 0;
margin: auto 0;
border: none; /* Mobile Safari */
-webkit-appearance: none;
/*-moz-appearance: none;*/
-ms-appearance: none;
-o-appearance: none;
appearance: none;
}
#todo-list li .toggle:after {
content: '✔';
line-height: 43px; /* 40 + a couple of pixels visual adjustment */
font-size: 20px;
color: #d9d9d9;
text-shadow: 0 -1px 0 #bfbfbf;
}
#todo-list li .toggle:checked:after {
color: #85ada7;
text-shadow: 0 1px 0 #669991;
bottom: 1px;
position: relative;
}
#todo-list li label {
word-break: break-word;
padding: 15px;
margin-left: 45px;
display: block;
line-height: 1.2;
-webkit-transition: color 0.4s;
-moz-transition: color 0.4s;
-ms-transition: color 0.4s;
-o-transition: color 0.4s;
transition: color 0.4s;
}
#todo-list li.completed label {
color: #a9a9a9;
text-decoration: line-through;
}
#todo-list li .destroy {
display: none;
position: absolute;
top: 0;
right: 10px;
bottom: 0;
width: 40px;
height: 40px;
margin: auto 0;
font-size: 22px;
color: #a88a8a;
-webkit-transition: all 0.2s;
-moz-transition: all 0.2s;
-ms-transition: all 0.2s;
-o-transition: all 0.2s;
transition: all 0.2s;
}
#todo-list li .destroy:hover {
text-shadow: 0 0 1px #000,
0 0 10px rgba(199, 107, 107, 0.8);
-webkit-transform: scale(1.3);
-moz-transform: scale(1.3);
-ms-transform: scale(1.3);
-o-transform: scale(1.3);
transform: scale(1.3);
}
#todo-list li .destroy:after {
content: '✖';
}
#todo-list li:hover .destroy {
display: block;
}
#todo-list li .edit {
display: none;
}
#todo-list li.editing:last-child {
margin-bottom: -1px;
}
#footer {
color: #777;
padding: 0 15px;
position: absolute;
right: 0;
bottom: -31px;
left: 0;
height: 20px;
z-index: 1;
text-align: center;
}
#footer:before {
content: '';
position: absolute;
right: 0;
bottom: 31px;
left: 0;
height: 50px;
z-index: -1;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.3),
0 6px 0 -3px rgba(255, 255, 255, 0.8),
0 7px 1px -3px rgba(0, 0, 0, 0.3),
0 43px 0 -6px rgba(255, 255, 255, 0.8),
0 44px 2px -6px rgba(0, 0, 0, 0.2);
}
#todo-count {
float: left;
text-align: left;
}
#filters {
margin: 0;
padding: 0;
list-style: none;
position: absolute;
right: 0;
left: 0;
}
#filters li {
display: inline;
}
#filters li a {
color: #83756f;
margin: 2px;
text-decoration: none;
}
#filters li a.selected {
font-weight: bold;
}
#clear-completed {
float: right;
position: relative;
line-height: 20px;
text-decoration: none;
background: rgba(0, 0, 0, 0.1);
font-size: 11px;
padding: 0 10px;
border-radius: 3px;
box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.2);
}
#clear-completed:hover {
background: rgba(0, 0, 0, 0.15);
box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.3);
}
#info {
margin: 65px auto 0;
color: #a6a6a6;
font-size: 12px;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.7);
text-align: center;
}
#info a {
color: inherit;
}
/*
Hack to remove background from Mobile Safari.
Can't use it globally since it destroys checkboxes in Firefox and Opera
*/
@media screen and (-webkit-min-device-pixel-ratio:0) {
#toggle-all,
#todo-list li .toggle {
background: none;
}
#todo-list li .toggle {
height: 40px;
}
#toggle-all {
top: -56px;
left: -15px;
width: 65px;
height: 41px;
-webkit-transform: rotate(90deg);
transform: rotate(90deg);
-webkit-appearance: none;
appearance: none;
}
}
.hidden{
display:none;
}
\ No newline at end of file
(function () {
'use strict';
if (location.hostname === 'todomvc.com') {
window._gaq = [['_setAccount','UA-31081062-1'],['_trackPageview']];(function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];g.src='//www.google-analytics.com/ga.js';s.parentNode.insertBefore(g,s)}(document,'script'));
}
function getSourcePath() {
// If accessed via addyosmani.github.io/todomvc/, strip the project path.
if (location.hostname.indexOf('github.io') > 0) {
return location.pathname.replace(/todomvc\//, '');
}
return location.pathname;
}
function appendSourceLink() {
var sourceLink = document.createElement('a');
var paragraph = document.createElement('p');
var footer = document.getElementById('info');
var urlBase = 'https://github.com/addyosmani/todomvc/tree/gh-pages';
if (footer) {
sourceLink.href = urlBase + getSourcePath();
sourceLink.appendChild(document.createTextNode('Check out the source'));
paragraph.appendChild(sourceLink);
footer.appendChild(paragraph);
}
}
function redirect() {
if (location.hostname === 'addyosmani.github.io') {
location.href = location.href.replace('addyosmani.github.io/todomvc', 'todomvc.com');
}
}
appendSourceLink();
redirect();
})();
\ No newline at end of file
(function() {
// enyo can use information from the script tag that loads this bootstrap file
var thisScript = 'enyo.js';
enyo = window.enyo || {};
enyo.locateScript = function(inName) {
var scripts = document.getElementsByTagName('script');
for (var i=scripts.length-1, s, src, l=inName.length; (i>=0) && (s=scripts[i]); i--) {
if (!s.located) {
src = s.getAttribute('src') || '';
if (src.slice(-l) == inName) {
s.located = true;
return {path: src.slice(0, Math.max(0, src.lastIndexOf('/'))), node: s};
}
}
}
};
enyo.args = enyo.args || {};
var tag = enyo.locateScript(thisScript);
if (tag) {
// infer the framework path from the document, unless the user has specified one explicitly
enyo.args.root = (enyo.args.root || tag.path);
// all attributes of the bootstrap script tag become enyo.args
for (var i=0, al = tag.node.attributes.length, it; (i < al) && (it = tag.node.attributes.item(i)); i++) {
enyo.args[it.nodeName] = it.value;
}
}
var root = enyo.args.root;
var script = function(inSrc) {
document.write('<scri' + 'pt src="' + root + "/source/boot/" + inSrc + '"></scri' + 'pt>');
};
script('ready.js');
script('../../loader.js');
script('boot.js');
script('../package.js');
})();
(function() {
enyo = window.enyo || {};
enyo.pathResolverFactory = function() {
this.paths = {};
};
enyo.pathResolverFactory.prototype = {
addPath: function(inName, inPath) {
return this.paths[inName] = inPath;
},
addPaths: function(inPaths) {
if (inPaths) {
for (var n in inPaths) {
this.addPath(n, inPaths[n]);
}
}
},
includeTrailingSlash: function(inPath) {
return (inPath && inPath.slice(-1) !== "/") ? inPath + "/" : inPath;
},
// match $name
rewritePattern: /\$([^\/\\]*)(\/)?/g,
// replace macros of the form $pathname with the mapped value of paths.pathname
rewrite: function (inPath) {
var working, its = this.includeTrailingSlash, paths = this.paths;
var fn = function(macro, name) {
working = true;
return its(paths[name]) || '';
};
var result = inPath;
do {
working = false;
result = result.replace(this.rewritePattern, fn);
} while (working);
return result;
}
};
enyo.path = new enyo.pathResolverFactory();
enyo.loaderFactory = function(inMachine, inPathResolver) {
this.machine = inMachine;
// package information
this.packages = [];
// module information
this.modules = [];
// stylesheet paths
this.sheets = [];
// designer metadata paths
this.designs = [];
// (protected) internal dependency stack
this.stack = [];
this.pathResolver = inPathResolver || enyo.path;
this.packageName = "";
this.packageFolder = "";
this.finishCallbacks = {};
};
enyo.loaderFactory.prototype = {
verbose: false,
loadScript: function(inScript, success, failure) {
this.machine.script(inScript, success, failure);
},
loadSheet: function(inSheet) {
this.machine.sheet(inSheet);
},
loadPackage: function(inPackage) {
this.machine.script(inPackage);
},
report: function() {
},
//
load: function(/*<inDependency0, inDependency1 ...>*/) {
// begin processing dependencies
this.more({
index: 0,
depends: arguments || []
});
},
more: function(inBlock) {
// a 'block' is a dependency list with a bookmark
// the bookmark (index) allows us to interrupt
// processing and then continue asynchronously.
if (inBlock) {
// returns true if this block has asynchronous requirements
// in that case, we unwind the stack. The asynchronous loader
// must provide the continuation (by calling 'more' again).
if (this.continueBlock(inBlock)) {
return;
}
}
// A package is now complete. Pop the block that was interrupted for that package (if any).
var block = this.stack.pop();
if (block) {
// propagate failed scripts to queued block
if(enyo.runtimeLoading && inBlock.failed) {
block.failed = block.failed || [];
block.failed.push.apply(block.failed, inBlock.failed);
}
// block.packageName is the name of the package that interrupted us
//this.report("finished package", block.packageName);
if (this.verbose) {
console.groupEnd("* finish package (" + (block.packageName || "anon") + ")");
}
// cache the folder for the currently processing package
this.packageFolder = block.folder;
// no current package
this.packageName = "";
// process this new block
this.more(block);
} else {
this.finish(inBlock);
}
},
finish: function(inBlock) {
this.packageFolder = "";
if (this.verbose) {
console.log("-------------- fini");
}
for (var i in this.finishCallbacks) {
if (this.finishCallbacks[i]) {
this.finishCallbacks[i](inBlock);
this.finishCallbacks[i] = null;
}
}
},
continueBlock: function(inBlock) {
while (inBlock.index < inBlock.depends.length) {
var d = inBlock.depends[inBlock.index++];
if (d) {
if (typeof d == "string") {
if (this.require(d, inBlock)) {
// return true to indicate we need to interrupt
// processing until asynchronous file load completes
// the load process itself must provide the
// continuation
return true;
}
} else {
this.pathResolver.addPaths(d);
}
}
}
},
require: function(inPath, inBlock) {
// process aliases
var path = this.pathResolver.rewrite(inPath);
// get path root
var prefix = this.getPathPrefix(inPath);
// assemble path
path = prefix + path;
// process path
if ((path.slice(-4) == ".css") || (path.slice(-5) == ".less")) {
if (this.verbose) {
console.log("+ stylesheet: [" + prefix + "][" + inPath + "]");
}
this.requireStylesheet(path);
} else if (path.slice(-3) == ".js" && path.slice(-10) != "package.js") {
if (this.verbose) {
console.log("+ module: [" + prefix + "][" + inPath + "]");
}
return this.requireScript(inPath, path, inBlock);
} else if (path.slice(-7) == ".design") {
if (this.verbose) {
console.log("+ design metadata: [" + prefix + "][" + inPath + "]");
}
this.requireDesign(path);
} else {
// package
this.requirePackage(path, inBlock);
// return true to indicate a package was located and
// we need to interrupt further processing until it's completed
return true;
}
},
getPathPrefix: function(inPath) {
var delim = inPath.slice(0, 1);
if ((delim != "/") && (delim != "\\") && (delim != "$") && !/^https?:/i.test(inPath)) {
return this.packageFolder;
}
return "";
},
requireStylesheet: function(inPath) {
// stylesheet
this.sheets.push(inPath);
this.loadSheet(inPath);
},
requireScript: function(inRawPath, inPath, inBlock) {
// script file
this.modules.push({
packageName: this.packageName,
rawPath: inRawPath,
path: inPath
});
if(enyo.runtimeLoading) {
var _this = this;
var success = function() {
_this.more(inBlock);
};
var failure = function() {
inBlock.failed = inBlock.failed || [];
inBlock.failed.push(inPath);
_this.more(inBlock);
}
this.loadScript(inPath, success, failure);
} else {
this.loadScript(inPath);
}
return enyo.runtimeLoading;
},
requireDesign: function(inPath) {
// designer metadata (no loading here)
this.designs.push({
packageName: this.packageName,
path: inPath
});
},
decodePackagePath: function(inPath) {
// A package path can be encoded in two ways:
//
// 1. [folder]
// 2. [folder]/[*package.js]
//
// Note: manifest file name must end in "package.js"
//
var alias = '', target = '', folder = '', manifest = 'package.js';
// convert back slashes to forward slashes, remove double slashes, split on slash
var parts = inPath.replace(/\\/g, "/").replace(/\/\//g, "/").replace(/:\//, "://").split("/");
var i, p;
if (parts.length) {
// if inPath has a trailing slash, parts has an empty string which we pop off and ignore
var name = parts.pop() || parts.pop() || "";
// test if name includes the manifest tag
if (name.slice(-manifest.length) !== manifest) {
// if not a manifest name, it's part of the folder path
parts.push(name);
} else {
// otherwise this is the manifest name
manifest = name;
}
//
folder = parts.join("/");
folder = (folder ? folder + "/" : "");
manifest = folder + manifest;
//
// build friendly aliasing:
//
for (i=parts.length-1; i >= 0; i--) {
if (parts[i] == "source") {
parts.splice(i, 1);
break;
}
}
target = parts.join("/");
//
// portable aliasing:
//
// packages that are rooted at a folder named "enyo" or "lib" do not
// include that root path in their alias
//
// remove */lib or */enyo prefix
//
// e.g. foo/bar/baz/lib/zot -> zot package
//
for (i=parts.length-1; (p=parts[i]); i--) {
if (p == "lib" || p == "enyo") {
parts = parts.slice(i+1);
break;
}
}
// remove ".." and "."
for (i=parts.length-1; (p=parts[i]); i--) {
if (p == ".." || p == ".") {
parts.splice(i, 1);
}
}
//
alias = parts.join("-");
}
return {
alias: alias,
target: target,
folder: folder,
manifest: manifest
};
},
aliasPackage: function(inPath) {
var parts = this.decodePackagePath(inPath);
// cache manifest path
this.manifest = parts.manifest;
// cache package info for named packages
if (parts.alias) {
// debug only
/*
var old = this.pathResolver.paths[parts.name];
if (old && old != parts.folder) {
this.verbose && console.warn("mapping alias [" + parts.name + "] to [" + parts.folder + "] replacing [" + old + "]");
}
this.verbose && console.log("mapping alias [" + parts.name + "] to [" + parts.folder + "]");
*/
//
// create a path alias for this package
this.pathResolver.addPath(parts.alias, parts.target);
//
// cache current name
this.packageName = parts.alias;
// cache package information
this.packages.push({
name: parts.alias,
folder: parts.folder
});
}
// cache current folder
this.packageFolder = parts.folder;
},
requirePackage: function(inPath, inBlock) {
// cache the interrupted packageFolder
inBlock.folder = this.packageFolder;
this.aliasPackage(inPath);
// cache the name of the package 'inBlock' is loading now
inBlock.packageName = this.packageName;
// push inBlock on the continuation stack
this.stack.push(inBlock);
// console/user reporting
this.report("loading package", this.packageName);
if (this.verbose) {
console.group("* start package [" + this.packageName + "]");
}
// load the actual package. the package MUST call a continuation function
// or the process will halt.
this.loadPackage(this.manifest);
}
};
})();
// Used when a certain platform restricts functionality due to security
enyo.execUnsafeLocalFunction = function(e) {
// Querying {MSApp} object - Windows 8
if (typeof MSApp === "undefined") {
e();
}
else {
MSApp.execUnsafeLocalFunction(e);
}
};
// machine for a loader instance
enyo.machine = {
sheet: function(inPath) {
var type = "text/css";
var rel = "stylesheet";
var isLess = (inPath.slice(-5) == ".less");
if (isLess) {
if (window.less) {
// If client-side less is loaded, insert the less stylesheet
type = "text/less";
rel = "stylesheet/less";
} else {
// Otherwise, we expect a css file of the same name to exist
inPath = inPath.slice(0, inPath.length-4) + "css";
}
}
var link;
if (enyo.runtimeLoading || isLess) {
link = document.createElement('link');
link.href = inPath;
link.media = "screen";
link.rel = rel;
link.type = type;
document.getElementsByTagName('head')[0].appendChild(link);
} else {
link = function() {
document.write('<link href="' + inPath + '" media="screen" rel="' + rel + '" type="' + type + '" />');
};
enyo.execUnsafeLocalFunction(link);
}
if (isLess && window.less) {
less.sheets.push(link);
if (!enyo.loader.finishCallbacks.lessRefresh) {
enyo.loader.finishCallbacks.lessRefresh = function() {
less.refresh(true);
};
}
}
},
script: function(inSrc, onLoad, onError) {
if (!enyo.runtimeLoading) {
document.write('<scri' + 'pt src="' + inSrc + '"' + (onLoad ? ' onload="' + onLoad + '"' : '') + (onError ? ' onerror="' + onError + '"' : '') + '></scri' + 'pt>');
} else {
var script = document.createElement('script');
script.src = inSrc;
script.onload = onLoad;
script.onerror = onError;
document.getElementsByTagName('head')[0].appendChild(script);
}
},
inject: function(inCode) {
document.write('<scri' + 'pt type="text/javascript">' + inCode + "</scri" + "pt>");
}
};
// create a dependency processor using our script machine
enyo.loader = new enyo.loaderFactory(enyo.machine);
// dependency API uses enyo loader
enyo.depends = function() {
var ldr = enyo.loader;
if (!ldr.packageFolder) {
var tag = enyo.locateScript("package.js");
if (tag && tag.path) {
ldr.aliasPackage(tag.path);
ldr.packageFolder = tag.path + "/";
//console.log("detected PACKAGEFOLDER [" + ldr.packageFolder + "]");
}
}
ldr.load.apply(ldr, arguments);
};
// Runtime loader
// Usage: enyo.load(depends, [onLoadCallback])
// where - depends is string or array of string paths to package.js, script, or css to load
// - doneCallback is fired after file or package loading has completed
// Only one file/package is loaded at a time; additional calls are queued and loading deferred
(function() {
var enyo = window.enyo;
var runtimeLoadQueue = [];
enyo.load = function(depends, onLoadCallback) {
runtimeLoadQueue.push(arguments);
if (!enyo.runtimeLoading) {
enyo.runtimeLoading = true;
runtimeLoad();
}
};
function runtimeLoad(onLoad) {
if (onLoad) {
onLoad(); // Run user callback function
}
if (runtimeLoadQueue.length) {
var args = runtimeLoadQueue.shift();
var depends = args[0];
var dependsArg = enyo.isArray(depends) ? depends : [depends];
var onLoadCallback = args[1];
enyo.loader.finishCallbacks.runtimeLoader = function(inBlock) {
// Once loader is done loading a package, we chain a call to runtimeLoad(),
// which will call the onLoadCallback from the original load call, passing
// a reference to the depends argument from the original call for tracking,
// followed by kicking off any additionally queued load() calls
runtimeLoad(function() {
if (onLoadCallback) {
onLoadCallback(inBlock);
}
});
};
enyo.loader.packageFolder = "./";
// Kick off next queued call to loader
enyo.depends.apply(this, dependsArg);
} else {
enyo.runtimeLoading = false;
enyo.loader.packageFolder = "";
}
}
})();
// predefined path aliases
enyo.path.addPaths({
enyo: enyo.args.root,
lib: "$enyo/../lib"
});
\ No newline at end of file
/* global enyo: false */
enyo.depends(
"enyo.js",
"ready.js",
"../../loader.js",
"boot.js"
);
(function (scope) {
// we need to register appropriately to know when
// the document is officially ready to ensure
// client code is only going to execute at the
// appropriate time
var doc = scope.document;
var queue = [];
var ready = ("complete" === doc.readyState);
var run;
var init;
var remove;
var add;
var flush;
enyo.ready = function (fn, context) {
if (ready) run(fn, context);
else queue.push([fn, context]);
};
run = function (fn, context) {
fn.call(context || enyo.global);
};
init = function (event) {
// if we're interactive it should be safe to move
// forward because the content has been parsed
if ((ready = ("interactive" === doc.readyState))) {
if (!~["DOMContentLoaded", "readystatechange"].indexOf(event.type)) {
remove(event.type, init);
flush();
}
}
// for an IE8 fallback and assurance
if ((ready = ("complete" === doc.readyState))) {
remove(event.type, init);
flush();
}
};
add = function (event, fn) {
var name = doc.addEventListener? "addEventListener": "attachEvent";
var on = name === "attachEvent"? "on": "";
doc[name](on + event, fn, false);
};
remove = function (event, fn) {
var name = doc.addEventListener? "removeEventListener": "detachEvent";
var on = name === "detachEvent"? "on": "";
doc[name](on + event, fn, false);
};
flush = function () {
if (ready && queue.length) {
while (queue.length) {
run.apply(scope, queue.shift());
}
}
};
// ok, lets hook this nonsense up
add("DOMContentLoaded", init);
add("readystatechange", init);
})(window);
/* global enyo: false */
enyo.depends(
'$lib'
);
\ No newline at end of file
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Enyo + Backbone.js • TodoMVC</title>
<link rel="stylesheet" href="components/todomvc-common/base.css">
<script src="components/todomvc-common/base.js"></script>
<!-- Enyo comes with a build and deploy process that will minify and concatenate your files into 2 js files. One for enyo, and one for app code. For the purpose of demonstration, I used the debug loading library to handle the dependency management. The loader nagivates directories for package.js files and injects files they list into the DOM. -->
<script src="enyo/enyo.js"></script>
<script src="js/package.js"></script>
</head>
<body class="enyo-unselectable"></body>
</html>
\ No newline at end of file
/*jshint strict:false*/
/*global enyo:false*/
// Base component. This is the app that holds everything. An app can be as big as the whole app or as big as a single module.
enyo.kind({
name: 'ToDo.Application',
kind: 'enyo.Application',
view: 'ToDo.WindowView',
// start up the controllers. By giving them names and starting them at the app level, the instances become global singletons.
controllers: [{
name: 'ToDo.notepadcontroller',
kind: 'ToDo.NotepadController'
}, {
name: 'ToDo.routes',
kind: 'ToDo.Routes'
}]
});
\ No newline at end of file
/*global enyo:false*/
enyo.depends(
'app.js'
);
\ No newline at end of file
/*jshint strict:false*/
/*global enyo:false, ToDo:false, Backbone: false*/
enyo.ready(function () {
ToDo.TaskCollection = Backbone.Collection.extend({
localStorage: new Backbone.LocalStorage('todos-enyo'),
model: ToDo.TaskModel
});
});
\ No newline at end of file
/*global enyo:false*/
enyo.depends(
'TaskCollection.js'
);
\ No newline at end of file
/*jshint strict:false*/
/*global enyo:false, $:false*/
/*exported ENTER_KEY, ESC_KEY*/
// This is based on Enyo 2.1.1. The next version (2.3) of Enyo will more tightly integrate MVC and should require less custom code.
var ENTER_KEY = 13;
var ESC_KEY = 27;
// The global controller for this app.
enyo.kind({
name: 'ToDo.NotepadController',
// Tells enyo this is a subclass of CollectionController and should inherit it's properties/methods
kind: 'enyo.CollectionController',
// Bind to this collection
collection: 'ToDo.TaskCollection',
// Automatically load the collection into the controller without a custom load function
autoLoad: true,
// Enyo automatically creates get/set and an event listener for published properties
published: {
route: ''
},
// These are the events this controller will listen for, and what function to call when they occur
handlers: {
onSaveNew: 'saveNew',
onDeleteTask: 'deleteTask',
onMarkComplete: 'toggleComplete',
onMarkAllComplete: 'toggleAllComplete',
onStartEdit: 'showEdit',
onSendEsc: 'clearEdit',
onblur: 'leaveField',
onClearCompleted: 'clearCompleted',
onReload: 'loadList'
},
// Clear all completed tasks
clearCompleted: function () {
var completedTasks;
// reset the collection stored inside the controller, in case the collection has been filtered
this.releaseCollection();
this.load();
// Collect all completed tasks from the collection, and destroy them
completedTasks = this.collection.where({
completed: true
});
completedTasks.forEach(function (task) {
task.destroy();
});
// reset the display
this.bubble('onReload');
},
routeChanged: function () {
// reset the display
this.bubble('onReload');
},
leaveField: function (inSender, inEvent) {
// All enyo events are provided a hook to the sender, and the event that spawned it
// The inEvent object has a pointer to the enyo object that first started the process
// InSender is more refelective of where the event came from
// Bubbling will send an event up the enyo object hierarchy so parent object along the way can get this event
var val = inEvent.originator.getValue();
// Make sure the sender has a valid controller to modify
if (inSender.controller) {
if (!val || val.trim() === '') {
// Bubble up the delete event
this.bubble('onDeleteTask', inEvent, inSender);
} else {
// update the data
// Each row is given its own controller with a direct link to the row's representative model
// Avoids having to navigate to the parent controller directly
inSender.controller.data.attributes.title = val.trim();
inSender.controller.data.save();
}
// in this case, the sender is the table row, so we remove the class from the sender
// the inEvent.originator is the field
inSender.removeClass('editing');
this.bubble('onReload');
}
return true;
},
showEdit: function (inSender) {
// in this case, the sender is the table row, so we remove the class from the sender
inSender.addClass('editing');
inSender.$.inputField.focus();
if (window.getSelection().rangeCount) {
window.getSelection().collapseToEnd();
}
return true;
},
saveNew: function (inSender, inEvent) {
var val = inEvent.originator.getValue().trim();
if (val !== '') {
this.collection.create({
title: val
});
inEvent.originator.setValue('');
}
// reload the display
this.bubble('onReload');
return true;
},
deleteTask: function (inSender) {
inSender.controller.model().destroy();
// reload the display
this.bubble('onReload');
return true;
},
toggleComplete: function (inSender) {
// toggle the completed attribute
inSender.controller.data.attributes.completed = !inSender.controller.data.attributes.completed;
inSender.controller.data.save();
// reload the display
this.bubble('onReload');
return true;
},
toggleAllComplete: function () {
// Find out which way the toggle all is flipped
var completed = enyo.$['toggle-all'].getAttribute('checked') ? true : false;
// enyo stores all its objects at the top, and each object knows where to point in the DOM
// No need to find something in the DOM, just grab the top level enyo object that represents it
// toggle all children of the todo list
this.releaseCollection();
this.load();
this.collection.models.forEach(function (child) {
child.attributes.completed = !completed;
child.save();
});
// reload the display
this.bubble('onReload');
return true;
},
clearEdit: function (inSender) {
inSender.removeClass('editing');
this.bubble('onReload');
return true;
},
loadList: function () {
var checked;
var falseTasks; // array of active tasks
var completedTasks; // array of completed tasks
var length; // length of all tasks
var todoList; // DOM representation of the todo list
// if we have created the todo-list in the enyo hierarchy
if (enyo.$['todo-list']) {
todoList = enyo.$['todo-list'];
$('#filters a').removeClass('selected');
// reset the collection being used by the controller
this.releaseCollection();
this.load();
length = this.length;
// Grab an array of false tasks and completed tasks
falseTasks = this.collection.where({
completed: false
});
completedTasks = this.collection.where({
completed: true
});
// set the appropriate filter link css, then update the collection to the correct models array
if (this.route === '/completed') {
$('#tagComplete').addClass('selected');
this.update(completedTasks);
} else if (this.route === '/active') {
$('#tagActive').addClass('selected');
this.update(falseTasks);
} else {
$('#tagAll').addClass('selected');
}
// hide or show based on number of total tasks
if (length === 0) {
enyo.$.main.hide();
enyo.$.footer.hide();
} else {
enyo.$.main.show();
enyo.$.footer.show();
// toggle the all button
if (falseTasks.length === 0) {
$('#toggle-all').attr('checked', true);
} else {
$('#toggle-all').removeAttr('checked');
}
// update the task counter
enyo.$['count-number'].setContent(falseTasks.length);
if (falseTasks.length === 1) {
enyo.$.countText.setContent(' item left');
} else {
enyo.$.countText.setContent(' items left');
}
// update the clear completed button
if (completedTasks.length === 0) {
enyo.$['clear-completed'].hide();
enyo.$['clear-completed'].setContent('');
} else {
enyo.$['clear-completed'].show();
enyo.$['clear-completed'].setContent('Clear completed (' + completedTasks.length + ')');
}
}
// set the correct ui representation for each task. Use the top level todo-list object to navigate down the rows
enyo.$['todo-list'].children.forEach(function (child) {
checked = child.controller.data.attributes.completed;
if (checked) {
child.addClass('completed');
$(child.$.checkbox.hasNode()).attr('checked', true);
} else {
child.removeClass('completed');
$(child.$.checkbox.hasNode()).removeAttr('checked', false);
}
});
}
}
});
\ No newline at end of file
/*jshint strict:false*/
/*global enyo:false, ToDo:false*/
enyo.kind({
kind: 'enyo.Router',
name: 'ToDo.Routes',
// These are the routes to listen for, and the function to call when they occur. A separate context can be provided but is unnecessary here.
routes: [{
path: '/active',
handler: 'changeCollection'
}, {
path: '/completed',
handler: 'changeCollection'
}, {
path: '/',
'default': true,
handler: 'changeCollection'
}],
// Set a property on our global controller
changeCollection: function () {
ToDo.notepadcontroller.setRoute(this.current);
}
});
\ No newline at end of file
/*global enyo:false*/
enyo.depends(
'NotepadController.js',
'Routes.js'
);
\ No newline at end of file
/*jshint strict:false*/
/*global enyo:false, ToDo:false, Backbone:false*/
enyo.ready(function () {
ToDo.TaskModel = Backbone.Model.extend({
defaults: {
title: '',
completed: false
}
});
});
\ No newline at end of file
/*global enyo:false*/
enyo.depends(
'TaskModel.js'
);
\ No newline at end of file
/*global enyo:false*/
enyo.depends(
'controllers',
'models',
'collections',
'views',
'apps',
'start.js'
);
\ No newline at end of file
/*jshint strict:false*/
/*global enyo:false, ToDo:false*/
// Once everything is loaded through enyo's dependency management, start the app
enyo.ready(function () {
window.app = new ToDo.Application();
window.app.render();
});
\ No newline at end of file
/*jshint strict:false*/
/*global enyo:false*/
// The footer section
enyo.kind({
name: 'ToDo.FooterView',
id: 'info',
tag: 'footer',
components: [{
tag: 'p',
content: 'Double-click to edit a todo'
}, {
tag: 'p',
components: [{
tag: 'span',
content: 'Written by: '
}, {
tag: 'a',
attributes: {
href: 'http://randomjavascript.blogspot.com/'
},
content: 'David Posin'
}]
}, {
tag: 'p',
allowHtml: true,
content: 'Part of <a href="http://todomvc.com">TodoMVC</a>'
}]
});
\ No newline at end of file
/*jshint strict:false*/
/*global enyo:false*/
enyo.kind({
name: 'ToDo.NotepadFooterView',
tag: 'footer',
id: 'footer',
showing: false,
components: [{
tag: 'span',
id: 'todo-count',
components: [{
name: 'count',
id: 'count-number',
tag: 'strong'
}, {
name: 'countText',
id: 'countText',
tag: 'span'
}]
}, {
tag: 'ul',
id: 'filters',
components: [{
tag: 'li',
components: [{
tag: 'a',
id: 'tagAll',
attributes: {
class: 'selected',
href: '#/'
},
content: 'All'
}]
}, {
tag: 'li',
components: [{
tag: 'a',
id: 'tagActive',
attributes: {
href: '#/active'
},
content: 'Active'
}]
}, {
tag: 'li',
components: [{
tag: 'a',
id: 'tagComplete',
attributes: {
href: '#/completed'
},
content: 'Completed'
}]
}]
}, {
kind: 'enyo.Button',
name: 'clear-completed',
id: 'clear-completed',
controller: 'ToDo.notepadcontroller',
showing: false,
handlers: {
ontap: 'clearCompleted'
},
clearCompleted: function (inSender, inEvent) {
this.bubble('onClearCompleted');
inEvent.preventDefault();
return true;
}
}]
});
\ No newline at end of file
/*jshint strict:false*/
/*global enyo:false, ENTER_KEY:false*/
// Header section for adding a new task.
enyo.kind({
name: 'ToDo.NotepadHeaderView',
tag: 'header',
id: 'header',
controller: 'ToDo.notepadcontroller',
components: [{
tag: 'h1',
content: 'todos'
}, {
tag: 'form',
id: 'todo-form',
components: [{
// instead of letting the event bubble up the DOM, let's stop it here and send a custom event name
// up the enyo object hierarchy to our controller
kind: 'enyo.Input',
id: 'new-todo',
placeholder: 'What needs to be done?',
handlers: {
onkeypress: 'saveNew'
},
saveNew: function (inSender, inEvent) {
if (inEvent.keyCode === ENTER_KEY) {
this.bubble('onSaveNew');
inEvent.preventDefault();
}
}
}]
}]
});
\ No newline at end of file
/*jshint strict:false*/
/*global enyo:false, ENTER_KEY:false, ESC_KEY:false*/
// The main task list view
enyo.kind({
name: 'ToDo.NotepadMainView',
tag: 'section', // give it a specific html tagS
id: 'main', // force an id value, otherwise enyo makes its own
focus: false,
showing: false, // hide initially
controller: 'ToDo.notepadcontroller', // connect it to the global controller
components: [{
// create the checkbox, and have it bubble markAllComplete events, not clicks.
// We don't have to listen for all clicks and sort through them, this click gives a specific event
tag: 'input',
id: 'toggle-all',
attributes: {
type: 'checkbox'
},
handlers: {
onclick: 'markAllComplete'
},
markAllComplete: function () {
// the event bubbles up the enyo object instance hierarchy, not the DOM hierarchy
this.bubble('onMarkAllComplete');
return true;
}
}, {
tag: 'label',
id: 'toggle-all-label',
content: 'Mark all as complete',
attributes: {
'for': 'toggle-all'
}
}, {
// the collection repeater automatically creates rows for us based on the tasks in the controller's collection
// each row is given its own controller directly linked to its specific model
// name: todo-list gives us a top level enyo object shortcut to grab the table
kind: 'enyo.CollectionRepeater',
controller: 'ToDo.notepadcontroller',
tag: 'ul',
name: 'todo-list',
id: 'todo-list',
// what the table is made of
components: [{
tag: 'li',
// create an automatic binding so the text and input fields automatically display content without custom code
// the next release of Enyo will better support two way communication so the rows can update the models
bindings: [{
from: '.controller.title',
to: '$.textLabel.content'
}, {
from: '.controller.title',
to: '$.inputField.value',
oneWay: false
}],
// what each row is made of
components: [{
tag: 'div',
name: 'taskRow',
attributes: {
class: 'view'
},
components: [{
tag: 'input',
handlers: {
onclick: 'markComplete'
},
name: 'checkbox',
attributes: {
type: 'checkbox',
class: 'toggle'
},
markComplete: function () {
this.inherited(arguments);
this.bubble('onMarkComplete');
return true;
}
}, {
tag: 'label',
name: 'textLabel',
handlers: {
ondblclick: 'throwEdit'
},
throwEdit: function () {
this.inherited(arguments);
this.bubble('onStartEdit', parent.$.inputField);
return true;
}
}, {
kind: 'enyo.Button',
handlers: {
ontap: 'deleteTask'
},
attributes: {
class: 'destroy'
},
deleteTask: function (inSender, inEvent) {
this.bubbleUp('onDeleteTask', inSender, inEvent);
return true;
}
}]
}, {
kind: 'enyo.Input',
name: 'inputField',
type: 'text',
defaultFocus: true,
attributes: {
class: 'edit',
onblur: enyo.bubbler
},
handlers: {
onkeypress: 'sendEnter',
onkeydown: 'sendEsc'
},
sendEnter: function (inSender, inEvent) {
if (inEvent.keyCode === ENTER_KEY) {
this.bubble('onblur');
inEvent.preventDefault();
}
return true;
},
sendEsc: function (inSender, inEvent) {
if (inEvent.keyCode === ESC_KEY) {
this.bubble('onSendEsc');
}
return true;
}
}]
}],
// when the table is rendered in the dom, then build its contents
rendered: function () {
this.bubble('onReload');
}
}]
});
\ No newline at end of file
/*jshint strict:false*/
/*global enyo:false*/
// This is the notepad area
enyo.kind({
name: 'ToDo.NotepadView',
id: 'todoapp',
tag: 'section',
// Break the notepad into three easily handled components by purpose.
components: [{
name: 'ToDo.notepadviewheader',
kind: 'ToDo.NotepadHeaderView'
}, {
name: 'ToDo.notepadviewmain',
kind: 'ToDo.NotepadMainView'
}, {
name: 'ToDo.notepadviewfooter',
kind: 'ToDo.NotepadFooterView'
}]
});
\ No newline at end of file
/*jshint strict:false*/
/*global enyo:false*/
// Top level window
enyo.kind({
name: 'ToDo.WindowView',
tag: 'body',
fit: false, // Tell enyo not to manage screen size for us. Fit true would tell enyo to adjust sizes to match the screen in a clean fashion.
// Have 2 components divided by purpose
components: [{
name: 'ToDo.notepadview',
kind: 'ToDo.NotepadView'
}, {
name: 'ToDo.footerview',
kind: 'ToDo.FooterView'
}]
});
\ No newline at end of file
/*global enyo:false*/
enyo.depends(
'NotepadMainView.js',
'NotepadHeaderView.js',
'NotepadFooterView.js',
'FooterView.js',
'NotepadView.js',
'WindowView.js'
);
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
/* global enyo: false */
enyo.depends(
'enyo.js',
'app.js'
);
\ 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