Commit 4da42bc1 authored by Romuald Quantin's avatar Romuald Quantin Committed by Sindre Sorhus

Close GH-901: soma.js - Fixes selenium tests #819..

parent a5d0ac74
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
"dependencies": { "dependencies": {
"todomvc-common": "~0.1.6", "todomvc-common": "~0.1.6",
"director": "~1.2.0", "director": "~1.2.0",
"soma.js": "~2.0.0", "soma.js": "~2.1.0",
"soma-template": "~0.1.8" "soma-template": "~0.2.8"
} }
} }
// //
// Generated on Sun Dec 16 2012 22:47:05 GMT-0500 (EST) by Nodejitsu, Inc (Using Codesurgeon). // Generated on Fri Dec 27 2013 12:02:11 GMT-0500 (EST) by Nodejitsu, Inc (Using Codesurgeon).
// Version 1.1.9 // Version 1.2.2
// //
(function (exports) { (function (exports) {
/* /*
* browser.js: Browser specific functionality for director. * browser.js: Browser specific functionality for director.
* *
...@@ -201,7 +200,7 @@ Router.prototype.init = function (r) { ...@@ -201,7 +200,7 @@ Router.prototype.init = function (r) {
this.handler = function(onChangeEvent) { this.handler = function(onChangeEvent) {
var newURL = onChangeEvent && onChangeEvent.newURL || window.location.hash; var newURL = onChangeEvent && onChangeEvent.newURL || window.location.hash;
var url = self.history === true ? self.getPath() : newURL.replace(/.*#/, ''); var url = self.history === true ? self.getPath() : newURL.replace(/.*#/, '');
self.dispatch('on', url); self.dispatch('on', url.charAt(0) === '/' ? url : '/' + url);
}; };
listener.init(this.handler, this.history); listener.init(this.handler, this.history);
...@@ -210,7 +209,7 @@ Router.prototype.init = function (r) { ...@@ -210,7 +209,7 @@ Router.prototype.init = function (r) {
if (dlocHashEmpty() && r) { if (dlocHashEmpty() && r) {
dloc.hash = r; dloc.hash = r;
} else if (!dlocHashEmpty()) { } else if (!dlocHashEmpty()) {
self.dispatch('on', dloc.hash.replace(/^#/, '')); self.dispatch('on', '/' + dloc.hash.replace(/^(#\/|#|\/)/, ''));
} }
} }
else { else {
...@@ -363,11 +362,16 @@ function regifyString(str, params) { ...@@ -363,11 +362,16 @@ function regifyString(str, params) {
out += str.substr(0, matches.index) + matches[0]; out += str.substr(0, matches.index) + matches[0];
} }
str = out += str.substr(last); str = out += str.substr(last);
var captures = str.match(/:([^\/]+)/ig), length; var captures = str.match(/:([^\/]+)/ig), capture, length;
if (captures) { if (captures) {
length = captures.length; length = captures.length;
for (var i = 0; i < length; i++) { for (var i = 0; i < length; i++) {
str = str.replace(captures[i], paramifyString(captures[i], params)); capture = captures[i];
if (capture.slice(0, 2) === "::") {
str = capture.slice(1);
} else {
str = str.replace(capture, paramifyString(capture, params));
}
} }
} }
return str; return str;
...@@ -485,20 +489,22 @@ Router.prototype.dispatch = function(method, path, callback) { ...@@ -485,20 +489,22 @@ Router.prototype.dispatch = function(method, path, callback) {
Router.prototype.invoke = function(fns, thisArg, callback) { Router.prototype.invoke = function(fns, thisArg, callback) {
var self = this; var self = this;
var apply;
if (this.async) { if (this.async) {
_asyncEverySeries(fns, function apply(fn, next) { apply = function(fn, next) {
if (Array.isArray(fn)) { if (Array.isArray(fn)) {
return _asyncEverySeries(fn, apply, next); return _asyncEverySeries(fn, apply, next);
} else if (typeof fn == "function") { } else if (typeof fn == "function") {
fn.apply(thisArg, fns.captures.concat(next)); fn.apply(thisArg, fns.captures.concat(next));
} }
}, function() { };
_asyncEverySeries(fns, apply, function() {
if (callback) { if (callback) {
callback.apply(thisArg, arguments); callback.apply(thisArg, arguments);
} }
}); });
} else { } else {
_every(fns, function apply(fn) { apply = function(fn) {
if (Array.isArray(fn)) { if (Array.isArray(fn)) {
return _every(fn, apply); return _every(fn, apply);
} else if (typeof fn === "function") { } else if (typeof fn === "function") {
...@@ -506,7 +512,8 @@ Router.prototype.invoke = function(fns, thisArg, callback) { ...@@ -506,7 +512,8 @@ Router.prototype.invoke = function(fns, thisArg, callback) {
} else if (typeof fn === "string" && self.resource) { } else if (typeof fn === "string" && self.resource) {
self.resource[fn].apply(thisArg, fns.captures || []); self.resource[fn].apply(thisArg, fns.captures || []);
} }
}); };
_every(fns, apply);
} }
}; };
...@@ -686,7 +693,7 @@ Router.prototype.mount = function(routes, path) { ...@@ -686,7 +693,7 @@ Router.prototype.mount = function(routes, path) {
function insertOrMount(route, local) { function insertOrMount(route, local) {
var rename = route, parts = route.split(self.delimiter), routeType = typeof routes[route], isRoute = parts[0] === "" || !self._methods[parts[0]], event = isRoute ? "on" : rename; var rename = route, parts = route.split(self.delimiter), routeType = typeof routes[route], isRoute = parts[0] === "" || !self._methods[parts[0]], event = isRoute ? "on" : rename;
if (isRoute) { if (isRoute) {
rename = rename.slice((rename.match(new RegExp(self.delimiter)) || [ "" ])[0].length); rename = rename.slice((rename.match(new RegExp("^" + self.delimiter)) || [ "" ])[0].length);
parts.shift(); parts.shift();
} }
if (isRoute && routeType === "object" && !Array.isArray(routes[route])) { if (isRoute && routeType === "object" && !Array.isArray(routes[route])) {
......
;(function (soma, undefined) { (function (soma) {
'use strict'; 'use strict';
soma.template = soma.template || {}; soma.template = soma.template || {};
soma.template.version = "0.1.8"; soma.template.version = '0.2.8';
var errors = soma.template.errors = { soma.template.errors = {
TEMPLATE_STRING_NO_ELEMENT: "Error in soma.template, a string template requirement a second parameter: an element target - soma.template.create('string', element)", TEMPLATE_STRING_NO_ELEMENT: 'Error in soma.template, a string template requirement a second parameter: an element target - soma.template.create(\'string\', element)',
TEMPLATE_NO_PARAM: "Error in soma.template, a template requires at least 1 parameter - soma.template.create(element)" TEMPLATE_NO_PARAM: 'Error in soma.template, a template requires at least 1 parameter - soma.template.create(element)'
}; };
var tokenStart = '{{'; var tokenStart = '{{';
var tokenEnd = '}}'; var tokenEnd = '}}';
var helpersObject = {}; var helpersObject = {};
var helpersScopeObject = {}; var helpersScopeObject = {};
var settings = soma.template.settings = soma.template.settings || {}; var settings = soma.template.settings = soma.template.settings || {};
settings.autocreate = true; settings.autocreate = true;
var tokens = settings.tokens = { var tokens = settings.tokens = {
start: function(value) { start: function(value) {
if (isDefined(value) && value !== '') { if (isDefined(value) && value !== '') {
tokenStart = escapeRegExp(value); tokenStart = escapeRegExp(value);
...@@ -34,41 +34,45 @@ var tokens = settings.tokens = { ...@@ -34,41 +34,45 @@ var tokens = settings.tokens = {
} }
return tokenEnd; return tokenEnd;
} }
}; };
var attributes = settings.attributes = { var attributes = settings.attributes = {
skip: "data-skip", skip: 'data-skip',
repeat: "data-repeat", repeat: 'data-repeat',
src: "data-src", src: 'data-src',
href: "data-href", href: 'data-href',
show: "data-show", show: 'data-show',
hide: "data-hide", hide: 'data-hide',
cloak: "data-cloak", cloak: 'data-cloak',
checked: "data-checked", checked: 'data-checked',
disabled: "data-disabled", disabled: 'data-disabled',
multiple: "data-multiple", multiple: 'data-multiple',
readonly: "data-readonly", readonly: 'data-readonly',
selected: "data-selected", selected: 'data-selected',
template: "data-template", template: 'data-template',
html: "data-html" html: 'data-html'
}; };
var vars = settings.vars = { var vars = settings.vars = {
index: "$index", index: '$index',
key: "$key" key: '$key',
}; element: '$element',
parentElement: '$parentElement',
var events = settings.events = {}; attribute: '$attribute',
settings.eventsPrefix = 'data-'; scope: '$scope'
var eventsString = 'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup focus blur change select selectstart scroll copy cut paste mousewheel keypress error contextmenu input textinput drag dragenter dragleave dragover dragend dragstart dragover drop load submit reset search resize beforepaste beforecut beforecopy'; };
eventsString += ' touchstart touchend touchmove touchenter touchleave touchcancel gesturestart gesturechange gestureend';
var eventsArray = eventsString.split(' '); var events = settings.events = {};
var i = -1, l = eventsArray.length; settings.eventsPrefix = 'data-';
while(++i < l) { var eventsString = 'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup focus blur change select selectstart scroll copy cut paste mousewheel keypress error contextmenu input textinput drag dragenter dragleave dragover dragend dragstart dragover drop load submit reset search resize beforepaste beforecut beforecopy';
eventsString += ' touchstart touchend touchmove touchenter touchleave touchcancel gesturestart gesturechange gestureend';
var eventsArray = eventsString.split(' ');
var i = -1, l = eventsArray.length;
while(++i < l) {
events[settings.eventsPrefix + eventsArray[i]] = eventsArray[i]; events[settings.eventsPrefix + eventsArray[i]] = eventsArray[i];
} }
var regex = { var regex = {
sequence: null, sequence: null,
token: null, token: null,
expression: null, expression: null,
...@@ -81,117 +85,138 @@ var regex = { ...@@ -81,117 +85,138 @@ var regex = {
content: /[^.|^\s]/gm, content: /[^.|^\s]/gm,
depth: /..\//g, depth: /..\//g,
string: /^(\"|\')(.*)(\"|\')$/ string: /^(\"|\')(.*)(\"|\')$/
}; };
var ie = (function(){ var ie = (function(){
if (typeof document !== 'object') return undefined; if (typeof document !== 'object') {
var undef, return undefined;
v = 3, }
div = document.createElement('div'), var v = 3,
all = div.getElementsByTagName('i'); stop = false,
while ( div = document.createElement('div');
div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->', all[0] while (!stop) {
); div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->';
return v > 4 ? v : undef; if (!div.getElementsByTagName('i')[0]) {
}()); stop = true;
function isArray(value) { }
}
return v > 4 ? v : undefined;
}());
function isArray(value) {
return Object.prototype.toString.apply(value) === '[object Array]'; return Object.prototype.toString.apply(value) === '[object Array]';
}; }
function isObject(value) { function isObject(value) {
return typeof value === 'object'; return typeof value === 'object';
} }
function isString(value) { function isString(value) {
return typeof value === 'string'; return typeof value === 'string';
} }
function isElement(value) { function isElement(value) {
return value ? value.nodeType > 0 : false; return value ? value.nodeType > 0 : false;
}; }
function isTextNode(el) { function isTextNode(el) {
return el && el.nodeType && el.nodeType === 3; return el && el.nodeType && el.nodeType === 3;
} }
function isFunction(value) { function isFunction(value) {
return value && typeof value === 'function'; return value && typeof value === 'function';
} }
function isDefined(value) { function isDefined(value) {
return value !== null && value !== undefined; return value !== null && value !== undefined;
} }
function isAttributeDefined(value) { function normalizeBoolean(value) {
return (value === "" || value === true || value === "true" || !isDefined(value)); if (!isDefined(value)) {
} return false;
function isExpression(value) { }
if (value === 'true' || value === '1' || value === true || value === 1) {
return true;
}
if (value === 'false' || value === '0' || value === false || value === 0 || (isString(value) && hasInterpolation(value))) {
return false;
}
return !!value;
}
function isExpression(value) {
return value && isFunction(value.toString) && value.toString() === '[object Expression]'; return value && isFunction(value.toString) && value.toString() === '[object Expression]';
} }
function isNode(value) { function isExpFunction(value) {
return value && isFunction(value.toString) && value.toString() === '[object Node]'; if (!isString(value)) {
} return false;
function isExpFunction(value) { }
if (!isString(value)) return false;
return !!value.match(regex.func); return !!value.match(regex.func);
} }
function childNodeIsTemplate(node) { function childNodeIsTemplate(node) {
return node && node.parent && templates.get(node.element); return node && node.parent && templates.get(node.element);
} }
function escapeRegExp(str) { function escapeRegExp(str) {
return str.replace(regex.escape, "\\$&"); return str.replace(regex.escape, '\\$&');
} }
function setRegEX(nonEscapedValue, isStartToken) { function setRegEX(nonEscapedValue, isStartToken) {
// sequence: \{\{.+?\}\}|[^{]+|\{(?!\{)[^{]* // sequence: \{\{.+?\}\}|[^{]+|\{(?!\{)[^{]*
var unescapedCurrentStartToken = tokens.start().replace(/\\/g, ''); var unescapedCurrentStartToken = tokens.start().replace(/\\/g, '');
var endSequence = ""; var endSequence = '';
var ts = isStartToken ? nonEscapedValue : unescapedCurrentStartToken; var ts = isStartToken ? nonEscapedValue : unescapedCurrentStartToken;
if (ts.length > 1) { if (ts.length > 1) {
endSequence = "|\\" + ts.substr(0, 1) + "(?!\\" + ts.substr(1, 1) + ")[^" + ts.substr(0, 1) + "]*"; endSequence = '|\\' + ts.substr(0, 1) + '(?!\\' + ts.substr(1, 1) + ')[^' + ts.substr(0, 1) + ']*';
} }
regex.sequence = new RegExp(tokens.start() + ".+?" + tokens.end() + "|[^" + tokens.start() + "]+" + endSequence, "g"); regex.sequence = new RegExp(tokens.start() + '.+?' + tokens.end() + '|[^' + tokens.start() + ']+' + endSequence, 'g');
regex.token = new RegExp(tokens.start() + ".*?" + tokens.end(), "g"); regex.token = new RegExp(tokens.start() + '.*?' + tokens.end(), 'g');
regex.expression = new RegExp(tokens.start() + "|" + tokens.end(), "gm"); regex.expression = new RegExp(tokens.start() + '|' + tokens.end(), 'gm');
} }
function trim(value) { function trim(value) {
return value.replace(regex.trim, ''); return value.replace(regex.trim, '');
} }
function trimQuotes(value) { function trimQuotes(value) {
if (regex.string.test(value)) { if (regex.string.test(value)) {
return value.substr(1, value.length-2); return value.substr(1, value.length-2);
} }
return value; return value;
} }
function trimArray(value) { function trimArray(value) {
if (value[0] === "") value.shift(); if (value[0] === '') {
if (value[value.length-1] === "") value.pop(); value.shift();
}
if (value[value.length-1] === '') {
value.pop();
}
return value; return value;
} }
function trimTokens(value) { function trimTokens(value) {
return value.replace(regex.expression, ''); return value.replace(regex.expression, '');
} }
function trimScopeDepth(value) { function trimScopeDepth(value) {
return value.replace(regex.depth, ''); return value.replace(regex.depth, '');
} }
function insertBefore(referenceNode, newNode) { function insertBefore(referenceNode, newNode) {
if (!referenceNode.parentNode) return; if (!referenceNode.parentNode) {
return;
}
referenceNode.parentNode.insertBefore(newNode, referenceNode); referenceNode.parentNode.insertBefore(newNode, referenceNode);
} }
function insertAfter(referenceNode, newNode) { function insertAfter(referenceNode, newNode) {
if (!referenceNode.parentNode) return; if (!referenceNode.parentNode) {
return;
}
referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling); referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
}
function removeClass(elm, className) {
if (document.documentElement.classList) {
removeClass = function (elm, className) {
elm.classList.remove(className);
} }
function removeClass(elm, className) {
var rmc;
if (typeof document === 'object' && document.documentElement.classList) {
rmc = function (elm, className) {
elm.classList.remove(className);
};
} else { } else {
removeClass = function (elm, className) { rmc = function (elm, className) {
if (!elm || !elm.className) { if (!elm || !elm.className) {
return false; return false;
} }
var reg = new RegExp("(^|\\s)" + className + "(\\s|$)", "g"); var reg = new RegExp('(^|\\s)' + className + '(\\s|$)', 'g');
elm.className = elm.className.replace(reg, "$2"); elm.className = elm.className.replace(reg, '$2');
};
} }
rmc(elm, className);
} }
removeClass(elm, className); // jquery contains
} var contains = typeof document !== 'object' ? function(){} : document.documentElement.contains ?
// jquery contains
var contains = typeof document !== 'object' ? function(){} : document.documentElement.contains ?
function( a, b ) { function( a, b ) {
var adown = a.nodeType === 9 ? a.documentElement : a, var adown = a.nodeType === 9 ? a.documentElement : a,
bup = b && b.parentNode; bup = b && b.parentNode;
...@@ -210,66 +235,79 @@ var contains = typeof document !== 'object' ? function(){} : document.documentEl ...@@ -210,66 +235,79 @@ var contains = typeof document !== 'object' ? function(){} : document.documentEl
return false; return false;
}; };
function HashMap(id) {
function HashMap() {
var items = {}; var items = {};
var id = 1; var count = 0;
//var uuid = function(a,b){for(b=a='';a++<36;b+=a*51&52?(a^15?8^Math.random()*(a^20?16:4):4).toString(16):'-');return b;} //var uuid = function(a,b){for(b=a='';a++<36;b+=a*51&52?(a^15?8^Math.random()*(a^20?16:4):4).toString(16):'-');return b;}
function uuid() { return ++id; }; function uuid() { return ++count + id; }
function getKey(target) { function getKey(target) {
if (!target) return; if (!target) {
if (typeof target !== 'object') return target; return;
}
if (typeof target !== 'object') {
return target;
}
var result; var result;
try { try {
// IE 7-8 needs a try catch, seems like I can't add a property on text nodes // IE 7-8 needs a try catch, seems like I can't add a property on text nodes
result = target.hashkey ? target.hashkey : target.hashkey = uuid(); result = target[id] ? target[id] : target[id] = uuid();
} catch(err){}; } catch(err){}
return result; return result;
} }
this.remove = function(key) { this.remove = function(key) {
delete items[getKey(key)]; delete items[getKey(key)];
} };
this.get = function(key) { this.get = function(key) {
return items[getKey(key)]; return items[getKey(key)];
} };
this.put = function(key, value) { this.put = function(key, value) {
items[getKey(key)] = value; items[getKey(key)] = value;
} };
this.has = function(key) { this.has = function(key) {
return typeof items[getKey(key)] !== 'undefined'; return typeof items[getKey(key)] !== 'undefined';
} };
this.getData = function() { this.getData = function() {
return items; return items;
} };
this.dispose = function() { this.dispose = function() {
for (var key in items) { for (var key in items) {
if (items.hasOwnProperty(key)) {
delete items[key]; delete items[key];
} }
}
this.length = 0; this.length = 0;
};
} }
}
function getRepeaterData(repeaterValue, scope) { function getRepeaterData(repeaterValue, scope) {
var parts = repeaterValue.match(regex.repeat); var parts = repeaterValue.match(regex.repeat);
if (!parts) return; if (!parts) {
return;
}
var source = parts[2]; var source = parts[2];
var exp = new Expression(source); var exp = new Expression(source);
return exp.getValue(scope); return exp.getValue(scope);
} }
function updateScopeWithRepeaterData(repeaterValue, scope, data) { function updateScopeWithRepeaterData(repeaterValue, scope, data) {
var parts = repeaterValue.match(regex.repeat); var parts = repeaterValue.match(regex.repeat);
if (!parts) return; if (!parts) {
return;
}
var name = parts[1]; var name = parts[1];
scope[name] = data; scope[name] = data;
} }
function getWatcherValue(exp, newValue) { function getWatcherValue(exp, newValue) {
var node = exp.node || exp.attribute.node; var node = exp.node || exp.attribute.node;
var watchers = node.template.watchers; var watchers = node.template.watchers;
var nodeTarget = node.element; var nodeTarget = node.element;
if (!watchers) return newValue; if (!watchers) {
return newValue;
}
var watcherNode = watchers.get(nodeTarget); var watcherNode = watchers.get(nodeTarget);
if (!watcherNode && isTextNode(node.element) && node.parent) watcherNode = watchers.get(node.parent.element); if (!watcherNode && isTextNode(node.element) && node.parent) {
watcherNode = watchers.get(node.parent.element);
}
var watcher = watcherNode ? watcherNode : watchers.get(exp.pattern); var watcher = watcherNode ? watcherNode : watchers.get(exp.pattern);
if (isFunction(watcher)) { if (isFunction(watcher)) {
var watcherValue = watcher(exp.value, newValue, exp.pattern, node.scope, node, exp.attribute); var watcherValue = watcher(exp.value, newValue, exp.pattern, node.scope, node, exp.attribute);
...@@ -278,9 +316,9 @@ function getWatcherValue(exp, newValue) { ...@@ -278,9 +316,9 @@ function getWatcherValue(exp, newValue) {
} }
} }
return newValue; return newValue;
} }
function getScopeFromPattern(scope, pattern) { function getScopeFromPattern(scope, pattern) {
var depth = getScopeDepth(pattern); var depth = getScopeDepth(pattern);
var scopeTarget = scope; var scopeTarget = scope;
while (depth > 0) { while (depth > 0) {
...@@ -288,14 +326,27 @@ function getScopeFromPattern(scope, pattern) { ...@@ -288,14 +326,27 @@ function getScopeFromPattern(scope, pattern) {
depth--; depth--;
} }
return scopeTarget; return scopeTarget;
} }
function getValueFromPattern(scope, pattern) { function getValueFromPattern(scope, pattern, context) {
var exp = new Expression(pattern); var exp = new Expression(pattern);
return getValue(scope, exp.pattern, exp.path, exp.params); return getValue(scope, exp.pattern, exp.path, exp.params, undefined, undefined, undefined, context);
} }
function getValue(scope, pattern, pathString, params, getFunction, getParams, paramsFound) { function getValue(scope, pattern, pathString, params, getFunction, getParams, paramsFound, context) {
// context
if (pattern === vars.element) {
return context[vars.element];
}
if (pattern === vars.parentElement) {
return context[vars.parentElement];
}
if (pattern === vars.attribute) {
return context[vars.attribute];
}
if (pattern === vars.scope) {
return context[vars.scope];
}
// string // string
if (regex.string.test(pattern)) { if (regex.string.test(pattern)) {
return trimQuotes(pattern); return trimQuotes(pattern);
...@@ -304,29 +355,39 @@ function getValue(scope, pattern, pathString, params, getFunction, getParams, pa ...@@ -304,29 +355,39 @@ function getValue(scope, pattern, pathString, params, getFunction, getParams, pa
var paramsValues = []; var paramsValues = [];
if (!paramsFound && params) { if (!paramsFound && params) {
for (var j = 0, jl = params.length; j < jl; j++) { for (var j = 0, jl = params.length; j < jl; j++) {
paramsValues.push(getValueFromPattern(scope, params[j])); paramsValues.push(getValueFromPattern(scope, params[j], context));
}
} }
else {
paramsValues = paramsFound;
}
if (getParams) {
return paramsValues;
} }
else paramsValues = paramsFound;
if (getParams) return paramsValues;
// find scope // find scope
var scopeTarget = getScopeFromPattern(scope, pattern); var scopeTarget = getScopeFromPattern(scope, pattern);
// remove parent string // remove parent string
pattern = pattern.replace(/..\//g, ''); pattern = pattern.replace(/..\//g, '');
pathString = pathString.replace(/..\//g, ''); pathString = pathString.replace(/..\//g, '');
if (!scopeTarget) return undefined; if (!scopeTarget) {
return undefined;
}
// search path // search path
var path = scopeTarget; var path = scopeTarget;
var pathParts = pathString.split(/\.|\[|\]/g); var pathParts = pathString.split(/\.|\[|\]/g);
if (pathParts.length > 0) { if (pathParts.length > 0) {
for (var i = 0, l = pathParts.length; i < l; i++) { for (var i = 0, l = pathParts.length; i < l; i++) {
if (pathParts[i] !== "") { if (pathParts[i] !== '') {
path = path[pathParts[i]]; path = path[pathParts[i]];
} }
if (!isDefined(path)) { if (!isDefined(path)) {
// no path, search in parent // no path, search in parent
if (scopeTarget._parent) return getValue(scopeTarget._parent, pattern, pathString, params, getFunction, getParams, paramsValues); if (scopeTarget._parent) {
else return undefined; return getValue(scopeTarget._parent, pattern, pathString, params, getFunction, getParams, paramsValues);
}
else {
return undefined;
}
} }
} }
} }
...@@ -335,46 +396,42 @@ function getValue(scope, pattern, pathString, params, getFunction, getParams, pa ...@@ -335,46 +396,42 @@ function getValue(scope, pattern, pathString, params, getFunction, getParams, pa
return path; return path;
} }
else { else {
if (getFunction) return path; if (getFunction) {
else return path.apply(null, paramsValues); return path;
}
else {
return path.apply(null, paramsValues);
}
} }
return undefined; return undefined;
} }
function getExpressionPath(value) { function getExpressionPath(value) {
var val = value.split('(')[0]; var val = value.split('(')[0];
val = trimScopeDepth(val); val = trimScopeDepth(val);
return val; return val;
} }
function getParamsFromString(value) { function getParamsFromString(value) {
return trimArray(value.split(regex.params)); return trimArray(value.split(regex.params));
} }
function getScopeDepth(value) { function getScopeDepth(value) {
var val = value.split('(')[0]; var val = value.split('(')[0];
var matches = val.match(regex.depth); var matches = val.match(regex.depth);
return !matches ? 0 : matches.length; return !matches ? 0 : matches.length;
} }
function getNodeFromElement(element, scope, isRepeaterDescendant) { function addAttribute(node, name, value) {
var node = new Node(element, scope); var attr;
node.previousSibling = element.previousSibling; node.attributes = node.attributes || [];
node.nextSibling = element.nextSibling;
var attributes = [];
var eventsArray = [];
for (var attr, name, value, attrs = element.attributes, j = 0, jj = attrs && attrs.length; j < jj; j++) {
attr = attrs[j];
if (attr.specified) {
name = attr.name;
value = attr.value;
if (name === settings.attributes.skip) { if (name === settings.attributes.skip) {
node.skip = (value === "" || value === "true"); node.skip = normalizeBoolean(value);
} }
if (name === settings.attributes.html) { if (name === settings.attributes.html) {
node.html = (value === "" || value === "true"); node.html = normalizeBoolean(value);
} }
if (name === settings.attributes.repeat && !isRepeaterDescendant) { if (name === settings.attributes.repeat && !node.isRepeaterDescendant) {
node.repeater = value; node.repeater = value;
} }
if ( if (
...@@ -392,59 +449,88 @@ function getNodeFromElement(element, scope, isRepeaterDescendant) { ...@@ -392,59 +449,88 @@ function getNodeFromElement(element, scope, isRepeaterDescendant) {
name === settings.attributes.selected || name === settings.attributes.selected ||
value.indexOf(settings.attributes.cloak) !== -1 value.indexOf(settings.attributes.cloak) !== -1
) { ) {
attributes.push(new Attribute(name, value, node)); attr = new Attribute(name, value, node);
node.attributes.push(attr);
}
if (events[name]) {
attr = new Attribute(name, value, node);
node.attributes.push(attr);
}
return attr;
}
function getNodeFromElement(element, scope) {
var node = new Node(element, scope);
node.previousSibling = element.previousSibling;
node.nextSibling = element.nextSibling;
var eventsArray = [];
for (var attr, attrs = element.attributes, j = 0, jj = attrs && attrs.length; j < jj; j++) {
attr = attrs[j];
if (attr.specified || attr.name === 'value') {
var newAttr = addAttribute(node, attr.name, attr.value);
if (events[attr.name]) {
if (events[attr.name] && !node.isRepeaterChild) {
eventsArray.push({name:events[attr.name], value:attr.value, attr: newAttr});
} }
if (events[name] && !isRepeaterDescendant) {
eventsArray.push({name:events[name], value:value});
attributes.push(new Attribute(name, value, node));
} }
} }
} }
node.attributes = attributes; for (var a=0, b=eventsArray.length; a<b; a++) {
for (var i = 0, l = eventsArray.length; i < l; i++) { node.addEvent(eventsArray[a].name, eventsArray[a].value, eventsArray[a].attr);
node.addEvent(eventsArray[i].name, eventsArray[i].value);
} }
return node; return node;
} }
function hasInterpolation(value) { function hasInterpolation(value) {
var matches = value.match(regex.token); var matches = value.match(regex.token);
return matches && matches.length > 0; return matches && matches.length > 0;
} }
function hasContent(value) { function hasContent(value) {
return regex.content.test(value) return regex.content.test(value);
} }
function isElementValid(element) { function isElementValid(element) {
if (!element) return; if (!element) {
return;
}
var type = element.nodeType; var type = element.nodeType;
if (!element || !type) return false; if (!element || !type) {
return false;
}
// comment // comment
if (type === 8) return false; if (type === 8) {
return false;
}
// empty text node // empty text node
if (type === 3 && !hasContent(element.nodeValue) && !hasInterpolation(element.nodeValue)) return false; if (type === 3 && !hasContent(element.nodeValue) && !hasInterpolation(element.nodeValue)) {
return false;
}
// result // result
return true; return true;
} }
function compile(template, element, parent, nodeTarget) { function compile(template, element, parent, nodeTarget) {
if (!isElementValid(element)) return; if (!isElementValid(element)) {
return;
}
// get node // get node
var node; var node;
if (!nodeTarget) { if (!nodeTarget) {
node = getNodeFromElement(element, parent ? parent.scope : new Scope(helpersScopeObject)._createChild(), parent && (parent.repeater || parent.isRepeaterDescendant) ); node = getNodeFromElement(element, parent ? parent.scope : new Scope(helpersScopeObject)._createChild());
} }
else { else {
node = nodeTarget; node = nodeTarget;
node.parent = parent; node.parent = parent;
} }
if (parent && (parent.repeater || parent.isRepeaterDescendant)) { if (parent && (parent.repeater || parent.isRepeaterChild)) {
node.isRepeaterDescendant = true; node.isRepeaterChild = true;
} }
node.template = template; node.template = template;
// children // children
if (node.skip) return; if (node.skip) {
return;
}
var child = element.firstChild; var child = element.firstChild;
while (child) { while (child) {
var childNode = compile(template, child, node); var childNode = compile(template, child, node);
...@@ -455,39 +541,47 @@ function compile(template, element, parent, nodeTarget) { ...@@ -455,39 +541,47 @@ function compile(template, element, parent, nodeTarget) {
child = child.nextSibling; child = child.nextSibling;
} }
return node; return node;
} }
function updateScopeWithData(scope, data) { function updateScopeWithData(scope, data) {
clearScope(scope); clearScope(scope);
for (var d in data) { for (var d in data) {
if (data.hasOwnProperty(d)) {
scope[d] = data[d]; scope[d] = data[d];
} }
} }
}
function clearScope(scope) { function clearScope(scope) {
for (var key in scope) { for (var key in scope) {
if (scope.hasOwnProperty(key)) {
if (key.substr(0, 1) !== '_') { if (key.substr(0, 1) !== '_') {
scope[key] = null; scope[key] = null;
delete scope[key]; delete scope[key];
} }
} }
} }
}
function updateNodeChildren(node) { function updateNodeChildren(node) {
if (node.repeater || !node.children || childNodeIsTemplate(node)) return; if (node.repeater || !node.children || childNodeIsTemplate(node)) {
return;
}
for (var i = 0, l = node.children.length; i < l; i++) { for (var i = 0, l = node.children.length; i < l; i++) {
node.children[i].update(); node.children[i].update();
} }
} }
function renderNodeChildren(node) { function renderNodeChildren(node) {
if (!node.children || childNodeIsTemplate(node)) return; if (!node.children || childNodeIsTemplate(node)) {
return;
}
for (var i = 0, l = node.children.length; i < l; i++) { for (var i = 0, l = node.children.length; i < l; i++) {
node.children[i].render(); node.children[i].render();
} }
} }
function renderNodeRepeater(node) { function renderNodeRepeater(node) {
var data = getRepeaterData(node.repeater, node.scope); var data = getRepeaterData(node.repeater, node.scope);
var previousElement; var previousElement;
if (isArray(data)) { if (isArray(data)) {
...@@ -509,9 +603,11 @@ function renderNodeRepeater(node) { ...@@ -509,9 +603,11 @@ function renderNodeRepeater(node) {
// process object // process object
var count = -1; var count = -1;
for (var o in data) { for (var o in data) {
if (data.hasOwnProperty(o)) {
count++; count++;
previousElement = createRepeaterChild(node, count, data[o], vars.key, o, previousElement); previousElement = createRepeaterChild(node, count, data[o], vars.key, o, previousElement);
} }
}
var size = count; var size = count;
while (count++ < node.childrenRepeater.length-1) { while (count++ < node.childrenRepeater.length-1) {
node.parent.element.removeChild(node.childrenRepeater[count].element); node.parent.element.removeChild(node.childrenRepeater[count].element);
...@@ -522,59 +618,88 @@ function renderNodeRepeater(node) { ...@@ -522,59 +618,88 @@ function renderNodeRepeater(node) {
if (node.element.parentNode) { if (node.element.parentNode) {
node.element.parentNode.removeChild(node.element); node.element.parentNode.removeChild(node.element);
} }
} }
function cloneRepeaterNode(element, node) { function compileClone(node, newNode) {
var newNode = new Node(element, node.scope._createChild()); if (!isElementValid(newNode.element)) {
return;
}
// create attribute
if (node.attributes) { if (node.attributes) {
var attrs = []; for (var i= 0, l=node.attributes.length; i<l; i++) {
for (var i = 0, l = node.attributes.length; i < l; i++) { var attr = node.attributes[i];
newNode.renderAsHtml = node.renderAsHtml; var newAttr = addAttribute(newNode, attr.name, attr.value);
if (node.attributes[i].name === settings.attributes.skip) { if (events[attr.name]) {
newNode.skip = (node.attributes[i].value === "" || node.attributes[i].value === "true"); newNode.addEvent(events[attr.name], attr.value, newAttr);
} }
if (node.attributes[i].name === settings.attributes.html) {
newNode.html = (node.attributes[i].value === "" || node.attributes[i].value === "true");
} }
if (node.attributes[i].name !== attributes.repeat) {
var attribute = new Attribute(node.attributes[i].name, node.attributes[i].value, newNode);
attrs.push(attribute);
} }
if (events[node.attributes[i].name]) { // children
newNode.addEvent(events[node.attributes[i].name], node.attributes[i].value); var child = node.element.firstChild;
var newChild = newNode.element.firstChild;
// loop
while (child && newChild) {
var childNode = node.getNode(child);
var newChildNode = new Node(newChild, newNode.scope);
newNode.children.push(newChildNode);
newChildNode.parent = newNode;
newChildNode.template = newNode.template;
newChildNode.isRepeaterChild = true;
var compiledNode = compileClone(childNode, newChildNode);
if (compiledNode) {
compiledNode.parent = newChildNode;
compiledNode.template = newChildNode.template;
newChildNode.children.push(compiledNode);
} }
child = child.nextSibling;
newChild = newChild.nextSibling;
} }
newNode.attributes = attrs; return newChildNode;
} }
function cloneRepeaterNode(element, node) {
var newNode = new Node(element, node.scope._createChild());
newNode.template = node.template;
newNode.parent = node;
newNode.isRepeaterChild = true;
newNode.isRepeaterDescendant = true;
compileClone(node, newNode);
return newNode; return newNode;
} }
function appendRepeaterElement(previousElement, node, newElement) {
if (!previousElement) {
if (node.element.previousSibling) {
insertAfter(node.element.previousSibling, newElement);
}
else if (node.element.nextSibling) {
insertBefore(node.element.nextSibling, newElement);
}
else {
node.parent.element.appendChild(newElement);
}
}
else {
insertAfter(previousElement, newElement);
}
}
function createRepeaterChild(node, count, data, indexVar, indexVarValue, previousElement) { function createRepeaterChild(node, count, data, indexVar, indexVarValue, previousElement) {
var existingChild = node.childrenRepeater[count]; var existingChild = node.childrenRepeater[count];
if (!existingChild) { if (!existingChild) {
// no existing node
var newElement = node.element.cloneNode(true); var newElement = node.element.cloneNode(true);
// need to append the cloned element to the DOM
// before changing attributes or IE will crash
appendRepeaterElement(previousElement, node, newElement);
// can't recreate the node with a cloned element on IE7 // can't recreate the node with a cloned element on IE7
// be cause the attributes are not specified annymore (attribute.specified) // be cause the attributes are not specified anymore (attribute.specified)
//var newNode = getNodeFromElement(newElement, node.scope._createChild(), true); //var newNode = getNodeFromElement(newElement, node.scope._createChild(), true);
var newNode = cloneRepeaterNode(newElement, node); var newNode = cloneRepeaterNode(newElement, node);
newNode.isRepeaterChild = true;
newNode.parent = node.parent;
newNode.template = node.template;
node.childrenRepeater[count] = newNode; node.childrenRepeater[count] = newNode;
updateScopeWithRepeaterData(node.repeater, newNode.scope, data); updateScopeWithRepeaterData(node.repeater, newNode.scope, data);
newNode.scope[indexVar] = indexVarValue; newNode.scope[indexVar] = indexVarValue;
compile(node.template, newElement, node.parent, newNode);
newNode.update(); newNode.update();
newNode.render(); newNode.render();
if (!previousElement) {
if (node.previousSibling) insertAfter(node.previousSibling, newElement);
else if (node.nextSibling) insertBefore(node.nextSibling, newElement);
else node.parent.element.appendChild(newElement);
}
else {
insertAfter(previousElement, newElement);
}
return newElement; return newElement;
} }
else { else {
...@@ -585,28 +710,29 @@ function createRepeaterChild(node, count, data, indexVar, indexVarValue, previou ...@@ -585,28 +710,29 @@ function createRepeaterChild(node, count, data, indexVar, indexVarValue, previou
existingChild.render(); existingChild.render();
return existingChild.element; return existingChild.element;
} }
} }
var Scope = function(data) { var Scope = function(data) {
var self;
function createChild(data) { function createChild(data) {
var obj = createObject(data); var obj = createObject(data);
obj._parent = this; obj._parent = self;
this._children.push(obj); self._children.push(obj);
return obj; return obj;
} }
function createObject(data) { function createObject(data) {
var obj = data || {}; var obj = data || {};
obj._parent = null; obj._parent = null;
obj._children = []; obj._children = [];
obj._createChild = function(data) { obj._createChild = function() {
self = obj;
return createChild.apply(obj, arguments); return createChild.apply(obj, arguments);
} };
return obj; return obj;
} }
return createObject(data); return createObject(data);
}; };
var Node = function(element, scope) {
var Node = function(element, scope) {
this.element = element; this.element = element;
this.scope = scope; this.scope = scope;
this.attributes = null; this.attributes = null;
...@@ -631,8 +757,8 @@ var Node = function(element, scope) { ...@@ -631,8 +757,8 @@ var Node = function(element, scope) {
this.interpolation = new Interpolation(this.value, this, undefined); this.interpolation = new Interpolation(this.value, this, undefined);
} }
}; };
Node.prototype = { Node.prototype = {
toString: function() { toString: function() {
return '[object Node]'; return '[object Node]';
}, },
...@@ -660,7 +786,6 @@ Node.prototype = { ...@@ -660,7 +786,6 @@ Node.prototype = {
this.element = null; this.element = null;
this.scope = null; this.scope = null;
this.attributes = null; this.attributes = null;
this.attributesHashMap = null;
this.value = null; this.value = null;
this.interpolation = null; this.interpolation = null;
this.repeater = null; this.repeater = null;
...@@ -674,17 +799,21 @@ Node.prototype = { ...@@ -674,17 +799,21 @@ Node.prototype = {
}, },
getNode: function(element) { getNode: function(element) {
var node; var node;
if (element === this.element) return this; if (element === this.element) {
else if (this.childrenRepeater.length > 0) { return this;
}
if (this.childrenRepeater.length > 0) {
for (var k = 0, kl = this.childrenRepeater.length; k < kl; k++) { for (var k = 0, kl = this.childrenRepeater.length; k < kl; k++) {
node = this.childrenRepeater[k].getNode(element); node = this.childrenRepeater[k].getNode(element);
if (node) return node; if (node) {
return node;
}
} }
} }
else {
for (var i = 0, l = this.children.length; i < l; i++) { for (var i = 0, l = this.children.length; i < l; i++) {
node = this.children[i].getNode(element); node = this.children[i].getNode(element);
if (node) return node; if (node) {
return node;
} }
} }
return null; return null;
...@@ -700,7 +829,9 @@ Node.prototype = { ...@@ -700,7 +829,9 @@ Node.prototype = {
} }
}, },
update: function() { update: function() {
if (childNodeIsTemplate(this)) return; if (childNodeIsTemplate(this)) {
return;
}
if (isDefined(this.interpolation)) { if (isDefined(this.interpolation)) {
this.interpolation.update(); this.interpolation.update();
} }
...@@ -712,7 +843,9 @@ Node.prototype = { ...@@ -712,7 +843,9 @@ Node.prototype = {
updateNodeChildren(this); updateNodeChildren(this);
}, },
invalidateData: function() { invalidateData: function() {
if (childNodeIsTemplate(this)) return; if (childNodeIsTemplate(this)) {
return;
}
this.invalidate = true; this.invalidate = true;
var i, l; var i, l;
if (this.attributes) { if (this.attributes) {
...@@ -727,14 +860,17 @@ Node.prototype = { ...@@ -727,14 +860,17 @@ Node.prototype = {
this.children[i].invalidateData(); this.children[i].invalidateData();
} }
}, },
addEvent: function(type, pattern) { addEvent: function(type, pattern, attr) {
if (this.repeater) return; if (this.repeater) {
return;
}
if (this.eventHandlers[type]) { if (this.eventHandlers[type]) {
this.removeEvent(type); this.removeEvent(type);
} }
var scope = this.scope; var scope = this.scope;
var node = this;
var handler = function(event) { var handler = function(event) {
var exp = new Expression(pattern, this.node); var exp = new Expression(pattern, node, attr);
var func = exp.getValue(scope, true); var func = exp.getValue(scope, true);
var params = exp.getValue(scope, false, true); var params = exp.getValue(scope, false, true);
params.unshift(event); params.unshift(event);
...@@ -753,7 +889,9 @@ Node.prototype = { ...@@ -753,7 +889,9 @@ Node.prototype = {
clearEvents: function() { clearEvents: function() {
if (this.eventHandlers) { if (this.eventHandlers) {
for (var key in this.eventHandlers) { for (var key in this.eventHandlers) {
this.removeEvent(key, this.eventHandlers[key]); if (this.eventHandlers.hasOwnProperty(key)) {
this.removeEvent(key);
}
} }
} }
if (this.children) { if (this.children) {
...@@ -768,7 +906,9 @@ Node.prototype = { ...@@ -768,7 +906,9 @@ Node.prototype = {
} }
}, },
render: function() { render: function() {
if (childNodeIsTemplate(this)) return; if (childNodeIsTemplate(this)) {
return;
}
if (this.invalidate) { if (this.invalidate) {
this.invalidate = false; this.invalidate = false;
if (isTextNode(this.element)) { if (isTextNode(this.element)) {
...@@ -792,22 +932,26 @@ Node.prototype = { ...@@ -792,22 +932,26 @@ Node.prototype = {
renderNodeChildren(this); renderNodeChildren(this);
} }
} }
}; };
var Attribute = function(name, value, node) { var Attribute = function(name, value, node) {
this.name = name; this.name = name;
this.value = value; this.value = value;
this.node = node; this.node = node;
this.interpolationName = new Interpolation(this.name, null, this); this.interpolationName = new Interpolation(this.name, null, this);
this.interpolationValue = new Interpolation(this.value, null, this); this.interpolationValue = new Interpolation(this.value, null, this);
this.invalidate = false; this.invalidate = false;
}; };
Attribute.prototype = { Attribute.prototype = {
toString: function() { toString: function() {
return '[object Attribute]'; return '[object Attribute]';
}, },
dispose: function() { dispose: function() {
if (this.interpolationName) this.interpolationName.dispose(); if (this.interpolationName) {
if (this.interpolationValue) this.interpolationValue.dispose(); this.interpolationName.dispose();
}
if (this.interpolationValue) {
this.interpolationValue.dispose();
}
this.interpolationName = null; this.interpolationName = null;
this.interpolationValue = null; this.interpolationValue = null;
this.node = null; this.node = null;
...@@ -816,47 +960,77 @@ Attribute.prototype = { ...@@ -816,47 +960,77 @@ Attribute.prototype = {
this.previousName = null; this.previousName = null;
}, },
update: function() { update: function() {
if (this.node.repeater) {
return;
}
this.interpolationName.update(); this.interpolationName.update();
this.interpolationValue.update(); this.interpolationValue.update();
}, },
render: function() { render: function() {
if (this.node.repeater) return; if (this.node.repeater) {
return;
}
// normal attribute
function renderAttribute(name, value, node) {
if (name === 'value' && node.element['value'] !== undefined) {
element.value = value;
}
else if (ie === 7 && name === 'class') {
element.className = value;
}
else {
element.setAttribute(name, value);
}
}
// boolean attribute
function renderBooleanAttribute(name, value) {
element.setAttribute(name, value);
}
// special attribute
function renderSpecialAttribute(value, attrName) {
if (normalizeBoolean(value)) {
element.setAttribute(attrName, attrName);
}
else {
element.removeAttribute(attrName);
}
}
// src attribute
function renderSrc(value) {
element.setAttribute('src', value);
}
// href attribute
function renderHref(value) {
element.setAttribute('href', value);
}
var element = this.node.element; var element = this.node.element;
if (this.invalidate) { if (this.invalidate) {
this.invalidate = false; this.invalidate = false;
this.previousName = this.name; this.previousName = this.name;
this.name = isDefined(this.interpolationName.render()) ? this.interpolationName.render() : this.name; this.name = isDefined(this.interpolationName.render()) ? this.interpolationName.render() : this.name;
this.value = isDefined(this.interpolationValue.render()) ? this.interpolationValue.render() : this.value; this.value = isDefined(this.interpolationValue.render()) ? this.interpolationValue.render() : this.value;
if (this.name === attributes.src) { if (this.name === attributes.src) {
renderSrc(this.name, this.value); renderSrc(this.value);
} }
else if (this.name === attributes.href) { else if (this.name === attributes.href) {
renderHref(this.name, this.value); renderHref(this.value);
}
else {
if (this.node.isRepeaterChild && ie === 7) {
// delete attributes on cloned elements crash IE7
} }
else { else {
if (ie !== 7 || (ie === 7 && !this.node.isRepeaterChild)) {
this.node.element.removeAttribute(this.interpolationName.value); this.node.element.removeAttribute(this.interpolationName.value);
} }
if (this.previousName) { if (this.previousName) {
if (ie === 7 && this.previousName === 'class') { if (ie === 7 && this.previousName === 'class') {
// iE // iE
this.node.element.className = ""; this.node.element.className = '';
}
else {
if (this.node.isRepeaterChild && ie === 7) {
// delete attributes on cloned elements crash IE7
} }
else { else {
if (ie !== 7 || (ie === 7 && !this.node.isRepeaterChild)) {
this.node.element.removeAttribute(this.previousName); this.node.element.removeAttribute(this.previousName);
} }
} }
} }
renderAttribute(this.name, this.value, this.previousName); renderAttribute(this.name, this.value, this.node);
} }
} }
// cloak // cloak
...@@ -865,68 +1039,52 @@ Attribute.prototype = { ...@@ -865,68 +1039,52 @@ Attribute.prototype = {
} }
// hide // hide
if (this.name === attributes.hide) { if (this.name === attributes.hide) {
element.style.display = isAttributeDefined(this.value) ? "none" : "block"; var bool = normalizeBoolean(this.value);
renderAttribute(this.name, bool, this.node);
element.style.display = bool ? 'none' : '';
} }
// show // show
if (this.name === attributes.show) { if (this.name === attributes.show) {
element.style.display = isAttributeDefined(this.value) ? "block" : "none"; var bool = normalizeBoolean(this.value);
renderAttribute(this.name, bool, this.node);
element.style.display = bool ? '' : 'none';
} }
// checked // checked
if (this.name === attributes.checked) { if (this.name === attributes.checked) {
renderSpecialAttribute(this.name, this.value, 'checked'); renderSpecialAttribute(this.value, 'checked');
element.checked = isAttributeDefined(this.value) ? true : false; renderAttribute(this.name, normalizeBoolean(this.value) ? true : false, this.node);
element.checked = normalizeBoolean(this.value) ? true : false;
} }
// disabled // disabled
if (this.name === attributes.disabled) { if (this.name === attributes.disabled) {
renderSpecialAttribute(this.name, this.value, 'disabled'); renderSpecialAttribute(this.value, 'disabled');
renderAttribute(this.name, normalizeBoolean(this.value) ? true : false, this.node);
} }
// multiple // multiple
if (this.name === attributes.multiple) { if (this.name === attributes.multiple) {
renderSpecialAttribute(this.name, this.value, 'multiple'); renderSpecialAttribute(this.value, 'multiple');
renderAttribute(this.name, normalizeBoolean(this.value) ? true : false, this.node);
} }
// readonly // readonly
if (this.name === attributes.readonly) { if (this.name === attributes.readonly) {
var bool = normalizeBoolean(this.value);
if (ie === 7) { if (ie === 7) {
element.readOnly = isAttributeDefined(this.value) ? true : false; element.readOnly = bool ? true : false;
} }
else { else {
renderSpecialAttribute(this.name, this.value, 'readonly'); renderSpecialAttribute(this.value, 'readonly');
} }
renderAttribute(this.name, bool ? true : false, this.node);
} }
// selected // selected
if (this.name === attributes.selected) { if (this.name === attributes.selected) {
renderSpecialAttribute(this.name, this.value, 'selected'); renderSpecialAttribute(this.value, 'selected');
} renderAttribute(this.name, normalizeBoolean(this.value) ? true : false, this.node);
// normal attribute
function renderAttribute(name, value) {
if (ie === 7 && name === "class") {
element.className = value;
}
else {
element.setAttribute(name, value);
} }
} }
// special attribute };
function renderSpecialAttribute(name, value, attrName) {
if (isAttributeDefined(value)) {
element.setAttribute(attrName, attrName);
}
else {
element.removeAttribute(attrName);
}
}
// src attribute
function renderSrc(name, value) {
element.setAttribute('src', value);
}
// href attribute
function renderHref(name, value) {
element.setAttribute('href', value);
}
}
};
var Interpolation = function(value, node, attribute) { var Interpolation = function(value, node, attribute) {
this.value = node && !isTextNode(node.element) ? trim(value) : value; this.value = node && !isTextNode(node.element) ? trim(value) : value;
this.node = node; this.node = node;
this.attribute = attribute; this.attribute = attribute;
...@@ -946,8 +1104,8 @@ var Interpolation = function(value, node, attribute) { ...@@ -946,8 +1104,8 @@ var Interpolation = function(value, node, attribute) {
} }
trimArray(this.sequence); trimArray(this.sequence);
} }
}; };
Interpolation.prototype = { Interpolation.prototype = {
toString: function() { toString: function() {
return '[object Interpolation]'; return '[object Interpolation]';
}, },
...@@ -970,22 +1128,30 @@ Interpolation.prototype = { ...@@ -970,22 +1128,30 @@ Interpolation.prototype = {
} }
}, },
render: function() { render: function() {
var rendered = ""; var rendered = '';
if (this.sequence) { if (this.sequence) {
for (var i = 0, l = this.sequence.length; i < l; i++) { for (var i = 0, l = this.sequence.length; i < l; i++) {
var val = ""; var val = '';
if (isExpression(this.sequence[i])) val = this.sequence[i].value; if (isExpression(this.sequence[i])) {
else val = this.sequence[i]; val = this.sequence[i].value;
if (!isDefined(val)) val = ""; }
else {
val = this.sequence[i];
}
if (!isDefined(val)) {
val = '';
}
rendered += val; rendered += val;
} }
} }
return rendered; return rendered;
} }
}; };
var Expression = function(pattern, node, attribute) { var Expression = function(pattern, node, attribute) {
if (!isDefined(pattern)) return; if (!isDefined(pattern)) {
return;
}
this.pattern = pattern; this.pattern = pattern;
this.isString = regex.string.test(pattern); this.isString = regex.string.test(pattern);
this.node = node; this.node = node;
...@@ -1003,8 +1169,8 @@ var Expression = function(pattern, node, attribute) { ...@@ -1003,8 +1169,8 @@ var Expression = function(pattern, node, attribute) {
this.path = getExpressionPath(this.pattern); this.path = getExpressionPath(this.pattern);
this.params = !this.isFunction ? null : getParamsFromString(this.pattern.match(regex.func)[2]); this.params = !this.isFunction ? null : getParamsFromString(this.pattern.match(regex.func)[2]);
} }
}; };
Expression.prototype = { Expression.prototype = {
toString: function() { toString: function() {
return '[object Expression]'; return '[object Expression]';
}, },
...@@ -1018,8 +1184,12 @@ Expression.prototype = { ...@@ -1018,8 +1184,12 @@ Expression.prototype = {
}, },
update: function() { update: function() {
var node = this.node; var node = this.node;
if (!node && this.attribute) node = this.attribute.node; if (!node && this.attribute) {
if (!node && node.scope) return; node = this.attribute.node;
}
if (!node && node.scope) {
return;
}
var newValue = this.getValue(node.scope); var newValue = this.getValue(node.scope);
newValue = getWatcherValue(this, newValue); newValue = getWatcherValue(this, newValue);
if (this.value !== newValue) { if (this.value !== newValue) {
...@@ -1028,42 +1198,69 @@ Expression.prototype = { ...@@ -1028,42 +1198,69 @@ Expression.prototype = {
} }
}, },
getValue: function(scope, getFunction, getParams) { getValue: function(scope, getFunction, getParams) {
return getValue(scope, this.pattern, this.path, this.params, getFunction, getParams); var node = this.node;
if (!node && this.attribute) {
node = this.attribute.node;
}
var context = {};
if (node) {
context[vars.element] = node.element;
if (node.element) {
context[vars.parentElement] = node.element.parentNode;
}
} }
}; context[vars.attribute] = this.attribute;
context[vars.scope] = scope;
return getValue(scope, this.pattern, this.path, this.params, getFunction, getParams, undefined, context);
}
};
var templates = new HashMap(); var templates = new HashMap('st');
var Template = function(element) { var Template = function(element) {
this.watchers = new HashMap(); this.watchers = new HashMap('stw');
this.node = null; this.node = null;
this.scope = null; this.scope = null;
this.compile(element); this.compile(element);
}; };
Template.prototype = { Template.prototype = {
toString: function() { toString: function() {
return '[object Template]'; return '[object Template]';
}, },
compile: function(element) { compile: function(element) {
if (element) this.element = element; if (element) {
if (this.node) this.node.dispose(); this.element = element;
}
if (this.node) {
this.node.dispose();
}
this.node = compile(this, this.element); this.node = compile(this, this.element);
this.node.root = true; this.node.root = true;
this.scope = this.node.scope; this.scope = this.node.scope;
}, },
update: function(data) { update: function(data) {
if (isDefined(data)) updateScopeWithData(this.node.scope, data); if (isDefined(data)) {
if (this.node) this.node.update(); updateScopeWithData(this.node.scope, data);
}
if (this.node) {
this.node.update();
}
}, },
render: function(data) { render: function(data) {
this.update(data); this.update(data);
if (this.node) this.node.render(); if (this.node) {
this.node.render();
}
}, },
invalidate: function() { invalidate: function() {
if (this.node) this.node.invalidateData(); if (this.node) {
this.node.invalidateData();
}
}, },
watch: function(target, watcher) { watch: function(target, watcher) {
if ( (!isString(target) && !isElement(target)) || !isFunction(watcher)) return; if ( (!isString(target) && !isElement(target)) || !isFunction(watcher)) {
return;
}
this.watchers.put(target, watcher); this.watchers.put(target, watcher);
}, },
unwatch: function(target) { unwatch: function(target) {
...@@ -1090,47 +1287,36 @@ Template.prototype = { ...@@ -1090,47 +1287,36 @@ Template.prototype = {
this.watchers = null; this.watchers = null;
this.node = null; this.node = null;
} }
}; };
// written by Dean Edwards, 2005 // written by Dean Edwards, 2005
// with input from Tino Zijdel, Matthias Miller, Diego Perini // with input from Tino Zijdel, Matthias Miller, Diego Perini
// http://dean.edwards.name/weblog/2005/10/add-event/ // http://dean.edwards.name/weblog/2005/10/add-event/
function addEvent(element, type, handler) { function addEvent(element, type, handler) {
if (element.addEventListener) { if (element.addEventListener) {
element.addEventListener(type, handler, false); element.addEventListener(type, handler, false);
} else { } else {
// assign each event handler a unique ID // assign each event handler a unique ID
if (!handler.$$guid) handler.$$guid = addEvent.guid++; if (!handler.$$guid) {
handler.$$guid = addEvent.guid++;
}
// create a hash table of event types for the element // create a hash table of event types for the element
if (!element.events) element.events = {}; if (!element.events) {
element.events = {};
}
// create a hash table of event handlers for each element/event pair // create a hash table of event handlers for each element/event pair
var handlers = element.events[type]; var handlers = element.events[type];
if (!handlers) { if (!handlers) {
handlers = element.events[type] = {}; handlers = element.events[type] = {};
// store the existing event handler (if there is one) // store the existing event handler (if there is one)
if (element["on" + type]) { if (element['on' + type]) {
handlers[0] = element["on" + type]; handlers[0] = element['on' + type];
} }
} }
// store the event handler in the hash table // store the event handler in the hash table
handlers[handler.$$guid] = handler; handlers[handler.$$guid] = handler;
// assign a global event handler to do all the work // assign a global event handler to do all the work
element["on" + type] = handleEvent; element['on' + type] = function(event) {
}
};
// a counter used to create unique IDs
addEvent.guid = 1;
function removeEvent(element, type, handler) {
if (element.removeEventListener) {
element.removeEventListener(type, handler, false);
} else {
// delete the event handler from the hash table
if (element.events && element.events[type]) {
delete element.events[type][handler.$$guid];
}
}
};
function handleEvent(event) {
var returnValue = true; var returnValue = true;
// grab the event object (IE uses a global event object) // grab the event object (IE uses a global event object)
event = event || fixEvent(((this.ownerDocument || this.document || this).parentWindow || window).event); event = event || fixEvent(((this.ownerDocument || this.document || this).parentWindow || window).event);
...@@ -1138,38 +1324,60 @@ function handleEvent(event) { ...@@ -1138,38 +1324,60 @@ function handleEvent(event) {
var handlers = this.events[event.type]; var handlers = this.events[event.type];
// execute each event handler // execute each event handler
for (var i in handlers) { for (var i in handlers) {
if (handlers.hasOwnProperty(i)) {
this.$$handleEvent = handlers[i]; this.$$handleEvent = handlers[i];
if (this.$$handleEvent(event) === false) { if (this.$$handleEvent(event) === false) {
returnValue = false; returnValue = false;
} }
} }
}
return returnValue; return returnValue;
}; };
function fixEvent(event) { }
}
// a counter used to create unique IDs
addEvent.guid = 1;
function removeEvent(element, type, handler) {
if (element.removeEventListener) {
element.removeEventListener(type, handler, false);
} else {
// delete the event handler from the hash table
if (element.events && element.events[type]) {
delete element.events[type][handler.$$guid];
}
}
}
function fixEvent(event) {
// add W3C standard event methods // add W3C standard event methods
event.preventDefault = fixEvent.preventDefault; event.preventDefault = fixEvent.preventDefault;
event.stopPropagation = fixEvent.stopPropagation; event.stopPropagation = fixEvent.stopPropagation;
return event; return event;
}; }
fixEvent.preventDefault = function() { fixEvent.preventDefault = function() {
this.returnValue = false; this.returnValue = false;
}; };
fixEvent.stopPropagation = function() { fixEvent.stopPropagation = function() {
this.cancelBubble = true; this.cancelBubble = true;
}; };
var maxDepth; var maxDepth;
var eventStore = []; var eventStore = [];
function parseEvents(element, object, depth) { function parseEvents(element, object, depth) {
maxDepth = depth === undefined ? Number.MAX_VALUE : depth; maxDepth = depth === undefined ? Number.MAX_VALUE : depth;
parseNode(element, object, 0, true); parseNode(element, object, 0, true);
} }
function parseNode(element, object, depth, isRoot) { function parseNode(element, object, depth, isRoot) {
if (!isElement(element)) throw new Error('Error in soma.template.parseEvents, only a DOM Element can be parsed.'); if (!isElement(element)) {
if (isRoot) parseAttributes(element, object); throw new Error('Error in soma.template.parseEvents, only a DOM Element can be parsed.');
if (maxDepth === 0) return; }
if (isRoot) {
parseAttributes(element, object);
}
if (maxDepth === 0) {
return;
}
var child = element.firstChild; var child = element.firstChild;
while (child) { while (child) {
if (child.nodeType === 1) { if (child.nodeType === 1) {
...@@ -1180,17 +1388,16 @@ function parseNode(element, object, depth, isRoot) { ...@@ -1180,17 +1388,16 @@ function parseNode(element, object, depth, isRoot) {
} }
child = child.nextSibling; child = child.nextSibling;
} }
} }
function parseAttributes(element, object) { function parseAttributes(element, object) {
var attributes = [];
for (var attr, name, value, attrs = element.attributes, j = 0, jj = attrs && attrs.length; j < jj; j++) { for (var attr, name, value, attrs = element.attributes, j = 0, jj = attrs && attrs.length; j < jj; j++) {
attr = attrs[j]; attr = attrs[j];
if (attr.specified) { if (attr.specified) {
name = attr.name; name = attr.name;
value = attr.value; value = attr.value;
if (events[name]) { if (events[name]) {
var handler = getHandlerFromPattern(object, value, element); var handler = getHandlerFromPattern(object, value);
if (handler && isFunction(handler)) { if (handler && isFunction(handler)) {
addEvent(element, events[name], handler); addEvent(element, events[name], handler);
eventStore.push({element:element, type:events[name], handler:handler}); eventStore.push({element:element, type:events[name], handler:handler});
...@@ -1198,9 +1405,9 @@ function parseAttributes(element, object) { ...@@ -1198,9 +1405,9 @@ function parseAttributes(element, object) {
} }
} }
} }
} }
function getHandlerFromPattern(object, pattern, child) { function getHandlerFromPattern(object, pattern) {
var parts = pattern.match(regex.func); var parts = pattern.match(regex.func);
if (parts) { if (parts) {
var func = parts[1]; var func = parts[1];
...@@ -1208,9 +1415,9 @@ function getHandlerFromPattern(object, pattern, child) { ...@@ -1208,9 +1415,9 @@ function getHandlerFromPattern(object, pattern, child) {
return object[func]; return object[func];
} }
} }
} }
function clearEvents(element) { function clearEvents(element) {
var i = eventStore.length, l = 0; var i = eventStore.length, l = 0;
while (--i >= l) { while (--i >= l) {
var item = eventStore[i]; var item = eventStore[i];
...@@ -1219,12 +1426,16 @@ function clearEvents(element) { ...@@ -1219,12 +1426,16 @@ function clearEvents(element) {
eventStore.splice(i, 1); eventStore.splice(i, 1);
} }
} }
} }
if (settings.autocreate && typeof document === 'object') {
var ready;
if (typeof document === 'object') {
// https://github.com/ded/domready // https://github.com/ded/domready
var ready=function(){function l(b){for(k=1;b=a.shift();)b()}var b,a=[],c=!1,d=document,e=d.documentElement,f=e.doScroll,g="DOMContentLoaded",h="addEventListener",i="onreadystatechange",j="readyState",k=/^loade|c/.test(d[j]);return d[h]&&d[h](g,b=function(){d.removeEventListener(g,b,c),l()},c),f&&d.attachEvent(i,b=function(){/^c/.test(d[j])&&(d.detachEvent(i,b),l())}),f?function(b){self!=top?k?b():a.push(b):function(){try{e.doScroll("left")}catch(a){return setTimeout(function(){ready(b)},50)}b()}()}:function(b){k?b():a.push(b)}}(); var ready=function(){function l(b){for(k=1;b=a.shift();)b()}var b,a=[],c=!1,d=document,e=d.documentElement,f=e.doScroll,g="DOMContentLoaded",h="addEventListener",i="onreadystatechange",j="readyState",k=/^loade|c/.test(d[j]);return d[h]&&d[h](g,b=function(){d.removeEventListener(g,b,c),l()},c),f&&d.attachEvent(i,b=function(){/^c/.test(d[j])&&(d.detachEvent(i,b),l())}),f?function(b){self!=top?k?b():a.push(b):function(){try{e.doScroll("left")}catch(a){return setTimeout(function(){ready(b)},50)}b()}()}:function(b){k?b():a.push(b)}}();
if (settings.autocreate) {
var parse = function(element) { var parse = function(element) {
var child = !element ? document.body : element.firstChild; var child = !element ? document.body : element.firstChild;
while (child) { while (child) {
...@@ -1238,19 +1449,21 @@ if (settings.autocreate && typeof document === 'object') { ...@@ -1238,19 +1449,21 @@ if (settings.autocreate && typeof document === 'object') {
if (isFunction(f)) { if (isFunction(f)) {
soma.template.bootstrap(attrValue, child, f); soma.template.bootstrap(attrValue, child, f);
} }
} catch(err){}; } catch(err){}
} }
} }
child = child.nextSibling; child = child.nextSibling;
} }
}; };
ready(parse); ready(parse);
} }
function bootstrapTemplate(attrValue, element, func) { }
function bootstrapTemplate(attrValue, element, func) {
var tpl = createTemplate(element); var tpl = createTemplate(element);
func(tpl, tpl.scope, tpl.element, tpl.node); func(tpl, tpl.scope, tpl.element, tpl.node);
} }
function createTemplate(source, target) {
function createTemplate(source, target) {
var element; var element;
if (isString(source)) { if (isString(source)) {
// string template // string template
...@@ -1283,19 +1496,22 @@ function createTemplate(source, target) { ...@@ -1283,19 +1496,22 @@ function createTemplate(source, target) {
var template = new Template(element); var template = new Template(element);
templates.put(element, template); templates.put(element, template);
return template; return template;
} }
function getTemplate(element) { function getTemplate(element) {
return templates.get(element); return templates.get(element);
} }
function renderAllTemplates() { function renderAllTemplates() {
var data = templates.getData();
for (var key in templates.getData()) { for (var key in templates.getData()) {
if (data.hasOwnProperty(key)) {
templates.get(key).render(); templates.get(key).render();
} }
} }
}
function appendHelpers(obj) { function appendHelpers(obj) {
if (obj === null) { if (obj === null) {
helpersObject = {}; helpersObject = {};
helpersScopeObject = {}; helpersScopeObject = {};
...@@ -1308,20 +1524,20 @@ function appendHelpers(obj) { ...@@ -1308,20 +1524,20 @@ function appendHelpers(obj) {
} }
} }
return helpersObject; return helpersObject;
} }
// set regex // set regex
tokens.start(tokenStart); tokens.start(tokenStart);
tokens.end(tokenEnd); tokens.end(tokenEnd);
// plugins // plugins
soma.plugins = soma.plugins || {}; soma.plugins = soma.plugins || {};
var TemplatePlugin = function(instance, injector) { var TemplatePlugin = function(instance, injector) {
instance.constructor.prototype.createTemplate = function(cl, domElement) { instance.constructor.prototype.createTemplate = function(cl, domElement) {
if (!cl || typeof cl !== "function") { if (!cl || typeof cl !== 'function') {
throw new Error("Error creating a template, the first parameter must be a function."); throw new Error('Error creating a template, the first parameter must be a function.');
} }
if (domElement && isElement(domElement)) { if (domElement && isElement(domElement)) {
var template = soma.template.create(domElement); var template = soma.template.create(domElement);
...@@ -1332,46 +1548,46 @@ var TemplatePlugin = function(instance, injector) { ...@@ -1332,46 +1548,46 @@ var TemplatePlugin = function(instance, injector) {
} }
cl.prototype.render = template.render.bind(template); cl.prototype.render = template.render.bind(template);
var childInjector = injector.createChild(); var childInjector = injector.createChild();
childInjector.mapValue("template", template); childInjector.mapValue('template', template);
childInjector.mapValue("scope", template.scope); childInjector.mapValue('scope', template.scope);
childInjector.mapValue("element", template.element); childInjector.mapValue('element', template.element);
return childInjector.createInstance(cl); return childInjector.createInstance(cl);
} }
return null; return null;
} };
soma.template.bootstrap = function(attrValue, element, func) { soma.template.bootstrap = function(attrValue, element, func) {
instance.createTemplate(func, element); instance.createTemplate(func, element);
} };
} };
if (soma.plugins && soma.plugins.add) { if (soma.plugins && soma.plugins.add) {
soma.plugins.add(TemplatePlugin); soma.plugins.add(TemplatePlugin);
}
soma.template.Plugin = TemplatePlugin;
// exports
soma.template.create = createTemplate;
soma.template.get = getTemplate;
soma.template.renderAll = renderAllTemplates;
soma.template.helpers = appendHelpers;
soma.template.bootstrap = bootstrapTemplate;
soma.template.addEvent = addEvent;
soma.template.removeEvent = removeEvent;
soma.template.parseEvents = parseEvents;
soma.template.clearEvents = clearEvents;
soma.template.ready = ready;
// register for AMD module
if (typeof define === 'function' && define.amd) {
define("soma-template", soma.template);
}
// export for node.js
if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
exports = module.exports = soma.template;
} }
soma.template.Plugin = TemplatePlugin;
// exports
soma.template.create = createTemplate;
soma.template.get = getTemplate;
soma.template.renderAll = renderAllTemplates;
soma.template.helpers = appendHelpers;
soma.template.bootstrap = bootstrapTemplate;
soma.template.addEvent = addEvent;
soma.template.removeEvent = removeEvent;
soma.template.parseEvents = parseEvents;
soma.template.clearEvents = clearEvents;
soma.template.ready = ready;
// register for AMD module
if (typeof define === 'function' && typeof define.amd !== 'undefined') {
define('soma-template', soma.template);
}
// export for node.js
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
module.exports = soma.template;
}
if (typeof exports !== 'undefined') {
exports = soma.template; exports = soma.template;
} }
})(this['soma'] = this['soma'] || {}); })(this['soma'] = this['soma'] || {});
\ No newline at end of file
/* /*
Copyright (c) | 2012 | infuse.js | Romuald Quantin | www.soundstep.com | romu@soundstep.com Copyright (c) | 2013 | infuse.js | Romuald Quantin | www.soundstep.com | romu@soundstep.com
Permission is hereby granted, free of charge, to any person obtaining a copy of this software Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without restriction, and associated documentation files (the "Software"), to deal in the Software without restriction,
...@@ -17,10 +17,11 @@ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN ...@@ -17,10 +17,11 @@ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
;(function(infuse, undefined) { (function(infuse) {
"use strict";
infuse.version = "0.6.3"; 'use strict';
infuse.version = '0.7.0';
// regex from angular JS (https://github.com/angular/angular.js) // regex from angular JS (https://github.com/angular/angular.js)
var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m; var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
...@@ -28,26 +29,26 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -28,26 +29,26 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/; var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
if(!Array.prototype.contains) { function contains(arr, value) {
Array.prototype.contains = function(value) { var i = arr.length;
var i = this.length;
while (i--) { while (i--) {
if (this[i] === value) return true; if (arr[i] === value) {
return true;
}
} }
return false; return false;
};
} }
infuse.InjectorError = { infuse.InjectorError = {
MAPPING_BAD_PROP: "[Error infuse.Injector.mapClass/mapValue] the first parameter is invalid, a string is expected", MAPPING_BAD_PROP: '[Error infuse.Injector.mapClass/mapValue] the first parameter is invalid, a string is expected',
MAPPING_BAD_VALUE: "[Error infuse.Injector.mapClass/mapValue] the second parameter is invalid, it can't null or undefined, with property: ", MAPPING_BAD_VALUE: '[Error infuse.Injector.mapClass/mapValue] the second parameter is invalid, it can\'t null or undefined, with property: ',
MAPPING_BAD_CLASS: "[Error infuse.Injector.mapClass/mapValue] the second parameter is invalid, a function is expected, with property: ", MAPPING_BAD_CLASS: '[Error infuse.Injector.mapClass/mapValue] the second parameter is invalid, a function is expected, with property: ',
MAPPING_BAD_SINGLETON: "[Error infuse.Injector.mapClass] the third parameter is invalid, a boolean is expected, with property: ", MAPPING_BAD_SINGLETON: '[Error infuse.Injector.mapClass] the third parameter is invalid, a boolean is expected, with property: ',
MAPPING_ALREADY_EXISTS: "[Error infuse.Injector.mapClass/mapValue] this mapping already exists, with property: ", MAPPING_ALREADY_EXISTS: '[Error infuse.Injector.mapClass/mapValue] this mapping already exists, with property: ',
CREATE_INSTANCE_INVALID_PARAM: "[Error infuse.Injector.createInstance] invalid parameter, a function is expected", CREATE_INSTANCE_INVALID_PARAM: '[Error infuse.Injector.createInstance] invalid parameter, a function is expected',
NO_MAPPING_FOUND: "[Error infuse.Injector.getInstance] no mapping found", NO_MAPPING_FOUND: '[Error infuse.Injector.getInstance] no mapping found',
INJECT_INSTANCE_IN_ITSELF_PROPERTY: "[Error infuse.Injector.getInjectedValue] A matching property has been found in the target, you can't inject an instance in itself", INJECT_INSTANCE_IN_ITSELF_PROPERTY: '[Error infuse.Injector.getInjectedValue] A matching property has been found in the target, you can\'t inject an instance in itself',
INJECT_INSTANCE_IN_ITSELF_CONSTRUCTOR: "[Error infuse.Injector.getInjectedValue] A matching constructor parameter has been found in the target, you can't inject an instance in itself" INJECT_INSTANCE_IN_ITSELF_CONSTRUCTOR: '[Error infuse.Injector.getInjectedValue] A matching constructor parameter has been found in the target, you can\'t inject an instance in itself'
}; };
var MappingVO = function(prop, value, cl, singleton) { var MappingVO = function(prop, value, cl, singleton) {
...@@ -58,7 +59,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -58,7 +59,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
}; };
var validateProp = function(prop) { var validateProp = function(prop) {
if (typeof prop !== "string") { if (typeof prop !== 'string') {
throw new Error(infuse.InjectorError.MAPPING_BAD_PROP); throw new Error(infuse.InjectorError.MAPPING_BAD_PROP);
} }
}; };
...@@ -70,20 +71,20 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -70,20 +71,20 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
}; };
var validateClass = function(prop, val) { var validateClass = function(prop, val) {
if (typeof val !== "function") { if (typeof val !== 'function') {
throw new Error(infuse.InjectorError.MAPPING_BAD_CLASS + prop); throw new Error(infuse.InjectorError.MAPPING_BAD_CLASS + prop);
} }
}; };
var validateBooleanSingleton = function(prop, singleton) { var validateBooleanSingleton = function(prop, singleton) {
if (typeof singleton !== "boolean") { if (typeof singleton !== 'boolean') {
throw new Error(infuse.InjectorError.MAPPING_BAD_SINGLETON + prop); throw new Error(infuse.InjectorError.MAPPING_BAD_SINGLETON + prop);
} }
}; };
var validateConstructorInjectionLoop = function(name, cl) { var validateConstructorInjectionLoop = function(name, cl) {
var params = infuse.getConstructorParams(cl); var params = infuse.getConstructorParams(cl);
if (params.contains(name)) { if (contains(params, name)) {
throw new Error(infuse.InjectorError.INJECT_INSTANCE_IN_ITSELF_CONSTRUCTOR); throw new Error(infuse.InjectorError.INJECT_INSTANCE_IN_ITSELF_CONSTRUCTOR);
} }
}; };
...@@ -94,33 +95,29 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -94,33 +95,29 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
} }
}; };
var instantiateIgnoringConstructor = function() {
if (typeof arguments[0] !== "function") {
throw new Error(infuse.InjectorError.CREATE_INSTANCE_INVALID_PARAM);
}
var TargetClass = arguments[0];
var args = [null];
for (var i=1; i<arguments.length; i++) {
args.push(arguments[i]);
}
return new (Function.prototype.bind.apply(TargetClass, args));
};
infuse.Injector = function() { infuse.Injector = function() {
this.mappings = {}; this.mappings = {};
this.parent = null; this.parent = null;
}; };
infuse.getConstructorParams = function(cl) { infuse.getConstructorParams = function(cl) {
var args = []; var args = [],
inject;
function extractName(all, underscore, name) {
args.push(name);
}
// Override arg name with inject array values if present
if( cl.hasOwnProperty('inject') && toString.call(cl.inject) === '[object Array]' && cl.inject.length > 0)
inject = cl.inject;
var clStr = cl.toString().replace(STRIP_COMMENTS, ''); var clStr = cl.toString().replace(STRIP_COMMENTS, '');
var argsFlat = clStr.match(FN_ARGS); var argsFlat = clStr.match(FN_ARGS);
var spl = argsFlat[1].split(FN_ARG_SPLIT); var spl = argsFlat[1].split(FN_ARG_SPLIT);
for (var i=0; i<spl.length; i++) { for (var i=0; i<spl.length; i++) {
var arg = spl[i]; // Only override arg with non-falsey inject value at same key
arg.replace(FN_ARG, function(all, underscore, name){ var arg = (inject && inject[i]) ? inject[i] : spl[i];
args.push(name); arg.replace(FN_ARG, extractName);
});
} }
return args; return args;
}; };
...@@ -134,9 +131,15 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -134,9 +131,15 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
}, },
getMappingVo: function(prop) { getMappingVo: function(prop) {
if (!this.mappings) return null; if (!this.mappings) {
if (this.mappings[prop]) return this.mappings[prop]; return null;
if (this.parent) return this.parent.getMappingVo(prop); }
if (this.mappings[prop]) {
return this.mappings[prop];
}
if (this.parent) {
return this.parent.getMappingVo(prop);
}
return null; return null;
}, },
...@@ -146,7 +149,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -146,7 +149,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
} }
validateProp(prop); validateProp(prop);
validateValue(prop, val); validateValue(prop, val);
this.mappings[prop] = new MappingVO(prop, val); this.mappings[prop] = new MappingVO(prop, val, undefined, undefined);
return this; return this;
}, },
...@@ -156,7 +159,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -156,7 +159,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
} }
validateProp(prop); validateProp(prop);
validateClass(prop, cl); validateClass(prop, cl);
if (singleton) validateBooleanSingleton(prop, singleton); if (singleton) {
validateBooleanSingleton(prop, singleton);
}
this.mappings[prop] = new MappingVO(prop, null, cl, singleton); this.mappings[prop] = new MappingVO(prop, null, cl, singleton);
return this; return this;
}, },
...@@ -177,22 +182,38 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -177,22 +182,38 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
getMapping: function(value) { getMapping: function(value) {
for (var name in this.mappings) { for (var name in this.mappings) {
if (this.mappings.hasOwnProperty(name)) {
var vo = this.mappings[name]; var vo = this.mappings[name];
if (vo.value === value || vo.cl === value) { if (vo.value === value || vo.cl === value) {
return vo.prop; return vo.prop;
} }
} }
}
return undefined;
}, },
getValue: function(prop) { getValue: function(prop) {
var vo = this.mappings[prop]; var vo = this.mappings[prop];
if (!vo) { if (!vo) {
if (this.parent) return this.parent.getValue.apply(this.parent, arguments); if (this.parent) {
else throw new Error(infuse.InjectorError.NO_MAPPING_FOUND); return this.parent.getValue.apply(this.parent, arguments);
}
else {
throw new Error(infuse.InjectorError.NO_MAPPING_FOUND);
}
} }
if (vo.cl) { if (vo.cl) {
arguments[0] = vo.cl; var args = Array.prototype.slice.call(arguments);
return this.getValueFromClass.apply(this, arguments); args[0] = vo.cl;
if (vo.singleton) {
if (!vo.value) {
vo.value = this.createInstance.apply(this, args);
}
return vo.value;
}
else {
return this.createInstance.apply(this, args);
}
} }
return vo.value; return vo.value;
}, },
...@@ -200,20 +221,25 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -200,20 +221,25 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
getClass: function(prop) { getClass: function(prop) {
var vo = this.mappings[prop]; var vo = this.mappings[prop];
if (!vo) { if (!vo) {
if (this.parent) return this.parent.getClass(prop); if (this.parent) {
else return undefined; return this.parent.getClass(prop);
}
else {
return undefined;
}
}
if (vo.cl) {
return vo.cl;
} }
if (vo.cl) return vo.cl;
return undefined; return undefined;
}, },
instantiate: function(TargetClass) { instantiate: function(TargetClass) {
if (typeof TargetClass !== "function") { if (typeof TargetClass !== 'function') {
throw new Error(infuse.InjectorError.CREATE_INSTANCE_INVALID_PARAM); throw new Error(infuse.InjectorError.CREATE_INSTANCE_INVALID_PARAM);
} }
var TargetClass = arguments[0];
var args = [null]; var args = [null];
var params = infuse.getConstructorParams(TargetClass, this.mappings); var params = infuse.getConstructorParams(TargetClass);
for (var i=0; i<params.length; i++) { for (var i=0; i<params.length; i++) {
if (arguments[i+1] !== undefined && arguments[i+1] !== null) { if (arguments[i+1] !== undefined && arguments[i+1] !== null) {
// argument found // argument found
...@@ -234,7 +260,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -234,7 +260,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
} }
} }
} }
return new (Function.prototype.bind.apply(TargetClass, args)); return new (Function.prototype.bind.apply(TargetClass, args))();
}, },
inject: function (target, isParent) { inject: function (target, isParent) {
...@@ -242,10 +268,11 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -242,10 +268,11 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
this.parent.inject(target, true); this.parent.inject(target, true);
} }
for (var name in this.mappings) { for (var name in this.mappings) {
if (this.mappings.hasOwnProperty(name)) {
var vo = this.getMappingVo(name); var vo = this.getMappingVo(name);
if (target.hasOwnProperty(vo.prop)) { if (target.hasOwnProperty(vo.prop) || (target.constructor && target.constructor.prototype && target.constructor.prototype.hasOwnProperty(vo.prop)) ) {
var val = this.getInjectedValue(vo, name); target[name] = this.getInjectedValue(vo, name);
target[name] = val; }
} }
} }
if (typeof target.postConstruct === 'function' && !isParent) { if (typeof target.postConstruct === 'function' && !isParent) {
...@@ -258,7 +285,6 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -258,7 +285,6 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
var val = vo.value; var val = vo.value;
var injectee; var injectee;
if (vo.cl) { if (vo.cl) {
var params = infuse.getConstructorParams(vo.cl);
if (vo.singleton) { if (vo.singleton) {
if (!vo.value) { if (!vo.value) {
validateConstructorInjectionLoop(name, vo.cl); validateConstructorInjectionLoop(name, vo.cl);
...@@ -288,10 +314,13 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -288,10 +314,13 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
getValueFromClass: function(cl) { getValueFromClass: function(cl) {
for (var name in this.mappings) { for (var name in this.mappings) {
if (this.mappings.hasOwnProperty(name)) {
var vo = this.mappings[name]; var vo = this.mappings[name];
if (vo.cl == cl) { if (vo.cl === cl) {
if (vo.singleton) { if (vo.singleton) {
if (!vo.value) vo.value = this.createInstance.apply(this, arguments); if (!vo.value) {
vo.value = this.createInstance.apply(this, arguments);
}
return vo.value; return vo.value;
} }
else { else {
...@@ -299,6 +328,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -299,6 +328,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
} }
} }
} }
}
if (this.parent) { if (this.parent) {
return this.parent.getValueFromClass.apply(this.parent, arguments); return this.parent.getValueFromClass.apply(this.parent, arguments);
} else { } else {
...@@ -315,15 +345,15 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -315,15 +345,15 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
if (!Function.prototype.bind) { if (!Function.prototype.bind) {
Function.prototype.bind = function bind(that) { Function.prototype.bind = function bind(that) {
var target = this; var target = this;
if (typeof target != "function") { if (typeof target !== 'function') {
throw new Error("Error, you must bind a function."); throw new Error('Error, you must bind a function.');
} }
var args = Array.prototype.slice.call(arguments, 1); // for normal call var args = Array.prototype.slice.call(arguments, 1); // for normal call
var bound = function () { var bound = function () {
if (this instanceof bound) { if (this instanceof bound) {
var F = function(){}; var F = function(){};
F.prototype = target.prototype; F.prototype = target.prototype;
var self = new F; var self = new F();
var result = target.apply( var result = target.apply(
self, self,
args.concat(Array.prototype.slice.call(arguments)) args.concat(Array.prototype.slice.call(arguments))
...@@ -344,55 +374,54 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -344,55 +374,54 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
} }
// register for AMD module // register for AMD module
if (typeof define === 'function' && define.amd) { if (typeof define === 'function' && typeof define.amd !== 'undefined') {
define("infuse", infuse); define("infuse", infuse);
} }
// export for node.js // export for node.js
if (typeof exports !== 'undefined') { if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) { module.exports = infuse;
exports = module.exports = infuse;
} }
if (typeof exports !== 'undefined') {
exports = infuse; exports = infuse;
} }
})(this['infuse'] = this['infuse'] || {}); })(this['infuse'] = this['infuse'] || {});
/* /*
Copyright (c) | 2012 | soma-events | Romuald Quantin | www.soundstep.com Copyright (c) | 2013 | soma-events | Romuald Quantin | www.soundstep.com
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following conditions: is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
(function (soma) {
;(function (soma, undefined) { 'use strict';
"use strict";
soma.events = {}; soma.events = {};
soma.events.version = "0.5.2"; soma.events.version = '0.5.6';
if (!Function.prototype.bind) { if (!Function.prototype.bind) {
Function.prototype.bind = function bind(that) { Function.prototype.bind = function bind(that) {
var target = this; var target = this;
if (typeof target != "function") { if (typeof target !== 'function') {
throw new Error("Error, you must bind a function."); throw new Error('Error, you must bind a function.');
} }
var args = Array.prototype.slice.call(arguments, 1); // for normal call var args = Array.prototype.slice.call(arguments, 1); // for normal call
var bound = function () { var bound = function () {
if (this instanceof bound) { if (this instanceof bound) {
var F = function(){}; var F = function(){};
F.prototype = target.prototype; F.prototype = target.prototype;
var self = new F; var self = new F();
var result = target.apply( var result = target.apply(
self, self,
args.concat(Array.prototype.slice.call(arguments)) args.concat(Array.prototype.slice.call(arguments))
...@@ -410,7 +439,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -410,7 +439,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
}; };
return bound; return bound;
}; };
}; }
soma.Event = function (type, params, bubbles, cancelable) { soma.Event = function (type, params, bubbles, cancelable) {
var e = soma.Event.createGenericEvent(type, bubbles, cancelable); var e = soma.Event.createGenericEvent(type, bubbles, cancelable);
...@@ -419,12 +448,14 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -419,12 +448,14 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
} }
e.isCloned = false; e.isCloned = false;
e.clone = this.clone.bind(e); e.clone = this.clone.bind(e);
e.isIE9 = this.isIE9; e.isIE9orIE10 = this.isIE9orIE10;
e.isDefaultPrevented = this.isDefaultPrevented; e.isDefaultPrevented = this.isDefaultPrevented;
if (this.isIE9() || !e.preventDefault || (e.getDefaultPrevented === undefined && e.defaultPrevented === undefined )) { if (this.isIE9orIE10() || !e.preventDefault || (e.getDefaultPrevented === undefined && e.defaultPrevented === undefined )) {
e.preventDefault = this.preventDefault.bind(e); e.preventDefault = this.preventDefault.bind(e);
} }
if (this.isIE9()) e.IE9PreventDefault = false; if (this.isIE9orIE10()) {
e.IE9or10PreventDefault = false;
}
return e; return e;
}; };
...@@ -434,22 +465,32 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -434,22 +465,32 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
e.isCloned = true; e.isCloned = true;
e.clone = this.clone; e.clone = this.clone;
e.isDefaultPrevented = this.isDefaultPrevented; e.isDefaultPrevented = this.isDefaultPrevented;
e.isIE9 = this.isIE9; e.isIE9orIE10 = this.isIE9orIE10;
if (this.isIE9()) e.IE9PreventDefault = this.IE9PreventDefault; if (this.isIE9orIE10()) {
e.IE9or10PreventDefault = this.IE9or10PreventDefault;
}
return e; return e;
}; };
soma.Event.prototype.preventDefault = function () { soma.Event.prototype.preventDefault = function () {
if (!this.cancelable) return false; if (!this.cancelable) {
return false;
}
if (this.isIE9orIE10()) {
this.IE9or10PreventDefault = true;
}
else {
this.defaultPrevented = true; this.defaultPrevented = true;
if (this.isIE9()) this.IE9PreventDefault = true; }
return this; return this;
}; };
soma.Event.prototype.isDefaultPrevented = function () { soma.Event.prototype.isDefaultPrevented = function () {
if (!this.cancelable) return false; if (!this.cancelable) {
if (this.isIE9()) { return false;
return this.IE9PreventDefault; }
if (this.isIE9orIE10()) {
return this.IE9or10PreventDefault;
} }
if (this.defaultPrevented !== undefined) { if (this.defaultPrevented !== undefined) {
return this.defaultPrevented; return this.defaultPrevented;
...@@ -462,10 +503,10 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -462,10 +503,10 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
soma.Event.createGenericEvent = function (type, bubbles, cancelable) { soma.Event.createGenericEvent = function (type, bubbles, cancelable) {
var event; var event;
bubbles = bubbles !== undefined ? bubbles : true; bubbles = bubbles !== undefined ? bubbles : true;
if (typeof document === "object" && document.createEvent) { if (typeof document === 'object' && document.createEvent) {
event = document.createEvent("Event"); event = document.createEvent('Event');
event.initEvent(type, !!bubbles, !!cancelable); event.initEvent(type, !!bubbles, !!cancelable);
} else if (typeof document === "object" && document.createEventObject) { } else if (typeof document === 'object' && document.createEventObject) {
event = document.createEventObject(); event = document.createEventObject();
event.type = type; event.type = type;
event.bubbles = !!bubbles; event.bubbles = !!bubbles;
...@@ -476,13 +517,15 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -476,13 +517,15 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
return event; return event;
}; };
soma.Event.prototype.isIE9 = function() { soma.Event.prototype.isIE9orIE10 = function() {
if (typeof document !== "object") return false; if (typeof document !== 'object') {
return document.body.style.scrollbar3dLightColor !== undefined && document.body.style.opacity !== undefined; return false;
}
return (document.body.style.scrollbar3dLightColor !== undefined && document.body.style.opacity !== undefined) || document.body.style.msTouchAction !== undefined;
}; };
soma.Event.prototype.toString = function() { soma.Event.prototype.toString = function() {
return "[soma.Event]"; return '[soma.Event]';
}; };
var EventObject = function(type, bubbles, cancelable) { var EventObject = function(type, bubbles, cancelable) {
...@@ -499,8 +542,12 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -499,8 +542,12 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
}; };
soma.EventDispatcher.prototype.addEventListener = function(type, listener, priority) { soma.EventDispatcher.prototype.addEventListener = function(type, listener, priority) {
if (!this.listeners || !type || !listener) return; if (!this.listeners || !type || !listener) {
if (isNaN(priority)) priority = 0; return;
}
if (isNaN(priority)) {
priority = 0;
}
for (var i=0; i<this.listeners.length; i++) { for (var i=0; i<this.listeners.length; i++) {
var eventObj = this.listeners[i]; var eventObj = this.listeners[i];
if (eventObj.type === type && eventObj.listener === listener) { if (eventObj.type === type && eventObj.listener === listener) {
...@@ -511,7 +558,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -511,7 +558,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
}; };
soma.EventDispatcher.prototype.removeEventListener = function(type, listener) { soma.EventDispatcher.prototype.removeEventListener = function(type, listener) {
if (!this.listeners || !type || !listener) return; if (!this.listeners || !type || !listener) {
return;
}
var i = this.listeners.length; var i = this.listeners.length;
while(i-- > 0) { while(i-- > 0) {
var eventObj = this.listeners[i]; var eventObj = this.listeners[i];
...@@ -522,7 +571,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -522,7 +571,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
}; };
soma.EventDispatcher.prototype.hasEventListener = function(type) { soma.EventDispatcher.prototype.hasEventListener = function(type) {
if (!this.listeners || !type) return false; if (!this.listeners || !type) {
return false;
}
var i = 0; var i = 0;
var l = this.listeners.length; var l = this.listeners.length;
for (; i < l; ++i) { for (; i < l; ++i) {
...@@ -535,7 +586,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -535,7 +586,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
}; };
soma.EventDispatcher.prototype.dispatchEvent = function(event) { soma.EventDispatcher.prototype.dispatchEvent = function(event) {
if (!this.listeners || !event) throw new Error("Error in EventDispatcher (dispatchEvent), one of the parameters is null or undefined."); if (!this.listeners || !event) {
throw new Error('Error in EventDispatcher (dispatchEvent), one of the parameters is null or undefined.');
}
var events = []; var events = [];
var i; var i;
for (i = 0; i < this.listeners.length; i++) { for (i = 0; i < this.listeners.length; i++) {
...@@ -554,7 +607,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -554,7 +607,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
}; };
soma.EventDispatcher.prototype.dispatch = function(type, params, bubbles, cancelable) { soma.EventDispatcher.prototype.dispatch = function(type, params, bubbles, cancelable) {
if (!this.listeners || !type || type === "") throw new Error("Error in EventDispatcher (dispatch), one of the parameters is null or undefined."); if (!this.listeners || !type || type === '') {
throw new Error('Error in EventDispatcher (dispatch), one of the parameters is null or undefined.');
}
var event = new soma.Event(type, params, bubbles, cancelable); var event = new soma.Event(type, params, bubbles, cancelable);
this.dispatchEvent(event); this.dispatchEvent(event);
return event; return event;
...@@ -565,30 +620,30 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -565,30 +620,30 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
}; };
soma.EventDispatcher.prototype.toString = function() { soma.EventDispatcher.prototype.toString = function() {
return "[soma.EventDispatcher]"; return '[soma.EventDispatcher]';
}; };
// register for AMD module // register for AMD module
if (typeof define === 'function' && define.amd) { if (typeof define === 'function' && typeof define.amd !== 'undefined') {
define("soma-events", soma); define("soma-events", soma);
}; }
// export for node.js // export for node.js
if (typeof exports !== 'undefined') { if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) { module.exports = soma;
exports = module.exports = soma;
} }
if (typeof exports !== 'undefined') {
exports = soma; exports = soma;
}; }
})(this['soma'] = this['soma'] || {}); })(this['soma'] = this['soma'] || {});
;(function (soma, infuse, undefined) { (function (soma, infuse) {
'use strict'; 'use strict';
soma.version = "2.0.0"; soma.version = '2.1.0';
soma.applyProperties = function(target, extension, bindToExtension, list) { soma.applyProperties = function(target, extension, bindToExtension, list) {
if (Object.prototype.toString.apply(list) === '[object Array]') { if (Object.prototype.toString.apply(list) === '[object Array]') {
...@@ -651,7 +706,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -651,7 +706,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Chain.prototype = parent.prototype; Chain.prototype = parent.prototype;
Subclass.prototype = new Chain(); Subclass.prototype = new Chain();
// add obj properties // add obj properties
if (obj) soma.applyProperties(Subclass.prototype, obj); if (obj) {
soma.applyProperties(Subclass.prototype, obj);
}
// point constructor to the Subclass // point constructor to the Subclass
Subclass.prototype.constructor = Subclass; Subclass.prototype.constructor = Subclass;
// set super class reference // set super class reference
...@@ -685,35 +742,38 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -685,35 +742,38 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// framework // framework
soma.Application = soma.extend({ soma.Application = soma.extend({
constructor: function() { constructor: function() {
setup.bind(this)();
this.init(); var self = this;
this.start();
function setup() { function setup() {
// injector // injector
this.injector = new infuse.Injector(this.dispatcher); self.injector = new infuse.Injector(self.dispatcher);
// dispatcher // dispatcher
this.dispatcher = new soma.EventDispatcher(); self.dispatcher = new soma.EventDispatcher();
// mapping // mapping
this.injector.mapValue('injector', this.injector); self.injector.mapValue('injector', self.injector);
this.injector.mapValue('instance', this); self.injector.mapValue('instance', self);
this.injector.mapValue('dispatcher', this.dispatcher); self.injector.mapValue('dispatcher', self.dispatcher);
// mediator // mediator
this.injector.mapClass('mediators', Mediators, true); self.injector.mapClass('mediators', Mediators, true);
this.mediators = this.injector.getValue('mediators'); self.mediators = self.injector.getValue('mediators');
// commands // commands
this.injector.mapClass('commands', Commands, true); self.injector.mapClass('commands', Commands, true);
this.commands = this.injector.getValue('commands'); self.commands = self.injector.getValue('commands');
// plugins // plugins
for (var i = 0, l = plugins.length; i < l; i++) { for (var i = 0, l = plugins.length; i < l; i++) {
this.createPlugin(plugins[i]); self.createPlugin(plugins[i]);
} }
} }
setup.bind(this)();
this.init();
this.start();
}, },
createPlugin: function() { createPlugin: function() {
if (arguments.length == 0 || !arguments[0]) { if (arguments.length === 0 || !arguments[0]) {
throw new Error("Error creating a plugin, plugin class is missing."); throw new Error('Error creating a plugin, plugin class is missing.');
} }
var params = infuse.getConstructorParams(arguments[0]); var params = infuse.getConstructorParams(arguments[0]);
var args = [arguments[0]]; var args = [arguments[0]];
...@@ -769,11 +829,11 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -769,11 +829,11 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
this.dispatcher = null; this.dispatcher = null;
}, },
create: function(cl, target) { create: function(cl, target) {
if (!cl || typeof cl !== "function") { if (!cl || typeof cl !== 'function') {
throw new Error("Error creating a mediator, the first parameter must be a function."); throw new Error('Error creating a mediator, the first parameter must be a function.');
} }
if (target === undefined || target === null) { if (target === undefined || target === null) {
throw new Error("Error creating a mediator, the second parameter cannot be undefined or null."); throw new Error('Error creating a mediator, the second parameter cannot be undefined or null.');
} }
var targets = []; var targets = [];
var meds = []; var meds = [];
...@@ -785,11 +845,11 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -785,11 +845,11 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
} }
for (var i= 0, l=targets.length; i<l; i++) { for (var i= 0, l=targets.length; i<l; i++) {
var injector = this.injector.createChild(); var injector = this.injector.createChild();
injector.mapValue("target", targets[i]); injector.mapValue('target', targets[i]);
//var mediator = injector.createInstance.apply(this.injector, params);
var mediator = injector.createInstance(cl); var mediator = injector.createInstance(cl);
// soma.applyProperties(mediator, this.dispatcher, true, ['dispatch', 'dispatchEvent', 'addEventListener', 'removeEventListener', 'hasEventListener']); if (targets.length === 1) {
if (targets.length === 1) return mediator; return mediator;
}
meds.push(mediator); meds.push(mediator);
} }
return meds; return meds;
...@@ -819,19 +879,21 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -819,19 +879,21 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
getAll: function() { getAll: function() {
var copy = {}; var copy = {};
for (var cmd in this.list) { for (var cmd in this.list) {
if (this.list.hasOwnProperty(cmd)) {
copy[cmd] = this.list[cmd]; copy[cmd] = this.list[cmd];
} }
}
return copy; return copy;
}, },
add: function(commandName, command) { add: function(commandName, command) {
if (typeof commandName !== 'string') { if (typeof commandName !== 'string') {
throw new Error("Error adding a command, the first parameter must be a string."); throw new Error('Error adding a command, the first parameter must be a string.');
} }
if (typeof command !== 'function') { if (typeof command !== 'function') {
throw new Error("Error adding a command with the name \"" + command + "\", the second parameter must be a function, and must contain an \"execute\" public method."); throw new Error('Error adding a command with the name "' + command + '", the second parameter must be a function, and must contain an "execute" public method.');
} }
if (this.has(commandName)) { if (this.has(commandName)) {
throw new Error("Error adding a command with the name: \"" + commandName + "\", already registered."); throw new Error('Error adding a command with the name: "' + commandName + '", already registered.');
} }
this.list[ commandName ] = command; this.list[ commandName ] = command;
this.addInterceptor(commandName); this.addInterceptor(commandName);
...@@ -860,16 +922,17 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -860,16 +922,17 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
if (this.has(commandName)) { if (this.has(commandName)) {
var command = this.injector.createInstance(this.list[commandName]); var command = this.injector.createInstance(this.list[commandName]);
if (!command.hasOwnProperty('execute') && command['execute'] === 'function') { if (!command.hasOwnProperty('execute') && command['execute'] === 'function') {
throw new Error("Error in " + this + " Command \"" + command + "\" must contain an execute public method."); throw new Error('Error in ' + this + ' Command ' + command + ' must contain an execute public method.');
} }
// soma.applyProperties(command, this.dispatcher, true, ['dispatch', 'dispatchEvent', 'addEventListener', 'removeEventListener', 'hasEventListener']);
command.execute(event); command.execute(event);
} }
}, },
dispose: function() { dispose: function() {
for (var cmd in this.list) { for (var cmd in this.list) {
if (this.list.hasOwnProperty(cmd)) {
this.remove(cmd); this.remove(cmd);
} }
}
this.boundHandler = undefined; this.boundHandler = undefined;
this.dispatcher = undefined; this.dispatcher = undefined;
this.injector = undefined; this.injector = undefined;
...@@ -893,16 +956,17 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -893,16 +956,17 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// register for AMD module // register for AMD module
if (typeof define === 'function' && define.amd) { /* globals define:false */
define("soma", soma); if (typeof define === 'function' && typeof define.amd !== 'undefined') {
define('soma', soma);
} }
// export for node.js // export for node.js
if (typeof exports !== 'undefined') { if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) { module.exports = soma;
exports = module.exports = soma;
} }
exports = soma; else {
window.soma = soma;
} }
})(this['soma'] = this['soma'] || {}, this['infuse']); })(this['soma'] = this['soma'] || {}, this['infuse']);
\ No newline at end of file
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
<label data-dblclick="edit(todo)">{{todo.title}}</label> <label data-dblclick="edit(todo)">{{todo.title}}</label>
<button class="destroy" data-click="remove(todo)"></button> <button class="destroy" data-click="remove(todo)"></button>
</div> </div>
<input class="edit" value="{{todo.title}}" data-keypress="update(todo)" data-blur="update(todo)"> <input class="edit" value="{{todo.title}}" data-keydown="update(todo)" data-blur="update(todo)">
</li> </li>
</ul> </ul>
</section> </section>
......
...@@ -6,12 +6,16 @@ ...@@ -6,12 +6,16 @@
todo.Router = function (dispatcher) { todo.Router = function (dispatcher) {
// create the router (director.js) // create the router (director.js)
var router = new Router().init(); var router = new Router().init().configure({
notfound: render
});
// dispatch a custom event to render the template on a route change // dispatch a custom event to render the template on a route change
router.on(/.*/, function () { router.on(/.*/, render);
function render() {
dispatcher.dispatch('render'); dispatcher.dispatch('render');
}); }
return { return {
getRoute: function () { getRoute: function () {
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
'use strict'; 'use strict';
var ENTER_KEY = 13; var ENTER_KEY = 13;
var ESCAPE_KEY = 27;
todo.MainView = function (scope, template, model, router, dispatcher) { todo.MainView = function (scope, template, model, router, dispatcher) {
...@@ -51,10 +52,14 @@ ...@@ -51,10 +52,14 @@
scope.edit = function (event, item) { scope.edit = function (event, item) {
item.editing = 'editing'; item.editing = 'editing';
template.render(); template.render();
template.element.querySelector('.edit').focus();
}; };
// template function: during edit mode, changes the value of an item after an enter key press // template function: during edit mode, changes the value of an item after an enter key press
scope.update = function (event, item) { scope.update = function (event, item) {
if (cancelEditing(event, item)) {
return;
}
var value = event.currentTarget.value.trim(); var value = event.currentTarget.value.trim();
if (event.which === ENTER_KEY || event.type === 'blur') { if (event.which === ENTER_KEY || event.type === 'blur') {
if (value) { if (value) {
...@@ -69,6 +74,19 @@ ...@@ -69,6 +74,19 @@
} }
}; };
// escape has been pressed, revert the value of the input
function cancelEditing(event, item) {
if (event.which === ESCAPE_KEY) {
event.currentTarget.value = item.title;
event.currentTarget.blur();
update();
return true;
}
else {
return false;
}
}
// save the changes to the model and dispatch a custom event to render the templates // save the changes to the model and dispatch a custom event to render the templates
function update() { function update() {
model.set(items); model.set(items);
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
"todomvc-common": "~0.1.6", "todomvc-common": "~0.1.6",
"director": "~1.2.0", "director": "~1.2.0",
"requirejs": "~2.1.5", "requirejs": "~2.1.5",
"soma.js": "~2.0.0", "soma.js": "~2.1.0",
"soma-template": "~0.1.8" "soma-template": "~0.2.8"
} }
} }
// //
// Generated on Sun Dec 16 2012 22:47:05 GMT-0500 (EST) by Nodejitsu, Inc (Using Codesurgeon). // Generated on Fri Dec 27 2013 12:02:11 GMT-0500 (EST) by Nodejitsu, Inc (Using Codesurgeon).
// Version 1.1.9 // Version 1.2.2
// //
(function (exports) { (function (exports) {
/* /*
* browser.js: Browser specific functionality for director. * browser.js: Browser specific functionality for director.
* *
...@@ -201,7 +200,7 @@ Router.prototype.init = function (r) { ...@@ -201,7 +200,7 @@ Router.prototype.init = function (r) {
this.handler = function(onChangeEvent) { this.handler = function(onChangeEvent) {
var newURL = onChangeEvent && onChangeEvent.newURL || window.location.hash; var newURL = onChangeEvent && onChangeEvent.newURL || window.location.hash;
var url = self.history === true ? self.getPath() : newURL.replace(/.*#/, ''); var url = self.history === true ? self.getPath() : newURL.replace(/.*#/, '');
self.dispatch('on', url); self.dispatch('on', url.charAt(0) === '/' ? url : '/' + url);
}; };
listener.init(this.handler, this.history); listener.init(this.handler, this.history);
...@@ -210,7 +209,7 @@ Router.prototype.init = function (r) { ...@@ -210,7 +209,7 @@ Router.prototype.init = function (r) {
if (dlocHashEmpty() && r) { if (dlocHashEmpty() && r) {
dloc.hash = r; dloc.hash = r;
} else if (!dlocHashEmpty()) { } else if (!dlocHashEmpty()) {
self.dispatch('on', dloc.hash.replace(/^#/, '')); self.dispatch('on', '/' + dloc.hash.replace(/^(#\/|#|\/)/, ''));
} }
} }
else { else {
...@@ -363,11 +362,16 @@ function regifyString(str, params) { ...@@ -363,11 +362,16 @@ function regifyString(str, params) {
out += str.substr(0, matches.index) + matches[0]; out += str.substr(0, matches.index) + matches[0];
} }
str = out += str.substr(last); str = out += str.substr(last);
var captures = str.match(/:([^\/]+)/ig), length; var captures = str.match(/:([^\/]+)/ig), capture, length;
if (captures) { if (captures) {
length = captures.length; length = captures.length;
for (var i = 0; i < length; i++) { for (var i = 0; i < length; i++) {
str = str.replace(captures[i], paramifyString(captures[i], params)); capture = captures[i];
if (capture.slice(0, 2) === "::") {
str = capture.slice(1);
} else {
str = str.replace(capture, paramifyString(capture, params));
}
} }
} }
return str; return str;
...@@ -485,20 +489,22 @@ Router.prototype.dispatch = function(method, path, callback) { ...@@ -485,20 +489,22 @@ Router.prototype.dispatch = function(method, path, callback) {
Router.prototype.invoke = function(fns, thisArg, callback) { Router.prototype.invoke = function(fns, thisArg, callback) {
var self = this; var self = this;
var apply;
if (this.async) { if (this.async) {
_asyncEverySeries(fns, function apply(fn, next) { apply = function(fn, next) {
if (Array.isArray(fn)) { if (Array.isArray(fn)) {
return _asyncEverySeries(fn, apply, next); return _asyncEverySeries(fn, apply, next);
} else if (typeof fn == "function") { } else if (typeof fn == "function") {
fn.apply(thisArg, fns.captures.concat(next)); fn.apply(thisArg, fns.captures.concat(next));
} }
}, function() { };
_asyncEverySeries(fns, apply, function() {
if (callback) { if (callback) {
callback.apply(thisArg, arguments); callback.apply(thisArg, arguments);
} }
}); });
} else { } else {
_every(fns, function apply(fn) { apply = function(fn) {
if (Array.isArray(fn)) { if (Array.isArray(fn)) {
return _every(fn, apply); return _every(fn, apply);
} else if (typeof fn === "function") { } else if (typeof fn === "function") {
...@@ -506,7 +512,8 @@ Router.prototype.invoke = function(fns, thisArg, callback) { ...@@ -506,7 +512,8 @@ Router.prototype.invoke = function(fns, thisArg, callback) {
} else if (typeof fn === "string" && self.resource) { } else if (typeof fn === "string" && self.resource) {
self.resource[fn].apply(thisArg, fns.captures || []); self.resource[fn].apply(thisArg, fns.captures || []);
} }
}); };
_every(fns, apply);
} }
}; };
...@@ -686,7 +693,7 @@ Router.prototype.mount = function(routes, path) { ...@@ -686,7 +693,7 @@ Router.prototype.mount = function(routes, path) {
function insertOrMount(route, local) { function insertOrMount(route, local) {
var rename = route, parts = route.split(self.delimiter), routeType = typeof routes[route], isRoute = parts[0] === "" || !self._methods[parts[0]], event = isRoute ? "on" : rename; var rename = route, parts = route.split(self.delimiter), routeType = typeof routes[route], isRoute = parts[0] === "" || !self._methods[parts[0]], event = isRoute ? "on" : rename;
if (isRoute) { if (isRoute) {
rename = rename.slice((rename.match(new RegExp(self.delimiter)) || [ "" ])[0].length); rename = rename.slice((rename.match(new RegExp("^" + self.delimiter)) || [ "" ])[0].length);
parts.shift(); parts.shift();
} }
if (isRoute && routeType === "object" && !Array.isArray(routes[route])) { if (isRoute && routeType === "object" && !Array.isArray(routes[route])) {
......
/** vim: et:ts=4:sw=4:sts=4 /** vim: et:ts=4:sw=4:sts=4
* @license RequireJS 2.1.5 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved. * @license RequireJS 2.1.11 Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved.
* Available via the MIT or new BSD license. * Available via the MIT or new BSD license.
* see: http://github.com/jrburke/requirejs for details * see: http://github.com/jrburke/requirejs for details
*/ */
...@@ -12,7 +12,7 @@ var requirejs, require, define; ...@@ -12,7 +12,7 @@ var requirejs, require, define;
(function (global) { (function (global) {
var req, s, head, baseElement, dataMain, src, var req, s, head, baseElement, dataMain, src,
interactiveScript, currentlyAddingScript, mainScript, subPath, interactiveScript, currentlyAddingScript, mainScript, subPath,
version = '2.1.5', version = '2.1.11',
commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg, commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg,
cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g, cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,
jsSuffixRegExp = /\.js$/, jsSuffixRegExp = /\.js$/,
...@@ -22,7 +22,7 @@ var requirejs, require, define; ...@@ -22,7 +22,7 @@ var requirejs, require, define;
hasOwn = op.hasOwnProperty, hasOwn = op.hasOwnProperty,
ap = Array.prototype, ap = Array.prototype,
apsp = ap.splice, apsp = ap.splice,
isBrowser = !!(typeof window !== 'undefined' && navigator && document), isBrowser = !!(typeof window !== 'undefined' && typeof navigator !== 'undefined' && window.document),
isWebWorker = !isBrowser && typeof importScripts !== 'undefined', isWebWorker = !isBrowser && typeof importScripts !== 'undefined',
//PS3 indicates loaded and complete, but need to wait for complete //PS3 indicates loaded and complete, but need to wait for complete
//specifically. Sequence is 'loading', 'loaded', execution, //specifically. Sequence is 'loading', 'loaded', execution,
...@@ -108,7 +108,10 @@ var requirejs, require, define; ...@@ -108,7 +108,10 @@ var requirejs, require, define;
if (source) { if (source) {
eachProp(source, function (value, prop) { eachProp(source, function (value, prop) {
if (force || !hasProp(target, prop)) { if (force || !hasProp(target, prop)) {
if (deepStringMixin && typeof value !== 'string') { if (deepStringMixin && typeof value === 'object' && value &&
!isArray(value) && !isFunction(value) &&
!(value instanceof RegExp)) {
if (!target[prop]) { if (!target[prop]) {
target[prop] = {}; target[prop] = {};
} }
...@@ -134,7 +137,11 @@ var requirejs, require, define; ...@@ -134,7 +137,11 @@ var requirejs, require, define;
return document.getElementsByTagName('script'); return document.getElementsByTagName('script');
} }
//Allow getting a global that expressed in function defaultOnError(err) {
throw err;
}
//Allow getting a global that is expressed in
//dot notation, like 'a.b.c'. //dot notation, like 'a.b.c'.
function getGlobal(value) { function getGlobal(value) {
if (!value) { if (!value) {
...@@ -197,6 +204,7 @@ var requirejs, require, define; ...@@ -197,6 +204,7 @@ var requirejs, require, define;
waitSeconds: 7, waitSeconds: 7,
baseUrl: './', baseUrl: './',
paths: {}, paths: {},
bundles: {},
pkgs: {}, pkgs: {},
shim: {}, shim: {},
config: {} config: {}
...@@ -210,6 +218,7 @@ var requirejs, require, define; ...@@ -210,6 +218,7 @@ var requirejs, require, define;
defQueue = [], defQueue = [],
defined = {}, defined = {},
urlFetched = {}, urlFetched = {},
bundlesMap = {},
requireCounter = 1, requireCounter = 1,
unnormalizedCounter = 1; unnormalizedCounter = 1;
...@@ -223,8 +232,8 @@ var requirejs, require, define; ...@@ -223,8 +232,8 @@ var requirejs, require, define;
* @param {Array} ary the array of path segments. * @param {Array} ary the array of path segments.
*/ */
function trimDots(ary) { function trimDots(ary) {
var i, part; var i, part, length = ary.length;
for (i = 0; ary[i]; i += 1) { for (i = 0; i < length; i++) {
part = ary[i]; part = ary[i];
if (part === '.') { if (part === '.') {
ary.splice(i, 1); ary.splice(i, 1);
...@@ -257,7 +266,7 @@ var requirejs, require, define; ...@@ -257,7 +266,7 @@ var requirejs, require, define;
* @returns {String} normalized name * @returns {String} normalized name
*/ */
function normalize(name, baseName, applyMap) { function normalize(name, baseName, applyMap) {
var pkgName, pkgConfig, mapValue, nameParts, i, j, nameSegment, var pkgMain, mapValue, nameParts, i, j, nameSegment, lastIndex,
foundMap, foundI, foundStarMap, starI, foundMap, foundI, foundStarMap, starI,
baseParts = baseName && baseName.split('/'), baseParts = baseName && baseName.split('/'),
normalizedBaseParts = baseParts, normalizedBaseParts = baseParts,
...@@ -270,29 +279,26 @@ var requirejs, require, define; ...@@ -270,29 +279,26 @@ var requirejs, require, define;
//otherwise, assume it is a top-level require that will //otherwise, assume it is a top-level require that will
//be relative to baseUrl in the end. //be relative to baseUrl in the end.
if (baseName) { if (baseName) {
if (getOwn(config.pkgs, baseName)) {
//If the baseName is a package name, then just treat it as one
//name to concat the name with.
normalizedBaseParts = baseParts = [baseName];
} else {
//Convert baseName to array, and lop off the last part, //Convert baseName to array, and lop off the last part,
//so that . matches that 'directory' and not name of the baseName's //so that . matches that 'directory' and not name of the baseName's
//module. For instance, baseName of 'one/two/three', maps to //module. For instance, baseName of 'one/two/three', maps to
//'one/two/three.js', but we want the directory, 'one/two' for //'one/two/three.js', but we want the directory, 'one/two' for
//this normalization. //this normalization.
normalizedBaseParts = baseParts.slice(0, baseParts.length - 1); normalizedBaseParts = baseParts.slice(0, baseParts.length - 1);
name = name.split('/');
lastIndex = name.length - 1;
// If wanting node ID compatibility, strip .js from end
// of IDs. Have to do this here, and not in nameToUrl
// because node allows either .js or non .js to map
// to same file.
if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) {
name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, '');
} }
name = normalizedBaseParts.concat(name.split('/')); name = normalizedBaseParts.concat(name);
trimDots(name); trimDots(name);
//Some use of packages may use a . path to reference the
//'main' module name, so normalize for that.
pkgConfig = getOwn(config.pkgs, (pkgName = name[0]));
name = name.join('/'); name = name.join('/');
if (pkgConfig && name === pkgName + '/' + pkgConfig.main) {
name = pkgName;
}
} else if (name.indexOf('./') === 0) { } else if (name.indexOf('./') === 0) {
// No baseName, so this is ID is resolved relative // No baseName, so this is ID is resolved relative
// to baseUrl, pull off the leading dot. // to baseUrl, pull off the leading dot.
...@@ -304,7 +310,7 @@ var requirejs, require, define; ...@@ -304,7 +310,7 @@ var requirejs, require, define;
if (applyMap && map && (baseParts || starMap)) { if (applyMap && map && (baseParts || starMap)) {
nameParts = name.split('/'); nameParts = name.split('/');
for (i = nameParts.length; i > 0; i -= 1) { outerLoop: for (i = nameParts.length; i > 0; i -= 1) {
nameSegment = nameParts.slice(0, i).join('/'); nameSegment = nameParts.slice(0, i).join('/');
if (baseParts) { if (baseParts) {
...@@ -321,16 +327,12 @@ var requirejs, require, define; ...@@ -321,16 +327,12 @@ var requirejs, require, define;
//Match, update name to the new value. //Match, update name to the new value.
foundMap = mapValue; foundMap = mapValue;
foundI = i; foundI = i;
break; break outerLoop;
} }
} }
} }
} }
if (foundMap) {
break;
}
//Check for a star map match, but just hold on to it, //Check for a star map match, but just hold on to it,
//if there is a shorter segment match later in a matching //if there is a shorter segment match later in a matching
//config, then favor over this star map. //config, then favor over this star map.
...@@ -351,7 +353,11 @@ var requirejs, require, define; ...@@ -351,7 +353,11 @@ var requirejs, require, define;
} }
} }
return name; // If the name points to a package's name, use
// the package main instead.
pkgMain = getOwn(config.pkgs, name);
return pkgMain ? pkgMain : name;
} }
function removeScript(name) { function removeScript(name) {
...@@ -369,7 +375,6 @@ var requirejs, require, define; ...@@ -369,7 +375,6 @@ var requirejs, require, define;
function hasPathFallback(id) { function hasPathFallback(id) {
var pathConfig = getOwn(config.paths, id); var pathConfig = getOwn(config.paths, id);
if (pathConfig && isArray(pathConfig) && pathConfig.length > 1) { if (pathConfig && isArray(pathConfig) && pathConfig.length > 1) {
removeScript(id);
//Pop off the first array value, since it failed, and //Pop off the first array value, since it failed, and
//retry //retry
pathConfig.shift(); pathConfig.shift();
...@@ -500,7 +505,12 @@ var requirejs, require, define; ...@@ -500,7 +505,12 @@ var requirejs, require, define;
fn(defined[id]); fn(defined[id]);
} }
} else { } else {
getModule(depMap).on(name, fn); mod = getModule(depMap);
if (mod.error && name === 'error') {
fn(mod.error);
} else {
mod.on(name, fn);
}
} }
} }
...@@ -540,7 +550,7 @@ var requirejs, require, define; ...@@ -540,7 +550,7 @@ var requirejs, require, define;
//local var ref to defQueue, so cannot just reassign the one //local var ref to defQueue, so cannot just reassign the one
//on context. //on context.
apsp.apply(defQueue, apsp.apply(defQueue,
[defQueue.length - 1, 0].concat(globalDefQueue)); [defQueue.length, 0].concat(globalDefQueue));
globalDefQueue = []; globalDefQueue = [];
} }
} }
...@@ -557,7 +567,7 @@ var requirejs, require, define; ...@@ -557,7 +567,7 @@ var requirejs, require, define;
mod.usingExports = true; mod.usingExports = true;
if (mod.map.isDefine) { if (mod.map.isDefine) {
if (mod.exports) { if (mod.exports) {
return mod.exports; return (defined[mod.map.id] = mod.exports);
} else { } else {
return (mod.exports = defined[mod.map.id] = {}); return (mod.exports = defined[mod.map.id] = {});
} }
...@@ -571,9 +581,9 @@ var requirejs, require, define; ...@@ -571,9 +581,9 @@ var requirejs, require, define;
id: mod.map.id, id: mod.map.id,
uri: mod.map.url, uri: mod.map.url,
config: function () { config: function () {
return (config.config && getOwn(config.config, mod.map.id)) || {}; return getOwn(config.config, mod.map.id) || {};
}, },
exports: defined[mod.map.id] exports: mod.exports || (mod.exports = {})
}); });
} }
} }
...@@ -614,7 +624,7 @@ var requirejs, require, define; ...@@ -614,7 +624,7 @@ var requirejs, require, define;
} }
function checkLoaded() { function checkLoaded() {
var map, modId, err, usingPathFallback, var err, usingPathFallback,
waitInterval = config.waitSeconds * 1000, waitInterval = config.waitSeconds * 1000,
//It is possible to disable the wait interval by using waitSeconds of 0. //It is possible to disable the wait interval by using waitSeconds of 0.
expired = waitInterval && (context.startTime + waitInterval) < new Date().getTime(), expired = waitInterval && (context.startTime + waitInterval) < new Date().getTime(),
...@@ -632,7 +642,7 @@ var requirejs, require, define; ...@@ -632,7 +642,7 @@ var requirejs, require, define;
//Figure out the state of all the modules. //Figure out the state of all the modules.
eachProp(enabledRegistry, function (mod) { eachProp(enabledRegistry, function (mod) {
map = mod.map; var map = mod.map,
modId = map.id; modId = map.id;
//Skip things that are not enabled or in error state. //Skip things that are not enabled or in error state.
...@@ -840,8 +850,13 @@ var requirejs, require, define; ...@@ -840,8 +850,13 @@ var requirejs, require, define;
if (this.depCount < 1 && !this.defined) { if (this.depCount < 1 && !this.defined) {
if (isFunction(factory)) { if (isFunction(factory)) {
//If there is an error listener, favor passing //If there is an error listener, favor passing
//to that instead of throwing an error. //to that instead of throwing an error. However,
if (this.events.error) { //only do it for define()'d modules. require
//errbacks should not be called for failures in
//their callbacks (#699). However if a global
//onError is set, use that.
if ((this.events.error && this.map.isDefine) ||
req.onError !== defaultOnError) {
try { try {
exports = context.execCb(id, factory, depExports, exports); exports = context.execCb(id, factory, depExports, exports);
} catch (e) { } catch (e) {
...@@ -851,17 +866,14 @@ var requirejs, require, define; ...@@ -851,17 +866,14 @@ var requirejs, require, define;
exports = context.execCb(id, factory, depExports, exports); exports = context.execCb(id, factory, depExports, exports);
} }
if (this.map.isDefine) { // Favor return value over exports. If node/cjs in play,
//If setting exports via 'module' is in play, // then will not have a return value anyway. Favor
//favor that over return value and exports. After that, // module.exports assignment over exports object.
//favor a non-undefined return value over exports use. if (this.map.isDefine && exports === undefined) {
cjsModule = this.module; cjsModule = this.module;
if (cjsModule && if (cjsModule) {
cjsModule.exports !== undefined &&
//Make sure it is not already the exports value
cjsModule.exports !== this.exports) {
exports = cjsModule.exports; exports = cjsModule.exports;
} else if (exports === undefined && this.usingExports) { } else if (this.usingExports) {
//exports already set the defined value. //exports already set the defined value.
exports = this.exports; exports = this.exports;
} }
...@@ -869,8 +881,8 @@ var requirejs, require, define; ...@@ -869,8 +881,8 @@ var requirejs, require, define;
if (err) { if (err) {
err.requireMap = this.map; err.requireMap = this.map;
err.requireModules = [this.map.id]; err.requireModules = this.map.isDefine ? [this.map.id] : null;
err.requireType = 'define'; err.requireType = this.map.isDefine ? 'define' : 'require';
return onError((this.error = err)); return onError((this.error = err));
} }
...@@ -921,6 +933,7 @@ var requirejs, require, define; ...@@ -921,6 +933,7 @@ var requirejs, require, define;
on(pluginMap, 'defined', bind(this, function (plugin) { on(pluginMap, 'defined', bind(this, function (plugin) {
var load, normalizedMap, normalizedMod, var load, normalizedMap, normalizedMod,
bundleId = getOwn(bundlesMap, this.map.id),
name = this.map.name, name = this.map.name,
parentName = this.map.parentMap ? this.map.parentMap.name : null, parentName = this.map.parentMap ? this.map.parentMap.name : null,
localRequire = context.makeRequire(map.parentMap, { localRequire = context.makeRequire(map.parentMap, {
...@@ -966,6 +979,14 @@ var requirejs, require, define; ...@@ -966,6 +979,14 @@ var requirejs, require, define;
return; return;
} }
//If a paths config, then just load that file instead to
//resolve the plugin, as it is built into that paths layer.
if (bundleId) {
this.map.url = context.nameToUrl(bundleId);
this.load();
return;
}
load = bind(this, function (value) { load = bind(this, function (value) {
this.init([], function () { return value; }, null, { this.init([], function () { return value; }, null, {
enabled: true enabled: true
...@@ -1093,7 +1114,7 @@ var requirejs, require, define; ...@@ -1093,7 +1114,7 @@ var requirejs, require, define;
})); }));
if (this.errback) { if (this.errback) {
on(depMap, 'error', this.errback); on(depMap, 'error', bind(this, this.errback));
} }
} }
...@@ -1230,31 +1251,38 @@ var requirejs, require, define; ...@@ -1230,31 +1251,38 @@ var requirejs, require, define;
} }
} }
//Save off the paths and packages since they require special processing, //Save off the paths since they require special processing,
//they are additive. //they are additive.
var pkgs = config.pkgs, var shim = config.shim,
shim = config.shim,
objs = { objs = {
paths: true, paths: true,
bundles: true,
config: true, config: true,
map: true map: true
}; };
eachProp(cfg, function (value, prop) { eachProp(cfg, function (value, prop) {
if (objs[prop]) { if (objs[prop]) {
if (prop === 'map') { if (!config[prop]) {
if (!config.map) { config[prop] = {};
config.map = {};
} }
mixin(config[prop], value, true, true); mixin(config[prop], value, true, true);
} else {
mixin(config[prop], value, true);
}
} else { } else {
config[prop] = value; config[prop] = value;
} }
}); });
//Reverse map the bundles
if (cfg.bundles) {
eachProp(cfg.bundles, function (value, prop) {
each(value, function (v) {
if (v !== prop) {
bundlesMap[v] = prop;
}
});
});
}
//Merge shim //Merge shim
if (cfg.shim) { if (cfg.shim) {
eachProp(cfg.shim, function (value, id) { eachProp(cfg.shim, function (value, id) {
...@@ -1275,29 +1303,25 @@ var requirejs, require, define; ...@@ -1275,29 +1303,25 @@ var requirejs, require, define;
//Adjust packages if necessary. //Adjust packages if necessary.
if (cfg.packages) { if (cfg.packages) {
each(cfg.packages, function (pkgObj) { each(cfg.packages, function (pkgObj) {
var location; var location, name;
pkgObj = typeof pkgObj === 'string' ? { name: pkgObj } : pkgObj; pkgObj = typeof pkgObj === 'string' ? { name: pkgObj } : pkgObj;
name = pkgObj.name;
location = pkgObj.location; location = pkgObj.location;
if (location) {
config.paths[name] = pkgObj.location;
}
//Create a brand new object on pkgs, since currentPackages can //Save pointer to main module ID for pkg name.
//be passed in again, and config.pkgs is the internal transformed
//state for all package configs.
pkgs[pkgObj.name] = {
name: pkgObj.name,
location: location || pkgObj.name,
//Remove leading dot in main, so main paths are normalized, //Remove leading dot in main, so main paths are normalized,
//and remove any trailing .js, since different package //and remove any trailing .js, since different package
//envs have different conventions: some use a module name, //envs have different conventions: some use a module name,
//some use a file name. //some use a file name.
main: (pkgObj.main || 'main') config.pkgs[name] = pkgObj.name + '/' + (pkgObj.main || 'main')
.replace(currDirRegExp, '') .replace(currDirRegExp, '')
.replace(jsSuffixRegExp, '') .replace(jsSuffixRegExp, '');
};
}); });
//Done with modifications, assing packages back to context config
config.pkgs = pkgs;
} }
//If there are any "waiting to execute" modules in the registry, //If there are any "waiting to execute" modules in the registry,
...@@ -1444,10 +1468,21 @@ var requirejs, require, define; ...@@ -1444,10 +1468,21 @@ var requirejs, require, define;
var map = makeModuleMap(id, relMap, true), var map = makeModuleMap(id, relMap, true),
mod = getOwn(registry, id); mod = getOwn(registry, id);
removeScript(id);
delete defined[id]; delete defined[id];
delete urlFetched[map.url]; delete urlFetched[map.url];
delete undefEvents[id]; delete undefEvents[id];
//Clean queued defines too. Go backwards
//in array so that the splices do not
//mess up the iteration.
eachReverse(defQueue, function(args, i) {
if(args[0] === id) {
defQueue.splice(i, 1);
}
});
if (mod) { if (mod) {
//Hold on to listeners in case the //Hold on to listeners in case the
//module will be attempted to be reloaded //module will be attempted to be reloaded
...@@ -1467,7 +1502,7 @@ var requirejs, require, define; ...@@ -1467,7 +1502,7 @@ var requirejs, require, define;
/** /**
* Called to enable a module if it is still in the registry * Called to enable a module if it is still in the registry
* awaiting enablement. A second arg, parent, the parent module, * awaiting enablement. A second arg, parent, the parent module,
* is passed in for context, when this method is overriden by * is passed in for context, when this method is overridden by
* the optimizer. Not shown here to keep code compact. * the optimizer. Not shown here to keep code compact.
*/ */
enable: function (depMap) { enable: function (depMap) {
...@@ -1541,8 +1576,19 @@ var requirejs, require, define; ...@@ -1541,8 +1576,19 @@ var requirejs, require, define;
* internal API, not a public one. Use toUrl for the public API. * internal API, not a public one. Use toUrl for the public API.
*/ */
nameToUrl: function (moduleName, ext, skipExt) { nameToUrl: function (moduleName, ext, skipExt) {
var paths, pkgs, pkg, pkgPath, syms, i, parentModule, url, var paths, syms, i, parentModule, url,
parentPath; parentPath, bundleId,
pkgMain = getOwn(config.pkgs, moduleName);
if (pkgMain) {
moduleName = pkgMain;
}
bundleId = getOwn(bundlesMap, moduleName);
if (bundleId) {
return context.nameToUrl(bundleId, ext, skipExt);
}
//If a colon is in the URL, it indicates a protocol is used and it is just //If a colon is in the URL, it indicates a protocol is used and it is just
//an URL to a file, or if it starts with a slash, contains a query arg (i.e. ?) //an URL to a file, or if it starts with a slash, contains a query arg (i.e. ?)
...@@ -1556,7 +1602,6 @@ var requirejs, require, define; ...@@ -1556,7 +1602,6 @@ var requirejs, require, define;
} else { } else {
//A module that needs to be converted to a path. //A module that needs to be converted to a path.
paths = config.paths; paths = config.paths;
pkgs = config.pkgs;
syms = moduleName.split('/'); syms = moduleName.split('/');
//For each module name segment, see if there is a path //For each module name segment, see if there is a path
...@@ -1564,7 +1609,7 @@ var requirejs, require, define; ...@@ -1564,7 +1609,7 @@ var requirejs, require, define;
//and work up from it. //and work up from it.
for (i = syms.length; i > 0; i -= 1) { for (i = syms.length; i > 0; i -= 1) {
parentModule = syms.slice(0, i).join('/'); parentModule = syms.slice(0, i).join('/');
pkg = getOwn(pkgs, parentModule);
parentPath = getOwn(paths, parentModule); parentPath = getOwn(paths, parentModule);
if (parentPath) { if (parentPath) {
//If an array, it means there are a few choices, //If an array, it means there are a few choices,
...@@ -1574,22 +1619,12 @@ var requirejs, require, define; ...@@ -1574,22 +1619,12 @@ var requirejs, require, define;
} }
syms.splice(0, i, parentPath); syms.splice(0, i, parentPath);
break; break;
} else if (pkg) {
//If module name is just the package name, then looking
//for the main module.
if (moduleName === pkg.name) {
pkgPath = pkg.location + '/' + pkg.main;
} else {
pkgPath = pkg.location;
}
syms.splice(0, i, pkgPath);
break;
} }
} }
//Join the path parts together, then figure out if baseUrl is needed. //Join the path parts together, then figure out if baseUrl is needed.
url = syms.join('/'); url = syms.join('/');
url += (ext || (/\?/.test(url) || skipExt ? '' : '.js')); url += (ext || (/^data\:|\?/.test(url) || skipExt ? '' : '.js'));
url = (url.charAt(0) === '/' || url.match(/^[\w\+\.\-]+:/) ? '' : config.baseUrl) + url; url = (url.charAt(0) === '/' || url.match(/^[\w\+\.\-]+:/) ? '' : config.baseUrl) + url;
} }
...@@ -1605,7 +1640,7 @@ var requirejs, require, define; ...@@ -1605,7 +1640,7 @@ var requirejs, require, define;
}, },
/** /**
* Executes a module callack function. Broken out as a separate function * Executes a module callback function. Broken out as a separate function
* solely to allow the build system to sequence the files in the built * solely to allow the build system to sequence the files in the built
* layer in the right sequence. * layer in the right sequence.
* *
...@@ -1643,7 +1678,7 @@ var requirejs, require, define; ...@@ -1643,7 +1678,7 @@ var requirejs, require, define;
onScriptError: function (evt) { onScriptError: function (evt) {
var data = getScriptData(evt); var data = getScriptData(evt);
if (!hasPathFallback(data.id)) { if (!hasPathFallback(data.id)) {
return onError(makeError('scripterror', 'Script error', evt, [data.id])); return onError(makeError('scripterror', 'Script error for: ' + data.id, evt, [data.id]));
} }
} }
}; };
...@@ -1772,8 +1807,19 @@ var requirejs, require, define; ...@@ -1772,8 +1807,19 @@ var requirejs, require, define;
* function. Intercept/override it if you want custom error handling. * function. Intercept/override it if you want custom error handling.
* @param {Error} err the error object. * @param {Error} err the error object.
*/ */
req.onError = function (err) { req.onError = defaultOnError;
throw err;
/**
* Creates the node for the load command. Only used in browser envs.
*/
req.createNode = function (config, moduleName, url) {
var node = config.xhtml ?
document.createElementNS('http://www.w3.org/1999/xhtml', 'html:script') :
document.createElement('script');
node.type = config.scriptType || 'text/javascript';
node.charset = 'utf-8';
node.async = true;
return node;
}; };
/** /**
...@@ -1790,12 +1836,7 @@ var requirejs, require, define; ...@@ -1790,12 +1836,7 @@ var requirejs, require, define;
node; node;
if (isBrowser) { if (isBrowser) {
//In the browser so use a script tag //In the browser so use a script tag
node = config.xhtml ? node = req.createNode(config, moduleName, url);
document.createElementNS('http://www.w3.org/1999/xhtml', 'html:script') :
document.createElement('script');
node.type = config.scriptType || 'text/javascript';
node.charset = 'utf-8';
node.async = true;
node.setAttribute('data-requirecontext', context.contextName); node.setAttribute('data-requirecontext', context.contextName);
node.setAttribute('data-requiremodule', moduleName); node.setAttribute('data-requiremodule', moduleName);
...@@ -1892,7 +1933,7 @@ var requirejs, require, define; ...@@ -1892,7 +1933,7 @@ var requirejs, require, define;
} }
//Look for a data-main script attribute, which could also adjust the baseUrl. //Look for a data-main script attribute, which could also adjust the baseUrl.
if (isBrowser) { if (isBrowser && !cfg.skipDataMain) {
//Figure out baseUrl. Get it from the script tag with require.js in it. //Figure out baseUrl. Get it from the script tag with require.js in it.
eachReverse(scripts(), function (script) { eachReverse(scripts(), function (script) {
//Set the 'head' where we can append children by //Set the 'head' where we can append children by
...@@ -1906,24 +1947,31 @@ var requirejs, require, define; ...@@ -1906,24 +1947,31 @@ var requirejs, require, define;
//baseUrl, if it is not already set. //baseUrl, if it is not already set.
dataMain = script.getAttribute('data-main'); dataMain = script.getAttribute('data-main');
if (dataMain) { if (dataMain) {
//Preserve dataMain in case it is a path (i.e. contains '?')
mainScript = dataMain;
//Set final baseUrl if there is not already an explicit one. //Set final baseUrl if there is not already an explicit one.
if (!cfg.baseUrl) { if (!cfg.baseUrl) {
//Pull off the directory of data-main for use as the //Pull off the directory of data-main for use as the
//baseUrl. //baseUrl.
src = dataMain.split('/'); src = mainScript.split('/');
mainScript = src.pop(); mainScript = src.pop();
subPath = src.length ? src.join('/') + '/' : './'; subPath = src.length ? src.join('/') + '/' : './';
cfg.baseUrl = subPath; cfg.baseUrl = subPath;
dataMain = mainScript;
} }
//Strip off any trailing .js since dataMain is now //Strip off any trailing .js since mainScript is now
//like a module name. //like a module name.
dataMain = dataMain.replace(jsSuffixRegExp, ''); mainScript = mainScript.replace(jsSuffixRegExp, '');
//If mainScript is still a path, fall back to dataMain
if (req.jsExtRegExp.test(mainScript)) {
mainScript = dataMain;
}
//Put the data-main script in the files to load. //Put the data-main script in the files to load.
cfg.deps = cfg.deps ? cfg.deps.concat(dataMain) : [dataMain]; cfg.deps = cfg.deps ? cfg.deps.concat(mainScript) : [mainScript];
return true; return true;
} }
...@@ -1951,12 +1999,13 @@ var requirejs, require, define; ...@@ -1951,12 +1999,13 @@ var requirejs, require, define;
//This module may not have dependencies //This module may not have dependencies
if (!isArray(deps)) { if (!isArray(deps)) {
callback = deps; callback = deps;
deps = []; deps = null;
} }
//If no name, and callback is a function, then figure out if it a //If no name, and callback is a function, then figure out if it a
//CommonJS thing with dependencies. //CommonJS thing with dependencies.
if (!deps.length && isFunction(callback)) { if (!deps && isFunction(callback)) {
deps = [];
//Remove comments from the callback string, //Remove comments from the callback string,
//look for require calls, and pull them into the dependencies, //look for require calls, and pull them into the dependencies,
//but only if there are function args. //but only if there are function args.
......
;(function (soma, undefined) { (function (soma) {
'use strict'; 'use strict';
soma.template = soma.template || {}; soma.template = soma.template || {};
soma.template.version = "0.1.8"; soma.template.version = '0.2.8';
var errors = soma.template.errors = { soma.template.errors = {
TEMPLATE_STRING_NO_ELEMENT: "Error in soma.template, a string template requirement a second parameter: an element target - soma.template.create('string', element)", TEMPLATE_STRING_NO_ELEMENT: 'Error in soma.template, a string template requirement a second parameter: an element target - soma.template.create(\'string\', element)',
TEMPLATE_NO_PARAM: "Error in soma.template, a template requires at least 1 parameter - soma.template.create(element)" TEMPLATE_NO_PARAM: 'Error in soma.template, a template requires at least 1 parameter - soma.template.create(element)'
}; };
var tokenStart = '{{'; var tokenStart = '{{';
var tokenEnd = '}}'; var tokenEnd = '}}';
var helpersObject = {}; var helpersObject = {};
var helpersScopeObject = {}; var helpersScopeObject = {};
var settings = soma.template.settings = soma.template.settings || {}; var settings = soma.template.settings = soma.template.settings || {};
settings.autocreate = true; settings.autocreate = true;
var tokens = settings.tokens = { var tokens = settings.tokens = {
start: function(value) { start: function(value) {
if (isDefined(value) && value !== '') { if (isDefined(value) && value !== '') {
tokenStart = escapeRegExp(value); tokenStart = escapeRegExp(value);
...@@ -34,41 +34,45 @@ var tokens = settings.tokens = { ...@@ -34,41 +34,45 @@ var tokens = settings.tokens = {
} }
return tokenEnd; return tokenEnd;
} }
}; };
var attributes = settings.attributes = { var attributes = settings.attributes = {
skip: "data-skip", skip: 'data-skip',
repeat: "data-repeat", repeat: 'data-repeat',
src: "data-src", src: 'data-src',
href: "data-href", href: 'data-href',
show: "data-show", show: 'data-show',
hide: "data-hide", hide: 'data-hide',
cloak: "data-cloak", cloak: 'data-cloak',
checked: "data-checked", checked: 'data-checked',
disabled: "data-disabled", disabled: 'data-disabled',
multiple: "data-multiple", multiple: 'data-multiple',
readonly: "data-readonly", readonly: 'data-readonly',
selected: "data-selected", selected: 'data-selected',
template: "data-template", template: 'data-template',
html: "data-html" html: 'data-html'
}; };
var vars = settings.vars = { var vars = settings.vars = {
index: "$index", index: '$index',
key: "$key" key: '$key',
}; element: '$element',
parentElement: '$parentElement',
var events = settings.events = {}; attribute: '$attribute',
settings.eventsPrefix = 'data-'; scope: '$scope'
var eventsString = 'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup focus blur change select selectstart scroll copy cut paste mousewheel keypress error contextmenu input textinput drag dragenter dragleave dragover dragend dragstart dragover drop load submit reset search resize beforepaste beforecut beforecopy'; };
eventsString += ' touchstart touchend touchmove touchenter touchleave touchcancel gesturestart gesturechange gestureend';
var eventsArray = eventsString.split(' '); var events = settings.events = {};
var i = -1, l = eventsArray.length; settings.eventsPrefix = 'data-';
while(++i < l) { var eventsString = 'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup focus blur change select selectstart scroll copy cut paste mousewheel keypress error contextmenu input textinput drag dragenter dragleave dragover dragend dragstart dragover drop load submit reset search resize beforepaste beforecut beforecopy';
eventsString += ' touchstart touchend touchmove touchenter touchleave touchcancel gesturestart gesturechange gestureend';
var eventsArray = eventsString.split(' ');
var i = -1, l = eventsArray.length;
while(++i < l) {
events[settings.eventsPrefix + eventsArray[i]] = eventsArray[i]; events[settings.eventsPrefix + eventsArray[i]] = eventsArray[i];
} }
var regex = { var regex = {
sequence: null, sequence: null,
token: null, token: null,
expression: null, expression: null,
...@@ -81,117 +85,138 @@ var regex = { ...@@ -81,117 +85,138 @@ var regex = {
content: /[^.|^\s]/gm, content: /[^.|^\s]/gm,
depth: /..\//g, depth: /..\//g,
string: /^(\"|\')(.*)(\"|\')$/ string: /^(\"|\')(.*)(\"|\')$/
}; };
var ie = (function(){ var ie = (function(){
if (typeof document !== 'object') return undefined; if (typeof document !== 'object') {
var undef, return undefined;
v = 3, }
div = document.createElement('div'), var v = 3,
all = div.getElementsByTagName('i'); stop = false,
while ( div = document.createElement('div');
div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->', all[0] while (!stop) {
); div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->';
return v > 4 ? v : undef; if (!div.getElementsByTagName('i')[0]) {
}()); stop = true;
function isArray(value) { }
}
return v > 4 ? v : undefined;
}());
function isArray(value) {
return Object.prototype.toString.apply(value) === '[object Array]'; return Object.prototype.toString.apply(value) === '[object Array]';
}; }
function isObject(value) { function isObject(value) {
return typeof value === 'object'; return typeof value === 'object';
} }
function isString(value) { function isString(value) {
return typeof value === 'string'; return typeof value === 'string';
} }
function isElement(value) { function isElement(value) {
return value ? value.nodeType > 0 : false; return value ? value.nodeType > 0 : false;
}; }
function isTextNode(el) { function isTextNode(el) {
return el && el.nodeType && el.nodeType === 3; return el && el.nodeType && el.nodeType === 3;
} }
function isFunction(value) { function isFunction(value) {
return value && typeof value === 'function'; return value && typeof value === 'function';
} }
function isDefined(value) { function isDefined(value) {
return value !== null && value !== undefined; return value !== null && value !== undefined;
} }
function isAttributeDefined(value) { function normalizeBoolean(value) {
return (value === "" || value === true || value === "true" || !isDefined(value)); if (!isDefined(value)) {
} return false;
function isExpression(value) { }
if (value === 'true' || value === '1' || value === true || value === 1) {
return true;
}
if (value === 'false' || value === '0' || value === false || value === 0 || (isString(value) && hasInterpolation(value))) {
return false;
}
return !!value;
}
function isExpression(value) {
return value && isFunction(value.toString) && value.toString() === '[object Expression]'; return value && isFunction(value.toString) && value.toString() === '[object Expression]';
} }
function isNode(value) { function isExpFunction(value) {
return value && isFunction(value.toString) && value.toString() === '[object Node]'; if (!isString(value)) {
} return false;
function isExpFunction(value) { }
if (!isString(value)) return false;
return !!value.match(regex.func); return !!value.match(regex.func);
} }
function childNodeIsTemplate(node) { function childNodeIsTemplate(node) {
return node && node.parent && templates.get(node.element); return node && node.parent && templates.get(node.element);
} }
function escapeRegExp(str) { function escapeRegExp(str) {
return str.replace(regex.escape, "\\$&"); return str.replace(regex.escape, '\\$&');
} }
function setRegEX(nonEscapedValue, isStartToken) { function setRegEX(nonEscapedValue, isStartToken) {
// sequence: \{\{.+?\}\}|[^{]+|\{(?!\{)[^{]* // sequence: \{\{.+?\}\}|[^{]+|\{(?!\{)[^{]*
var unescapedCurrentStartToken = tokens.start().replace(/\\/g, ''); var unescapedCurrentStartToken = tokens.start().replace(/\\/g, '');
var endSequence = ""; var endSequence = '';
var ts = isStartToken ? nonEscapedValue : unescapedCurrentStartToken; var ts = isStartToken ? nonEscapedValue : unescapedCurrentStartToken;
if (ts.length > 1) { if (ts.length > 1) {
endSequence = "|\\" + ts.substr(0, 1) + "(?!\\" + ts.substr(1, 1) + ")[^" + ts.substr(0, 1) + "]*"; endSequence = '|\\' + ts.substr(0, 1) + '(?!\\' + ts.substr(1, 1) + ')[^' + ts.substr(0, 1) + ']*';
} }
regex.sequence = new RegExp(tokens.start() + ".+?" + tokens.end() + "|[^" + tokens.start() + "]+" + endSequence, "g"); regex.sequence = new RegExp(tokens.start() + '.+?' + tokens.end() + '|[^' + tokens.start() + ']+' + endSequence, 'g');
regex.token = new RegExp(tokens.start() + ".*?" + tokens.end(), "g"); regex.token = new RegExp(tokens.start() + '.*?' + tokens.end(), 'g');
regex.expression = new RegExp(tokens.start() + "|" + tokens.end(), "gm"); regex.expression = new RegExp(tokens.start() + '|' + tokens.end(), 'gm');
} }
function trim(value) { function trim(value) {
return value.replace(regex.trim, ''); return value.replace(regex.trim, '');
} }
function trimQuotes(value) { function trimQuotes(value) {
if (regex.string.test(value)) { if (regex.string.test(value)) {
return value.substr(1, value.length-2); return value.substr(1, value.length-2);
} }
return value; return value;
} }
function trimArray(value) { function trimArray(value) {
if (value[0] === "") value.shift(); if (value[0] === '') {
if (value[value.length-1] === "") value.pop(); value.shift();
}
if (value[value.length-1] === '') {
value.pop();
}
return value; return value;
} }
function trimTokens(value) { function trimTokens(value) {
return value.replace(regex.expression, ''); return value.replace(regex.expression, '');
} }
function trimScopeDepth(value) { function trimScopeDepth(value) {
return value.replace(regex.depth, ''); return value.replace(regex.depth, '');
} }
function insertBefore(referenceNode, newNode) { function insertBefore(referenceNode, newNode) {
if (!referenceNode.parentNode) return; if (!referenceNode.parentNode) {
return;
}
referenceNode.parentNode.insertBefore(newNode, referenceNode); referenceNode.parentNode.insertBefore(newNode, referenceNode);
} }
function insertAfter(referenceNode, newNode) { function insertAfter(referenceNode, newNode) {
if (!referenceNode.parentNode) return; if (!referenceNode.parentNode) {
return;
}
referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling); referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
}
function removeClass(elm, className) {
if (document.documentElement.classList) {
removeClass = function (elm, className) {
elm.classList.remove(className);
} }
function removeClass(elm, className) {
var rmc;
if (typeof document === 'object' && document.documentElement.classList) {
rmc = function (elm, className) {
elm.classList.remove(className);
};
} else { } else {
removeClass = function (elm, className) { rmc = function (elm, className) {
if (!elm || !elm.className) { if (!elm || !elm.className) {
return false; return false;
} }
var reg = new RegExp("(^|\\s)" + className + "(\\s|$)", "g"); var reg = new RegExp('(^|\\s)' + className + '(\\s|$)', 'g');
elm.className = elm.className.replace(reg, "$2"); elm.className = elm.className.replace(reg, '$2');
};
} }
rmc(elm, className);
} }
removeClass(elm, className); // jquery contains
} var contains = typeof document !== 'object' ? function(){} : document.documentElement.contains ?
// jquery contains
var contains = typeof document !== 'object' ? function(){} : document.documentElement.contains ?
function( a, b ) { function( a, b ) {
var adown = a.nodeType === 9 ? a.documentElement : a, var adown = a.nodeType === 9 ? a.documentElement : a,
bup = b && b.parentNode; bup = b && b.parentNode;
...@@ -210,66 +235,79 @@ var contains = typeof document !== 'object' ? function(){} : document.documentEl ...@@ -210,66 +235,79 @@ var contains = typeof document !== 'object' ? function(){} : document.documentEl
return false; return false;
}; };
function HashMap(id) {
function HashMap() {
var items = {}; var items = {};
var id = 1; var count = 0;
//var uuid = function(a,b){for(b=a='';a++<36;b+=a*51&52?(a^15?8^Math.random()*(a^20?16:4):4).toString(16):'-');return b;} //var uuid = function(a,b){for(b=a='';a++<36;b+=a*51&52?(a^15?8^Math.random()*(a^20?16:4):4).toString(16):'-');return b;}
function uuid() { return ++id; }; function uuid() { return ++count + id; }
function getKey(target) { function getKey(target) {
if (!target) return; if (!target) {
if (typeof target !== 'object') return target; return;
}
if (typeof target !== 'object') {
return target;
}
var result; var result;
try { try {
// IE 7-8 needs a try catch, seems like I can't add a property on text nodes // IE 7-8 needs a try catch, seems like I can't add a property on text nodes
result = target.hashkey ? target.hashkey : target.hashkey = uuid(); result = target[id] ? target[id] : target[id] = uuid();
} catch(err){}; } catch(err){}
return result; return result;
} }
this.remove = function(key) { this.remove = function(key) {
delete items[getKey(key)]; delete items[getKey(key)];
} };
this.get = function(key) { this.get = function(key) {
return items[getKey(key)]; return items[getKey(key)];
} };
this.put = function(key, value) { this.put = function(key, value) {
items[getKey(key)] = value; items[getKey(key)] = value;
} };
this.has = function(key) { this.has = function(key) {
return typeof items[getKey(key)] !== 'undefined'; return typeof items[getKey(key)] !== 'undefined';
} };
this.getData = function() { this.getData = function() {
return items; return items;
} };
this.dispose = function() { this.dispose = function() {
for (var key in items) { for (var key in items) {
if (items.hasOwnProperty(key)) {
delete items[key]; delete items[key];
} }
}
this.length = 0; this.length = 0;
};
} }
}
function getRepeaterData(repeaterValue, scope) { function getRepeaterData(repeaterValue, scope) {
var parts = repeaterValue.match(regex.repeat); var parts = repeaterValue.match(regex.repeat);
if (!parts) return; if (!parts) {
return;
}
var source = parts[2]; var source = parts[2];
var exp = new Expression(source); var exp = new Expression(source);
return exp.getValue(scope); return exp.getValue(scope);
} }
function updateScopeWithRepeaterData(repeaterValue, scope, data) { function updateScopeWithRepeaterData(repeaterValue, scope, data) {
var parts = repeaterValue.match(regex.repeat); var parts = repeaterValue.match(regex.repeat);
if (!parts) return; if (!parts) {
return;
}
var name = parts[1]; var name = parts[1];
scope[name] = data; scope[name] = data;
} }
function getWatcherValue(exp, newValue) { function getWatcherValue(exp, newValue) {
var node = exp.node || exp.attribute.node; var node = exp.node || exp.attribute.node;
var watchers = node.template.watchers; var watchers = node.template.watchers;
var nodeTarget = node.element; var nodeTarget = node.element;
if (!watchers) return newValue; if (!watchers) {
return newValue;
}
var watcherNode = watchers.get(nodeTarget); var watcherNode = watchers.get(nodeTarget);
if (!watcherNode && isTextNode(node.element) && node.parent) watcherNode = watchers.get(node.parent.element); if (!watcherNode && isTextNode(node.element) && node.parent) {
watcherNode = watchers.get(node.parent.element);
}
var watcher = watcherNode ? watcherNode : watchers.get(exp.pattern); var watcher = watcherNode ? watcherNode : watchers.get(exp.pattern);
if (isFunction(watcher)) { if (isFunction(watcher)) {
var watcherValue = watcher(exp.value, newValue, exp.pattern, node.scope, node, exp.attribute); var watcherValue = watcher(exp.value, newValue, exp.pattern, node.scope, node, exp.attribute);
...@@ -278,9 +316,9 @@ function getWatcherValue(exp, newValue) { ...@@ -278,9 +316,9 @@ function getWatcherValue(exp, newValue) {
} }
} }
return newValue; return newValue;
} }
function getScopeFromPattern(scope, pattern) { function getScopeFromPattern(scope, pattern) {
var depth = getScopeDepth(pattern); var depth = getScopeDepth(pattern);
var scopeTarget = scope; var scopeTarget = scope;
while (depth > 0) { while (depth > 0) {
...@@ -288,14 +326,27 @@ function getScopeFromPattern(scope, pattern) { ...@@ -288,14 +326,27 @@ function getScopeFromPattern(scope, pattern) {
depth--; depth--;
} }
return scopeTarget; return scopeTarget;
} }
function getValueFromPattern(scope, pattern) { function getValueFromPattern(scope, pattern, context) {
var exp = new Expression(pattern); var exp = new Expression(pattern);
return getValue(scope, exp.pattern, exp.path, exp.params); return getValue(scope, exp.pattern, exp.path, exp.params, undefined, undefined, undefined, context);
} }
function getValue(scope, pattern, pathString, params, getFunction, getParams, paramsFound) { function getValue(scope, pattern, pathString, params, getFunction, getParams, paramsFound, context) {
// context
if (pattern === vars.element) {
return context[vars.element];
}
if (pattern === vars.parentElement) {
return context[vars.parentElement];
}
if (pattern === vars.attribute) {
return context[vars.attribute];
}
if (pattern === vars.scope) {
return context[vars.scope];
}
// string // string
if (regex.string.test(pattern)) { if (regex.string.test(pattern)) {
return trimQuotes(pattern); return trimQuotes(pattern);
...@@ -304,29 +355,39 @@ function getValue(scope, pattern, pathString, params, getFunction, getParams, pa ...@@ -304,29 +355,39 @@ function getValue(scope, pattern, pathString, params, getFunction, getParams, pa
var paramsValues = []; var paramsValues = [];
if (!paramsFound && params) { if (!paramsFound && params) {
for (var j = 0, jl = params.length; j < jl; j++) { for (var j = 0, jl = params.length; j < jl; j++) {
paramsValues.push(getValueFromPattern(scope, params[j])); paramsValues.push(getValueFromPattern(scope, params[j], context));
}
} }
else {
paramsValues = paramsFound;
}
if (getParams) {
return paramsValues;
} }
else paramsValues = paramsFound;
if (getParams) return paramsValues;
// find scope // find scope
var scopeTarget = getScopeFromPattern(scope, pattern); var scopeTarget = getScopeFromPattern(scope, pattern);
// remove parent string // remove parent string
pattern = pattern.replace(/..\//g, ''); pattern = pattern.replace(/..\//g, '');
pathString = pathString.replace(/..\//g, ''); pathString = pathString.replace(/..\//g, '');
if (!scopeTarget) return undefined; if (!scopeTarget) {
return undefined;
}
// search path // search path
var path = scopeTarget; var path = scopeTarget;
var pathParts = pathString.split(/\.|\[|\]/g); var pathParts = pathString.split(/\.|\[|\]/g);
if (pathParts.length > 0) { if (pathParts.length > 0) {
for (var i = 0, l = pathParts.length; i < l; i++) { for (var i = 0, l = pathParts.length; i < l; i++) {
if (pathParts[i] !== "") { if (pathParts[i] !== '') {
path = path[pathParts[i]]; path = path[pathParts[i]];
} }
if (!isDefined(path)) { if (!isDefined(path)) {
// no path, search in parent // no path, search in parent
if (scopeTarget._parent) return getValue(scopeTarget._parent, pattern, pathString, params, getFunction, getParams, paramsValues); if (scopeTarget._parent) {
else return undefined; return getValue(scopeTarget._parent, pattern, pathString, params, getFunction, getParams, paramsValues);
}
else {
return undefined;
}
} }
} }
} }
...@@ -335,46 +396,42 @@ function getValue(scope, pattern, pathString, params, getFunction, getParams, pa ...@@ -335,46 +396,42 @@ function getValue(scope, pattern, pathString, params, getFunction, getParams, pa
return path; return path;
} }
else { else {
if (getFunction) return path; if (getFunction) {
else return path.apply(null, paramsValues); return path;
}
else {
return path.apply(null, paramsValues);
}
} }
return undefined; return undefined;
} }
function getExpressionPath(value) { function getExpressionPath(value) {
var val = value.split('(')[0]; var val = value.split('(')[0];
val = trimScopeDepth(val); val = trimScopeDepth(val);
return val; return val;
} }
function getParamsFromString(value) { function getParamsFromString(value) {
return trimArray(value.split(regex.params)); return trimArray(value.split(regex.params));
} }
function getScopeDepth(value) { function getScopeDepth(value) {
var val = value.split('(')[0]; var val = value.split('(')[0];
var matches = val.match(regex.depth); var matches = val.match(regex.depth);
return !matches ? 0 : matches.length; return !matches ? 0 : matches.length;
} }
function getNodeFromElement(element, scope, isRepeaterDescendant) { function addAttribute(node, name, value) {
var node = new Node(element, scope); var attr;
node.previousSibling = element.previousSibling; node.attributes = node.attributes || [];
node.nextSibling = element.nextSibling;
var attributes = [];
var eventsArray = [];
for (var attr, name, value, attrs = element.attributes, j = 0, jj = attrs && attrs.length; j < jj; j++) {
attr = attrs[j];
if (attr.specified) {
name = attr.name;
value = attr.value;
if (name === settings.attributes.skip) { if (name === settings.attributes.skip) {
node.skip = (value === "" || value === "true"); node.skip = normalizeBoolean(value);
} }
if (name === settings.attributes.html) { if (name === settings.attributes.html) {
node.html = (value === "" || value === "true"); node.html = normalizeBoolean(value);
} }
if (name === settings.attributes.repeat && !isRepeaterDescendant) { if (name === settings.attributes.repeat && !node.isRepeaterDescendant) {
node.repeater = value; node.repeater = value;
} }
if ( if (
...@@ -392,59 +449,88 @@ function getNodeFromElement(element, scope, isRepeaterDescendant) { ...@@ -392,59 +449,88 @@ function getNodeFromElement(element, scope, isRepeaterDescendant) {
name === settings.attributes.selected || name === settings.attributes.selected ||
value.indexOf(settings.attributes.cloak) !== -1 value.indexOf(settings.attributes.cloak) !== -1
) { ) {
attributes.push(new Attribute(name, value, node)); attr = new Attribute(name, value, node);
node.attributes.push(attr);
}
if (events[name]) {
attr = new Attribute(name, value, node);
node.attributes.push(attr);
}
return attr;
}
function getNodeFromElement(element, scope) {
var node = new Node(element, scope);
node.previousSibling = element.previousSibling;
node.nextSibling = element.nextSibling;
var eventsArray = [];
for (var attr, attrs = element.attributes, j = 0, jj = attrs && attrs.length; j < jj; j++) {
attr = attrs[j];
if (attr.specified || attr.name === 'value') {
var newAttr = addAttribute(node, attr.name, attr.value);
if (events[attr.name]) {
if (events[attr.name] && !node.isRepeaterChild) {
eventsArray.push({name:events[attr.name], value:attr.value, attr: newAttr});
} }
if (events[name] && !isRepeaterDescendant) {
eventsArray.push({name:events[name], value:value});
attributes.push(new Attribute(name, value, node));
} }
} }
} }
node.attributes = attributes; for (var a=0, b=eventsArray.length; a<b; a++) {
for (var i = 0, l = eventsArray.length; i < l; i++) { node.addEvent(eventsArray[a].name, eventsArray[a].value, eventsArray[a].attr);
node.addEvent(eventsArray[i].name, eventsArray[i].value);
} }
return node; return node;
} }
function hasInterpolation(value) { function hasInterpolation(value) {
var matches = value.match(regex.token); var matches = value.match(regex.token);
return matches && matches.length > 0; return matches && matches.length > 0;
} }
function hasContent(value) { function hasContent(value) {
return regex.content.test(value) return regex.content.test(value);
} }
function isElementValid(element) { function isElementValid(element) {
if (!element) return; if (!element) {
return;
}
var type = element.nodeType; var type = element.nodeType;
if (!element || !type) return false; if (!element || !type) {
return false;
}
// comment // comment
if (type === 8) return false; if (type === 8) {
return false;
}
// empty text node // empty text node
if (type === 3 && !hasContent(element.nodeValue) && !hasInterpolation(element.nodeValue)) return false; if (type === 3 && !hasContent(element.nodeValue) && !hasInterpolation(element.nodeValue)) {
return false;
}
// result // result
return true; return true;
} }
function compile(template, element, parent, nodeTarget) { function compile(template, element, parent, nodeTarget) {
if (!isElementValid(element)) return; if (!isElementValid(element)) {
return;
}
// get node // get node
var node; var node;
if (!nodeTarget) { if (!nodeTarget) {
node = getNodeFromElement(element, parent ? parent.scope : new Scope(helpersScopeObject)._createChild(), parent && (parent.repeater || parent.isRepeaterDescendant) ); node = getNodeFromElement(element, parent ? parent.scope : new Scope(helpersScopeObject)._createChild());
} }
else { else {
node = nodeTarget; node = nodeTarget;
node.parent = parent; node.parent = parent;
} }
if (parent && (parent.repeater || parent.isRepeaterDescendant)) { if (parent && (parent.repeater || parent.isRepeaterChild)) {
node.isRepeaterDescendant = true; node.isRepeaterChild = true;
} }
node.template = template; node.template = template;
// children // children
if (node.skip) return; if (node.skip) {
return;
}
var child = element.firstChild; var child = element.firstChild;
while (child) { while (child) {
var childNode = compile(template, child, node); var childNode = compile(template, child, node);
...@@ -455,39 +541,47 @@ function compile(template, element, parent, nodeTarget) { ...@@ -455,39 +541,47 @@ function compile(template, element, parent, nodeTarget) {
child = child.nextSibling; child = child.nextSibling;
} }
return node; return node;
} }
function updateScopeWithData(scope, data) { function updateScopeWithData(scope, data) {
clearScope(scope); clearScope(scope);
for (var d in data) { for (var d in data) {
if (data.hasOwnProperty(d)) {
scope[d] = data[d]; scope[d] = data[d];
} }
} }
}
function clearScope(scope) { function clearScope(scope) {
for (var key in scope) { for (var key in scope) {
if (scope.hasOwnProperty(key)) {
if (key.substr(0, 1) !== '_') { if (key.substr(0, 1) !== '_') {
scope[key] = null; scope[key] = null;
delete scope[key]; delete scope[key];
} }
} }
} }
}
function updateNodeChildren(node) { function updateNodeChildren(node) {
if (node.repeater || !node.children || childNodeIsTemplate(node)) return; if (node.repeater || !node.children || childNodeIsTemplate(node)) {
return;
}
for (var i = 0, l = node.children.length; i < l; i++) { for (var i = 0, l = node.children.length; i < l; i++) {
node.children[i].update(); node.children[i].update();
} }
} }
function renderNodeChildren(node) { function renderNodeChildren(node) {
if (!node.children || childNodeIsTemplate(node)) return; if (!node.children || childNodeIsTemplate(node)) {
return;
}
for (var i = 0, l = node.children.length; i < l; i++) { for (var i = 0, l = node.children.length; i < l; i++) {
node.children[i].render(); node.children[i].render();
} }
} }
function renderNodeRepeater(node) { function renderNodeRepeater(node) {
var data = getRepeaterData(node.repeater, node.scope); var data = getRepeaterData(node.repeater, node.scope);
var previousElement; var previousElement;
if (isArray(data)) { if (isArray(data)) {
...@@ -509,9 +603,11 @@ function renderNodeRepeater(node) { ...@@ -509,9 +603,11 @@ function renderNodeRepeater(node) {
// process object // process object
var count = -1; var count = -1;
for (var o in data) { for (var o in data) {
if (data.hasOwnProperty(o)) {
count++; count++;
previousElement = createRepeaterChild(node, count, data[o], vars.key, o, previousElement); previousElement = createRepeaterChild(node, count, data[o], vars.key, o, previousElement);
} }
}
var size = count; var size = count;
while (count++ < node.childrenRepeater.length-1) { while (count++ < node.childrenRepeater.length-1) {
node.parent.element.removeChild(node.childrenRepeater[count].element); node.parent.element.removeChild(node.childrenRepeater[count].element);
...@@ -522,59 +618,88 @@ function renderNodeRepeater(node) { ...@@ -522,59 +618,88 @@ function renderNodeRepeater(node) {
if (node.element.parentNode) { if (node.element.parentNode) {
node.element.parentNode.removeChild(node.element); node.element.parentNode.removeChild(node.element);
} }
} }
function cloneRepeaterNode(element, node) { function compileClone(node, newNode) {
var newNode = new Node(element, node.scope._createChild()); if (!isElementValid(newNode.element)) {
return;
}
// create attribute
if (node.attributes) { if (node.attributes) {
var attrs = []; for (var i= 0, l=node.attributes.length; i<l; i++) {
for (var i = 0, l = node.attributes.length; i < l; i++) { var attr = node.attributes[i];
newNode.renderAsHtml = node.renderAsHtml; var newAttr = addAttribute(newNode, attr.name, attr.value);
if (node.attributes[i].name === settings.attributes.skip) { if (events[attr.name]) {
newNode.skip = (node.attributes[i].value === "" || node.attributes[i].value === "true"); newNode.addEvent(events[attr.name], attr.value, newAttr);
} }
if (node.attributes[i].name === settings.attributes.html) {
newNode.html = (node.attributes[i].value === "" || node.attributes[i].value === "true");
} }
if (node.attributes[i].name !== attributes.repeat) {
var attribute = new Attribute(node.attributes[i].name, node.attributes[i].value, newNode);
attrs.push(attribute);
} }
if (events[node.attributes[i].name]) { // children
newNode.addEvent(events[node.attributes[i].name], node.attributes[i].value); var child = node.element.firstChild;
var newChild = newNode.element.firstChild;
// loop
while (child && newChild) {
var childNode = node.getNode(child);
var newChildNode = new Node(newChild, newNode.scope);
newNode.children.push(newChildNode);
newChildNode.parent = newNode;
newChildNode.template = newNode.template;
newChildNode.isRepeaterChild = true;
var compiledNode = compileClone(childNode, newChildNode);
if (compiledNode) {
compiledNode.parent = newChildNode;
compiledNode.template = newChildNode.template;
newChildNode.children.push(compiledNode);
} }
child = child.nextSibling;
newChild = newChild.nextSibling;
} }
newNode.attributes = attrs; return newChildNode;
} }
function cloneRepeaterNode(element, node) {
var newNode = new Node(element, node.scope._createChild());
newNode.template = node.template;
newNode.parent = node;
newNode.isRepeaterChild = true;
newNode.isRepeaterDescendant = true;
compileClone(node, newNode);
return newNode; return newNode;
} }
function appendRepeaterElement(previousElement, node, newElement) {
if (!previousElement) {
if (node.element.previousSibling) {
insertAfter(node.element.previousSibling, newElement);
}
else if (node.element.nextSibling) {
insertBefore(node.element.nextSibling, newElement);
}
else {
node.parent.element.appendChild(newElement);
}
}
else {
insertAfter(previousElement, newElement);
}
}
function createRepeaterChild(node, count, data, indexVar, indexVarValue, previousElement) { function createRepeaterChild(node, count, data, indexVar, indexVarValue, previousElement) {
var existingChild = node.childrenRepeater[count]; var existingChild = node.childrenRepeater[count];
if (!existingChild) { if (!existingChild) {
// no existing node
var newElement = node.element.cloneNode(true); var newElement = node.element.cloneNode(true);
// need to append the cloned element to the DOM
// before changing attributes or IE will crash
appendRepeaterElement(previousElement, node, newElement);
// can't recreate the node with a cloned element on IE7 // can't recreate the node with a cloned element on IE7
// be cause the attributes are not specified annymore (attribute.specified) // be cause the attributes are not specified anymore (attribute.specified)
//var newNode = getNodeFromElement(newElement, node.scope._createChild(), true); //var newNode = getNodeFromElement(newElement, node.scope._createChild(), true);
var newNode = cloneRepeaterNode(newElement, node); var newNode = cloneRepeaterNode(newElement, node);
newNode.isRepeaterChild = true;
newNode.parent = node.parent;
newNode.template = node.template;
node.childrenRepeater[count] = newNode; node.childrenRepeater[count] = newNode;
updateScopeWithRepeaterData(node.repeater, newNode.scope, data); updateScopeWithRepeaterData(node.repeater, newNode.scope, data);
newNode.scope[indexVar] = indexVarValue; newNode.scope[indexVar] = indexVarValue;
compile(node.template, newElement, node.parent, newNode);
newNode.update(); newNode.update();
newNode.render(); newNode.render();
if (!previousElement) {
if (node.previousSibling) insertAfter(node.previousSibling, newElement);
else if (node.nextSibling) insertBefore(node.nextSibling, newElement);
else node.parent.element.appendChild(newElement);
}
else {
insertAfter(previousElement, newElement);
}
return newElement; return newElement;
} }
else { else {
...@@ -585,28 +710,29 @@ function createRepeaterChild(node, count, data, indexVar, indexVarValue, previou ...@@ -585,28 +710,29 @@ function createRepeaterChild(node, count, data, indexVar, indexVarValue, previou
existingChild.render(); existingChild.render();
return existingChild.element; return existingChild.element;
} }
} }
var Scope = function(data) { var Scope = function(data) {
var self;
function createChild(data) { function createChild(data) {
var obj = createObject(data); var obj = createObject(data);
obj._parent = this; obj._parent = self;
this._children.push(obj); self._children.push(obj);
return obj; return obj;
} }
function createObject(data) { function createObject(data) {
var obj = data || {}; var obj = data || {};
obj._parent = null; obj._parent = null;
obj._children = []; obj._children = [];
obj._createChild = function(data) { obj._createChild = function() {
self = obj;
return createChild.apply(obj, arguments); return createChild.apply(obj, arguments);
} };
return obj; return obj;
} }
return createObject(data); return createObject(data);
}; };
var Node = function(element, scope) {
var Node = function(element, scope) {
this.element = element; this.element = element;
this.scope = scope; this.scope = scope;
this.attributes = null; this.attributes = null;
...@@ -631,8 +757,8 @@ var Node = function(element, scope) { ...@@ -631,8 +757,8 @@ var Node = function(element, scope) {
this.interpolation = new Interpolation(this.value, this, undefined); this.interpolation = new Interpolation(this.value, this, undefined);
} }
}; };
Node.prototype = { Node.prototype = {
toString: function() { toString: function() {
return '[object Node]'; return '[object Node]';
}, },
...@@ -660,7 +786,6 @@ Node.prototype = { ...@@ -660,7 +786,6 @@ Node.prototype = {
this.element = null; this.element = null;
this.scope = null; this.scope = null;
this.attributes = null; this.attributes = null;
this.attributesHashMap = null;
this.value = null; this.value = null;
this.interpolation = null; this.interpolation = null;
this.repeater = null; this.repeater = null;
...@@ -674,17 +799,21 @@ Node.prototype = { ...@@ -674,17 +799,21 @@ Node.prototype = {
}, },
getNode: function(element) { getNode: function(element) {
var node; var node;
if (element === this.element) return this; if (element === this.element) {
else if (this.childrenRepeater.length > 0) { return this;
}
if (this.childrenRepeater.length > 0) {
for (var k = 0, kl = this.childrenRepeater.length; k < kl; k++) { for (var k = 0, kl = this.childrenRepeater.length; k < kl; k++) {
node = this.childrenRepeater[k].getNode(element); node = this.childrenRepeater[k].getNode(element);
if (node) return node; if (node) {
return node;
}
} }
} }
else {
for (var i = 0, l = this.children.length; i < l; i++) { for (var i = 0, l = this.children.length; i < l; i++) {
node = this.children[i].getNode(element); node = this.children[i].getNode(element);
if (node) return node; if (node) {
return node;
} }
} }
return null; return null;
...@@ -700,7 +829,9 @@ Node.prototype = { ...@@ -700,7 +829,9 @@ Node.prototype = {
} }
}, },
update: function() { update: function() {
if (childNodeIsTemplate(this)) return; if (childNodeIsTemplate(this)) {
return;
}
if (isDefined(this.interpolation)) { if (isDefined(this.interpolation)) {
this.interpolation.update(); this.interpolation.update();
} }
...@@ -712,7 +843,9 @@ Node.prototype = { ...@@ -712,7 +843,9 @@ Node.prototype = {
updateNodeChildren(this); updateNodeChildren(this);
}, },
invalidateData: function() { invalidateData: function() {
if (childNodeIsTemplate(this)) return; if (childNodeIsTemplate(this)) {
return;
}
this.invalidate = true; this.invalidate = true;
var i, l; var i, l;
if (this.attributes) { if (this.attributes) {
...@@ -727,14 +860,17 @@ Node.prototype = { ...@@ -727,14 +860,17 @@ Node.prototype = {
this.children[i].invalidateData(); this.children[i].invalidateData();
} }
}, },
addEvent: function(type, pattern) { addEvent: function(type, pattern, attr) {
if (this.repeater) return; if (this.repeater) {
return;
}
if (this.eventHandlers[type]) { if (this.eventHandlers[type]) {
this.removeEvent(type); this.removeEvent(type);
} }
var scope = this.scope; var scope = this.scope;
var node = this;
var handler = function(event) { var handler = function(event) {
var exp = new Expression(pattern, this.node); var exp = new Expression(pattern, node, attr);
var func = exp.getValue(scope, true); var func = exp.getValue(scope, true);
var params = exp.getValue(scope, false, true); var params = exp.getValue(scope, false, true);
params.unshift(event); params.unshift(event);
...@@ -753,7 +889,9 @@ Node.prototype = { ...@@ -753,7 +889,9 @@ Node.prototype = {
clearEvents: function() { clearEvents: function() {
if (this.eventHandlers) { if (this.eventHandlers) {
for (var key in this.eventHandlers) { for (var key in this.eventHandlers) {
this.removeEvent(key, this.eventHandlers[key]); if (this.eventHandlers.hasOwnProperty(key)) {
this.removeEvent(key);
}
} }
} }
if (this.children) { if (this.children) {
...@@ -768,7 +906,9 @@ Node.prototype = { ...@@ -768,7 +906,9 @@ Node.prototype = {
} }
}, },
render: function() { render: function() {
if (childNodeIsTemplate(this)) return; if (childNodeIsTemplate(this)) {
return;
}
if (this.invalidate) { if (this.invalidate) {
this.invalidate = false; this.invalidate = false;
if (isTextNode(this.element)) { if (isTextNode(this.element)) {
...@@ -792,22 +932,26 @@ Node.prototype = { ...@@ -792,22 +932,26 @@ Node.prototype = {
renderNodeChildren(this); renderNodeChildren(this);
} }
} }
}; };
var Attribute = function(name, value, node) { var Attribute = function(name, value, node) {
this.name = name; this.name = name;
this.value = value; this.value = value;
this.node = node; this.node = node;
this.interpolationName = new Interpolation(this.name, null, this); this.interpolationName = new Interpolation(this.name, null, this);
this.interpolationValue = new Interpolation(this.value, null, this); this.interpolationValue = new Interpolation(this.value, null, this);
this.invalidate = false; this.invalidate = false;
}; };
Attribute.prototype = { Attribute.prototype = {
toString: function() { toString: function() {
return '[object Attribute]'; return '[object Attribute]';
}, },
dispose: function() { dispose: function() {
if (this.interpolationName) this.interpolationName.dispose(); if (this.interpolationName) {
if (this.interpolationValue) this.interpolationValue.dispose(); this.interpolationName.dispose();
}
if (this.interpolationValue) {
this.interpolationValue.dispose();
}
this.interpolationName = null; this.interpolationName = null;
this.interpolationValue = null; this.interpolationValue = null;
this.node = null; this.node = null;
...@@ -816,47 +960,77 @@ Attribute.prototype = { ...@@ -816,47 +960,77 @@ Attribute.prototype = {
this.previousName = null; this.previousName = null;
}, },
update: function() { update: function() {
if (this.node.repeater) {
return;
}
this.interpolationName.update(); this.interpolationName.update();
this.interpolationValue.update(); this.interpolationValue.update();
}, },
render: function() { render: function() {
if (this.node.repeater) return; if (this.node.repeater) {
return;
}
// normal attribute
function renderAttribute(name, value, node) {
if (name === 'value' && node.element['value'] !== undefined) {
element.value = value;
}
else if (ie === 7 && name === 'class') {
element.className = value;
}
else {
element.setAttribute(name, value);
}
}
// boolean attribute
function renderBooleanAttribute(name, value) {
element.setAttribute(name, value);
}
// special attribute
function renderSpecialAttribute(value, attrName) {
if (normalizeBoolean(value)) {
element.setAttribute(attrName, attrName);
}
else {
element.removeAttribute(attrName);
}
}
// src attribute
function renderSrc(value) {
element.setAttribute('src', value);
}
// href attribute
function renderHref(value) {
element.setAttribute('href', value);
}
var element = this.node.element; var element = this.node.element;
if (this.invalidate) { if (this.invalidate) {
this.invalidate = false; this.invalidate = false;
this.previousName = this.name; this.previousName = this.name;
this.name = isDefined(this.interpolationName.render()) ? this.interpolationName.render() : this.name; this.name = isDefined(this.interpolationName.render()) ? this.interpolationName.render() : this.name;
this.value = isDefined(this.interpolationValue.render()) ? this.interpolationValue.render() : this.value; this.value = isDefined(this.interpolationValue.render()) ? this.interpolationValue.render() : this.value;
if (this.name === attributes.src) { if (this.name === attributes.src) {
renderSrc(this.name, this.value); renderSrc(this.value);
} }
else if (this.name === attributes.href) { else if (this.name === attributes.href) {
renderHref(this.name, this.value); renderHref(this.value);
}
else {
if (this.node.isRepeaterChild && ie === 7) {
// delete attributes on cloned elements crash IE7
} }
else { else {
if (ie !== 7 || (ie === 7 && !this.node.isRepeaterChild)) {
this.node.element.removeAttribute(this.interpolationName.value); this.node.element.removeAttribute(this.interpolationName.value);
} }
if (this.previousName) { if (this.previousName) {
if (ie === 7 && this.previousName === 'class') { if (ie === 7 && this.previousName === 'class') {
// iE // iE
this.node.element.className = ""; this.node.element.className = '';
}
else {
if (this.node.isRepeaterChild && ie === 7) {
// delete attributes on cloned elements crash IE7
} }
else { else {
if (ie !== 7 || (ie === 7 && !this.node.isRepeaterChild)) {
this.node.element.removeAttribute(this.previousName); this.node.element.removeAttribute(this.previousName);
} }
} }
} }
renderAttribute(this.name, this.value, this.previousName); renderAttribute(this.name, this.value, this.node);
} }
} }
// cloak // cloak
...@@ -865,68 +1039,52 @@ Attribute.prototype = { ...@@ -865,68 +1039,52 @@ Attribute.prototype = {
} }
// hide // hide
if (this.name === attributes.hide) { if (this.name === attributes.hide) {
element.style.display = isAttributeDefined(this.value) ? "none" : "block"; var bool = normalizeBoolean(this.value);
renderAttribute(this.name, bool, this.node);
element.style.display = bool ? 'none' : '';
} }
// show // show
if (this.name === attributes.show) { if (this.name === attributes.show) {
element.style.display = isAttributeDefined(this.value) ? "block" : "none"; var bool = normalizeBoolean(this.value);
renderAttribute(this.name, bool, this.node);
element.style.display = bool ? '' : 'none';
} }
// checked // checked
if (this.name === attributes.checked) { if (this.name === attributes.checked) {
renderSpecialAttribute(this.name, this.value, 'checked'); renderSpecialAttribute(this.value, 'checked');
element.checked = isAttributeDefined(this.value) ? true : false; renderAttribute(this.name, normalizeBoolean(this.value) ? true : false, this.node);
element.checked = normalizeBoolean(this.value) ? true : false;
} }
// disabled // disabled
if (this.name === attributes.disabled) { if (this.name === attributes.disabled) {
renderSpecialAttribute(this.name, this.value, 'disabled'); renderSpecialAttribute(this.value, 'disabled');
renderAttribute(this.name, normalizeBoolean(this.value) ? true : false, this.node);
} }
// multiple // multiple
if (this.name === attributes.multiple) { if (this.name === attributes.multiple) {
renderSpecialAttribute(this.name, this.value, 'multiple'); renderSpecialAttribute(this.value, 'multiple');
renderAttribute(this.name, normalizeBoolean(this.value) ? true : false, this.node);
} }
// readonly // readonly
if (this.name === attributes.readonly) { if (this.name === attributes.readonly) {
var bool = normalizeBoolean(this.value);
if (ie === 7) { if (ie === 7) {
element.readOnly = isAttributeDefined(this.value) ? true : false; element.readOnly = bool ? true : false;
} }
else { else {
renderSpecialAttribute(this.name, this.value, 'readonly'); renderSpecialAttribute(this.value, 'readonly');
} }
renderAttribute(this.name, bool ? true : false, this.node);
} }
// selected // selected
if (this.name === attributes.selected) { if (this.name === attributes.selected) {
renderSpecialAttribute(this.name, this.value, 'selected'); renderSpecialAttribute(this.value, 'selected');
} renderAttribute(this.name, normalizeBoolean(this.value) ? true : false, this.node);
// normal attribute
function renderAttribute(name, value) {
if (ie === 7 && name === "class") {
element.className = value;
}
else {
element.setAttribute(name, value);
} }
} }
// special attribute };
function renderSpecialAttribute(name, value, attrName) {
if (isAttributeDefined(value)) {
element.setAttribute(attrName, attrName);
}
else {
element.removeAttribute(attrName);
}
}
// src attribute
function renderSrc(name, value) {
element.setAttribute('src', value);
}
// href attribute
function renderHref(name, value) {
element.setAttribute('href', value);
}
}
};
var Interpolation = function(value, node, attribute) { var Interpolation = function(value, node, attribute) {
this.value = node && !isTextNode(node.element) ? trim(value) : value; this.value = node && !isTextNode(node.element) ? trim(value) : value;
this.node = node; this.node = node;
this.attribute = attribute; this.attribute = attribute;
...@@ -946,8 +1104,8 @@ var Interpolation = function(value, node, attribute) { ...@@ -946,8 +1104,8 @@ var Interpolation = function(value, node, attribute) {
} }
trimArray(this.sequence); trimArray(this.sequence);
} }
}; };
Interpolation.prototype = { Interpolation.prototype = {
toString: function() { toString: function() {
return '[object Interpolation]'; return '[object Interpolation]';
}, },
...@@ -970,22 +1128,30 @@ Interpolation.prototype = { ...@@ -970,22 +1128,30 @@ Interpolation.prototype = {
} }
}, },
render: function() { render: function() {
var rendered = ""; var rendered = '';
if (this.sequence) { if (this.sequence) {
for (var i = 0, l = this.sequence.length; i < l; i++) { for (var i = 0, l = this.sequence.length; i < l; i++) {
var val = ""; var val = '';
if (isExpression(this.sequence[i])) val = this.sequence[i].value; if (isExpression(this.sequence[i])) {
else val = this.sequence[i]; val = this.sequence[i].value;
if (!isDefined(val)) val = ""; }
else {
val = this.sequence[i];
}
if (!isDefined(val)) {
val = '';
}
rendered += val; rendered += val;
} }
} }
return rendered; return rendered;
} }
}; };
var Expression = function(pattern, node, attribute) { var Expression = function(pattern, node, attribute) {
if (!isDefined(pattern)) return; if (!isDefined(pattern)) {
return;
}
this.pattern = pattern; this.pattern = pattern;
this.isString = regex.string.test(pattern); this.isString = regex.string.test(pattern);
this.node = node; this.node = node;
...@@ -1003,8 +1169,8 @@ var Expression = function(pattern, node, attribute) { ...@@ -1003,8 +1169,8 @@ var Expression = function(pattern, node, attribute) {
this.path = getExpressionPath(this.pattern); this.path = getExpressionPath(this.pattern);
this.params = !this.isFunction ? null : getParamsFromString(this.pattern.match(regex.func)[2]); this.params = !this.isFunction ? null : getParamsFromString(this.pattern.match(regex.func)[2]);
} }
}; };
Expression.prototype = { Expression.prototype = {
toString: function() { toString: function() {
return '[object Expression]'; return '[object Expression]';
}, },
...@@ -1018,8 +1184,12 @@ Expression.prototype = { ...@@ -1018,8 +1184,12 @@ Expression.prototype = {
}, },
update: function() { update: function() {
var node = this.node; var node = this.node;
if (!node && this.attribute) node = this.attribute.node; if (!node && this.attribute) {
if (!node && node.scope) return; node = this.attribute.node;
}
if (!node && node.scope) {
return;
}
var newValue = this.getValue(node.scope); var newValue = this.getValue(node.scope);
newValue = getWatcherValue(this, newValue); newValue = getWatcherValue(this, newValue);
if (this.value !== newValue) { if (this.value !== newValue) {
...@@ -1028,42 +1198,69 @@ Expression.prototype = { ...@@ -1028,42 +1198,69 @@ Expression.prototype = {
} }
}, },
getValue: function(scope, getFunction, getParams) { getValue: function(scope, getFunction, getParams) {
return getValue(scope, this.pattern, this.path, this.params, getFunction, getParams); var node = this.node;
if (!node && this.attribute) {
node = this.attribute.node;
}
var context = {};
if (node) {
context[vars.element] = node.element;
if (node.element) {
context[vars.parentElement] = node.element.parentNode;
}
} }
}; context[vars.attribute] = this.attribute;
context[vars.scope] = scope;
return getValue(scope, this.pattern, this.path, this.params, getFunction, getParams, undefined, context);
}
};
var templates = new HashMap(); var templates = new HashMap('st');
var Template = function(element) { var Template = function(element) {
this.watchers = new HashMap(); this.watchers = new HashMap('stw');
this.node = null; this.node = null;
this.scope = null; this.scope = null;
this.compile(element); this.compile(element);
}; };
Template.prototype = { Template.prototype = {
toString: function() { toString: function() {
return '[object Template]'; return '[object Template]';
}, },
compile: function(element) { compile: function(element) {
if (element) this.element = element; if (element) {
if (this.node) this.node.dispose(); this.element = element;
}
if (this.node) {
this.node.dispose();
}
this.node = compile(this, this.element); this.node = compile(this, this.element);
this.node.root = true; this.node.root = true;
this.scope = this.node.scope; this.scope = this.node.scope;
}, },
update: function(data) { update: function(data) {
if (isDefined(data)) updateScopeWithData(this.node.scope, data); if (isDefined(data)) {
if (this.node) this.node.update(); updateScopeWithData(this.node.scope, data);
}
if (this.node) {
this.node.update();
}
}, },
render: function(data) { render: function(data) {
this.update(data); this.update(data);
if (this.node) this.node.render(); if (this.node) {
this.node.render();
}
}, },
invalidate: function() { invalidate: function() {
if (this.node) this.node.invalidateData(); if (this.node) {
this.node.invalidateData();
}
}, },
watch: function(target, watcher) { watch: function(target, watcher) {
if ( (!isString(target) && !isElement(target)) || !isFunction(watcher)) return; if ( (!isString(target) && !isElement(target)) || !isFunction(watcher)) {
return;
}
this.watchers.put(target, watcher); this.watchers.put(target, watcher);
}, },
unwatch: function(target) { unwatch: function(target) {
...@@ -1090,47 +1287,36 @@ Template.prototype = { ...@@ -1090,47 +1287,36 @@ Template.prototype = {
this.watchers = null; this.watchers = null;
this.node = null; this.node = null;
} }
}; };
// written by Dean Edwards, 2005 // written by Dean Edwards, 2005
// with input from Tino Zijdel, Matthias Miller, Diego Perini // with input from Tino Zijdel, Matthias Miller, Diego Perini
// http://dean.edwards.name/weblog/2005/10/add-event/ // http://dean.edwards.name/weblog/2005/10/add-event/
function addEvent(element, type, handler) { function addEvent(element, type, handler) {
if (element.addEventListener) { if (element.addEventListener) {
element.addEventListener(type, handler, false); element.addEventListener(type, handler, false);
} else { } else {
// assign each event handler a unique ID // assign each event handler a unique ID
if (!handler.$$guid) handler.$$guid = addEvent.guid++; if (!handler.$$guid) {
handler.$$guid = addEvent.guid++;
}
// create a hash table of event types for the element // create a hash table of event types for the element
if (!element.events) element.events = {}; if (!element.events) {
element.events = {};
}
// create a hash table of event handlers for each element/event pair // create a hash table of event handlers for each element/event pair
var handlers = element.events[type]; var handlers = element.events[type];
if (!handlers) { if (!handlers) {
handlers = element.events[type] = {}; handlers = element.events[type] = {};
// store the existing event handler (if there is one) // store the existing event handler (if there is one)
if (element["on" + type]) { if (element['on' + type]) {
handlers[0] = element["on" + type]; handlers[0] = element['on' + type];
} }
} }
// store the event handler in the hash table // store the event handler in the hash table
handlers[handler.$$guid] = handler; handlers[handler.$$guid] = handler;
// assign a global event handler to do all the work // assign a global event handler to do all the work
element["on" + type] = handleEvent; element['on' + type] = function(event) {
}
};
// a counter used to create unique IDs
addEvent.guid = 1;
function removeEvent(element, type, handler) {
if (element.removeEventListener) {
element.removeEventListener(type, handler, false);
} else {
// delete the event handler from the hash table
if (element.events && element.events[type]) {
delete element.events[type][handler.$$guid];
}
}
};
function handleEvent(event) {
var returnValue = true; var returnValue = true;
// grab the event object (IE uses a global event object) // grab the event object (IE uses a global event object)
event = event || fixEvent(((this.ownerDocument || this.document || this).parentWindow || window).event); event = event || fixEvent(((this.ownerDocument || this.document || this).parentWindow || window).event);
...@@ -1138,38 +1324,60 @@ function handleEvent(event) { ...@@ -1138,38 +1324,60 @@ function handleEvent(event) {
var handlers = this.events[event.type]; var handlers = this.events[event.type];
// execute each event handler // execute each event handler
for (var i in handlers) { for (var i in handlers) {
if (handlers.hasOwnProperty(i)) {
this.$$handleEvent = handlers[i]; this.$$handleEvent = handlers[i];
if (this.$$handleEvent(event) === false) { if (this.$$handleEvent(event) === false) {
returnValue = false; returnValue = false;
} }
} }
}
return returnValue; return returnValue;
}; };
function fixEvent(event) { }
}
// a counter used to create unique IDs
addEvent.guid = 1;
function removeEvent(element, type, handler) {
if (element.removeEventListener) {
element.removeEventListener(type, handler, false);
} else {
// delete the event handler from the hash table
if (element.events && element.events[type]) {
delete element.events[type][handler.$$guid];
}
}
}
function fixEvent(event) {
// add W3C standard event methods // add W3C standard event methods
event.preventDefault = fixEvent.preventDefault; event.preventDefault = fixEvent.preventDefault;
event.stopPropagation = fixEvent.stopPropagation; event.stopPropagation = fixEvent.stopPropagation;
return event; return event;
}; }
fixEvent.preventDefault = function() { fixEvent.preventDefault = function() {
this.returnValue = false; this.returnValue = false;
}; };
fixEvent.stopPropagation = function() { fixEvent.stopPropagation = function() {
this.cancelBubble = true; this.cancelBubble = true;
}; };
var maxDepth; var maxDepth;
var eventStore = []; var eventStore = [];
function parseEvents(element, object, depth) { function parseEvents(element, object, depth) {
maxDepth = depth === undefined ? Number.MAX_VALUE : depth; maxDepth = depth === undefined ? Number.MAX_VALUE : depth;
parseNode(element, object, 0, true); parseNode(element, object, 0, true);
} }
function parseNode(element, object, depth, isRoot) { function parseNode(element, object, depth, isRoot) {
if (!isElement(element)) throw new Error('Error in soma.template.parseEvents, only a DOM Element can be parsed.'); if (!isElement(element)) {
if (isRoot) parseAttributes(element, object); throw new Error('Error in soma.template.parseEvents, only a DOM Element can be parsed.');
if (maxDepth === 0) return; }
if (isRoot) {
parseAttributes(element, object);
}
if (maxDepth === 0) {
return;
}
var child = element.firstChild; var child = element.firstChild;
while (child) { while (child) {
if (child.nodeType === 1) { if (child.nodeType === 1) {
...@@ -1180,17 +1388,16 @@ function parseNode(element, object, depth, isRoot) { ...@@ -1180,17 +1388,16 @@ function parseNode(element, object, depth, isRoot) {
} }
child = child.nextSibling; child = child.nextSibling;
} }
} }
function parseAttributes(element, object) { function parseAttributes(element, object) {
var attributes = [];
for (var attr, name, value, attrs = element.attributes, j = 0, jj = attrs && attrs.length; j < jj; j++) { for (var attr, name, value, attrs = element.attributes, j = 0, jj = attrs && attrs.length; j < jj; j++) {
attr = attrs[j]; attr = attrs[j];
if (attr.specified) { if (attr.specified) {
name = attr.name; name = attr.name;
value = attr.value; value = attr.value;
if (events[name]) { if (events[name]) {
var handler = getHandlerFromPattern(object, value, element); var handler = getHandlerFromPattern(object, value);
if (handler && isFunction(handler)) { if (handler && isFunction(handler)) {
addEvent(element, events[name], handler); addEvent(element, events[name], handler);
eventStore.push({element:element, type:events[name], handler:handler}); eventStore.push({element:element, type:events[name], handler:handler});
...@@ -1198,9 +1405,9 @@ function parseAttributes(element, object) { ...@@ -1198,9 +1405,9 @@ function parseAttributes(element, object) {
} }
} }
} }
} }
function getHandlerFromPattern(object, pattern, child) { function getHandlerFromPattern(object, pattern) {
var parts = pattern.match(regex.func); var parts = pattern.match(regex.func);
if (parts) { if (parts) {
var func = parts[1]; var func = parts[1];
...@@ -1208,9 +1415,9 @@ function getHandlerFromPattern(object, pattern, child) { ...@@ -1208,9 +1415,9 @@ function getHandlerFromPattern(object, pattern, child) {
return object[func]; return object[func];
} }
} }
} }
function clearEvents(element) { function clearEvents(element) {
var i = eventStore.length, l = 0; var i = eventStore.length, l = 0;
while (--i >= l) { while (--i >= l) {
var item = eventStore[i]; var item = eventStore[i];
...@@ -1219,12 +1426,16 @@ function clearEvents(element) { ...@@ -1219,12 +1426,16 @@ function clearEvents(element) {
eventStore.splice(i, 1); eventStore.splice(i, 1);
} }
} }
} }
if (settings.autocreate && typeof document === 'object') {
var ready;
if (typeof document === 'object') {
// https://github.com/ded/domready // https://github.com/ded/domready
var ready=function(){function l(b){for(k=1;b=a.shift();)b()}var b,a=[],c=!1,d=document,e=d.documentElement,f=e.doScroll,g="DOMContentLoaded",h="addEventListener",i="onreadystatechange",j="readyState",k=/^loade|c/.test(d[j]);return d[h]&&d[h](g,b=function(){d.removeEventListener(g,b,c),l()},c),f&&d.attachEvent(i,b=function(){/^c/.test(d[j])&&(d.detachEvent(i,b),l())}),f?function(b){self!=top?k?b():a.push(b):function(){try{e.doScroll("left")}catch(a){return setTimeout(function(){ready(b)},50)}b()}()}:function(b){k?b():a.push(b)}}(); var ready=function(){function l(b){for(k=1;b=a.shift();)b()}var b,a=[],c=!1,d=document,e=d.documentElement,f=e.doScroll,g="DOMContentLoaded",h="addEventListener",i="onreadystatechange",j="readyState",k=/^loade|c/.test(d[j]);return d[h]&&d[h](g,b=function(){d.removeEventListener(g,b,c),l()},c),f&&d.attachEvent(i,b=function(){/^c/.test(d[j])&&(d.detachEvent(i,b),l())}),f?function(b){self!=top?k?b():a.push(b):function(){try{e.doScroll("left")}catch(a){return setTimeout(function(){ready(b)},50)}b()}()}:function(b){k?b():a.push(b)}}();
if (settings.autocreate) {
var parse = function(element) { var parse = function(element) {
var child = !element ? document.body : element.firstChild; var child = !element ? document.body : element.firstChild;
while (child) { while (child) {
...@@ -1238,19 +1449,21 @@ if (settings.autocreate && typeof document === 'object') { ...@@ -1238,19 +1449,21 @@ if (settings.autocreate && typeof document === 'object') {
if (isFunction(f)) { if (isFunction(f)) {
soma.template.bootstrap(attrValue, child, f); soma.template.bootstrap(attrValue, child, f);
} }
} catch(err){}; } catch(err){}
} }
} }
child = child.nextSibling; child = child.nextSibling;
} }
}; };
ready(parse); ready(parse);
} }
function bootstrapTemplate(attrValue, element, func) { }
function bootstrapTemplate(attrValue, element, func) {
var tpl = createTemplate(element); var tpl = createTemplate(element);
func(tpl, tpl.scope, tpl.element, tpl.node); func(tpl, tpl.scope, tpl.element, tpl.node);
} }
function createTemplate(source, target) {
function createTemplate(source, target) {
var element; var element;
if (isString(source)) { if (isString(source)) {
// string template // string template
...@@ -1283,19 +1496,22 @@ function createTemplate(source, target) { ...@@ -1283,19 +1496,22 @@ function createTemplate(source, target) {
var template = new Template(element); var template = new Template(element);
templates.put(element, template); templates.put(element, template);
return template; return template;
} }
function getTemplate(element) { function getTemplate(element) {
return templates.get(element); return templates.get(element);
} }
function renderAllTemplates() { function renderAllTemplates() {
var data = templates.getData();
for (var key in templates.getData()) { for (var key in templates.getData()) {
if (data.hasOwnProperty(key)) {
templates.get(key).render(); templates.get(key).render();
} }
} }
}
function appendHelpers(obj) { function appendHelpers(obj) {
if (obj === null) { if (obj === null) {
helpersObject = {}; helpersObject = {};
helpersScopeObject = {}; helpersScopeObject = {};
...@@ -1308,20 +1524,20 @@ function appendHelpers(obj) { ...@@ -1308,20 +1524,20 @@ function appendHelpers(obj) {
} }
} }
return helpersObject; return helpersObject;
} }
// set regex // set regex
tokens.start(tokenStart); tokens.start(tokenStart);
tokens.end(tokenEnd); tokens.end(tokenEnd);
// plugins // plugins
soma.plugins = soma.plugins || {}; soma.plugins = soma.plugins || {};
var TemplatePlugin = function(instance, injector) { var TemplatePlugin = function(instance, injector) {
instance.constructor.prototype.createTemplate = function(cl, domElement) { instance.constructor.prototype.createTemplate = function(cl, domElement) {
if (!cl || typeof cl !== "function") { if (!cl || typeof cl !== 'function') {
throw new Error("Error creating a template, the first parameter must be a function."); throw new Error('Error creating a template, the first parameter must be a function.');
} }
if (domElement && isElement(domElement)) { if (domElement && isElement(domElement)) {
var template = soma.template.create(domElement); var template = soma.template.create(domElement);
...@@ -1332,46 +1548,46 @@ var TemplatePlugin = function(instance, injector) { ...@@ -1332,46 +1548,46 @@ var TemplatePlugin = function(instance, injector) {
} }
cl.prototype.render = template.render.bind(template); cl.prototype.render = template.render.bind(template);
var childInjector = injector.createChild(); var childInjector = injector.createChild();
childInjector.mapValue("template", template); childInjector.mapValue('template', template);
childInjector.mapValue("scope", template.scope); childInjector.mapValue('scope', template.scope);
childInjector.mapValue("element", template.element); childInjector.mapValue('element', template.element);
return childInjector.createInstance(cl); return childInjector.createInstance(cl);
} }
return null; return null;
} };
soma.template.bootstrap = function(attrValue, element, func) { soma.template.bootstrap = function(attrValue, element, func) {
instance.createTemplate(func, element); instance.createTemplate(func, element);
} };
} };
if (soma.plugins && soma.plugins.add) { if (soma.plugins && soma.plugins.add) {
soma.plugins.add(TemplatePlugin); soma.plugins.add(TemplatePlugin);
}
soma.template.Plugin = TemplatePlugin;
// exports
soma.template.create = createTemplate;
soma.template.get = getTemplate;
soma.template.renderAll = renderAllTemplates;
soma.template.helpers = appendHelpers;
soma.template.bootstrap = bootstrapTemplate;
soma.template.addEvent = addEvent;
soma.template.removeEvent = removeEvent;
soma.template.parseEvents = parseEvents;
soma.template.clearEvents = clearEvents;
soma.template.ready = ready;
// register for AMD module
if (typeof define === 'function' && define.amd) {
define("soma-template", soma.template);
}
// export for node.js
if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
exports = module.exports = soma.template;
} }
soma.template.Plugin = TemplatePlugin;
// exports
soma.template.create = createTemplate;
soma.template.get = getTemplate;
soma.template.renderAll = renderAllTemplates;
soma.template.helpers = appendHelpers;
soma.template.bootstrap = bootstrapTemplate;
soma.template.addEvent = addEvent;
soma.template.removeEvent = removeEvent;
soma.template.parseEvents = parseEvents;
soma.template.clearEvents = clearEvents;
soma.template.ready = ready;
// register for AMD module
if (typeof define === 'function' && typeof define.amd !== 'undefined') {
define('soma-template', soma.template);
}
// export for node.js
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
module.exports = soma.template;
}
if (typeof exports !== 'undefined') {
exports = soma.template; exports = soma.template;
} }
})(this['soma'] = this['soma'] || {}); })(this['soma'] = this['soma'] || {});
\ No newline at end of file
/* /*
Copyright (c) | 2012 | infuse.js | Romuald Quantin | www.soundstep.com | romu@soundstep.com Copyright (c) | 2013 | infuse.js | Romuald Quantin | www.soundstep.com | romu@soundstep.com
Permission is hereby granted, free of charge, to any person obtaining a copy of this software Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without restriction, and associated documentation files (the "Software"), to deal in the Software without restriction,
...@@ -17,10 +17,11 @@ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN ...@@ -17,10 +17,11 @@ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
;(function(infuse, undefined) { (function(infuse) {
"use strict";
infuse.version = "0.6.3"; 'use strict';
infuse.version = '0.7.0';
// regex from angular JS (https://github.com/angular/angular.js) // regex from angular JS (https://github.com/angular/angular.js)
var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m; var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
...@@ -28,26 +29,26 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -28,26 +29,26 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/; var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
if(!Array.prototype.contains) { function contains(arr, value) {
Array.prototype.contains = function(value) { var i = arr.length;
var i = this.length;
while (i--) { while (i--) {
if (this[i] === value) return true; if (arr[i] === value) {
return true;
}
} }
return false; return false;
};
} }
infuse.InjectorError = { infuse.InjectorError = {
MAPPING_BAD_PROP: "[Error infuse.Injector.mapClass/mapValue] the first parameter is invalid, a string is expected", MAPPING_BAD_PROP: '[Error infuse.Injector.mapClass/mapValue] the first parameter is invalid, a string is expected',
MAPPING_BAD_VALUE: "[Error infuse.Injector.mapClass/mapValue] the second parameter is invalid, it can't null or undefined, with property: ", MAPPING_BAD_VALUE: '[Error infuse.Injector.mapClass/mapValue] the second parameter is invalid, it can\'t null or undefined, with property: ',
MAPPING_BAD_CLASS: "[Error infuse.Injector.mapClass/mapValue] the second parameter is invalid, a function is expected, with property: ", MAPPING_BAD_CLASS: '[Error infuse.Injector.mapClass/mapValue] the second parameter is invalid, a function is expected, with property: ',
MAPPING_BAD_SINGLETON: "[Error infuse.Injector.mapClass] the third parameter is invalid, a boolean is expected, with property: ", MAPPING_BAD_SINGLETON: '[Error infuse.Injector.mapClass] the third parameter is invalid, a boolean is expected, with property: ',
MAPPING_ALREADY_EXISTS: "[Error infuse.Injector.mapClass/mapValue] this mapping already exists, with property: ", MAPPING_ALREADY_EXISTS: '[Error infuse.Injector.mapClass/mapValue] this mapping already exists, with property: ',
CREATE_INSTANCE_INVALID_PARAM: "[Error infuse.Injector.createInstance] invalid parameter, a function is expected", CREATE_INSTANCE_INVALID_PARAM: '[Error infuse.Injector.createInstance] invalid parameter, a function is expected',
NO_MAPPING_FOUND: "[Error infuse.Injector.getInstance] no mapping found", NO_MAPPING_FOUND: '[Error infuse.Injector.getInstance] no mapping found',
INJECT_INSTANCE_IN_ITSELF_PROPERTY: "[Error infuse.Injector.getInjectedValue] A matching property has been found in the target, you can't inject an instance in itself", INJECT_INSTANCE_IN_ITSELF_PROPERTY: '[Error infuse.Injector.getInjectedValue] A matching property has been found in the target, you can\'t inject an instance in itself',
INJECT_INSTANCE_IN_ITSELF_CONSTRUCTOR: "[Error infuse.Injector.getInjectedValue] A matching constructor parameter has been found in the target, you can't inject an instance in itself" INJECT_INSTANCE_IN_ITSELF_CONSTRUCTOR: '[Error infuse.Injector.getInjectedValue] A matching constructor parameter has been found in the target, you can\'t inject an instance in itself'
}; };
var MappingVO = function(prop, value, cl, singleton) { var MappingVO = function(prop, value, cl, singleton) {
...@@ -58,7 +59,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -58,7 +59,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
}; };
var validateProp = function(prop) { var validateProp = function(prop) {
if (typeof prop !== "string") { if (typeof prop !== 'string') {
throw new Error(infuse.InjectorError.MAPPING_BAD_PROP); throw new Error(infuse.InjectorError.MAPPING_BAD_PROP);
} }
}; };
...@@ -70,20 +71,20 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -70,20 +71,20 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
}; };
var validateClass = function(prop, val) { var validateClass = function(prop, val) {
if (typeof val !== "function") { if (typeof val !== 'function') {
throw new Error(infuse.InjectorError.MAPPING_BAD_CLASS + prop); throw new Error(infuse.InjectorError.MAPPING_BAD_CLASS + prop);
} }
}; };
var validateBooleanSingleton = function(prop, singleton) { var validateBooleanSingleton = function(prop, singleton) {
if (typeof singleton !== "boolean") { if (typeof singleton !== 'boolean') {
throw new Error(infuse.InjectorError.MAPPING_BAD_SINGLETON + prop); throw new Error(infuse.InjectorError.MAPPING_BAD_SINGLETON + prop);
} }
}; };
var validateConstructorInjectionLoop = function(name, cl) { var validateConstructorInjectionLoop = function(name, cl) {
var params = infuse.getConstructorParams(cl); var params = infuse.getConstructorParams(cl);
if (params.contains(name)) { if (contains(params, name)) {
throw new Error(infuse.InjectorError.INJECT_INSTANCE_IN_ITSELF_CONSTRUCTOR); throw new Error(infuse.InjectorError.INJECT_INSTANCE_IN_ITSELF_CONSTRUCTOR);
} }
}; };
...@@ -94,33 +95,29 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -94,33 +95,29 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
} }
}; };
var instantiateIgnoringConstructor = function() {
if (typeof arguments[0] !== "function") {
throw new Error(infuse.InjectorError.CREATE_INSTANCE_INVALID_PARAM);
}
var TargetClass = arguments[0];
var args = [null];
for (var i=1; i<arguments.length; i++) {
args.push(arguments[i]);
}
return new (Function.prototype.bind.apply(TargetClass, args));
};
infuse.Injector = function() { infuse.Injector = function() {
this.mappings = {}; this.mappings = {};
this.parent = null; this.parent = null;
}; };
infuse.getConstructorParams = function(cl) { infuse.getConstructorParams = function(cl) {
var args = []; var args = [],
inject;
function extractName(all, underscore, name) {
args.push(name);
}
// Override arg name with inject array values if present
if( cl.hasOwnProperty('inject') && toString.call(cl.inject) === '[object Array]' && cl.inject.length > 0)
inject = cl.inject;
var clStr = cl.toString().replace(STRIP_COMMENTS, ''); var clStr = cl.toString().replace(STRIP_COMMENTS, '');
var argsFlat = clStr.match(FN_ARGS); var argsFlat = clStr.match(FN_ARGS);
var spl = argsFlat[1].split(FN_ARG_SPLIT); var spl = argsFlat[1].split(FN_ARG_SPLIT);
for (var i=0; i<spl.length; i++) { for (var i=0; i<spl.length; i++) {
var arg = spl[i]; // Only override arg with non-falsey inject value at same key
arg.replace(FN_ARG, function(all, underscore, name){ var arg = (inject && inject[i]) ? inject[i] : spl[i];
args.push(name); arg.replace(FN_ARG, extractName);
});
} }
return args; return args;
}; };
...@@ -134,9 +131,15 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -134,9 +131,15 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
}, },
getMappingVo: function(prop) { getMappingVo: function(prop) {
if (!this.mappings) return null; if (!this.mappings) {
if (this.mappings[prop]) return this.mappings[prop]; return null;
if (this.parent) return this.parent.getMappingVo(prop); }
if (this.mappings[prop]) {
return this.mappings[prop];
}
if (this.parent) {
return this.parent.getMappingVo(prop);
}
return null; return null;
}, },
...@@ -146,7 +149,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -146,7 +149,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
} }
validateProp(prop); validateProp(prop);
validateValue(prop, val); validateValue(prop, val);
this.mappings[prop] = new MappingVO(prop, val); this.mappings[prop] = new MappingVO(prop, val, undefined, undefined);
return this; return this;
}, },
...@@ -156,7 +159,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -156,7 +159,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
} }
validateProp(prop); validateProp(prop);
validateClass(prop, cl); validateClass(prop, cl);
if (singleton) validateBooleanSingleton(prop, singleton); if (singleton) {
validateBooleanSingleton(prop, singleton);
}
this.mappings[prop] = new MappingVO(prop, null, cl, singleton); this.mappings[prop] = new MappingVO(prop, null, cl, singleton);
return this; return this;
}, },
...@@ -177,22 +182,38 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -177,22 +182,38 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
getMapping: function(value) { getMapping: function(value) {
for (var name in this.mappings) { for (var name in this.mappings) {
if (this.mappings.hasOwnProperty(name)) {
var vo = this.mappings[name]; var vo = this.mappings[name];
if (vo.value === value || vo.cl === value) { if (vo.value === value || vo.cl === value) {
return vo.prop; return vo.prop;
} }
} }
}
return undefined;
}, },
getValue: function(prop) { getValue: function(prop) {
var vo = this.mappings[prop]; var vo = this.mappings[prop];
if (!vo) { if (!vo) {
if (this.parent) return this.parent.getValue.apply(this.parent, arguments); if (this.parent) {
else throw new Error(infuse.InjectorError.NO_MAPPING_FOUND); return this.parent.getValue.apply(this.parent, arguments);
}
else {
throw new Error(infuse.InjectorError.NO_MAPPING_FOUND);
}
} }
if (vo.cl) { if (vo.cl) {
arguments[0] = vo.cl; var args = Array.prototype.slice.call(arguments);
return this.getValueFromClass.apply(this, arguments); args[0] = vo.cl;
if (vo.singleton) {
if (!vo.value) {
vo.value = this.createInstance.apply(this, args);
}
return vo.value;
}
else {
return this.createInstance.apply(this, args);
}
} }
return vo.value; return vo.value;
}, },
...@@ -200,20 +221,25 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -200,20 +221,25 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
getClass: function(prop) { getClass: function(prop) {
var vo = this.mappings[prop]; var vo = this.mappings[prop];
if (!vo) { if (!vo) {
if (this.parent) return this.parent.getClass(prop); if (this.parent) {
else return undefined; return this.parent.getClass(prop);
}
else {
return undefined;
}
}
if (vo.cl) {
return vo.cl;
} }
if (vo.cl) return vo.cl;
return undefined; return undefined;
}, },
instantiate: function(TargetClass) { instantiate: function(TargetClass) {
if (typeof TargetClass !== "function") { if (typeof TargetClass !== 'function') {
throw new Error(infuse.InjectorError.CREATE_INSTANCE_INVALID_PARAM); throw new Error(infuse.InjectorError.CREATE_INSTANCE_INVALID_PARAM);
} }
var TargetClass = arguments[0];
var args = [null]; var args = [null];
var params = infuse.getConstructorParams(TargetClass, this.mappings); var params = infuse.getConstructorParams(TargetClass);
for (var i=0; i<params.length; i++) { for (var i=0; i<params.length; i++) {
if (arguments[i+1] !== undefined && arguments[i+1] !== null) { if (arguments[i+1] !== undefined && arguments[i+1] !== null) {
// argument found // argument found
...@@ -234,7 +260,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -234,7 +260,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
} }
} }
} }
return new (Function.prototype.bind.apply(TargetClass, args)); return new (Function.prototype.bind.apply(TargetClass, args))();
}, },
inject: function (target, isParent) { inject: function (target, isParent) {
...@@ -242,10 +268,11 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -242,10 +268,11 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
this.parent.inject(target, true); this.parent.inject(target, true);
} }
for (var name in this.mappings) { for (var name in this.mappings) {
if (this.mappings.hasOwnProperty(name)) {
var vo = this.getMappingVo(name); var vo = this.getMappingVo(name);
if (target.hasOwnProperty(vo.prop)) { if (target.hasOwnProperty(vo.prop) || (target.constructor && target.constructor.prototype && target.constructor.prototype.hasOwnProperty(vo.prop)) ) {
var val = this.getInjectedValue(vo, name); target[name] = this.getInjectedValue(vo, name);
target[name] = val; }
} }
} }
if (typeof target.postConstruct === 'function' && !isParent) { if (typeof target.postConstruct === 'function' && !isParent) {
...@@ -258,7 +285,6 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -258,7 +285,6 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
var val = vo.value; var val = vo.value;
var injectee; var injectee;
if (vo.cl) { if (vo.cl) {
var params = infuse.getConstructorParams(vo.cl);
if (vo.singleton) { if (vo.singleton) {
if (!vo.value) { if (!vo.value) {
validateConstructorInjectionLoop(name, vo.cl); validateConstructorInjectionLoop(name, vo.cl);
...@@ -288,10 +314,13 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -288,10 +314,13 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
getValueFromClass: function(cl) { getValueFromClass: function(cl) {
for (var name in this.mappings) { for (var name in this.mappings) {
if (this.mappings.hasOwnProperty(name)) {
var vo = this.mappings[name]; var vo = this.mappings[name];
if (vo.cl == cl) { if (vo.cl === cl) {
if (vo.singleton) { if (vo.singleton) {
if (!vo.value) vo.value = this.createInstance.apply(this, arguments); if (!vo.value) {
vo.value = this.createInstance.apply(this, arguments);
}
return vo.value; return vo.value;
} }
else { else {
...@@ -299,6 +328,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -299,6 +328,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
} }
} }
} }
}
if (this.parent) { if (this.parent) {
return this.parent.getValueFromClass.apply(this.parent, arguments); return this.parent.getValueFromClass.apply(this.parent, arguments);
} else { } else {
...@@ -315,15 +345,15 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -315,15 +345,15 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
if (!Function.prototype.bind) { if (!Function.prototype.bind) {
Function.prototype.bind = function bind(that) { Function.prototype.bind = function bind(that) {
var target = this; var target = this;
if (typeof target != "function") { if (typeof target !== 'function') {
throw new Error("Error, you must bind a function."); throw new Error('Error, you must bind a function.');
} }
var args = Array.prototype.slice.call(arguments, 1); // for normal call var args = Array.prototype.slice.call(arguments, 1); // for normal call
var bound = function () { var bound = function () {
if (this instanceof bound) { if (this instanceof bound) {
var F = function(){}; var F = function(){};
F.prototype = target.prototype; F.prototype = target.prototype;
var self = new F; var self = new F();
var result = target.apply( var result = target.apply(
self, self,
args.concat(Array.prototype.slice.call(arguments)) args.concat(Array.prototype.slice.call(arguments))
...@@ -344,55 +374,54 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -344,55 +374,54 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
} }
// register for AMD module // register for AMD module
if (typeof define === 'function' && define.amd) { if (typeof define === 'function' && typeof define.amd !== 'undefined') {
define("infuse", infuse); define("infuse", infuse);
} }
// export for node.js // export for node.js
if (typeof exports !== 'undefined') { if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) { module.exports = infuse;
exports = module.exports = infuse;
} }
if (typeof exports !== 'undefined') {
exports = infuse; exports = infuse;
} }
})(this['infuse'] = this['infuse'] || {}); })(this['infuse'] = this['infuse'] || {});
/* /*
Copyright (c) | 2012 | soma-events | Romuald Quantin | www.soundstep.com Copyright (c) | 2013 | soma-events | Romuald Quantin | www.soundstep.com
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following conditions: is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
(function (soma) {
;(function (soma, undefined) { 'use strict';
"use strict";
soma.events = {}; soma.events = {};
soma.events.version = "0.5.2"; soma.events.version = '0.5.6';
if (!Function.prototype.bind) { if (!Function.prototype.bind) {
Function.prototype.bind = function bind(that) { Function.prototype.bind = function bind(that) {
var target = this; var target = this;
if (typeof target != "function") { if (typeof target !== 'function') {
throw new Error("Error, you must bind a function."); throw new Error('Error, you must bind a function.');
} }
var args = Array.prototype.slice.call(arguments, 1); // for normal call var args = Array.prototype.slice.call(arguments, 1); // for normal call
var bound = function () { var bound = function () {
if (this instanceof bound) { if (this instanceof bound) {
var F = function(){}; var F = function(){};
F.prototype = target.prototype; F.prototype = target.prototype;
var self = new F; var self = new F();
var result = target.apply( var result = target.apply(
self, self,
args.concat(Array.prototype.slice.call(arguments)) args.concat(Array.prototype.slice.call(arguments))
...@@ -410,7 +439,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -410,7 +439,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
}; };
return bound; return bound;
}; };
}; }
soma.Event = function (type, params, bubbles, cancelable) { soma.Event = function (type, params, bubbles, cancelable) {
var e = soma.Event.createGenericEvent(type, bubbles, cancelable); var e = soma.Event.createGenericEvent(type, bubbles, cancelable);
...@@ -419,12 +448,14 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -419,12 +448,14 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
} }
e.isCloned = false; e.isCloned = false;
e.clone = this.clone.bind(e); e.clone = this.clone.bind(e);
e.isIE9 = this.isIE9; e.isIE9orIE10 = this.isIE9orIE10;
e.isDefaultPrevented = this.isDefaultPrevented; e.isDefaultPrevented = this.isDefaultPrevented;
if (this.isIE9() || !e.preventDefault || (e.getDefaultPrevented === undefined && e.defaultPrevented === undefined )) { if (this.isIE9orIE10() || !e.preventDefault || (e.getDefaultPrevented === undefined && e.defaultPrevented === undefined )) {
e.preventDefault = this.preventDefault.bind(e); e.preventDefault = this.preventDefault.bind(e);
} }
if (this.isIE9()) e.IE9PreventDefault = false; if (this.isIE9orIE10()) {
e.IE9or10PreventDefault = false;
}
return e; return e;
}; };
...@@ -434,22 +465,32 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -434,22 +465,32 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
e.isCloned = true; e.isCloned = true;
e.clone = this.clone; e.clone = this.clone;
e.isDefaultPrevented = this.isDefaultPrevented; e.isDefaultPrevented = this.isDefaultPrevented;
e.isIE9 = this.isIE9; e.isIE9orIE10 = this.isIE9orIE10;
if (this.isIE9()) e.IE9PreventDefault = this.IE9PreventDefault; if (this.isIE9orIE10()) {
e.IE9or10PreventDefault = this.IE9or10PreventDefault;
}
return e; return e;
}; };
soma.Event.prototype.preventDefault = function () { soma.Event.prototype.preventDefault = function () {
if (!this.cancelable) return false; if (!this.cancelable) {
return false;
}
if (this.isIE9orIE10()) {
this.IE9or10PreventDefault = true;
}
else {
this.defaultPrevented = true; this.defaultPrevented = true;
if (this.isIE9()) this.IE9PreventDefault = true; }
return this; return this;
}; };
soma.Event.prototype.isDefaultPrevented = function () { soma.Event.prototype.isDefaultPrevented = function () {
if (!this.cancelable) return false; if (!this.cancelable) {
if (this.isIE9()) { return false;
return this.IE9PreventDefault; }
if (this.isIE9orIE10()) {
return this.IE9or10PreventDefault;
} }
if (this.defaultPrevented !== undefined) { if (this.defaultPrevented !== undefined) {
return this.defaultPrevented; return this.defaultPrevented;
...@@ -462,10 +503,10 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -462,10 +503,10 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
soma.Event.createGenericEvent = function (type, bubbles, cancelable) { soma.Event.createGenericEvent = function (type, bubbles, cancelable) {
var event; var event;
bubbles = bubbles !== undefined ? bubbles : true; bubbles = bubbles !== undefined ? bubbles : true;
if (typeof document === "object" && document.createEvent) { if (typeof document === 'object' && document.createEvent) {
event = document.createEvent("Event"); event = document.createEvent('Event');
event.initEvent(type, !!bubbles, !!cancelable); event.initEvent(type, !!bubbles, !!cancelable);
} else if (typeof document === "object" && document.createEventObject) { } else if (typeof document === 'object' && document.createEventObject) {
event = document.createEventObject(); event = document.createEventObject();
event.type = type; event.type = type;
event.bubbles = !!bubbles; event.bubbles = !!bubbles;
...@@ -476,13 +517,15 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -476,13 +517,15 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
return event; return event;
}; };
soma.Event.prototype.isIE9 = function() { soma.Event.prototype.isIE9orIE10 = function() {
if (typeof document !== "object") return false; if (typeof document !== 'object') {
return document.body.style.scrollbar3dLightColor !== undefined && document.body.style.opacity !== undefined; return false;
}
return (document.body.style.scrollbar3dLightColor !== undefined && document.body.style.opacity !== undefined) || document.body.style.msTouchAction !== undefined;
}; };
soma.Event.prototype.toString = function() { soma.Event.prototype.toString = function() {
return "[soma.Event]"; return '[soma.Event]';
}; };
var EventObject = function(type, bubbles, cancelable) { var EventObject = function(type, bubbles, cancelable) {
...@@ -499,8 +542,12 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -499,8 +542,12 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
}; };
soma.EventDispatcher.prototype.addEventListener = function(type, listener, priority) { soma.EventDispatcher.prototype.addEventListener = function(type, listener, priority) {
if (!this.listeners || !type || !listener) return; if (!this.listeners || !type || !listener) {
if (isNaN(priority)) priority = 0; return;
}
if (isNaN(priority)) {
priority = 0;
}
for (var i=0; i<this.listeners.length; i++) { for (var i=0; i<this.listeners.length; i++) {
var eventObj = this.listeners[i]; var eventObj = this.listeners[i];
if (eventObj.type === type && eventObj.listener === listener) { if (eventObj.type === type && eventObj.listener === listener) {
...@@ -511,7 +558,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -511,7 +558,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
}; };
soma.EventDispatcher.prototype.removeEventListener = function(type, listener) { soma.EventDispatcher.prototype.removeEventListener = function(type, listener) {
if (!this.listeners || !type || !listener) return; if (!this.listeners || !type || !listener) {
return;
}
var i = this.listeners.length; var i = this.listeners.length;
while(i-- > 0) { while(i-- > 0) {
var eventObj = this.listeners[i]; var eventObj = this.listeners[i];
...@@ -522,7 +571,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -522,7 +571,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
}; };
soma.EventDispatcher.prototype.hasEventListener = function(type) { soma.EventDispatcher.prototype.hasEventListener = function(type) {
if (!this.listeners || !type) return false; if (!this.listeners || !type) {
return false;
}
var i = 0; var i = 0;
var l = this.listeners.length; var l = this.listeners.length;
for (; i < l; ++i) { for (; i < l; ++i) {
...@@ -535,7 +586,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -535,7 +586,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
}; };
soma.EventDispatcher.prototype.dispatchEvent = function(event) { soma.EventDispatcher.prototype.dispatchEvent = function(event) {
if (!this.listeners || !event) throw new Error("Error in EventDispatcher (dispatchEvent), one of the parameters is null or undefined."); if (!this.listeners || !event) {
throw new Error('Error in EventDispatcher (dispatchEvent), one of the parameters is null or undefined.');
}
var events = []; var events = [];
var i; var i;
for (i = 0; i < this.listeners.length; i++) { for (i = 0; i < this.listeners.length; i++) {
...@@ -554,7 +607,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -554,7 +607,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
}; };
soma.EventDispatcher.prototype.dispatch = function(type, params, bubbles, cancelable) { soma.EventDispatcher.prototype.dispatch = function(type, params, bubbles, cancelable) {
if (!this.listeners || !type || type === "") throw new Error("Error in EventDispatcher (dispatch), one of the parameters is null or undefined."); if (!this.listeners || !type || type === '') {
throw new Error('Error in EventDispatcher (dispatch), one of the parameters is null or undefined.');
}
var event = new soma.Event(type, params, bubbles, cancelable); var event = new soma.Event(type, params, bubbles, cancelable);
this.dispatchEvent(event); this.dispatchEvent(event);
return event; return event;
...@@ -565,30 +620,30 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -565,30 +620,30 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
}; };
soma.EventDispatcher.prototype.toString = function() { soma.EventDispatcher.prototype.toString = function() {
return "[soma.EventDispatcher]"; return '[soma.EventDispatcher]';
}; };
// register for AMD module // register for AMD module
if (typeof define === 'function' && define.amd) { if (typeof define === 'function' && typeof define.amd !== 'undefined') {
define("soma-events", soma); define("soma-events", soma);
}; }
// export for node.js // export for node.js
if (typeof exports !== 'undefined') { if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) { module.exports = soma;
exports = module.exports = soma;
} }
if (typeof exports !== 'undefined') {
exports = soma; exports = soma;
}; }
})(this['soma'] = this['soma'] || {}); })(this['soma'] = this['soma'] || {});
;(function (soma, infuse, undefined) { (function (soma, infuse) {
'use strict'; 'use strict';
soma.version = "2.0.0"; soma.version = '2.1.0';
soma.applyProperties = function(target, extension, bindToExtension, list) { soma.applyProperties = function(target, extension, bindToExtension, list) {
if (Object.prototype.toString.apply(list) === '[object Array]') { if (Object.prototype.toString.apply(list) === '[object Array]') {
...@@ -651,7 +706,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -651,7 +706,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Chain.prototype = parent.prototype; Chain.prototype = parent.prototype;
Subclass.prototype = new Chain(); Subclass.prototype = new Chain();
// add obj properties // add obj properties
if (obj) soma.applyProperties(Subclass.prototype, obj); if (obj) {
soma.applyProperties(Subclass.prototype, obj);
}
// point constructor to the Subclass // point constructor to the Subclass
Subclass.prototype.constructor = Subclass; Subclass.prototype.constructor = Subclass;
// set super class reference // set super class reference
...@@ -685,35 +742,38 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -685,35 +742,38 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// framework // framework
soma.Application = soma.extend({ soma.Application = soma.extend({
constructor: function() { constructor: function() {
setup.bind(this)();
this.init(); var self = this;
this.start();
function setup() { function setup() {
// injector // injector
this.injector = new infuse.Injector(this.dispatcher); self.injector = new infuse.Injector(self.dispatcher);
// dispatcher // dispatcher
this.dispatcher = new soma.EventDispatcher(); self.dispatcher = new soma.EventDispatcher();
// mapping // mapping
this.injector.mapValue('injector', this.injector); self.injector.mapValue('injector', self.injector);
this.injector.mapValue('instance', this); self.injector.mapValue('instance', self);
this.injector.mapValue('dispatcher', this.dispatcher); self.injector.mapValue('dispatcher', self.dispatcher);
// mediator // mediator
this.injector.mapClass('mediators', Mediators, true); self.injector.mapClass('mediators', Mediators, true);
this.mediators = this.injector.getValue('mediators'); self.mediators = self.injector.getValue('mediators');
// commands // commands
this.injector.mapClass('commands', Commands, true); self.injector.mapClass('commands', Commands, true);
this.commands = this.injector.getValue('commands'); self.commands = self.injector.getValue('commands');
// plugins // plugins
for (var i = 0, l = plugins.length; i < l; i++) { for (var i = 0, l = plugins.length; i < l; i++) {
this.createPlugin(plugins[i]); self.createPlugin(plugins[i]);
} }
} }
setup.bind(this)();
this.init();
this.start();
}, },
createPlugin: function() { createPlugin: function() {
if (arguments.length == 0 || !arguments[0]) { if (arguments.length === 0 || !arguments[0]) {
throw new Error("Error creating a plugin, plugin class is missing."); throw new Error('Error creating a plugin, plugin class is missing.');
} }
var params = infuse.getConstructorParams(arguments[0]); var params = infuse.getConstructorParams(arguments[0]);
var args = [arguments[0]]; var args = [arguments[0]];
...@@ -769,11 +829,11 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -769,11 +829,11 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
this.dispatcher = null; this.dispatcher = null;
}, },
create: function(cl, target) { create: function(cl, target) {
if (!cl || typeof cl !== "function") { if (!cl || typeof cl !== 'function') {
throw new Error("Error creating a mediator, the first parameter must be a function."); throw new Error('Error creating a mediator, the first parameter must be a function.');
} }
if (target === undefined || target === null) { if (target === undefined || target === null) {
throw new Error("Error creating a mediator, the second parameter cannot be undefined or null."); throw new Error('Error creating a mediator, the second parameter cannot be undefined or null.');
} }
var targets = []; var targets = [];
var meds = []; var meds = [];
...@@ -785,11 +845,11 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -785,11 +845,11 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
} }
for (var i= 0, l=targets.length; i<l; i++) { for (var i= 0, l=targets.length; i<l; i++) {
var injector = this.injector.createChild(); var injector = this.injector.createChild();
injector.mapValue("target", targets[i]); injector.mapValue('target', targets[i]);
//var mediator = injector.createInstance.apply(this.injector, params);
var mediator = injector.createInstance(cl); var mediator = injector.createInstance(cl);
// soma.applyProperties(mediator, this.dispatcher, true, ['dispatch', 'dispatchEvent', 'addEventListener', 'removeEventListener', 'hasEventListener']); if (targets.length === 1) {
if (targets.length === 1) return mediator; return mediator;
}
meds.push(mediator); meds.push(mediator);
} }
return meds; return meds;
...@@ -819,19 +879,21 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -819,19 +879,21 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
getAll: function() { getAll: function() {
var copy = {}; var copy = {};
for (var cmd in this.list) { for (var cmd in this.list) {
if (this.list.hasOwnProperty(cmd)) {
copy[cmd] = this.list[cmd]; copy[cmd] = this.list[cmd];
} }
}
return copy; return copy;
}, },
add: function(commandName, command) { add: function(commandName, command) {
if (typeof commandName !== 'string') { if (typeof commandName !== 'string') {
throw new Error("Error adding a command, the first parameter must be a string."); throw new Error('Error adding a command, the first parameter must be a string.');
} }
if (typeof command !== 'function') { if (typeof command !== 'function') {
throw new Error("Error adding a command with the name \"" + command + "\", the second parameter must be a function, and must contain an \"execute\" public method."); throw new Error('Error adding a command with the name "' + command + '", the second parameter must be a function, and must contain an "execute" public method.');
} }
if (this.has(commandName)) { if (this.has(commandName)) {
throw new Error("Error adding a command with the name: \"" + commandName + "\", already registered."); throw new Error('Error adding a command with the name: "' + commandName + '", already registered.');
} }
this.list[ commandName ] = command; this.list[ commandName ] = command;
this.addInterceptor(commandName); this.addInterceptor(commandName);
...@@ -860,16 +922,17 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -860,16 +922,17 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
if (this.has(commandName)) { if (this.has(commandName)) {
var command = this.injector.createInstance(this.list[commandName]); var command = this.injector.createInstance(this.list[commandName]);
if (!command.hasOwnProperty('execute') && command['execute'] === 'function') { if (!command.hasOwnProperty('execute') && command['execute'] === 'function') {
throw new Error("Error in " + this + " Command \"" + command + "\" must contain an execute public method."); throw new Error('Error in ' + this + ' Command ' + command + ' must contain an execute public method.');
} }
// soma.applyProperties(command, this.dispatcher, true, ['dispatch', 'dispatchEvent', 'addEventListener', 'removeEventListener', 'hasEventListener']);
command.execute(event); command.execute(event);
} }
}, },
dispose: function() { dispose: function() {
for (var cmd in this.list) { for (var cmd in this.list) {
if (this.list.hasOwnProperty(cmd)) {
this.remove(cmd); this.remove(cmd);
} }
}
this.boundHandler = undefined; this.boundHandler = undefined;
this.dispatcher = undefined; this.dispatcher = undefined;
this.injector = undefined; this.injector = undefined;
...@@ -893,16 +956,17 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -893,16 +956,17 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// register for AMD module // register for AMD module
if (typeof define === 'function' && define.amd) { /* globals define:false */
define("soma", soma); if (typeof define === 'function' && typeof define.amd !== 'undefined') {
define('soma', soma);
} }
// export for node.js // export for node.js
if (typeof exports !== 'undefined') { if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) { module.exports = soma;
exports = module.exports = soma;
} }
exports = soma; else {
window.soma = soma;
} }
})(this['soma'] = this['soma'] || {}, this['infuse']); })(this['soma'] = this['soma'] || {}, this['infuse']);
\ No newline at end of file
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
<label data-dblclick="edit(todo)">{{todo.title}}</label> <label data-dblclick="edit(todo)">{{todo.title}}</label>
<button class="destroy" data-click="remove(todo)"></button> <button class="destroy" data-click="remove(todo)"></button>
</div> </div>
<input class="edit" value="{{todo.title}}" data-keypress="update(todo)" data-blur="update(todo)"> <input class="edit" value="{{todo.title}}" data-keydown="update(todo)" data-blur="update(todo)">
</li> </li>
</ul> </ul>
</section> </section>
......
...@@ -8,12 +8,16 @@ ...@@ -8,12 +8,16 @@
var RouterModel = function (dispatcher) { var RouterModel = function (dispatcher) {
// create the router (director.js) // create the router (director.js)
var router = new Router().init(); var router = new Router().init().configure({
notfound: render
});
// dispatch a custom event to render the template on a route change // dispatch a custom event to render the template on a route change
router.on(/.*/, function () { router.on(/.*/, render);
function render() {
dispatcher.dispatch('render'); dispatcher.dispatch('render');
}); }
return { return {
getRoute: function () { getRoute: function () {
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
define([], function () { define([], function () {
var ENTER_KEY = 13; var ENTER_KEY = 13;
var ESCAPE_KEY = 27;
var MainView = function (scope, template, model, router, dispatcher) { var MainView = function (scope, template, model, router, dispatcher) {
...@@ -54,10 +55,14 @@ ...@@ -54,10 +55,14 @@
scope.edit = function (event, item) { scope.edit = function (event, item) {
item.editing = 'editing'; item.editing = 'editing';
template.render(); template.render();
template.element.querySelector('.edit').focus();
}; };
// template function: during edit mode, changes the value of an item after an enter key press // template function: during edit mode, changes the value of an item after an enter key press
scope.update = function (event, item) { scope.update = function (event, item) {
if (cancelEditing(event, item)) {
return;
}
var value = event.currentTarget.value.trim(); var value = event.currentTarget.value.trim();
if (event.which === ENTER_KEY || event.type === 'blur') { if (event.which === ENTER_KEY || event.type === 'blur') {
if (value) { if (value) {
...@@ -72,6 +77,19 @@ ...@@ -72,6 +77,19 @@
} }
}; };
// escape has been pressed, revert the value of the input
function cancelEditing(event, item) {
if (event.which === ESCAPE_KEY) {
event.currentTarget.value = item.title;
event.currentTarget.blur();
update();
return true;
}
else {
return false;
}
}
// save the changes to the model and dispatch a custom event to render the templates // save the changes to the model and dispatch a custom event to render the templates
function update() { function update() {
model.set(items); model.set(items);
......
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