Commit a0b75f7b authored by James Thomas's avatar James Thomas

Merge pull request #5 from asudoh/dojo18

Incorporating 1.8 version of dojox/mvc features
parents d325281c 2bebace2
......@@ -86,3 +86,10 @@
.inline_edit[style~='0;'] ~ .toggle {
visibility: hidden !important;
}
.dijitOffScreen { /* For 1.8 in-line edit box */
position: absolute !important;
left: 50% !important;
top: -10000px !important;
}
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Dojo • TodoMVC</title>
<link rel="stylesheet" href="../../assets/base.css">
<!-- CSS overrides - remove if you don't need it -->
<link rel="stylesheet" href="css/app.css">
<!--[if IE]>
<script src="../../assets/ie.js"></script>
<![endif]-->
</head>
<body>
<section id="todoapp" data-dojo-type="todo.app"></section>
<footer id="info">
<p>Double-click to edit a todo</p>
<p>Created by <a href="http://jamesthom.as/">James Thomas</a> and <a href="https://github.com/edchat">Ed Chatelain</a></p>
</footer>
<script src="../../assets/base.js"></script>
<script data-dojo-config="async:true, parseOnLoad:true, locale:'en', paths:{'todo':'../todo/'}, deps:['dojo/parser', 'todo/app']" src="js/lib/dojo/dojo.js"></script>
</body>
</html>
<!doctype html>
<!--
This is a Dojo version of TodoMVC using uncompressed Dojo source.
To use this, place todomvc directory at the directory containing dojo/dijit/dojox.
-->
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Dojo • TodoMVC</title>
<link rel="stylesheet" href="../../assets/base.css">
<!-- CSS overrides - remove if you don't need it -->
<link rel="stylesheet" href="css/app.css">
<!--[if IE]>
<script>
nativeDate = Date;
</script>
<script src="../../assets/ie.js"></script>
<script>
(function(nativeDate){
// It appears that the modification to Date constructor done by ie.js negatively affects new Date("X"), ending up a JS error.
// Using the native Date constructor for that case.
var origDate = Date;
Date = function(){
return (arguments.length == 1 && typeof arguments[0] == "string" ? nativeDate : origDate).apply(this, [].slice.call(arguments, 0));
};
Date.prototype = new origDate();
})(nativeDate);
</script>
<![endif]-->
</head>
<body>
<section id="todoapp" data-dojo-type="todo/app18"></section>
<footer id="info">
<p>Double-click to edit a todo</p>
<p>Created by <a href="http://jamesthom.as/">James Thomas</a> and <a href="https://github.com/edchat">Ed Chatelain</a></p>
</footer>
<script src="../../assets/base.js"></script>
<script>
require = {
async: true,
parseOnLoad: true,
locale: "en",
paths: {
"todo": "../todomvc/architecture-examples/dojo/js/todo"
},
deps: ["dojo/parser", "dojo/domReady!"],
mvc: {debugBindings: true}
};
</script>
<script src="../../../dojo/dojo.js"></script>
</body>
</html>
......@@ -8,17 +8,43 @@
<!-- CSS overrides - remove if you don't need it -->
<link rel="stylesheet" href="css/app.css">
<!--[if IE]>
<script>
nativeDate = Date;
</script>
<script src="../../assets/ie.js"></script>
<script>
(function(nativeDate){
// It appears that the modification to Date constructor done by ie.js negatively affects new Date("X"), ending up a JS error.
// Using the native Date constructor for that case.
var origDate = Date;
Date = function(){
return (arguments.length == 1 && typeof arguments[0] == "string" ? nativeDate : origDate).apply(this, [].slice.call(arguments, 0));
};
Date.prototype = new origDate();
})(nativeDate);
</script>
<![endif]-->
</head>
<body>
<section id="todoapp" data-dojo-type="todo.app"></section>
<section id="todoapp" data-dojo-type="todo/app18"></section>
<footer id="info">
<p>Double-click to edit a todo</p>
<p>Created by <a href="http://jamesthom.as/">James Thomas</a> and <a href="https://github.com/edchat">Ed Chatelain</a></p>
</footer>
<script src="../../assets/base.js"></script>
<script data-dojo-config="async:true, parseOnLoad:true, locale:'en', paths:{'todo':'../todo/'}, deps:['dojo/parser', 'todo/app']" src="js/lib/dojo/dojo.js"></script>
<script>
require = {
async: true,
parseOnLoad: true,
locale: "en",
paths: {
"dijit": "../dijit-1.8"
},
deps: ["dojo/parser", "dojo/domReady!"],
mvc: {debugBindings: true}
};
</script>
<script src="js/lib/dojo-1.8/dojo.js"></script>
</body>
</html>
define("dijit/nls/common", { root:
//begin v1.x content
({
buttonOk: "OK",
buttonCancel: "Cancel",
buttonSave: "Save",
itemClose: "Close"
})
//end v1.x content
,
"zh": true,
"zh-tw": true,
"tr": true,
"th": true,
"sv": true,
"sl": true,
"sk": true,
"ru": true,
"ro": true,
"pt": true,
"pt-pt": true,
"pl": true,
"nl": true,
"nb": true,
"ko": true,
"kk": true,
"ja": true,
"it": true,
"hu": true,
"hr": true,
"he": true,
"fr": true,
"fi": true,
"es": true,
"el": true,
"de": true,
"da": true,
"cs": true,
"ca": true,
"az": true,
"ar": true
});
define(
//begin v1.x content
{
"decimal": ".",
"group": ",",
"list": ";",
"percentSign": "%",
"plusSign": "+",
"minusSign": "-",
"exponential": "E",
"perMille": "",
"infinity": "",
"nan": "NaN",
"decimalFormat": "#,##0.###",
"decimalFormat-short": "000T",
"scientificFormat": "#E0",
"percentFormat": "#,##0%",
"currencyFormat": "¤#,##0.00;(¤#,##0.00)"
}
//end v1.x content
);
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
define('dojo/nls/dojo_ROOT',{
'dijit/nls/common':{"buttonOk":"OK","buttonCancel":"Cancel","buttonSave":"Save","itemClose":"Close"}
});
\ No newline at end of file
define([
"dojo/_base/array",
"dojo/_base/declare",
"dojo/_base/lang",
"dojo/dom-class",
"dijit/_WidgetBase"
], function(array, declare, lang, domClass, _WidgetBase){
return declare(_WidgetBase, {
// summary:
// Widget supporting widget attributes with classExists type.
// classExists type allows boolean value of an attribute to reflect existence of a CSS class in a DOM node in the widget.
// example:
// In this example, the text will be bold when the check box is checked.
// | <html>
// | <head>
// | <script src="/path/to/dojo-toolkit/dojo/dojo.js" type="text/javascript"
// | data-dojo-config="parseOnLoad: 1,
// | packages: [{name: 'todo', location: '/path/to/todo-package'}],
// | deps: ['dojo/parser', 'dojo/domReady!']"></script>
// | <style type="text/css">
// | .boldText {
// | font-weight: bold;
// | }
// | </style>
// | </head>
// | <body>
// | <script type="dojo/require">at: "dojox/mvc/at"</script>
// | <input id="checkbox" data-dojo-type="dijit/form/CheckBox">
// | <div data-dojo-type="todo/CssToggleWidget"
// | data-dojo-props="_setBoldAttr: {type: 'classExists', className: 'boldText'},
// | bold: at('widget:checkbox', 'checked')">This text will be bold when above check box is checked.</div>
// | </body>
// | </html>
_attrToDom: function(/*String*/ attr, /*String*/ value, /*Object?*/ commands){
// summary:
// Handle widget attribute with classExists type.
// See dijit/_WidgetBase._attrToDom() for more details.
var callee = arguments.callee;
array.forEach((function(){ return lang.isArray(commands) ? commands.slice(0) : [commands]; })(arguments.length >= 3 ? commands : this.attributeMap[attr]), function(command){
command.type != "classExists" ?
this.inherited("_attrToDom", lang.mixin([attr, value, command], {callee: callee})) :
domClass.toggle(this[command.node || "domNode"], command.className || attr, value);
}, this);
}
});
});
define([
"dojo/_base/declare",
"dijit/_TemplatedMixin",
"dijit/_WidgetsInTemplateMixin",
"dojox/mvc/WidgetList",
"dojox/mvc/_InlineTemplateMixin",
"todo/CssToggleWidget",
"todo/ctrl/_HashCompletedMixin"
], function(declare, _TemplatedMixin, _WidgetsInTemplateMixin, WidgetList, _InlineTemplateMixin, CssToggleWidget, _HashCompletedMixin){
return declare([WidgetList, _InlineTemplateMixin], {
childClz: declare([CssToggleWidget, _TemplatedMixin, _WidgetsInTemplateMixin, _HashCompletedMixin], {
_setCompletedAttr: {type: "classExists", className: "completed"},
_setHiddenAttr: {type: "classExists", className: "hidden"},
onRemoveClick: function(){
this.parent.listCtrl.removeItem(this.uniqueId);
},
onEditBoxChange: function(){
if(!this.editBox.value){
this.parent.listCtrl.removeItem(this.uniqueId);
}
}
})
});
});
<section>
<script type="dojo/require">
at: "dojox/mvc/at",
SimpleTodoModel: "todo/model/SimpleTodoModel",
HashSelectedConverter: "todo/misc/HashSelectedConverter",
LessThanOrEqualToConverter: "todo/misc/LessThanOrEqualToConverter"
</script>
<span data-dojo-id="${id}_routeCtl" data-dojo-type="todo/ctrl/RouteController"></span>
<span data-dojo-id="${id}_localStorage" data-dojo-type="todo/store/LocalStorage"></span>
<span data-dojo-id="${id}_ctrl"
data-dojo-type="todo/ctrl/TodoRefController"
data-dojo-props="defaultId: 'todos-dojo', store: ${id}_localStorage, modelClass: SimpleTodoModel, complete: at('widget:${id}', 'complete')"></span>
<span data-dojo-id="${id}_listCtrl"
data-dojo-type="todo/ctrl/TodoListRefController"
data-dojo-props="model: at(${id}_ctrl, 'todos'), length: at('widget:${id}', 'present').direction(at.to)"></span>
<header id="header">
<h1>todos</h1>
<input id="new-todo" placeholder="What needs to be done?" type="text" autofocus
data-dojo-attach-event="onkeypress: onKeyPressNewItem">
<span class="ui-tooltip-top" style="display:none;">Press Enter to save this task</span>
</header>
<section id="main">
<input id="toggle-all" type="checkbox"
data-dojo-type="dijit/form/CheckBox"
data-dojo-props="checked: at(${id}_ctrl, 'incomplete').transform(LessThanOrEqualToConverter),
onClick: function(e){ ${id}_listCtrl.markAll(e.target.checked); }">
<label for="toggle-all">Mark all as complete</label>
<ul id="todo-list"
data-dojo-type="todo/TodoList"
data-dojo-props="children: at(${id}_listCtrl, 'model'), partialRebuild: true, listCtrl: ${id}_listCtrl"
data-mvc-child-props="uniqueId: at(this.target, 'uniqueId'), completed: at(this.target, 'completed'), hash: at(${id}_routeCtl, 'hash')">
<script type="dojox/mvc/InlineTemplate">
<li>
<div class="view">
<label class="dijitInline inline_edit"
data-dojo-attach-point="editBox"
data-dojo-attach-event="onChange: onEditBoxChange"
data-dojo-type="todo/form/InlineEditBox"
data-dojo-props="value: at('rel:', 'title'), editor: 'dijit/form/TextBox', autosave: true"></label>
<input class="toggle" type="checkbox"
data-dojo-type="dijit/form/CheckBox"
data-dojo-props="checked: at('rel:', 'completed')">
<button class="destroy" data-dojo-attach-event="onclick: onRemoveClick"></button>
</div>
</li>
</script>
</ul>
</section>
<footer id="footer">
<span id="todo-count">
<strong>
<span class="number"
data-dojo-type="dijit/_WidgetBase"
data-dojo-props="_setValueAttr: {type: 'innerText', node: 'domNode'}, value: at(${id}_ctrl, 'incomplete')"></span>
</strong>
<span class="word">item<!--
--><span data-dojo-type="todo/CssToggleWidget"
data-dojo-props="_setSingleAttr: {type: 'classExists', className: 'plural'},
constraints: {lessThanOrEqualTo: 1},
single: at(${id}_ctrl, 'incomplete').transform(LessThanOrEqualToConverter)">s</span></span>
left.
</span>
<!-- Remove this if you don't implement routing -->
<ul id="filters">
<li>
<a href="#/"
data-dojo-type="todo/CssToggleWidget"
data-dojo-props="_setSelectedAttr: {type: 'classExists', className: 'selected'},
selected: at(${id}_routeCtl, 'hash').transform(HashSelectedConverter)">All</a>
</li>
<li>
<a href="#/active"
data-dojo-type="todo/CssToggleWidget"
data-dojo-props="_setSelectedAttr: {type: 'classExists', className: 'selected'},
selected: at(${id}_routeCtl, 'hash').transform(HashSelectedConverter)">Active</a>
</li>
<li>
<a href="#/completed"
data-dojo-type="todo/CssToggleWidget"
data-dojo-props="_setSelectedAttr: {type: 'classExists', className: 'selected'},
selected: at(${id}_routeCtl, 'hash').transform(HashSelectedConverter)">Completed</a>
</li>
</ul>
<button id="clear-completed" onclick="${id}_listCtrl.removeCompletedItems();">
Clear completed (
<span class="number-done"
data-dojo-type="dijit/_WidgetBase"
data-dojo-props="_setValueAttr: {type: 'innerText', node: 'domNode'}, value: at(${id}_ctrl, 'complete')"></span>
)
</button>
</footer>
</section>
define([
"dojo/_base/declare",
"dojo/_base/lang",
"dojo/_base/unload",
"dojo/keys",
"dojo/string",
"dijit/_TemplatedMixin",
"dijit/_WidgetsInTemplateMixin",
"todo/CssToggleWidget",
"dojo/text!./app18.html",
// Below modules are referred in template.
// dijit/_WidgetsInTemplateMixin requires all modules referred in template to have been loaded before it's instantiated.
"dijit/form/CheckBox",
"dijit/form/TextBox",
"dojox/mvc/at",
"todo/TodoList",
"todo/ctrl/RouteController",
"todo/ctrl/TodoListRefController",
"todo/ctrl/TodoRefController",
"todo/form/InlineEditBox",
"todo/misc/HashSelectedConverter",
"todo/misc/LessThanOrEqualToConverter",
"todo/model/SimpleTodoModel",
"todo/store/LocalStorage"
], function(declare, lang, unload, keys, string, _TemplatedMixin, _WidgetsInTemplateMixin, CssToggleWidget, template){
return declare([CssToggleWidget, _TemplatedMixin, _WidgetsInTemplateMixin], {
// summary:
// A widgets-in-template widget that composes the application UI of TodoMVC (Dojo 1.8 version).
// Also, this class inherits todo/CssToggleWidget so that it can react to change in "present"/"complete" attributes and add/remove CSS class to the root DOM node of this widget.
// templateString: String
// The HTML of widget template.
templateString: template,
// _setPresentAttr: Object
// A object used by todo/CssToggleWidget to reflect true/false state of "present" attribute to existence of "todos_present" CSS class in this widget's root DOM.
_setPresentAttr: {type: "classExists", className: "todos_present"},
// _setCompleteAttr: Object
// A object used by todo/CssToggleWidget to reflect true/false state of "complete" attribute to existence of "todos_selected" CSS class in this widget's root DOM.
_setCompleteAttr: {type: "classExists", className: "todos_selected"},
startup: function(){
// summary:
// A function called after the DOM fragment declaring this controller is added to the document.
// See documentation for dijit/_WidgetBase.startup() for more details.
var _self = this;
this.inherited(arguments);
unload.addOnUnload(function(){
_self.destroy(); // When this page is being unloaded, call destroy callbacks of inner-widgets to let them clean up
});
},
onKeyPressNewItem: function(/*DOMEvent*/ event){
// summary:
// Handle key press events for the input field for new todo.
// description:
// If user has pressed enter, add current text value as new todo item in the model.
if(event.keyCode !== keys.ENTER || !string.trim(event.target.value).length){
return; // If the key is not Enter, or the input field is empty, bail
}
lang.getObject(this.id + "_listCtrl").addItem(event.target.value); // Add a todo item with the given text
event.target.value = ""; // Clear the input field
event.preventDefault();
event.stopPropagation();
}
});
});
define([
"dojo/_base/declare",
"dojo/router",
"dijit/Destroyable",
"dojox/mvc/_Controller"
], function(declare, router, Destroyable, _Controller){
return declare([_Controller, Destroyable], {
// summary:
// A controller that maintains hash attribute in sync with location.hash.
// example:
// In this example, the text box is in sync with the URL hash.
// (The change in URL hash is reflected to the text box, and the edit in text box will be reflected to the URL hash)
// | <html>
// | <head>
// | <script src="/path/to/dojo-toolkit/dojo/dojo.js" type="text/javascript"
// | data-dojo-config="parseOnLoad: 1,
// | packages: [{name: 'todo', location: '/path/to/todo-package'}],
// | deps: ['dojo/parser', 'dojo/domReady!']"></script>
// | </head>
// | <body>
// | <script type="dojo/require">at: "dojox/mvc/at"</script>
// | <span id="routeCtrl" data-dojo-type="todo/ctrl/RouteController"></span>
// | <input data-dojo-type="dijit/form/TextBox"
// | data-dojo-props="value: at('widget:routeCtrl', 'hash')">
// | </body>
// | </html>
postscript: function(/*Object?*/ params, /*DOMNode?*/ srcNodeRef){
// summary:
// Kicks off instantiation of this controller, in a similar manner as dijit/_WidgetBase.postscript().
// params: Object?
// The optional parameters for this controller.
// srcNodeRef: DOMNode?
// The DOM node declaring this controller. Set if this controller is created via Dojo parser.
this.inherited(arguments);
srcNodeRef && srcNodeRef.setAttribute("widgetId", this.id); // If this is created via Dojo parser, set widgetId attribute so that destroyDescendants() of parent widget works
},
startup: function(){
// summary:
// A function called after the DOM fragment declaring this controller is added to the document, in a similar manner as dijit/_WidgetBase.startup().
var _self = this;
this.own(router.register(/.*/, function(e){ // Register a route handling callback for any route, make sure it's cleaned up upon this controller being destroyed
_self._set("hash", e.newPath); // Update hash property
}));
router.startup(); // Activate dojo/router
this.set("hash", router._currentPath); // Set the inital value of hash property
},
_setHashAttr: function(value){
// summary:
// Handler for calls to set("hash", val).
// description:
// If the new value is different from location.hash, updates location.hash.
if(this.hash != value){
router.go(value); // If the new value is different from location.hash, updates location.hash
}
this._set("hash", value); // Assign the new value to the property
}
});
})
define([
"dojo/_base/array",
"dojo/_base/declare",
"dojo/Stateful",
"dojox/mvc/ModelRefController"
], function(array, declare, Stateful, ModelRefController){
return declare(ModelRefController, {
// summary:
// Our custom controller that does:
//
// - Handle actions like adding/removing/marking
// - Provide references to the todo list in data model, whose data comes from above Dojo Object Store
//
// description:
// The todo list in the data model, which is based on dojox/mvc/StatefulArray, can be referred via this[this._refModelProp].
// Actions are implemented in the manner of manipulating array.
// The change will automatically be reflected to the UI via the notification system of dojox/mvc/StatefulArray.
addItem: function(/*String*/ title){
// summary:
// Adds a todo item with the given title.
// title: String
// The title of todo item.
this[this._refModelProp].push(new Stateful({title: title, completed: false}));
},
removeItem: function(/*String*/ uniqueId){
// summary:
// Removes a todo item having the given unique ID.
// uniqueId: String
// The unique ID of the todo item to be removed.
var model = this[this._refModelProp],
indices = array.filter(array.map(model, function(item, idx){ return item.uniqueId == uniqueId ? idx : -1; }), function(idx){ return idx >= 0; }); // The array index of the todo item to bd removed
if(indices.length > 0){
model.splice(indices[0], 1);
}
},
removeCompletedItems: function(){
// summary:
// Removes todo items that have been marked as complete.
var model = this[this._refModelProp];
for(var i = model.length - 1; i >= 0; --i){
if(model[i].get("completed")){
model.splice(i, 1);
}
}
},
markAll: function(/*Boolean*/ markComplete){
// summary:
// Mark all todo items as complete or incomplete.
// markComplete: Boolean
// True to mark all todo items as complete. Otherwise to mark all todo items as incomplete.
array.forEach(this[this._refModelProp], function(item){
item.set("completed", markComplete);
});
}
});
});
define([
"dojo/_base/array",
"dojo/_base/declare",
"dojox/mvc/EditStoreRefController"
], function(array, declare, EditStoreRefController){
return declare(EditStoreRefController, {
// summary:
// Our custom controller that does:
//
// - Load and save data using a Dojo Object Store
// - Provide references to the data model, whose data comes from above Dojo Object Store
// defaultId: String
// The default ID to fetch data as this controller starts up.
defaultId: "",
// store: dojo/store
// Our custom Dojo Object Store, backed by localStorage.
// This will be used to read the initial items, if available, and persist the current items when the application finishes.
store: null,
// modelClass: todo/model/SimpleTodoModel
// The class of our data model, based on dojo/Stateful and dojox/mvc/StatefulArray.
// This will be used to automatically keep various Todo properties (e.g. number of complete/incomplete items) consistent, and to auto-generate properties (e.g. default unique IDs).
modelClass: null,
getStatefulOptions: {
// summary:
// An object that specifies how plain object from Dojo Object Store should be converted to a data model based on dojo/Stateful and dojox/mvc/StatefulArray.
getType: function(/*Object*/ v){
return "specifiedModel"; // We are converting given object at once using modelClass here, instead of using per-data-item based data conversion callbacks
},
getStatefulSpecifiedModel: function(/*Object*/ o){
return new (this.parent.modelClass)(o); // Create an instance of modelClass given the plain object from Dojo Object Store
}
},
getPlainValueOptions: {
// summary:
// An object that specifies how a data model based on dojo/Stateful and dojox/mvc/StatefulArray should be converted to a plain object, to be saved to Dojo Object Store.
getType: function(/*todo/model/SimpleTodoModel*/ o){
return "specifiedModel"; // We are converting given data model at once, instead of using per-data-item based data conversion callbacks
},
getPlainSpecifiedModel: function(/*todo/model/SimpleTodoModel*/ o){
return { // Pick up only meaningful data here
id: o.id,
todos: array.map(o.todos, function(item){ return {title: item.title, completed: item.completed }; }),
incomplete: o.incomplete,
complete: o.complete
};
}
},
postscript: function(/*Object?*/ params, /*DOMNode?*/ srcNodeRef){
// summary:
// Kicks off instantiation of this controller, in a similar manner as dijit/_WidgetBase.postscript().
// params: Object?
// The optional parameters for this controller.
// srcNodeRef: DOMNode?
// The DOM node declaring this controller. Set if this controller is created via Dojo parser.
this.getStatefulOptions.parent = this; // For getStatefulOptions object to have a reference to this object
this.inherited(arguments);
srcNodeRef && srcNodeRef.setAttribute("widgetId", this.id); // If this is created via Dojo parser, set widgetId attribute so that destroyDescendants() of parent widget works
},
startup: function(){
// summary:
// A function called after the DOM fragment declaring this controller is added to the document, in a similar manner as dijit/_WidgetBase.startup().
this.inherited(arguments);
this.getStore(this.defaultId); // Obtain data from Dojo Object Store as soon as this starts up
},
set: function(/*String*/ name, /*Anything*/ value){
// summary:
// A function called when a property value is set to this controller.
if(name == this._refSourceModelProp && this[this._refSourceModelProp] != value && (this[this._refSourceModelProp] || {}).destroy){
this[this._refSourceModelProp].destroy(); // If we have a data model and it's being replaced, make sure it's cleaned up
}
this.inherited(arguments);
},
destroy: function(){
// summary:
// A function called when this controller is being destroyed, in a similar manner as dijit/_WidgetBase.destroy().
this.commit(); // Save the data to Dojo Object Store when this is destroyed
if((this[this._refSourceModelProp] || {}).destroy){
this[this._refSourceModelProp].destroy(); // If we have a data model, make sure it's cleaned up
}
this.inherited(arguments);
}
});
});
define(["dojo/_base/declare"], function(declare){
var ACTIVE = "/active",
COMPLETED = "/completed";
function getHiddenState(/*Object*/ props){
// summary:
// Returns the new hidden state of todo item, given the URL hash and the completed state of todo item.
// props: Object
// An object containing the URL hash and the completed state of todo item.
return props.hash == ACTIVE ? props.completed :
props.hash == COMPLETED ? !props.completed :
false;
}
return declare(null, {
// summary:
// A mix-in class for widgets-in-template for todo item, that looks at URL hash and completed state of todo item, and updates the hidden state.
// description:
// A todo item should be hidden if:
//
// - URL hash is "/active" and the todo item is complete -OR-
// - URL hash is "/copleted" and the todo item is incomplete
_setHashAttr: function(/*String*/ value){
// summary:
// Handler for calls to set("hash", val), to update hidden state given the new value and the completed state.
this.set("hidden", getHiddenState({hash: value, completed: this.completed})); // Update hidden state given the new value and the completed state
this._set("hash", value); // Assign the new value to the attribute
},
_setCompletedAttr: function(/*Boolean*/ value){
// summary:
// Handler for calls to set("completed", val), to update hidden state given the new value and the hash.
this.set("hidden", getHiddenState({hash: this.hash, completed: value})); // Update hidden state given the new value and the hash
this._set("completed", value); // Assign the new value to the attribute
}
});
});
define({
// summary:
// A dojox/mvc data converter, that runs between todo/ctrl/HashController and a widget having <a> tag as its DOM node.
// It does one-way conversion from URL hash to boolean state of whether the URL hash matches href attribute of the widget's DOM node.
format: function(/*String*/ value){
// summary:
// Returns whether given value matches href attribute of the widget's DOM node.
return this.target.domNode.getAttribute("href").substr(1) == (value || "/");
},
parse: function(/*Boolean*/ value){
// summary:
// This functions throws an error so that the new value won't be reflected.
throw new Error();
}
});
define({
// summary:
// A dojox/mvc data converter, that does one-way conversion that returns whether we have less than n todo items in a specific state, where n is the given number in data converter options.
// Data converter options can be specified by setting constraints property in one of data binding endpoints.
// See data converter section of dojox/mvc/sync library's documentation for more details.
format: function(/*Number*/ value, /*Object*/ constraints){
// summary:
// Returns whether given value is less than or equal to the given number in data converter options (default zero).
return value <= (constraints.lessThanOrEqualTo || 0);
},
parse: function(/*Boolean*/ value){
// summary:
// This functions throws an error so that the new value won't be reflected.
throw new Error();
}
});
define([
"dojo/_base/array",
"dojo/_base/lang",
"dojox/mvc/getStateful"
], function(array, lang, getStateful){
var defaultData = {
// summary:
// Data used when there is no saved data.
// The same structure is used to save data to localStrage (when the page is unloaded).
// id: String
// The ID of saved data, used in Dojo Object Store as well as in localStorage that the Dojo Object Store uses.
id: "todos-dojo",
// todos: Object[]
// The todo items.
todos : [],
// incomplete: Number
// The count of incomplete todo items.
incomplete: 0,
// complete: Number
// The count of complete todo items.
complete: 0
};
var seq = 0; // The sequence number to generate unique IDs of todo items
return function(/*Object*/ data){
// summary:
// The data model for TodoMVC.
// description:
// This data model does:
//
// - Keep complete/incomplete properties up to date
// - Add unique ID to new todo items
//
// data: Object
// The plain data, coming from Dojo Object Store.
function assignPropertiesToNewItem(/*dojo/Stateful*/ item){
// summary:
// Add additional properties to a todo item.
// description:
// This function does:
//
// - Add unique ID to the given todo item
// - Add setter function for completed property to the given todo item
// item: dojo/Stateful
// The todo item.
lang.mixin(item, {
_completedSetter: function(/*Boolean*/ value){
// summary
// The setter function for completed property, which keeps complete/incomplete properties of the data model up to date.
// value: Boolean
// True means this todo item is completed. Otherwise means this todo item is incomplete.
if(this.completed ^ value){ // Update is done only if there is a flip in value
// If the todo item is turned to completed state, increase the count of completed items.
// If the todo item is turned to incomplete state, decrease the count of completed items.
statefulData.set("complete", statefulData.get("complete") + (value ? 1 : -1));
}
this.completed = value; // Assign the new value
},
// uniqueId: String
// The unique ID of the todo item.
uniqueId: "TODO-" + seq++
});
}
var statefulData = getStateful(data || defaultData); // Convert a plain object to dojo/Stateful, hierarchically. Use the default data if (lastly saved) data is not there
array.forEach(statefulData.todos, assignPropertiesToNewItem); // Add additional properties to todo items
return lang.mixin(statefulData, {
_elementWatchHandle: statefulData.todos.watchElements(function(/*Number*/ idx, /*dojo/Stateful[]*/ removals, /*dojo/Stateful[]*/adds){
// summary:
// The dojox/mvc/StatefulArray watch callback for adds/removals of todo items.
// description:
// This callback does:
//
// - Keep complete/incomplete properties up to date, upon adds/removals of todo items
// - Add unique ID to new todo items
// - Add setter function for completed property to new todo items
//
// idx: Number
// The array index where adds/removals happened at.
// removals: dojo/Stateful[]
// The removed todo items.
// adds: dojo/Stateful[]
// The added todo items.
var complete = statefulData.get("complete");
array.forEach(removals, function(item){ complete -= !!item.completed; }); // If completed items are removed, decrease the count of completed items
array.forEach(adds, function(item){ complete += !!item.completed; }); // If completed items are added, increase the count of completed items
statefulData.set("complete", complete);
statefulData.set("incomplete", statefulData.todos.get("length") - complete);
array.forEach(adds, assignPropertiesToNewItem); // Add additional properties to newly added todo items
}),
_incompleteSetter: function(/*Number*/ value){
// summary:
// dojo/Stateful setter function for incomplete property.
// description:
// This setter function keeps complete property up to date.
// value: Number
// The new count of incomplete todo items.
var changed = this.incomplete != value;
this.incomplete = value; // Assign the new value
if(changed){ // Update is done only if there is a change in value
this.set("complete", this.todos.get("length") - value); // Make the count of complete items reflect the new count of incomplete items
}
},
_completeSetter: function(/*Number*/ value){
// summary:
// dojo/Stateful setter function for complete property.
// description:
// This setter function keeps incomplete property up to date.
// value: Number
// The new count of complete todo items.
var changed = this.complete != value;
this.complete = value; // Assign the new value
if(changed){ // Update is done only if there is a change in value
this.set("incomplete", this.todos.get("length") - value); // Make the count of incomplete items reflect the new count of complete items
}
},
destroy: function(){
this._elementWatchHandle.remove(); // Stop watching for adds/removals of todo items
}
});
};
});
// This is a build profile for Dojo version of TodoMVC.
// To use this, place todomvc directory at the directory containing dojo/dijit/dojox/util (and follow the procedure of building Dojo).
// Refer to todomvc/architecture-examples/dojo/js/lib/dojo-1.8 and todomvc/architecture-examples/dojo/js/lib/dijit-1.8 to see what built files need to be copied.
dependencies = {
stripConsole: "normal",
selectorEngine:"acme",
layers: [
{
name: "dojo.js",
dependencies: [
"dojo.domReady",
"dojo.parser",
"todo.app18"
]
}
],
prefixes: [
[ "dijit", "../dijit" ],
[ "dojox", "../dojox" ],
[ "doh", "../util/doh" ],
[ "todo", "../todomvc/architecture-examples/dojo/js/todo" ]
]
}
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