Commit 8248b68f authored by Christoph Burgmer's avatar Christoph Burgmer Committed by Sindre Sorhus

Close GH-739: VanillaJS refactoring.

parent 47cf86c3
{
"name": "todomvc-vanillajs",
"version": "0.0.0",
"dependencies": {
"todomvc-common": "~0.1.4"
}
"name": "todomvc-vanillajs",
"version": "0.0.0",
"dependencies": {
"todomvc-common": "~0.1.4"
},
"devDependencies": {
"jasmine": "~1.3.1"
}
}
body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; }
#HTMLReporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; }
#HTMLReporter a { text-decoration: none; }
#HTMLReporter a:hover { text-decoration: underline; }
#HTMLReporter p, #HTMLReporter h1, #HTMLReporter h2, #HTMLReporter h3, #HTMLReporter h4, #HTMLReporter h5, #HTMLReporter h6 { margin: 0; line-height: 14px; }
#HTMLReporter .banner, #HTMLReporter .symbolSummary, #HTMLReporter .summary, #HTMLReporter .resultMessage, #HTMLReporter .specDetail .description, #HTMLReporter .alert .bar, #HTMLReporter .stackTrace { padding-left: 9px; padding-right: 9px; }
#HTMLReporter #jasmine_content { position: fixed; right: 100%; }
#HTMLReporter .version { color: #aaaaaa; }
#HTMLReporter .banner { margin-top: 14px; }
#HTMLReporter .duration { color: #aaaaaa; float: right; }
#HTMLReporter .symbolSummary { overflow: hidden; *zoom: 1; margin: 14px 0; }
#HTMLReporter .symbolSummary li { display: block; float: left; height: 7px; width: 14px; margin-bottom: 7px; font-size: 16px; }
#HTMLReporter .symbolSummary li.passed { font-size: 14px; }
#HTMLReporter .symbolSummary li.passed:before { color: #5e7d00; content: "\02022"; }
#HTMLReporter .symbolSummary li.failed { line-height: 9px; }
#HTMLReporter .symbolSummary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; }
#HTMLReporter .symbolSummary li.skipped { font-size: 14px; }
#HTMLReporter .symbolSummary li.skipped:before { color: #bababa; content: "\02022"; }
#HTMLReporter .symbolSummary li.pending { line-height: 11px; }
#HTMLReporter .symbolSummary li.pending:before { color: #aaaaaa; content: "-"; }
#HTMLReporter .exceptions { color: #fff; float: right; margin-top: 5px; margin-right: 5px; }
#HTMLReporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; }
#HTMLReporter .runningAlert { background-color: #666666; }
#HTMLReporter .skippedAlert { background-color: #aaaaaa; }
#HTMLReporter .skippedAlert:first-child { background-color: #333333; }
#HTMLReporter .skippedAlert:hover { text-decoration: none; color: white; text-decoration: underline; }
#HTMLReporter .passingAlert { background-color: #a6b779; }
#HTMLReporter .passingAlert:first-child { background-color: #5e7d00; }
#HTMLReporter .failingAlert { background-color: #cf867e; }
#HTMLReporter .failingAlert:first-child { background-color: #b03911; }
#HTMLReporter .results { margin-top: 14px; }
#HTMLReporter #details { display: none; }
#HTMLReporter .resultsMenu, #HTMLReporter .resultsMenu a { background-color: #fff; color: #333333; }
#HTMLReporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; }
#HTMLReporter.showDetails .summaryMenuItem:hover { text-decoration: underline; }
#HTMLReporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; }
#HTMLReporter.showDetails .summary { display: none; }
#HTMLReporter.showDetails #details { display: block; }
#HTMLReporter .summaryMenuItem { font-weight: bold; text-decoration: underline; }
#HTMLReporter .summary { margin-top: 14px; }
#HTMLReporter .summary .suite .suite, #HTMLReporter .summary .specSummary { margin-left: 14px; }
#HTMLReporter .summary .specSummary.passed a { color: #5e7d00; }
#HTMLReporter .summary .specSummary.failed a { color: #b03911; }
#HTMLReporter .description + .suite { margin-top: 0; }
#HTMLReporter .suite { margin-top: 14px; }
#HTMLReporter .suite a { color: #333333; }
#HTMLReporter #details .specDetail { margin-bottom: 28px; }
#HTMLReporter #details .specDetail .description { display: block; color: white; background-color: #b03911; }
#HTMLReporter .resultMessage { padding-top: 14px; color: #333333; }
#HTMLReporter .resultMessage span.result { display: block; }
#HTMLReporter .stackTrace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; }
#TrivialReporter { padding: 8px 13px; position: absolute; top: 0; bottom: 0; left: 0; right: 0; overflow-y: scroll; background-color: white; font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; /*.resultMessage {*/ /*white-space: pre;*/ /*}*/ }
#TrivialReporter a:visited, #TrivialReporter a { color: #303; }
#TrivialReporter a:hover, #TrivialReporter a:active { color: blue; }
#TrivialReporter .run_spec { float: right; padding-right: 5px; font-size: .8em; text-decoration: none; }
#TrivialReporter .banner { color: #303; background-color: #fef; padding: 5px; }
#TrivialReporter .logo { float: left; font-size: 1.1em; padding-left: 5px; }
#TrivialReporter .logo .version { font-size: .6em; padding-left: 1em; }
#TrivialReporter .runner.running { background-color: yellow; }
#TrivialReporter .options { text-align: right; font-size: .8em; }
#TrivialReporter .suite { border: 1px outset gray; margin: 5px 0; padding-left: 1em; }
#TrivialReporter .suite .suite { margin: 5px; }
#TrivialReporter .suite.passed { background-color: #dfd; }
#TrivialReporter .suite.failed { background-color: #fdd; }
#TrivialReporter .spec { margin: 5px; padding-left: 1em; clear: both; }
#TrivialReporter .spec.failed, #TrivialReporter .spec.passed, #TrivialReporter .spec.skipped { padding-bottom: 5px; border: 1px solid gray; }
#TrivialReporter .spec.failed { background-color: #fbb; border-color: red; }
#TrivialReporter .spec.passed { background-color: #bfb; border-color: green; }
#TrivialReporter .spec.skipped { background-color: #bbb; }
#TrivialReporter .messages { border-left: 1px dashed gray; padding-left: 1em; padding-right: 1em; }
#TrivialReporter .passed { background-color: #cfc; display: none; }
#TrivialReporter .failed { background-color: #fbb; }
#TrivialReporter .skipped { color: #777; background-color: #eee; display: none; }
#TrivialReporter .resultMessage span.result { display: block; line-height: 2em; color: black; }
#TrivialReporter .resultMessage .mismatch { color: black; }
#TrivialReporter .stackTrace { white-space: pre; font-size: .8em; margin-left: 10px; max-height: 5em; overflow: auto; border: 1px inset red; padding: 1em; background: #eef; }
#TrivialReporter .finished-at { padding-left: 1em; font-size: .6em; }
#TrivialReporter.show-passed .passed, #TrivialReporter.show-skipped .skipped { display: block; }
#TrivialReporter #jasmine_content { position: fixed; right: 100%; }
#TrivialReporter .runner { border: 1px solid gray; display: block; margin: 5px 0; padding: 2px 0 2px 10px; }
......@@ -39,13 +39,10 @@
<p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
</footer>
<script src="bower_components/todomvc-common/base.js"></script>
<script>
// Bootstrap app data
window.app = {};
</script>
<script src="js/helpers.js"></script>
<script src="js/store.js"></script>
<script src="js/model.js"></script>
<script src="js/template.js"></script>
<script src="js/view.js"></script>
<script src="js/controller.js"></script>
<script src="js/app.js"></script>
......
/*global $$, app */
/*global app */
(function () {
'use strict';
......@@ -10,63 +10,17 @@
function Todo(name) {
this.storage = new app.Store(name);
this.model = new app.Model(this.storage);
this.view = new app.View();
this.template = new app.Template();
this.view = new app.View(this.template);
this.controller = new app.Controller(this.model, this.view);
}
var todo = new Todo('todos-vanillajs');
/**
* Finds the model ID of the clicked DOM element
*
* @param {object} target The starting point in the DOM for it to try to find
* the ID of the model.
*/
function lookupId(target) {
var lookup = target;
while (lookup.nodeName !== 'LI') {
lookup = lookup.parentNode;
}
return lookup.dataset.id;
}
// When the enter key is pressed fire the addItem method.
$$('#new-todo').addEventListener('keypress', function (e) {
todo.controller.addItem(e);
});
// A delegation event. Will check what item was clicked whenever you click on any
// part of a list item.
$$('#todo-list').addEventListener('click', function (e) {
var target = e.target;
// If you click a destroy button
if (target.className.indexOf('destroy') > -1) {
todo.controller.removeItem(lookupId(target));
}
// If you click the checkmark
if (target.className.indexOf('toggle') > -1) {
todo.controller.toggleComplete(lookupId(target), target);
}
});
$$('#todo-list').addEventListener('dblclick', function (e) {
var target = e.target;
if (target.nodeName === 'LABEL') {
todo.controller.editItem(lookupId(target), target);
}
});
$$('#toggle-all').addEventListener('click', function (e) {
todo.controller.toggleAll(e);
});
$$('#clear-completed').addEventListener('click', function () {
todo.controller.removeCompletedItems();
});
window.addEventListener('load', function () {
todo.controller.setView(document.location.hash);
}.bind(this));
window.addEventListener('hashchange', function () {
todo.controller.setView(document.location.hash);
}.bind(this));
})();
This diff is collapsed.
/*global NodeList */
(function (window) {
'use strict';
......@@ -5,13 +6,53 @@
window.$ = document.querySelectorAll.bind(document);
window.$$ = document.querySelector.bind(document);
// Allow for looping on Objects by chaining:
// $('.foo').each(function () {})
Object.prototype.each = function (callback) {
for (var x in this) {
if (this.hasOwnProperty(x)) {
callback.call(this, this[x]);
// Register events on elements that may or may not exist yet:
// $live('div a', 'click', function (e) {});
window.$live = (function () {
var eventRegistry = {};
var globalEventDispatcher = function (e) {
var targetElement = e.target;
if (eventRegistry[e.type]) {
eventRegistry[e.type].forEach(function (entry) {
var potentialElements = document.querySelectorAll(entry.selector),
hasMatch = Array.prototype.indexOf.call(potentialElements, targetElement) >= 0;
if (hasMatch) {
entry.handler(e);
}
});
}
};
return function (selector, event, handler) {
if (!eventRegistry[event]) {
document.documentElement.addEventListener(event, globalEventDispatcher, true);
eventRegistry[event] = [];
}
eventRegistry[event].push({
selector: selector,
handler: handler
});
};
}());
// Find the element's parent with the given tag name:
// $parent($$('a'), 'div');
window.$parent = function (element, tagName) {
if (!element.parentNode) {
return;
}
if (element.parentNode.tagName.toLowerCase() === tagName.toLowerCase()) {
return element.parentNode;
}
return window.$parent(element.parentNode, tagName);
};
// Allow for looping on nodes by chaining:
// $('.foo').forEach(function () {})
NodeList.prototype.forEach = Array.prototype.forEach;
})(window);
......@@ -23,7 +23,7 @@
var newItem = {
title: title.trim(),
completed: 0
completed: false
};
this.storage.save(newItem, callback);
......@@ -52,6 +52,7 @@
callback = query;
return this.storage.findAll(callback);
} else if (queryType === 'string' || queryType === 'number') {
query = parseInt(query, 10);
this.storage.find({ id: query }, callback);
} else {
this.storage.find(query, callback);
......@@ -100,8 +101,8 @@
};
this.storage.findAll(function (data) {
data.each(function (todo) {
if (todo.completed === 1) {
data.forEach(function (todo) {
if (todo.completed) {
todos.completed++;
} else {
todos.active++;
......@@ -115,5 +116,6 @@
};
// Export to window
window.app = window.app || {};
window.app.Model = Model;
})(window);
......@@ -142,5 +142,6 @@
};
// Export to window
window.app = window.app || {};
window.app.Store = Store;
})(window);
/*jshint laxbreak:true */
(function (window) {
'use strict';
/**
* Sets up defaults for all the Template methods such as a default template
*
* @constructor
*/
function Template() {
this.defaultTemplate
= '<li data-id="{{id}}" class="{{completed}}">'
+ '<div class="view">'
+ '<input class="toggle" type="checkbox" {{checked}}>'
+ '<label>{{title}}</label>'
+ '<button class="destroy"></button>'
+ '</div>'
+ '</li>';
}
/**
* Creates an <li> HTML string and returns it for placement in your app.
*
* NOTE: In real life you should be using a templating engine such as Mustache
* or Handlebars, however, this is a vanilla JS example.
*
* @param {object} data The object containing keys you want to find in the
* template to replace.
* @returns {string} HTML String of an <li> element
*
* @example
* view.show({
* id: 1,
* title: "Hello World",
* completed: 0,
* });
*/
Template.prototype.show = function (data) {
var i, l;
var view = '';
for (i = 0, l = data.length; i < l; i++) {
var template = this.defaultTemplate;
var completed = '';
var checked = '';
if (data[i].completed) {
completed = 'completed';
checked = 'checked';
}
template = template.replace('{{id}}', data[i].id);
template = template.replace('{{title}}', data[i].title);
template = template.replace('{{completed}}', completed);
template = template.replace('{{checked}}', checked);
view = view + template;
}
return view;
};
/**
* Displays a counter of how many to dos are left to complete
*
* @param {number} activeTodos The number of active todos.
* @returns {string} String containing the count
*/
Template.prototype.itemCounter = function (activeTodos) {
var plural = activeTodos === 1 ? '' : 's';
return '<strong>' + activeTodos + '</strong> item' + plural + ' left';
};
/**
* Updates the text within the "Clear completed" button
*
* @param {[type]} completedTodos The number of completed todos.
* @returns {string} String containing the count
*/
Template.prototype.clearCompletedButton = function (completedTodos) {
if (completedTodos > 0) {
return 'Clear completed (' + completedTodos + ')';
} else {
return '';
}
};
// Export to window
window.app = window.app || {};
window.app.Template = Template;
})(window);
This diff is collapsed.
This diff is collapsed.
<!doctype html>
<html>
<head>
<title>Jasmine Spec Runner</title>
<link rel="stylesheet" type="text/css" href="../bower_components/jasmine/lib/jasmine-core/jasmine.css">
<script type="text/javascript" src="../bower_components/jasmine/lib/jasmine-core/jasmine.js"></script>
<script type="text/javascript" src="../bower_components/jasmine/lib/jasmine-core/jasmine-html.js"></script>
<!-- include spec files here... -->
<script type="text/javascript" src="ControllerSpec.js"></script>
<!-- include source files here... -->
<script>
// Bootstrap app data
window.app = {};
</script>
<script src="../js/helpers.js"></script>
<script src="../js/view.js"></script>
<script src="../js/controller.js"></script>
<script type="text/javascript">
(function() {
var jasmineEnv = jasmine.getEnv();
var htmlReporter = new jasmine.HtmlReporter();
jasmineEnv.addReporter(htmlReporter);
jasmineEnv.specFilter = function(spec) {
return htmlReporter.specFilter(spec);
};
var currentWindowOnload = window.onload;
window.onload = function() {
if (currentWindowOnload) {
currentWindowOnload();
}
jasmineEnv.execute();
};
})();
</script>
</head>
<body>
</body>
</html>
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