Commit 531d16dd authored by Chris Price's avatar Chris Price Committed by Sindre Sorhus

Closes #172: Closure app update. Fixes #155

parent 265fa96d
# TodoMVC Closure Architecture Example
## Introduction
An example making use of the [Closure toolkit](https://developers.google.com/closure/). Note this project breaks with the convention of the others and uses spaces in place of tabs withing JavaScript files. This is to comply with the Google style guidelines which the Closure Linter enforces (see Linting below).
## Running
A third party build tool called [Plovr](http://plovr.com/) is used to make running and compiling the code easier. To serve the code for development purposes (the example should run in compiled mode without using Plovr), first download the latest stable version from the [Plovr Google Code project](http://code.google.com/p/plovr/downloads/list) (at the time of writing plovr-4b3caf2b7d84.jar). Copy the file into the build folder, rename it plovr.jar and run the following command from this folder -
`java -jar build/plovr.jar serve plovr.json`
You'll also need to change the HTML file so that it references the served files instead of the compiled version (**make sure you comment out the compiled version otherwise it will not work**), to do this remove the compiled script reference and add the following -
`<script type="text/javascript" src="http://localhost:9810/compile?id=todomvc&mode=RAW"></script>`
This will serve up the javascript files in RAW mode which is ideal for rapid development and debugging. To run the compiler, and therefore all the associated type checks etc., change RAW for ADVANCED -
`<script type="text/javascript" src="http://localhost:9810/compile?id=todomvc&mode=ADVANCED"></script>`
## Linting
Whilst Plovr features many of the tools from the Closure toolkit, one very useful one that's missing is the linter. The linter checks for common mistakes in your code, e.g. unused dependencies, whitespace errors. One restriction with the linter is that it will only permit code that adheres to the [Google JavaScript style guide](http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml). In this case that means that we break with the project conventions and use space indentation instead of tabs.
The linter must be installed before use, the installation package is included in the build folder and the instructions are available on the [linter homepage](https://developers.google.com/closure/utilities/). Once installed run the following to check for errors -
`find . -name *.js | xargs gjslint`
(or whatever floats your OSs boat)
## Compiling
To compile the code from the command line run Plovr like so -
`java -jar build/plovr.jar build plovr.json > js/compiled.js`
This will overwrite the js/compiled.js file with the new version, be sure to change the script tag reference in the HTML page.
## Credits
Template by [Sindre Sorhus](http://github.com/sindresorhus)
Created by [Chris Price](http://www.scottlogic.co.uk/blog/chris/)
Part of [TodoMVC](http://todomvc.com)
## License
Public Domain
\ No newline at end of file
plovr-4b3caf2b7d84
\ No newline at end of file
<!doctype html> <!doctype html>
<html> <html lang="en">
<head> <head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Closure • TodoMVC</title> <title>Closure • TodoMVC</title>
<link href="css/todos.css" rel="stylesheet"> <link rel="stylesheet" href="../../assets/base.css">
<!--[if IE]> <!--[if IE]>
<script src="../../assets/ie.js"></script> <script src="../../assets/ie.js"></script>
<![endif]--> <![endif]-->
</head> </head>
<body> <body>
<div id="todoapp"> <section id="todoapp">
<div class="title"> <header id="header">
<h1>Todos</h1> <h1>todos</h1>
</div> <input id="new-todo" placeholder="What needs to be done?" autofocus>
<div class="content"> </header>
<div id="create-todo"> <section id="main">
<input id="new-todo" placeholder="What needs to be done?" type="text"> <input id="toggle-all" type="checkbox">
</div> <label for="toggle-all">Mark all as complete</label>
<div id="todos"> <ul id="todo-list">
<ul id="todo-list"> </ul>
</ul> </section>
</div> <footer id="footer">
<div id="todo-stats"> <ul id="filters">
</div> <li>
</div> <a class="selected" href="#/">All</a>
</div> </li>
<ul id="instructions"> <li>
<li>Click to edit a todo</li> <a href="#/active">Active</a>
</ul> </li>
<div id="credits"> <li>
Created by <a href="http://www.scottlogic.co.uk/blog/chris/">Chris Price</a> <a href="#/completed">Completed</a>
</div> </li>
</ul>
</footer>
</section>
<footer id="info">
<p>Double-click to edit a todo</p>
<p>Template by <a href="http://github.com/sindresorhus">Sindre Sorhus</a></p>
<p>Created by <a href="http://www.scottlogic.co.uk/blog/chris/">Chris Price</a></p>
<p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
</footer>
<script src="../../assets/base.js"></script> <script src="../../assets/base.js"></script>
<!-- The compiled version (to update run java -jar build/plovr.jar build plovr.json > web/compiled.js) --> <!-- The compiled version (to update run java -jar build/plovr.jar build plovr.json > js/compiled.js) -->
<script type="text/javascript" src="js/compiled.js"></script> <script type="text/javascript" src="js/compiled.js"></script>
<!-- The RAW development version (to serve the files run java -jar build/plovr.jar serve plovr.json) --> <!-- The RAW development version (to serve the files run java -jar build/plovr.jar serve plovr.json) -->
<!-- <script type="text/javascript" src="http://localhost:9810/compile?id=todomvc&mode=RAW"></script> --> <!-- <script type="text/javascript" src="http://localhost:9810/compile?id=todomvc&mode=RAW"></script> -->
......
goog.provide('todomvc');
goog.require('goog.History');
goog.require('goog.array');
goog.require('goog.dom.query');
goog.require('goog.events.EventType');
goog.require('goog.events.KeyCodes');
goog.require('goog.storage.Storage');
goog.require('goog.storage.mechanism.mechanismfactory');
goog.require('goog.string');
goog.require('goog.ui.Component');
goog.require('goog.ui.Control');
goog.require('todomvc.model.ToDoItem');
goog.require('todomvc.model.ToDoItemStore');
goog.require('todomvc.view');
goog.require('todomvc.view.ClearCompletedControlRenderer');
goog.require('todomvc.view.ItemCountControlRenderer');
goog.require('todomvc.view.ToDoItemControl');
goog.require('todomvc.view.ToDoListContainer');
/**
* @fileoverview The controller/business logic for the application.
*
* This file creates the interface and marshals changes from the interface
* to the model and back.
*/
/**
* @type {todomvc.model.ToDoItemStore}
*/
var itemStore = new todomvc.model.ToDoItemStore();
itemStore.addEventListener(todomvc.model.ToDoItemStore.ChangeEventType,
redraw);
/**
* @type {todomvc.view.ToDoListContainer}
*/
var container = new todomvc.view.ToDoListContainer();
container.decorate(document.getElementById('todo-list'));
/**
* @type {Element}
*/
var main = document.getElementById('main');
/**
* @type {Element}
*/
var footer = document.getElementById('footer');
/**
* @type {goog.ui.Control}
*/
var itemCountControl = new goog.ui.Control(null,
todomvc.view.ItemCountControlRenderer.getInstance());
itemCountControl.render(footer);
/**
* @type {goog.ui.Control}
*/
var clearCompletedControl = new goog.ui.Control(null,
todomvc.view.ClearCompletedControlRenderer.getInstance());
clearCompletedControl.render(footer);
goog.events.listen(clearCompletedControl,
goog.ui.Component.EventType.ACTION, function(e) {
// go backwards to avoid collection modification problems
goog.array.forEachRight(itemStore.getAll(), function(model) {
if (model.isDone()) {
itemStore.remove(model);
}
});
});
/**
* @type {Element}
*/
var toggleAll = document.getElementById('toggle-all');
goog.events.listen(toggleAll, goog.events.EventType.CLICK, function(e) {
/**
* @type {boolean}
*/
var state = toggleAll.checked;
goog.array.forEach(itemStore.getAll(), function(model) {
/**
* @type {!todomvc.model.ToDoItem}
*/
var updatedModel = new todomvc.model.ToDoItem(
model.getNote(), state, model.getId());
itemStore.addOrUpdate(updatedModel);
});
});
/**
* Enum for the three possible route values
* @enum {!string}
*/
todomvc.Route = {
ALL: '/',
ACTIVE: '/active',
COMPLETED: '/completed'
};
/**
* @type {!todomvc.Route}
*/
var currentRoute = todomvc.Route.ALL;
/**
* @type {!goog.History}
*/
var history = new goog.History();
goog.events.listen(history, goog.history.EventType.NAVIGATE,
function(e) {
// constrain the route to be one of the enum values
switch (e.token) {
case todomvc.Route.ALL:
case todomvc.Route.ACTIVE:
case todomvc.Route.COMPLETED:
if (e.token !== currentRoute) {
currentRoute = e.token;
redraw();
}
break;
default:
history.replaceToken(todomvc.Route.ALL);
break;
}
});
function redraw() {
container.removeChildren(true);
/**
* @type {Array.<todomvc.model.ToDoItem>}
*/
var items = itemStore.getAll();
goog.array.forEach(items, function(item) {
// filter based on current route
if ((currentRoute === todomvc.Route.ACTIVE && item.isDone()) ||
(currentRoute === todomvc.Route.COMPLETED && !item.isDone())) {
return;
}
/**
* @type {todomvc.view.ToDoItemControl}
*/
var control = new todomvc.view.ToDoItemControl();
control.setContent(item.getNote());
control.setChecked(item.isDone());
control.setModel(item);
container.addChild(control, true);
});
var doneCount = /** @type {number} */
(goog.array.reduce(items, function(count, model) {
return model.isDone() ? count + 1 : count;
}, 0));
var remainingCount = items.length - (doneCount);
toggleAll.checked = remainingCount === 0;
itemCountControl.setContent(remainingCount.toString());
clearCompletedControl.setContent(doneCount.toString());
clearCompletedControl.setVisible(doneCount > 0);
goog.style.showElement(main, items.length > 0);
goog.style.showElement(footer, items.length > 0);
/**
* @type {Array.<Element>}
*/
var routeLinks = /** @type {Array.<Element>} */
(goog.dom.query('#filters a'));
goog.array.forEach(routeLinks, function(link, i) {
if ((currentRoute === todomvc.Route.ALL && i === 0) ||
(currentRoute === todomvc.Route.ACTIVE && i === 1) ||
(currentRoute === todomvc.Route.COMPLETED && i === 2)) {
link.className = 'selected';
} else {
link.className = '';
}
});
}
goog.events.listen(container,
todomvc.view.ToDoItemControl.EventType.EDIT, function(e) {
/**
* @type {todomvc.view.ToDoItemControl}
*/
var control = e.target;
/**
* @type {todomvc.model.ToDoItem}
*/
var originalModel = /**@type {todomvc.model.ToDoItem} */
(control.getModel());
/**
* @type {!todomvc.model.ToDoItem}
*/
var updatedModel = new todomvc.model.ToDoItem(
(/**@type {!string} */ control.getContent()),
(/**@type {!boolean} */ control.isChecked()),
originalModel.getId());
itemStore.addOrUpdate(updatedModel);
});
goog.events.listen(container,
todomvc.view.ToDoItemControl.EventType.DESTROY, function(e) {
/**
* @type {todomvc.view.ToDoItemControl}
*/
var control = e.target;
/**
* @type {todomvc.model.ToDoItem}
*/
var model = (/**@type {todomvc.model.ToDoItem} */ control.getModel());
if (model !== null) {
itemStore.remove(model);
}
});
/**
* @type {Element}
*/
var newToDo = document.getElementById('new-todo');
goog.events.listen(newToDo, goog.events.EventType.KEYUP, function(e) {
if (e.keyCode !== goog.events.KeyCodes.ENTER) {
return;
}
// get the text
var value = goog.string.trim(newToDo.value);
if (value === '') {
return;
}
// clear the input box
newToDo.value = '';
// create the item
itemStore.addOrUpdate(new todomvc.model.ToDoItem(value));
});
itemStore.load();
history.setEnabled(true);
goog.require('goog.array');
goog.require('goog.events.EventType');
goog.require('goog.events.KeyCodes');
goog.require('goog.ui.Component');
goog.require('goog.ui.Control');
goog.require('todomvc.model.ToDoItem');
goog.require('todomvc.view');
goog.require('todomvc.view.ClearCompletedControlRenderer');
goog.require('todomvc.view.ItemCountControlRenderer');
goog.require('todomvc.view.ToDoItemControl');
goog.require('todomvc.view.ToDoListContainer');
/**
* @fileoverview The controller/business logic for the application.
*
* This file creates the interface and marshalls changes from the interface to the model and back.
*/
/**
* @type {Array.<todomvc.model.ToDoItem>}
*/
var items = [];
/**
* @type {Element}
*/
var todoStats = document.getElementById('todo-stats');
/**
* @type {goog.ui.Control}
*/
var itemCountControl = new goog.ui.Control(null, todomvc.view.ItemCountControlRenderer.getInstance());
itemCountControl.render(todoStats);
/**
* @type {goog.ui.Control}
*/
var clearCompletedControl = new goog.ui.Control(null, todomvc.view.ClearCompletedControlRenderer.getInstance());
clearCompletedControl.render(todoStats);
goog.events.listen(clearCompletedControl, goog.ui.Component.EventType.ACTION, function(e) {
// go backwards to avoid collection modification problems
goog.array.forEachRight(items, function(model) {
if (model.isDone()) {
goog.array.remove(items, model);
// do optimised model view sync
container.forEachChild(function(control) {
if (control.getModel() === model) {
container.removeChild(control, true);
}
});
}
});
updateStats();
});
function updateStats() {
var doneCount = goog.array.reduce(items, function(count, model) {
return model.isDone() ? count + 1 : count;
}, 0);
var remainingCount = items.length - (/**@type {number}*/ doneCount);
itemCountControl.setContent((/**@type {string}*/ remainingCount));
itemCountControl.setVisible(remainingCount > 0);
clearCompletedControl.setContent((/**@type {string}*/ doneCount));
clearCompletedControl.setVisible((/**@type {number}*/ doneCount) > 0);
}
updateStats();
/**
* @type {todomvc.view.ToDoListContainer}
*/
var container = new todomvc.view.ToDoListContainer();
container.decorate(document.getElementById('todo-list'));
goog.events.listen(container, todomvc.view.ToDoItemControl.EventType.EDIT, function(e) {
/**
* @type {todomvc.view.ToDoItemControl}
*/
var control = e.target;
/**
* @type {todomvc.model.ToDoItem}
*/
var model = (/**@type {todomvc.model.ToDoItem} */ control.getModel());
// do optimised model view sync
model.setNote((/**@type {!string} */ control.getContent()));
model.setDone((/**@type {!boolean} */ control.isChecked()));
updateStats();
});
goog.events.listen(container, todomvc.view.ToDoItemControl.EventType.DESTROY, function(e) {
/**
* @type {todomvc.view.ToDoItemControl}
*/
var control = e.target;
/**
* @type {todomvc.model.ToDoItem}
*/
var model = (/**@type {todomvc.model.ToDoItem} */ control.getModel());
// do optimised model view sync
goog.array.remove(items, model);
container.removeChild(control, true);
updateStats();
});
/**
* @type {Element}
*/
var newToDo = document.getElementById('new-todo');
goog.events.listen(newToDo, goog.events.EventType.KEYUP, function(e) {
if (e.keyCode === goog.events.KeyCodes.ENTER) {
/**
* @type {todomvc.model.ToDoItem}
*/
var model = new todomvc.model.ToDoItem(newToDo.value);
/**
* @type {todomvc.view.ToDoItemControl}
*/
var control = new todomvc.view.ToDoItemControl();
// do optimised model view sync
items.push(model);
control.setContent(model.getNote());
control.setChecked(model.isDone());
control.setModel(model);
container.addChild(control, true);
// clear the input box
newToDo.value = '';
updateStats();
}
});
\ No newline at end of file
...@@ -2,51 +2,73 @@ goog.provide('todomvc.model.ToDoItem'); ...@@ -2,51 +2,73 @@ goog.provide('todomvc.model.ToDoItem');
/** /**
* The model object representing a todo item. * The model object representing a todo item.
* *
* @param {!string} note the text associated with this item * @param {!string} note the text associated with this item.
* @param {!boolean=} opt_done is this item complete? defaults to false * @param {!boolean=} opt_done is this item complete? defaults to false.
* @param {!number=} opt_id the id for the item defaults to 0 meaning undefined.
* @constructor * @constructor
*/ */
todomvc.model.ToDoItem = function(note, opt_done) { todomvc.model.ToDoItem = function(note, opt_done, opt_id) {
/** /**
* note the text associated with this item * note the text associated with this item
* @private * @private
* @type {!string} * @type {!string}
*/ */
this.note_ = note; this.note_ = note;
/** /**
* is this item complete? * is this item complete?
* @private * @private
* @type {!boolean} * @type {!boolean}
*/ */
this.done_ = opt_done || false; this.done_ = opt_done || false;
/**
* the id for the item, or 0 if it is not yet defined
* @private
* @type {!number}
*/
this.id_ = opt_id || 0;
}; };
/** /**
* @return {!string} the text associated with this item * @return {!string} the text associated with this item.
*/ */
todomvc.model.ToDoItem.prototype.getNote = function() { todomvc.model.ToDoItem.prototype.getNote = function() {
return this.note_; return this.note_;
}; };
/** /**
* @return {!boolean} is this item complete? * @return {!boolean} is this item complete?
*/ */
todomvc.model.ToDoItem.prototype.isDone = function() { todomvc.model.ToDoItem.prototype.isDone = function() {
return this.done_; return this.done_;
};
/**
* @return {!number} the id for the item, or 0 if it is not yet defined.
*/
todomvc.model.ToDoItem.prototype.getId = function() {
return this.id_;
}; };
/** /**
* @param {!string} note the text associated with this item * @param {!string} note the text associated with this item.
*/ */
todomvc.model.ToDoItem.prototype.setNote = function(note) { todomvc.model.ToDoItem.prototype.setNote = function(note) {
this.note_ = note; this.note_ = note;
}; };
/** /**
* @param {!boolean} done is this item complete? * @param {!boolean} done is this item complete?
*/ */
todomvc.model.ToDoItem.prototype.setDone = function(done) { todomvc.model.ToDoItem.prototype.setDone = function(done) {
this.done_ = done; this.done_ = done;
}; };
\ No newline at end of file
/**
* @param {!number} id the id for the item, or 0 if it is not yet defined.
*/
todomvc.model.ToDoItem.prototype.setId = function(id) {
this.id_ = id;
};
goog.provide('todomvc.model.ToDoItemStore');
goog.require('goog.array');
goog.require('goog.events.Event');
goog.require('goog.events.EventTarget');
goog.require('goog.storage.Storage');
goog.require('goog.storage.mechanism.mechanismfactory');
goog.require('goog.string');
goog.require('goog.ui.Component');
goog.require('goog.ui.Control');
goog.require('todomvc.model.ToDoItem');
/**
* @constructor
* @extends {goog.events.EventTarget}
*/
todomvc.model.ToDoItemStore = function() {
var mechanism = goog.storage.mechanism.mechanismfactory
.createHTML5LocalStorage();
/**
* @type {goog.storage.Storage}
* @private
*/
this.storage_ = mechanism ? new goog.storage.Storage(mechanism) : null;
/**
* @type {!Array.<todomvc.model.ToDoItem>}
* @private
*/
this.items_ = [];
/**
* Fundamentally flawed approach to ID-ing but fine for demo
* @type {!number}
* @private
*/
this.maxId_ = 0;
};
goog.inherits(todomvc.model.ToDoItemStore, goog.events.EventTarget);
/**
* Load item list from storage
*/
todomvc.model.ToDoItemStore.prototype.load = function() {
if (!this.storage_) {
this.notify_(false);
return; // no storage = no loading!
}
goog.array.clear(this.items_);
/**
* @type {Array.<*>}
*/
var serializedItems = /** @type {Array.<*>} */
(this.storage_.get('todos-closure'));
if (!serializedItems) {
this.notify_(false);
return; // nothing in storage
}
goog.array.forEach(serializedItems, function(serializedItem) {
var item = new todomvc.model.ToDoItem(serializedItem['title'],
serializedItem['completed'], serializedItem['id']);
if (item.getId() > this.maxId_) {
this.maxId_ = item.getId();
}
this.items_.push(item);
}, this);
this.notify_(false);
};
/**
* @param {!todomvc.model.ToDoItem} updatedItem A prototype model to update.
*/
todomvc.model.ToDoItemStore.prototype.addOrUpdate = function(updatedItem) {
var idx = goog.array.findIndex(this.items_, function(item) {
return updatedItem.getId() === item.getId();
});
if (idx === -1) {
if (updatedItem.getId() === 0) {
updatedItem.setId(++this.maxId_);
}
this.items_.push(updatedItem);
} else {
this.items_[idx] = updatedItem;
}
this.notify_();
};
/**
* @param {!todomvc.model.ToDoItem} itemToRemove A prototype model to remove.
*/
todomvc.model.ToDoItemStore.prototype.remove = function(itemToRemove) {
goog.array.removeIf(this.items_, function(item) {
return itemToRemove.getId() === item.getId();
});
this.notify_();
};
/**
* @param {boolean=} opt_save whether to save to storage, defaults to true.
* @private
*/
todomvc.model.ToDoItemStore.prototype.notify_ = function(opt_save) {
// TODO delay until all changes have been made
if (!goog.isDef(opt_save) || opt_save) {
this.save_();
}
this.dispatchEvent(new todomvc.model.ToDoItemStore.ChangeEvent(this));
};
/**
* @return {Array.<todomvc.model.ToDoItem>} All of the stored items.
*/
todomvc.model.ToDoItemStore.prototype.getAll = function() {
return this.items_;
};
/**
* @private
*/
todomvc.model.ToDoItemStore.prototype.save_ = function() {
if (!this.storage_) {
return; // no storage = no saving!
}
/**
* @type {Array.<*>}
*/
var serializedItems = [];
goog.array.forEach(this.items_, function(item) {
serializedItems.push({
'completed' : item.isDone(),
'title': item.getNote(),
'id' : item.getId()
});
});
this.storage_.set('todos-closure', serializedItems);
};
/**
* @const
*/
todomvc.model.ToDoItemStore.ChangeEventType = 'change';
/**
* @constructor
* @extends {goog.events.Event}
* @param {todomvc.model.ToDoItemStore} target The item store.
*/
todomvc.model.ToDoItemStore.ChangeEvent = function(target) {
goog.events.Event.call(this,
todomvc.model.ToDoItemStore.ChangeEventType, target);
};
goog.inherits(todomvc.model.ToDoItemStore.ChangeEvent, goog.events.Event);
...@@ -6,14 +6,15 @@ goog.require('goog.ui.ControlRenderer'); ...@@ -6,14 +6,15 @@ goog.require('goog.ui.ControlRenderer');
/** /**
* A renderer for the clear completed control. * A renderer for the clear completed control.
* *
* @constructor * @constructor
* @extends {goog.ui.ControlRenderer} * @extends {goog.ui.ControlRenderer}
*/ */
todomvc.view.ClearCompletedControlRenderer = function() { todomvc.view.ClearCompletedControlRenderer = function() {
goog.ui.ControlRenderer.call(this); goog.ui.ControlRenderer.call(this);
}; };
goog.inherits(todomvc.view.ClearCompletedControlRenderer, goog.ui.ControlRenderer); goog.inherits(todomvc.view.ClearCompletedControlRenderer,
goog.ui.ControlRenderer);
// add getInstance method to todomvc.view.ClearCompletedControlRenderer // add getInstance method to todomvc.view.ClearCompletedControlRenderer
goog.addSingletonGetter(todomvc.view.ClearCompletedControlRenderer); goog.addSingletonGetter(todomvc.view.ClearCompletedControlRenderer);
...@@ -22,43 +23,47 @@ goog.addSingletonGetter(todomvc.view.ClearCompletedControlRenderer); ...@@ -22,43 +23,47 @@ goog.addSingletonGetter(todomvc.view.ClearCompletedControlRenderer);
* @param {goog.ui.Control} control Control to render. * @param {goog.ui.Control} control Control to render.
* @return {Element} Root element for the control. * @return {Element} Root element for the control.
*/ */
todomvc.view.ClearCompletedControlRenderer.prototype.createDom = function(control) { todomvc.view.ClearCompletedControlRenderer.prototype.createDom =
var html = todomvc.view.clearCompleted({ function(control) {
number : control.getContent() var html = todomvc.view.clearCompleted({
}); number: control.getContent()
var element = (/**@type {!Element}*/ goog.dom.htmlToDocumentFragment(html)); });
this.setAriaStates(control, element); var element = (/**@type {!Element}*/ goog.dom.htmlToDocumentFragment(html));
return element; this.setAriaStates(control, element);
return element;
}; };
/** /**
* @param {Element} element Element to decorate. * @param {Element} element Element to decorate.
* @return {boolean} Whether the renderer can decorate the element. * @return {boolean} Whether the renderer can decorate the element.
*/ */
todomvc.view.ClearCompletedControlRenderer.prototype.canDecorate = function(element) { todomvc.view.ClearCompletedControlRenderer.prototype.canDecorate =
return false; function(element) {
return false;
}; };
/** /**
* @param {Element} element Element to populate. * @param {Element} element Element to populate.
* @param {goog.ui.ControlContent} content Text caption or DOM * @param {goog.ui.ControlContent} content Text caption or DOM.
*/ */
todomvc.view.ClearCompletedControlRenderer.prototype.setContent = function(element, content) { todomvc.view.ClearCompletedControlRenderer.prototype.setContent =
element.innerHTML = todomvc.view.clearCompletedInner({ function(element, content) {
number : content element.innerHTML = todomvc.view.clearCompletedInner({
}); number: content
});
}; };
/** /**
* Updates the appearance of the control in response to a state change. * Updates the appearance of the control in response to a state change.
* *
* @param {goog.ui.Control} control Control instance to update. * @param {goog.ui.Control} control Control instance to update.
* @param {goog.ui.Component.State} state State to enable or disable. * @param {goog.ui.Component.State} state State to enable or disable.
* @param {boolean} enable Whether the control is entering or exiting the state. * @param {boolean} enable Whether the control is entering or exiting the state.
*/ */
todomvc.view.ClearCompletedControlRenderer.prototype.setState = function(control, state, enable) { todomvc.view.ClearCompletedControlRenderer.prototype.setState =
var element = control.getElement(); function(control, state, enable) {
if (element) { var element = control.getElement();
this.updateAriaState(element, state, enable); if (element) {
} this.updateAriaState(element, state, enable);
}
}; };
...@@ -6,12 +6,12 @@ goog.require('goog.ui.ControlRenderer'); ...@@ -6,12 +6,12 @@ goog.require('goog.ui.ControlRenderer');
/** /**
* A renderer for the item count control. * A renderer for the item count control.
* *
* @constructor * @constructor
* @extends {goog.ui.ControlRenderer} * @extends {goog.ui.ControlRenderer}
*/ */
todomvc.view.ItemCountControlRenderer = function() { todomvc.view.ItemCountControlRenderer = function() {
goog.ui.ControlRenderer.call(this); goog.ui.ControlRenderer.call(this);
}; };
goog.inherits(todomvc.view.ItemCountControlRenderer, goog.ui.ControlRenderer); goog.inherits(todomvc.view.ItemCountControlRenderer, goog.ui.ControlRenderer);
...@@ -23,42 +23,45 @@ goog.addSingletonGetter(todomvc.view.ItemCountControlRenderer); ...@@ -23,42 +23,45 @@ goog.addSingletonGetter(todomvc.view.ItemCountControlRenderer);
* @return {Element} Root element for the control. * @return {Element} Root element for the control.
*/ */
todomvc.view.ItemCountControlRenderer.prototype.createDom = function(control) { todomvc.view.ItemCountControlRenderer.prototype.createDom = function(control) {
var html = todomvc.view.itemCount({ var html = todomvc.view.itemCount({
number : control.getContent() number: control.getContent()
}); });
var element = (/**@type {!Element}*/ goog.dom.htmlToDocumentFragment(html)); var element = (/**@type {!Element}*/ goog.dom.htmlToDocumentFragment(html));
this.setAriaStates(control, element); this.setAriaStates(control, element);
return element; return element;
}; };
/** /**
* @param {Element} element Element to decorate. * @param {Element} element Element to decorate.
* @return {boolean} Whether the renderer can decorate the element. * @return {boolean} Whether the renderer can decorate the element.
*/ */
todomvc.view.ItemCountControlRenderer.prototype.canDecorate = function(element) { todomvc.view.ItemCountControlRenderer.prototype.canDecorate =
return false; function(element) {
return false;
}; };
/** /**
* @param {Element} element Element to populate. * @param {Element} element Element to populate.
* @param {goog.ui.ControlContent} content Text caption or DOM * @param {goog.ui.ControlContent} content Text caption or DOM.
*/ */
todomvc.view.ItemCountControlRenderer.prototype.setContent = function(element, content) { todomvc.view.ItemCountControlRenderer.prototype.setContent =
element.innerHTML = todomvc.view.itemCountInner({ function(element, content) {
number : content element.innerHTML = todomvc.view.itemCountInner({
}); number: content
});
}; };
/** /**
* Updates the appearance of the control in response to a state change. * Updates the appearance of the control in response to a state change.
* *
* @param {goog.ui.Control} control Control instance to update. * @param {goog.ui.Control} control Control instance to update.
* @param {goog.ui.Component.State} state State to enable or disable. * @param {goog.ui.Component.State} state State to enable or disable.
* @param {boolean} enable Whether the control is entering or exiting the state. * @param {boolean} enable Whether the control is entering or exiting the state.
*/ */
todomvc.view.ItemCountControlRenderer.prototype.setState = function(control, state, enable) { todomvc.view.ItemCountControlRenderer.prototype.setState =
var element = control.getElement(); function(control, state, enable) {
if (element) { var element = control.getElement();
this.updateAriaState(element, state, enable); if (element) {
} this.updateAriaState(element, state, enable);
}
}; };
...@@ -2,123 +2,149 @@ goog.provide('todomvc.view.ToDoItemControl'); ...@@ -2,123 +2,149 @@ goog.provide('todomvc.view.ToDoItemControl');
goog.require('goog.dom'); goog.require('goog.dom');
goog.require('goog.events'); goog.require('goog.events');
goog.require('goog.events.KeyCodes');
goog.require('goog.string');
goog.require('goog.ui.Component.State'); goog.require('goog.ui.Component.State');
goog.require('goog.ui.Control'); goog.require('goog.ui.Control');
goog.require('todomvc.view.ToDoItemControlRenderer'); goog.require('todomvc.view.ToDoItemControlRenderer');
/** /**
* A control representing each item in the todo list. It makes use of the CHECKED and SELECTED states to represent being * A control representing each item in the todo list. It makes use of the
* done and being in edit mode. * CHECKED and SELECTED states to represent being done and being in edit mode.
* *
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for document interaction. * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper,
* used for document interaction.
* @constructor * @constructor
* @extends {goog.ui.Control} * @extends {goog.ui.Control}
*/ */
todomvc.view.ToDoItemControl = function(opt_domHelper) { todomvc.view.ToDoItemControl = function(opt_domHelper) {
goog.ui.Control.call(this, "", todomvc.view.ToDoItemControlRenderer goog.ui.Control.call(this, '', todomvc.view.ToDoItemControlRenderer
.getInstance(), opt_domHelper); .getInstance(), opt_domHelper);
// enable CHECKED and SELECTED states // enable CHECKED and SELECTED states
this.setSupportedState(goog.ui.Component.State.CHECKED, true); this.setSupportedState(goog.ui.Component.State.CHECKED, true);
this.setSupportedState(goog.ui.Component.State.SELECTED, true); this.setSupportedState(goog.ui.Component.State.SELECTED, true);
// disable auto handling of CHECKED and SELECTED states
this.setAutoStates(goog.ui.Component.State.CHECKED, false);
this.setAutoStates(goog.ui.Component.State.SELECTED, false);
// allow text selection within this control
this.setAllowTextSelection(true);
// disable auto handling of CHECKED and SELECTED states
this.setAutoStates(goog.ui.Component.State.CHECKED, false);
this.setAutoStates(goog.ui.Component.State.SELECTED, false);
// allow text selection
this.setAllowTextSelection(true);
}; };
goog.inherits(todomvc.view.ToDoItemControl, goog.ui.Control); goog.inherits(todomvc.view.ToDoItemControl, goog.ui.Control);
/**
* The event types this control dispatches.
*/
todomvc.view.ToDoItemControl.EventType = { todomvc.view.ToDoItemControl.EventType = {
EDIT: "edit", EDIT: 'edit',
DESTROY: "destroy" DESTROY: 'destroy'
}; };
/** /**
* Configures the component after its DOM has been rendered, and sets up event * Configures the component after its DOM has been rendered, and sets up event
* handling. Overrides {@link goog.ui.Component#enterDocument}. * handling. Overrides {@link goog.ui.Component#enterDocument}.
* *
* @override * @override
*/ */
todomvc.view.ToDoItemControl.prototype.enterDocument = function() { todomvc.view.ToDoItemControl.prototype.enterDocument = function() {
todomvc.view.ToDoItemControl.superClass_.enterDocument.call(this); todomvc.view.ToDoItemControl.superClass_.enterDocument.call(this);
// prevent clicking the checkbox (or anything within the root element) // prevent clicking the checkbox (or anything within the root element)
// from having any default behaviour. This stops the checkbox being set // from having any default behaviour. This stops the checkbox being set
// by the browser. // by the browser.
this.getHandler().listen(this.getElement(), goog.events.EventType.CLICK, this.getHandler().listen(this.getElement(), goog.events.EventType.CLICK,
function(e) { function(e) {
e.preventDefault(); e.preventDefault();
}); });
this.getHandler().listen(this.getElement(), goog.events.EventType.DBLCLICK,
function(e) {
this.setSelected(true);
});
/**
* @type {Element}
*/
var inputElement = this.getRenderer().getInputElement(
this.getElement());
this.getHandler().listen(inputElement, goog.events.EventType.KEYUP,
function(e) {
var be = e.getBrowserEvent();
if (be.keyCode === goog.events.KeyCodes.ENTER) {
this.setFocused(false);
}
});
}; };
/** /**
* Returns the renderer used by this component to render itself or to decorate * Returns the renderer used by this component to render itself or to decorate
* an existing element. * an existing element.
* *
* @return {todomvc.view.ToDoItemControlRenderer} Renderer used by the component * @return {todomvc.view.ToDoItemControlRenderer} Renderer used by the
* component.
*/ */
todomvc.view.ToDoItemControl.prototype.getRenderer = function() { todomvc.view.ToDoItemControl.prototype.getRenderer = function() {
return (/**@type {todomvc.view.ToDoItemControlRenderer}*/ this.renderer_); return (/**@type {todomvc.view.ToDoItemControlRenderer}*/ this.renderer_);
}; };
/** /**
* Specialised handling of mouse events when clicking on the checkbox, label, * Specialised handling of mouse events when clicking on the checkbox, label,
* textbox or remove link. * textbox or remove link.
* *
* @param {goog.events.Event} e Mouse event to handle. * @param {goog.events.Event} e Mouse event to handle.
*/ */
todomvc.view.ToDoItemControl.prototype.handleMouseUp = function(e) { todomvc.view.ToDoItemControl.prototype.handleMouseUp = function(e) {
todomvc.view.ToDoItemControl.superClass_.handleMouseUp.call(this, e); todomvc.view.ToDoItemControl.superClass_.handleMouseUp.call(this, e);
if (this.isEnabled()) { if (this.isEnabled()) {
if (e.target === this.getRenderer().getCheckboxElement( if (e.target === this.getRenderer().getCheckboxElement(
this.getElement())) { this.getElement())) {
this.setChecked(!this.isChecked()); this.setChecked(!this.isChecked());
this.dispatchEvent(todomvc.view.ToDoItemControl.EventType.EDIT); this.dispatchEvent(todomvc.view.ToDoItemControl.EventType.EDIT);
} else if (e.target === this.getRenderer().getDestroyElement( } else if (e.target === this.getRenderer().getDestroyElement(
this.getElement())) { this.getElement())) {
this.dispatchEvent(todomvc.view.ToDoItemControl.EventType.DESTROY); this.dispatchEvent(todomvc.view.ToDoItemControl.EventType.DESTROY);
} else if (!this.isSelected()) { }
this.setSelected(true); }
}
}
}; };
/** /**
* Override the behaviour when the control is unfocused. * Override the behaviour when the control is unfocused.
* @param {boolean} focused * @param {boolean} focused is focused?
*/ */
todomvc.view.ToDoItemControl.prototype.setFocused = function(focused) { todomvc.view.ToDoItemControl.prototype.setFocused = function(focused) {
todomvc.view.ToDoItemControl.superClass_.setFocused.call(this, focused); todomvc.view.ToDoItemControl.superClass_.setFocused.call(this, focused);
if (!focused && this.isSelected()) { if (!focused && this.isSelected()) {
/** /**
* @type {Element} * @type {Element}
*/ */
var inputElement = this.getRenderer().getInputElement( var inputElement = this.getRenderer().getInputElement(
this.getElement()); this.getElement());
this.setContent(inputElement.value); var value = goog.string.trim(inputElement.value);
this.setSelected(false); if (value === '') {
this.dispatchEvent(todomvc.view.ToDoItemControl.EventType.EDIT); this.dispatchEvent(todomvc.view.ToDoItemControl.EventType.DESTROY);
} } else {
this.setContent(value);
this.setSelected(false);
this.dispatchEvent(todomvc.view.ToDoItemControl.EventType.EDIT);
}
}
}; };
/** /**
* Override the behaviour to switch to editing mode when the control is selected * Override the behaviour to switch to editing mode when the control is selected
* @param {boolean} selected * @param {boolean} selected is selected?
*/ */
todomvc.view.ToDoItemControl.prototype.setSelected = function(selected) { todomvc.view.ToDoItemControl.prototype.setSelected = function(selected) {
todomvc.view.ToDoItemControl.superClass_.setSelected.call(this, selected); todomvc.view.ToDoItemControl.superClass_.setSelected.call(this, selected);
if (selected) { // populate the input box when selected
/** if (selected) {
* @type {Element} /**
*/ * @type {Element}
var inputElement = this.getRenderer().getInputElement( */
this.getElement()); var inputElement = this.getRenderer().getInputElement(
inputElement.value = this.getContent(); this.getElement());
inputElement.focus(); inputElement.value = this.getContent();
} inputElement.select();
}; }
\ No newline at end of file };
...@@ -4,14 +4,14 @@ goog.require('goog.ui.Component.State'); ...@@ -4,14 +4,14 @@ goog.require('goog.ui.Component.State');
goog.require('goog.ui.ControlRenderer'); goog.require('goog.ui.ControlRenderer');
/** /**
* The renderer for the ToDoItemControl which has knowledge of the DOM structure of the Control and the applicable CSS * The renderer for the ToDoItemControl which has knowledge of the DOM
* classes. * structure of the Control and the applicable CSS classes.
* *
* @constructor * @constructor
* @extends {goog.ui.ControlRenderer} * @extends {goog.ui.ControlRenderer}
*/ */
todomvc.view.ToDoItemControlRenderer = function() { todomvc.view.ToDoItemControlRenderer = function() {
goog.ui.ControlRenderer.call(this); goog.ui.ControlRenderer.call(this);
}; };
goog.inherits(todomvc.view.ToDoItemControlRenderer, goog.ui.ControlRenderer); goog.inherits(todomvc.view.ToDoItemControlRenderer, goog.ui.ControlRenderer);
...@@ -23,36 +23,40 @@ goog.addSingletonGetter(todomvc.view.ToDoItemControlRenderer); ...@@ -23,36 +23,40 @@ goog.addSingletonGetter(todomvc.view.ToDoItemControlRenderer);
* @return {Element} Root element for the control. * @return {Element} Root element for the control.
*/ */
todomvc.view.ToDoItemControlRenderer.prototype.createDom = function(control) { todomvc.view.ToDoItemControlRenderer.prototype.createDom = function(control) {
var html = todomvc.view.toDoItem({ var html = todomvc.view.toDoItem({
content : control.getContent() content: control.getContent(),
}); checked: control.isChecked()
var element = (/**@type {!Element}*/ goog.dom.htmlToDocumentFragment(html)); });
this.setAriaStates(control, element); var element = (/**@type {!Element}*/ goog.dom.htmlToDocumentFragment(html));
return element; this.setAriaStates(control, element);
this.setState(control, /** @type {goog.ui.Component.State} */
(control.getState()), true);
return element;
}; };
/** /**
* Updates the appearance of the control in response to a state change. * Updates the appearance of the control in response to a state change.
* *
* @param {goog.ui.Control} control Control instance to update. * @param {goog.ui.Control} control Control instance to update.
* @param {goog.ui.Component.State} state State to enable or disable. * @param {goog.ui.Component.State} state State to enable or disable.
* @param {boolean} enable Whether the control is entering or exiting the state. * @param {boolean} enable Whether the control is entering or exiting the state.
*/ */
todomvc.view.ToDoItemControlRenderer.prototype.setState = function(control, state, enable) { todomvc.view.ToDoItemControlRenderer.prototype.setState =
var element = control.getElement(); function(control, state, enable) {
if (element) { var element = control.getElement();
switch (state) { if (element) {
case goog.ui.Component.State.CHECKED: switch (state) {
this.enableClassName(control, "done", enable); case goog.ui.Component.State.CHECKED:
this.getCheckboxElement(element).checked = enable; this.enableClassName(control, 'done', enable);
break; this.getCheckboxElement(element).checked = enable;
case goog.ui.Component.State.SELECTED: break;
this.enableClassName(control, "editing", enable); case goog.ui.Component.State.SELECTED:
break; this.enableClassName(control, 'editing', enable);
} break;
}
this.updateAriaState(element, state, enable); this.updateAriaState(element, state, enable);
} }
}; };
/** /**
...@@ -63,67 +67,68 @@ todomvc.view.ToDoItemControlRenderer.prototype.setState = function(control, stat ...@@ -63,67 +67,68 @@ todomvc.view.ToDoItemControlRenderer.prototype.setState = function(control, stat
* returned. * returned.
* @return {Element} The key event target. * @return {Element} The key event target.
*/ */
todomvc.view.ToDoItemControlRenderer.prototype.getKeyEventTarget = function(control) { todomvc.view.ToDoItemControlRenderer.prototype.getKeyEventTarget =
function(control) {
return this.getInputElement(control.getElement()); return this.getInputElement(control.getElement());
}; };
/** /**
* Takes the control's root element and returns the display element * Takes the control's root element and returns the display element
* *
* @param {Element} element Root element of the control whose display element is * @param {Element} element Root element of the control whose display element is
* to be returned. * to be returned.
* @return {Element} The control's display element. * @return {Element} The control's display element.
*/ */
todomvc.view.ToDoItemControlRenderer.prototype.getDisplayElement = function( todomvc.view.ToDoItemControlRenderer.prototype.getDisplayElement = function(
element) { element) {
return element ? element.childNodes[0].childNodes[0] : null; return element ? element.childNodes[0] : null;
}; };
/** /**
* Takes the control's root element and returns the parent element of the * Takes the control's root element and returns the parent element of the
* control's contents. * control's contents.
* *
* @param {Element} element Root element of the control whose content element is * @param {Element} element Root element of the control whose content element is
* to be returned. * to be returned.
* @return {Element} The control's content element. * @return {Element} The control's content element.
*/ */
todomvc.view.ToDoItemControlRenderer.prototype.getContentElement = function( todomvc.view.ToDoItemControlRenderer.prototype.getContentElement = function(
element) { element) {
return element ? this.getDisplayElement(element).childNodes[1] : null; return element ? this.getDisplayElement(element).childNodes[1] : null;
}; };
/** /**
* Takes the control's root element and returns the checkbox element * Takes the control's root element and returns the checkbox element
* *
* @param {Element} element Root element of the control whose checkbox element * @param {Element} element Root element of the control whose checkbox element
* is to be returned. * is to be returned.
* @return {Element} The control's checkbox element. * @return {Element} The control's checkbox element.
*/ */
todomvc.view.ToDoItemControlRenderer.prototype.getCheckboxElement = function( todomvc.view.ToDoItemControlRenderer.prototype.getCheckboxElement = function(
element) { element) {
return element ? this.getDisplayElement(element).childNodes[0] : null; return element ? this.getDisplayElement(element).childNodes[0] : null;
}; };
/** /**
* Takes the control's root element and returns the destroy element * Takes the control's root element and returns the destroy element
* *
* @param {Element} element Root element of the control whose destroy element is * @param {Element} element Root element of the control whose destroy element is
* to be returned. * to be returned.
* @return {Element} The control's destroy element. * @return {Element} The control's destroy element.
*/ */
todomvc.view.ToDoItemControlRenderer.prototype.getDestroyElement = function( todomvc.view.ToDoItemControlRenderer.prototype.getDestroyElement = function(
element) { element) {
return element ? this.getDisplayElement(element).childNodes[2] : null; return element ? this.getDisplayElement(element).childNodes[2] : null;
}; };
/** /**
* Takes the control's root element and returns the input element * Takes the control's root element and returns the input element
* *
* @param {Element} element Root element of the control whose input element is * @param {Element} element Root element of the control whose input element is
* to be returned. * to be returned.
* @return {Element} The control's input element. * @return {Element} The control's input element.
*/ */
todomvc.view.ToDoItemControlRenderer.prototype.getInputElement = function( todomvc.view.ToDoItemControlRenderer.prototype.getInputElement = function(
element) { element) {
return element ? element.childNodes[0].childNodes[1].childNodes[0] : null; return element ? element.childNodes[1] : null;
}; };
...@@ -5,31 +5,33 @@ goog.require('goog.ui.Container'); ...@@ -5,31 +5,33 @@ goog.require('goog.ui.Container');
goog.require('todomvc.view.ToDoListContainerRenderer'); goog.require('todomvc.view.ToDoListContainerRenderer');
/** /**
* A container for the ToDoItemControls, overridden to support keyboard focus on child controls. * A container for the ToDoItemControls, overridden to support keyboard focus
* * on child controls.
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for document interaction. *
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
* document interaction.
* @constructor * @constructor
* @extends {goog.ui.Container} * @extends {goog.ui.Container}
*/ */
todomvc.view.ToDoListContainer = function(opt_domHelper) { todomvc.view.ToDoListContainer = function(opt_domHelper) {
goog.ui.Container goog.ui.Container
.call(this, goog.ui.Container.Orientation.VERTICAL, .call(this, goog.ui.Container.Orientation.VERTICAL,
todomvc.view.ToDoListContainerRenderer.getInstance(), todomvc.view.ToDoListContainerRenderer.getInstance(),
opt_domHelper); opt_domHelper);
// allow focus on children // allow focus on children
this.setFocusable(false); this.setFocusable(false);
this.setFocusableChildrenAllowed(true); this.setFocusableChildrenAllowed(true);
}; };
goog.inherits(todomvc.view.ToDoListContainer, goog.ui.Container); goog.inherits(todomvc.view.ToDoListContainer, goog.ui.Container);
/** /**
* Override this method to allow text selection in children. * Override this method to allow text selection in children.
* *
* @param {goog.events.BrowserEvent} e Mousedown event to handle. * @param {goog.events.BrowserEvent} e Mousedown event to handle.
*/ */
todomvc.view.ToDoListContainer.prototype.handleMouseDown = function(e) { todomvc.view.ToDoListContainer.prototype.handleMouseDown = function(e) {
if (this.enabled_) { if (this.enabled_) {
this.setMouseButtonPressed(true); this.setMouseButtonPressed(true);
} }
}; };
\ No newline at end of file
...@@ -5,37 +5,40 @@ goog.require('goog.ui.Container'); ...@@ -5,37 +5,40 @@ goog.require('goog.ui.Container');
goog.require('goog.ui.ContainerRenderer'); goog.require('goog.ui.ContainerRenderer');
/** /**
* A renderer for the container, overridden to support keyboard focus on child controls. * A renderer for the container, overridden to support keyboard focus
* on child controls.
* @constructor * @constructor
* @extends {goog.ui.ContainerRenderer} * @extends {goog.ui.ContainerRenderer}
*/ */
todomvc.view.ToDoListContainerRenderer = function() { todomvc.view.ToDoListContainerRenderer = function() {
goog.ui.ContainerRenderer.call(this); goog.ui.ContainerRenderer.call(this);
}; };
goog.inherits(todomvc.view.ToDoListContainerRenderer, goog.inherits(todomvc.view.ToDoListContainerRenderer,
goog.ui.ContainerRenderer); goog.ui.ContainerRenderer);
goog.addSingletonGetter(todomvc.view.ToDoListContainerRenderer); goog.addSingletonGetter(todomvc.view.ToDoListContainerRenderer);
/** /**
* @param {Element} element Element to decorate. * @param {Element} element Element to decorate.
* @return {boolean} Whether the renderer can decorate the element. * @return {boolean} Whether the renderer can decorate the element.
*/ */
todomvc.view.ToDoListContainerRenderer.prototype.canDecorate = function(element) { todomvc.view.ToDoListContainerRenderer.prototype.canDecorate =
return element.tagName == 'UL'; function(element) {
return element.tagName == 'UL';
}; };
/** /**
* Override this method to allow text selection in children * Override this method to allow text selection in children
* *
* @param {goog.ui.Container} container Container whose DOM is to be initialized * @param {goog.ui.Container} container Container whose DOM is to be initialized
* as it enters the document. * as it enters the document.
*/ */
todomvc.view.ToDoListContainerRenderer.prototype.initializeDom = function(container) { todomvc.view.ToDoListContainerRenderer.prototype.initializeDom =
var elem = (/**@type {!Element}*/ container.getElement()); function(container) {
var elem = (/**@type {!Element}*/ container.getElement());
// Set the ARIA role. // Set the ARIA role.
var ariaRole = this.getAriaRole(); var ariaRole = this.getAriaRole();
if (ariaRole) { if (ariaRole) {
goog.dom.a11y.setRole(elem, ariaRole); goog.dom.a11y.setRole(elem, ariaRole);
} }
}; };
\ No newline at end of file
...@@ -3,20 +3,17 @@ ...@@ -3,20 +3,17 @@
/** /**
* A todo list item template * A todo list item template
* @param content the label for this item * @param content the label for this item
* @param checked whether the item is checked
*/ */
{template .toDoItem} {template .toDoItem}
<li> <li>
<div> <div class="view">
<div class="display"> <input class="toggle" type="checkbox" {if $checked}checked{/if}>
<input class="check" type="checkbox" /> <label>{$content}</label>
<div class="todo-content" style="cursor: pointer;">{$content}</div> <button class="destroy"></button>
<span class="todo-destroy"></span> </div>
</div> <input class="edit" value="Rule the web">
<div class="edit"> </li>
<input class="todo-input" type="text"/>
</div>
</div>
</li>
{/template} {/template}
/** /**
...@@ -24,9 +21,7 @@ ...@@ -24,9 +21,7 @@
* @param number the count of items * @param number the count of items
*/ */
{template .itemCount} {template .itemCount}
<span class="todo-count"> <span id="todo-count">{call .itemCountInner data="all"/}</span>
{call .itemCountInner data="all"/}
</span>
{/template} {/template}
/** /**
...@@ -34,7 +29,7 @@ ...@@ -34,7 +29,7 @@
* @param number the count of items * @param number the count of items
*/ */
{template .itemCountInner} {template .itemCountInner}
<span class="number">{$number}</span> <span class="word">{if $number > 1}items{else}item{/if}</span> left. <strong>{$number}</strong> {if $number == 1}item{else}items{/if} left
{/template} {/template}
/** /**
...@@ -42,9 +37,7 @@ ...@@ -42,9 +37,7 @@
* @param number the count of items * @param number the count of items
*/ */
{template .clearCompleted} {template .clearCompleted}
<span class="todo-clear"> <button id="clear-completed">{call .clearCompletedInner data="all"/}</button>
{call .clearCompletedInner data="all"/}
</span>
{/template} {/template}
/** /**
...@@ -52,7 +45,5 @@ ...@@ -52,7 +45,5 @@
* @param number the count of items * @param number the count of items
*/ */
{template .clearCompletedInner} {template .clearCompletedInner}
<a href="#"> Clear completed ({$number})
Clear <span class="number-done">{$number}</span> <span class="word-done">{if $number > 1}items{else}item{/if}</span>
</a>
{/template} {/template}
\ No newline at end of file
{ {
"id" : "todomvc", "id" : "todomvc",
"inputs" : "js/main.js", "inputs" : "js/app.js",
"paths" : "js/", "paths" : "js/",
"output-wrapper" : "(function(){%output%})();", "output-wrapper" : "(function(){%output%})();",
"mode" : "ADVANCED", "mode" : "ADVANCED",
......
File mode changed from 100755 to 100644
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