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);
setRegEX(value, true); setRegEX(value, true);
}
return tokenStart;
},
end: function(value) {
if (isDefined(value) && value !== '') {
tokenEnd = escapeRegExp(value);
setRegEX(value, false);
}
return tokenEnd;
}
};
var attributes = settings.attributes = {
skip: "data-skip",
repeat: "data-repeat",
src: "data-src",
href: "data-href",
show: "data-show",
hide: "data-hide",
cloak: "data-cloak",
checked: "data-checked",
disabled: "data-disabled",
multiple: "data-multiple",
readonly: "data-readonly",
selected: "data-selected",
template: "data-template",
html: "data-html"
};
var vars = settings.vars = {
index: "$index",
key: "$key"
};
var events = settings.events = {};
settings.eventsPrefix = 'data-';
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];
}
var regex = {
sequence: null,
token: null,
expression: null,
escape: /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,
trim: /^[\s+]+|[\s+]+$/g,
repeat: /(.*)\s+in\s+(.*)/,
func: /(.*)\((.*)\)/,
params: /,\s+|,|\s+,\s+/,
quote: /\"|\'/g,
content: /[^.|^\s]/gm,
depth: /..\//g,
string: /^(\"|\')(.*)(\"|\')$/
};
var ie = (function(){
if (typeof document !== 'object') return undefined;
var undef,
v = 3,
div = document.createElement('div'),
all = div.getElementsByTagName('i');
while (
div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->', all[0]
);
return v > 4 ? v : undef;
}());
function isArray(value) {
return Object.prototype.toString.apply(value) === '[object Array]';
};
function isObject(value) {
return typeof value === 'object';
}
function isString(value) {
return typeof value === 'string';
}
function isElement(value) {
return value ? value.nodeType > 0 : false;
};
function isTextNode(el) {
return el && el.nodeType && el.nodeType === 3;
}
function isFunction(value) {
return value && typeof value === 'function';
}
function isDefined(value) {
return value !== null && value !== undefined;
}
function isAttributeDefined(value) {
return (value === "" || value === true || value === "true" || !isDefined(value));
}
function isExpression(value) {
return value && isFunction(value.toString) && value.toString() === '[object Expression]';
}
function isNode(value) {
return value && isFunction(value.toString) && value.toString() === '[object Node]';
}
function isExpFunction(value) {
if (!isString(value)) return false;
return !!value.match(regex.func);
}
function childNodeIsTemplate(node) {
return node && node.parent && templates.get(node.element);
}
function escapeRegExp(str) {
return str.replace(regex.escape, "\\$&");
}
function setRegEX(nonEscapedValue, isStartToken) {
// sequence: \{\{.+?\}\}|[^{]+|\{(?!\{)[^{]*
var unescapedCurrentStartToken = tokens.start().replace(/\\/g, '');
var endSequence = "";
var ts = isStartToken ? nonEscapedValue : unescapedCurrentStartToken;
if (ts.length > 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.token = new RegExp(tokens.start() + ".*?" + tokens.end(), "g");
regex.expression = new RegExp(tokens.start() + "|" + tokens.end(), "gm");
}
function trim(value) {
return value.replace(regex.trim, '');
}
function trimQuotes(value) {
if (regex.string.test(value)) {
return value.substr(1, value.length-2);
}
return value;
}
function trimArray(value) {
if (value[0] === "") value.shift();
if (value[value.length-1] === "") value.pop();
return value;
}
function trimTokens(value) {
return value.replace(regex.expression, '');
}
function trimScopeDepth(value) {
return value.replace(regex.depth, '');
}
function insertBefore(referenceNode, newNode) {
if (!referenceNode.parentNode) return;
referenceNode.parentNode.insertBefore(newNode, referenceNode);
}
function insertAfter(referenceNode, newNode) {
if (!referenceNode.parentNode) return;
referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
}
function removeClass(elm, className) {
if (document.documentElement.classList) {
removeClass = function (elm, className) {
elm.classList.remove(className);
}
} else {
removeClass = function (elm, className) {
if (!elm || !elm.className) {
return false;
} }
var reg = new RegExp("(^|\\s)" + className + "(\\s|$)", "g"); return tokenStart;
elm.className = elm.className.replace(reg, "$2"); },
end: function(value) {
if (isDefined(value) && value !== '') {
tokenEnd = escapeRegExp(value);
setRegEX(value, false);
}
return tokenEnd;
}
};
var attributes = settings.attributes = {
skip: 'data-skip',
repeat: 'data-repeat',
src: 'data-src',
href: 'data-href',
show: 'data-show',
hide: 'data-hide',
cloak: 'data-cloak',
checked: 'data-checked',
disabled: 'data-disabled',
multiple: 'data-multiple',
readonly: 'data-readonly',
selected: 'data-selected',
template: 'data-template',
html: 'data-html'
};
var vars = settings.vars = {
index: '$index',
key: '$key',
element: '$element',
parentElement: '$parentElement',
attribute: '$attribute',
scope: '$scope'
};
var events = settings.events = {};
settings.eventsPrefix = 'data-';
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];
}
var regex = {
sequence: null,
token: null,
expression: null,
escape: /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,
trim: /^[\s+]+|[\s+]+$/g,
repeat: /(.*)\s+in\s+(.*)/,
func: /(.*)\((.*)\)/,
params: /,\s+|,|\s+,\s+/,
quote: /\"|\'/g,
content: /[^.|^\s]/gm,
depth: /..\//g,
string: /^(\"|\')(.*)(\"|\')$/
};
var ie = (function(){
if (typeof document !== 'object') {
return undefined;
}
var v = 3,
stop = false,
div = document.createElement('div');
while (!stop) {
div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->';
if (!div.getElementsByTagName('i')[0]) {
stop = true;
}
}
return v > 4 ? v : undefined;
}());
function isArray(value) {
return Object.prototype.toString.apply(value) === '[object Array]';
}
function isObject(value) {
return typeof value === 'object';
}
function isString(value) {
return typeof value === 'string';
}
function isElement(value) {
return value ? value.nodeType > 0 : false;
}
function isTextNode(el) {
return el && el.nodeType && el.nodeType === 3;
}
function isFunction(value) {
return value && typeof value === 'function';
}
function isDefined(value) {
return value !== null && value !== undefined;
}
function normalizeBoolean(value) {
if (!isDefined(value)) {
return false;
}
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]';
}
function isExpFunction(value) {
if (!isString(value)) {
return false;
}
return !!value.match(regex.func);
}
function childNodeIsTemplate(node) {
return node && node.parent && templates.get(node.element);
}
function escapeRegExp(str) {
return str.replace(regex.escape, '\\$&');
} }
removeClass(elm, className); function setRegEX(nonEscapedValue, isStartToken) {
} // sequence: \{\{.+?\}\}|[^{]+|\{(?!\{)[^{]*
// jquery contains var unescapedCurrentStartToken = tokens.start().replace(/\\/g, '');
var contains = typeof document !== 'object' ? function(){} : document.documentElement.contains ? var endSequence = '';
function( a, b ) { var ts = isStartToken ? nonEscapedValue : unescapedCurrentStartToken;
var adown = a.nodeType === 9 ? a.documentElement : a, if (ts.length > 1) {
bup = b && b.parentNode; endSequence = '|\\' + ts.substr(0, 1) + '(?!\\' + ts.substr(1, 1) + ')[^' + ts.substr(0, 1) + ']*';
return a === bup || !!( bup && bup.nodeType === 1 && adown.contains && adown.contains(bup) ); }
} : regex.sequence = new RegExp(tokens.start() + '.+?' + tokens.end() + '|[^' + tokens.start() + ']+' + endSequence, 'g');
document.documentElement.compareDocumentPosition ? regex.token = new RegExp(tokens.start() + '.*?' + tokens.end(), 'g');
regex.expression = new RegExp(tokens.start() + '|' + tokens.end(), 'gm');
}
function trim(value) {
return value.replace(regex.trim, '');
}
function trimQuotes(value) {
if (regex.string.test(value)) {
return value.substr(1, value.length-2);
}
return value;
}
function trimArray(value) {
if (value[0] === '') {
value.shift();
}
if (value[value.length-1] === '') {
value.pop();
}
return value;
}
function trimTokens(value) {
return value.replace(regex.expression, '');
}
function trimScopeDepth(value) {
return value.replace(regex.depth, '');
}
function insertBefore(referenceNode, newNode) {
if (!referenceNode.parentNode) {
return;
}
referenceNode.parentNode.insertBefore(newNode, referenceNode);
}
function insertAfter(referenceNode, newNode) {
if (!referenceNode.parentNode) {
return;
}
referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
}
function removeClass(elm, className) {
var rmc;
if (typeof document === 'object' && document.documentElement.classList) {
rmc = function (elm, className) {
elm.classList.remove(className);
};
} else {
rmc = function (elm, className) {
if (!elm || !elm.className) {
return false;
}
var reg = new RegExp('(^|\\s)' + className + '(\\s|$)', 'g');
elm.className = elm.className.replace(reg, '$2');
};
}
rmc(elm, className);
}
// jquery contains
var contains = typeof document !== 'object' ? function(){} : document.documentElement.contains ?
function( a, b ) { function( a, b ) {
return b && !!( a.compareDocumentPosition( b ) & 16 ); var adown = a.nodeType === 9 ? a.documentElement : a,
bup = b && b.parentNode;
return a === bup || !!( bup && bup.nodeType === 1 && adown.contains && adown.contains(bup) );
} : } :
function( a, b ) { document.documentElement.compareDocumentPosition ?
while ( (b = b.parentNode) ) { function( a, b ) {
if ( b === a ) { return b && !!( a.compareDocumentPosition( b ) & 16 );
return true; } :
function( a, b ) {
while ( (b = b.parentNode) ) {
if ( b === a ) {
return true;
}
} }
return false;
};
function HashMap(id) {
var items = {};
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;}
function uuid() { return ++count + id; }
function getKey(target) {
if (!target) {
return;
} }
return false; if (typeof target !== 'object') {
return target;
}
var result;
try {
// IE 7-8 needs a try catch, seems like I can't add a property on text nodes
result = target[id] ? target[id] : target[id] = uuid();
} catch(err){}
return result;
}
this.remove = function(key) {
delete items[getKey(key)];
};
this.get = function(key) {
return items[getKey(key)];
};
this.put = function(key, value) {
items[getKey(key)] = value;
};
this.has = function(key) {
return typeof items[getKey(key)] !== 'undefined';
};
this.getData = function() {
return items;
}; };
this.dispose = function() {
for (var key in items) {
if (items.hasOwnProperty(key)) {
delete items[key];
}
}
this.length = 0;
};
}
function getRepeaterData(repeaterValue, scope) {
var parts = repeaterValue.match(regex.repeat);
if (!parts) {
return;
}
var source = parts[2];
var exp = new Expression(source);
return exp.getValue(scope);
}
function HashMap() { function updateScopeWithRepeaterData(repeaterValue, scope, data) {
var items = {}; var parts = repeaterValue.match(regex.repeat);
var id = 1; if (!parts) {
//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;} return;
function uuid() { return ++id; }; }
function getKey(target) { var name = parts[1];
if (!target) return; scope[name] = data;
if (typeof target !== 'object') return target; }
var result; function getWatcherValue(exp, newValue) {
try { var node = exp.node || exp.attribute.node;
// IE 7-8 needs a try catch, seems like I can't add a property on text nodes var watchers = node.template.watchers;
result = target.hashkey ? target.hashkey : target.hashkey = uuid(); var nodeTarget = node.element;
} catch(err){}; if (!watchers) {
return result; return newValue;
} }
this.remove = function(key) { var watcherNode = watchers.get(nodeTarget);
delete items[getKey(key)]; if (!watcherNode && isTextNode(node.element) && node.parent) {
} watcherNode = watchers.get(node.parent.element);
this.get = function(key) { }
return items[getKey(key)]; var watcher = watcherNode ? watcherNode : watchers.get(exp.pattern);
} if (isFunction(watcher)) {
this.put = function(key, value) { var watcherValue = watcher(exp.value, newValue, exp.pattern, node.scope, node, exp.attribute);
items[getKey(key)] = value; if (isDefined(watcherValue)) {
} return watcherValue;
this.has = function(key) {
return typeof items[getKey(key)] !== 'undefined';
}
this.getData = function() {
return items;
}
this.dispose = function() {
for (var key in items) {
delete items[key];
}
this.length = 0;
}
}
function getRepeaterData(repeaterValue, scope) {
var parts = repeaterValue.match(regex.repeat);
if (!parts) return;
var source = parts[2];
var exp = new Expression(source);
return exp.getValue(scope);
}
function updateScopeWithRepeaterData(repeaterValue, scope, data) {
var parts = repeaterValue.match(regex.repeat);
if (!parts) return;
var name = parts[1];
scope[name] = data;
}
function getWatcherValue(exp, newValue) {
var node = exp.node || exp.attribute.node;
var watchers = node.template.watchers;
var nodeTarget = node.element;
if (!watchers) return newValue;
var watcherNode = watchers.get(nodeTarget);
if (!watcherNode && isTextNode(node.element) && node.parent) watcherNode = watchers.get(node.parent.element);
var watcher = watcherNode ? watcherNode : watchers.get(exp.pattern);
if (isFunction(watcher)) {
var watcherValue = watcher(exp.value, newValue, exp.pattern, node.scope, node, exp.attribute);
if (isDefined(watcherValue)) {
return watcherValue;
}
}
return newValue;
}
function getScopeFromPattern(scope, pattern) {
var depth = getScopeDepth(pattern);
var scopeTarget = scope;
while (depth > 0) {
scopeTarget = scopeTarget._parent ? scopeTarget._parent : scopeTarget;
depth--;
}
return scopeTarget;
}
function getValueFromPattern(scope, pattern) {
var exp = new Expression(pattern);
return getValue(scope, exp.pattern, exp.path, exp.params);
}
function getValue(scope, pattern, pathString, params, getFunction, getParams, paramsFound) {
// string
if (regex.string.test(pattern)) {
return trimQuotes(pattern);
}
// find params
var paramsValues = [];
if (!paramsFound && params) {
for (var j = 0, jl = params.length; j < jl; j++) {
paramsValues.push(getValueFromPattern(scope, params[j]));
}
}
else paramsValues = paramsFound;
if (getParams) return paramsValues;
// find scope
var scopeTarget = getScopeFromPattern(scope, pattern);
// remove parent string
pattern = pattern.replace(/..\//g, '');
pathString = pathString.replace(/..\//g, '');
if (!scopeTarget) return undefined;
// search path
var path = scopeTarget;
var pathParts = pathString.split(/\.|\[|\]/g);
if (pathParts.length > 0) {
for (var i = 0, l = pathParts.length; i < l; i++) {
if (pathParts[i] !== "") {
path = path[pathParts[i]];
}
if (!isDefined(path)) {
// no path, search in parent
if (scopeTarget._parent) return getValue(scopeTarget._parent, pattern, pathString, params, getFunction, getParams, paramsValues);
else return undefined;
}
}
}
// return value
if (!isFunction(path)) {
return path;
}
else {
if (getFunction) return path;
else return path.apply(null, paramsValues);
}
return undefined;
}
function getExpressionPath(value) {
var val = value.split('(')[0];
val = trimScopeDepth(val);
return val;
}
function getParamsFromString(value) {
return trimArray(value.split(regex.params));
}
function getScopeDepth(value) {
var val = value.split('(')[0];
var matches = val.match(regex.depth);
return !matches ? 0 : matches.length;
}
function getNodeFromElement(element, scope, isRepeaterDescendant) {
var node = new Node(element, scope);
node.previousSibling = element.previousSibling;
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) {
node.skip = (value === "" || value === "true");
}
if (name === settings.attributes.html) {
node.html = (value === "" || value === "true");
}
if (name === settings.attributes.repeat && !isRepeaterDescendant) {
node.repeater = value;
}
if (
hasInterpolation(name + ':' + value) ||
name === settings.attributes.repeat ||
name === settings.attributes.skip ||
name === settings.attributes.html ||
name === settings.attributes.show ||
name === settings.attributes.hide ||
name === settings.attributes.href ||
name === settings.attributes.checked ||
name === settings.attributes.disabled ||
name === settings.attributes.multiple ||
name === settings.attributes.readonly ||
name === settings.attributes.selected ||
value.indexOf(settings.attributes.cloak) !== -1
) {
attributes.push(new Attribute(name, value, node));
}
if (events[name] && !isRepeaterDescendant) {
eventsArray.push({name:events[name], value:value});
attributes.push(new Attribute(name, value, node));
}
}
}
node.attributes = attributes;
for (var i = 0, l = eventsArray.length; i < l; i++) {
node.addEvent(eventsArray[i].name, eventsArray[i].value);
}
return node;
}
function hasInterpolation(value) {
var matches = value.match(regex.token);
return matches && matches.length > 0;
}
function hasContent(value) {
return regex.content.test(value)
}
function isElementValid(element) {
if (!element) return;
var type = element.nodeType;
if (!element || !type) return false;
// comment
if (type === 8) return false;
// empty text node
if (type === 3 && !hasContent(element.nodeValue) && !hasInterpolation(element.nodeValue)) return false;
// result
return true;
}
function compile(template, element, parent, nodeTarget) {
if (!isElementValid(element)) return;
// get node
var node;
if (!nodeTarget) {
node = getNodeFromElement(element, parent ? parent.scope : new Scope(helpersScopeObject)._createChild(), parent && (parent.repeater || parent.isRepeaterDescendant) );
}
else {
node = nodeTarget;
node.parent = parent;
}
if (parent && (parent.repeater || parent.isRepeaterDescendant)) {
node.isRepeaterDescendant = true;
}
node.template = template;
// children
if (node.skip) return;
var child = element.firstChild;
while (child) {
var childNode = compile(template, child, node);
if (childNode) {
childNode.parent = node;
node.children.push(childNode);
}
child = child.nextSibling;
}
return node;
}
function updateScopeWithData(scope, data) {
clearScope(scope);
for (var d in data) {
scope[d] = data[d];
}
}
function clearScope(scope) {
for (var key in scope) {
if (key.substr(0, 1) !== '_') {
scope[key] = null;
delete scope[key];
}
}
}
function updateNodeChildren(node) {
if (node.repeater || !node.children || childNodeIsTemplate(node)) return;
for (var i = 0, l = node.children.length; i < l; i++) {
node.children[i].update();
}
}
function renderNodeChildren(node) {
if (!node.children || childNodeIsTemplate(node)) return;
for (var i = 0, l = node.children.length; i < l; i++) {
node.children[i].render();
}
}
function renderNodeRepeater(node) {
var data = getRepeaterData(node.repeater, node.scope);
var previousElement;
if (isArray(data)) {
// process array
for (var i = 0, l1 = data.length, l2 = node.childrenRepeater.length, l = l1 > l2 ? l1 : l2; i < l; i++) {
if (i < l1) {
previousElement = createRepeaterChild(node, i, data[i], vars.index, i, previousElement);
}
else {
node.parent.element.removeChild(node.childrenRepeater[i].element);
node.childrenRepeater[i].dispose();
} }
} }
if (node.childrenRepeater.length > data.length) { return newValue;
node.childrenRepeater.length = data.length; }
function getScopeFromPattern(scope, pattern) {
var depth = getScopeDepth(pattern);
var scopeTarget = scope;
while (depth > 0) {
scopeTarget = scopeTarget._parent ? scopeTarget._parent : scopeTarget;
depth--;
} }
return scopeTarget;
} }
else {
// process object function getValueFromPattern(scope, pattern, context) {
var count = -1; var exp = new Expression(pattern);
for (var o in data) { return getValue(scope, exp.pattern, exp.path, exp.params, undefined, undefined, undefined, context);
count++; }
previousElement = createRepeaterChild(node, count, data[o], vars.key, o, previousElement);
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
if (regex.string.test(pattern)) {
return trimQuotes(pattern);
}
// find params
var paramsValues = [];
if (!paramsFound && params) {
for (var j = 0, jl = params.length; j < jl; j++) {
paramsValues.push(getValueFromPattern(scope, params[j], context));
}
}
else {
paramsValues = paramsFound;
}
if (getParams) {
return paramsValues;
}
// find scope
var scopeTarget = getScopeFromPattern(scope, pattern);
// remove parent string
pattern = pattern.replace(/..\//g, '');
pathString = pathString.replace(/..\//g, '');
if (!scopeTarget) {
return undefined;
}
// search path
var path = scopeTarget;
var pathParts = pathString.split(/\.|\[|\]/g);
if (pathParts.length > 0) {
for (var i = 0, l = pathParts.length; i < l; i++) {
if (pathParts[i] !== '') {
path = path[pathParts[i]];
}
if (!isDefined(path)) {
// no path, search in parent
if (scopeTarget._parent) {
return getValue(scopeTarget._parent, pattern, pathString, params, getFunction, getParams, paramsValues);
}
else {
return undefined;
}
}
}
}
// return value
if (!isFunction(path)) {
return path;
} }
var size = count; else {
while (count++ < node.childrenRepeater.length-1) { if (getFunction) {
node.parent.element.removeChild(node.childrenRepeater[count].element); return path;
node.childrenRepeater[count].dispose(); }
else {
return path.apply(null, paramsValues);
}
} }
node.childrenRepeater.length = size+1; return undefined;
} }
if (node.element.parentNode) {
node.element.parentNode.removeChild(node.element); function getExpressionPath(value) {
var val = value.split('(')[0];
val = trimScopeDepth(val);
return val;
} }
}
function cloneRepeaterNode(element, node) { function getParamsFromString(value) {
var newNode = new Node(element, node.scope._createChild()); return trimArray(value.split(regex.params));
if (node.attributes) { }
var attrs = [];
for (var i = 0, l = node.attributes.length; i < l; i++) { function getScopeDepth(value) {
newNode.renderAsHtml = node.renderAsHtml; var val = value.split('(')[0];
if (node.attributes[i].name === settings.attributes.skip) { var matches = val.match(regex.depth);
newNode.skip = (node.attributes[i].value === "" || node.attributes[i].value === "true"); return !matches ? 0 : matches.length;
}
function addAttribute(node, name, value) {
var attr;
node.attributes = node.attributes || [];
if (name === settings.attributes.skip) {
node.skip = normalizeBoolean(value);
}
if (name === settings.attributes.html) {
node.html = normalizeBoolean(value);
}
if (name === settings.attributes.repeat && !node.isRepeaterDescendant) {
node.repeater = value;
}
if (
hasInterpolation(name + ':' + value) ||
name === settings.attributes.repeat ||
name === settings.attributes.skip ||
name === settings.attributes.html ||
name === settings.attributes.show ||
name === settings.attributes.hide ||
name === settings.attributes.href ||
name === settings.attributes.checked ||
name === settings.attributes.disabled ||
name === settings.attributes.multiple ||
name === settings.attributes.readonly ||
name === settings.attributes.selected ||
value.indexOf(settings.attributes.cloak) !== -1
) {
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 (node.attributes[i].name === settings.attributes.html) { }
newNode.html = (node.attributes[i].value === "" || node.attributes[i].value === "true"); for (var a=0, b=eventsArray.length; a<b; a++) {
node.addEvent(eventsArray[a].name, eventsArray[a].value, eventsArray[a].attr);
}
return node;
}
function hasInterpolation(value) {
var matches = value.match(regex.token);
return matches && matches.length > 0;
}
function hasContent(value) {
return regex.content.test(value);
}
function isElementValid(element) {
if (!element) {
return;
}
var type = element.nodeType;
if (!element || !type) {
return false;
}
// comment
if (type === 8) {
return false;
}
// empty text node
if (type === 3 && !hasContent(element.nodeValue) && !hasInterpolation(element.nodeValue)) {
return false;
}
// result
return true;
}
function compile(template, element, parent, nodeTarget) {
if (!isElementValid(element)) {
return;
}
// get node
var node;
if (!nodeTarget) {
node = getNodeFromElement(element, parent ? parent.scope : new Scope(helpersScopeObject)._createChild());
}
else {
node = nodeTarget;
node.parent = parent;
}
if (parent && (parent.repeater || parent.isRepeaterChild)) {
node.isRepeaterChild = true;
}
node.template = template;
// children
if (node.skip) {
return;
}
var child = element.firstChild;
while (child) {
var childNode = compile(template, child, node);
if (childNode) {
childNode.parent = node;
node.children.push(childNode);
} }
if (node.attributes[i].name !== attributes.repeat) { child = child.nextSibling;
var attribute = new Attribute(node.attributes[i].name, node.attributes[i].value, newNode); }
attrs.push(attribute); return node;
}
function updateScopeWithData(scope, data) {
clearScope(scope);
for (var d in data) {
if (data.hasOwnProperty(d)) {
scope[d] = data[d];
} }
if (events[node.attributes[i].name]) { }
newNode.addEvent(events[node.attributes[i].name], node.attributes[i].value); }
function clearScope(scope) {
for (var key in scope) {
if (scope.hasOwnProperty(key)) {
if (key.substr(0, 1) !== '_') {
scope[key] = null;
delete scope[key];
}
} }
} }
newNode.attributes = attrs;
} }
return newNode;
}
function createRepeaterChild(node, count, data, indexVar, indexVarValue, previousElement) { function updateNodeChildren(node) {
var existingChild = node.childrenRepeater[count]; if (node.repeater || !node.children || childNodeIsTemplate(node)) {
if (!existingChild) { return;
// no existing node }
var newElement = node.element.cloneNode(true); for (var i = 0, l = node.children.length; i < l; i++) {
// can't recreate the node with a cloned element on IE7 node.children[i].update();
// be cause the attributes are not specified annymore (attribute.specified) }
//var newNode = getNodeFromElement(newElement, node.scope._createChild(), true); }
var newNode = cloneRepeaterNode(newElement, node);
newNode.isRepeaterChild = true; function renderNodeChildren(node) {
newNode.parent = node.parent; if (!node.children || childNodeIsTemplate(node)) {
newNode.template = node.template; return;
node.childrenRepeater[count] = newNode; }
updateScopeWithRepeaterData(node.repeater, newNode.scope, data); for (var i = 0, l = node.children.length; i < l; i++) {
newNode.scope[indexVar] = indexVarValue; node.children[i].render();
compile(node.template, newElement, node.parent, newNode); }
newNode.update(); }
newNode.render();
if (!previousElement) { function renderNodeRepeater(node) {
if (node.previousSibling) insertAfter(node.previousSibling, newElement); var data = getRepeaterData(node.repeater, node.scope);
else if (node.nextSibling) insertBefore(node.nextSibling, newElement); var previousElement;
else node.parent.element.appendChild(newElement); if (isArray(data)) {
// process array
for (var i = 0, l1 = data.length, l2 = node.childrenRepeater.length, l = l1 > l2 ? l1 : l2; i < l; i++) {
if (i < l1) {
previousElement = createRepeaterChild(node, i, data[i], vars.index, i, previousElement);
}
else {
node.parent.element.removeChild(node.childrenRepeater[i].element);
node.childrenRepeater[i].dispose();
}
}
if (node.childrenRepeater.length > data.length) {
node.childrenRepeater.length = data.length;
}
} }
else { else {
insertAfter(previousElement, newElement); // process object
var count = -1;
for (var o in data) {
if (data.hasOwnProperty(o)) {
count++;
previousElement = createRepeaterChild(node, count, data[o], vars.key, o, previousElement);
}
}
var size = count;
while (count++ < node.childrenRepeater.length-1) {
node.parent.element.removeChild(node.childrenRepeater[count].element);
node.childrenRepeater[count].dispose();
}
node.childrenRepeater.length = size+1;
} }
return newElement; if (node.element.parentNode) {
} node.element.parentNode.removeChild(node.element);
else { }
// existing node }
updateScopeWithRepeaterData(node.repeater, existingChild.scope, data);
existingChild.scope[indexVar] = indexVarValue; function compileClone(node, newNode) {
existingChild.update(); if (!isElementValid(newNode.element)) {
existingChild.render(); return;
return existingChild.element; }
} // create attribute
} if (node.attributes) {
for (var i= 0, l=node.attributes.length; i<l; i++) {
var Scope = function(data) { var attr = node.attributes[i];
function createChild(data) { var newAttr = addAttribute(newNode, attr.name, attr.value);
var obj = createObject(data); if (events[attr.name]) {
obj._parent = this; newNode.addEvent(events[attr.name], attr.value, newAttr);
this._children.push(obj); }
return obj;
}
function createObject(data) {
var obj = data || {};
obj._parent = null;
obj._children = [];
obj._createChild = function(data) {
return createChild.apply(obj, arguments);
}
return obj;
}
return createObject(data);
};
var Node = function(element, scope) {
this.element = element;
this.scope = scope;
this.attributes = null;
this.value = null;
this.interpolation = null;
this.invalidate = false;
this.skip = false;
this.repeater = null;
this.isRepeaterDescendant = false;
this.isRepeaterChild = false;
this.parent = null;
this.children = [];
this.childrenRepeater = [];
this.previousSibling = null;
this.nextSibling = null;
this.template = null;
this.eventHandlers = {};
this.html = false;
if (isTextNode(this.element)) {
this.value = this.element.nodeValue;
this.interpolation = new Interpolation(this.value, this, undefined);
}
};
Node.prototype = {
toString: function() {
return '[object Node]';
},
dispose: function() {
this.clearEvents();
var i, l;
if (this.children) {
for (i = 0, l = this.children.length; i < l; i++) {
this.children[i].dispose();
} }
} }
if (this.childrenRepeater) { // children
for (i = 0, l = this.childrenRepeater.length; i < l; i++) { var child = node.element.firstChild;
this.childrenRepeater[i].dispose(); 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;
} }
if (this.attributes) { return newChildNode;
for (i = 0, l = this.attributes.length; i < l; i++) { }
this.attributes[i].dispose();
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;
}
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);
} }
} }
if (this.interpolation) { else {
this.interpolation.dispose(); insertAfter(previousElement, newElement);
} }
this.element = null; }
this.scope = null;
function createRepeaterChild(node, count, data, indexVar, indexVarValue, previousElement) {
var existingChild = node.childrenRepeater[count];
if (!existingChild) {
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
// be cause the attributes are not specified anymore (attribute.specified)
//var newNode = getNodeFromElement(newElement, node.scope._createChild(), true);
var newNode = cloneRepeaterNode(newElement, node);
node.childrenRepeater[count] = newNode;
updateScopeWithRepeaterData(node.repeater, newNode.scope, data);
newNode.scope[indexVar] = indexVarValue;
newNode.update();
newNode.render();
return newElement;
}
else {
// existing node
updateScopeWithRepeaterData(node.repeater, existingChild.scope, data);
existingChild.scope[indexVar] = indexVarValue;
existingChild.update();
existingChild.render();
return existingChild.element;
}
}
var Scope = function(data) {
var self;
function createChild(data) {
var obj = createObject(data);
obj._parent = self;
self._children.push(obj);
return obj;
}
function createObject(data) {
var obj = data || {};
obj._parent = null;
obj._children = [];
obj._createChild = function() {
self = obj;
return createChild.apply(obj, arguments);
};
return obj;
}
return createObject(data);
};
var Node = function(element, scope) {
this.element = element;
this.scope = scope;
this.attributes = null; this.attributes = null;
this.attributesHashMap = null;
this.value = null; this.value = null;
this.interpolation = null; this.interpolation = null;
this.invalidate = false;
this.skip = false;
this.repeater = null; this.repeater = null;
this.isRepeaterDescendant = false;
this.isRepeaterChild = false;
this.parent = null; this.parent = null;
this.children = null; this.children = [];
this.childrenRepeater = null; this.childrenRepeater = [];
this.previousSibling = null; this.previousSibling = null;
this.nextSibling = null; this.nextSibling = null;
this.template = null; this.template = null;
this.eventHandlers = null; this.eventHandlers = {};
}, this.html = false;
getNode: function(element) {
var node; if (isTextNode(this.element)) {
if (element === this.element) return this; this.value = this.element.nodeValue;
else if (this.childrenRepeater.length > 0) { this.interpolation = new Interpolation(this.value, this, undefined);
for (var k = 0, kl = this.childrenRepeater.length; k < kl; k++) {
node = this.childrenRepeater[k].getNode(element);
if (node) return node;
}
} }
else {
};
Node.prototype = {
toString: function() {
return '[object Node]';
},
dispose: function() {
this.clearEvents();
var i, l;
if (this.children) {
for (i = 0, l = this.children.length; i < l; i++) {
this.children[i].dispose();
}
}
if (this.childrenRepeater) {
for (i = 0, l = this.childrenRepeater.length; i < l; i++) {
this.childrenRepeater[i].dispose();
}
}
if (this.attributes) {
for (i = 0, l = this.attributes.length; i < l; i++) {
this.attributes[i].dispose();
}
}
if (this.interpolation) {
this.interpolation.dispose();
}
this.element = null;
this.scope = null;
this.attributes = null;
this.value = null;
this.interpolation = null;
this.repeater = null;
this.parent = null;
this.children = null;
this.childrenRepeater = null;
this.previousSibling = null;
this.nextSibling = null;
this.template = null;
this.eventHandlers = null;
},
getNode: function(element) {
var node;
if (element === this.element) {
return this;
}
if (this.childrenRepeater.length > 0) {
for (var k = 0, kl = this.childrenRepeater.length; k < kl; k++) {
node = this.childrenRepeater[k].getNode(element);
if (node) {
return node;
}
}
}
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; },
}, getAttribute: function(name) {
getAttribute: function(name) { if (this.attributes) {
if (this.attributes) { for (var i = 0, l = this.attributes.length; i < l; i++) {
for (var i = 0, l = this.attributes.length; i < l; i++) { var att = this.attributes[i];
var att = this.attributes[i]; if (att.interpolationName && att.interpolationName.value === name) {
if (att.interpolationName && att.interpolationName.value === name) { return att;
return att; }
} }
} }
} },
}, update: function() {
update: function() { if (childNodeIsTemplate(this)) {
if (childNodeIsTemplate(this)) return; return;
if (isDefined(this.interpolation)) {
this.interpolation.update();
}
if (isDefined(this.attributes)) {
for (var i = 0, l = this.attributes.length; i < l; i++) {
this.attributes[i].update();
} }
} if (isDefined(this.interpolation)) {
updateNodeChildren(this); this.interpolation.update();
},
invalidateData: function() {
if (childNodeIsTemplate(this)) return;
this.invalidate = true;
var i, l;
if (this.attributes) {
for (i = 0, l = this.attributes.length; i < l; i++) {
this.attributes[i].invalidate = true;
} }
} if (isDefined(this.attributes)) {
for (i = 0, l = this.childrenRepeater.length; i < l; i++) { for (var i = 0, l = this.attributes.length; i < l; i++) {
this.childrenRepeater[i].invalidateData(); this.attributes[i].update();
} }
for (i = 0, l = this.children.length; i < l; i++) {
this.children[i].invalidateData();
}
},
addEvent: function(type, pattern) {
if (this.repeater) return;
if (this.eventHandlers[type]) {
this.removeEvent(type);
}
var scope = this.scope;
var handler = function(event) {
var exp = new Expression(pattern, this.node);
var func = exp.getValue(scope, true);
var params = exp.getValue(scope, false, true);
params.unshift(event);
if (func) {
func.apply(null, params);
} }
}; updateNodeChildren(this);
this.eventHandlers[type] = handler; },
addEvent(this.element, type, handler); invalidateData: function() {
}, if (childNodeIsTemplate(this)) {
removeEvent: function(type) { return;
removeEvent(this.element, type, this.eventHandlers[type]); }
this.eventHandlers[type] = null; this.invalidate = true;
delete this.eventHandlers[type]; var i, l;
}, if (this.attributes) {
clearEvents: function() { for (i = 0, l = this.attributes.length; i < l; i++) {
if (this.eventHandlers) { this.attributes[i].invalidate = true;
for (var key in this.eventHandlers) {
this.removeEvent(key, this.eventHandlers[key]);
}
}
if (this.children) {
for (var k = 0, kl = this.children.length; k < kl; k++) {
this.children[k].clearEvents();
}
}
if (this.childrenRepeater) {
for (var f = 0, fl = this.childrenRepeater.length; f < fl; f++) {
this.childrenRepeater[f].clearEvents();
}
}
},
render: function() {
if (childNodeIsTemplate(this)) return;
if (this.invalidate) {
this.invalidate = false;
if (isTextNode(this.element)) {
if (this.parent && this.parent.html) {
this.value = this.parent.element.innerHTML = this.interpolation.render();
} }
else { }
this.value = this.element.nodeValue = this.interpolation.render(); for (i = 0, l = this.childrenRepeater.length; i < l; i++) {
this.childrenRepeater[i].invalidateData();
}
for (i = 0, l = this.children.length; i < l; i++) {
this.children[i].invalidateData();
}
},
addEvent: function(type, pattern, attr) {
if (this.repeater) {
return;
}
if (this.eventHandlers[type]) {
this.removeEvent(type);
}
var scope = this.scope;
var node = this;
var handler = function(event) {
var exp = new Expression(pattern, node, attr);
var func = exp.getValue(scope, true);
var params = exp.getValue(scope, false, true);
params.unshift(event);
if (func) {
func.apply(null, params);
}
};
this.eventHandlers[type] = handler;
addEvent(this.element, type, handler);
},
removeEvent: function(type) {
removeEvent(this.element, type, this.eventHandlers[type]);
this.eventHandlers[type] = null;
delete this.eventHandlers[type];
},
clearEvents: function() {
if (this.eventHandlers) {
for (var key in this.eventHandlers) {
if (this.eventHandlers.hasOwnProperty(key)) {
this.removeEvent(key);
}
} }
} }
} if (this.children) {
if (this.attributes) { for (var k = 0, kl = this.children.length; k < kl; k++) {
for (var i = 0, l = this.attributes.length; i < l; i++) { this.children[k].clearEvents();
this.attributes[i].render(); }
} }
} if (this.childrenRepeater) {
if (this.repeater) { for (var f = 0, fl = this.childrenRepeater.length; f < fl; f++) {
renderNodeRepeater(this); this.childrenRepeater[f].clearEvents();
} }
else { }
renderNodeChildren(this); },
} render: function() {
} if (childNodeIsTemplate(this)) {
}; return;
var Attribute = function(name, value, node) { }
this.name = name; if (this.invalidate) {
this.value = value; this.invalidate = false;
this.node = node; if (isTextNode(this.element)) {
this.interpolationName = new Interpolation(this.name, null, this); if (this.parent && this.parent.html) {
this.interpolationValue = new Interpolation(this.value, null, this); this.value = this.parent.element.innerHTML = this.interpolation.render();
this.invalidate = false; }
}; else {
Attribute.prototype = { this.value = this.element.nodeValue = this.interpolation.render();
toString: function() { }
return '[object Attribute]'; }
}, }
dispose: function() { if (this.attributes) {
if (this.interpolationName) this.interpolationName.dispose(); for (var i = 0, l = this.attributes.length; i < l; i++) {
if (this.interpolationValue) this.interpolationValue.dispose(); this.attributes[i].render();
this.interpolationName = null; }
this.interpolationValue = null; }
this.node = null; if (this.repeater) {
this.name = null; renderNodeRepeater(this);
this.value = null;
this.previousName = null;
},
update: function() {
this.interpolationName.update();
this.interpolationValue.update();
},
render: function() {
if (this.node.repeater) return;
var element = this.node.element;
if (this.invalidate) {
this.invalidate = false;
this.previousName = this.name;
this.name = isDefined(this.interpolationName.render()) ? this.interpolationName.render() : this.name;
this.value = isDefined(this.interpolationValue.render()) ? this.interpolationValue.render() : this.value;
if (this.name === attributes.src) {
renderSrc(this.name, this.value);
}
else if (this.name === attributes.href) {
renderHref(this.name, this.value);
} }
else { else {
if (this.node.isRepeaterChild && ie === 7) { renderNodeChildren(this);
// delete attributes on cloned elements crash IE7 }
}
};
var Attribute = function(name, value, node) {
this.name = name;
this.value = value;
this.node = node;
this.interpolationName = new Interpolation(this.name, null, this);
this.interpolationValue = new Interpolation(this.value, null, this);
this.invalidate = false;
};
Attribute.prototype = {
toString: function() {
return '[object Attribute]';
},
dispose: function() {
if (this.interpolationName) {
this.interpolationName.dispose();
}
if (this.interpolationValue) {
this.interpolationValue.dispose();
}
this.interpolationName = null;
this.interpolationValue = null;
this.node = null;
this.name = null;
this.value = null;
this.previousName = null;
},
update: function() {
if (this.node.repeater) {
return;
}
this.interpolationName.update();
this.interpolationValue.update();
},
render: function() {
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 { else {
this.node.element.removeAttribute(this.interpolationName.value); element.removeAttribute(attrName);
} }
if (this.previousName) { }
if (ie === 7 && this.previousName === 'class') { // src attribute
// iE function renderSrc(value) {
this.node.element.className = ""; element.setAttribute('src', value);
}
// href attribute
function renderHref(value) {
element.setAttribute('href', value);
}
var element = this.node.element;
if (this.invalidate) {
this.invalidate = false;
this.previousName = this.name;
this.name = isDefined(this.interpolationName.render()) ? this.interpolationName.render() : this.name;
this.value = isDefined(this.interpolationValue.render()) ? this.interpolationValue.render() : this.value;
if (this.name === attributes.src) {
renderSrc(this.value);
}
else if (this.name === attributes.href) {
renderHref(this.value);
}
else {
if (ie !== 7 || (ie === 7 && !this.node.isRepeaterChild)) {
this.node.element.removeAttribute(this.interpolationName.value);
} }
else { if (this.previousName) {
if (this.node.isRepeaterChild && ie === 7) { if (ie === 7 && this.previousName === 'class') {
// delete attributes on cloned elements crash IE7 // iE
this.node.element.className = '';
} }
else { else {
this.node.element.removeAttribute(this.previousName); if (ie !== 7 || (ie === 7 && !this.node.isRepeaterChild)) {
this.node.element.removeAttribute(this.previousName);
}
} }
} }
renderAttribute(this.name, this.value, this.node);
} }
renderAttribute(this.name, this.value, this.previousName); }
// cloak
if (this.name === 'class' && this.value.indexOf(settings.attributes.cloak) !== -1) {
removeClass(this.node.element, settings.attributes.cloak);
}
// hide
if (this.name === attributes.hide) {
var bool = normalizeBoolean(this.value);
renderAttribute(this.name, bool, this.node);
element.style.display = bool ? 'none' : '';
}
// show
if (this.name === attributes.show) {
var bool = normalizeBoolean(this.value);
renderAttribute(this.name, bool, this.node);
element.style.display = bool ? '' : 'none';
}
// checked
if (this.name === attributes.checked) {
renderSpecialAttribute(this.value, 'checked');
renderAttribute(this.name, normalizeBoolean(this.value) ? true : false, this.node);
element.checked = normalizeBoolean(this.value) ? true : false;
}
// disabled
if (this.name === attributes.disabled) {
renderSpecialAttribute(this.value, 'disabled');
renderAttribute(this.name, normalizeBoolean(this.value) ? true : false, this.node);
}
// multiple
if (this.name === attributes.multiple) {
renderSpecialAttribute(this.value, 'multiple');
renderAttribute(this.name, normalizeBoolean(this.value) ? true : false, this.node);
}
// readonly
if (this.name === attributes.readonly) {
var bool = normalizeBoolean(this.value);
if (ie === 7) {
element.readOnly = bool ? true : false;
}
else {
renderSpecialAttribute(this.value, 'readonly');
}
renderAttribute(this.name, bool ? true : false, this.node);
}
// selected
if (this.name === attributes.selected) {
renderSpecialAttribute(this.value, 'selected');
renderAttribute(this.name, normalizeBoolean(this.value) ? true : false, this.node);
} }
} }
// cloak };
if (this.name === 'class' && this.value.indexOf(settings.attributes.cloak) !== -1) {
removeClass(this.node.element, settings.attributes.cloak); var Interpolation = function(value, node, attribute) {
} this.value = node && !isTextNode(node.element) ? trim(value) : value;
// hide this.node = node;
if (this.name === attributes.hide) { this.attribute = attribute;
element.style.display = isAttributeDefined(this.value) ? "none" : "block"; this.sequence = [];
} this.expressions = [];
// show var parts = this.value.match(regex.sequence);
if (this.name === attributes.show) { if (parts) {
element.style.display = isAttributeDefined(this.value) ? "block" : "none"; for (var i = 0, l = parts.length; i < l; i++) {
if (parts[i].match(regex.token)) {
var exp = new Expression(trimTokens(parts[i]), this.node, this.attribute);
this.sequence.push(exp);
this.expressions.push(exp);
}
else {
this.sequence.push(parts[i]);
}
}
trimArray(this.sequence);
} }
// checked };
if (this.name === attributes.checked) { Interpolation.prototype = {
renderSpecialAttribute(this.name, this.value, 'checked'); toString: function() {
element.checked = isAttributeDefined(this.value) ? true : false; return '[object Interpolation]';
},
dispose: function() {
if (this.expressions) {
for (var i = 0, l = this.expressions.length; i < l; i++) {
this.expressions[i].dispose();
}
}
this.value = null;
this.node = null;
this.attribute = null;
this.sequence = null;
this.expressions = null;
},
update: function() {
var i = -1, l = this.expressions.length;
while (++i < l) {
this.expressions[i].update();
}
},
render: function() {
var rendered = '';
if (this.sequence) {
for (var i = 0, l = this.sequence.length; i < l; i++) {
var val = '';
if (isExpression(this.sequence[i])) {
val = this.sequence[i].value;
}
else {
val = this.sequence[i];
}
if (!isDefined(val)) {
val = '';
}
rendered += val;
}
}
return rendered;
} }
// disabled };
if (this.name === attributes.disabled) {
renderSpecialAttribute(this.name, this.value, 'disabled'); var Expression = function(pattern, node, attribute) {
if (!isDefined(pattern)) {
return;
}
this.pattern = pattern;
this.isString = regex.string.test(pattern);
this.node = node;
this.attribute = attribute;
this.value = this.isString ? this.pattern : undefined;
if (this.isString) {
this.isFunction = false;
this.depth = null;
this.path = null;
this.params = null;
} }
// multiple else {
if (this.name === attributes.multiple) { this.isFunction = isExpFunction(this.pattern);
renderSpecialAttribute(this.name, this.value, 'multiple'); this.depth = getScopeDepth(this.pattern);
this.path = getExpressionPath(this.pattern);
this.params = !this.isFunction ? null : getParamsFromString(this.pattern.match(regex.func)[2]);
} }
// readonly };
if (this.name === attributes.readonly) { Expression.prototype = {
if (ie === 7) { toString: function() {
element.readOnly = isAttributeDefined(this.value) ? true : false; return '[object Expression]';
},
dispose: function() {
this.pattern = null;
this.node = null;
this.attribute = null;
this.path = null;
this.params = null;
this.value = null;
},
update: function() {
var node = this.node;
if (!node && this.attribute) {
node = this.attribute.node;
} }
else { if (!node && node.scope) {
renderSpecialAttribute(this.name, this.value, 'readonly'); return;
} }
} var newValue = this.getValue(node.scope);
// selected newValue = getWatcherValue(this, newValue);
if (this.name === attributes.selected) { if (this.value !== newValue) {
renderSpecialAttribute(this.name, this.value, 'selected'); this.value = newValue;
} (this.node || this.attribute).invalidate = true;
// normal attribute
function renderAttribute(name, value) {
if (ie === 7 && name === "class") {
element.className = value;
} }
else { },
element.setAttribute(name, value); getValue: function(scope, 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);
} }
// special attribute };
function renderSpecialAttribute(name, value, attrName) {
if (isAttributeDefined(value)) { var templates = new HashMap('st');
element.setAttribute(attrName, attrName);
var Template = function(element) {
this.watchers = new HashMap('stw');
this.node = null;
this.scope = null;
this.compile(element);
};
Template.prototype = {
toString: function() {
return '[object Template]';
},
compile: function(element) {
if (element) {
this.element = element;
} }
else { if (this.node) {
element.removeAttribute(attrName); this.node.dispose();
} }
this.node = compile(this, this.element);
this.node.root = true;
this.scope = this.node.scope;
},
update: function(data) {
if (isDefined(data)) {
updateScopeWithData(this.node.scope, data);
}
if (this.node) {
this.node.update();
}
},
render: function(data) {
this.update(data);
if (this.node) {
this.node.render();
}
},
invalidate: function() {
if (this.node) {
this.node.invalidateData();
}
},
watch: function(target, watcher) {
if ( (!isString(target) && !isElement(target)) || !isFunction(watcher)) {
return;
}
this.watchers.put(target, watcher);
},
unwatch: function(target) {
this.watchers.remove(target);
},
clearWatchers: function() {
this.watchers.dispose();
},
clearEvents: function() {
this.node.clearEvents();
},
getNode: function(element) {
return this.node.getNode(element);
},
dispose: function() {
templates.remove(this.element);
if (this.watchers) {
this.watchers.dispose();
}
if (this.node) {
this.node.dispose();
}
this.element = null;
this.watchers = null;
this.node = null;
} }
// 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) { // written by Dean Edwards, 2005
this.value = node && !isTextNode(node.element) ? trim(value) : value; // with input from Tino Zijdel, Matthias Miller, Diego Perini
this.node = node; // http://dean.edwards.name/weblog/2005/10/add-event/
this.attribute = attribute; function addEvent(element, type, handler) {
this.sequence = []; if (element.addEventListener) {
this.expressions = []; element.addEventListener(type, handler, false);
var parts = this.value.match(regex.sequence); } else {
if (parts) { // assign each event handler a unique ID
for (var i = 0, l = parts.length; i < l; i++) { if (!handler.$$guid) {
if (parts[i].match(regex.token)) { handler.$$guid = addEvent.guid++;
var exp = new Expression(trimTokens(parts[i]), this.node, this.attribute);
this.sequence.push(exp);
this.expressions.push(exp);
} }
else { // create a hash table of event types for the element
this.sequence.push(parts[i]); if (!element.events) {
element.events = {};
} }
// create a hash table of event handlers for each element/event pair
var handlers = element.events[type];
if (!handlers) {
handlers = element.events[type] = {};
// store the existing event handler (if there is one)
if (element['on' + type]) {
handlers[0] = element['on' + type];
}
}
// store the event handler in the hash table
handlers[handler.$$guid] = handler;
// assign a global event handler to do all the work
element['on' + type] = function(event) {
var returnValue = true;
// grab the event object (IE uses a global event object)
event = event || fixEvent(((this.ownerDocument || this.document || this).parentWindow || window).event);
// get a reference to the hash table of event handlers
var handlers = this.events[event.type];
// execute each event handler
for (var i in handlers) {
if (handlers.hasOwnProperty(i)) {
this.$$handleEvent = handlers[i];
if (this.$$handleEvent(event) === false) {
returnValue = false;
}
}
}
return returnValue;
};
} }
trimArray(this.sequence);
} }
}; // a counter used to create unique IDs
Interpolation.prototype = { addEvent.guid = 1;
toString: function() { function removeEvent(element, type, handler) {
return '[object Interpolation]'; if (element.removeEventListener) {
}, element.removeEventListener(type, handler, false);
dispose: function() { } else {
if (this.expressions) { // delete the event handler from the hash table
for (var i = 0, l = this.expressions.length; i < l; i++) { if (element.events && element.events[type]) {
this.expressions[i].dispose(); delete element.events[type][handler.$$guid];
} }
} }
this.value = null; }
this.node = null; function fixEvent(event) {
this.attribute = null; // add W3C standard event methods
this.sequence = null; event.preventDefault = fixEvent.preventDefault;
this.expressions = null; event.stopPropagation = fixEvent.stopPropagation;
}, return event;
update: function() { }
var i = -1, l = this.expressions.length; fixEvent.preventDefault = function() {
while (++i < l) { this.returnValue = false;
this.expressions[i].update(); };
} fixEvent.stopPropagation = function() {
}, this.cancelBubble = true;
render: function() { };
var rendered = "";
if (this.sequence) { var maxDepth;
for (var i = 0, l = this.sequence.length; i < l; i++) { var eventStore = [];
var val = "";
if (isExpression(this.sequence[i])) val = this.sequence[i].value; function parseEvents(element, object, depth) {
else val = this.sequence[i]; maxDepth = depth === undefined ? Number.MAX_VALUE : depth;
if (!isDefined(val)) val = ""; parseNode(element, object, 0, true);
rendered += val; }
}
} function parseNode(element, object, depth, isRoot) {
return rendered; if (!isElement(element)) {
} throw new Error('Error in soma.template.parseEvents, only a DOM Element can be parsed.');
};
var Expression = function(pattern, node, attribute) {
if (!isDefined(pattern)) return;
this.pattern = pattern;
this.isString = regex.string.test(pattern);
this.node = node;
this.attribute = attribute;
this.value = this.isString ? this.pattern : undefined;
if (this.isString) {
this.isFunction = false;
this.depth = null;
this.path = null;
this.params = null;
}
else {
this.isFunction = isExpFunction(this.pattern);
this.depth = getScopeDepth(this.pattern);
this.path = getExpressionPath(this.pattern);
this.params = !this.isFunction ? null : getParamsFromString(this.pattern.match(regex.func)[2]);
}
};
Expression.prototype = {
toString: function() {
return '[object Expression]';
},
dispose: function() {
this.pattern = null;
this.node = null;
this.attribute = null;
this.path = null;
this.params = null;
this.value = null;
},
update: function() {
var node = this.node;
if (!node && this.attribute) node = this.attribute.node;
if (!node && node.scope) return;
var newValue = this.getValue(node.scope);
newValue = getWatcherValue(this, newValue);
if (this.value !== newValue) {
this.value = newValue;
(this.node || this.attribute).invalidate = true;
}
},
getValue: function(scope, getFunction, getParams) {
return getValue(scope, this.pattern, this.path, this.params, getFunction, getParams);
}
};
var templates = new HashMap();
var Template = function(element) {
this.watchers = new HashMap();
this.node = null;
this.scope = null;
this.compile(element);
};
Template.prototype = {
toString: function() {
return '[object Template]';
},
compile: function(element) {
if (element) this.element = element;
if (this.node) this.node.dispose();
this.node = compile(this, this.element);
this.node.root = true;
this.scope = this.node.scope;
},
update: function(data) {
if (isDefined(data)) updateScopeWithData(this.node.scope, data);
if (this.node) this.node.update();
},
render: function(data) {
this.update(data);
if (this.node) this.node.render();
},
invalidate: function() {
if (this.node) this.node.invalidateData();
},
watch: function(target, watcher) {
if ( (!isString(target) && !isElement(target)) || !isFunction(watcher)) return;
this.watchers.put(target, watcher);
},
unwatch: function(target) {
this.watchers.remove(target);
},
clearWatchers: function() {
this.watchers.dispose();
},
clearEvents: function() {
this.node.clearEvents();
},
getNode: function(element) {
return this.node.getNode(element);
},
dispose: function() {
templates.remove(this.element);
if (this.watchers) {
this.watchers.dispose();
} }
if (this.node) { if (isRoot) {
this.node.dispose(); parseAttributes(element, object);
}
if (maxDepth === 0) {
return;
}
var child = element.firstChild;
while (child) {
if (child.nodeType === 1) {
if (depth < maxDepth) {
parseNode(child, object, ++depth);
parseAttributes(child, object);
}
}
child = child.nextSibling;
} }
this.element = null;
this.watchers = null;
this.node = null;
} }
};
function parseAttributes(element, object) {
// written by Dean Edwards, 2005 for (var attr, name, value, attrs = element.attributes, j = 0, jj = attrs && attrs.length; j < jj; j++) {
// with input from Tino Zijdel, Matthias Miller, Diego Perini attr = attrs[j];
// http://dean.edwards.name/weblog/2005/10/add-event/ if (attr.specified) {
function addEvent(element, type, handler) { name = attr.name;
if (element.addEventListener) { value = attr.value;
element.addEventListener(type, handler, false); if (events[name]) {
} else { var handler = getHandlerFromPattern(object, value);
// assign each event handler a unique ID if (handler && isFunction(handler)) {
if (!handler.$$guid) handler.$$guid = addEvent.guid++; addEvent(element, events[name], handler);
// create a hash table of event types for the element eventStore.push({element:element, type:events[name], handler:handler});
if (!element.events) element.events = {}; }
// create a hash table of event handlers for each element/event pair
var handlers = element.events[type];
if (!handlers) {
handlers = element.events[type] = {};
// store the existing event handler (if there is one)
if (element["on" + type]) {
handlers[0] = element["on" + type];
}
}
// store the event handler in the hash table
handlers[handler.$$guid] = handler;
// assign a global event handler to do all the work
element["on" + type] = handleEvent;
}
};
// 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;
// grab the event object (IE uses a global event object)
event = event || fixEvent(((this.ownerDocument || this.document || this).parentWindow || window).event);
// get a reference to the hash table of event handlers
var handlers = this.events[event.type];
// execute each event handler
for (var i in handlers) {
this.$$handleEvent = handlers[i];
if (this.$$handleEvent(event) === false) {
returnValue = false;
}
}
return returnValue;
};
function fixEvent(event) {
// add W3C standard event methods
event.preventDefault = fixEvent.preventDefault;
event.stopPropagation = fixEvent.stopPropagation;
return event;
};
fixEvent.preventDefault = function() {
this.returnValue = false;
};
fixEvent.stopPropagation = function() {
this.cancelBubble = true;
};
var maxDepth;
var eventStore = [];
function parseEvents(element, object, depth) {
maxDepth = depth === undefined ? Number.MAX_VALUE : depth;
parseNode(element, object, 0, true);
}
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 (isRoot) parseAttributes(element, object);
if (maxDepth === 0) return;
var child = element.firstChild;
while (child) {
if (child.nodeType === 1) {
if (depth < maxDepth) {
parseNode(child, object, ++depth);
parseAttributes(child, object);
}
}
child = child.nextSibling;
}
}
function parseAttributes(element, object) {
var attributes = [];
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 (events[name]) {
var handler = getHandlerFromPattern(object, value, element);
if (handler && isFunction(handler)) {
addEvent(element, events[name], handler);
eventStore.push({element:element, type:events[name], handler:handler});
} }
} }
} }
} }
}
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];
if (isFunction(object[func])) { if (isFunction(object[func])) {
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];
if (element === item.element || contains(element, item.element)) { if (element === item.element || contains(element, item.element)) {
removeEvent(item.element, item.type, item.handler); removeEvent(item.element, item.type, item.handler);
eventStore.splice(i, 1); eventStore.splice(i, 1);
}
} }
} }
}
if (settings.autocreate && typeof document === 'object') {
// 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;
var parse = function(element) { if (typeof document === 'object') {
var child = !element ? document.body : element.firstChild; // https://github.com/ded/domready
while (child) { 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 (child.nodeType === 1) { if (settings.autocreate) {
parse(child); var parse = function(element) {
var attrValue = child.getAttribute(attributes.template); var child = !element ? document.body : element.firstChild;
if (attrValue) { while (child) {
var getFunction = new Function('return ' + attrValue + ';'); if (child.nodeType === 1) {
try { parse(child);
var f = getFunction(); var attrValue = child.getAttribute(attributes.template);
if (isFunction(f)) { if (attrValue) {
soma.template.bootstrap(attrValue, child, f); var getFunction = new Function('return ' + attrValue + ';');
try {
var f = getFunction();
if (isFunction(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) {
} var tpl = createTemplate(element);
function bootstrapTemplate(attrValue, element, func) { func(tpl, tpl.scope, tpl.element, tpl.node);
var tpl = createTemplate(element); }
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 if (!isElement(target)) {
if (!isElement(target)) { throw new Error(soma.template.errors.TEMPLATE_STRING_NO_ELEMENT);
throw new Error(soma.template.errors.TEMPLATE_STRING_NO_ELEMENT); }
} target.innerHTML = source;
target.innerHTML = source;
element = target;
}
else if (isElement(source)) {
if (isElement(target)) {
// element template with target
target.innerHTML = source.innerHTML;
element = target; element = target;
} }
else if (isElement(source)) {
if (isElement(target)) {
// element template with target
target.innerHTML = source.innerHTML;
element = target;
}
else {
// element template
element = source;
}
}
else { else {
// element template throw new Error(soma.template.errors.TEMPLATE_NO_PARAM);
element = source;
} }
// existing template
if (getTemplate(element)) {
getTemplate(element).dispose();
templates.remove(element);
}
// create template
var template = new Template(element);
templates.put(element, template);
return template;
} }
else {
throw new Error(soma.template.errors.TEMPLATE_NO_PARAM);
}
// existing template
if (getTemplate(element)) {
getTemplate(element).dispose();
templates.remove(element);
}
// create template
var template = new Template(element);
templates.put(element, template);
return template;
}
function getTemplate(element) { function getTemplate(element) {
return templates.get(element); return templates.get(element);
}
function renderAllTemplates() {
for (var key in templates.getData()) {
templates.get(key).render();
} }
}
function appendHelpers(obj) { function renderAllTemplates() {
if (obj === null) { var data = templates.getData();
helpersObject = {}; for (var key in templates.getData()) {
helpersScopeObject = {}; if (data.hasOwnProperty(key)) {
templates.get(key).render();
}
}
} }
if (isDefined(obj) && isObject(obj)) {
for (var key in obj) { function appendHelpers(obj) {
if (obj.hasOwnProperty(key)) { if (obj === null) {
helpersObject[key] = helpersScopeObject[key] = obj[key]; helpersObject = {};
helpersScopeObject = {};
}
if (isDefined(obj) && isObject(obj)) {
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
helpersObject[key] = helpersScopeObject[key] = obj[key];
}
} }
} }
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);
for (var key in template) { for (var key in template) {
if (typeof template[key] === 'function') { if (typeof template[key] === 'function') {
cl.prototype[key] = template[key].bind(template); cl.prototype[key] = template[key].bind(template);
}
} }
cl.prototype.render = template.render.bind(template);
var childInjector = injector.createChild();
childInjector.mapValue('template', template);
childInjector.mapValue('scope', template.scope);
childInjector.mapValue('element', template.element);
return childInjector.createInstance(cl);
} }
cl.prototype.render = template.render.bind(template); return null;
var childInjector = injector.createChild(); };
childInjector.mapValue("template", template); soma.template.bootstrap = function(attrValue, element, func) {
childInjector.mapValue("scope", template.scope); instance.createTemplate(func, element);
childInjector.mapValue("element", template.element); };
return childInjector.createInstance(cl); };
} if (soma.plugins && soma.plugins.add) {
return null; soma.plugins.add(TemplatePlugin);
} }
soma.template.bootstrap = function(attrValue, element, func) {
instance.createTemplate(func, element); soma.template.Plugin = TemplatePlugin;
}
} // exports
if (soma.plugins && soma.plugins.add) { soma.template.create = createTemplate;
soma.plugins.add(TemplatePlugin); soma.template.get = getTemplate;
} soma.template.renderAll = renderAllTemplates;
soma.template.helpers = appendHelpers;
soma.template.Plugin = TemplatePlugin; soma.template.bootstrap = bootstrapTemplate;
soma.template.addEvent = addEvent;
// exports soma.template.removeEvent = removeEvent;
soma.template.create = createTemplate; soma.template.parseEvents = parseEvents;
soma.template.get = getTemplate; soma.template.clearEvents = clearEvents;
soma.template.renderAll = renderAllTemplates; soma.template.ready = ready;
soma.template.helpers = appendHelpers;
soma.template.bootstrap = bootstrapTemplate; // register for AMD module
soma.template.addEvent = addEvent; if (typeof define === 'function' && typeof define.amd !== 'undefined') {
soma.template.removeEvent = removeEvent; define('soma-template', soma.template);
soma.template.parseEvents = parseEvents; }
soma.template.clearEvents = clearEvents;
soma.template.ready = ready; // export for node.js
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
// register for AMD module module.exports = soma.template;
if (typeof define === 'function' && define.amd) { }
define("soma-template", soma.template); if (typeof exports !== 'undefined') {
} exports = soma.template;
}
// export for node.js
if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
exports = module.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 (arr[i] === value) {
if (this[i] === value) return true; 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) {
var vo = this.mappings[name]; if (this.mappings.hasOwnProperty(name)) {
if (vo.value === value || vo.cl === value) { var vo = this.mappings[name];
return vo.prop; if (vo.value === value || vo.cl === value) {
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) {
var vo = this.getMappingVo(name); if (this.mappings.hasOwnProperty(name)) {
if (target.hasOwnProperty(vo.prop)) { var vo = this.getMappingVo(name);
var val = this.getInjectedValue(vo, name); if (target.hasOwnProperty(vo.prop) || (target.constructor && target.constructor.prototype && target.constructor.prototype.hasOwnProperty(vo.prop)) ) {
target[name] = val; target[name] = this.getInjectedValue(vo, name);
}
} }
} }
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,14 +314,18 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -288,14 +314,18 @@ 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) {
var vo = this.mappings[name]; if (this.mappings.hasOwnProperty(name)) {
if (vo.cl == cl) { var vo = this.mappings[name];
if (vo.singleton) { if (vo.cl === cl) {
if (!vo.value) vo.value = this.createInstance.apply(this, arguments); if (vo.singleton) {
return vo.value; if (!vo.value) {
} vo.value = this.createInstance.apply(this, arguments);
else { }
return this.createInstance.apply(this, arguments); return vo.value;
}
else {
return this.createInstance.apply(this, arguments);
}
} }
} }
} }
...@@ -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,73 +374,72 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -344,73 +374,72 @@ 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 module !== 'undefined' && typeof module.exports !== 'undefined') {
module.exports = infuse;
}
if (typeof exports !== 'undefined') { if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
exports = module.exports = infuse;
}
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, undefined) { (function (soma) {
"use strict";
soma.events = {}; 'use strict';
soma.events.version = "0.5.2";
if (!Function.prototype.bind) { soma.events = {};
Function.prototype.bind = function bind(that) { soma.events.version = '0.5.6';
var target = this;
if (typeof target != "function") { if (!Function.prototype.bind) {
throw new Error("Error, you must bind a function."); Function.prototype.bind = function bind(that) {
} var target = this;
var args = Array.prototype.slice.call(arguments, 1); // for normal call if (typeof target !== 'function') {
var bound = function () { throw new Error('Error, you must bind a function.');
if (this instanceof bound) { }
var F = function(){}; var args = Array.prototype.slice.call(arguments, 1); // for normal call
F.prototype = target.prototype; var bound = function () {
var self = new F; if (this instanceof bound) {
var result = target.apply( var F = function(){};
self, F.prototype = target.prototype;
args.concat(Array.prototype.slice.call(arguments)) var self = new F();
); var result = target.apply(
if (Object(result) === result) { self,
return result; args.concat(Array.prototype.slice.call(arguments))
} );
return self; if (Object(result) === result) {
} else { return result;
return target.apply( }
that, return self;
args.concat(Array.prototype.slice.call(arguments)) } else {
); return target.apply(
} that,
}; args.concat(Array.prototype.slice.call(arguments))
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) {
this.defaultPrevented = true; return false;
if (this.isIE9()) this.IE9PreventDefault = true; }
if (this.isIE9orIE10()) {
this.IE9or10PreventDefault = true;
}
else {
this.defaultPrevented = 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 module !== 'undefined' && typeof module.exports !== 'undefined') {
module.exports = soma;
}
if (typeof exports !== 'undefined') { if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
exports = module.exports = soma;
}
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) {
copy[cmd] = this.list[cmd]; if (this.list.hasOwnProperty(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,15 +922,16 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -860,15 +922,16 @@ 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) {
this.remove(cmd); if (this.list.hasOwnProperty(cmd)) {
this.remove(cmd);
}
} }
this.boundHandler = undefined; this.boundHandler = undefined;
this.dispatcher = undefined; this.dispatcher = 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; }
} else {
exports = soma; 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)) { //Convert baseName to array, and lop off the last part,
//If the baseName is a package name, then just treat it as one //so that . matches that 'directory' and not name of the baseName's
//name to concat the name with. //module. For instance, baseName of 'one/two/three', maps to
normalizedBaseParts = baseParts = [baseName]; //'one/two/three.js', but we want the directory, 'one/two' for
} else { //this normalization.
//Convert baseName to array, and lop off the last part, normalizedBaseParts = baseParts.slice(0, baseParts.length - 1);
//so that . matches that 'directory' and not name of the baseName's name = name.split('/');
//module. For instance, baseName of 'one/two/three', maps to lastIndex = name.length - 1;
//'one/two/three.js', but we want the directory, 'one/two' for
//this normalization. // If wanting node ID compatibility, strip .js from end
normalizedBaseParts = baseParts.slice(0, baseParts.length - 1); // 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,8 +642,8 @@ var requirejs, require, define; ...@@ -632,8 +642,8 @@ 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.
if (!mod.enabled) { if (!mod.enabled) {
...@@ -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);
} else {
mixin(config[prop], value, true);
} }
mixin(config[prop], value, true, 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 //Remove leading dot in main, so main paths are normalized,
//state for all package configs. //and remove any trailing .js, since different package
pkgs[pkgObj.name] = { //envs have different conventions: some use a module name,
name: pkgObj.name, //some use a file name.
location: location || pkgObj.name, config.pkgs[name] = pkgObj.name + '/' + (pkgObj.main || 'main')
//Remove leading dot in main, so main paths are normalized, .replace(currDirRegExp, '')
//and remove any trailing .js, since different package .replace(jsSuffixRegExp, '');
//envs have different conventions: some use a module name,
//some use a file name.
main: (pkgObj.main || 'main')
.replace(currDirRegExp, '')
.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);
setRegEX(value, true); setRegEX(value, true);
}
return tokenStart;
},
end: function(value) {
if (isDefined(value) && value !== '') {
tokenEnd = escapeRegExp(value);
setRegEX(value, false);
}
return tokenEnd;
}
};
var attributes = settings.attributes = {
skip: "data-skip",
repeat: "data-repeat",
src: "data-src",
href: "data-href",
show: "data-show",
hide: "data-hide",
cloak: "data-cloak",
checked: "data-checked",
disabled: "data-disabled",
multiple: "data-multiple",
readonly: "data-readonly",
selected: "data-selected",
template: "data-template",
html: "data-html"
};
var vars = settings.vars = {
index: "$index",
key: "$key"
};
var events = settings.events = {};
settings.eventsPrefix = 'data-';
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];
}
var regex = {
sequence: null,
token: null,
expression: null,
escape: /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,
trim: /^[\s+]+|[\s+]+$/g,
repeat: /(.*)\s+in\s+(.*)/,
func: /(.*)\((.*)\)/,
params: /,\s+|,|\s+,\s+/,
quote: /\"|\'/g,
content: /[^.|^\s]/gm,
depth: /..\//g,
string: /^(\"|\')(.*)(\"|\')$/
};
var ie = (function(){
if (typeof document !== 'object') return undefined;
var undef,
v = 3,
div = document.createElement('div'),
all = div.getElementsByTagName('i');
while (
div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->', all[0]
);
return v > 4 ? v : undef;
}());
function isArray(value) {
return Object.prototype.toString.apply(value) === '[object Array]';
};
function isObject(value) {
return typeof value === 'object';
}
function isString(value) {
return typeof value === 'string';
}
function isElement(value) {
return value ? value.nodeType > 0 : false;
};
function isTextNode(el) {
return el && el.nodeType && el.nodeType === 3;
}
function isFunction(value) {
return value && typeof value === 'function';
}
function isDefined(value) {
return value !== null && value !== undefined;
}
function isAttributeDefined(value) {
return (value === "" || value === true || value === "true" || !isDefined(value));
}
function isExpression(value) {
return value && isFunction(value.toString) && value.toString() === '[object Expression]';
}
function isNode(value) {
return value && isFunction(value.toString) && value.toString() === '[object Node]';
}
function isExpFunction(value) {
if (!isString(value)) return false;
return !!value.match(regex.func);
}
function childNodeIsTemplate(node) {
return node && node.parent && templates.get(node.element);
}
function escapeRegExp(str) {
return str.replace(regex.escape, "\\$&");
}
function setRegEX(nonEscapedValue, isStartToken) {
// sequence: \{\{.+?\}\}|[^{]+|\{(?!\{)[^{]*
var unescapedCurrentStartToken = tokens.start().replace(/\\/g, '');
var endSequence = "";
var ts = isStartToken ? nonEscapedValue : unescapedCurrentStartToken;
if (ts.length > 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.token = new RegExp(tokens.start() + ".*?" + tokens.end(), "g");
regex.expression = new RegExp(tokens.start() + "|" + tokens.end(), "gm");
}
function trim(value) {
return value.replace(regex.trim, '');
}
function trimQuotes(value) {
if (regex.string.test(value)) {
return value.substr(1, value.length-2);
}
return value;
}
function trimArray(value) {
if (value[0] === "") value.shift();
if (value[value.length-1] === "") value.pop();
return value;
}
function trimTokens(value) {
return value.replace(regex.expression, '');
}
function trimScopeDepth(value) {
return value.replace(regex.depth, '');
}
function insertBefore(referenceNode, newNode) {
if (!referenceNode.parentNode) return;
referenceNode.parentNode.insertBefore(newNode, referenceNode);
}
function insertAfter(referenceNode, newNode) {
if (!referenceNode.parentNode) return;
referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
}
function removeClass(elm, className) {
if (document.documentElement.classList) {
removeClass = function (elm, className) {
elm.classList.remove(className);
}
} else {
removeClass = function (elm, className) {
if (!elm || !elm.className) {
return false;
} }
var reg = new RegExp("(^|\\s)" + className + "(\\s|$)", "g"); return tokenStart;
elm.className = elm.className.replace(reg, "$2"); },
end: function(value) {
if (isDefined(value) && value !== '') {
tokenEnd = escapeRegExp(value);
setRegEX(value, false);
}
return tokenEnd;
}
};
var attributes = settings.attributes = {
skip: 'data-skip',
repeat: 'data-repeat',
src: 'data-src',
href: 'data-href',
show: 'data-show',
hide: 'data-hide',
cloak: 'data-cloak',
checked: 'data-checked',
disabled: 'data-disabled',
multiple: 'data-multiple',
readonly: 'data-readonly',
selected: 'data-selected',
template: 'data-template',
html: 'data-html'
};
var vars = settings.vars = {
index: '$index',
key: '$key',
element: '$element',
parentElement: '$parentElement',
attribute: '$attribute',
scope: '$scope'
};
var events = settings.events = {};
settings.eventsPrefix = 'data-';
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];
}
var regex = {
sequence: null,
token: null,
expression: null,
escape: /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,
trim: /^[\s+]+|[\s+]+$/g,
repeat: /(.*)\s+in\s+(.*)/,
func: /(.*)\((.*)\)/,
params: /,\s+|,|\s+,\s+/,
quote: /\"|\'/g,
content: /[^.|^\s]/gm,
depth: /..\//g,
string: /^(\"|\')(.*)(\"|\')$/
};
var ie = (function(){
if (typeof document !== 'object') {
return undefined;
}
var v = 3,
stop = false,
div = document.createElement('div');
while (!stop) {
div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->';
if (!div.getElementsByTagName('i')[0]) {
stop = true;
}
}
return v > 4 ? v : undefined;
}());
function isArray(value) {
return Object.prototype.toString.apply(value) === '[object Array]';
}
function isObject(value) {
return typeof value === 'object';
}
function isString(value) {
return typeof value === 'string';
}
function isElement(value) {
return value ? value.nodeType > 0 : false;
}
function isTextNode(el) {
return el && el.nodeType && el.nodeType === 3;
}
function isFunction(value) {
return value && typeof value === 'function';
}
function isDefined(value) {
return value !== null && value !== undefined;
}
function normalizeBoolean(value) {
if (!isDefined(value)) {
return false;
}
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]';
}
function isExpFunction(value) {
if (!isString(value)) {
return false;
}
return !!value.match(regex.func);
}
function childNodeIsTemplate(node) {
return node && node.parent && templates.get(node.element);
}
function escapeRegExp(str) {
return str.replace(regex.escape, '\\$&');
} }
removeClass(elm, className); function setRegEX(nonEscapedValue, isStartToken) {
} // sequence: \{\{.+?\}\}|[^{]+|\{(?!\{)[^{]*
// jquery contains var unescapedCurrentStartToken = tokens.start().replace(/\\/g, '');
var contains = typeof document !== 'object' ? function(){} : document.documentElement.contains ? var endSequence = '';
function( a, b ) { var ts = isStartToken ? nonEscapedValue : unescapedCurrentStartToken;
var adown = a.nodeType === 9 ? a.documentElement : a, if (ts.length > 1) {
bup = b && b.parentNode; endSequence = '|\\' + ts.substr(0, 1) + '(?!\\' + ts.substr(1, 1) + ')[^' + ts.substr(0, 1) + ']*';
return a === bup || !!( bup && bup.nodeType === 1 && adown.contains && adown.contains(bup) ); }
} : regex.sequence = new RegExp(tokens.start() + '.+?' + tokens.end() + '|[^' + tokens.start() + ']+' + endSequence, 'g');
document.documentElement.compareDocumentPosition ? regex.token = new RegExp(tokens.start() + '.*?' + tokens.end(), 'g');
regex.expression = new RegExp(tokens.start() + '|' + tokens.end(), 'gm');
}
function trim(value) {
return value.replace(regex.trim, '');
}
function trimQuotes(value) {
if (regex.string.test(value)) {
return value.substr(1, value.length-2);
}
return value;
}
function trimArray(value) {
if (value[0] === '') {
value.shift();
}
if (value[value.length-1] === '') {
value.pop();
}
return value;
}
function trimTokens(value) {
return value.replace(regex.expression, '');
}
function trimScopeDepth(value) {
return value.replace(regex.depth, '');
}
function insertBefore(referenceNode, newNode) {
if (!referenceNode.parentNode) {
return;
}
referenceNode.parentNode.insertBefore(newNode, referenceNode);
}
function insertAfter(referenceNode, newNode) {
if (!referenceNode.parentNode) {
return;
}
referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
}
function removeClass(elm, className) {
var rmc;
if (typeof document === 'object' && document.documentElement.classList) {
rmc = function (elm, className) {
elm.classList.remove(className);
};
} else {
rmc = function (elm, className) {
if (!elm || !elm.className) {
return false;
}
var reg = new RegExp('(^|\\s)' + className + '(\\s|$)', 'g');
elm.className = elm.className.replace(reg, '$2');
};
}
rmc(elm, className);
}
// jquery contains
var contains = typeof document !== 'object' ? function(){} : document.documentElement.contains ?
function( a, b ) { function( a, b ) {
return b && !!( a.compareDocumentPosition( b ) & 16 ); var adown = a.nodeType === 9 ? a.documentElement : a,
bup = b && b.parentNode;
return a === bup || !!( bup && bup.nodeType === 1 && adown.contains && adown.contains(bup) );
} : } :
function( a, b ) { document.documentElement.compareDocumentPosition ?
while ( (b = b.parentNode) ) { function( a, b ) {
if ( b === a ) { return b && !!( a.compareDocumentPosition( b ) & 16 );
return true; } :
function( a, b ) {
while ( (b = b.parentNode) ) {
if ( b === a ) {
return true;
}
} }
return false;
};
function HashMap(id) {
var items = {};
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;}
function uuid() { return ++count + id; }
function getKey(target) {
if (!target) {
return;
} }
return false; if (typeof target !== 'object') {
return target;
}
var result;
try {
// IE 7-8 needs a try catch, seems like I can't add a property on text nodes
result = target[id] ? target[id] : target[id] = uuid();
} catch(err){}
return result;
}
this.remove = function(key) {
delete items[getKey(key)];
};
this.get = function(key) {
return items[getKey(key)];
};
this.put = function(key, value) {
items[getKey(key)] = value;
};
this.has = function(key) {
return typeof items[getKey(key)] !== 'undefined';
};
this.getData = function() {
return items;
}; };
this.dispose = function() {
for (var key in items) {
if (items.hasOwnProperty(key)) {
delete items[key];
}
}
this.length = 0;
};
}
function getRepeaterData(repeaterValue, scope) {
var parts = repeaterValue.match(regex.repeat);
if (!parts) {
return;
}
var source = parts[2];
var exp = new Expression(source);
return exp.getValue(scope);
}
function HashMap() { function updateScopeWithRepeaterData(repeaterValue, scope, data) {
var items = {}; var parts = repeaterValue.match(regex.repeat);
var id = 1; if (!parts) {
//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;} return;
function uuid() { return ++id; }; }
function getKey(target) { var name = parts[1];
if (!target) return; scope[name] = data;
if (typeof target !== 'object') return target; }
var result; function getWatcherValue(exp, newValue) {
try { var node = exp.node || exp.attribute.node;
// IE 7-8 needs a try catch, seems like I can't add a property on text nodes var watchers = node.template.watchers;
result = target.hashkey ? target.hashkey : target.hashkey = uuid(); var nodeTarget = node.element;
} catch(err){}; if (!watchers) {
return result; return newValue;
} }
this.remove = function(key) { var watcherNode = watchers.get(nodeTarget);
delete items[getKey(key)]; if (!watcherNode && isTextNode(node.element) && node.parent) {
} watcherNode = watchers.get(node.parent.element);
this.get = function(key) { }
return items[getKey(key)]; var watcher = watcherNode ? watcherNode : watchers.get(exp.pattern);
} if (isFunction(watcher)) {
this.put = function(key, value) { var watcherValue = watcher(exp.value, newValue, exp.pattern, node.scope, node, exp.attribute);
items[getKey(key)] = value; if (isDefined(watcherValue)) {
} return watcherValue;
this.has = function(key) {
return typeof items[getKey(key)] !== 'undefined';
}
this.getData = function() {
return items;
}
this.dispose = function() {
for (var key in items) {
delete items[key];
}
this.length = 0;
}
}
function getRepeaterData(repeaterValue, scope) {
var parts = repeaterValue.match(regex.repeat);
if (!parts) return;
var source = parts[2];
var exp = new Expression(source);
return exp.getValue(scope);
}
function updateScopeWithRepeaterData(repeaterValue, scope, data) {
var parts = repeaterValue.match(regex.repeat);
if (!parts) return;
var name = parts[1];
scope[name] = data;
}
function getWatcherValue(exp, newValue) {
var node = exp.node || exp.attribute.node;
var watchers = node.template.watchers;
var nodeTarget = node.element;
if (!watchers) return newValue;
var watcherNode = watchers.get(nodeTarget);
if (!watcherNode && isTextNode(node.element) && node.parent) watcherNode = watchers.get(node.parent.element);
var watcher = watcherNode ? watcherNode : watchers.get(exp.pattern);
if (isFunction(watcher)) {
var watcherValue = watcher(exp.value, newValue, exp.pattern, node.scope, node, exp.attribute);
if (isDefined(watcherValue)) {
return watcherValue;
}
}
return newValue;
}
function getScopeFromPattern(scope, pattern) {
var depth = getScopeDepth(pattern);
var scopeTarget = scope;
while (depth > 0) {
scopeTarget = scopeTarget._parent ? scopeTarget._parent : scopeTarget;
depth--;
}
return scopeTarget;
}
function getValueFromPattern(scope, pattern) {
var exp = new Expression(pattern);
return getValue(scope, exp.pattern, exp.path, exp.params);
}
function getValue(scope, pattern, pathString, params, getFunction, getParams, paramsFound) {
// string
if (regex.string.test(pattern)) {
return trimQuotes(pattern);
}
// find params
var paramsValues = [];
if (!paramsFound && params) {
for (var j = 0, jl = params.length; j < jl; j++) {
paramsValues.push(getValueFromPattern(scope, params[j]));
}
}
else paramsValues = paramsFound;
if (getParams) return paramsValues;
// find scope
var scopeTarget = getScopeFromPattern(scope, pattern);
// remove parent string
pattern = pattern.replace(/..\//g, '');
pathString = pathString.replace(/..\//g, '');
if (!scopeTarget) return undefined;
// search path
var path = scopeTarget;
var pathParts = pathString.split(/\.|\[|\]/g);
if (pathParts.length > 0) {
for (var i = 0, l = pathParts.length; i < l; i++) {
if (pathParts[i] !== "") {
path = path[pathParts[i]];
}
if (!isDefined(path)) {
// no path, search in parent
if (scopeTarget._parent) return getValue(scopeTarget._parent, pattern, pathString, params, getFunction, getParams, paramsValues);
else return undefined;
}
}
}
// return value
if (!isFunction(path)) {
return path;
}
else {
if (getFunction) return path;
else return path.apply(null, paramsValues);
}
return undefined;
}
function getExpressionPath(value) {
var val = value.split('(')[0];
val = trimScopeDepth(val);
return val;
}
function getParamsFromString(value) {
return trimArray(value.split(regex.params));
}
function getScopeDepth(value) {
var val = value.split('(')[0];
var matches = val.match(regex.depth);
return !matches ? 0 : matches.length;
}
function getNodeFromElement(element, scope, isRepeaterDescendant) {
var node = new Node(element, scope);
node.previousSibling = element.previousSibling;
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) {
node.skip = (value === "" || value === "true");
}
if (name === settings.attributes.html) {
node.html = (value === "" || value === "true");
}
if (name === settings.attributes.repeat && !isRepeaterDescendant) {
node.repeater = value;
}
if (
hasInterpolation(name + ':' + value) ||
name === settings.attributes.repeat ||
name === settings.attributes.skip ||
name === settings.attributes.html ||
name === settings.attributes.show ||
name === settings.attributes.hide ||
name === settings.attributes.href ||
name === settings.attributes.checked ||
name === settings.attributes.disabled ||
name === settings.attributes.multiple ||
name === settings.attributes.readonly ||
name === settings.attributes.selected ||
value.indexOf(settings.attributes.cloak) !== -1
) {
attributes.push(new Attribute(name, value, node));
}
if (events[name] && !isRepeaterDescendant) {
eventsArray.push({name:events[name], value:value});
attributes.push(new Attribute(name, value, node));
}
}
}
node.attributes = attributes;
for (var i = 0, l = eventsArray.length; i < l; i++) {
node.addEvent(eventsArray[i].name, eventsArray[i].value);
}
return node;
}
function hasInterpolation(value) {
var matches = value.match(regex.token);
return matches && matches.length > 0;
}
function hasContent(value) {
return regex.content.test(value)
}
function isElementValid(element) {
if (!element) return;
var type = element.nodeType;
if (!element || !type) return false;
// comment
if (type === 8) return false;
// empty text node
if (type === 3 && !hasContent(element.nodeValue) && !hasInterpolation(element.nodeValue)) return false;
// result
return true;
}
function compile(template, element, parent, nodeTarget) {
if (!isElementValid(element)) return;
// get node
var node;
if (!nodeTarget) {
node = getNodeFromElement(element, parent ? parent.scope : new Scope(helpersScopeObject)._createChild(), parent && (parent.repeater || parent.isRepeaterDescendant) );
}
else {
node = nodeTarget;
node.parent = parent;
}
if (parent && (parent.repeater || parent.isRepeaterDescendant)) {
node.isRepeaterDescendant = true;
}
node.template = template;
// children
if (node.skip) return;
var child = element.firstChild;
while (child) {
var childNode = compile(template, child, node);
if (childNode) {
childNode.parent = node;
node.children.push(childNode);
}
child = child.nextSibling;
}
return node;
}
function updateScopeWithData(scope, data) {
clearScope(scope);
for (var d in data) {
scope[d] = data[d];
}
}
function clearScope(scope) {
for (var key in scope) {
if (key.substr(0, 1) !== '_') {
scope[key] = null;
delete scope[key];
}
}
}
function updateNodeChildren(node) {
if (node.repeater || !node.children || childNodeIsTemplate(node)) return;
for (var i = 0, l = node.children.length; i < l; i++) {
node.children[i].update();
}
}
function renderNodeChildren(node) {
if (!node.children || childNodeIsTemplate(node)) return;
for (var i = 0, l = node.children.length; i < l; i++) {
node.children[i].render();
}
}
function renderNodeRepeater(node) {
var data = getRepeaterData(node.repeater, node.scope);
var previousElement;
if (isArray(data)) {
// process array
for (var i = 0, l1 = data.length, l2 = node.childrenRepeater.length, l = l1 > l2 ? l1 : l2; i < l; i++) {
if (i < l1) {
previousElement = createRepeaterChild(node, i, data[i], vars.index, i, previousElement);
}
else {
node.parent.element.removeChild(node.childrenRepeater[i].element);
node.childrenRepeater[i].dispose();
} }
} }
if (node.childrenRepeater.length > data.length) { return newValue;
node.childrenRepeater.length = data.length; }
function getScopeFromPattern(scope, pattern) {
var depth = getScopeDepth(pattern);
var scopeTarget = scope;
while (depth > 0) {
scopeTarget = scopeTarget._parent ? scopeTarget._parent : scopeTarget;
depth--;
} }
return scopeTarget;
} }
else {
// process object function getValueFromPattern(scope, pattern, context) {
var count = -1; var exp = new Expression(pattern);
for (var o in data) { return getValue(scope, exp.pattern, exp.path, exp.params, undefined, undefined, undefined, context);
count++; }
previousElement = createRepeaterChild(node, count, data[o], vars.key, o, previousElement);
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
if (regex.string.test(pattern)) {
return trimQuotes(pattern);
}
// find params
var paramsValues = [];
if (!paramsFound && params) {
for (var j = 0, jl = params.length; j < jl; j++) {
paramsValues.push(getValueFromPattern(scope, params[j], context));
}
}
else {
paramsValues = paramsFound;
}
if (getParams) {
return paramsValues;
}
// find scope
var scopeTarget = getScopeFromPattern(scope, pattern);
// remove parent string
pattern = pattern.replace(/..\//g, '');
pathString = pathString.replace(/..\//g, '');
if (!scopeTarget) {
return undefined;
}
// search path
var path = scopeTarget;
var pathParts = pathString.split(/\.|\[|\]/g);
if (pathParts.length > 0) {
for (var i = 0, l = pathParts.length; i < l; i++) {
if (pathParts[i] !== '') {
path = path[pathParts[i]];
}
if (!isDefined(path)) {
// no path, search in parent
if (scopeTarget._parent) {
return getValue(scopeTarget._parent, pattern, pathString, params, getFunction, getParams, paramsValues);
}
else {
return undefined;
}
}
}
}
// return value
if (!isFunction(path)) {
return path;
} }
var size = count; else {
while (count++ < node.childrenRepeater.length-1) { if (getFunction) {
node.parent.element.removeChild(node.childrenRepeater[count].element); return path;
node.childrenRepeater[count].dispose(); }
else {
return path.apply(null, paramsValues);
}
} }
node.childrenRepeater.length = size+1; return undefined;
} }
if (node.element.parentNode) {
node.element.parentNode.removeChild(node.element); function getExpressionPath(value) {
var val = value.split('(')[0];
val = trimScopeDepth(val);
return val;
} }
}
function cloneRepeaterNode(element, node) { function getParamsFromString(value) {
var newNode = new Node(element, node.scope._createChild()); return trimArray(value.split(regex.params));
if (node.attributes) { }
var attrs = [];
for (var i = 0, l = node.attributes.length; i < l; i++) { function getScopeDepth(value) {
newNode.renderAsHtml = node.renderAsHtml; var val = value.split('(')[0];
if (node.attributes[i].name === settings.attributes.skip) { var matches = val.match(regex.depth);
newNode.skip = (node.attributes[i].value === "" || node.attributes[i].value === "true"); return !matches ? 0 : matches.length;
}
function addAttribute(node, name, value) {
var attr;
node.attributes = node.attributes || [];
if (name === settings.attributes.skip) {
node.skip = normalizeBoolean(value);
}
if (name === settings.attributes.html) {
node.html = normalizeBoolean(value);
}
if (name === settings.attributes.repeat && !node.isRepeaterDescendant) {
node.repeater = value;
}
if (
hasInterpolation(name + ':' + value) ||
name === settings.attributes.repeat ||
name === settings.attributes.skip ||
name === settings.attributes.html ||
name === settings.attributes.show ||
name === settings.attributes.hide ||
name === settings.attributes.href ||
name === settings.attributes.checked ||
name === settings.attributes.disabled ||
name === settings.attributes.multiple ||
name === settings.attributes.readonly ||
name === settings.attributes.selected ||
value.indexOf(settings.attributes.cloak) !== -1
) {
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 (node.attributes[i].name === settings.attributes.html) { }
newNode.html = (node.attributes[i].value === "" || node.attributes[i].value === "true"); for (var a=0, b=eventsArray.length; a<b; a++) {
node.addEvent(eventsArray[a].name, eventsArray[a].value, eventsArray[a].attr);
}
return node;
}
function hasInterpolation(value) {
var matches = value.match(regex.token);
return matches && matches.length > 0;
}
function hasContent(value) {
return regex.content.test(value);
}
function isElementValid(element) {
if (!element) {
return;
}
var type = element.nodeType;
if (!element || !type) {
return false;
}
// comment
if (type === 8) {
return false;
}
// empty text node
if (type === 3 && !hasContent(element.nodeValue) && !hasInterpolation(element.nodeValue)) {
return false;
}
// result
return true;
}
function compile(template, element, parent, nodeTarget) {
if (!isElementValid(element)) {
return;
}
// get node
var node;
if (!nodeTarget) {
node = getNodeFromElement(element, parent ? parent.scope : new Scope(helpersScopeObject)._createChild());
}
else {
node = nodeTarget;
node.parent = parent;
}
if (parent && (parent.repeater || parent.isRepeaterChild)) {
node.isRepeaterChild = true;
}
node.template = template;
// children
if (node.skip) {
return;
}
var child = element.firstChild;
while (child) {
var childNode = compile(template, child, node);
if (childNode) {
childNode.parent = node;
node.children.push(childNode);
} }
if (node.attributes[i].name !== attributes.repeat) { child = child.nextSibling;
var attribute = new Attribute(node.attributes[i].name, node.attributes[i].value, newNode); }
attrs.push(attribute); return node;
}
function updateScopeWithData(scope, data) {
clearScope(scope);
for (var d in data) {
if (data.hasOwnProperty(d)) {
scope[d] = data[d];
} }
if (events[node.attributes[i].name]) { }
newNode.addEvent(events[node.attributes[i].name], node.attributes[i].value); }
function clearScope(scope) {
for (var key in scope) {
if (scope.hasOwnProperty(key)) {
if (key.substr(0, 1) !== '_') {
scope[key] = null;
delete scope[key];
}
} }
} }
newNode.attributes = attrs;
} }
return newNode;
}
function createRepeaterChild(node, count, data, indexVar, indexVarValue, previousElement) { function updateNodeChildren(node) {
var existingChild = node.childrenRepeater[count]; if (node.repeater || !node.children || childNodeIsTemplate(node)) {
if (!existingChild) { return;
// no existing node }
var newElement = node.element.cloneNode(true); for (var i = 0, l = node.children.length; i < l; i++) {
// can't recreate the node with a cloned element on IE7 node.children[i].update();
// be cause the attributes are not specified annymore (attribute.specified) }
//var newNode = getNodeFromElement(newElement, node.scope._createChild(), true); }
var newNode = cloneRepeaterNode(newElement, node);
newNode.isRepeaterChild = true; function renderNodeChildren(node) {
newNode.parent = node.parent; if (!node.children || childNodeIsTemplate(node)) {
newNode.template = node.template; return;
node.childrenRepeater[count] = newNode; }
updateScopeWithRepeaterData(node.repeater, newNode.scope, data); for (var i = 0, l = node.children.length; i < l; i++) {
newNode.scope[indexVar] = indexVarValue; node.children[i].render();
compile(node.template, newElement, node.parent, newNode); }
newNode.update(); }
newNode.render();
if (!previousElement) { function renderNodeRepeater(node) {
if (node.previousSibling) insertAfter(node.previousSibling, newElement); var data = getRepeaterData(node.repeater, node.scope);
else if (node.nextSibling) insertBefore(node.nextSibling, newElement); var previousElement;
else node.parent.element.appendChild(newElement); if (isArray(data)) {
// process array
for (var i = 0, l1 = data.length, l2 = node.childrenRepeater.length, l = l1 > l2 ? l1 : l2; i < l; i++) {
if (i < l1) {
previousElement = createRepeaterChild(node, i, data[i], vars.index, i, previousElement);
}
else {
node.parent.element.removeChild(node.childrenRepeater[i].element);
node.childrenRepeater[i].dispose();
}
}
if (node.childrenRepeater.length > data.length) {
node.childrenRepeater.length = data.length;
}
} }
else { else {
insertAfter(previousElement, newElement); // process object
var count = -1;
for (var o in data) {
if (data.hasOwnProperty(o)) {
count++;
previousElement = createRepeaterChild(node, count, data[o], vars.key, o, previousElement);
}
}
var size = count;
while (count++ < node.childrenRepeater.length-1) {
node.parent.element.removeChild(node.childrenRepeater[count].element);
node.childrenRepeater[count].dispose();
}
node.childrenRepeater.length = size+1;
} }
return newElement; if (node.element.parentNode) {
} node.element.parentNode.removeChild(node.element);
else { }
// existing node }
updateScopeWithRepeaterData(node.repeater, existingChild.scope, data);
existingChild.scope[indexVar] = indexVarValue; function compileClone(node, newNode) {
existingChild.update(); if (!isElementValid(newNode.element)) {
existingChild.render(); return;
return existingChild.element; }
} // create attribute
} if (node.attributes) {
for (var i= 0, l=node.attributes.length; i<l; i++) {
var Scope = function(data) { var attr = node.attributes[i];
function createChild(data) { var newAttr = addAttribute(newNode, attr.name, attr.value);
var obj = createObject(data); if (events[attr.name]) {
obj._parent = this; newNode.addEvent(events[attr.name], attr.value, newAttr);
this._children.push(obj); }
return obj;
}
function createObject(data) {
var obj = data || {};
obj._parent = null;
obj._children = [];
obj._createChild = function(data) {
return createChild.apply(obj, arguments);
}
return obj;
}
return createObject(data);
};
var Node = function(element, scope) {
this.element = element;
this.scope = scope;
this.attributes = null;
this.value = null;
this.interpolation = null;
this.invalidate = false;
this.skip = false;
this.repeater = null;
this.isRepeaterDescendant = false;
this.isRepeaterChild = false;
this.parent = null;
this.children = [];
this.childrenRepeater = [];
this.previousSibling = null;
this.nextSibling = null;
this.template = null;
this.eventHandlers = {};
this.html = false;
if (isTextNode(this.element)) {
this.value = this.element.nodeValue;
this.interpolation = new Interpolation(this.value, this, undefined);
}
};
Node.prototype = {
toString: function() {
return '[object Node]';
},
dispose: function() {
this.clearEvents();
var i, l;
if (this.children) {
for (i = 0, l = this.children.length; i < l; i++) {
this.children[i].dispose();
} }
} }
if (this.childrenRepeater) { // children
for (i = 0, l = this.childrenRepeater.length; i < l; i++) { var child = node.element.firstChild;
this.childrenRepeater[i].dispose(); 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;
} }
if (this.attributes) { return newChildNode;
for (i = 0, l = this.attributes.length; i < l; i++) { }
this.attributes[i].dispose();
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;
}
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);
} }
} }
if (this.interpolation) { else {
this.interpolation.dispose(); insertAfter(previousElement, newElement);
} }
this.element = null; }
this.scope = null;
function createRepeaterChild(node, count, data, indexVar, indexVarValue, previousElement) {
var existingChild = node.childrenRepeater[count];
if (!existingChild) {
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
// be cause the attributes are not specified anymore (attribute.specified)
//var newNode = getNodeFromElement(newElement, node.scope._createChild(), true);
var newNode = cloneRepeaterNode(newElement, node);
node.childrenRepeater[count] = newNode;
updateScopeWithRepeaterData(node.repeater, newNode.scope, data);
newNode.scope[indexVar] = indexVarValue;
newNode.update();
newNode.render();
return newElement;
}
else {
// existing node
updateScopeWithRepeaterData(node.repeater, existingChild.scope, data);
existingChild.scope[indexVar] = indexVarValue;
existingChild.update();
existingChild.render();
return existingChild.element;
}
}
var Scope = function(data) {
var self;
function createChild(data) {
var obj = createObject(data);
obj._parent = self;
self._children.push(obj);
return obj;
}
function createObject(data) {
var obj = data || {};
obj._parent = null;
obj._children = [];
obj._createChild = function() {
self = obj;
return createChild.apply(obj, arguments);
};
return obj;
}
return createObject(data);
};
var Node = function(element, scope) {
this.element = element;
this.scope = scope;
this.attributes = null; this.attributes = null;
this.attributesHashMap = null;
this.value = null; this.value = null;
this.interpolation = null; this.interpolation = null;
this.invalidate = false;
this.skip = false;
this.repeater = null; this.repeater = null;
this.isRepeaterDescendant = false;
this.isRepeaterChild = false;
this.parent = null; this.parent = null;
this.children = null; this.children = [];
this.childrenRepeater = null; this.childrenRepeater = [];
this.previousSibling = null; this.previousSibling = null;
this.nextSibling = null; this.nextSibling = null;
this.template = null; this.template = null;
this.eventHandlers = null; this.eventHandlers = {};
}, this.html = false;
getNode: function(element) {
var node; if (isTextNode(this.element)) {
if (element === this.element) return this; this.value = this.element.nodeValue;
else if (this.childrenRepeater.length > 0) { this.interpolation = new Interpolation(this.value, this, undefined);
for (var k = 0, kl = this.childrenRepeater.length; k < kl; k++) {
node = this.childrenRepeater[k].getNode(element);
if (node) return node;
}
} }
else {
};
Node.prototype = {
toString: function() {
return '[object Node]';
},
dispose: function() {
this.clearEvents();
var i, l;
if (this.children) {
for (i = 0, l = this.children.length; i < l; i++) {
this.children[i].dispose();
}
}
if (this.childrenRepeater) {
for (i = 0, l = this.childrenRepeater.length; i < l; i++) {
this.childrenRepeater[i].dispose();
}
}
if (this.attributes) {
for (i = 0, l = this.attributes.length; i < l; i++) {
this.attributes[i].dispose();
}
}
if (this.interpolation) {
this.interpolation.dispose();
}
this.element = null;
this.scope = null;
this.attributes = null;
this.value = null;
this.interpolation = null;
this.repeater = null;
this.parent = null;
this.children = null;
this.childrenRepeater = null;
this.previousSibling = null;
this.nextSibling = null;
this.template = null;
this.eventHandlers = null;
},
getNode: function(element) {
var node;
if (element === this.element) {
return this;
}
if (this.childrenRepeater.length > 0) {
for (var k = 0, kl = this.childrenRepeater.length; k < kl; k++) {
node = this.childrenRepeater[k].getNode(element);
if (node) {
return node;
}
}
}
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; },
}, getAttribute: function(name) {
getAttribute: function(name) { if (this.attributes) {
if (this.attributes) { for (var i = 0, l = this.attributes.length; i < l; i++) {
for (var i = 0, l = this.attributes.length; i < l; i++) { var att = this.attributes[i];
var att = this.attributes[i]; if (att.interpolationName && att.interpolationName.value === name) {
if (att.interpolationName && att.interpolationName.value === name) { return att;
return att; }
} }
} }
} },
}, update: function() {
update: function() { if (childNodeIsTemplate(this)) {
if (childNodeIsTemplate(this)) return; return;
if (isDefined(this.interpolation)) {
this.interpolation.update();
}
if (isDefined(this.attributes)) {
for (var i = 0, l = this.attributes.length; i < l; i++) {
this.attributes[i].update();
} }
} if (isDefined(this.interpolation)) {
updateNodeChildren(this); this.interpolation.update();
},
invalidateData: function() {
if (childNodeIsTemplate(this)) return;
this.invalidate = true;
var i, l;
if (this.attributes) {
for (i = 0, l = this.attributes.length; i < l; i++) {
this.attributes[i].invalidate = true;
} }
} if (isDefined(this.attributes)) {
for (i = 0, l = this.childrenRepeater.length; i < l; i++) { for (var i = 0, l = this.attributes.length; i < l; i++) {
this.childrenRepeater[i].invalidateData(); this.attributes[i].update();
} }
for (i = 0, l = this.children.length; i < l; i++) {
this.children[i].invalidateData();
}
},
addEvent: function(type, pattern) {
if (this.repeater) return;
if (this.eventHandlers[type]) {
this.removeEvent(type);
}
var scope = this.scope;
var handler = function(event) {
var exp = new Expression(pattern, this.node);
var func = exp.getValue(scope, true);
var params = exp.getValue(scope, false, true);
params.unshift(event);
if (func) {
func.apply(null, params);
} }
}; updateNodeChildren(this);
this.eventHandlers[type] = handler; },
addEvent(this.element, type, handler); invalidateData: function() {
}, if (childNodeIsTemplate(this)) {
removeEvent: function(type) { return;
removeEvent(this.element, type, this.eventHandlers[type]); }
this.eventHandlers[type] = null; this.invalidate = true;
delete this.eventHandlers[type]; var i, l;
}, if (this.attributes) {
clearEvents: function() { for (i = 0, l = this.attributes.length; i < l; i++) {
if (this.eventHandlers) { this.attributes[i].invalidate = true;
for (var key in this.eventHandlers) {
this.removeEvent(key, this.eventHandlers[key]);
}
}
if (this.children) {
for (var k = 0, kl = this.children.length; k < kl; k++) {
this.children[k].clearEvents();
}
}
if (this.childrenRepeater) {
for (var f = 0, fl = this.childrenRepeater.length; f < fl; f++) {
this.childrenRepeater[f].clearEvents();
}
}
},
render: function() {
if (childNodeIsTemplate(this)) return;
if (this.invalidate) {
this.invalidate = false;
if (isTextNode(this.element)) {
if (this.parent && this.parent.html) {
this.value = this.parent.element.innerHTML = this.interpolation.render();
} }
else { }
this.value = this.element.nodeValue = this.interpolation.render(); for (i = 0, l = this.childrenRepeater.length; i < l; i++) {
this.childrenRepeater[i].invalidateData();
}
for (i = 0, l = this.children.length; i < l; i++) {
this.children[i].invalidateData();
}
},
addEvent: function(type, pattern, attr) {
if (this.repeater) {
return;
}
if (this.eventHandlers[type]) {
this.removeEvent(type);
}
var scope = this.scope;
var node = this;
var handler = function(event) {
var exp = new Expression(pattern, node, attr);
var func = exp.getValue(scope, true);
var params = exp.getValue(scope, false, true);
params.unshift(event);
if (func) {
func.apply(null, params);
}
};
this.eventHandlers[type] = handler;
addEvent(this.element, type, handler);
},
removeEvent: function(type) {
removeEvent(this.element, type, this.eventHandlers[type]);
this.eventHandlers[type] = null;
delete this.eventHandlers[type];
},
clearEvents: function() {
if (this.eventHandlers) {
for (var key in this.eventHandlers) {
if (this.eventHandlers.hasOwnProperty(key)) {
this.removeEvent(key);
}
} }
} }
} if (this.children) {
if (this.attributes) { for (var k = 0, kl = this.children.length; k < kl; k++) {
for (var i = 0, l = this.attributes.length; i < l; i++) { this.children[k].clearEvents();
this.attributes[i].render(); }
} }
} if (this.childrenRepeater) {
if (this.repeater) { for (var f = 0, fl = this.childrenRepeater.length; f < fl; f++) {
renderNodeRepeater(this); this.childrenRepeater[f].clearEvents();
} }
else { }
renderNodeChildren(this); },
} render: function() {
} if (childNodeIsTemplate(this)) {
}; return;
var Attribute = function(name, value, node) { }
this.name = name; if (this.invalidate) {
this.value = value; this.invalidate = false;
this.node = node; if (isTextNode(this.element)) {
this.interpolationName = new Interpolation(this.name, null, this); if (this.parent && this.parent.html) {
this.interpolationValue = new Interpolation(this.value, null, this); this.value = this.parent.element.innerHTML = this.interpolation.render();
this.invalidate = false; }
}; else {
Attribute.prototype = { this.value = this.element.nodeValue = this.interpolation.render();
toString: function() { }
return '[object Attribute]'; }
}, }
dispose: function() { if (this.attributes) {
if (this.interpolationName) this.interpolationName.dispose(); for (var i = 0, l = this.attributes.length; i < l; i++) {
if (this.interpolationValue) this.interpolationValue.dispose(); this.attributes[i].render();
this.interpolationName = null; }
this.interpolationValue = null; }
this.node = null; if (this.repeater) {
this.name = null; renderNodeRepeater(this);
this.value = null;
this.previousName = null;
},
update: function() {
this.interpolationName.update();
this.interpolationValue.update();
},
render: function() {
if (this.node.repeater) return;
var element = this.node.element;
if (this.invalidate) {
this.invalidate = false;
this.previousName = this.name;
this.name = isDefined(this.interpolationName.render()) ? this.interpolationName.render() : this.name;
this.value = isDefined(this.interpolationValue.render()) ? this.interpolationValue.render() : this.value;
if (this.name === attributes.src) {
renderSrc(this.name, this.value);
}
else if (this.name === attributes.href) {
renderHref(this.name, this.value);
} }
else { else {
if (this.node.isRepeaterChild && ie === 7) { renderNodeChildren(this);
// delete attributes on cloned elements crash IE7 }
}
};
var Attribute = function(name, value, node) {
this.name = name;
this.value = value;
this.node = node;
this.interpolationName = new Interpolation(this.name, null, this);
this.interpolationValue = new Interpolation(this.value, null, this);
this.invalidate = false;
};
Attribute.prototype = {
toString: function() {
return '[object Attribute]';
},
dispose: function() {
if (this.interpolationName) {
this.interpolationName.dispose();
}
if (this.interpolationValue) {
this.interpolationValue.dispose();
}
this.interpolationName = null;
this.interpolationValue = null;
this.node = null;
this.name = null;
this.value = null;
this.previousName = null;
},
update: function() {
if (this.node.repeater) {
return;
}
this.interpolationName.update();
this.interpolationValue.update();
},
render: function() {
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 { else {
this.node.element.removeAttribute(this.interpolationName.value); element.removeAttribute(attrName);
} }
if (this.previousName) { }
if (ie === 7 && this.previousName === 'class') { // src attribute
// iE function renderSrc(value) {
this.node.element.className = ""; element.setAttribute('src', value);
}
// href attribute
function renderHref(value) {
element.setAttribute('href', value);
}
var element = this.node.element;
if (this.invalidate) {
this.invalidate = false;
this.previousName = this.name;
this.name = isDefined(this.interpolationName.render()) ? this.interpolationName.render() : this.name;
this.value = isDefined(this.interpolationValue.render()) ? this.interpolationValue.render() : this.value;
if (this.name === attributes.src) {
renderSrc(this.value);
}
else if (this.name === attributes.href) {
renderHref(this.value);
}
else {
if (ie !== 7 || (ie === 7 && !this.node.isRepeaterChild)) {
this.node.element.removeAttribute(this.interpolationName.value);
} }
else { if (this.previousName) {
if (this.node.isRepeaterChild && ie === 7) { if (ie === 7 && this.previousName === 'class') {
// delete attributes on cloned elements crash IE7 // iE
this.node.element.className = '';
} }
else { else {
this.node.element.removeAttribute(this.previousName); if (ie !== 7 || (ie === 7 && !this.node.isRepeaterChild)) {
this.node.element.removeAttribute(this.previousName);
}
} }
} }
renderAttribute(this.name, this.value, this.node);
} }
renderAttribute(this.name, this.value, this.previousName); }
// cloak
if (this.name === 'class' && this.value.indexOf(settings.attributes.cloak) !== -1) {
removeClass(this.node.element, settings.attributes.cloak);
}
// hide
if (this.name === attributes.hide) {
var bool = normalizeBoolean(this.value);
renderAttribute(this.name, bool, this.node);
element.style.display = bool ? 'none' : '';
}
// show
if (this.name === attributes.show) {
var bool = normalizeBoolean(this.value);
renderAttribute(this.name, bool, this.node);
element.style.display = bool ? '' : 'none';
}
// checked
if (this.name === attributes.checked) {
renderSpecialAttribute(this.value, 'checked');
renderAttribute(this.name, normalizeBoolean(this.value) ? true : false, this.node);
element.checked = normalizeBoolean(this.value) ? true : false;
}
// disabled
if (this.name === attributes.disabled) {
renderSpecialAttribute(this.value, 'disabled');
renderAttribute(this.name, normalizeBoolean(this.value) ? true : false, this.node);
}
// multiple
if (this.name === attributes.multiple) {
renderSpecialAttribute(this.value, 'multiple');
renderAttribute(this.name, normalizeBoolean(this.value) ? true : false, this.node);
}
// readonly
if (this.name === attributes.readonly) {
var bool = normalizeBoolean(this.value);
if (ie === 7) {
element.readOnly = bool ? true : false;
}
else {
renderSpecialAttribute(this.value, 'readonly');
}
renderAttribute(this.name, bool ? true : false, this.node);
}
// selected
if (this.name === attributes.selected) {
renderSpecialAttribute(this.value, 'selected');
renderAttribute(this.name, normalizeBoolean(this.value) ? true : false, this.node);
} }
} }
// cloak };
if (this.name === 'class' && this.value.indexOf(settings.attributes.cloak) !== -1) {
removeClass(this.node.element, settings.attributes.cloak); var Interpolation = function(value, node, attribute) {
} this.value = node && !isTextNode(node.element) ? trim(value) : value;
// hide this.node = node;
if (this.name === attributes.hide) { this.attribute = attribute;
element.style.display = isAttributeDefined(this.value) ? "none" : "block"; this.sequence = [];
} this.expressions = [];
// show var parts = this.value.match(regex.sequence);
if (this.name === attributes.show) { if (parts) {
element.style.display = isAttributeDefined(this.value) ? "block" : "none"; for (var i = 0, l = parts.length; i < l; i++) {
if (parts[i].match(regex.token)) {
var exp = new Expression(trimTokens(parts[i]), this.node, this.attribute);
this.sequence.push(exp);
this.expressions.push(exp);
}
else {
this.sequence.push(parts[i]);
}
}
trimArray(this.sequence);
} }
// checked };
if (this.name === attributes.checked) { Interpolation.prototype = {
renderSpecialAttribute(this.name, this.value, 'checked'); toString: function() {
element.checked = isAttributeDefined(this.value) ? true : false; return '[object Interpolation]';
},
dispose: function() {
if (this.expressions) {
for (var i = 0, l = this.expressions.length; i < l; i++) {
this.expressions[i].dispose();
}
}
this.value = null;
this.node = null;
this.attribute = null;
this.sequence = null;
this.expressions = null;
},
update: function() {
var i = -1, l = this.expressions.length;
while (++i < l) {
this.expressions[i].update();
}
},
render: function() {
var rendered = '';
if (this.sequence) {
for (var i = 0, l = this.sequence.length; i < l; i++) {
var val = '';
if (isExpression(this.sequence[i])) {
val = this.sequence[i].value;
}
else {
val = this.sequence[i];
}
if (!isDefined(val)) {
val = '';
}
rendered += val;
}
}
return rendered;
} }
// disabled };
if (this.name === attributes.disabled) {
renderSpecialAttribute(this.name, this.value, 'disabled'); var Expression = function(pattern, node, attribute) {
if (!isDefined(pattern)) {
return;
}
this.pattern = pattern;
this.isString = regex.string.test(pattern);
this.node = node;
this.attribute = attribute;
this.value = this.isString ? this.pattern : undefined;
if (this.isString) {
this.isFunction = false;
this.depth = null;
this.path = null;
this.params = null;
} }
// multiple else {
if (this.name === attributes.multiple) { this.isFunction = isExpFunction(this.pattern);
renderSpecialAttribute(this.name, this.value, 'multiple'); this.depth = getScopeDepth(this.pattern);
this.path = getExpressionPath(this.pattern);
this.params = !this.isFunction ? null : getParamsFromString(this.pattern.match(regex.func)[2]);
} }
// readonly };
if (this.name === attributes.readonly) { Expression.prototype = {
if (ie === 7) { toString: function() {
element.readOnly = isAttributeDefined(this.value) ? true : false; return '[object Expression]';
},
dispose: function() {
this.pattern = null;
this.node = null;
this.attribute = null;
this.path = null;
this.params = null;
this.value = null;
},
update: function() {
var node = this.node;
if (!node && this.attribute) {
node = this.attribute.node;
} }
else { if (!node && node.scope) {
renderSpecialAttribute(this.name, this.value, 'readonly'); return;
} }
} var newValue = this.getValue(node.scope);
// selected newValue = getWatcherValue(this, newValue);
if (this.name === attributes.selected) { if (this.value !== newValue) {
renderSpecialAttribute(this.name, this.value, 'selected'); this.value = newValue;
} (this.node || this.attribute).invalidate = true;
// normal attribute
function renderAttribute(name, value) {
if (ie === 7 && name === "class") {
element.className = value;
} }
else { },
element.setAttribute(name, value); getValue: function(scope, 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);
} }
// special attribute };
function renderSpecialAttribute(name, value, attrName) {
if (isAttributeDefined(value)) { var templates = new HashMap('st');
element.setAttribute(attrName, attrName);
var Template = function(element) {
this.watchers = new HashMap('stw');
this.node = null;
this.scope = null;
this.compile(element);
};
Template.prototype = {
toString: function() {
return '[object Template]';
},
compile: function(element) {
if (element) {
this.element = element;
} }
else { if (this.node) {
element.removeAttribute(attrName); this.node.dispose();
} }
this.node = compile(this, this.element);
this.node.root = true;
this.scope = this.node.scope;
},
update: function(data) {
if (isDefined(data)) {
updateScopeWithData(this.node.scope, data);
}
if (this.node) {
this.node.update();
}
},
render: function(data) {
this.update(data);
if (this.node) {
this.node.render();
}
},
invalidate: function() {
if (this.node) {
this.node.invalidateData();
}
},
watch: function(target, watcher) {
if ( (!isString(target) && !isElement(target)) || !isFunction(watcher)) {
return;
}
this.watchers.put(target, watcher);
},
unwatch: function(target) {
this.watchers.remove(target);
},
clearWatchers: function() {
this.watchers.dispose();
},
clearEvents: function() {
this.node.clearEvents();
},
getNode: function(element) {
return this.node.getNode(element);
},
dispose: function() {
templates.remove(this.element);
if (this.watchers) {
this.watchers.dispose();
}
if (this.node) {
this.node.dispose();
}
this.element = null;
this.watchers = null;
this.node = null;
} }
// 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) { // written by Dean Edwards, 2005
this.value = node && !isTextNode(node.element) ? trim(value) : value; // with input from Tino Zijdel, Matthias Miller, Diego Perini
this.node = node; // http://dean.edwards.name/weblog/2005/10/add-event/
this.attribute = attribute; function addEvent(element, type, handler) {
this.sequence = []; if (element.addEventListener) {
this.expressions = []; element.addEventListener(type, handler, false);
var parts = this.value.match(regex.sequence); } else {
if (parts) { // assign each event handler a unique ID
for (var i = 0, l = parts.length; i < l; i++) { if (!handler.$$guid) {
if (parts[i].match(regex.token)) { handler.$$guid = addEvent.guid++;
var exp = new Expression(trimTokens(parts[i]), this.node, this.attribute);
this.sequence.push(exp);
this.expressions.push(exp);
} }
else { // create a hash table of event types for the element
this.sequence.push(parts[i]); if (!element.events) {
element.events = {};
} }
// create a hash table of event handlers for each element/event pair
var handlers = element.events[type];
if (!handlers) {
handlers = element.events[type] = {};
// store the existing event handler (if there is one)
if (element['on' + type]) {
handlers[0] = element['on' + type];
}
}
// store the event handler in the hash table
handlers[handler.$$guid] = handler;
// assign a global event handler to do all the work
element['on' + type] = function(event) {
var returnValue = true;
// grab the event object (IE uses a global event object)
event = event || fixEvent(((this.ownerDocument || this.document || this).parentWindow || window).event);
// get a reference to the hash table of event handlers
var handlers = this.events[event.type];
// execute each event handler
for (var i in handlers) {
if (handlers.hasOwnProperty(i)) {
this.$$handleEvent = handlers[i];
if (this.$$handleEvent(event) === false) {
returnValue = false;
}
}
}
return returnValue;
};
} }
trimArray(this.sequence);
} }
}; // a counter used to create unique IDs
Interpolation.prototype = { addEvent.guid = 1;
toString: function() { function removeEvent(element, type, handler) {
return '[object Interpolation]'; if (element.removeEventListener) {
}, element.removeEventListener(type, handler, false);
dispose: function() { } else {
if (this.expressions) { // delete the event handler from the hash table
for (var i = 0, l = this.expressions.length; i < l; i++) { if (element.events && element.events[type]) {
this.expressions[i].dispose(); delete element.events[type][handler.$$guid];
} }
} }
this.value = null; }
this.node = null; function fixEvent(event) {
this.attribute = null; // add W3C standard event methods
this.sequence = null; event.preventDefault = fixEvent.preventDefault;
this.expressions = null; event.stopPropagation = fixEvent.stopPropagation;
}, return event;
update: function() { }
var i = -1, l = this.expressions.length; fixEvent.preventDefault = function() {
while (++i < l) { this.returnValue = false;
this.expressions[i].update(); };
} fixEvent.stopPropagation = function() {
}, this.cancelBubble = true;
render: function() { };
var rendered = "";
if (this.sequence) { var maxDepth;
for (var i = 0, l = this.sequence.length; i < l; i++) { var eventStore = [];
var val = "";
if (isExpression(this.sequence[i])) val = this.sequence[i].value; function parseEvents(element, object, depth) {
else val = this.sequence[i]; maxDepth = depth === undefined ? Number.MAX_VALUE : depth;
if (!isDefined(val)) val = ""; parseNode(element, object, 0, true);
rendered += val; }
}
} function parseNode(element, object, depth, isRoot) {
return rendered; if (!isElement(element)) {
} throw new Error('Error in soma.template.parseEvents, only a DOM Element can be parsed.');
};
var Expression = function(pattern, node, attribute) {
if (!isDefined(pattern)) return;
this.pattern = pattern;
this.isString = regex.string.test(pattern);
this.node = node;
this.attribute = attribute;
this.value = this.isString ? this.pattern : undefined;
if (this.isString) {
this.isFunction = false;
this.depth = null;
this.path = null;
this.params = null;
}
else {
this.isFunction = isExpFunction(this.pattern);
this.depth = getScopeDepth(this.pattern);
this.path = getExpressionPath(this.pattern);
this.params = !this.isFunction ? null : getParamsFromString(this.pattern.match(regex.func)[2]);
}
};
Expression.prototype = {
toString: function() {
return '[object Expression]';
},
dispose: function() {
this.pattern = null;
this.node = null;
this.attribute = null;
this.path = null;
this.params = null;
this.value = null;
},
update: function() {
var node = this.node;
if (!node && this.attribute) node = this.attribute.node;
if (!node && node.scope) return;
var newValue = this.getValue(node.scope);
newValue = getWatcherValue(this, newValue);
if (this.value !== newValue) {
this.value = newValue;
(this.node || this.attribute).invalidate = true;
}
},
getValue: function(scope, getFunction, getParams) {
return getValue(scope, this.pattern, this.path, this.params, getFunction, getParams);
}
};
var templates = new HashMap();
var Template = function(element) {
this.watchers = new HashMap();
this.node = null;
this.scope = null;
this.compile(element);
};
Template.prototype = {
toString: function() {
return '[object Template]';
},
compile: function(element) {
if (element) this.element = element;
if (this.node) this.node.dispose();
this.node = compile(this, this.element);
this.node.root = true;
this.scope = this.node.scope;
},
update: function(data) {
if (isDefined(data)) updateScopeWithData(this.node.scope, data);
if (this.node) this.node.update();
},
render: function(data) {
this.update(data);
if (this.node) this.node.render();
},
invalidate: function() {
if (this.node) this.node.invalidateData();
},
watch: function(target, watcher) {
if ( (!isString(target) && !isElement(target)) || !isFunction(watcher)) return;
this.watchers.put(target, watcher);
},
unwatch: function(target) {
this.watchers.remove(target);
},
clearWatchers: function() {
this.watchers.dispose();
},
clearEvents: function() {
this.node.clearEvents();
},
getNode: function(element) {
return this.node.getNode(element);
},
dispose: function() {
templates.remove(this.element);
if (this.watchers) {
this.watchers.dispose();
} }
if (this.node) { if (isRoot) {
this.node.dispose(); parseAttributes(element, object);
}
if (maxDepth === 0) {
return;
}
var child = element.firstChild;
while (child) {
if (child.nodeType === 1) {
if (depth < maxDepth) {
parseNode(child, object, ++depth);
parseAttributes(child, object);
}
}
child = child.nextSibling;
} }
this.element = null;
this.watchers = null;
this.node = null;
} }
};
function parseAttributes(element, object) {
// written by Dean Edwards, 2005 for (var attr, name, value, attrs = element.attributes, j = 0, jj = attrs && attrs.length; j < jj; j++) {
// with input from Tino Zijdel, Matthias Miller, Diego Perini attr = attrs[j];
// http://dean.edwards.name/weblog/2005/10/add-event/ if (attr.specified) {
function addEvent(element, type, handler) { name = attr.name;
if (element.addEventListener) { value = attr.value;
element.addEventListener(type, handler, false); if (events[name]) {
} else { var handler = getHandlerFromPattern(object, value);
// assign each event handler a unique ID if (handler && isFunction(handler)) {
if (!handler.$$guid) handler.$$guid = addEvent.guid++; addEvent(element, events[name], handler);
// create a hash table of event types for the element eventStore.push({element:element, type:events[name], handler:handler});
if (!element.events) element.events = {}; }
// create a hash table of event handlers for each element/event pair
var handlers = element.events[type];
if (!handlers) {
handlers = element.events[type] = {};
// store the existing event handler (if there is one)
if (element["on" + type]) {
handlers[0] = element["on" + type];
}
}
// store the event handler in the hash table
handlers[handler.$$guid] = handler;
// assign a global event handler to do all the work
element["on" + type] = handleEvent;
}
};
// 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;
// grab the event object (IE uses a global event object)
event = event || fixEvent(((this.ownerDocument || this.document || this).parentWindow || window).event);
// get a reference to the hash table of event handlers
var handlers = this.events[event.type];
// execute each event handler
for (var i in handlers) {
this.$$handleEvent = handlers[i];
if (this.$$handleEvent(event) === false) {
returnValue = false;
}
}
return returnValue;
};
function fixEvent(event) {
// add W3C standard event methods
event.preventDefault = fixEvent.preventDefault;
event.stopPropagation = fixEvent.stopPropagation;
return event;
};
fixEvent.preventDefault = function() {
this.returnValue = false;
};
fixEvent.stopPropagation = function() {
this.cancelBubble = true;
};
var maxDepth;
var eventStore = [];
function parseEvents(element, object, depth) {
maxDepth = depth === undefined ? Number.MAX_VALUE : depth;
parseNode(element, object, 0, true);
}
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 (isRoot) parseAttributes(element, object);
if (maxDepth === 0) return;
var child = element.firstChild;
while (child) {
if (child.nodeType === 1) {
if (depth < maxDepth) {
parseNode(child, object, ++depth);
parseAttributes(child, object);
}
}
child = child.nextSibling;
}
}
function parseAttributes(element, object) {
var attributes = [];
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 (events[name]) {
var handler = getHandlerFromPattern(object, value, element);
if (handler && isFunction(handler)) {
addEvent(element, events[name], handler);
eventStore.push({element:element, type:events[name], handler:handler});
} }
} }
} }
} }
}
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];
if (isFunction(object[func])) { if (isFunction(object[func])) {
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];
if (element === item.element || contains(element, item.element)) { if (element === item.element || contains(element, item.element)) {
removeEvent(item.element, item.type, item.handler); removeEvent(item.element, item.type, item.handler);
eventStore.splice(i, 1); eventStore.splice(i, 1);
}
} }
} }
}
if (settings.autocreate && typeof document === 'object') {
// 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;
var parse = function(element) { if (typeof document === 'object') {
var child = !element ? document.body : element.firstChild; // https://github.com/ded/domready
while (child) { 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 (child.nodeType === 1) { if (settings.autocreate) {
parse(child); var parse = function(element) {
var attrValue = child.getAttribute(attributes.template); var child = !element ? document.body : element.firstChild;
if (attrValue) { while (child) {
var getFunction = new Function('return ' + attrValue + ';'); if (child.nodeType === 1) {
try { parse(child);
var f = getFunction(); var attrValue = child.getAttribute(attributes.template);
if (isFunction(f)) { if (attrValue) {
soma.template.bootstrap(attrValue, child, f); var getFunction = new Function('return ' + attrValue + ';');
try {
var f = getFunction();
if (isFunction(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) {
} var tpl = createTemplate(element);
function bootstrapTemplate(attrValue, element, func) { func(tpl, tpl.scope, tpl.element, tpl.node);
var tpl = createTemplate(element); }
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 if (!isElement(target)) {
if (!isElement(target)) { throw new Error(soma.template.errors.TEMPLATE_STRING_NO_ELEMENT);
throw new Error(soma.template.errors.TEMPLATE_STRING_NO_ELEMENT); }
} target.innerHTML = source;
target.innerHTML = source;
element = target;
}
else if (isElement(source)) {
if (isElement(target)) {
// element template with target
target.innerHTML = source.innerHTML;
element = target; element = target;
} }
else if (isElement(source)) {
if (isElement(target)) {
// element template with target
target.innerHTML = source.innerHTML;
element = target;
}
else {
// element template
element = source;
}
}
else { else {
// element template throw new Error(soma.template.errors.TEMPLATE_NO_PARAM);
element = source;
} }
// existing template
if (getTemplate(element)) {
getTemplate(element).dispose();
templates.remove(element);
}
// create template
var template = new Template(element);
templates.put(element, template);
return template;
} }
else {
throw new Error(soma.template.errors.TEMPLATE_NO_PARAM);
}
// existing template
if (getTemplate(element)) {
getTemplate(element).dispose();
templates.remove(element);
}
// create template
var template = new Template(element);
templates.put(element, template);
return template;
}
function getTemplate(element) { function getTemplate(element) {
return templates.get(element); return templates.get(element);
}
function renderAllTemplates() {
for (var key in templates.getData()) {
templates.get(key).render();
} }
}
function appendHelpers(obj) { function renderAllTemplates() {
if (obj === null) { var data = templates.getData();
helpersObject = {}; for (var key in templates.getData()) {
helpersScopeObject = {}; if (data.hasOwnProperty(key)) {
templates.get(key).render();
}
}
} }
if (isDefined(obj) && isObject(obj)) {
for (var key in obj) { function appendHelpers(obj) {
if (obj.hasOwnProperty(key)) { if (obj === null) {
helpersObject[key] = helpersScopeObject[key] = obj[key]; helpersObject = {};
helpersScopeObject = {};
}
if (isDefined(obj) && isObject(obj)) {
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
helpersObject[key] = helpersScopeObject[key] = obj[key];
}
} }
} }
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);
for (var key in template) { for (var key in template) {
if (typeof template[key] === 'function') { if (typeof template[key] === 'function') {
cl.prototype[key] = template[key].bind(template); cl.prototype[key] = template[key].bind(template);
}
} }
cl.prototype.render = template.render.bind(template);
var childInjector = injector.createChild();
childInjector.mapValue('template', template);
childInjector.mapValue('scope', template.scope);
childInjector.mapValue('element', template.element);
return childInjector.createInstance(cl);
} }
cl.prototype.render = template.render.bind(template); return null;
var childInjector = injector.createChild(); };
childInjector.mapValue("template", template); soma.template.bootstrap = function(attrValue, element, func) {
childInjector.mapValue("scope", template.scope); instance.createTemplate(func, element);
childInjector.mapValue("element", template.element); };
return childInjector.createInstance(cl); };
} if (soma.plugins && soma.plugins.add) {
return null; soma.plugins.add(TemplatePlugin);
} }
soma.template.bootstrap = function(attrValue, element, func) {
instance.createTemplate(func, element); soma.template.Plugin = TemplatePlugin;
}
} // exports
if (soma.plugins && soma.plugins.add) { soma.template.create = createTemplate;
soma.plugins.add(TemplatePlugin); soma.template.get = getTemplate;
} soma.template.renderAll = renderAllTemplates;
soma.template.helpers = appendHelpers;
soma.template.Plugin = TemplatePlugin; soma.template.bootstrap = bootstrapTemplate;
soma.template.addEvent = addEvent;
// exports soma.template.removeEvent = removeEvent;
soma.template.create = createTemplate; soma.template.parseEvents = parseEvents;
soma.template.get = getTemplate; soma.template.clearEvents = clearEvents;
soma.template.renderAll = renderAllTemplates; soma.template.ready = ready;
soma.template.helpers = appendHelpers;
soma.template.bootstrap = bootstrapTemplate; // register for AMD module
soma.template.addEvent = addEvent; if (typeof define === 'function' && typeof define.amd !== 'undefined') {
soma.template.removeEvent = removeEvent; define('soma-template', soma.template);
soma.template.parseEvents = parseEvents; }
soma.template.clearEvents = clearEvents;
soma.template.ready = ready; // export for node.js
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
// register for AMD module module.exports = soma.template;
if (typeof define === 'function' && define.amd) { }
define("soma-template", soma.template); if (typeof exports !== 'undefined') {
} exports = soma.template;
}
// export for node.js
if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
exports = module.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 (arr[i] === value) {
if (this[i] === value) return true; 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) {
var vo = this.mappings[name]; if (this.mappings.hasOwnProperty(name)) {
if (vo.value === value || vo.cl === value) { var vo = this.mappings[name];
return vo.prop; if (vo.value === value || vo.cl === value) {
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) {
var vo = this.getMappingVo(name); if (this.mappings.hasOwnProperty(name)) {
if (target.hasOwnProperty(vo.prop)) { var vo = this.getMappingVo(name);
var val = this.getInjectedValue(vo, name); if (target.hasOwnProperty(vo.prop) || (target.constructor && target.constructor.prototype && target.constructor.prototype.hasOwnProperty(vo.prop)) ) {
target[name] = val; target[name] = this.getInjectedValue(vo, name);
}
} }
} }
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,14 +314,18 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -288,14 +314,18 @@ 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) {
var vo = this.mappings[name]; if (this.mappings.hasOwnProperty(name)) {
if (vo.cl == cl) { var vo = this.mappings[name];
if (vo.singleton) { if (vo.cl === cl) {
if (!vo.value) vo.value = this.createInstance.apply(this, arguments); if (vo.singleton) {
return vo.value; if (!vo.value) {
} vo.value = this.createInstance.apply(this, arguments);
else { }
return this.createInstance.apply(this, arguments); return vo.value;
}
else {
return this.createInstance.apply(this, arguments);
}
} }
} }
} }
...@@ -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,73 +374,72 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -344,73 +374,72 @@ 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 module !== 'undefined' && typeof module.exports !== 'undefined') {
module.exports = infuse;
}
if (typeof exports !== 'undefined') { if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
exports = module.exports = infuse;
}
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, undefined) { (function (soma) {
"use strict";
soma.events = {}; 'use strict';
soma.events.version = "0.5.2";
if (!Function.prototype.bind) { soma.events = {};
Function.prototype.bind = function bind(that) { soma.events.version = '0.5.6';
var target = this;
if (typeof target != "function") { if (!Function.prototype.bind) {
throw new Error("Error, you must bind a function."); Function.prototype.bind = function bind(that) {
} var target = this;
var args = Array.prototype.slice.call(arguments, 1); // for normal call if (typeof target !== 'function') {
var bound = function () { throw new Error('Error, you must bind a function.');
if (this instanceof bound) { }
var F = function(){}; var args = Array.prototype.slice.call(arguments, 1); // for normal call
F.prototype = target.prototype; var bound = function () {
var self = new F; if (this instanceof bound) {
var result = target.apply( var F = function(){};
self, F.prototype = target.prototype;
args.concat(Array.prototype.slice.call(arguments)) var self = new F();
); var result = target.apply(
if (Object(result) === result) { self,
return result; args.concat(Array.prototype.slice.call(arguments))
} );
return self; if (Object(result) === result) {
} else { return result;
return target.apply( }
that, return self;
args.concat(Array.prototype.slice.call(arguments)) } else {
); return target.apply(
} that,
}; args.concat(Array.prototype.slice.call(arguments))
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) {
this.defaultPrevented = true; return false;
if (this.isIE9()) this.IE9PreventDefault = true; }
if (this.isIE9orIE10()) {
this.IE9or10PreventDefault = true;
}
else {
this.defaultPrevented = 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 module !== 'undefined' && typeof module.exports !== 'undefined') {
module.exports = soma;
}
if (typeof exports !== 'undefined') { if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
exports = module.exports = soma;
}
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) {
copy[cmd] = this.list[cmd]; if (this.list.hasOwnProperty(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,15 +922,16 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -860,15 +922,16 @@ 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) {
this.remove(cmd); if (this.list.hasOwnProperty(cmd)) {
this.remove(cmd);
}
} }
this.boundHandler = undefined; this.boundHandler = undefined;
this.dispatcher = undefined; this.dispatcher = 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; }
} else {
exports = soma; 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