Commit a3e94ba8 authored by asudoh's avatar asudoh

TodoMVC using new dojox/mvc 1.8 features.

parent 4aa4c864
...@@ -86,3 +86,10 @@ ...@@ -86,3 +86,10 @@
.inline_edit[style~='0;'] ~ .toggle { .inline_edit[style~='0;'] ~ .toggle {
visibility: hidden !important; 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 @@ ...@@ -8,17 +8,43 @@
<!-- CSS overrides - remove if you don't need it --> <!-- CSS overrides - remove if you don't need it -->
<link rel="stylesheet" href="css/app.css"> <link rel="stylesheet" href="css/app.css">
<!--[if IE]> <!--[if IE]>
<script>
nativeDate = Date;
</script>
<script src="../../assets/ie.js"></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]--> <![endif]-->
</head> </head>
<body> <body>
<section id="todoapp" data-dojo-type="todo.app"></section> <section id="todoapp" data-dojo-type="todo/app18"></section>
<footer id="info"> <footer id="info">
<p>Double-click to edit a todo</p> <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> <p>Created by <a href="http://jamesthom.as/">James Thomas</a> and <a href="https://github.com/edchat">Ed Chatelain</a></p>
</footer> </footer>
<script src="../../assets/base.js"></script> <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> </body>
</html> </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"
], function(array, declare, lang, domClass){
return declare(null, {
// summary:
// Mixin class to support 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="dijit/_WidgetBase"
// | data-dojo-mixins="todo/_CssToggleMixin"
// | 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);
}
});
});
<section>
<!-- This is the widgets-in-template that composes the application UI of TodoMVC (Dojo 1.8 version) -->
<!--
Notes:
data-dojo-id creates object reference of the widget. Though TodoMVC application does not have multiple instances of this widgets-in-template, "${id}_" prefix is added to data-dojo-id attribute to avoid ID duplication.
-->
<!--
This <script> tag assigns module references to variables that can be used in this template. The modules used here are:
- dojox/mvc/at: Function to create a pointer to dojo/Stateful, for the purpose of data binding
- todo/model/SimpleTodoModel: The data model class for TodoMVC data
- todo/misc/HashSelectedConverter: dojox/mvc data converter for widgets of the <a> tags for All/Active/Completed, for the selected state of the <a> tags
- todo/misc/LessThanOrEqualToConverter: dojox/mvc data converter for the shown/hidden state of "s" in "items left", as well as for the check box to mark all todo items complete/incomplete
-->
<script type="dojo/require">
at: "dojox/mvc/at",
SimpleTodoModel: "todo/model/SimpleTodoModel",
HashSelectedConverter: "todo/misc/HashSelectedConverter",
LessThanOrEqualToConverter: "todo/misc/LessThanOrEqualToConverter"
</script>
<!-- A controller that keeps its hash attribute in sync with the URL hash -->
<span data-dojo-id="${id}_routeCtl"
data-dojo-type="todo/ctrl/RouteController"></span>
<!-- A Dojo Object Store for fetching/saving data from/to localStorage -->
<span data-dojo-id="${id}_localStorage"
data-dojo-type="todo/store/LocalStorage"></span>
<!--
Our custom controller, based on dojox/mvc/EditStoreRefController, that loads and saves data using a Dojo Object Store, and provides references to the data model (from the Dojo Object Store).
The complete attribute (count of completed todo items, coming as a reference in the data model) is bound to the parent widgets-in-template, so that it can update a CSS class in its root DOM node.
-->
<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>
<!--
Our custom controller, based on dojox/mvc/ModelRefController, that provides references to the todo list in data model, and handles actions like adding/removing/marking.
The model attribute (the todo list in data model) is bound to above todo/ctrl/TodoRefController instance, so that it's updated as soon as todo/ctrl/TodoRefController obtains the data from Dojo Object Store.
The length attribute (count of all todo items, coming as a reference in the todo list in the data model) is bound to the parent widgets-in-template, so that it can update a CSS class in its root DOM node.
-->
<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>
<!--
The text box to enter new todo item.
The keypress event is handled by a method of parent widgets-in-template, so that pressing Enter key ends up with adds the new todo item.
-->
<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">
<!--
The check box to mark todo items complete/incomplete.
It's automatically checked when there is no incomplete items, by the data binding to the count of incomplete todo items (via the todo/ctrl/TodoRefController instance), which is converted to a boolean state representing whether there is no incomplete todo items.
-->
<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>
<!--
The todo list UI, composed by repeating <li> for each todo item with dojox/mvc/WidgetList.
The children attribute (the list of the todo items) is bound to the todo/ctrl/TodoListRefController instance, so that it's updated as soon as todo/ctrl/TodoRefController obtains the data from Dojo Object Store.
Each <li> is created along with a widgets-in-template associated with it.
Each widgets-in-template here does:
- Associate completed attribute to the existence of "completed" CSS class of its root DOM node (Such association is used by todo/_CssToggleMixin code)
- Associate hidden attribute to the existence of "hidden" CSS class of its root DOM node (Such association is used by todo/_CssToggleMixin code)
- Mix-in in todo/_CssToggleMixin as well as todo/ctrl/_HashCompletedMixin, so that the combination of URL hash and todo item's completed state will end up with the shown/hidden states being updated (by adding/removing "hidden" CSS class)
- Bind its uniqueId attribute to the data model for the todo item, so that the action handler for removing a todo item can specify which todo item should be removed
- Bind its completed attribute to the data model for the todo item, so that todo/ctrl/_HashCompletedMixin code can determine the shown/hidden state with the URL hash
- Bind its hash attribute to the todo/ctrl/RouteController instance, so that todo/ctrl/_HashCompletedMixin code can determine the shown/hidden state with the completed state
-->
<ul id="todo-list"
data-dojo-type="dojox/mvc/WidgetList"
data-dojo-mixins="dojox/mvc/_InlineTemplateMixin"
data-dojo-props="children: at(${id}_listCtrl, 'model'),
partialRebuild: true"
data-mvc-child-type="dijit/_WidgetBase"
data-mvc-child-mixins="dijit/_TemplatedMixin,dijit/_WidgetsInTemplateMixin,todo/_CssToggleMixin,todo/ctrl/_HashCompletedMixin"
data-mvc-child-props="_setCompletedAttr: {type: 'classExists', className: 'completed'},
_setHiddenAttr: {type: 'classExists', className: 'hidden'},
uniqueId: at(this.target, 'uniqueId'),
completed: at(this.target, 'completed'),
hash: at(${id}_routeCtl, 'hash'),
onRemoveClick: function(){ ${id}_listCtrl.removeItem(this.uniqueId); }">
<!--
dojox/mvc/_InlineTemplateMixin picks up the contents in <script type="dojox/mvc/InlineTemplate"> as the template string.
dojox/mvc/WidgetList used its template string for each child widget.
-->
<script type="dojox/mvc/InlineTemplate">
<li>
<div class="view">
<!--
The title of the todo item, with in-line editing feature.
The value attribute is bound to the data model for the todo item, so that the title of the todo item is in sync with the data model.
The uniqueId attribute is bound to the data model for the todo item, so that the action handler for removing the todo item (when its title is empty) can specify which todo item should be removed.
-->
<label class="dijitInline inline_edit"
data-dojo-type="todo/form/InlineEditBox"
data-dojo-props="value: at('rel:', 'title'),
uniqueId: at('rel:', 'uniqueId'),
editor: 'dijit/form/TextBox',
autosave: true,
onChange: function(){ if(!this.value){ ${id}_listCtrl.removeItem(this.uniqueId); return false; } }"></label>
<!--
The check box indicating whether the todo item is completed or not.
The checked attribute is bound to the data model for the todo item, so that the checked state is in sync with the completed state in the data model.
-->
<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>
<!--
The count of incomplete todo items.
The value attribute is associated with the text in this widget, and bound to the todo/ctrl/TodoRefController instance, so that the count of incomplete todo items is updated as soon as todo/ctrl/TodoRefController obtains the data from Dojo Object Store.
-->
<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<!--
The 's' in "items".
The single attribute is associated with the existence of "plural" CSS class of its root DOM node. (Such association is used by todo/_CssToggleMixin code)
The single attribute is bound to the todo/ctrl/TodoRefController instance, so that the state whether the count of incomplete todo item is less than or equal to 1 is updated as soon as todo/ctrl/TodoRefController obtains the data from Dojo Object Store.
--><span data-dojo-type="dijit/_WidgetBase"
data-dojo-mixins="todo/_CssToggleMixin"
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>
<!--
The link to show all todo items.
The selected attribute is associated with the existence of "selected" CSS class of its root DOM node. (Such association is used by todo/_CssToggleMixin code)
The selected attribute is bound to todo/ctrl/RouteController instance, so that change in URL hash will be reflected here.
The URL hash is converted to the state whether the hash matches to the href here, by HashSelectedConverter (a dojox/mvc data converter).
-->
<a href="#/"
data-dojo-type="dijit/_WidgetBase"
data-dojo-mixins="todo/_CssToggleMixin"
data-dojo-props="_setSelectedAttr: {type: 'classExists', className: 'selected'},
selected: at(${id}_routeCtl, 'hash').transform(HashSelectedConverter)">All</a>
</li>
<li>
<!--
The link to show all todo items.
The selected attribute is associated with the existence of "selected" CSS class of its root DOM node. (Such association is used by todo/_CssToggleMixin code)
The selected attribute is bound to todo/ctrl/RouteController instance, so that change in URL hash will be reflected here.
The URL hash is converted to the state whether the hash matches to the href here, by HashSelectedConverter (a dojox/mvc data converter).
-->
<a href="#/active"
data-dojo-type="dijit/_WidgetBase"
data-dojo-mixins="todo/_CssToggleMixin"
data-dojo-props="_setSelectedAttr: {type: 'classExists', className: 'selected'},
selected: at(${id}_routeCtl, 'hash').transform(HashSelectedConverter)">Active</a>
</li>
<li>
<!--
The link to show all todo items.
The selected attribute is associated with the existence of "selected" CSS class of its root DOM node. (Such association is used by todo/_CssToggleMixin code)
The selected attribute is bound to todo/ctrl/RouteController instance, so that change in URL hash will be reflected here.
The URL hash is converted to the state whether the hash matches to the href here, by HashSelectedConverter (a dojox/mvc data converter).
-->
<a href="#/completed"
data-dojo-type="dijit/_WidgetBase"
data-dojo-mixins="todo/_CssToggleMixin"
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 (
<!--
The count of completed todo items.
The value attribute is associated with the text in this widget, and bound to the todo/ctrl/TodoRefController instance, so that the count of completed todo items is updated as soon as todo/ctrl/TodoRefController obtains the data from Dojo Object Store.
-->
<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/_WidgetBase",
"dijit/_TemplatedMixin",
"dijit/_WidgetsInTemplateMixin",
"todo/_CssToggleMixin",
"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",
"dojox/mvc/_InlineTemplateMixin",
"dojox/mvc/WidgetList",
"todo/ctrl/_HashCompletedMixin",
"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, _WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin, _CssToggleMixin, template){
return declare([_WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin, _CssToggleMixin], {
// summary:
// A widgets-in-template widget that composes the application UI of TodoMVC (Dojo 1.8 version).
// Also, this class inherits todo/_CssToggleMixin 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/_CssToggleMixin 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/_CssToggleMixin 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
}
});
};
});
<!doctype html>
<!--
This is a test runner for Dojo version of TodoMVC.
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: false,
locale: "en",
paths: {
"todo": "../todomvc/architecture-examples/dojo/js/todo"
},
deps: ["doh", "dojo/dom-style", "dojo/json", "dojo/keys", "dojo/on", "dojo/parser", "dojo/query", "dojo/when", "dojo/domReady!"],
mvc: {debugBindings: true},
callback: function(doh, domStyle, json, keys, on, parser, query, when){
var id = "todos-dojo";
localStorage.removeItem(id);
localStorage.setItem(id, json.stringify({
id: "todos-dojo",
todos : [
{title: "Task0", completed: false},
{title: "Task1", completed: true},
{title: "Task2", completed: false}
],
incomplete: 2,
complete: 1
}));
when(parser.parse(), function(){
doh.register("todo.app18", [
function initialState(){
doh.f(query("#toggle-all")[0].checked, "The check box to mark all todo items complete/incomplete should be unchecked");
var complete = 0, incomplete = 0;
query("#todo-list input[type=checkbox]").forEach(function(node){
complete += !!node.checked;
incomplete += !node.checked;
});
doh.is(1, complete, "Count of completed todo items should be 1");
doh.is(2, incomplete, "Count of incomplete todo items should be 2");
doh.t(/^\s*2\s*$/.test(query("#todo-count strong span.number")[0].innerHTML), "Count of incomplete todo items should be shown as 2 in the UI");
doh.f(/^none$/i.test(domStyle.getComputedStyle(query("#todo-count span.word span")[0]).display), "'s' in \"items\" should be shown");
doh.f(/^none$/i.test(domStyle.getComputedStyle(query("#clear-completed")[0]).display), "Clear completed button should be shown");
doh.t(/^\s*1\s*$/.test(query("#clear-completed span.number-done")[0].innerHTML), "Count of completed todo items should be shown as 1 in the UI");
},
function markItemComplete(){
query("#todo-list input[type=checkbox]")[0].click();
doh.f(query("#toggle-all")[0].checked, "The check box to mark all todo items complete/incomplete should be unchecked");
var complete = 0, incomplete = 0;
query("#todo-list input[type=checkbox]").forEach(function(node){
complete += !!node.checked;
incomplete += !node.checked;
});
doh.is(2, complete, "Count of completed todo items should be 2");
doh.is(1, incomplete, "Count of incomplete todo items should be 1");
doh.t(/^\s*1\s*$/.test(query("#todo-count strong span.number")[0].innerHTML), "Count of incomplete todo items should be shown as 1 in the UI");
doh.t(/^none$/i.test(domStyle.getComputedStyle(query("#todo-count span.word span")[0]).display), "'s' in \"items\" should be hidden");
doh.f(/^none$/i.test(domStyle.getComputedStyle(query("#clear-completed")[0]).display), "Clear completed button should be shown");
doh.t(/^\s*2\s*$/.test(query("#clear-completed span.number-done")[0].innerHTML), "Count of completed todo items should be shown as 2 in the UI");
},
function markItemIncomplete(){
query("#todo-list input[type=checkbox]")[1].click();
doh.f(query("#toggle-all")[0].checked, "The check box to mark all todo items complete/incomplete should be unchecked");
var complete = 0, incomplete = 0;
query("#todo-list input[type=checkbox]").forEach(function(node){
complete += !!node.checked;
incomplete += !node.checked;
});
doh.is(1, complete, "Count of completed todo items should be 1");
doh.is(2, incomplete, "Count of incomplete todo items should be 2");
doh.t(/^\s*2\s*$/.test(query("#todo-count strong span.number")[0].innerHTML), "Count of incomplete todo items should be shown as 2 in the UI");
doh.f(/^none$/i.test(domStyle.getComputedStyle(query("#todo-count span.word span")[0]).display), "'s' in \"items\" should be shown");
doh.f(/^none$/i.test(domStyle.getComputedStyle(query("#clear-completed")[0]).display), "Clear completed button should be shown");
doh.t(/^\s*1\s*$/.test(query("#clear-completed span.number-done")[0].innerHTML), "Count of completed todo items should be shown as 1 in the UI");
},
function clearCompleted(){
query("#clear-completed")[0].click();
doh.f(query("#toggle-all")[0].checked, "The check box to mark all todo items complete/incomplete should be unchecked");
var complete = 0, incomplete = 0;
query("#todo-list input[type=checkbox]").forEach(function(node){
complete += !!node.checked;
incomplete += !node.checked;
});
doh.is(0, complete, "Count of completed todo items should be 0");
doh.is(2, incomplete, "Count of incomplete todo items should be 2");
doh.t(/^\s*2\s*$/.test(query("#todo-count strong span.number")[0].innerHTML), "Count of incomplete todo items should be shown as 2 in the UI");
doh.f(/^none$/i.test(domStyle.getComputedStyle(query("#todo-count span.word span")[0]).display), "'s' in \"items\" should be shown");
doh.t(/^none$/i.test(domStyle.getComputedStyle(query("#clear-completed")[0]).display), "Clear completed button should be hidden");
doh.t(/^\s*0\s*$/.test(query("#clear-completed span.number-done")[0].innerHTML), "Count of completed todo items should be shown as 0 in the UI");
},
function markAllComplete(){
query("#toggle-all")[0].click();
doh.t(query("#toggle-all")[0].checked, "The check box to mark all todo items complete/incomplete should be checked");
var complete = 0, incomplete = 0;
query("#todo-list input[type=checkbox]").forEach(function(node){
complete += !!node.checked;
incomplete += !node.checked;
});
doh.is(2, complete, "Count of completed todo items should be 2");
doh.is(0, incomplete, "Count of incomplete todo items should be 0");
doh.t(/^\s*0\s*$/.test(query("#todo-count strong span.number")[0].innerHTML), "Count of incomplete todo items should be shown as 0 in the UI");
doh.t(/^none$/i.test(domStyle.getComputedStyle(query("#todo-count span.word span")[0]).display), "'s' in \"items\" should be hidden");
doh.f(/^none$/i.test(domStyle.getComputedStyle(query("#clear-completed")[0]).display), "Clear completed button should be shown");
doh.t(/^\s*2\s*$/.test(query("#clear-completed span.number-done")[0].innerHTML), "Count of completed todo items should be shown as 2 in the UI");
},
function markAllInComplete(){
query("#toggle-all")[0].click();
doh.f(query("#toggle-all")[0].checked, "The check box to mark all todo items complete/incomplete should be unchecked");
var complete = 0, incomplete = 0;
query("#todo-list input[type=checkbox]").forEach(function(node){
complete += !!node.checked;
incomplete += !node.checked;
});
doh.is(0, complete, "Count of completed todo items should be 0");
doh.is(2, incomplete, "Count of incomplete todo items should be 2");
doh.t(/^\s*2\s*$/.test(query("#todo-count strong span.number")[0].innerHTML), "Count of incomplete todo items should be shown as 2 in the UI");
doh.f(/^none$/i.test(domStyle.getComputedStyle(query("#todo-count span.word span")[0]).display), "'s' in \"items\" should be shown");
doh.t(/^none$/i.test(domStyle.getComputedStyle(query("#clear-completed")[0]).display), "Clear completed button should be hidden");
doh.t(/^\s*0\s*$/.test(query("#clear-completed span.number-done")[0].innerHTML), "Count of completed todo items should be shown as 0 in the UI");
},
function removeItem(){
query("button.destroy")[1].click();
doh.f(query("#toggle-all")[0].checked, "The check box to mark all todo items complete/incomplete should be unchecked");
var complete = 0, incomplete = 0;
query("#todo-list input[type=checkbox]").forEach(function(node){
complete += !!node.checked;
incomplete += !node.checked;
});
doh.is(0, complete, "Count of completed todo items should be 0");
doh.is(1, incomplete, "Count of incomplete todo items should be 1");
doh.t(/^\s*1\s*$/.test(query("#todo-count strong span.number")[0].innerHTML), "Count of incomplete todo items should be shown as 1 in the UI");
doh.t(/^none$/i.test(domStyle.getComputedStyle(query("#todo-count span.word span")[0]).display), "'s' in \"items\" should be hidden");
doh.t(/^none$/i.test(domStyle.getComputedStyle(query("#clear-completed")[0]).display), "Clear completed button should be hidden");
doh.t(/^\s*0\s*$/.test(query("#clear-completed span.number-done")[0].innerHTML), "Count of completed todo items should be shown as 0 in the UI");
},
function markItemComplete(){
query("#todo-list input[type=checkbox]")[0].click();
doh.t(query("#toggle-all")[0].checked, "The check box to mark all todo items complete/incomplete should be checked");
var complete = 0, incomplete = 0;
query("#todo-list input[type=checkbox]").forEach(function(node){
complete += !!node.checked;
incomplete += !node.checked;
});
doh.is(1, complete, "Count of completed todo items should be 1");
doh.is(0, incomplete, "Count of incomplete todo items should be 0");
doh.t(/^\s*0\s*$/.test(query("#todo-count strong span.number")[0].innerHTML), "Count of incomplete todo items should be shown as 0 in the UI");
doh.t(/^none$/i.test(domStyle.getComputedStyle(query("#todo-count span.word span")[0]).display), "'s' in \"items\" should be hidden");
doh.f(/^none$/i.test(domStyle.getComputedStyle(query("#clear-completed")[0]).display), "Clear completed button should be shown");
doh.t(/^\s*1\s*$/.test(query("#clear-completed span.number-done")[0].innerHTML), "Count of completed todo items should be shown as 1 in the UI");
},
function addItem(){
query("#new-todo")[0].value = "Task3";
on.emit(query("#new-todo")[0], "keypress", {keyCode: keys.ENTER});
var complete = 0, incomplete = 0;
query("#todo-list input[type=checkbox]").forEach(function(node){
complete += !!node.checked;
incomplete += !node.checked;
});
doh.is(1, complete, "Count of completed todo items should be 1");
doh.is(1, incomplete, "Count of incomplete todo items should be 1");
doh.t(/^\s*1\s*$/.test(query("#todo-count strong span.number")[0].innerHTML), "Count of incomplete todo items should be shown as 1 in the UI");
doh.t(/^none$/i.test(domStyle.getComputedStyle(query("#todo-count span.word span")[0]).display), "'s' in \"items\" should be hidden");
doh.f(/^none$/i.test(domStyle.getComputedStyle(query("#clear-completed")[0]).display), "Clear completed button should be shown");
doh.t(/^\s*1\s*$/.test(query("#clear-completed span.number-done")[0].innerHTML), "Count of completed todo items should be shown as 1 in the UI");
}
]);
doh.run();
});
}
};
</script>
<script src="../../../../../../dojo/dojo.js"></script>
</body>
</html>
define([
"doh",
"dojo/json",
"dojox/mvc/at",
"../../ctrl/TodoListRefController",
"../../ctrl/TodoRefController",
"../../model/SimpleTodoModel",
"../../store/LocalStorage"
], function(doh, json, at, TodoListRefController, TodoRefController, SimpleTodoModel, LocalStorage){
doh.register("todo.tests.ctrl.TodoListRefController", [
function empty(){
localStorage.removeItem("todos-dojo");
var ctrl = new TodoRefController({
defaultId: "todos-dojo",
modelClass: SimpleTodoModel,
store: new LocalStorage()
}), listCtrl = new TodoListRefController({
model: at(ctrl, "todos")
});
doh.is(0, ctrl.get("complete"), "The current count of completed todo items should be 0");
doh.is(0, ctrl.get("incomplete"), "The current count of incomplete todo items should be 0");
doh.is(0, listCtrl.get("length"), "The current length should be 0");
listCtrl.model.push({title: "Task0", completed: false});
doh.is(0, ctrl.get("complete"), "The current count of completed todo items should be 0");
doh.is(1, ctrl.get("incomplete"), "The current count of incomplete todo items should be 1");
doh.is(1, listCtrl.get("length"), "The current length should be 1");
},
function existing(){
localStorage.setItem("todos-dojo", json.stringify({
id: "todos-dojo",
todos : [
{title: "Task0", completed: false},
{title: "Task1", completed: true},
{title: "Task2", completed: false}
],
incomplete: 2,
complete: 1
}));
var ctrl = new TodoRefController({
defaultId: "todos-dojo",
modelClass: SimpleTodoModel,
store: new LocalStorage()
}), listCtrl = new TodoListRefController({
model: at(ctrl, "todos")
});
doh.is(1, ctrl.get("complete"), "The current count of completed todo items should be 1");
doh.is(2, ctrl.get("incomplete"), "The current count of incomplete todo items should be 2");
doh.is(3, listCtrl.get("length"), "The current length should be 3");
}
]);
});
\ No newline at end of file
(function(require){
require.baseUrl = "../../dojo";
require.packages = (require.packages || []).concat([{
name: "todo",
location: "../todomvc/architecture-examples/dojo/js/todo"
}]);
})(require);
define([
"doh",
"dojo/Stateful",
"../../model/SimpleTodoModel"
], function(doh, Stateful, SimpleTodoModel){
doh.register("todo.tests.model.SimpleTodoModel", [
function basic(){
var model = new SimpleTodoModel({
id: "todos-dojo",
todos : [
{title: "Task0", completed: false},
{title: "Task1", completed: true},
{title: "Task2", completed: false}
],
incomplete: 2,
complete: 1
});
model.todos.push(new Stateful({title: "Task3", completed: true}), new Stateful({title: "Task4", completed: false}));
doh.is(2, model.complete, "We should have two complete tasks");
doh.is(3, model.incomplete, "We should have three incomplete tasks");
model.todos.push(new Stateful({title: "Task5", completed: false}));
doh.is(2, model.complete, "We should have two complete tasks");
doh.is(4, model.incomplete, "We should have four incomplete tasks");
model.todos[4].set("completed", true);
doh.is(3, model.complete, "We should have three complete tasks");
doh.is(3, model.incomplete, "We should have three incomplete tasks");
model.todos.splice(4, 1);
doh.is(2, model.complete, "We should have two complete tasks");
doh.is(3, model.incomplete, "We should have three incomplete tasks");
}
]);
});
\ No newline at end of file
define([
"doh",
"./ctrl/TodoListRefController",
"./model/SimpleTodoModel"
], function(doh){
var userArgs = window.location.search.replace(/[\?&](dojoUrl|testUrl|testModule)=[^&]*/g, "").replace(/^&/, "?");
doh.registerUrl("todo.tests.app18", require.toUrl("todo/tests/app18.html") + userArgs, 999999);
});
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<!--
This is a test runner for Dojo version of TodoMVC.
To use this, place todomvc directory at the directory containing dojo/dijit/dojox.
-->
<html>
<head>
<title>Unit Test Runner</title>
<script>
function onLoadHandler(){
var queryString = window.location.search.substr(1), queries = queryString.split("&"), moduleFound = false;
for(var i = 0; i < queries.length; i++){
if(/^test(|Url|Module)=/i.test(queries[i])){
moduleFound = true;
break;
}
}
if(!moduleFound){
queryString += (queryString ? "&" : "") + "test=todo/tests/module&async=true";
}
document.getElementById("testFrame").src = "../../../../../../util/doh/runner.html?boot=../../todomvc/architecture-examples/dojo/js/todo/tests/dojoConfig.js" + (queryString ? ("&" + queryString) : "");
}
function iFrameOnLoadHandler(){
// summary:
// Makes sure todo/tests/dojoConfig.js is loaded before dojo.js is loaded.
// DOH's &boot URL arg handler does not allow us to specify what order the modules are loaded.
var w = document.getElementById("testFrame").contentWindow, found = false;
for(var packs = (w.require || {}).packages, i = 0; packs && i < packs.length; i++){
if(packs[i].name == "todo"){
found = true;
break;
}
}
if(!found){
return setTimeout(iFrameOnLoadHandler, 500);
}
var script = document.createElement("script");
script.type = "text/javascript";
script.src = "../../../../../../dojo/dojo.js";
script.charset = "utf-8";
w.document.getElementsByTagName("head")[0].appendChild(script);
}
</script>
</head>
<body onload="onLoadHandler();" style="width:100%;height:100%;margin:0;padding:0;">
<iframe id="testFrame" onload="iFrameOnLoadHandler();" frameborder="0" style="width:100%;height:100%;margin:0;padding:0;"></iframe>
</body>
</html>
// 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