Commit ab760073 authored by James Thomas's avatar James Thomas

Added built version of Dojo, final re-factor and commenting.

Also, added dijit theme files for the page widgets.
parent abbe1a7e
- Remove use of IDs
- Support local storage using custom Dojo Store
- Use local Dojo JS version
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -4,35 +4,43 @@
<head>
<title>Dojo</title>
<style type="text/css">
@import "/code/dtk/dojotoolkit/dijit/themes/claro/claro.css";
@import "./css/claro.css";
</style>
<link href="css/todos.css" media="all" rel="stylesheet" type="text/css"/>
<script data-dojo-config="async:true, parseOnLoad:true, paths:{'todo':'../../todo'}" src="./js/dtk/dojo/dojo.js"></script>
<script>
require(["dojo/parser", "dojo/dom-class", "dojo/_base/array", "dojox/mvc", "dojox/mvc/Group",
"dojox/mvc/Repeat", "dojox/mvc/Output", "dijit/InlineEditBox", "todo/form/CheckBox"],
function (parser, domClass, array, mvc) {
var data = {
require(["dojo/parser", "dojo/dom-class", "dojo/_base/array", "dojox/mvc", "todo/store/LocalStorage",
"dojox/mvc/Group", "dojox/mvc/Repeat", "dojox/mvc/Output", "dijit/InlineEditBox", "todo/form/CheckBox"],
function (parser, domClass, array, mvc, localStorage) {
/**
* Initialise our custom dojo store, backed by localStorage. This will be
* used to read the initial items, if available, and persist the current items
* when the application finishes.
*/
var store = new localStorage();
/**
* Attempt to read todo items from localStorage,
* returning default value the first time the application
* is loaded... The "id" parameter is used as the unique
* localStorage key for this object.
*/
var getLocalStorageItems = function () {
var initial = {
id: "local_storage_todos",
todos : [],
incomplete: 0,
complete: 0
};
window.model = mvc.newStatefulModel({ data : data });
mvc.bind(model.incomplete, "value", model.complete, "value", function (value) {
return this.model.todos.get("length") - value;
});
mvc.bindInputs([model.incomplete], function () {
domClass[model.todos.get("length") ? "add" : "remove" ]("todo-stats", "items_present");
});
mvc.bindInputs([model.complete], function () {
domClass[model.complete.value > 0 ? "add" : "remove" ]("todo-stats", "items_selected");
});
return store.get(initial.id) || initial;
};
/**
* Update the model's "incomplete" value with the
* total number of items not finished. This will automatically
* cause the bound "complete" value to be updated as well.
*/
var updateTotalItemsLeft = function () {
var remaining = 0;
......@@ -43,28 +51,106 @@
model.incomplete.set("value", remaining);
};
model.todos.watch(updateTotalItemsLeft);
/**
* Add new a new todo item as the last element
* in the parent model.
* Set up bindings to the checkbox value, changes
* should automatically update total items left.
*/
var addToModel = function (content, isDone) {
var insert = mvc.newStatefulModel({
data: {content: content, isDone: isDone}
});
mvc.bindInputs([insert.isDone], updateTotalItemsLeft);
bindIsDone(insert);
model.todos.add(model.todos.length, insert);
}
/**
* Set up binding on a todo item, so that when the
* item's checked attribute changes, we re-calculate
* the composite model attribute's value, "complete".
*/
var bindIsDone = function (item) {
mvc.bindInputs([item.isDone], updateTotalItemsLeft);
}
/**
* Create new application Model class, this will be used to bind
* the UI elements to the data store items. Pre-populate model with
* items from localStorage if they exist...
*/
window.model = mvc.newStatefulModel({ data : getLocalStorageItems() });
/**
* Set up a composite model attribute, "complete", that is automatically
* calculated whenever the "incomplete" source value is modified. The "complete"
* attribute is bound to a view widget, displaying the number of items that can
* be cleared using the link.
*/
mvc.bind(model.incomplete, "value", model.complete, "value", function (value) {
return this.model.todos.get("length") - value;
});
/**
* The methods below set up a function binding to the composite (complete & incomplete)
* model attributes. These values are used to append CSS classes to dynamically show & hide
* the "stats" elements when the model is non-empty and has some completed items. Whenever
* the values below are updated, the function will be executed.
*/
mvc.bindInputs([model.incomplete], function () {
domClass[model.todos.get("length") ? "add" : "remove" ]("todo-stats", "items_present");
});
mvc.bindInputs([model.complete], function () {
domClass[model.complete.value > 0 ? "add" : "remove" ]("todo-stats", "items_selected");
});
/**
* Bind all pre-populated todo items to update the
* total item values when the "isDone" attribute is changed.
*/
array.forEach(model.todos, bindIsDone);
/**
* Whenever the "todos" array is modified, an element is added
* or deleted, we need to recompute the model composite values.
* Binding attributes changes to the "updateTotalItemsLeft" function
* using "watch", an attribute on dojo.Stateful objects.
*/
model.todos.watch(updateTotalItemsLeft);
/**
* Update the stats panel for the pre-populated
* items that may have been retrieved from localStorage
*/
updateTotalItemsLeft();
/**
* Global event handlers, used to process user events
* (add item, remove item) generated by the views.
*/
window.addTodoItem = function (input, event) {
if (event.keyCode !== 13) return;
addToModel(input.value, false);
input.value = "";
};
/**
* Remove all items that have been completed from
* model. We have to individually check each todo
* item, removing if true.
*/
window.removeCompletedItems = function () {
var len = model.todos.length, idx = 0;
/**
* Removing array indices from a Dojo MVC Model
* array left-shifts the remaining items. When
* we find an item to remove, don't increment the
* index and, instead, decrement the total item count.
*/
while (idx < len) {
if (model.todos[idx].isDone.value) {
model.todos.remove(idx);
......@@ -75,6 +161,16 @@
idx++;
};
};
/**
* Hook into unload event to trigger persisting
* of the current model contents into the localStorage
* backed data store.
*/
window.onbeforeunload = function () {
model.commit(store);
};
});
</script>
</head>
......@@ -134,7 +230,7 @@
<div id="credits">
Created by
<br />
<a href="http://jamesthom.as/">James Thomas</a>.
<a href="http://jamesthom.as/">James Thomas</a> and <a href="https://github.com/edchat">Ed Chatelain</a>.
</div>
</body>
......
define(
//begin v1.x content
{
"group": ",",
"percentSign": "%",
"exponential": "E",
"scientificFormat": "#E0",
"percentFormat": "#,##0%",
"list": ";",
"infinity": "",
"patternDigit": "#",
"minusSign": "-",
"decimal": ".",
"nan": "NaN",
"nativeZeroDigit": "0",
"perMille": "",
"decimalFormat": "#,##0.###",
"currencyFormat": "¤#,##0.00;(¤#,##0.00)",
"plusSign": "+",
"decimalFormat-short": "000T"
}
//end v1.x content
);
\ No newline at end of file
define({ root:
//begin v1.x content
{
"scientificFormat": "#E0",
"currencySpacing-afterCurrency-currencyMatch": "[:letter:]",
"infinity": "",
"list": ";",
"percentSign": "%",
"minusSign": "-",
"currencySpacing-beforeCurrency-surroundingMatch": "[:digit:]",
"decimalFormat-short": "000T",
"currencySpacing-afterCurrency-insertBetween": " ",
"nan": "NaN",
"nativeZeroDigit": "0",
"plusSign": "+",
"currencySpacing-afterCurrency-surroundingMatch": "[:digit:]",
"currencySpacing-beforeCurrency-currencyMatch": "[:letter:]",
"currencyFormat": "¤ #,##0.00",
"perMille": "",
"group": ",",
"percentFormat": "#,##0%",
"decimalFormat": "#,##0.###",
"decimal": ".",
"patternDigit": "#",
"currencySpacing-beforeCurrency-insertBetween": " ",
"exponential": "E"
}
//end v1.x content
,
"ar": true,
"ca": true,
"cs": true,
"da": true,
"de": true,
"el": true,
"en": true,
"en-au": true,
"en-gb": true,
"es": true,
"fi": true,
"fr": true,
"fr-ch": true,
"he": true,
"hu": true,
"it": true,
"ja": true,
"ko": true,
"nb": true,
"nl": true,
"pl": true,
"pt": true,
"pt-pt": true,
"ro": true,
"ru": true,
"sk": true,
"sl": true,
"sv": true,
"th": true,
"tr": true,
"zh": true,
"zh-hant": true,
"zh-hk": true
});
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
/**
* There's an incompatibility between the Dojo CheckBox and the Dojo MVC
* module. To use them together, I've manually tied the "checked" attribute
* value to push updates to the "value" attribute, which the Dojo MVC module
* expects.
*/
define(["dojo/_base/declare", "dijit/form/CheckBox"], function (declare, CheckBox) {
return declare("todo.form.CheckBox", [CheckBox], {
_setCheckedAttr: function (checked) {
......
/**
* Original source from https://gist.github.com/880822
* Converted to AMD-baseless format
*/
define(["dojo/_base/declare", "dojo/_base/lang", "dojo/_base/json", "dojo/store/util/QueryResults", "dojo/store/util/SimpleQueryEngine"],
function(declare, lang, json, QueryResults, SimpleQueryEngine) {
return declare(null, {
constructor: function(/*dojo.store.LocalStorage*/ options){
// summary:
// localStorage based object store.
// options:
// This provides any configuration information that will be mixed into the store.
// This should generally include the data property to provide the starting set of data.
if (!window.localStorage){
throw Error("LocalStorage not available on this device");
}
lang.mixin(this, options);
this.setData(this.data || []);
},
// idProperty: String
// Indicates the property to use as the identity property. The values of this
// property should be unique.
idProperty: "id",
// queryEngine: Function
// Defines the query engine to use for querying the data store
queryEngine: SimpleQueryEngine,
get: function(id){
// summary:
// Retrieves an object by its identity
// id: Number
// The identity to use to lookup the object
// returns: Object
// The object in the store that matches the given id.
return json.fromJson(localStorage.getItem(id));
},
getIdentity: function(object){
// summary:
// Returns an object's identity
// object: Object
// The object to get the identity from
// returns: Number
return object[this.idProperty];
},
put: function(object, options){
// summary:
// Stores an object
// object: Object
// The object to store.
// options: Object?
// Additional metadata for storing the data. Includes an "id"
// property if a specific id is to be used.
// returns: Number
var id = options && options.id || object[this.idProperty] || Math.random();
localStorage.setItem(id,dojo.toJson(object));
return id;
},
add: function(object, options){
// summary:
// Creates an object, throws an error if the object already exists
// object: Object
// The object to store.
// options: Object?
// Additional metadata for storing the data. Includes an "id"
// property if a specific id is to be used.
// returns: Number
if (this.get(object[this.idProperty])){
throw new Error("Object already exists");
}
return this.put(object, options);
},
remove: function(id){
// summary:
// Deletes an object by its identity
// id: Number
// The identity to use to delete the object
localStorage.removeItem(id);
},
query: function(query, options){
// summary:
// Queries the store for objects.
// query: Object
// The query to use for retrieving objects from the store.
// options: dojo.store.util.SimpleQueryEngine.__queryOptions?
// The optional arguments to apply to the resultset.
// returns: dojo.store.util.QueryResults
// The results of the query, extended with iterative methods.
//
// example:
// Given the following store:
//
// | var store = new dojo.store.LocalStorage({
// | data: [
// | {id: 1, name: "one", prime: false },
// | {id: 2, name: "two", even: true, prime: true},
// | {id: 3, name: "three", prime: true},
// | {id: 4, name: "four", even: true, prime: false},
// | {id: 5, name: "five", prime: true}
// | ]
// | });
//
// ...find all items where "prime" is true:
//
// | var results = store.query({ prime: true });
//
// ...or find all items where "even" is true:
//
// | var results = store.query({ even: true });
var data=[];
for (var i=0; i<localStorage.length;i++){
data.push(this.get(localStorage.key(i)));
}
return QueryResults(this.queryEngine(query, options)(data));
},
setData: function(data){
// summary:
// Sets the given data as the source for this store, and indexes it
// data: Object[]
// An array of objects to use as the source of data.
if(data.items){
// just for convenience with the data format IFRS expects
this.idProperty = data.identifier;
data = this.data = data.items;
}
for(var i = 0, l = data.length; i < l; i++){
var object = data[i];
this.put(object);
}
}
});
});
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