Commit 9e536a3c authored by Rhsy's avatar Rhsy Committed by Sindre Sorhus

Close #163: PlastronJS app

parent 2b4a9d49
# PlastronJS • [TodoMVC](http://todomvc.com)
A todo app using [PlastronJS](https://github.com/rhysbrettbowen/PlastronJS)
and [Closure Tools](https://developers.google.com/closure/)
## Run
Just open the index.html in your browser
## Run uncompiled
The app is built with [Plovr](http://plovr.com/). To run the unminified version you need to [download Plovr](http://plovr.com/download.html), create a directory called "build" and put the jar file in the build folder.
You will then need to [download PlastronJS](https://github.com/rhysbrettbowen/PlastronJS) to js/lib (you will need to create the folder).
Next open a command prompt in the base directory of the plastronjs todomvc example and type in.
```
java -jar build/plovr.jar serve plovr.json
```
Change the script src at the bottom of the index.html from js/compiled.js to:
```
http://localhost:9810/compile?id=todomvc&mode=raw
```
You can now view the uncompiled example and play around with it!
# Compilation
Once you have done the steps above you can compile any changes you make by running the below command:
```
java -jar build/plovr.jar build plovr.json
```
and view the compiled version fo the page by changing the bottom script src back to js/compiled.
## Need help?
shoot me a quick message on [twitter](https://twitter.com/#!/RhysBB)
## Credit
Created by [Rhys Brett-Bowen](http://rhysbrettbowen.com)
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>PlastronJS • TodoMVC</title>
<link rel="stylesheet" href="../../../assets/base.css">
<!--[if IE]>
<script src="../../../assets/ie.js"></script>
<![endif]-->
</head>
<body>
<section id="todoapp">
<header id="header">
<h1>todos</h1>
<input id="new-todo" class="todo-entry" placeholder="What needs to be done?" autofocus>
</header>
<section id="main">
<input id="toggle-all" class="toggle-all" type="checkbox">
<label for="toggle-all">Mark all as complete</label>
<ul id="todo-list"></ul>
</section>
<footer id="footer">
<span id="todo-count"><strong>0</strong> item left</span>
<ul id="filters">
<li>
<a class="selected" href="#/">All</a>
</li>
<li>
<a href="#/active">Active</a>
</li>
<li>
<a href="#/completed">Completed</a>
</li>
</ul>
<button id="clear-completed" class="clear-completed">Clear completed</button>
</footer>
</section>
<footer id="info">
<p>Double-click to edit a todo</p>
<p>Created by <a href="http://rhysbrettbowen.com">Rhys Brett-Bowen</a> (<a href="https://twitter.com/#!/RhysBB">RhysBB</a>).</p>
<p>Using <a href="https://github.com/rhysbrettbowen/PlastronJS">PlastronJS</a> and <a href="https://developers.google.com/closure/">Closure Tools</a></p>
<p>Part of <a href="http://todomvc.com">TodoMVC</a>.</p>
</footer>
<script src="../../../assets/base.js"></script>
<!-- <script src="http://localhost:9810/compile?id=todomvc&mode=raw"></script> -->
<script src="js/compiled.js"></script>
</body>
</html>
\ No newline at end of file
goog.require('goog.dom');
goog.require('goog.dom.classes');
goog.require('mvc.Collection');
goog.require('mvc.Router');
goog.require('todomvc.listcontrol');
goog.require('todomvc.listmodel');
var todolist = new todomvc.listmodel();
//create the control for the collection.
var todolistControl = new todomvc.listcontrol(todolist);
todolistControl.decorate(goog.dom.getElement('todoapp'));
//setup router
var router = new mvc.Router();
/**
* toggles selected class on filters list
*
* @param {string} chosenFilter selected filter by name.
*/
var toggleFilters = function(chosenFilter) {
var filters = goog.dom.getElementsByTagNameAndClass('A', undefined,
goog.dom.getElement('filters'));
goog.array.forEach(filters, function(filter) {
goog.dom.classes.enable(filter, 'selected',
goog.dom.getTextContent(filter) === chosenFilter);
});
};
router.route('/', function() {
todolistControl.setFilter(todomvc.listcontrol.Filter.ALL);
toggleFilters('All');
});
router.route('/active', function() {
todolistControl.setFilter(todomvc.listcontrol.Filter.ACTIVE);
toggleFilters('Active');
});
router.route('/completed', function() {
todolistControl.setFilter(todomvc.listcontrol.Filter.COMPLETED);
toggleFilters('Completed');
});
This diff is collapsed.
goog.provide('todomvc.listcontrol');
goog.require('goog.dom');
goog.require('goog.events.KeyCodes');
goog.require('goog.string');
goog.require('mvc.Control');
goog.require('todomvc.templates');
goog.require('todomvc.todocontrol');
/**
* the control for the todo list, handles the page as well
*
* @constructor
* @param {mvc.Collection} list model for todo items.
* @extends {mvc.Control}
*/
todomvc.listcontrol = function(list) {
goog.base(this, list);
this.filter_ = todomvc.listcontrol.Filter.ALL;
};
goog.inherits(todomvc.listcontrol, mvc.Control);
/**
* @enum {Function}
*/
todomvc.listcontrol.Filter = {
ALL: function() {return true},
ACTIVE: function(model) {return !model.get('completed')},
COMPLETED: function(model) {return model.get('completed')}
};
/**
* setup for event listeners.
*
* @inheritDoc
*/
todomvc.listcontrol.prototype.enterDocument = function() {
goog.base(this, 'enterDocument');
var list = /** @type {Object} */(this.getModel());
// create new model from text box
var input = this.getEls('input')[0];
this.on(goog.events.EventType.KEYUP, function(e) {
// on return get trimmed text
if (e.keyCode !== goog.events.KeyCodes.ENTER) return;
var text = goog.string.trim(input.value);
if (text === '') return;
//create new model
list.newModel({'title': text});
input.value = '';
}, 'todo-entry');
// clear completed
this.click(function(e) {
goog.array.forEach(list.get('completed'), function(model) {
model.dispose();
});
}, 'clear-completed');
// toggle completed
this.click(function(e) {
var checked = e.target.checked;
goog.array.forEach(list.getModels(), function(model) {
model.set('completed', checked);
});
}, 'toggle-all');
// refresh the view on changes that effect the models order
this.anyModelChange(function() {
this.refresh();
list.save();
}, this);
// hide/show footer and main body
this.modelChange(function() {
this.showMainFooter(!!list.getLength());
}, this);
this.showMainFooter(!!list.getLength());
// update counts
this.bind('completed', function(completedModels) {
// update "left" count
soy.renderElement(goog.dom.getElement('todo-count'),
todomvc.templates.itemsLeft, {
left: list.getLength() - completedModels.length
});
// update clear button
var clearButton = goog.dom.getElement('clear-completed');
goog.dom.setTextContent(clearButton,
'Clear completed (' + completedModels.length + ')');
goog.style.showElement(clearButton, completedModels.length);
// update checkbox
var checkBox = this.getEls('.toggle-all')[0];
checkBox.checked = completedModels.length === list.getLength();
});
// get the saved todos
list.fetch();
};
/**
* show or hide the footer.
*
* @param {boolean=} opt_hide whether to hide the footer.
*/
todomvc.listcontrol.prototype.showMainFooter = function(opt_hide) {
var main = goog.dom.getElement('main');
var footer = goog.dom.getElementsByTagNameAndClass('footer')[0];
goog.style.showElement(main, opt_hide);
goog.style.showElement(footer, opt_hide);
};
/**
* sets the function to determine which children are returned by the control.
*
* @param {Function} filter to decide models returned.
*/
todomvc.listcontrol.prototype.setFilter = function(filter) {
this.filter_ = filter;
this.refresh();
};
/**
* refreshes the view of the childen.
*/
todomvc.listcontrol.prototype.refresh = function() {
// dispose and remove all the children.
this.forEachChild(function(child) {child.dispose();});
this.removeChildren(true);
// create new controls for the models
goog.array.forEach(this.getModel().getModels(this.filter_),
function(model) {
var newModelControl = new todomvc.todocontrol(model);
this.addChild(newModelControl);
newModelControl.render(goog.dom.getElement('todo-list'));
}, this);
};
goog.provide('todomvc.todocontrol');
goog.require('goog.dom');
goog.require('goog.events.KeyCodes');
goog.require('mvc.Control');
goog.require('todomvc.templates');
/**
* this is the control for a todo item.
*
* @constructor
* @param {mvc.Model} model for the control.
* @extends {mvc.Control}
*/
todomvc.todocontrol = function(model) {
goog.base(this, model);
};
goog.inherits(todomvc.todocontrol, mvc.Control);
/**
* overrides goog.ui.Component#createDom with the todo template.
*
* @inheritDoc
*/
todomvc.todocontrol.prototype.createDom = function() {
var el = soy.renderAsFragment(todomvc.templates.todoItem, {
model: this.getModel().toJson()
}, null);
this.setElementInternal(/** @type {Element} */(el));
};
/**
* setup for event listeners.
*
* @inheritDoc
*/
todomvc.todocontrol.prototype.enterDocument = function() {
var model = this.getModel();
// toggle complete
this.click(function(e) {
model.set('completed', e.target.checked);
}, 'toggle');
// delete the model
this.click(function(e) {
model.dispose();
}, 'destroy');
// dblclick to edit
this.on(goog.events.EventType.DBLCLICK, function(e) {
this.makeEditable(true);
}, 'view');
// save on edit
var inputEl = this.getEls('.edit')[0];
this.on(goog.events.EventType.KEYUP, function(e) {
if (e.keyCode === goog.events.KeyCodes.ENTER) {
model.set('title', inputEl.value);
}
}, 'edit');
this.on(goog.events.EventType.BLUR, function(e) {
model.set('title', inputEl.value);
}, 'edit');
};
/**
* make (un)editable
*
* @param {boolean} editable whether to make editable.
*/
todomvc.todocontrol.prototype.makeEditable = function(editable) {
var inputEl = this.getEls('.edit')[0];
goog.dom.classes.enable(this.getElement(), 'editing', editable);
if (editable) {
inputEl.value = this.getModel().get('title');
inputEl.select();
}
};
goog.provide('todomvc.listmodel');
goog.require('mvc.Collection');
goog.require('todomvc.listsync');
goog.require('todomvc.todomodel');
/**
* @constructor
* @extends {mvc.Collection}
*/
todomvc.listmodel = function() {
var todosSchema = {
'completed': {
get: function() {
return this.getModels(function(mod) {
return mod.get('completed');
});
},
models: true
}
};
goog.base(this, {
'id': 'todos-plastronjs',
'sync': new todomvc.listsync(),
'schema': todosSchema,
'modelType': todomvc.todomodel
});
};
goog.inherits(todomvc.listmodel, mvc.Collection);
/**
* @return {Object} todos as json.
*/
todomvc.listmodel.prototype.toJson = function() {
return goog.array.map(this.getModels(), function(mod) {
return mod.toJson();
});
};
goog.provide('todomvc.todomodel');
goog.require('goog.string');
goog.require('mvc.Model');
goog.require('mvc.Model.ValidateError');
/**
* @constructor
* @param {Object=} opt_options to be put on the model.
* @extends {mvc.Model}
*/
todomvc.todomodel = function(opt_options) {
goog.base(this, opt_options);
this.setter('title', function(title) {
var updated = goog.string.trim(title);
if (!updated.length)
throw new mvc.Model.ValidateError('null string');
return updated;
});
this.errorHandler(function() {
this.dispose();
});
};
goog.inherits(todomvc.todomodel, mvc.Model);
goog.provide('todomvc.listsync');
goog.require('mvc.LocalSync');
/**
* @constructor
* @extends {mvc.LocalSync}
*/
todomvc.listsync = function() {
goog.base(this);
};
goog.inherits(todomvc.listsync, mvc.LocalSync);
/**
* @inheritDoc
*/
todomvc.listsync.prototype.read = function(model, opt_callback) {
var id = /** @type {string} */(model.get('id'));
var todos = this.store_.get(id) || [];
goog.array.forEach(/** @type {Array} */(todos),
function(todo) {
model.newModel(todo, true);
});
model.change();
};
{
"id" : "todomvc",
"inputs" : "js/app.js",
"paths" : ["js/","template/"],
"output-wrapper" : "(function(){%output%})();",
"mode" : "ADVANCED",
"level" : "VERBOSE",
"output-file" : "js/compiled.js",
"define" : {
"goog.LOCALE": "en_GB"
},
"checks": {
// Unfortunately, the Closure Library violates these in many places.
// "accessControls": "ERROR",
// "visibility": "ERROR"
"checkRegExp": "WARNING",
"checkTypes": "WARNING",
"checkVars": "WARNING",
"deprecated": "WARNING",
"fileoverviewTags": "WARNING",
"invalidCasts": "WARNING",
"missingProperties": "WARNING",
"nonStandardJsDocs": "WARNING",
"undefinedVars": "WARNING"
}
}
{namespace todomvc.templates}
/**
* @param model
*/
{template .todoItem}
<li {if $model['completed']}class="completed"{/if}>
<div class="view">
<input class="toggle" type="checkbox" {if $model['completed']}checked{/if}>
<label>{$model['title']}</label>
<button class="destroy"></button>
</div>
<input class="edit" type="text" value="">
</li>
{/template}
/**
* @param left
*/
{template .itemsLeft}
<strong>{$left}</strong> item{if $left != 1}s{/if} left
{/template}
......@@ -89,6 +89,9 @@
<li>
<a href="architecture-examples/o_O/index.html" data-source="http://weepy.github.com/o_O/" data-content="o_O: HTML binding for teh lulz: &lt;br>- Elegantly binds objects to HTML&lt;br>- Proxies through jQuery, Ender, etc&lt;br>- Automatic dependency resolution&lt;br>- Plays well with others">Funnyface.js</a>
</li>
<li>
<a href="architecture-examples/plastronjs/index.html" data-source="https://github.com/rhysbrettbowen/PlastronJS" data-content="PlastronJS is an mvc framework built on top of the Closure Library and built to compile with projects that use the Closure Compiler.">PlastronJS</a>
</li>
</ul>
<ul class="nav nav-pills">
<li>
......
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