Commit 834b79f6 authored by Addy Osmani's avatar Addy Osmani

Merge pull request #553 from somajs/somajs

soma.js v2 updates
parents 3af9f7bb 10dee133
...@@ -3,7 +3,8 @@ ...@@ -3,7 +3,8 @@
"version": "0.0.0", "version": "0.0.0",
"dependencies": { "dependencies": {
"todomvc-common": "~0.1.6", "todomvc-common": "~0.1.6",
"jquery": "~2.0.0", "director": "~1.2.0",
"handlebars.js": "~1.0.0-rc.3" "soma.js": "~2.0.0",
"soma-template": "~0.1.8"
} }
} }
/*
Copyright (C) 2011 by Yehuda Katz
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, 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:
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 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 IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// lib/handlebars/base.js
/*jshint eqnull:true*/
this.Handlebars = {};
(function(Handlebars) {
Handlebars.VERSION = "1.0.0-rc.3";
Handlebars.COMPILER_REVISION = 2;
Handlebars.REVISION_CHANGES = {
1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it
2: '>= 1.0.0-rc.3'
};
Handlebars.helpers = {};
Handlebars.partials = {};
Handlebars.registerHelper = function(name, fn, inverse) {
if(inverse) { fn.not = inverse; }
this.helpers[name] = fn;
};
Handlebars.registerPartial = function(name, str) {
this.partials[name] = str;
};
Handlebars.registerHelper('helperMissing', function(arg) {
if(arguments.length === 2) {
return undefined;
} else {
throw new Error("Could not find property '" + arg + "'");
}
});
var toString = Object.prototype.toString, functionType = "[object Function]";
Handlebars.registerHelper('blockHelperMissing', function(context, options) {
var inverse = options.inverse || function() {}, fn = options.fn;
var ret = "";
var type = toString.call(context);
if(type === functionType) { context = context.call(this); }
if(context === true) {
return fn(this);
} else if(context === false || context == null) {
return inverse(this);
} else if(type === "[object Array]") {
if(context.length > 0) {
return Handlebars.helpers.each(context, options);
} else {
return inverse(this);
}
} else {
return fn(context);
}
});
Handlebars.K = function() {};
Handlebars.createFrame = Object.create || function(object) {
Handlebars.K.prototype = object;
var obj = new Handlebars.K();
Handlebars.K.prototype = null;
return obj;
};
Handlebars.logger = {
DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3, level: 3,
methodMap: {0: 'debug', 1: 'info', 2: 'warn', 3: 'error'},
// can be overridden in the host environment
log: function(level, obj) {
if (Handlebars.logger.level <= level) {
var method = Handlebars.logger.methodMap[level];
if (typeof console !== 'undefined' && console[method]) {
console[method].call(console, obj);
}
}
}
};
Handlebars.log = function(level, obj) { Handlebars.logger.log(level, obj); };
Handlebars.registerHelper('each', function(context, options) {
var fn = options.fn, inverse = options.inverse;
var i = 0, ret = "", data;
if (options.data) {
data = Handlebars.createFrame(options.data);
}
if(context && typeof context === 'object') {
if(context instanceof Array){
for(var j = context.length; i<j; i++) {
if (data) { data.index = i; }
ret = ret + fn(context[i], { data: data });
}
} else {
for(var key in context) {
if(context.hasOwnProperty(key)) {
if(data) { data.key = key; }
ret = ret + fn(context[key], {data: data});
i++;
}
}
}
}
if(i === 0){
ret = inverse(this);
}
return ret;
});
Handlebars.registerHelper('if', function(context, options) {
var type = toString.call(context);
if(type === functionType) { context = context.call(this); }
if(!context || Handlebars.Utils.isEmpty(context)) {
return options.inverse(this);
} else {
return options.fn(this);
}
});
Handlebars.registerHelper('unless', function(context, options) {
var fn = options.fn, inverse = options.inverse;
options.fn = inverse;
options.inverse = fn;
return Handlebars.helpers['if'].call(this, context, options);
});
Handlebars.registerHelper('with', function(context, options) {
return options.fn(context);
});
Handlebars.registerHelper('log', function(context, options) {
var level = options.data && options.data.level != null ? parseInt(options.data.level, 10) : 1;
Handlebars.log(level, context);
});
}(this.Handlebars));
;
// lib/handlebars/compiler/parser.js
/* Jison generated parser */
var handlebars = (function(){
var parser = {trace: function trace() { },
yy: {},
symbols_: {"error":2,"root":3,"program":4,"EOF":5,"simpleInverse":6,"statements":7,"statement":8,"openInverse":9,"closeBlock":10,"openBlock":11,"mustache":12,"partial":13,"CONTENT":14,"COMMENT":15,"OPEN_BLOCK":16,"inMustache":17,"CLOSE":18,"OPEN_INVERSE":19,"OPEN_ENDBLOCK":20,"path":21,"OPEN":22,"OPEN_UNESCAPED":23,"OPEN_PARTIAL":24,"partialName":25,"params":26,"hash":27,"DATA":28,"param":29,"STRING":30,"INTEGER":31,"BOOLEAN":32,"hashSegments":33,"hashSegment":34,"ID":35,"EQUALS":36,"PARTIAL_NAME":37,"pathSegments":38,"SEP":39,"$accept":0,"$end":1},
terminals_: {2:"error",5:"EOF",14:"CONTENT",15:"COMMENT",16:"OPEN_BLOCK",18:"CLOSE",19:"OPEN_INVERSE",20:"OPEN_ENDBLOCK",22:"OPEN",23:"OPEN_UNESCAPED",24:"OPEN_PARTIAL",28:"DATA",30:"STRING",31:"INTEGER",32:"BOOLEAN",35:"ID",36:"EQUALS",37:"PARTIAL_NAME",39:"SEP"},
productions_: [0,[3,2],[4,2],[4,3],[4,2],[4,1],[4,1],[4,0],[7,1],[7,2],[8,3],[8,3],[8,1],[8,1],[8,1],[8,1],[11,3],[9,3],[10,3],[12,3],[12,3],[13,3],[13,4],[6,2],[17,3],[17,2],[17,2],[17,1],[17,1],[26,2],[26,1],[29,1],[29,1],[29,1],[29,1],[29,1],[27,1],[33,2],[33,1],[34,3],[34,3],[34,3],[34,3],[34,3],[25,1],[21,1],[38,3],[38,1]],
performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) {
var $0 = $$.length - 1;
switch (yystate) {
case 1: return $$[$0-1];
break;
case 2: this.$ = new yy.ProgramNode([], $$[$0]);
break;
case 3: this.$ = new yy.ProgramNode($$[$0-2], $$[$0]);
break;
case 4: this.$ = new yy.ProgramNode($$[$0-1], []);
break;
case 5: this.$ = new yy.ProgramNode($$[$0]);
break;
case 6: this.$ = new yy.ProgramNode([], []);
break;
case 7: this.$ = new yy.ProgramNode([]);
break;
case 8: this.$ = [$$[$0]];
break;
case 9: $$[$0-1].push($$[$0]); this.$ = $$[$0-1];
break;
case 10: this.$ = new yy.BlockNode($$[$0-2], $$[$0-1].inverse, $$[$0-1], $$[$0]);
break;
case 11: this.$ = new yy.BlockNode($$[$0-2], $$[$0-1], $$[$0-1].inverse, $$[$0]);
break;
case 12: this.$ = $$[$0];
break;
case 13: this.$ = $$[$0];
break;
case 14: this.$ = new yy.ContentNode($$[$0]);
break;
case 15: this.$ = new yy.CommentNode($$[$0]);
break;
case 16: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1]);
break;
case 17: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1]);
break;
case 18: this.$ = $$[$0-1];
break;
case 19: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1]);
break;
case 20: this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1], true);
break;
case 21: this.$ = new yy.PartialNode($$[$0-1]);
break;
case 22: this.$ = new yy.PartialNode($$[$0-2], $$[$0-1]);
break;
case 23:
break;
case 24: this.$ = [[$$[$0-2]].concat($$[$0-1]), $$[$0]];
break;
case 25: this.$ = [[$$[$0-1]].concat($$[$0]), null];
break;
case 26: this.$ = [[$$[$0-1]], $$[$0]];
break;
case 27: this.$ = [[$$[$0]], null];
break;
case 28: this.$ = [[new yy.DataNode($$[$0])], null];
break;
case 29: $$[$0-1].push($$[$0]); this.$ = $$[$0-1];
break;
case 30: this.$ = [$$[$0]];
break;
case 31: this.$ = $$[$0];
break;
case 32: this.$ = new yy.StringNode($$[$0]);
break;
case 33: this.$ = new yy.IntegerNode($$[$0]);
break;
case 34: this.$ = new yy.BooleanNode($$[$0]);
break;
case 35: this.$ = new yy.DataNode($$[$0]);
break;
case 36: this.$ = new yy.HashNode($$[$0]);
break;
case 37: $$[$0-1].push($$[$0]); this.$ = $$[$0-1];
break;
case 38: this.$ = [$$[$0]];
break;
case 39: this.$ = [$$[$0-2], $$[$0]];
break;
case 40: this.$ = [$$[$0-2], new yy.StringNode($$[$0])];
break;
case 41: this.$ = [$$[$0-2], new yy.IntegerNode($$[$0])];
break;
case 42: this.$ = [$$[$0-2], new yy.BooleanNode($$[$0])];
break;
case 43: this.$ = [$$[$0-2], new yy.DataNode($$[$0])];
break;
case 44: this.$ = new yy.PartialNameNode($$[$0]);
break;
case 45: this.$ = new yy.IdNode($$[$0]);
break;
case 46: $$[$0-2].push($$[$0]); this.$ = $$[$0-2];
break;
case 47: this.$ = [$$[$0]];
break;
}
},
table: [{3:1,4:2,5:[2,7],6:3,7:4,8:6,9:7,11:8,12:9,13:10,14:[1,11],15:[1,12],16:[1,13],19:[1,5],22:[1,14],23:[1,15],24:[1,16]},{1:[3]},{5:[1,17]},{5:[2,6],7:18,8:6,9:7,11:8,12:9,13:10,14:[1,11],15:[1,12],16:[1,13],19:[1,19],20:[2,6],22:[1,14],23:[1,15],24:[1,16]},{5:[2,5],6:20,8:21,9:7,11:8,12:9,13:10,14:[1,11],15:[1,12],16:[1,13],19:[1,5],20:[2,5],22:[1,14],23:[1,15],24:[1,16]},{17:23,18:[1,22],21:24,28:[1,25],35:[1,27],38:26},{5:[2,8],14:[2,8],15:[2,8],16:[2,8],19:[2,8],20:[2,8],22:[2,8],23:[2,8],24:[2,8]},{4:28,6:3,7:4,8:6,9:7,11:8,12:9,13:10,14:[1,11],15:[1,12],16:[1,13],19:[1,5],20:[2,7],22:[1,14],23:[1,15],24:[1,16]},{4:29,6:3,7:4,8:6,9:7,11:8,12:9,13:10,14:[1,11],15:[1,12],16:[1,13],19:[1,5],20:[2,7],22:[1,14],23:[1,15],24:[1,16]},{5:[2,12],14:[2,12],15:[2,12],16:[2,12],19:[2,12],20:[2,12],22:[2,12],23:[2,12],24:[2,12]},{5:[2,13],14:[2,13],15:[2,13],16:[2,13],19:[2,13],20:[2,13],22:[2,13],23:[2,13],24:[2,13]},{5:[2,14],14:[2,14],15:[2,14],16:[2,14],19:[2,14],20:[2,14],22:[2,14],23:[2,14],24:[2,14]},{5:[2,15],14:[2,15],15:[2,15],16:[2,15],19:[2,15],20:[2,15],22:[2,15],23:[2,15],24:[2,15]},{17:30,21:24,28:[1,25],35:[1,27],38:26},{17:31,21:24,28:[1,25],35:[1,27],38:26},{17:32,21:24,28:[1,25],35:[1,27],38:26},{25:33,37:[1,34]},{1:[2,1]},{5:[2,2],8:21,9:7,11:8,12:9,13:10,14:[1,11],15:[1,12],16:[1,13],19:[1,19],20:[2,2],22:[1,14],23:[1,15],24:[1,16]},{17:23,21:24,28:[1,25],35:[1,27],38:26},{5:[2,4],7:35,8:6,9:7,11:8,12:9,13:10,14:[1,11],15:[1,12],16:[1,13],19:[1,19],20:[2,4],22:[1,14],23:[1,15],24:[1,16]},{5:[2,9],14:[2,9],15:[2,9],16:[2,9],19:[2,9],20:[2,9],22:[2,9],23:[2,9],24:[2,9]},{5:[2,23],14:[2,23],15:[2,23],16:[2,23],19:[2,23],20:[2,23],22:[2,23],23:[2,23],24:[2,23]},{18:[1,36]},{18:[2,27],21:41,26:37,27:38,28:[1,45],29:39,30:[1,42],31:[1,43],32:[1,44],33:40,34:46,35:[1,47],38:26},{18:[2,28]},{18:[2,45],28:[2,45],30:[2,45],31:[2,45],32:[2,45],35:[2,45],39:[1,48]},{18:[2,47],28:[2,47],30:[2,47],31:[2,47],32:[2,47],35:[2,47],39:[2,47]},{10:49,20:[1,50]},{10:51,20:[1,50]},{18:[1,52]},{18:[1,53]},{18:[1,54]},{18:[1,55],21:56,35:[1,27],38:26},{18:[2,44],35:[2,44]},{5:[2,3],8:21,9:7,11:8,12:9,13:10,14:[1,11],15:[1,12],16:[1,13],19:[1,19],20:[2,3],22:[1,14],23:[1,15],24:[1,16]},{14:[2,17],15:[2,17],16:[2,17],19:[2,17],20:[2,17],22:[2,17],23:[2,17],24:[2,17]},{18:[2,25],21:41,27:57,28:[1,45],29:58,30:[1,42],31:[1,43],32:[1,44],33:40,34:46,35:[1,47],38:26},{18:[2,26]},{18:[2,30],28:[2,30],30:[2,30],31:[2,30],32:[2,30],35:[2,30]},{18:[2,36],34:59,35:[1,60]},{18:[2,31],28:[2,31],30:[2,31],31:[2,31],32:[2,31],35:[2,31]},{18:[2,32],28:[2,32],30:[2,32],31:[2,32],32:[2,32],35:[2,32]},{18:[2,33],28:[2,33],30:[2,33],31:[2,33],32:[2,33],35:[2,33]},{18:[2,34],28:[2,34],30:[2,34],31:[2,34],32:[2,34],35:[2,34]},{18:[2,35],28:[2,35],30:[2,35],31:[2,35],32:[2,35],35:[2,35]},{18:[2,38],35:[2,38]},{18:[2,47],28:[2,47],30:[2,47],31:[2,47],32:[2,47],35:[2,47],36:[1,61],39:[2,47]},{35:[1,62]},{5:[2,10],14:[2,10],15:[2,10],16:[2,10],19:[2,10],20:[2,10],22:[2,10],23:[2,10],24:[2,10]},{21:63,35:[1,27],38:26},{5:[2,11],14:[2,11],15:[2,11],16:[2,11],19:[2,11],20:[2,11],22:[2,11],23:[2,11],24:[2,11]},{14:[2,16],15:[2,16],16:[2,16],19:[2,16],20:[2,16],22:[2,16],23:[2,16],24:[2,16]},{5:[2,19],14:[2,19],15:[2,19],16:[2,19],19:[2,19],20:[2,19],22:[2,19],23:[2,19],24:[2,19]},{5:[2,20],14:[2,20],15:[2,20],16:[2,20],19:[2,20],20:[2,20],22:[2,20],23:[2,20],24:[2,20]},{5:[2,21],14:[2,21],15:[2,21],16:[2,21],19:[2,21],20:[2,21],22:[2,21],23:[2,21],24:[2,21]},{18:[1,64]},{18:[2,24]},{18:[2,29],28:[2,29],30:[2,29],31:[2,29],32:[2,29],35:[2,29]},{18:[2,37],35:[2,37]},{36:[1,61]},{21:65,28:[1,69],30:[1,66],31:[1,67],32:[1,68],35:[1,27],38:26},{18:[2,46],28:[2,46],30:[2,46],31:[2,46],32:[2,46],35:[2,46],39:[2,46]},{18:[1,70]},{5:[2,22],14:[2,22],15:[2,22],16:[2,22],19:[2,22],20:[2,22],22:[2,22],23:[2,22],24:[2,22]},{18:[2,39],35:[2,39]},{18:[2,40],35:[2,40]},{18:[2,41],35:[2,41]},{18:[2,42],35:[2,42]},{18:[2,43],35:[2,43]},{5:[2,18],14:[2,18],15:[2,18],16:[2,18],19:[2,18],20:[2,18],22:[2,18],23:[2,18],24:[2,18]}],
defaultActions: {17:[2,1],25:[2,28],38:[2,26],57:[2,24]},
parseError: function parseError(str, hash) {
throw new Error(str);
},
parse: function parse(input) {
var self = this, stack = [0], vstack = [null], lstack = [], table = this.table, yytext = "", yylineno = 0, yyleng = 0, recovering = 0, TERROR = 2, EOF = 1;
this.lexer.setInput(input);
this.lexer.yy = this.yy;
this.yy.lexer = this.lexer;
this.yy.parser = this;
if (typeof this.lexer.yylloc == "undefined")
this.lexer.yylloc = {};
var yyloc = this.lexer.yylloc;
lstack.push(yyloc);
var ranges = this.lexer.options && this.lexer.options.ranges;
if (typeof this.yy.parseError === "function")
this.parseError = this.yy.parseError;
function popStack(n) {
stack.length = stack.length - 2 * n;
vstack.length = vstack.length - n;
lstack.length = lstack.length - n;
}
function lex() {
var token;
token = self.lexer.lex() || 1;
if (typeof token !== "number") {
token = self.symbols_[token] || token;
}
return token;
}
var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected;
while (true) {
state = stack[stack.length - 1];
if (this.defaultActions[state]) {
action = this.defaultActions[state];
} else {
if (symbol === null || typeof symbol == "undefined") {
symbol = lex();
}
action = table[state] && table[state][symbol];
}
if (typeof action === "undefined" || !action.length || !action[0]) {
var errStr = "";
if (!recovering) {
expected = [];
for (p in table[state])
if (this.terminals_[p] && p > 2) {
expected.push("'" + this.terminals_[p] + "'");
}
if (this.lexer.showPosition) {
errStr = "Parse error on line " + (yylineno + 1) + ":\n" + this.lexer.showPosition() + "\nExpecting " + expected.join(", ") + ", got '" + (this.terminals_[symbol] || symbol) + "'";
} else {
errStr = "Parse error on line " + (yylineno + 1) + ": Unexpected " + (symbol == 1?"end of input":"'" + (this.terminals_[symbol] || symbol) + "'");
}
this.parseError(errStr, {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected});
}
}
if (action[0] instanceof Array && action.length > 1) {
throw new Error("Parse Error: multiple actions possible at state: " + state + ", token: " + symbol);
}
switch (action[0]) {
case 1:
stack.push(symbol);
vstack.push(this.lexer.yytext);
lstack.push(this.lexer.yylloc);
stack.push(action[1]);
symbol = null;
if (!preErrorSymbol) {
yyleng = this.lexer.yyleng;
yytext = this.lexer.yytext;
yylineno = this.lexer.yylineno;
yyloc = this.lexer.yylloc;
if (recovering > 0)
recovering--;
} else {
symbol = preErrorSymbol;
preErrorSymbol = null;
}
break;
case 2:
len = this.productions_[action[1]][1];
yyval.$ = vstack[vstack.length - len];
yyval._$ = {first_line: lstack[lstack.length - (len || 1)].first_line, last_line: lstack[lstack.length - 1].last_line, first_column: lstack[lstack.length - (len || 1)].first_column, last_column: lstack[lstack.length - 1].last_column};
if (ranges) {
yyval._$.range = [lstack[lstack.length - (len || 1)].range[0], lstack[lstack.length - 1].range[1]];
}
r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack);
if (typeof r !== "undefined") {
return r;
}
if (len) {
stack = stack.slice(0, -1 * len * 2);
vstack = vstack.slice(0, -1 * len);
lstack = lstack.slice(0, -1 * len);
}
stack.push(this.productions_[action[1]][0]);
vstack.push(yyval.$);
lstack.push(yyval._$);
newState = table[stack[stack.length - 2]][stack[stack.length - 1]];
stack.push(newState);
break;
case 3:
return true;
}
}
return true;
}
};
/* Jison generated lexer */
var lexer = (function(){
var lexer = ({EOF:1,
parseError:function parseError(str, hash) {
if (this.yy.parser) {
this.yy.parser.parseError(str, hash);
} else {
throw new Error(str);
}
},
setInput:function (input) {
this._input = input;
this._more = this._less = this.done = false;
this.yylineno = this.yyleng = 0;
this.yytext = this.matched = this.match = '';
this.conditionStack = ['INITIAL'];
this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0};
if (this.options.ranges) this.yylloc.range = [0,0];
this.offset = 0;
return this;
},
input:function () {
var ch = this._input[0];
this.yytext += ch;
this.yyleng++;
this.offset++;
this.match += ch;
this.matched += ch;
var lines = ch.match(/(?:\r\n?|\n).*/g);
if (lines) {
this.yylineno++;
this.yylloc.last_line++;
} else {
this.yylloc.last_column++;
}
if (this.options.ranges) this.yylloc.range[1]++;
this._input = this._input.slice(1);
return ch;
},
unput:function (ch) {
var len = ch.length;
var lines = ch.split(/(?:\r\n?|\n)/g);
this._input = ch + this._input;
this.yytext = this.yytext.substr(0, this.yytext.length-len-1);
//this.yyleng -= len;
this.offset -= len;
var oldLines = this.match.split(/(?:\r\n?|\n)/g);
this.match = this.match.substr(0, this.match.length-1);
this.matched = this.matched.substr(0, this.matched.length-1);
if (lines.length-1) this.yylineno -= lines.length-1;
var r = this.yylloc.range;
this.yylloc = {first_line: this.yylloc.first_line,
last_line: this.yylineno+1,
first_column: this.yylloc.first_column,
last_column: lines ?
(lines.length === oldLines.length ? this.yylloc.first_column : 0) + oldLines[oldLines.length - lines.length].length - lines[0].length:
this.yylloc.first_column - len
};
if (this.options.ranges) {
this.yylloc.range = [r[0], r[0] + this.yyleng - len];
}
return this;
},
more:function () {
this._more = true;
return this;
},
less:function (n) {
this.unput(this.match.slice(n));
},
pastInput:function () {
var past = this.matched.substr(0, this.matched.length - this.match.length);
return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, "");
},
upcomingInput:function () {
var next = this.match;
if (next.length < 20) {
next += this._input.substr(0, 20-next.length);
}
return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, "");
},
showPosition:function () {
var pre = this.pastInput();
var c = new Array(pre.length + 1).join("-");
return pre + this.upcomingInput() + "\n" + c+"^";
},
next:function () {
if (this.done) {
return this.EOF;
}
if (!this._input) this.done = true;
var token,
match,
tempMatch,
index,
col,
lines;
if (!this._more) {
this.yytext = '';
this.match = '';
}
var rules = this._currentRules();
for (var i=0;i < rules.length; i++) {
tempMatch = this._input.match(this.rules[rules[i]]);
if (tempMatch && (!match || tempMatch[0].length > match[0].length)) {
match = tempMatch;
index = i;
if (!this.options.flex) break;
}
}
if (match) {
lines = match[0].match(/(?:\r\n?|\n).*/g);
if (lines) this.yylineno += lines.length;
this.yylloc = {first_line: this.yylloc.last_line,
last_line: this.yylineno+1,
first_column: this.yylloc.last_column,
last_column: lines ? lines[lines.length-1].length-lines[lines.length-1].match(/\r?\n?/)[0].length : this.yylloc.last_column + match[0].length};
this.yytext += match[0];
this.match += match[0];
this.matches = match;
this.yyleng = this.yytext.length;
if (this.options.ranges) {
this.yylloc.range = [this.offset, this.offset += this.yyleng];
}
this._more = false;
this._input = this._input.slice(match[0].length);
this.matched += match[0];
token = this.performAction.call(this, this.yy, this, rules[index],this.conditionStack[this.conditionStack.length-1]);
if (this.done && this._input) this.done = false;
if (token) return token;
else return;
}
if (this._input === "") {
return this.EOF;
} else {
return this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(),
{text: "", token: null, line: this.yylineno});
}
},
lex:function lex() {
var r = this.next();
if (typeof r !== 'undefined') {
return r;
} else {
return this.lex();
}
},
begin:function begin(condition) {
this.conditionStack.push(condition);
},
popState:function popState() {
return this.conditionStack.pop();
},
_currentRules:function _currentRules() {
return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules;
},
topState:function () {
return this.conditionStack[this.conditionStack.length-2];
},
pushState:function begin(condition) {
this.begin(condition);
}});
lexer.options = {};
lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) {
var YYSTATE=YY_START
switch($avoiding_name_collisions) {
case 0:
if(yy_.yytext.slice(-1) !== "\\") this.begin("mu");
if(yy_.yytext.slice(-1) === "\\") yy_.yytext = yy_.yytext.substr(0,yy_.yyleng-1), this.begin("emu");
if(yy_.yytext) return 14;
break;
case 1: return 14;
break;
case 2:
if(yy_.yytext.slice(-1) !== "\\") this.popState();
if(yy_.yytext.slice(-1) === "\\") yy_.yytext = yy_.yytext.substr(0,yy_.yyleng-1);
return 14;
break;
case 3: yy_.yytext = yy_.yytext.substr(0, yy_.yyleng-4); this.popState(); return 15;
break;
case 4: this.begin("par"); return 24;
break;
case 5: return 16;
break;
case 6: return 20;
break;
case 7: return 19;
break;
case 8: return 19;
break;
case 9: return 23;
break;
case 10: return 23;
break;
case 11: this.popState(); this.begin('com');
break;
case 12: yy_.yytext = yy_.yytext.substr(3,yy_.yyleng-5); this.popState(); return 15;
break;
case 13: return 22;
break;
case 14: return 36;
break;
case 15: return 35;
break;
case 16: return 35;
break;
case 17: return 39;
break;
case 18: /*ignore whitespace*/
break;
case 19: this.popState(); return 18;
break;
case 20: this.popState(); return 18;
break;
case 21: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\"/g,'"'); return 30;
break;
case 22: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\'/g,"'"); return 30;
break;
case 23: yy_.yytext = yy_.yytext.substr(1); return 28;
break;
case 24: return 32;
break;
case 25: return 32;
break;
case 26: return 31;
break;
case 27: return 35;
break;
case 28: yy_.yytext = yy_.yytext.substr(1, yy_.yyleng-2); return 35;
break;
case 29: return 'INVALID';
break;
case 30: /*ignore whitespace*/
break;
case 31: this.popState(); return 37;
break;
case 32: return 5;
break;
}
};
lexer.rules = [/^(?:[^\x00]*?(?=(\{\{)))/,/^(?:[^\x00]+)/,/^(?:[^\x00]{2,}?(?=(\{\{|$)))/,/^(?:[\s\S]*?--\}\})/,/^(?:\{\{>)/,/^(?:\{\{#)/,/^(?:\{\{\/)/,/^(?:\{\{\^)/,/^(?:\{\{\s*else\b)/,/^(?:\{\{\{)/,/^(?:\{\{&)/,/^(?:\{\{!--)/,/^(?:\{\{![\s\S]*?\}\})/,/^(?:\{\{)/,/^(?:=)/,/^(?:\.(?=[} ]))/,/^(?:\.\.)/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}\}\})/,/^(?:\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@[a-zA-Z]+)/,/^(?:true(?=[}\s]))/,/^(?:false(?=[}\s]))/,/^(?:[0-9]+(?=[}\s]))/,/^(?:[a-zA-Z0-9_$-]+(?=[=}\s\/.]))/,/^(?:\[[^\]]*\])/,/^(?:.)/,/^(?:\s+)/,/^(?:[a-zA-Z0-9_$-/]+)/,/^(?:$)/];
lexer.conditions = {"mu":{"rules":[4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,32],"inclusive":false},"emu":{"rules":[2],"inclusive":false},"com":{"rules":[3],"inclusive":false},"par":{"rules":[30,31],"inclusive":false},"INITIAL":{"rules":[0,1,32],"inclusive":true}};
return lexer;})()
parser.lexer = lexer;
function Parser () { this.yy = {}; }Parser.prototype = parser;parser.Parser = Parser;
return new Parser;
})();;
// lib/handlebars/compiler/base.js
Handlebars.Parser = handlebars;
Handlebars.parse = function(input) {
// Just return if an already-compile AST was passed in.
if(input.constructor === Handlebars.AST.ProgramNode) { return input; }
Handlebars.Parser.yy = Handlebars.AST;
return Handlebars.Parser.parse(input);
};
Handlebars.print = function(ast) {
return new Handlebars.PrintVisitor().accept(ast);
};;
// lib/handlebars/compiler/ast.js
(function() {
Handlebars.AST = {};
Handlebars.AST.ProgramNode = function(statements, inverse) {
this.type = "program";
this.statements = statements;
if(inverse) { this.inverse = new Handlebars.AST.ProgramNode(inverse); }
};
Handlebars.AST.MustacheNode = function(rawParams, hash, unescaped) {
this.type = "mustache";
this.escaped = !unescaped;
this.hash = hash;
var id = this.id = rawParams[0];
var params = this.params = rawParams.slice(1);
// a mustache is an eligible helper if:
// * its id is simple (a single part, not `this` or `..`)
var eligibleHelper = this.eligibleHelper = id.isSimple;
// a mustache is definitely a helper if:
// * it is an eligible helper, and
// * it has at least one parameter or hash segment
this.isHelper = eligibleHelper && (params.length || hash);
// if a mustache is an eligible helper but not a definite
// helper, it is ambiguous, and will be resolved in a later
// pass or at runtime.
};
Handlebars.AST.PartialNode = function(partialName, context) {
this.type = "partial";
this.partialName = partialName;
this.context = context;
};
var verifyMatch = function(open, close) {
if(open.original !== close.original) {
throw new Handlebars.Exception(open.original + " doesn't match " + close.original);
}
};
Handlebars.AST.BlockNode = function(mustache, program, inverse, close) {
verifyMatch(mustache.id, close);
this.type = "block";
this.mustache = mustache;
this.program = program;
this.inverse = inverse;
if (this.inverse && !this.program) {
this.isInverse = true;
}
};
Handlebars.AST.ContentNode = function(string) {
this.type = "content";
this.string = string;
};
Handlebars.AST.HashNode = function(pairs) {
this.type = "hash";
this.pairs = pairs;
};
Handlebars.AST.IdNode = function(parts) {
this.type = "ID";
this.original = parts.join(".");
var dig = [], depth = 0;
for(var i=0,l=parts.length; i<l; i++) {
var part = parts[i];
if (part === ".." || part === "." || part === "this") {
if (dig.length > 0) { throw new Handlebars.Exception("Invalid path: " + this.original); }
else if (part === "..") { depth++; }
else { this.isScoped = true; }
}
else { dig.push(part); }
}
this.parts = dig;
this.string = dig.join('.');
this.depth = depth;
// an ID is simple if it only has one part, and that part is not
// `..` or `this`.
this.isSimple = parts.length === 1 && !this.isScoped && depth === 0;
this.stringModeValue = this.string;
};
Handlebars.AST.PartialNameNode = function(name) {
this.type = "PARTIAL_NAME";
this.name = name;
};
Handlebars.AST.DataNode = function(id) {
this.type = "DATA";
this.id = id;
};
Handlebars.AST.StringNode = function(string) {
this.type = "STRING";
this.string = string;
this.stringModeValue = string;
};
Handlebars.AST.IntegerNode = function(integer) {
this.type = "INTEGER";
this.integer = integer;
this.stringModeValue = Number(integer);
};
Handlebars.AST.BooleanNode = function(bool) {
this.type = "BOOLEAN";
this.bool = bool;
this.stringModeValue = bool === "true";
};
Handlebars.AST.CommentNode = function(comment) {
this.type = "comment";
this.comment = comment;
};
})();;
// lib/handlebars/utils.js
var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack'];
Handlebars.Exception = function(message) {
var tmp = Error.prototype.constructor.apply(this, arguments);
// Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work.
for (var idx = 0; idx < errorProps.length; idx++) {
this[errorProps[idx]] = tmp[errorProps[idx]];
}
};
Handlebars.Exception.prototype = new Error();
// Build out our basic SafeString type
Handlebars.SafeString = function(string) {
this.string = string;
};
Handlebars.SafeString.prototype.toString = function() {
return this.string.toString();
};
(function() {
var escape = {
"&": "&amp;",
"<": "&lt;",
">": "&gt;",
'"': "&quot;",
"'": "&#x27;",
"`": "&#x60;"
};
var badChars = /[&<>"'`]/g;
var possible = /[&<>"'`]/;
var escapeChar = function(chr) {
return escape[chr] || "&amp;";
};
Handlebars.Utils = {
escapeExpression: function(string) {
// don't escape SafeStrings, since they're already safe
if (string instanceof Handlebars.SafeString) {
return string.toString();
} else if (string == null || string === false) {
return "";
}
if(!possible.test(string)) { return string; }
return string.replace(badChars, escapeChar);
},
isEmpty: function(value) {
if (!value && value !== 0) {
return true;
} else if(Object.prototype.toString.call(value) === "[object Array]" && value.length === 0) {
return true;
} else {
return false;
}
}
};
})();;
// lib/handlebars/compiler/compiler.js
/*jshint eqnull:true*/
Handlebars.Compiler = function() {};
Handlebars.JavaScriptCompiler = function() {};
(function(Compiler, JavaScriptCompiler) {
// the foundHelper register will disambiguate helper lookup from finding a
// function in a context. This is necessary for mustache compatibility, which
// requires that context functions in blocks are evaluated by blockHelperMissing,
// and then proceed as if the resulting value was provided to blockHelperMissing.
Compiler.prototype = {
compiler: Compiler,
disassemble: function() {
var opcodes = this.opcodes, opcode, out = [], params, param;
for (var i=0, l=opcodes.length; i<l; i++) {
opcode = opcodes[i];
if (opcode.opcode === 'DECLARE') {
out.push("DECLARE " + opcode.name + "=" + opcode.value);
} else {
params = [];
for (var j=0; j<opcode.args.length; j++) {
param = opcode.args[j];
if (typeof param === "string") {
param = "\"" + param.replace("\n", "\\n") + "\"";
}
params.push(param);
}
out.push(opcode.opcode + " " + params.join(" "));
}
}
return out.join("\n");
},
equals: function(other) {
var len = this.opcodes.length;
if (other.opcodes.length !== len) {
return false;
}
for (var i = 0; i < len; i++) {
var opcode = this.opcodes[i],
otherOpcode = other.opcodes[i];
if (opcode.opcode !== otherOpcode.opcode || opcode.args.length !== otherOpcode.args.length) {
return false;
}
for (var j = 0; j < opcode.args.length; j++) {
if (opcode.args[j] !== otherOpcode.args[j]) {
return false;
}
}
}
return true;
},
guid: 0,
compile: function(program, options) {
this.children = [];
this.depths = {list: []};
this.options = options;
// These changes will propagate to the other compiler components
var knownHelpers = this.options.knownHelpers;
this.options.knownHelpers = {
'helperMissing': true,
'blockHelperMissing': true,
'each': true,
'if': true,
'unless': true,
'with': true,
'log': true
};
if (knownHelpers) {
for (var name in knownHelpers) {
this.options.knownHelpers[name] = knownHelpers[name];
}
}
return this.program(program);
},
accept: function(node) {
return this[node.type](node);
},
program: function(program) {
var statements = program.statements, statement;
this.opcodes = [];
for(var i=0, l=statements.length; i<l; i++) {
statement = statements[i];
this[statement.type](statement);
}
this.isSimple = l === 1;
this.depths.list = this.depths.list.sort(function(a, b) {
return a - b;
});
return this;
},
compileProgram: function(program) {
var result = new this.compiler().compile(program, this.options);
var guid = this.guid++, depth;
this.usePartial = this.usePartial || result.usePartial;
this.children[guid] = result;
for(var i=0, l=result.depths.list.length; i<l; i++) {
depth = result.depths.list[i];
if(depth < 2) { continue; }
else { this.addDepth(depth - 1); }
}
return guid;
},
block: function(block) {
var mustache = block.mustache,
program = block.program,
inverse = block.inverse;
if (program) {
program = this.compileProgram(program);
}
if (inverse) {
inverse = this.compileProgram(inverse);
}
var type = this.classifyMustache(mustache);
if (type === "helper") {
this.helperMustache(mustache, program, inverse);
} else if (type === "simple") {
this.simpleMustache(mustache);
// now that the simple mustache is resolved, we need to
// evaluate it by executing `blockHelperMissing`
this.opcode('pushProgram', program);
this.opcode('pushProgram', inverse);
this.opcode('emptyHash');
this.opcode('blockValue');
} else {
this.ambiguousMustache(mustache, program, inverse);
// now that the simple mustache is resolved, we need to
// evaluate it by executing `blockHelperMissing`
this.opcode('pushProgram', program);
this.opcode('pushProgram', inverse);
this.opcode('emptyHash');
this.opcode('ambiguousBlockValue');
}
this.opcode('append');
},
hash: function(hash) {
var pairs = hash.pairs, pair, val;
this.opcode('pushHash');
for(var i=0, l=pairs.length; i<l; i++) {
pair = pairs[i];
val = pair[1];
if (this.options.stringParams) {
this.opcode('pushStringParam', val.stringModeValue, val.type);
} else {
this.accept(val);
}
this.opcode('assignToHash', pair[0]);
}
this.opcode('popHash');
},
partial: function(partial) {
var partialName = partial.partialName;
this.usePartial = true;
if(partial.context) {
this.ID(partial.context);
} else {
this.opcode('push', 'depth0');
}
this.opcode('invokePartial', partialName.name);
this.opcode('append');
},
content: function(content) {
this.opcode('appendContent', content.string);
},
mustache: function(mustache) {
var options = this.options;
var type = this.classifyMustache(mustache);
if (type === "simple") {
this.simpleMustache(mustache);
} else if (type === "helper") {
this.helperMustache(mustache);
} else {
this.ambiguousMustache(mustache);
}
if(mustache.escaped && !options.noEscape) {
this.opcode('appendEscaped');
} else {
this.opcode('append');
}
},
ambiguousMustache: function(mustache, program, inverse) {
var id = mustache.id,
name = id.parts[0],
isBlock = program != null || inverse != null;
this.opcode('getContext', id.depth);
this.opcode('pushProgram', program);
this.opcode('pushProgram', inverse);
this.opcode('invokeAmbiguous', name, isBlock);
},
simpleMustache: function(mustache) {
var id = mustache.id;
if (id.type === 'DATA') {
this.DATA(id);
} else if (id.parts.length) {
this.ID(id);
} else {
// Simplified ID for `this`
this.addDepth(id.depth);
this.opcode('getContext', id.depth);
this.opcode('pushContext');
}
this.opcode('resolvePossibleLambda');
},
helperMustache: function(mustache, program, inverse) {
var params = this.setupFullMustacheParams(mustache, program, inverse),
name = mustache.id.parts[0];
if (this.options.knownHelpers[name]) {
this.opcode('invokeKnownHelper', params.length, name);
} else if (this.knownHelpersOnly) {
throw new Error("You specified knownHelpersOnly, but used the unknown helper " + name);
} else {
this.opcode('invokeHelper', params.length, name);
}
},
ID: function(id) {
this.addDepth(id.depth);
this.opcode('getContext', id.depth);
var name = id.parts[0];
if (!name) {
this.opcode('pushContext');
} else {
this.opcode('lookupOnContext', id.parts[0]);
}
for(var i=1, l=id.parts.length; i<l; i++) {
this.opcode('lookup', id.parts[i]);
}
},
DATA: function(data) {
this.options.data = true;
this.opcode('lookupData', data.id);
},
STRING: function(string) {
this.opcode('pushString', string.string);
},
INTEGER: function(integer) {
this.opcode('pushLiteral', integer.integer);
},
BOOLEAN: function(bool) {
this.opcode('pushLiteral', bool.bool);
},
comment: function() {},
// HELPERS
opcode: function(name) {
this.opcodes.push({ opcode: name, args: [].slice.call(arguments, 1) });
},
declare: function(name, value) {
this.opcodes.push({ opcode: 'DECLARE', name: name, value: value });
},
addDepth: function(depth) {
if(isNaN(depth)) { throw new Error("EWOT"); }
if(depth === 0) { return; }
if(!this.depths[depth]) {
this.depths[depth] = true;
this.depths.list.push(depth);
}
},
classifyMustache: function(mustache) {
var isHelper = mustache.isHelper;
var isEligible = mustache.eligibleHelper;
var options = this.options;
// if ambiguous, we can possibly resolve the ambiguity now
if (isEligible && !isHelper) {
var name = mustache.id.parts[0];
if (options.knownHelpers[name]) {
isHelper = true;
} else if (options.knownHelpersOnly) {
isEligible = false;
}
}
if (isHelper) { return "helper"; }
else if (isEligible) { return "ambiguous"; }
else { return "simple"; }
},
pushParams: function(params) {
var i = params.length, param;
while(i--) {
param = params[i];
if(this.options.stringParams) {
if(param.depth) {
this.addDepth(param.depth);
}
this.opcode('getContext', param.depth || 0);
this.opcode('pushStringParam', param.stringModeValue, param.type);
} else {
this[param.type](param);
}
}
},
setupMustacheParams: function(mustache) {
var params = mustache.params;
this.pushParams(params);
if(mustache.hash) {
this.hash(mustache.hash);
} else {
this.opcode('emptyHash');
}
return params;
},
// this will replace setupMustacheParams when we're done
setupFullMustacheParams: function(mustache, program, inverse) {
var params = mustache.params;
this.pushParams(params);
this.opcode('pushProgram', program);
this.opcode('pushProgram', inverse);
if(mustache.hash) {
this.hash(mustache.hash);
} else {
this.opcode('emptyHash');
}
return params;
}
};
var Literal = function(value) {
this.value = value;
};
JavaScriptCompiler.prototype = {
// PUBLIC API: You can override these methods in a subclass to provide
// alternative compiled forms for name lookup and buffering semantics
nameLookup: function(parent, name /* , type*/) {
if (/^[0-9]+$/.test(name)) {
return parent + "[" + name + "]";
} else if (JavaScriptCompiler.isValidJavaScriptVariableName(name)) {
return parent + "." + name;
}
else {
return parent + "['" + name + "']";
}
},
appendToBuffer: function(string) {
if (this.environment.isSimple) {
return "return " + string + ";";
} else {
return {
appendToBuffer: true,
content: string,
toString: function() { return "buffer += " + string + ";"; }
};
}
},
initializeBuffer: function() {
return this.quotedString("");
},
namespace: "Handlebars",
// END PUBLIC API
compile: function(environment, options, context, asObject) {
this.environment = environment;
this.options = options || {};
Handlebars.log(Handlebars.logger.DEBUG, this.environment.disassemble() + "\n\n");
this.name = this.environment.name;
this.isChild = !!context;
this.context = context || {
programs: [],
environments: [],
aliases: { }
};
this.preamble();
this.stackSlot = 0;
this.stackVars = [];
this.registers = { list: [] };
this.compileStack = [];
this.inlineStack = [];
this.compileChildren(environment, options);
var opcodes = environment.opcodes, opcode;
this.i = 0;
for(l=opcodes.length; this.i<l; this.i++) {
opcode = opcodes[this.i];
if(opcode.opcode === 'DECLARE') {
this[opcode.name] = opcode.value;
} else {
this[opcode.opcode].apply(this, opcode.args);
}
}
return this.createFunctionContext(asObject);
},
nextOpcode: function() {
var opcodes = this.environment.opcodes;
return opcodes[this.i + 1];
},
eat: function() {
this.i = this.i + 1;
},
preamble: function() {
var out = [];
if (!this.isChild) {
var namespace = this.namespace;
var copies = "helpers = helpers || " + namespace + ".helpers;";
if (this.environment.usePartial) { copies = copies + " partials = partials || " + namespace + ".partials;"; }
if (this.options.data) { copies = copies + " data = data || {};"; }
out.push(copies);
} else {
out.push('');
}
if (!this.environment.isSimple) {
out.push(", buffer = " + this.initializeBuffer());
} else {
out.push("");
}
// track the last context pushed into place to allow skipping the
// getContext opcode when it would be a noop
this.lastContext = 0;
this.source = out;
},
createFunctionContext: function(asObject) {
var locals = this.stackVars.concat(this.registers.list);
if(locals.length > 0) {
this.source[1] = this.source[1] + ", " + locals.join(", ");
}
// Generate minimizer alias mappings
if (!this.isChild) {
for (var alias in this.context.aliases) {
this.source[1] = this.source[1] + ', ' + alias + '=' + this.context.aliases[alias];
}
}
if (this.source[1]) {
this.source[1] = "var " + this.source[1].substring(2) + ";";
}
// Merge children
if (!this.isChild) {
this.source[1] += '\n' + this.context.programs.join('\n') + '\n';
}
if (!this.environment.isSimple) {
this.source.push("return buffer;");
}
var params = this.isChild ? ["depth0", "data"] : ["Handlebars", "depth0", "helpers", "partials", "data"];
for(var i=0, l=this.environment.depths.list.length; i<l; i++) {
params.push("depth" + this.environment.depths.list[i]);
}
// Perform a second pass over the output to merge content when possible
var source = this.mergeSource();
if (!this.isChild) {
var revision = Handlebars.COMPILER_REVISION,
versions = Handlebars.REVISION_CHANGES[revision];
source = "this.compilerInfo = ["+revision+",'"+versions+"'];\n"+source;
}
if (asObject) {
params.push(source);
return Function.apply(this, params);
} else {
var functionSource = 'function ' + (this.name || '') + '(' + params.join(',') + ') {\n ' + source + '}';
Handlebars.log(Handlebars.logger.DEBUG, functionSource + "\n\n");
return functionSource;
}
},
mergeSource: function() {
// WARN: We are not handling the case where buffer is still populated as the source should
// not have buffer append operations as their final action.
var source = '',
buffer;
for (var i = 0, len = this.source.length; i < len; i++) {
var line = this.source[i];
if (line.appendToBuffer) {
if (buffer) {
buffer = buffer + '\n + ' + line.content;
} else {
buffer = line.content;
}
} else {
if (buffer) {
source += 'buffer += ' + buffer + ';\n ';
buffer = undefined;
}
source += line + '\n ';
}
}
return source;
},
// [blockValue]
//
// On stack, before: hash, inverse, program, value
// On stack, after: return value of blockHelperMissing
//
// The purpose of this opcode is to take a block of the form
// `{{#foo}}...{{/foo}}`, resolve the value of `foo`, and
// replace it on the stack with the result of properly
// invoking blockHelperMissing.
blockValue: function() {
this.context.aliases.blockHelperMissing = 'helpers.blockHelperMissing';
var params = ["depth0"];
this.setupParams(0, params);
this.replaceStack(function(current) {
params.splice(1, 0, current);
return "blockHelperMissing.call(" + params.join(", ") + ")";
});
},
// [ambiguousBlockValue]
//
// On stack, before: hash, inverse, program, value
// Compiler value, before: lastHelper=value of last found helper, if any
// On stack, after, if no lastHelper: same as [blockValue]
// On stack, after, if lastHelper: value
ambiguousBlockValue: function() {
this.context.aliases.blockHelperMissing = 'helpers.blockHelperMissing';
var params = ["depth0"];
this.setupParams(0, params);
var current = this.topStack();
params.splice(1, 0, current);
// Use the options value generated from the invocation
params[params.length-1] = 'options';
this.source.push("if (!" + this.lastHelper + ") { " + current + " = blockHelperMissing.call(" + params.join(", ") + "); }");
},
// [appendContent]
//
// On stack, before: ...
// On stack, after: ...
//
// Appends the string value of `content` to the current buffer
appendContent: function(content) {
this.source.push(this.appendToBuffer(this.quotedString(content)));
},
// [append]
//
// On stack, before: value, ...
// On stack, after: ...
//
// Coerces `value` to a String and appends it to the current buffer.
//
// If `value` is truthy, or 0, it is coerced into a string and appended
// Otherwise, the empty string is appended
append: function() {
// Force anything that is inlined onto the stack so we don't have duplication
// when we examine local
this.flushInline();
var local = this.popStack();
this.source.push("if(" + local + " || " + local + " === 0) { " + this.appendToBuffer(local) + " }");
if (this.environment.isSimple) {
this.source.push("else { " + this.appendToBuffer("''") + " }");
}
},
// [appendEscaped]
//
// On stack, before: value, ...
// On stack, after: ...
//
// Escape `value` and append it to the buffer
appendEscaped: function() {
this.context.aliases.escapeExpression = 'this.escapeExpression';
this.source.push(this.appendToBuffer("escapeExpression(" + this.popStack() + ")"));
},
// [getContext]
//
// On stack, before: ...
// On stack, after: ...
// Compiler value, after: lastContext=depth
//
// Set the value of the `lastContext` compiler value to the depth
getContext: function(depth) {
if(this.lastContext !== depth) {
this.lastContext = depth;
}
},
// [lookupOnContext]
//
// On stack, before: ...
// On stack, after: currentContext[name], ...
//
// Looks up the value of `name` on the current context and pushes
// it onto the stack.
lookupOnContext: function(name) {
this.push(this.nameLookup('depth' + this.lastContext, name, 'context'));
},
// [pushContext]
//
// On stack, before: ...
// On stack, after: currentContext, ...
//
// Pushes the value of the current context onto the stack.
pushContext: function() {
this.pushStackLiteral('depth' + this.lastContext);
},
// [resolvePossibleLambda]
//
// On stack, before: value, ...
// On stack, after: resolved value, ...
//
// If the `value` is a lambda, replace it on the stack by
// the return value of the lambda
resolvePossibleLambda: function() {
this.context.aliases.functionType = '"function"';
this.replaceStack(function(current) {
return "typeof " + current + " === functionType ? " + current + ".apply(depth0) : " + current;
});
},
// [lookup]
//
// On stack, before: value, ...
// On stack, after: value[name], ...
//
// Replace the value on the stack with the result of looking
// up `name` on `value`
lookup: function(name) {
this.replaceStack(function(current) {
return current + " == null || " + current + " === false ? " + current + " : " + this.nameLookup(current, name, 'context');
});
},
// [lookupData]
//
// On stack, before: ...
// On stack, after: data[id], ...
//
// Push the result of looking up `id` on the current data
lookupData: function(id) {
this.push(this.nameLookup('data', id, 'data'));
},
// [pushStringParam]
//
// On stack, before: ...
// On stack, after: string, currentContext, ...
//
// This opcode is designed for use in string mode, which
// provides the string value of a parameter along with its
// depth rather than resolving it immediately.
pushStringParam: function(string, type) {
this.pushStackLiteral('depth' + this.lastContext);
this.pushString(type);
if (typeof string === 'string') {
this.pushString(string);
} else {
this.pushStackLiteral(string);
}
},
emptyHash: function() {
this.pushStackLiteral('{}');
if (this.options.stringParams) {
this.register('hashTypes', '{}');
}
},
pushHash: function() {
this.hash = {values: [], types: []};
},
popHash: function() {
var hash = this.hash;
this.hash = undefined;
if (this.options.stringParams) {
this.register('hashTypes', '{' + hash.types.join(',') + '}');
}
this.push('{\n ' + hash.values.join(',\n ') + '\n }');
},
// [pushString]
//
// On stack, before: ...
// On stack, after: quotedString(string), ...
//
// Push a quoted version of `string` onto the stack
pushString: function(string) {
this.pushStackLiteral(this.quotedString(string));
},
// [push]
//
// On stack, before: ...
// On stack, after: expr, ...
//
// Push an expression onto the stack
push: function(expr) {
this.inlineStack.push(expr);
return expr;
},
// [pushLiteral]
//
// On stack, before: ...
// On stack, after: value, ...
//
// Pushes a value onto the stack. This operation prevents
// the compiler from creating a temporary variable to hold
// it.
pushLiteral: function(value) {
this.pushStackLiteral(value);
},
// [pushProgram]
//
// On stack, before: ...
// On stack, after: program(guid), ...
//
// Push a program expression onto the stack. This takes
// a compile-time guid and converts it into a runtime-accessible
// expression.
pushProgram: function(guid) {
if (guid != null) {
this.pushStackLiteral(this.programExpression(guid));
} else {
this.pushStackLiteral(null);
}
},
// [invokeHelper]
//
// On stack, before: hash, inverse, program, params..., ...
// On stack, after: result of helper invocation
//
// Pops off the helper's parameters, invokes the helper,
// and pushes the helper's return value onto the stack.
//
// If the helper is not found, `helperMissing` is called.
invokeHelper: function(paramSize, name) {
this.context.aliases.helperMissing = 'helpers.helperMissing';
var helper = this.lastHelper = this.setupHelper(paramSize, name, true);
this.push(helper.name);
this.replaceStack(function(name) {
return name + ' ? ' + name + '.call(' +
helper.callParams + ") " + ": helperMissing.call(" +
helper.helperMissingParams + ")";
});
},
// [invokeKnownHelper]
//
// On stack, before: hash, inverse, program, params..., ...
// On stack, after: result of helper invocation
//
// This operation is used when the helper is known to exist,
// so a `helperMissing` fallback is not required.
invokeKnownHelper: function(paramSize, name) {
var helper = this.setupHelper(paramSize, name);
this.push(helper.name + ".call(" + helper.callParams + ")");
},
// [invokeAmbiguous]
//
// On stack, before: hash, inverse, program, params..., ...
// On stack, after: result of disambiguation
//
// This operation is used when an expression like `{{foo}}`
// is provided, but we don't know at compile-time whether it
// is a helper or a path.
//
// This operation emits more code than the other options,
// and can be avoided by passing the `knownHelpers` and
// `knownHelpersOnly` flags at compile-time.
invokeAmbiguous: function(name, helperCall) {
this.context.aliases.functionType = '"function"';
this.pushStackLiteral('{}'); // Hash value
var helper = this.setupHelper(0, name, helperCall);
var helperName = this.lastHelper = this.nameLookup('helpers', name, 'helper');
var nonHelper = this.nameLookup('depth' + this.lastContext, name, 'context');
var nextStack = this.nextStack();
this.source.push('if (' + nextStack + ' = ' + helperName + ') { ' + nextStack + ' = ' + nextStack + '.call(' + helper.callParams + '); }');
this.source.push('else { ' + nextStack + ' = ' + nonHelper + '; ' + nextStack + ' = typeof ' + nextStack + ' === functionType ? ' + nextStack + '.apply(depth0) : ' + nextStack + '; }');
},
// [invokePartial]
//
// On stack, before: context, ...
// On stack after: result of partial invocation
//
// This operation pops off a context, invokes a partial with that context,
// and pushes the result of the invocation back.
invokePartial: function(name) {
var params = [this.nameLookup('partials', name, 'partial'), "'" + name + "'", this.popStack(), "helpers", "partials"];
if (this.options.data) {
params.push("data");
}
this.context.aliases.self = "this";
this.push("self.invokePartial(" + params.join(", ") + ")");
},
// [assignToHash]
//
// On stack, before: value, hash, ...
// On stack, after: hash, ...
//
// Pops a value and hash off the stack, assigns `hash[key] = value`
// and pushes the hash back onto the stack.
assignToHash: function(key) {
var value = this.popStack(),
type;
if (this.options.stringParams) {
type = this.popStack();
this.popStack();
}
var hash = this.hash;
if (type) {
hash.types.push("'" + key + "': " + type);
}
hash.values.push("'" + key + "': (" + value + ")");
},
// HELPERS
compiler: JavaScriptCompiler,
compileChildren: function(environment, options) {
var children = environment.children, child, compiler;
for(var i=0, l=children.length; i<l; i++) {
child = children[i];
compiler = new this.compiler();
var index = this.matchExistingProgram(child);
if (index == null) {
this.context.programs.push(''); // Placeholder to prevent name conflicts for nested children
index = this.context.programs.length;
child.index = index;
child.name = 'program' + index;
this.context.programs[index] = compiler.compile(child, options, this.context);
this.context.environments[index] = child;
} else {
child.index = index;
child.name = 'program' + index;
}
}
},
matchExistingProgram: function(child) {
for (var i = 0, len = this.context.environments.length; i < len; i++) {
var environment = this.context.environments[i];
if (environment && environment.equals(child)) {
return i;
}
}
},
programExpression: function(guid) {
this.context.aliases.self = "this";
if(guid == null) {
return "self.noop";
}
var child = this.environment.children[guid],
depths = child.depths.list, depth;
var programParams = [child.index, child.name, "data"];
for(var i=0, l = depths.length; i<l; i++) {
depth = depths[i];
if(depth === 1) { programParams.push("depth0"); }
else { programParams.push("depth" + (depth - 1)); }
}
if(depths.length === 0) {
return "self.program(" + programParams.join(", ") + ")";
} else {
programParams.shift();
return "self.programWithDepth(" + programParams.join(", ") + ")";
}
},
register: function(name, val) {
this.useRegister(name);
this.source.push(name + " = " + val + ";");
},
useRegister: function(name) {
if(!this.registers[name]) {
this.registers[name] = true;
this.registers.list.push(name);
}
},
pushStackLiteral: function(item) {
return this.push(new Literal(item));
},
pushStack: function(item) {
this.flushInline();
var stack = this.incrStack();
if (item) {
this.source.push(stack + " = " + item + ";");
}
this.compileStack.push(stack);
return stack;
},
replaceStack: function(callback) {
var prefix = '',
inline = this.isInline(),
stack;
// If we are currently inline then we want to merge the inline statement into the
// replacement statement via ','
if (inline) {
var top = this.popStack(true);
if (top instanceof Literal) {
// Literals do not need to be inlined
stack = top.value;
} else {
// Get or create the current stack name for use by the inline
var name = this.stackSlot ? this.topStackName() : this.incrStack();
prefix = '(' + this.push(name) + ' = ' + top + '),';
stack = this.topStack();
}
} else {
stack = this.topStack();
}
var item = callback.call(this, stack);
if (inline) {
if (this.inlineStack.length || this.compileStack.length) {
this.popStack();
}
this.push('(' + prefix + item + ')');
} else {
// Prevent modification of the context depth variable. Through replaceStack
if (!/^stack/.test(stack)) {
stack = this.nextStack();
}
this.source.push(stack + " = (" + prefix + item + ");");
}
return stack;
},
nextStack: function() {
return this.pushStack();
},
incrStack: function() {
this.stackSlot++;
if(this.stackSlot > this.stackVars.length) { this.stackVars.push("stack" + this.stackSlot); }
return this.topStackName();
},
topStackName: function() {
return "stack" + this.stackSlot;
},
flushInline: function() {
var inlineStack = this.inlineStack;
if (inlineStack.length) {
this.inlineStack = [];
for (var i = 0, len = inlineStack.length; i < len; i++) {
var entry = inlineStack[i];
if (entry instanceof Literal) {
this.compileStack.push(entry);
} else {
this.pushStack(entry);
}
}
}
},
isInline: function() {
return this.inlineStack.length;
},
popStack: function(wrapped) {
var inline = this.isInline(),
item = (inline ? this.inlineStack : this.compileStack).pop();
if (!wrapped && (item instanceof Literal)) {
return item.value;
} else {
if (!inline) {
this.stackSlot--;
}
return item;
}
},
topStack: function(wrapped) {
var stack = (this.isInline() ? this.inlineStack : this.compileStack),
item = stack[stack.length - 1];
if (!wrapped && (item instanceof Literal)) {
return item.value;
} else {
return item;
}
},
quotedString: function(str) {
return '"' + str
.replace(/\\/g, '\\\\')
.replace(/"/g, '\\"')
.replace(/\n/g, '\\n')
.replace(/\r/g, '\\r') + '"';
},
setupHelper: function(paramSize, name, missingParams) {
var params = [];
this.setupParams(paramSize, params, missingParams);
var foundHelper = this.nameLookup('helpers', name, 'helper');
return {
params: params,
name: foundHelper,
callParams: ["depth0"].concat(params).join(", "),
helperMissingParams: missingParams && ["depth0", this.quotedString(name)].concat(params).join(", ")
};
},
// the params and contexts arguments are passed in arrays
// to fill in
setupParams: function(paramSize, params, useRegister) {
var options = [], contexts = [], types = [], param, inverse, program;
options.push("hash:" + this.popStack());
inverse = this.popStack();
program = this.popStack();
// Avoid setting fn and inverse if neither are set. This allows
// helpers to do a check for `if (options.fn)`
if (program || inverse) {
if (!program) {
this.context.aliases.self = "this";
program = "self.noop";
}
if (!inverse) {
this.context.aliases.self = "this";
inverse = "self.noop";
}
options.push("inverse:" + inverse);
options.push("fn:" + program);
}
for(var i=0; i<paramSize; i++) {
param = this.popStack();
params.push(param);
if(this.options.stringParams) {
types.push(this.popStack());
contexts.push(this.popStack());
}
}
if (this.options.stringParams) {
options.push("contexts:[" + contexts.join(",") + "]");
options.push("types:[" + types.join(",") + "]");
options.push("hashTypes:hashTypes");
}
if(this.options.data) {
options.push("data:data");
}
options = "{" + options.join(",") + "}";
if (useRegister) {
this.register('options', options);
params.push('options');
} else {
params.push(options);
}
return params.join(", ");
}
};
var reservedWords = (
"break else new var" +
" case finally return void" +
" catch for switch while" +
" continue function this with" +
" default if throw" +
" delete in try" +
" do instanceof typeof" +
" abstract enum int short" +
" boolean export interface static" +
" byte extends long super" +
" char final native synchronized" +
" class float package throws" +
" const goto private transient" +
" debugger implements protected volatile" +
" double import public let yield"
).split(" ");
var compilerWords = JavaScriptCompiler.RESERVED_WORDS = {};
for(var i=0, l=reservedWords.length; i<l; i++) {
compilerWords[reservedWords[i]] = true;
}
JavaScriptCompiler.isValidJavaScriptVariableName = function(name) {
if(!JavaScriptCompiler.RESERVED_WORDS[name] && /^[a-zA-Z_$][0-9a-zA-Z_$]+$/.test(name)) {
return true;
}
return false;
};
})(Handlebars.Compiler, Handlebars.JavaScriptCompiler);
Handlebars.precompile = function(input, options) {
if (!input || (typeof input !== 'string' && input.constructor !== Handlebars.AST.ProgramNode)) {
throw new Handlebars.Exception("You must pass a string or Handlebars AST to Handlebars.compile. You passed " + input);
}
options = options || {};
if (!('data' in options)) {
options.data = true;
}
var ast = Handlebars.parse(input);
var environment = new Handlebars.Compiler().compile(ast, options);
return new Handlebars.JavaScriptCompiler().compile(environment, options);
};
Handlebars.compile = function(input, options) {
if (!input || (typeof input !== 'string' && input.constructor !== Handlebars.AST.ProgramNode)) {
throw new Handlebars.Exception("You must pass a string or Handlebars AST to Handlebars.compile. You passed " + input);
}
options = options || {};
if (!('data' in options)) {
options.data = true;
}
var compiled;
function compile() {
var ast = Handlebars.parse(input);
var environment = new Handlebars.Compiler().compile(ast, options);
var templateSpec = new Handlebars.JavaScriptCompiler().compile(environment, options, undefined, true);
return Handlebars.template(templateSpec);
}
// Template is only compiled on first use and cached after that point.
return function(context, options) {
if (!compiled) {
compiled = compile();
}
return compiled.call(this, context, options);
};
};
;
// lib/handlebars/runtime.js
Handlebars.VM = {
template: function(templateSpec) {
// Just add water
var container = {
escapeExpression: Handlebars.Utils.escapeExpression,
invokePartial: Handlebars.VM.invokePartial,
programs: [],
program: function(i, fn, data) {
var programWrapper = this.programs[i];
if(data) {
return Handlebars.VM.program(fn, data);
} else if(programWrapper) {
return programWrapper;
} else {
programWrapper = this.programs[i] = Handlebars.VM.program(fn);
return programWrapper;
}
},
programWithDepth: Handlebars.VM.programWithDepth,
noop: Handlebars.VM.noop,
compilerInfo: null
};
return function(context, options) {
options = options || {};
var result = templateSpec.call(container, Handlebars, context, options.helpers, options.partials, options.data);
var compilerInfo = container.compilerInfo || [],
compilerRevision = compilerInfo[0] || 1,
currentRevision = Handlebars.COMPILER_REVISION;
if (compilerRevision !== currentRevision) {
if (compilerRevision < currentRevision) {
var runtimeVersions = Handlebars.REVISION_CHANGES[currentRevision],
compilerVersions = Handlebars.REVISION_CHANGES[compilerRevision];
throw "Template was precompiled with an older version of Handlebars than the current runtime. "+
"Please update your precompiler to a newer version ("+runtimeVersions+") or downgrade your runtime to an older version ("+compilerVersions+").";
} else {
// Use the embedded version info since the runtime doesn't know about this revision yet
throw "Template was precompiled with a newer version of Handlebars than the current runtime. "+
"Please update your runtime to a newer version ("+compilerInfo[1]+").";
}
}
return result;
};
},
programWithDepth: function(fn, data, $depth) {
var args = Array.prototype.slice.call(arguments, 2);
return function(context, options) {
options = options || {};
return fn.apply(this, [context, options.data || data].concat(args));
};
},
program: function(fn, data) {
return function(context, options) {
options = options || {};
return fn(context, options.data || data);
};
},
noop: function() { return ""; },
invokePartial: function(partial, name, context, helpers, partials, data) {
var options = { helpers: helpers, partials: partials, data: data };
if(partial === undefined) {
throw new Handlebars.Exception("The partial " + name + " could not be found");
} else if(partial instanceof Function) {
return partial(context, options);
} else if (!Handlebars.compile) {
throw new Handlebars.Exception("The partial " + name + " could not be compiled when running in runtime-only mode");
} else {
partials[name] = Handlebars.compile(partial, {data: data !== undefined});
return partials[name](context, options);
}
}
};
Handlebars.template = Handlebars.VM.template;
;
This source diff could not be displayed because it is too large. You can view the blob instead.
//
// Generated on Sun Dec 16 2012 22:47:05 GMT-0500 (EST) by Nodejitsu, Inc (Using Codesurgeon).
// Version 1.1.9
//
(function (exports) {
/*
* browser.js: Browser specific functionality for director.
*
* (C) 2011, Nodejitsu Inc.
* MIT LICENSE
*
*/
if (!Array.prototype.filter) {
Array.prototype.filter = function(filter, that) {
var other = [], v;
for (var i = 0, n = this.length; i < n; i++) {
if (i in this && filter.call(that, v = this[i], i, this)) {
other.push(v);
}
}
return other;
};
}
if (!Array.isArray){
Array.isArray = function(obj) {
return Object.prototype.toString.call(obj) === '[object Array]';
};
}
var dloc = document.location;
function dlocHashEmpty() {
// Non-IE browsers return '' when the address bar shows '#'; Director's logic
// assumes both mean empty.
return dloc.hash === '' || dloc.hash === '#';
}
var listener = {
mode: 'modern',
hash: dloc.hash,
history: false,
check: function () {
var h = dloc.hash;
if (h != this.hash) {
this.hash = h;
this.onHashChanged();
}
},
fire: function () {
if (this.mode === 'modern') {
this.history === true ? window.onpopstate() : window.onhashchange();
}
else {
this.onHashChanged();
}
},
init: function (fn, history) {
var self = this;
this.history = history;
if (!Router.listeners) {
Router.listeners = [];
}
function onchange(onChangeEvent) {
for (var i = 0, l = Router.listeners.length; i < l; i++) {
Router.listeners[i](onChangeEvent);
}
}
//note IE8 is being counted as 'modern' because it has the hashchange event
if ('onhashchange' in window && (document.documentMode === undefined
|| document.documentMode > 7)) {
// At least for now HTML5 history is available for 'modern' browsers only
if (this.history === true) {
// There is an old bug in Chrome that causes onpopstate to fire even
// upon initial page load. Since the handler is run manually in init(),
// this would cause Chrome to run it twise. Currently the only
// workaround seems to be to set the handler after the initial page load
// http://code.google.com/p/chromium/issues/detail?id=63040
setTimeout(function() {
window.onpopstate = onchange;
}, 500);
}
else {
window.onhashchange = onchange;
}
this.mode = 'modern';
}
else {
//
// IE support, based on a concept by Erik Arvidson ...
//
var frame = document.createElement('iframe');
frame.id = 'state-frame';
frame.style.display = 'none';
document.body.appendChild(frame);
this.writeFrame('');
if ('onpropertychange' in document && 'attachEvent' in document) {
document.attachEvent('onpropertychange', function () {
if (event.propertyName === 'location') {
self.check();
}
});
}
window.setInterval(function () { self.check(); }, 50);
this.onHashChanged = onchange;
this.mode = 'legacy';
}
Router.listeners.push(fn);
return this.mode;
},
destroy: function (fn) {
if (!Router || !Router.listeners) {
return;
}
var listeners = Router.listeners;
for (var i = listeners.length - 1; i >= 0; i--) {
if (listeners[i] === fn) {
listeners.splice(i, 1);
}
}
},
setHash: function (s) {
// Mozilla always adds an entry to the history
if (this.mode === 'legacy') {
this.writeFrame(s);
}
if (this.history === true) {
window.history.pushState({}, document.title, s);
// Fire an onpopstate event manually since pushing does not obviously
// trigger the pop event.
this.fire();
} else {
dloc.hash = (s[0] === '/') ? s : '/' + s;
}
return this;
},
writeFrame: function (s) {
// IE support...
var f = document.getElementById('state-frame');
var d = f.contentDocument || f.contentWindow.document;
d.open();
d.write("<script>_hash = '" + s + "'; onload = parent.listener.syncHash;<script>");
d.close();
},
syncHash: function () {
// IE support...
var s = this._hash;
if (s != dloc.hash) {
dloc.hash = s;
}
return this;
},
onHashChanged: function () {}
};
var Router = exports.Router = function (routes) {
if (!(this instanceof Router)) return new Router(routes);
this.params = {};
this.routes = {};
this.methods = ['on', 'once', 'after', 'before'];
this.scope = [];
this._methods = {};
this._insert = this.insert;
this.insert = this.insertEx;
this.historySupport = (window.history != null ? window.history.pushState : null) != null
this.configure();
this.mount(routes || {});
};
Router.prototype.init = function (r) {
var self = this;
this.handler = function(onChangeEvent) {
var newURL = onChangeEvent && onChangeEvent.newURL || window.location.hash;
var url = self.history === true ? self.getPath() : newURL.replace(/.*#/, '');
self.dispatch('on', url);
};
listener.init(this.handler, this.history);
if (this.history === false) {
if (dlocHashEmpty() && r) {
dloc.hash = r;
} else if (!dlocHashEmpty()) {
self.dispatch('on', dloc.hash.replace(/^#/, ''));
}
}
else {
var routeTo = dlocHashEmpty() && r ? r : !dlocHashEmpty() ? dloc.hash.replace(/^#/, '') : null;
if (routeTo) {
window.history.replaceState({}, document.title, routeTo);
}
// Router has been initialized, but due to the chrome bug it will not
// yet actually route HTML5 history state changes. Thus, decide if should route.
if (routeTo || this.run_in_init === true) {
this.handler();
}
}
return this;
};
Router.prototype.explode = function () {
var v = this.history === true ? this.getPath() : dloc.hash;
if (v.charAt(1) === '/') { v=v.slice(1) }
return v.slice(1, v.length).split("/");
};
Router.prototype.setRoute = function (i, v, val) {
var url = this.explode();
if (typeof i === 'number' && typeof v === 'string') {
url[i] = v;
}
else if (typeof val === 'string') {
url.splice(i, v, s);
}
else {
url = [i];
}
listener.setHash(url.join('/'));
return url;
};
//
// ### function insertEx(method, path, route, parent)
// #### @method {string} Method to insert the specific `route`.
// #### @path {Array} Parsed path to insert the `route` at.
// #### @route {Array|function} Route handlers to insert.
// #### @parent {Object} **Optional** Parent "routes" to insert into.
// insert a callback that will only occur once per the matched route.
//
Router.prototype.insertEx = function(method, path, route, parent) {
if (method === "once") {
method = "on";
route = function(route) {
var once = false;
return function() {
if (once) return;
once = true;
return route.apply(this, arguments);
};
}(route);
}
return this._insert(method, path, route, parent);
};
Router.prototype.getRoute = function (v) {
var ret = v;
if (typeof v === "number") {
ret = this.explode()[v];
}
else if (typeof v === "string"){
var h = this.explode();
ret = h.indexOf(v);
}
else {
ret = this.explode();
}
return ret;
};
Router.prototype.destroy = function () {
listener.destroy(this.handler);
return this;
};
Router.prototype.getPath = function () {
var path = window.location.pathname;
if (path.substr(0, 1) !== '/') {
path = '/' + path;
}
return path;
};
function _every(arr, iterator) {
for (var i = 0; i < arr.length; i += 1) {
if (iterator(arr[i], i, arr) === false) {
return;
}
}
}
function _flatten(arr) {
var flat = [];
for (var i = 0, n = arr.length; i < n; i++) {
flat = flat.concat(arr[i]);
}
return flat;
}
function _asyncEverySeries(arr, iterator, callback) {
if (!arr.length) {
return callback();
}
var completed = 0;
(function iterate() {
iterator(arr[completed], function(err) {
if (err || err === false) {
callback(err);
callback = function() {};
} else {
completed += 1;
if (completed === arr.length) {
callback();
} else {
iterate();
}
}
});
})();
}
function paramifyString(str, params, mod) {
mod = str;
for (var param in params) {
if (params.hasOwnProperty(param)) {
mod = params[param](str);
if (mod !== str) {
break;
}
}
}
return mod === str ? "([._a-zA-Z0-9-]+)" : mod;
}
function regifyString(str, params) {
var matches, last = 0, out = "";
while (matches = str.substr(last).match(/[^\w\d\- %@&]*\*[^\w\d\- %@&]*/)) {
last = matches.index + matches[0].length;
matches[0] = matches[0].replace(/^\*/, "([_.()!\\ %@&a-zA-Z0-9-]+)");
out += str.substr(0, matches.index) + matches[0];
}
str = out += str.substr(last);
var captures = str.match(/:([^\/]+)/ig), length;
if (captures) {
length = captures.length;
for (var i = 0; i < length; i++) {
str = str.replace(captures[i], paramifyString(captures[i], params));
}
}
return str;
}
function terminator(routes, delimiter, start, stop) {
var last = 0, left = 0, right = 0, start = (start || "(").toString(), stop = (stop || ")").toString(), i;
for (i = 0; i < routes.length; i++) {
var chunk = routes[i];
if (chunk.indexOf(start, last) > chunk.indexOf(stop, last) || ~chunk.indexOf(start, last) && !~chunk.indexOf(stop, last) || !~chunk.indexOf(start, last) && ~chunk.indexOf(stop, last)) {
left = chunk.indexOf(start, last);
right = chunk.indexOf(stop, last);
if (~left && !~right || !~left && ~right) {
var tmp = routes.slice(0, (i || 1) + 1).join(delimiter);
routes = [ tmp ].concat(routes.slice((i || 1) + 1));
}
last = (right > left ? right : left) + 1;
i = 0;
} else {
last = 0;
}
}
return routes;
}
Router.prototype.configure = function(options) {
options = options || {};
for (var i = 0; i < this.methods.length; i++) {
this._methods[this.methods[i]] = true;
}
this.recurse = options.recurse || this.recurse || false;
this.async = options.async || false;
this.delimiter = options.delimiter || "/";
this.strict = typeof options.strict === "undefined" ? true : options.strict;
this.notfound = options.notfound;
this.resource = options.resource;
this.history = options.html5history && this.historySupport || false;
this.run_in_init = this.history === true && options.run_handler_in_init !== false;
this.every = {
after: options.after || null,
before: options.before || null,
on: options.on || null
};
return this;
};
Router.prototype.param = function(token, matcher) {
if (token[0] !== ":") {
token = ":" + token;
}
var compiled = new RegExp(token, "g");
this.params[token] = function(str) {
return str.replace(compiled, matcher.source || matcher);
};
};
Router.prototype.on = Router.prototype.route = function(method, path, route) {
var self = this;
if (!route && typeof path == "function") {
route = path;
path = method;
method = "on";
}
if (Array.isArray(path)) {
return path.forEach(function(p) {
self.on(method, p, route);
});
}
if (path.source) {
path = path.source.replace(/\\\//ig, "/");
}
if (Array.isArray(method)) {
return method.forEach(function(m) {
self.on(m.toLowerCase(), path, route);
});
}
path = path.split(new RegExp(this.delimiter));
path = terminator(path, this.delimiter);
this.insert(method, this.scope.concat(path), route);
};
Router.prototype.dispatch = function(method, path, callback) {
var self = this, fns = this.traverse(method, path, this.routes, ""), invoked = this._invoked, after;
this._invoked = true;
if (!fns || fns.length === 0) {
this.last = [];
if (typeof this.notfound === "function") {
this.invoke([ this.notfound ], {
method: method,
path: path
}, callback);
}
return false;
}
if (this.recurse === "forward") {
fns = fns.reverse();
}
function updateAndInvoke() {
self.last = fns.after;
self.invoke(self.runlist(fns), self, callback);
}
after = this.every && this.every.after ? [ this.every.after ].concat(this.last) : [ this.last ];
if (after && after.length > 0 && invoked) {
if (this.async) {
this.invoke(after, this, updateAndInvoke);
} else {
this.invoke(after, this);
updateAndInvoke();
}
return true;
}
updateAndInvoke();
return true;
};
Router.prototype.invoke = function(fns, thisArg, callback) {
var self = this;
if (this.async) {
_asyncEverySeries(fns, function apply(fn, next) {
if (Array.isArray(fn)) {
return _asyncEverySeries(fn, apply, next);
} else if (typeof fn == "function") {
fn.apply(thisArg, fns.captures.concat(next));
}
}, function() {
if (callback) {
callback.apply(thisArg, arguments);
}
});
} else {
_every(fns, function apply(fn) {
if (Array.isArray(fn)) {
return _every(fn, apply);
} else if (typeof fn === "function") {
return fn.apply(thisArg, fns.captures || []);
} else if (typeof fn === "string" && self.resource) {
self.resource[fn].apply(thisArg, fns.captures || []);
}
});
}
};
Router.prototype.traverse = function(method, path, routes, regexp, filter) {
var fns = [], current, exact, match, next, that;
function filterRoutes(routes) {
if (!filter) {
return routes;
}
function deepCopy(source) {
var result = [];
for (var i = 0; i < source.length; i++) {
result[i] = Array.isArray(source[i]) ? deepCopy(source[i]) : source[i];
}
return result;
}
function applyFilter(fns) {
for (var i = fns.length - 1; i >= 0; i--) {
if (Array.isArray(fns[i])) {
applyFilter(fns[i]);
if (fns[i].length === 0) {
fns.splice(i, 1);
}
} else {
if (!filter(fns[i])) {
fns.splice(i, 1);
}
}
}
}
var newRoutes = deepCopy(routes);
newRoutes.matched = routes.matched;
newRoutes.captures = routes.captures;
newRoutes.after = routes.after.filter(filter);
applyFilter(newRoutes);
return newRoutes;
}
if (path === this.delimiter && routes[method]) {
next = [ [ routes.before, routes[method] ].filter(Boolean) ];
next.after = [ routes.after ].filter(Boolean);
next.matched = true;
next.captures = [];
return filterRoutes(next);
}
for (var r in routes) {
if (routes.hasOwnProperty(r) && (!this._methods[r] || this._methods[r] && typeof routes[r] === "object" && !Array.isArray(routes[r]))) {
current = exact = regexp + this.delimiter + r;
if (!this.strict) {
exact += "[" + this.delimiter + "]?";
}
match = path.match(new RegExp("^" + exact));
if (!match) {
continue;
}
if (match[0] && match[0] == path && routes[r][method]) {
next = [ [ routes[r].before, routes[r][method] ].filter(Boolean) ];
next.after = [ routes[r].after ].filter(Boolean);
next.matched = true;
next.captures = match.slice(1);
if (this.recurse && routes === this.routes) {
next.push([ routes.before, routes.on ].filter(Boolean));
next.after = next.after.concat([ routes.after ].filter(Boolean));
}
return filterRoutes(next);
}
next = this.traverse(method, path, routes[r], current);
if (next.matched) {
if (next.length > 0) {
fns = fns.concat(next);
}
if (this.recurse) {
fns.push([ routes[r].before, routes[r].on ].filter(Boolean));
next.after = next.after.concat([ routes[r].after ].filter(Boolean));
if (routes === this.routes) {
fns.push([ routes["before"], routes["on"] ].filter(Boolean));
next.after = next.after.concat([ routes["after"] ].filter(Boolean));
}
}
fns.matched = true;
fns.captures = next.captures;
fns.after = next.after;
return filterRoutes(fns);
}
}
}
return false;
};
Router.prototype.insert = function(method, path, route, parent) {
var methodType, parentType, isArray, nested, part;
path = path.filter(function(p) {
return p && p.length > 0;
});
parent = parent || this.routes;
part = path.shift();
if (/\:|\*/.test(part) && !/\\d|\\w/.test(part)) {
part = regifyString(part, this.params);
}
if (path.length > 0) {
parent[part] = parent[part] || {};
return this.insert(method, path, route, parent[part]);
}
if (!part && !path.length && parent === this.routes) {
methodType = typeof parent[method];
switch (methodType) {
case "function":
parent[method] = [ parent[method], route ];
return;
case "object":
parent[method].push(route);
return;
case "undefined":
parent[method] = route;
return;
}
return;
}
parentType = typeof parent[part];
isArray = Array.isArray(parent[part]);
if (parent[part] && !isArray && parentType == "object") {
methodType = typeof parent[part][method];
switch (methodType) {
case "function":
parent[part][method] = [ parent[part][method], route ];
return;
case "object":
parent[part][method].push(route);
return;
case "undefined":
parent[part][method] = route;
return;
}
} else if (parentType == "undefined") {
nested = {};
nested[method] = route;
parent[part] = nested;
return;
}
throw new Error("Invalid route context: " + parentType);
};
Router.prototype.extend = function(methods) {
var self = this, len = methods.length, i;
function extend(method) {
self._methods[method] = true;
self[method] = function() {
var extra = arguments.length === 1 ? [ method, "" ] : [ method ];
self.on.apply(self, extra.concat(Array.prototype.slice.call(arguments)));
};
}
for (i = 0; i < len; i++) {
extend(methods[i]);
}
};
Router.prototype.runlist = function(fns) {
var runlist = this.every && this.every.before ? [ this.every.before ].concat(_flatten(fns)) : _flatten(fns);
if (this.every && this.every.on) {
runlist.push(this.every.on);
}
runlist.captures = fns.captures;
runlist.source = fns.source;
return runlist;
};
Router.prototype.mount = function(routes, path) {
if (!routes || typeof routes !== "object" || Array.isArray(routes)) {
return;
}
var self = this;
path = path || [];
if (!Array.isArray(path)) {
path = path.split(self.delimiter);
}
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;
if (isRoute) {
rename = rename.slice((rename.match(new RegExp(self.delimiter)) || [ "" ])[0].length);
parts.shift();
}
if (isRoute && routeType === "object" && !Array.isArray(routes[route])) {
local = local.concat(parts);
self.mount(routes[route], local);
return;
}
if (isRoute) {
local = local.concat(rename.split(self.delimiter));
local = terminator(local, self.delimiter);
}
self.insert(event, local, routes[route]);
}
for (var route in routes) {
if (routes.hasOwnProperty(route)) {
insertOrMount(route, path.slice(0));
}
}
};
}(typeof exports === "object" ? exports : window));
\ No newline at end of file
;(function (soma, undefined) {
'use strict';
soma.template = soma.template || {};
soma.template.version = "0.1.8";
var 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_NO_PARAM: "Error in soma.template, a template requires at least 1 parameter - soma.template.create(element)"
};
var tokenStart = '{{';
var tokenEnd = '}}';
var helpersObject = {};
var helpersScopeObject = {};
var settings = soma.template.settings = soma.template.settings || {};
settings.autocreate = true;
var tokens = settings.tokens = {
start: function(value) {
if (isDefined(value) && value !== '') {
tokenStart = escapeRegExp(value);
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");
elm.className = elm.className.replace(reg, "$2");
}
}
removeClass(elm, className);
}
// jquery contains
var contains = typeof document !== 'object' ? function(){} : document.documentElement.contains ?
function( a, b ) {
var adown = a.nodeType === 9 ? a.documentElement : a,
bup = b && b.parentNode;
return a === bup || !!( bup && bup.nodeType === 1 && adown.contains && adown.contains(bup) );
} :
document.documentElement.compareDocumentPosition ?
function( a, b ) {
return b && !!( a.compareDocumentPosition( b ) & 16 );
} :
function( a, b ) {
while ( (b = b.parentNode) ) {
if ( b === a ) {
return true;
}
}
return false;
};
function HashMap() {
var items = {};
var id = 1;
//var uuid = function(a,b){for(b=a='';a++<36;b+=a*51&52?(a^15?8^Math.random()*(a^20?16:4):4).toString(16):'-');return b;}
function uuid() { return ++id; };
function getKey(target) {
if (!target) return;
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.hashkey ? target.hashkey : target.hashkey = 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) {
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) {
node.childrenRepeater.length = data.length;
}
}
else {
// process object
var count = -1;
for (var o in data) {
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;
}
if (node.element.parentNode) {
node.element.parentNode.removeChild(node.element);
}
}
function cloneRepeaterNode(element, node) {
var newNode = new Node(element, node.scope._createChild());
if (node.attributes) {
var attrs = [];
for (var i = 0, l = node.attributes.length; i < l; i++) {
newNode.renderAsHtml = node.renderAsHtml;
if (node.attributes[i].name === settings.attributes.skip) {
newNode.skip = (node.attributes[i].value === "" || node.attributes[i].value === "true");
}
if (node.attributes[i].name === settings.attributes.html) {
newNode.html = (node.attributes[i].value === "" || node.attributes[i].value === "true");
}
if (node.attributes[i].name !== attributes.repeat) {
var attribute = new Attribute(node.attributes[i].name, node.attributes[i].value, newNode);
attrs.push(attribute);
}
if (events[node.attributes[i].name]) {
newNode.addEvent(events[node.attributes[i].name], node.attributes[i].value);
}
}
newNode.attributes = attrs;
}
return newNode;
}
function createRepeaterChild(node, count, data, indexVar, indexVarValue, previousElement) {
var existingChild = node.childrenRepeater[count];
if (!existingChild) {
// no existing node
var newElement = node.element.cloneNode(true);
// can't recreate the node with a cloned element on IE7
// 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;
newNode.parent = node.parent;
newNode.template = node.template;
node.childrenRepeater[count] = newNode;
updateScopeWithRepeaterData(node.repeater, newNode.scope, data);
newNode.scope[indexVar] = indexVarValue;
compile(node.template, newElement, node.parent, newNode);
newNode.update();
newNode.render();
if (!previousElement) {
if (node.previousSibling) insertAfter(node.previousSibling, newElement);
else if (node.nextSibling) insertBefore(node.nextSibling, newElement);
else node.parent.element.appendChild(newElement);
}
else {
insertAfter(previousElement, newElement);
}
return newElement;
}
else {
// existing node
updateScopeWithRepeaterData(node.repeater, existingChild.scope, data);
existingChild.scope[indexVar] = indexVarValue;
existingChild.update();
existingChild.render();
return existingChild.element;
}
}
var Scope = function(data) {
function createChild(data) {
var obj = createObject(data);
obj._parent = this;
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) {
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.attributesHashMap = 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;
else 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;
}
}
else {
for (var i = 0, l = this.children.length; i < l; i++) {
node = this.children[i].getNode(element);
if (node) return node;
}
}
return null;
},
getAttribute: function(name) {
if (this.attributes) {
for (var i = 0, l = this.attributes.length; i < l; i++) {
var att = this.attributes[i];
if (att.interpolationName && att.interpolationName.value === name) {
return att;
}
}
}
},
update: function() {
if (childNodeIsTemplate(this)) 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();
}
}
updateNodeChildren(this);
},
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;
}
}
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) {
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);
}
};
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) {
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();
}
}
}
if (this.attributes) {
for (var i = 0, l = this.attributes.length; i < l; i++) {
this.attributes[i].render();
}
}
if (this.repeater) {
renderNodeRepeater(this);
}
else {
renderNodeChildren(this);
}
}
};
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() {
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 {
if (this.node.isRepeaterChild && ie === 7) {
// delete attributes on cloned elements crash IE7
}
else {
this.node.element.removeAttribute(this.interpolationName.value);
}
if (this.previousName) {
if (ie === 7 && this.previousName === 'class') {
// iE
this.node.element.className = "";
}
else {
if (this.node.isRepeaterChild && ie === 7) {
// delete attributes on cloned elements crash IE7
}
else {
this.node.element.removeAttribute(this.previousName);
}
}
}
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) {
element.style.display = isAttributeDefined(this.value) ? "none" : "block";
}
// show
if (this.name === attributes.show) {
element.style.display = isAttributeDefined(this.value) ? "block" : "none";
}
// checked
if (this.name === attributes.checked) {
renderSpecialAttribute(this.name, this.value, 'checked');
element.checked = isAttributeDefined(this.value) ? true : false;
}
// disabled
if (this.name === attributes.disabled) {
renderSpecialAttribute(this.name, this.value, 'disabled');
}
// multiple
if (this.name === attributes.multiple) {
renderSpecialAttribute(this.name, this.value, 'multiple');
}
// readonly
if (this.name === attributes.readonly) {
if (ie === 7) {
element.readOnly = isAttributeDefined(this.value) ? true : false;
}
else {
renderSpecialAttribute(this.name, this.value, 'readonly');
}
}
// selected
if (this.name === attributes.selected) {
renderSpecialAttribute(this.name, this.value, 'selected');
}
// normal attribute
function renderAttribute(name, value) {
if (ie === 7 && name === "class") {
element.className = value;
}
else {
element.setAttribute(name, value);
}
}
// special attribute
function renderSpecialAttribute(name, value, attrName) {
if (isAttributeDefined(value)) {
element.setAttribute(attrName, attrName);
}
else {
element.removeAttribute(attrName);
}
}
// src attribute
function renderSrc(name, value) {
element.setAttribute('src', value);
}
// href attribute
function renderHref(name, value) {
element.setAttribute('href', value);
}
}
};
var Interpolation = function(value, node, attribute) {
this.value = node && !isTextNode(node.element) ? trim(value) : value;
this.node = node;
this.attribute = attribute;
this.sequence = [];
this.expressions = [];
var parts = this.value.match(regex.sequence);
if (parts) {
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);
}
};
Interpolation.prototype = {
toString: function() {
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;
}
};
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) {
this.node.dispose();
}
this.element = null;
this.watchers = null;
this.node = null;
}
};
// written by Dean Edwards, 2005
// with input from Tino Zijdel, Matthias Miller, Diego Perini
// http://dean.edwards.name/weblog/2005/10/add-event/
function addEvent(element, type, handler) {
if (element.addEventListener) {
element.addEventListener(type, handler, false);
} else {
// assign each event handler a unique ID
if (!handler.$$guid) handler.$$guid = addEvent.guid++;
// create a hash table of event types for the element
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) {
var parts = pattern.match(regex.func);
if (parts) {
var func = parts[1];
if (isFunction(object[func])) {
return object[func];
}
}
}
function clearEvents(element) {
var i = eventStore.length, l = 0;
while (--i >= l) {
var item = eventStore[i];
if (element === item.element || contains(element, item.element)) {
removeEvent(item.element, item.type, item.handler);
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 parse = function(element) {
var child = !element ? document.body : element.firstChild;
while (child) {
if (child.nodeType === 1) {
parse(child);
var attrValue = child.getAttribute(attributes.template);
if (attrValue) {
var getFunction = new Function('return ' + attrValue + ';');
try {
var f = getFunction();
if (isFunction(f)) {
soma.template.bootstrap(attrValue, child, f);
}
} catch(err){};
}
}
child = child.nextSibling;
}
};
ready(parse);
}
function bootstrapTemplate(attrValue, element, func) {
var tpl = createTemplate(element);
func(tpl, tpl.scope, tpl.element, tpl.node);
}
function createTemplate(source, target) {
var element;
if (isString(source)) {
// string template
if (!isElement(target)) {
throw new Error(soma.template.errors.TEMPLATE_STRING_NO_ELEMENT);
}
target.innerHTML = source;
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 {
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) {
return templates.get(element);
}
function renderAllTemplates() {
for (var key in templates.getData()) {
templates.get(key).render();
}
}
function appendHelpers(obj) {
if (obj === null) {
helpersObject = {};
helpersScopeObject = {};
}
if (isDefined(obj) && isObject(obj)) {
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
helpersObject[key] = helpersScopeObject[key] = obj[key];
}
}
}
return helpersObject;
}
// set regex
tokens.start(tokenStart);
tokens.end(tokenEnd);
// plugins
soma.plugins = soma.plugins || {};
var TemplatePlugin = function(instance, injector) {
instance.constructor.prototype.createTemplate = function(cl, domElement) {
if (!cl || typeof cl !== "function") {
throw new Error("Error creating a template, the first parameter must be a function.");
}
if (domElement && isElement(domElement)) {
var template = soma.template.create(domElement);
for (var key in template) {
if (typeof template[key] === 'function') {
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);
}
return null;
}
soma.template.bootstrap = function(attrValue, element, func) {
instance.createTemplate(func, element);
}
}
if (soma.plugins && soma.plugins.add) {
soma.plugins.add(TemplatePlugin);
}
soma.template.Plugin = TemplatePlugin;
// exports
soma.template.create = createTemplate;
soma.template.get = getTemplate;
soma.template.renderAll = renderAllTemplates;
soma.template.helpers = appendHelpers;
soma.template.bootstrap = bootstrapTemplate;
soma.template.addEvent = addEvent;
soma.template.removeEvent = removeEvent;
soma.template.parseEvents = parseEvents;
soma.template.clearEvents = clearEvents;
soma.template.ready = ready;
// register for AMD module
if (typeof define === 'function' && define.amd) {
define("soma-template", soma.template);
}
// export for node.js
if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
exports = module.exports = soma.template;
}
exports = soma.template;
}
})(this['soma'] = this['soma'] || {});
\ No newline at end of file
/*
Copyright (c) | 2012 | 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
and associated documentation 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 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 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 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.
*/
;(function(infuse, undefined) {
"use strict";
infuse.version = "0.6.3";
// regex from angular JS (https://github.com/angular/angular.js)
var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
var FN_ARG_SPLIT = /,/;
var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
if(!Array.prototype.contains) {
Array.prototype.contains = function(value) {
var i = this.length;
while (i--) {
if (this[i] === value) return true;
}
return false;
};
}
infuse.InjectorError = {
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_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_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",
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_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) {
this.prop = prop;
this.value = value;
this.cl = cl;
this.singleton = singleton || false;
};
var validateProp = function(prop) {
if (typeof prop !== "string") {
throw new Error(infuse.InjectorError.MAPPING_BAD_PROP);
}
};
var validateValue = function(prop, val) {
if (val === undefined || val === null) {
throw new Error(infuse.InjectorError.MAPPING_BAD_VALUE + prop);
}
};
var validateClass = function(prop, val) {
if (typeof val !== "function") {
throw new Error(infuse.InjectorError.MAPPING_BAD_CLASS + prop);
}
};
var validateBooleanSingleton = function(prop, singleton) {
if (typeof singleton !== "boolean") {
throw new Error(infuse.InjectorError.MAPPING_BAD_SINGLETON + prop);
}
};
var validateConstructorInjectionLoop = function(name, cl) {
var params = infuse.getConstructorParams(cl);
if (params.contains(name)) {
throw new Error(infuse.InjectorError.INJECT_INSTANCE_IN_ITSELF_CONSTRUCTOR);
}
};
var validatePropertyInjectionLoop = function(name, target) {
if (target.hasOwnProperty(name)) {
throw new Error(infuse.InjectorError.INJECT_INSTANCE_IN_ITSELF_PROPERTY);
}
};
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() {
this.mappings = {};
this.parent = null;
};
infuse.getConstructorParams = function(cl) {
var args = [];
var clStr = cl.toString().replace(STRIP_COMMENTS, '');
var argsFlat = clStr.match(FN_ARGS);
var spl = argsFlat[1].split(FN_ARG_SPLIT);
for (var i=0; i<spl.length; i++) {
var arg = spl[i];
arg.replace(FN_ARG, function(all, underscore, name){
args.push(name);
});
}
return args;
};
infuse.Injector.prototype = {
createChild: function() {
var injector = new infuse.Injector();
injector.parent = this;
return injector;
},
getMappingVo: function(prop) {
if (!this.mappings) return null;
if (this.mappings[prop]) return this.mappings[prop];
if (this.parent) return this.parent.getMappingVo(prop);
return null;
},
mapValue: function(prop, val) {
if (this.mappings[prop]) {
throw new Error(infuse.InjectorError.MAPPING_ALREADY_EXISTS + prop);
}
validateProp(prop);
validateValue(prop, val);
this.mappings[prop] = new MappingVO(prop, val);
return this;
},
mapClass: function(prop, cl, singleton) {
if (this.mappings[prop]) {
throw new Error(infuse.InjectorError.MAPPING_ALREADY_EXISTS + prop);
}
validateProp(prop);
validateClass(prop, cl);
if (singleton) validateBooleanSingleton(prop, singleton);
this.mappings[prop] = new MappingVO(prop, null, cl, singleton);
return this;
},
removeMapping: function(prop) {
this.mappings[prop] = null;
delete this.mappings[prop];
return this;
},
hasMapping: function(prop) {
return !!this.mappings[prop];
},
hasInheritedMapping: function(prop) {
return !!this.getMappingVo(prop);
},
getMapping: function(value) {
for (var name in this.mappings) {
var vo = this.mappings[name];
if (vo.value === value || vo.cl === value) {
return vo.prop;
}
}
},
getValue: function(prop) {
var vo = this.mappings[prop];
if (!vo) {
if (this.parent) return this.parent.getValue.apply(this.parent, arguments);
else throw new Error(infuse.InjectorError.NO_MAPPING_FOUND);
}
if (vo.cl) {
arguments[0] = vo.cl;
return this.getValueFromClass.apply(this, arguments);
}
return vo.value;
},
getClass: function(prop) {
var vo = this.mappings[prop];
if (!vo) {
if (this.parent) return this.parent.getClass(prop);
else return undefined;
}
if (vo.cl) return vo.cl;
return undefined;
},
instantiate: function(TargetClass) {
if (typeof TargetClass !== "function") {
throw new Error(infuse.InjectorError.CREATE_INSTANCE_INVALID_PARAM);
}
var TargetClass = arguments[0];
var args = [null];
var params = infuse.getConstructorParams(TargetClass, this.mappings);
for (var i=0; i<params.length; i++) {
if (arguments[i+1] !== undefined && arguments[i+1] !== null) {
// argument found
args.push(arguments[i+1]);
}
else {
var name = params[i];
// no argument found
var vo = this.getMappingVo(name);
if (!!vo) {
// found mapping
var val = this.getInjectedValue(vo, name);
args.push(val);
}
else {
// no mapping found
args.push(undefined);
}
}
}
return new (Function.prototype.bind.apply(TargetClass, args));
},
inject: function (target, isParent) {
if (this.parent) {
this.parent.inject(target, true);
}
for (var name in this.mappings) {
var vo = this.getMappingVo(name);
if (target.hasOwnProperty(vo.prop)) {
var val = this.getInjectedValue(vo, name);
target[name] = val;
}
}
if (typeof target.postConstruct === 'function' && !isParent) {
target.postConstruct();
}
return this;
},
getInjectedValue: function(vo, name) {
var val = vo.value;
var injectee;
if (vo.cl) {
var params = infuse.getConstructorParams(vo.cl);
if (vo.singleton) {
if (!vo.value) {
validateConstructorInjectionLoop(name, vo.cl);
vo.value = this.instantiate(vo.cl);
injectee = vo.value;
}
val = vo.value;
}
else {
validateConstructorInjectionLoop(name, vo.cl);
val = this.instantiate(vo.cl);
injectee = val;
}
}
if (injectee) {
validatePropertyInjectionLoop(name, injectee);
this.inject(injectee);
}
return val;
},
createInstance: function() {
var instance = this.instantiate.apply(this, arguments);
this.inject(instance);
return instance;
},
getValueFromClass: function(cl) {
for (var name in this.mappings) {
var vo = this.mappings[name];
if (vo.cl == cl) {
if (vo.singleton) {
if (!vo.value) vo.value = this.createInstance.apply(this, arguments);
return vo.value;
}
else {
return this.createInstance.apply(this, arguments);
}
}
}
if (this.parent) {
return this.parent.getValueFromClass.apply(this.parent, arguments);
} else {
throw new Error(infuse.InjectorError.NO_MAPPING_FOUND);
}
},
dispose: function() {
this.mappings = {};
}
};
if (!Function.prototype.bind) {
Function.prototype.bind = function bind(that) {
var target = this;
if (typeof target != "function") {
throw new Error("Error, you must bind a function.");
}
var args = Array.prototype.slice.call(arguments, 1); // for normal call
var bound = function () {
if (this instanceof bound) {
var F = function(){};
F.prototype = target.prototype;
var self = new F;
var result = target.apply(
self,
args.concat(Array.prototype.slice.call(arguments))
);
if (Object(result) === result) {
return result;
}
return self;
} else {
return target.apply(
that,
args.concat(Array.prototype.slice.call(arguments))
);
}
};
return bound;
};
}
// register for AMD module
if (typeof define === 'function' && define.amd) {
define("infuse", infuse);
}
// export for node.js
if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
exports = module.exports = infuse;
}
exports = infuse;
}
})(this['infuse'] = this['infuse'] || {});
/*
Copyright (c) | 2012 | 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
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
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 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
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.
*/
;(function (soma, undefined) {
"use strict";
soma.events = {};
soma.events.version = "0.5.2";
if (!Function.prototype.bind) {
Function.prototype.bind = function bind(that) {
var target = this;
if (typeof target != "function") {
throw new Error("Error, you must bind a function.");
}
var args = Array.prototype.slice.call(arguments, 1); // for normal call
var bound = function () {
if (this instanceof bound) {
var F = function(){};
F.prototype = target.prototype;
var self = new F;
var result = target.apply(
self,
args.concat(Array.prototype.slice.call(arguments))
);
if (Object(result) === result) {
return result;
}
return self;
} else {
return target.apply(
that,
args.concat(Array.prototype.slice.call(arguments))
);
}
};
return bound;
};
};
soma.Event = function (type, params, bubbles, cancelable) {
var e = soma.Event.createGenericEvent(type, bubbles, cancelable);
if (params !== null && params !== undefined) {
e.params = params;
}
e.isCloned = false;
e.clone = this.clone.bind(e);
e.isIE9 = this.isIE9;
e.isDefaultPrevented = this.isDefaultPrevented;
if (this.isIE9() || !e.preventDefault || (e.getDefaultPrevented === undefined && e.defaultPrevented === undefined )) {
e.preventDefault = this.preventDefault.bind(e);
}
if (this.isIE9()) e.IE9PreventDefault = false;
return e;
};
soma.Event.prototype.clone = function () {
var e = soma.Event.createGenericEvent(this.type, this.bubbles, this.cancelable);
e.params = this.params;
e.isCloned = true;
e.clone = this.clone;
e.isDefaultPrevented = this.isDefaultPrevented;
e.isIE9 = this.isIE9;
if (this.isIE9()) e.IE9PreventDefault = this.IE9PreventDefault;
return e;
};
soma.Event.prototype.preventDefault = function () {
if (!this.cancelable) return false;
this.defaultPrevented = true;
if (this.isIE9()) this.IE9PreventDefault = true;
return this;
};
soma.Event.prototype.isDefaultPrevented = function () {
if (!this.cancelable) return false;
if (this.isIE9()) {
return this.IE9PreventDefault;
}
if (this.defaultPrevented !== undefined) {
return this.defaultPrevented;
} else if (this.getDefaultPrevented !== undefined) {
return this.getDefaultPrevented();
}
return false;
};
soma.Event.createGenericEvent = function (type, bubbles, cancelable) {
var event;
bubbles = bubbles !== undefined ? bubbles : true;
if (typeof document === "object" && document.createEvent) {
event = document.createEvent("Event");
event.initEvent(type, !!bubbles, !!cancelable);
} else if (typeof document === "object" && document.createEventObject) {
event = document.createEventObject();
event.type = type;
event.bubbles = !!bubbles;
event.cancelable = !!cancelable;
} else {
event = new EventObject(type, !!bubbles, !!cancelable);
}
return event;
};
soma.Event.prototype.isIE9 = function() {
if (typeof document !== "object") return false;
return document.body.style.scrollbar3dLightColor !== undefined && document.body.style.opacity !== undefined;
};
soma.Event.prototype.toString = function() {
return "[soma.Event]";
};
var EventObject = function(type, bubbles, cancelable) {
this.type = type;
this.bubbles = !!bubbles;
this.cancelable = !!cancelable;
this.defaultPrevented = false;
this.currentTarget = null;
this.target = null;
};
soma.EventDispatcher = function () {
this.listeners = [];
};
soma.EventDispatcher.prototype.addEventListener = function(type, listener, priority) {
if (!this.listeners || !type || !listener) return;
if (isNaN(priority)) priority = 0;
for (var i=0; i<this.listeners.length; i++) {
var eventObj = this.listeners[i];
if (eventObj.type === type && eventObj.listener === listener) {
return;
}
}
this.listeners.push({type: type, listener: listener, priority: priority, scope:this});
};
soma.EventDispatcher.prototype.removeEventListener = function(type, listener) {
if (!this.listeners || !type || !listener) return;
var i = this.listeners.length;
while(i-- > 0) {
var eventObj = this.listeners[i];
if (eventObj.type === type && eventObj.listener === listener) {
this.listeners.splice(i, 1);
}
}
};
soma.EventDispatcher.prototype.hasEventListener = function(type) {
if (!this.listeners || !type) return false;
var i = 0;
var l = this.listeners.length;
for (; i < l; ++i) {
var eventObj = this.listeners[i];
if (eventObj.type === type) {
return true;
}
}
return false;
};
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.");
var events = [];
var i;
for (i = 0; i < this.listeners.length; i++) {
var eventObj = this.listeners[i];
if (eventObj.type === event.type) {
events.push(eventObj);
}
}
events.sort(function(a, b) {
return b.priority - a.priority;
});
for (i = 0; i < events.length; i++) {
events[i].listener.apply((event.srcElement) ? event.srcElement : event.currentTarget, [event]);
}
return !event.isDefaultPrevented();
};
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.");
var event = new soma.Event(type, params, bubbles, cancelable);
this.dispatchEvent(event);
return event;
};
soma.EventDispatcher.prototype.dispose = function() {
this.listeners = null;
};
soma.EventDispatcher.prototype.toString = function() {
return "[soma.EventDispatcher]";
};
// register for AMD module
if (typeof define === 'function' && define.amd) {
define("soma-events", soma);
};
// export for node.js
if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
exports = module.exports = soma;
}
exports = soma;
};
})(this['soma'] = this['soma'] || {});
;(function (soma, infuse, undefined) {
'use strict';
soma.version = "2.0.0";
soma.applyProperties = function(target, extension, bindToExtension, list) {
if (Object.prototype.toString.apply(list) === '[object Array]') {
for (var i = 0, l = list.length; i < l; i++) {
if (target[list[i]] === undefined || target[list[i]] === null) {
if (bindToExtension && typeof extension[list[i]] === 'function') {
target[list[i]] = extension[list[i]].bind(extension);
}
else {
target[list[i]] = extension[list[i]];
}
}
}
}
else {
for (var prop in extension) {
if (bindToExtension && typeof extension[prop] === 'function') {
target[prop] = extension[prop].bind(extension);
}
else {
target[prop] = extension[prop];
}
}
}
};
soma.augment = function (target, extension, list) {
if (!extension.prototype || !target.prototype) {
return;
}
if (Object.prototype.toString.apply(list) === '[object Array]') {
for (var i = 0, l = list.length; i < l; i++) {
if (!target.prototype[list[i]]) {
target.prototype[list[i]] = extension.prototype[list[i]];
}
}
}
else {
for (var prop in extension.prototype) {
if (!target.prototype[prop]) {
target.prototype[prop] = extension.prototype[prop];
}
}
}
};
soma.inherit = function (parent, obj) {
var Subclass;
if (obj && obj.hasOwnProperty('constructor')) {
// use constructor if defined
Subclass = obj.constructor;
} else {
// call the super constructor
Subclass = function () {
return parent.apply(this, arguments);
};
}
// set the prototype chain to inherit from the parent without calling parent's constructor
var Chain = function(){};
Chain.prototype = parent.prototype;
Subclass.prototype = new Chain();
// add obj properties
if (obj) soma.applyProperties(Subclass.prototype, obj);
// point constructor to the Subclass
Subclass.prototype.constructor = Subclass;
// set super class reference
Subclass.parent = parent.prototype;
// add extend shortcut
Subclass.extend = function (obj) {
return soma.inherit(Subclass, obj);
};
return Subclass;
};
soma.extend = function (obj) {
return soma.inherit(function () {
}, obj);
};
// plugins
var plugins = [];
soma.plugins = soma.plugins || {};
soma.plugins.add = function(plugin) {
plugins.push(plugin);
};
soma.plugins.remove = function(plugin) {
for (var i = plugins.length-1, l = 0; i >= l; i--) {
if (plugin === plugins[i]) {
plugins.splice(i, 1);
}
}
};
// framework
soma.Application = soma.extend({
constructor: function() {
setup.bind(this)();
this.init();
this.start();
function setup() {
// injector
this.injector = new infuse.Injector(this.dispatcher);
// dispatcher
this.dispatcher = new soma.EventDispatcher();
// mapping
this.injector.mapValue('injector', this.injector);
this.injector.mapValue('instance', this);
this.injector.mapValue('dispatcher', this.dispatcher);
// mediator
this.injector.mapClass('mediators', Mediators, true);
this.mediators = this.injector.getValue('mediators');
// commands
this.injector.mapClass('commands', Commands, true);
this.commands = this.injector.getValue('commands');
// plugins
for (var i = 0, l = plugins.length; i < l; i++) {
this.createPlugin(plugins[i]);
}
}
},
createPlugin: function() {
if (arguments.length == 0 || !arguments[0]) {
throw new Error("Error creating a plugin, plugin class is missing.");
}
var params = infuse.getConstructorParams(arguments[0]);
var args = [arguments[0]];
for (var i=0; i<params.length; i++) {
if (this.injector.hasMapping(params[i]) || this.injector.hasInheritedMapping(params[i])) {
args.push(this.injector.getValue(params[i]));
}
}
for (var j=1; j<arguments.length; j++) {
args.push(arguments[j]);
}
return this.injector.createInstance.apply(this.injector, args);
},
init: function() {
},
start: function() {
},
dispose: function() {
// mapping
if (this.injector) {
this.injector.removeMapping('injector');
this.injector.removeMapping('dispatcher');
this.injector.removeMapping('mediators');
this.injector.removeMapping('commands');
this.injector.removeMapping('instance');
}
// variables
if (this.injector) {
this.injector.dispose();
}
if (this.dispatcher) {
this.dispatcher.dispose();
}
if (this.mediators) {
this.mediators.dispose();
}
if (this.commands) {
this.commands.dispose();
}
this.injector = undefined;
this.dispatcher = undefined;
this.mediators = undefined;
this.commands = undefined;
this.instance = undefined;
}
});
var Mediators = soma.extend({
constructor: function() {
this.injector = null;
this.dispatcher = null;
},
create: function(cl, target) {
if (!cl || typeof cl !== "function") {
throw new Error("Error creating a mediator, the first parameter must be a function.");
}
if (target === undefined || target === null) {
throw new Error("Error creating a mediator, the second parameter cannot be undefined or null.");
}
var targets = [];
var meds = [];
if (target.length > 0) {
targets = target;
}
else {
targets.push(target);
}
for (var i= 0, l=targets.length; i<l; i++) {
var injector = this.injector.createChild();
injector.mapValue("target", targets[i]);
//var mediator = injector.createInstance.apply(this.injector, params);
var mediator = injector.createInstance(cl);
// soma.applyProperties(mediator, this.dispatcher, true, ['dispatch', 'dispatchEvent', 'addEventListener', 'removeEventListener', 'hasEventListener']);
if (targets.length === 1) return mediator;
meds.push(mediator);
}
return meds;
},
dispose: function() {
this.injector = undefined;
this.dispatcher = undefined;
}
});
var Commands = soma.extend({
constructor: function() {
this.boundHandler = this.handler.bind(this);
this.dispatcher = null;
this.injector = null;
this.list = {};
},
has: function(commandName) {
return this.list[commandName] !== null && this.list[commandName] !== undefined;
},
get: function(commandName) {
if (this.has(commandName)) {
return this.list[commandName];
}
return undefined;
},
getAll: function() {
var copy = {};
for (var cmd in this.list) {
copy[cmd] = this.list[cmd];
}
return copy;
},
add: function(commandName, command) {
if (typeof commandName !== 'string') {
throw new Error("Error adding a command, the first parameter must be a string.");
}
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.");
}
if (this.has(commandName)) {
throw new Error("Error adding a command with the name: \"" + commandName + "\", already registered.");
}
this.list[ commandName ] = command;
this.addInterceptor(commandName);
},
remove: function(commandName) {
if (!this.has(commandName)) {
return;
}
this.list[commandName] = undefined;
delete this.list[commandName];
this.removeInterceptor(commandName);
},
addInterceptor: function(commandName) {
this.dispatcher.addEventListener(commandName, this.boundHandler, -Number.MAX_VALUE);
},
removeInterceptor: function(commandName) {
this.dispatcher.removeEventListener(commandName, this.boundHandler);
},
handler: function(event) {
if (event.isDefaultPrevented && !event.isDefaultPrevented()) {
this.executeCommand(event);
}
},
executeCommand: function(event) {
var commandName = event.type;
if (this.has(commandName)) {
var command = this.injector.createInstance(this.list[commandName]);
if (!command.hasOwnProperty('execute') && command['execute'] === 'function') {
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);
}
},
dispose: function() {
for (var cmd in this.list) {
this.remove(cmd);
}
this.boundHandler = undefined;
this.dispatcher = undefined;
this.injector = undefined;
this.list = undefined;
}
});
// event extend utils
soma.EventDispatcher.extend = function (obj) {
return soma.inherit(soma.EventDispatcher, obj);
};
soma.Event.extend = function (obj) {
return soma.inherit(soma.Event, obj);
};
infuse.Injector.extend = function(obj) {
return soma.inherit(infuse.Injector, obj);
};
// register for AMD module
if (typeof define === 'function' && define.amd) {
define("soma", soma);
}
// export for node.js
if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
exports = module.exports = soma;
}
exports = soma;
}
})(this['soma'] = this['soma'] || {}, this['infuse']);
\ No newline at end of file
{
"name": "todomvc-common",
"version": "0.1.7",
"gitHead": "42348a8146fe0be847b93cd98664813fbae62be9",
"_id": "todomvc-common@0.1.7",
"readme": "ERROR: No README.md file found!",
"description": "ERROR: No README.md file found!",
"repository": {
"type": "git",
"url": "git://github.com/tastejs/todomvc-common.git"
}
}
\ No newline at end of file
.data-cloak {
display: none;
}
\ No newline at end of file
<!doctype html> <!doctype html>
<html lang="en" data-framework="somajs"> <html lang="en" data-framework="somajs">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>soma.js • TodoMVC</title> <title>soma.js • TodoMVC</title>
<link rel="stylesheet" href="bower_components/todomvc-common/base.css"> <link rel="stylesheet" href="components/todomvc-common/base.css">
</head> <link rel="stylesheet" href="css/app.css">
<body> </head>
<body>
<section id="todoapp"> <section id="todoapp">
<!-- HEADER VIEW, template: /views/header.js (todo.HeaderView) -->
<header id="header"> <header id="header">
<h1>todos</h1> <h1>todos</h1>
<input id="new-todo" placeholder="What needs to be done?" autofocus> <input id="new-todo" placeholder="What needs to be done?" autofocus data-keypress="add()" data-blur="clear()">
</header> </header>
<section id="main">
<input id="toggle-all" type="checkbox"> <!-- MAIN VIEW, template: /views/main.js (todo.MainView) -->
<section id="main" data-show="{{isVisible}}">
<input id="toggle-all" type="checkbox" data-click="toggleAll()" data-checked="{{allCompleted}}">
<label for="toggle-all">Mark all as complete</label> <label for="toggle-all">Mark all as complete</label>
<ul id="todo-list"></ul> <ul id="todo-list" class="data-cloak">
<li data-repeat="todo in items" class="{{completedClass(todo.completed)}} {{todo.editing}}">
<div class="view">
<input class="toggle" type="checkbox" data-checked="{{todo.completed}}" data-click="toggle(todo)">
<label data-dblclick="edit(todo)">{{todo.title}}</label>
<button class="destroy" data-click="remove(todo)"></button>
</div>
<input class="edit" value="{{todo.title}}" data-keypress="update(todo)" data-blur="update(todo)">
</li>
</ul>
</section> </section>
<footer id="footer"></footer>
<!-- FOOTER VIEW, template: /views/footer.js (todo.FooterView) -->
<footer id="footer" class="data-cloak" data-show="{{footerVisible}}">
<span id="todo-count"><strong>{{active}}</strong> {{itemLabel}} left</span>
<ul id="filters">
<li>
<a class="{{highlightFilter('')}}" href="#/">All</a>
</li>
<li>
<a class="{{highlightFilter('active')}}" href="#/active">Active</a>
</li>
<li>
<a class="{{highlightFilter('completed')}}" href="#/completed">Completed</a>
</li>
</ul>
<button id="clear-completed" data-show="{{clearCompletedVisible()}}" data-click="clearCompleted()">Clear completed ({{completed}})</button>
</footer>
</section> </section>
<footer id="info"> <footer id="info">
<p>Double-click to edit a todo</p> <p>Double-click to edit a todo</p>
<p>Created by <a href="http://soundstep.com">Romuald Quantin</a></p> <p>Created by <a href="http://soundstep.com">Romuald Quantin</a> (<a href="http://somajs.github.io/somajs/">soma.js</a>)</p>
<p>With <a href="http://somajs.github.com/somajs">soma.js</a></p>
<p>Part of <a href="http://todomvc.com">TodoMVC</a></p> <p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
</footer> </footer>
<script id="todo-list-template" type="text/x-handlebars-template">
{{#this}} <!-- LIBRARIES -->
<li {{#if completed}}class="completed"{{/if}} data-id="{{id}}">
<div class="view"> <script src="components/todomvc-common/base.js"></script>
<input class="toggle" type="checkbox" {{#if completed}}checked{{/if}}> <script src="components/director/build/director.js"></script>
<label>{{title}}</label> <script src="components/soma.js/build/soma.js"></script>
<button class="destroy"></button> <script src="components/soma-template/build/soma-template.js"></script>
</div>
<input class="edit" value="{{title}}"> <!-- TODO APP -->
</li>
{{/this}} <script src="js/models/todos.js"></script>
</script> <script src="js/models/router.js"></script>
<script id="footer-template" type="text/x-handlebars-template"> <script src="js/views/header.js"></script>
<span id="todo-count"><strong>{{active}}</strong> {{itemLabel}} left</span> <script src="js/views/main.js"></script>
<button id="clear-completed">Clear completed ({{completed}})</button> <script src="js/views/footer.js"></script>
</script>
<script src="bower_components/todomvc-common/base.js"></script>
<script src="bower_components/handlebars.js/handlebars.js"></script>
<script src="bower_components/jquery/jquery.js"></script>
<script src="js/lib/soma-native_v1.0.2_min.js"></script>
<script src="js/todos/models/models.js"></script>
<script src="js/todos/views/views.js"></script>
<script src="js/todos/controllers/controllers.js"></script>
<script src="js/app.js"></script> <script src="js/app.js"></script>
</body>
</body>
</html> </html>
var todo = window.todo || {}; /*global soma:false */
(function (todo, soma) {
(function( window ) {
'use strict'; 'use strict';
todo.TodoApp = new soma.Application.extend({ todo.TodoApp = soma.Application.extend({
init: function () {
init: function() { // mapping rules so the model and router can be injected
this.injector.mapClass('model', todo.Model, true);
this.addModel( todo.TodoModel.NAME, new todo.TodoModel() ); this.injector.mapClass('router', todo.Router, true);
// create templates for DOM Elements (optional soma-template plugin)
this.addCommand( todo.TodoEvent.RENDER, todo.TodoCommand ); this.createTemplate(todo.HeaderView, document.getElementById('header'));
this.addCommand( todo.TodoEvent.CREATE, todo.TodoCommand ); this.createTemplate(todo.MainView, document.getElementById('main'));
this.addCommand( todo.TodoEvent.DELETE, todo.TodoCommand ); this.createTemplate(todo.FooterView, document.getElementById('footer'));
this.addCommand( todo.TodoEvent.TOGGLE, todo.TodoCommand );
this.addCommand( todo.TodoEvent.UPDATE, todo.TodoCommand );
this.addCommand( todo.TodoEvent.TOGGLE_ALL, todo.TodoCommand );
this.addCommand( todo.TodoEvent.CLEAR_COMPLETED, todo.TodoCommand );
this.addView( todo.TodoListView.NAME, new todo.TodoListView( $('#todo-list')[0] ) );
this.addView( todo.FooterView.NAME, new todo.FooterView( $('#footer')[0] ) );
this.addView( todo.TodoInputView.NAME, new todo.TodoInputView( $('#new-todo')[0] ) );
}, },
start: function () {
start: function() { // dispatch a custom event to render the templates
this.dispatchEvent( new todo.TodoEvent( todo.TodoEvent.RENDER ) ); this.dispatcher.dispatch('render');
} }
}); });
var app = new todo.TodoApp(); // create the application
new todo.TodoApp();
})( window ); })(window.todo = window.todo || {}, soma);
(function(){soma={};soma.version="1.0.2";soma.type="native";if(!Function.prototype.bind){Function.prototype.bind=function c(f){var g=this;if(typeof g!="function"){throw new Error("Error, you must bind a function.")}var d=Array.prototype.slice.call(arguments,1);var e=function(){if(this instanceof e){var j=function(){};j.prototype=g.prototype;var i=new j;var h=g.apply(i,d.concat(Array.prototype.slice.call(arguments)));if(Object(h)===h){return h}return i}else{return g.apply(f,d.concat(Array.prototype.slice.call(arguments)))}};return e}}soma.applyProperties=function(d,f){for(var e in f){d[e]=f[e]}};soma.inherit=function(g,f){var d;if(f&&f.hasOwnProperty("constructor")){d=f.constructor}else{d=function(){return g.apply(this,arguments)}}soma.applyProperties(d.prototype,g.prototype);var e=function(){};e.prototype=g.prototype;d.prototype=new e();if(f){soma.applyProperties(d.prototype,f,g.prototype)}d.prototype.constructor=d;d.parent=g.prototype;d.extend=function(h){return soma.inherit(d,h)};return d};soma.extend=function(d){return soma.inherit(function(){},d)};var b=soma.extend({createPlugin:function(){return this.instance.createPlugin.apply(this.instance,arguments)},dispatchEvent:function(){this.instance.dispatchEvent.apply(this.instance,arguments)},addEventListener:function(){this.instance.addEventListener.apply(this.instance,arguments)},removeEventListener:function(){this.instance.removeEventListener.apply(this.instance,arguments)},hasEventListener:function(){return this.instance.hasEventListener.apply(this.instance,arguments)},hasCommand:function(d){return this.instance.hasCommand(d)},getCommand:function(d){return this.instance.getCommand(d)},getCommands:function(){return this.instance.getCommands()},addCommand:function(e,d){this.instance.controller.addCommand(e,d)},removeCommand:function(d){this.instance.controller.removeCommand(d)},hasWire:function(d){return this.instance.hasWire(d)},getWire:function(d){return this.instance.getWire(d)},addWire:function(e,d){return this.instance.addWire(e,d)},removeWire:function(d){this.instance.removeWire(d)},hasModel:function(d){return this.instance.hasModel(d)},getModel:function(d){return this.instance.getModel(d)},addModel:function(d,e){return this.instance.addModel(d,e)},removeModel:function(d){this.instance.removeModel(d)},getSequencer:function(d){return !!this.instance.controller?this.instance.controller.getSequencer(d):null},stopSequencerWithEvent:function(d){return !!this.instance.controller?this.instance.controller.stopSequencerWithEvent(d):null},stopSequencer:function(d){if(this.instance.controller){return this.instance.controller.stopSequencer(d)}},stopAllSequencers:function(){if(this.instance.controller){this.instance.controller.stopAllSequencers()}},isPartOfASequence:function(d){return !!this.instance.controller?this.instance.controller.isPartOfASequence(d):false},getLastSequencer:function(){return !!this.instance.controller?this.instance.controller.getLastSequencer():null},getRunningSequencers:function(){return !!this.instance.controller?this.instance.controller.getRunningSequencers():null},hasView:function(d){return this.instance.hasView(d)},getView:function(d){return this.instance.getView(d)},addView:function(e,d){return this.instance.addView(e,d)},removeView:function(d){this.instance.removeView(d)}});soma.AutoBind={blackList:["initialize","parent","constructor","$constructor","addEventListener","removeEventListener"],autobind:function(){if(this.wasAutoBound){return}var g=this;var e=g.AutoBindPattern;var f="([lL]istener|[hH]andler|[cB]allback)$";if(!e){e=f}else{e=f+"|"+e}for(var d in g){if(typeof g[d]=="function"){if(this._autobindIsBlacklisted(d)){continue}if(!d.match(e)){continue}g[d]=g[d].bind(g)}}},_autobindIsBlacklisted:function(d){var f=this.blackList;for(var e=0;e<f.length;e++){if(f[e]==d){return true}}return false}};soma.Command=b.extend({instance:null,registerInstance:function(d){this.instance=d},execute:function(d){},toString:function(){return"[soma.Command]"}});var a=soma.extend({event:null,sequenceId:null,constructor:function(d){this.event=d}});soma.SequenceCommand=soma.Command.extend({commands:null,currentCommand:null,id:null,constructor:function(d){if(d==null){throw new Error("SequenceCommand Children expect an unique id as constructor arg")}this.commands=[];this.id=d;soma.Command.call(this)},registerInstance:function(d){this.instance=d;this.initializeSubCommands()},initializeSubCommands:function(){throw new Error("Subclasses of SequenceCommand must implement initializeSubCommands()")},addSubCommand:function(d){var e=new a(d);this.commands.push(e);this.instance.controller.registerSequencedCommand(this,e)},execute:function(d){if(this.commands==null||this.commands.length===0){return}this.currentCommand=this.commands.shift();if(this.hasCommand(this.currentCommand.event.type)){this.dispatchEvent(this.currentCommand.event)}},executeNextCommand:function(){if(this.commands==null){return}this.instance.controller.unregisterSequencedCommand(this,this.currentCommand.event.type);if(this.commands.length>0){this.execute(this.commands[0].event)}else{this.commands=null;this.currentCommand=null}},getLength:function(){if(this.commands==null){return -1}return this.commands.length},stop:function(){this.commands=null;this.commands=null;this.currentCommand=null;return this.instance.controller.unregisterSequencer(this)},getCurrentCommand:function(){return this.currentCommand},getCommands:function(){return this.commands},toString:function(){return"[soma.SequenceCommand]"}});soma.ParallelCommand=soma.Command.extend({commands:null,constructor:function(){this.commands=[]},registerInstance:function(d){this.instance=d;this.initializeSubCommands()},initializeSubCommands:function(){throw new Error("Subclasses of ParallelCommand must implement initializeSubCommands()")},addSubCommand:function(d){this.commands.push(d)},execute:function(){while(this.commands.length>0){var d=this.commands.shift();if(this.hasCommand(d.type)){this.dispatchEvent(d)}}this.commands=null},getLength:function(){return this.commands!=null?this.commands.length:-1},getCommands:function(){return this.commands},toString:function(){return"[soma.ParallelCommand]"}});soma.Wire=b.extend({name:null,instance:null,constructor:function(d){this.name=d},registerInstance:function(d){this.instance=d},init:function(){},dispose:function(){},getName:function(){return this.name},setName:function(d){this.name=d},toString:function(){return"[soma.Wire]"}});soma.applyProperties(soma.Wire.prototype,soma.AutoBind);soma.IDisposable=soma.extend({dispose:function(){}});soma.SomaController=soma.extend({instance:null,constructor:function(d){this.boundInstance=this.instanceHandler.bind(this);this.boundDomtree=this.domTreeHandler.bind(this);this.commands={};this.sequencers={};this.sequencersInfo={};this.lastEvent=null;this.lastSequencer=null;this.instance=d},addInterceptor:function(d){if(!soma){throw new Error("soma package has been overwritten by local variable")}if(this.instance.body.addEventListener){this.instance.body.addEventListener(d,this.boundDomtree,true)}this.instance.addEventListener(d,this.boundInstance,-Number.MAX_VALUE)},removeInterceptor:function(d){if(this.instance.body.removeEventListener){this.instance.body.removeEventListener(d,this.boundDomtree,true)}this.instance.removeEventListener(d,this.boundInstance)},executeCommand:function(f){var d=f.type;if(this.hasCommand(d)){var g=new this.commands[d]();g.registerInstance(this.instance);g.execute(f)}},registerSequencedCommand:function(d,f){if(!(f instanceof a)){throw new Error("capsulate sequence commands in SequenceCommandProxy objects!")}var e=this.sequencersInfo;if(e[d.id]==null||this.sequencers[d.id]==null){this.lastSequencer=d;e[d.id]=[];this.sequencers[d.id]=d}f.sequenceId=d.id;e[d.id].push(f)},unregisterSequencedCommand:function(e,g){if(typeof g!="string"){throw new Error("Controller::unregisterSequencedCommand() expects commandName to be of type String, given:"+g)}var h=this.sequencersInfo;if(h[e.id]!=null&&h[e.id]!=undefined){var d=h[e.id].length;for(var f=0;f<d;f++){if(h[e.id][f].event.type==g){h[e.id][f]=null;h[e.id].splice(f,1);if(h[e.id].length==0){h[e.id]=null;delete h[e.id]}break}}}},unregisterSequencer:function(e){var g=this.sequencers;if(g[e.id]!=null&&g[e.id]!=undefined){g[e.id]=null;delete g[e.id];g=this.sequencersInfo;if(g[e.id]!=null){var d=g[e.id].length;for(var f=0;f<d;f++){g[e.id][f]=null}g[e.id]=null;delete g[e.id];return true}}return false},hasCommand:function(d){return this.commands[d]!=null},getCommand:function(d){if(this.hasCommand(d)){return this.commands[d]}return null},getCommands:function(){var e=[];var d=this.commands;for(var f in d){e.push(f)}return e},addCommand:function(d,e){if(this.hasCommand(d)){throw new Error("Error in "+this+' Command "'+d+'" already registered.')}this.commands[d]=e;this.addInterceptor(d)},removeCommand:function(d){if(!this.hasCommand(d)){return}this.commands[d]=null;delete this.commands[d];this.removeInterceptor(d)},getSequencer:function(j){var g=this.sequencersInfo;for(var h in g){var d=g[h].length;for(var f=0;f<d;f++){if(g[h][f]&&g[h][f].event.type===j.type){var e=this.sequencers[g[h][f].sequenceId];return !!e?e:null}}}return null},stopSequencerWithEvent:function(j){var g=this.sequencersInfo;for(var h in g){var d=g[h].length;for(var f=0;f<d;f++){if(g[h][f].event.type===j.type){try{this.sequencers[g[h][f].sequenceId].stop()}catch(k){return false}return true}}}return false},stopSequencer:function(d){if(d==null){return false}d.stop();return true},stopAllSequencers:function(){var e=this.sequencers;var g=this.sequencersInfo;for(var f in e){if(g[f]==null){continue}var d=g[f].length;g[f]=null;delete g[f];e[f].stop();e[f]=null;delete e[f]}},isPartOfASequence:function(d){return(this.getSequencer(d)!=null)},getRunningSequencers:function(){var d=[];var e=this.sequencers;for(var f in e){d.push(e[f])}return d},getLastSequencer:function(){return this.lastSequencer},dispose:function(){for(var e in this.commands){this.removeCommand(e)}for(var d in this.sequencers){this.sequencers[d]=null;delete this.sequencers[d]}this.commands=null;this.sequencers=null;this.lastEvent=null;this.lastSequencer=null},domTreeHandler:function(f){if(f.bubbles&&this.hasCommand(f.type)&&!f.isCloned){if(f.stopPropagation){f.stopPropagation()}else{f.cancelBubble=true}var d=f.clone();this.lastEvent=d;this.instance.dispatchEvent(d);if(!d.isDefaultPrevented()){this.executeCommand(f)}this.lastEvent=null}},instanceHandler:function(d){if(d.bubbles&&this.hasCommand(d.type)){if(this.lastEvent!=d){if(!d.isDefaultPrevented()){this.executeCommand(d)}}}this.lastEvent=null}});soma.SomaViews=soma.extend({views:null,autoBound:false,instance:null,constructor:function(d){this.views={};this.instance=d},hasView:function(d){return this.views[d]!=null},addView:function(e,d){if(this.hasView(e)){throw new Error('View "'+e+'" already exists')}if(document.attachEvent){d.instance=this.instance}if(!this.autoBound){soma.applyProperties(soma.View.prototype,soma.AutoBind);this.autoBound=true}if(d.shouldAutobind){d.autobind()}this.views[e]=d;if(d.init!=null){d.init()}return d},getView:function(d){if(this.hasView(d)){return this.views[d]}return null},getViews:function(){var e={};for(var d in this.views){e[d]=this.views[d]}return e},removeView:function(d){if(!this.hasView(d)){return}if(this.views[d]["dispose"]!=null){this.views[d].dispose()}this.views[d]=null;delete this.views[d]},dispose:function(){for(var d in this.views){this.removeView(d)}this.views=null;this.instance=null}});soma.EventDispatcher=soma.extend({listeners:null,constructor:function(){this.listeners=[]},addEventListener:function(e,f,d){if(!this.listeners||!e||!f){throw new Error("Error in EventDispatcher (addEventListener), one of the parameters is null or undefined.")}if(isNaN(d)){d=0}this.listeners.push({type:e,listener:f,priority:d,scope:this})},removeEventListener:function(f,h){if(!this.listeners){return false}if(!f||!h){throw new Error("Error in EventDispatcher (removeEventListener), one of the parameters is null or undefined.")}var e=0;var d=this.listeners.length;for(e=d-1;e>-1;e--){var g=this.listeners[e];if(g.type==f&&g.listener==h){this.listeners.splice(e,1)}}},hasEventListener:function(f){if(!this.listeners){return false}if(!f){throw new Error("Error in EventDispatcher (hasEventListener), one of the parameters is null or undefined.")}var e=0;var d=this.listeners.length;for(;e<d;++e){var g=this.listeners[e];if(g.type==f){return true}}return false},dispatchEvent:function(f){if(!this.listeners||!f){throw new Error("Error in EventDispatcher (dispatchEvent), one of the parameters is null or undefined.")}var e=[];var d;for(d=0;d<this.listeners.length;d++){var g=this.listeners[d];if(g.type==f.type){e.push(g)}}e.sort(function(i,h){return h.priority-i.priority});for(d=0;d<e.length;d++){e[d].listener.apply((f.srcElement)?f.srcElement:f.currentTarget,[f])}},getListeners:function(){return this.listeners.slice()},toString:function(){return"[soma.EventDispatcher]"},dispose:function(){this.listeners=null}});soma.Application=soma.EventDispatcher.extend({body:null,models:null,controller:null,wires:null,views:null,constructor:function(){soma.EventDispatcher.call(this);this.body=document.body;if(!this.body){throw new Error("soma requires body of type Element")}this.controller=new soma.SomaController(this);this.models=new soma.SomaModels(this);this.wires=new soma.SomaWires(this);this.views=new soma.SomaViews(this);this.init();this.registerModels();this.registerViews();this.registerCommands();this.registerWires();this.start()},createPlugin:function(){if(arguments.length==0||!arguments[0]){throw new Error("Error creating a plugin, plugin class is missing.")}var f=arguments[0];arguments[0]=this;var d=[null];for(var e=0;e<arguments.length;e++){d.push(arguments[e])}return new (Function.prototype.bind.apply(f,d))},hasCommand:function(d){return(!this.controller)?false:this.controller.hasCommand(d)},getCommand:function(d){return(!this.controller)?null:this.controller.getCommand(d)},getCommands:function(){return(!this.controller)?null:this.controller.getCommands()},addCommand:function(d,e){this.controller.addCommand(d,e)},removeCommand:function(d){this.controller.removeCommand(d)},hasWire:function(d){return(!this.wires)?false:this.wires.hasWire(d)},getWire:function(d){return(!this.wires)?null:this.wires.getWire(d)},getWires:function(){return(!this.wires)?null:this.wires.getWires()},addWire:function(e,d){return this.wires.addWire(e,d)},removeWire:function(d){this.wires.removeWire(d)},hasModel:function(d){return(!this.models)?false:this.models.hasModel(d)},getModel:function(d){return(!this.models)?null:this.models.getModel(d)},getModels:function(){return(!this.models)?null:this.models.getModels()},addModel:function(d,e){return this.models.addModel(d,e)},removeModel:function(d){this.models.removeModel(d)},hasView:function(d){return(!this.views)?false:this.views.hasView(d)},getView:function(d){return(!this.views)?null:this.views.getView(d)},getViews:function(){return(!this.views)?null:this.views.getViews()},addView:function(e,d){return this.views.addView(e,d)},removeView:function(d){this.views.removeView(d)},getSequencer:function(d){return !!this.controller?this.controller.getSequencer(d):null},isPartOfASequence:function(d){return(this.getSequencer(d)!=null)},stopSequencerWithEvent:function(d){return !!this.controller?this.controller.stopSequencerWithEvent(d):false},stopSequencer:function(d){return !!this.controller?this.controller.stopSequencer(d):false},stopAllSequencers:function(){if(this.controller){this.controller.stopAllSequencers()}},getRunningSequencers:function(){return !!this.controller?this.controller.getRunningSequencers():null},getLastSequencer:function(){return !!this.controller?this.controller.getLastSequencer():null},dispose:function(){if(this.models){this.models.dispose();this.models=null}if(this.views){this.views.dispose();this.views=null}if(this.controller){this.controller.dispose();this.controller=null}if(this.wires){this.wires.dispose();this.wires=null}this.body=null;soma.EventDispatcher.prototype.dispose.call(this)},toString:function(){return"[soma.Application]"},init:function(){},registerModels:function(){},registerViews:function(){},registerCommands:function(){},registerWires:function(){},start:function(){}});soma.SomaModels=soma.extend({models:null,instance:null,constructor:function(d){this.models={};this.instance=d},hasModel:function(d){return this.models[d]!=null},getModel:function(d){if(this.hasModel(d)){return this.models[d]}return null},getModels:function(){var f={};var e=this.models;for(var d in e){f[d]=e[d]}return f},addModel:function(d,e){if(this.hasModel(d)){throw new Error('Model "'+d+'" already exists')}this.models[d]=e;if(!e.dispatcher){e.dispatcher=this.instance}e.init();return e},removeModel:function(d){if(!this.hasModel(d)){return}this.models[d].dispose();this.models[d]=null;delete this.models[d]},dispose:function(){for(var d in this.models){this.removeModel(d)}this.models=null;this.instance=null}});soma.Model=soma.extend({name:null,data:null,dispatcher:null,constructor:function(d,f,e){this.data=f;this.dispatcher=e;if(d!=null){this.name=d}},init:function(){},dispose:function(){},dispatchEvent:function(){if(this.dispatcher){this.dispatcher.dispatchEvent.apply(this.dispatcher,arguments)}},addEventListener:function(){if(this.dispatcher){this.dispatcher.addEventListener.apply(this.dispatcher,arguments)}},removeEventListener:function(){if(this.dispatcher){this.dispatcher.addEventListener.apply(this.dispatcher,arguments)}},getName:function(){return this.name},setName:function(d){this.name=d},toString:function(){return"[soma.Model]"}});soma.View=soma.extend({instance:null,domElement:null,constructor:function(f){var e;if(f!=undefined){if(f.nodeType){e=f}else{throw new Error("domElement has to be a DOM-ELement")}}else{e=document.body}this.domElement=e},dispatchEvent:function(d){if(this.domElement.dispatchEvent){this.domElement.dispatchEvent(d)}else{if(this.instance){this.instance.dispatchEvent(d)}}},addEventListener:function(){if(this.domElement.addEventListener){this.domElement.addEventListener.apply(this.domElement,arguments)}else{if(this.instance){this.instance.addEventListener.apply(this.instance,arguments)}}},removeEventListener:function(){if(this.domElement.addEventListener){this.domElement.removeEventListener.apply(this.domElement,arguments)}else{if(this.instance){this.instance.removeEventListener.apply(this.instance,arguments)}}},init:function(){},dispose:function(){},toString:function(){return"[soma.View]"}});soma.SomaWires=soma.extend({wires:null,instance:null,constructor:function(d){this.wires={};this.instance=d},hasWire:function(d){return this.wires[d]!=null},addWire:function(e,d){if(this.hasWire(e)){throw new Error('Wire "'+e+'" already exists')}if(d.shouldAutobind){d.autobind()}this.wires[e]=d;d.registerInstance(this.instance);d.init();return d},getWire:function(d){if(this.hasWire(d)){return this.wires[d]}return null},getWires:function(){var e={};for(var d in this.wires){e[d]=this.wires[d]}return e},removeWire:function(d){if(!this.hasWire(d)){return}this.wires[d].dispose();this.wires[d]=null;delete this.wires[d]},dispose:function(){for(var d in this.wires){this.removeWire(d)}this.wires=null;this.instance=null}});soma.Mediator=soma.Wire.extend({viewComponent:null,constructor:function(d){soma.Wire.call(this,d);this.viewComponent=viewComponent},dispose:function(){this.viewComponent=null},toString:function(){return"[soma.Mediator]"}});soma.Event=soma.extend({constructor:function(g,i,f,d){var h=soma.Event.createGenericEvent(g,f,d);if(i!=null&&i!=undefined){h.params=i}h.isCloned=false;h.clone=this.clone.bind(h);h.isIE9=this.isIE9;h.isDefaultPrevented=this.isDefaultPrevented;if(this.isIE9()||!h.preventDefault||(h.getDefaultPrevented==undefined&&h.defaultPrevented==undefined)){h.preventDefault=this.preventDefault.bind(h)}if(this.isIE9()){h.IE9PreventDefault=false}return h},clone:function(){var d=soma.Event.createGenericEvent(this.type,this.bubbles,this.cancelable);d.params=this.params;d.isCloned=true;d.clone=this.clone;d.isDefaultPrevented=this.isDefaultPrevented;d.isIE9=this.isIE9;if(this.isIE9()){d.IE9PreventDefault=this.IE9PreventDefault}return d},preventDefault:function(){if(!this.cancelable){return false}this.defaultPrevented=true;if(this.isIE9()){this.IE9PreventDefault=true}this.returnValue=false;return this},isDefaultPrevented:function(){if(!this.cancelable){return false}if(this.isIE9()){return this.IE9PreventDefault}if(this.defaultPrevented!=undefined){return this.defaultPrevented}else{if(this.getDefaultPrevented!=undefined){return this.getDefaultPrevented()}}return false},isIE9:function(){return document.body.style.scrollbar3dLightColor!=undefined&&document.body.style.opacity!=undefined},toString:function(){return"[soma.Event]"}});soma.Event.createGenericEvent=function(g,f,d){var h;f=f!==undefined?f:true;if(document.createEvent){h=document.createEvent("Event");h.initEvent(g,f,!!d)}else{h=document.createEventObject();h.type=g;h.bubbles=!!f;h.cancelable=!!d}return h};soma.IResponder=soma.extend({fault:function(d){},result:function(d){}})})();
/*global Router:false */
(function (todo, Router) {
'use strict';
todo.Router = function (dispatcher) {
// create the router (director.js)
var router = new Router().init();
// dispatch a custom event to render the template on a route change
router.on(/.*/, function () {
dispatcher.dispatch('render');
});
return {
getRoute: function () {
return router.getRoute()[0];
}
};
};
})(window.todo = window.todo || {}, Router);
(function (todo) {
'use strict';
todo.Model = function () {
var storeKey = 'todos-somajs';
return {
get: function () {
// get the data from the local storage
return JSON.parse(localStorage.getItem(storeKey) || '[]');
},
set: function (items) {
// set the data to the local storage
localStorage.setItem(storeKey, JSON.stringify(items));
},
getActive: function () {
// returns items that are not completed
return this.get().filter(function (item) {
return !item.completed;
}).length;
}
};
};
})(window.todo = window.todo || {});
var todo = window.todo || {};
(function( window ) {
'use strict';
todo.TodoCommand = soma.Command.extend({
execute: function( event ) {
var model = this.getModel( todo.TodoModel.NAME );
switch( event.type ) {
case todo.TodoEvent.RENDER:
this.getView( todo.TodoListView.NAME ).render( model.data, model.getActiveLength() );
this.getView( todo.FooterView.NAME ).render( model.dataFooter );
break;
case todo.TodoEvent.CREATE:
model.addItem( event.params.todoTitle );
break;
case todo.TodoEvent.DELETE:
model.removeItem( event.params.todoId );
break;
case todo.TodoEvent.TOGGLE:
model.toggleItem( event.params.todoId );
break;
case todo.TodoEvent.TOGGLE_ALL:
model.toggleAll( event.params.toggleAll );
break;
case todo.TodoEvent.UPDATE:
model.updateItem( event.params.todoId, event.params.todoTitle );
break;
case todo.TodoEvent.CLEAR_COMPLETED:
model.clearCompleted();
break;
}
}
});
todo.TodoEvent = soma.Event.extend({
constructor: function( type, todoTitle, todoId, toggleAll ) {
return soma.Event.call( this, type, {
todoTitle: todoTitle,
todoId: todoId,
toggleAll: toggleAll
});
}
});
todo.TodoEvent.RENDER = 'TodoEvent.RENDER';
todo.TodoEvent.CREATE = 'TodoEvent.CREATE';
todo.TodoEvent.DELETE = 'TodoEvent.DELETE';
todo.TodoEvent.UPDATE = 'TodoEvent.UPDATE';
todo.TodoEvent.TOGGLE = 'TodoEvent.TOGGLE';
todo.TodoEvent.TOGGLE_ALL = 'TodoEvent.TOGGLE_ALL';
todo.TodoEvent.CLEAR_COMPLETED = 'TodoEvent.CLEAR_COMPLETED';
})( window );
var todo = window.todo || {};
(function( window ) {
'use strict';
todo.TodoModel = new soma.Model.extend({
dataFooter: null,
init: function() {
this.storeKey = 'todos-somajs';
this.data = JSON.parse( this.getStore() ) || [];
this.updateDataFooter();
},
updateDataFooter: function() {
var active = this.getActiveLength();
this.dataFooter = {
active: active,
itemLabel: active === 1 ? 'item' : 'items',
completed: this.data.length - active,
length: this.data.length
};
},
addItem: function( title ) {
this.data.push({
id: this.uuid(),
title: title,
completed: false
});
this.update();
},
removeItem: function( id ) {
this.data.splice( this.getIndexById( id ), 1 );
this.update();
},
toggleItem: function( id ) {
var item = this.data[ this.getIndexById( id ) ];
item.completed = !item.completed;
this.update();
},
updateItem: function( id, title ) {
this.data[ this.getIndexById( id ) ].title = title;
this.update();
},
toggleAll: function( toggleValue ) {
var i;
for ( i = 0; i < this.data.length; i++ ) {
this.data[i].completed = toggleValue;
}
this.update();
},
clearCompleted: function() {
var i = this.data.length;
while ( i-- ) {
if ( this.data[ i ].completed ) {
this.data.splice( i, 1 );
}
}
this.update();
},
getIndexById: function( id ) {
var i;
for ( i = 0; i < this.data.length; i++ ) {
if ( this.data[ i ].id === id ) {
return i;
}
}
return -1;
},
getActiveLength: function() {
var i,
count = 0;
for ( i = 0; i < this.data.length; i++ ) {
if ( !this.data[ i ].completed ) {
count++;
}
}
return count;
},
update: function() {
this.updateDataFooter();
this.setStore( this.data );
this.dispatchEvent( new todo.TodoEvent( todo.TodoEvent.RENDER ) );
},
getStore: function() {
return localStorage.getItem( this.storeKey );
},
setStore: function() {
localStorage.setItem( this.storeKey, JSON.stringify( this.data ) );
},
// https://gist.github.com/1308368
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;}
});
todo.TodoModel.NAME = 'TodoModel';
})( window );
var todo = window.todo || {};
(function( window ) {
'use strict';
var ENTER_KEY = 13;
todo.TodoListView = soma.View.extend({
template: null,
init: function() {
this.template = Handlebars.compile( $( '#' + this.domElement.id + '-template' ).html() );
$( this.domElement ).on( 'click', '.destroy', this.destroy.bind( this ) );
$( this.domElement ).on( 'click', '.toggle', this.toggle.bind( this ) );
$( this.domElement ).on( 'dblclick', 'label', this.edit );
$( this.domElement ).on( 'blur', '.edit', this.update.bind( this ) );
$( this.domElement ).on( 'keypress', '.edit', this.blurInput );
$('#toggle-all').click( this.toggleAll );
},
render: function( data, activeCount ) {
$(this.domElement).html( this.template( data ) );
$('#toggle-all').prop( 'checked', !activeCount );
$('#main').toggle( !!data.length );
},
destroy: function( event ) {
var id = $(event.target).closest('li').attr('data-id');
this.dispatchEvent( new todo.TodoEvent( todo.TodoEvent.DELETE, null, id ) );
},
toggle: function( event ) {
var id = $(event.target).closest('li').attr('data-id');
this.dispatchEvent( new todo.TodoEvent( todo.TodoEvent.TOGGLE, null, id ) );
},
toggleAll: function() {
this.dispatchEvent( new todo.TodoEvent( todo.TodoEvent.TOGGLE_ALL, null, null, $( this ).prop('checked') ) );
},
edit: function( event ) {
$( this ).closest('li').addClass('editing').find('.edit').focus();
},
update: function( event ) {
var li = $( event.target ).closest('li').removeClass('editing'),
id = li.data('id'),
val = li.find('.edit').val().trim();
if ( val ) {
this.dispatchEvent( new todo.TodoEvent( todo.TodoEvent.UPDATE, val, id ) );
}
else {
this.dispatchEvent( new todo.TodoEvent( todo.TodoEvent.DELETE, null, id ) );
}
},
blurInput: function( event ) {
if ( event.which === ENTER_KEY ) {
event.target.blur();
}
}
});
todo.TodoListView.NAME = 'TodoListView';
todo.TodoInputView = soma.View.extend({
init: function() {
$( this.domElement ).keypress( this.keyPressHandler.bind( this ) );
$( this.domElement ).blur( this.blur );
},
keyPressHandler: function( event ) {
if ( event.which === ENTER_KEY ) {
this.createItem();
}
},
createItem: function() {
var value = this.domElement.value.trim();
if ( value ) {
this.dispatchEvent( new todo.TodoEvent( todo.TodoEvent.CREATE, value ) );
}
this.domElement.value = '';
},
blur: function( event ) {
if ( !this.value.trim() ) {
this.value = '';
}
}
});
todo.TodoInputView.NAME = 'TodoInputView';
todo.FooterView = soma.View.extend({
template: null,
init: function() {
this.template = Handlebars.compile( $( '#' + this.domElement.id + '-template' ).html() );
$( this.domElement ).on( 'click', '#clear-completed', this.clearCompleted.bind( this ) );
},
render: function( data ) {
$( this.domElement ).html( this.template( data ) );
$( this.domElement ).toggle( !!data.length );
$('#clear-completed').toggle( !!data.completed );
},
clearCompleted: function( event ) {
this.dispatchEvent( new todo.TodoEvent( todo.TodoEvent.CLEAR_COMPLETED ) );
}
});
todo.FooterView.NAME = 'FooterView';
})( window );
(function (todo) {
'use strict';
todo.FooterView = function (scope, template, model, router, dispatcher) {
// get data from the injected model
var items = model.get();
// template function: returns a css class for the current filter (all/active/completed)
scope.highlightFilter = function (filter) {
var route = router.getRoute();
return route === filter ? 'selected' : '';
};
// template function: returns the number of completed items
scope.clearCompleted = function () {
items = items.filter(function (item) {
return !item.completed;
});
update();
};
// save the changes to the model and dispatch a custom event to render the templates
function update() {
model.set(items);
dispatcher.dispatch('render');
}
// listen to a custom event to render the footer view
dispatcher.addEventListener('render', function () {
items = model.get();
scope.active = model.getActive();
scope.completed = items.length - scope.active;
scope.itemLabel = scope.active === 1 ? 'item' : 'items';
scope.footerVisible = items.length > 0 ? true : false;
scope.clearCompletedVisible = scope.completed > 0 ? true : false;
template.render();
});
};
})(window.todo = window.todo || {});
(function (todo) {
'use strict';
var ENTER_KEY = 13;
todo.HeaderView = function (scope, template, model, dispatcher) {
// get data from the injected model
var items = model.get();
// template function: add a new item on an enter key press
scope.add = function (event) {
var value = event.currentTarget.value.trim();
if (event.which === ENTER_KEY && value !== '') {
items.push({
title: value,
completed: false
});
event.currentTarget.value = '';
update();
}
};
// template function: remove text from the input (used on blur event)
scope.clear = function (event) {
event.currentTarget.value = '';
};
// save the changes to the model and dispatch a custom event to render the templates
function update() {
model.set(items);
dispatcher.dispatch('render');
}
// listen to a custom event to render the header view
dispatcher.addEventListener('render', function () {
items = model.get();
template.render();
});
};
})(window.todo = window.todo || {});
(function (todo) {
'use strict';
var ENTER_KEY = 13;
todo.MainView = function (scope, template, model, router, dispatcher) {
// get data from the injected model
var items = model.get();
// template function: returns an array of items to display
// can be different depending on the filter selected
scope.items = function () {
var filter = router.getRoute();
if (filter === '') {
return items;
}
return items.filter(function (item) {
return filter === 'active' ? !item.completed : item.completed;
});
};
// template function: set all items to either completed or not completed
scope.toggleAll = function (event) {
items.forEach(function (item) {
item.completed = event.currentTarget.checked;
});
update();
};
// template function: set 1 item to either completed or not completed
scope.toggle = function (event, item) {
item.completed = !item.completed;
update();
};
// template function: returns a css class depending if the item is completed or not completed
scope.completedClass = function (completed) {
return completed ? 'completed' : '';
};
// template function: removes an item
scope.remove = function (event, item) {
if (item) {
items.splice(items.indexOf(item), 1);
update();
}
};
// template function: edit an item (used on a double click event)
scope.edit = function (event, item) {
item.editing = 'editing';
template.render();
};
// template function: during edit mode, changes the value of an item after an enter key press
scope.update = function (event, item) {
var value = event.currentTarget.value.trim();
if (event.which === ENTER_KEY || event.type === 'blur') {
if (value) {
item.title = value;
}
else {
items.splice(items.indexOf(item), 1);
}
item.editing = '';
event.currentTarget.value = value;
update();
}
};
// save the changes to the model and dispatch a custom event to render the templates
function update() {
model.set(items);
dispatcher.dispatch('render');
}
// listen to a custom event to render the main view
dispatcher.addEventListener('render', function () {
items = model.get();
scope.active = model.getActive();
scope.isVisible = scope.items().length > 0 ? true : false;
scope.allCompleted = items.length > 0 && scope.active === 0 ? true : false;
template.render();
});
};
})(window.todo = window.todo || {});
...@@ -21,7 +21,6 @@ Get help from other soma.js users: ...@@ -21,7 +21,6 @@ Get help from other soma.js users:
* [Mailing list on Google Groups](https://groups.google.com/forum/#!forum/somajs) * [Mailing list on Google Groups](https://groups.google.com/forum/#!forum/somajs)
* [soma.js on Twitter](http://twitter.com/soundstep) * [soma.js on Twitter](http://twitter.com/soundstep)
_If you have other helpful links to share, or find any of the links above no longer work, please [let us know](https://github.com/tastejs/todomvc/issues)._ _If you have other helpful links to share, or find any of the links above no longer work, please [let us know](https://github.com/tastejs/todomvc/issues)._
......
{
"name": "todomvc-somajs",
"version": "0.0.0",
"dependencies": {
"todomvc-common": "~0.1.6",
"director": "~1.2.0",
"requirejs": "~2.1.5",
"soma.js": "~2.0.0",
"soma-template": "~0.1.8"
}
}
//
// Generated on Sun Dec 16 2012 22:47:05 GMT-0500 (EST) by Nodejitsu, Inc (Using Codesurgeon).
// Version 1.1.9
//
(function (exports) {
/*
* browser.js: Browser specific functionality for director.
*
* (C) 2011, Nodejitsu Inc.
* MIT LICENSE
*
*/
if (!Array.prototype.filter) {
Array.prototype.filter = function(filter, that) {
var other = [], v;
for (var i = 0, n = this.length; i < n; i++) {
if (i in this && filter.call(that, v = this[i], i, this)) {
other.push(v);
}
}
return other;
};
}
if (!Array.isArray){
Array.isArray = function(obj) {
return Object.prototype.toString.call(obj) === '[object Array]';
};
}
var dloc = document.location;
function dlocHashEmpty() {
// Non-IE browsers return '' when the address bar shows '#'; Director's logic
// assumes both mean empty.
return dloc.hash === '' || dloc.hash === '#';
}
var listener = {
mode: 'modern',
hash: dloc.hash,
history: false,
check: function () {
var h = dloc.hash;
if (h != this.hash) {
this.hash = h;
this.onHashChanged();
}
},
fire: function () {
if (this.mode === 'modern') {
this.history === true ? window.onpopstate() : window.onhashchange();
}
else {
this.onHashChanged();
}
},
init: function (fn, history) {
var self = this;
this.history = history;
if (!Router.listeners) {
Router.listeners = [];
}
function onchange(onChangeEvent) {
for (var i = 0, l = Router.listeners.length; i < l; i++) {
Router.listeners[i](onChangeEvent);
}
}
//note IE8 is being counted as 'modern' because it has the hashchange event
if ('onhashchange' in window && (document.documentMode === undefined
|| document.documentMode > 7)) {
// At least for now HTML5 history is available for 'modern' browsers only
if (this.history === true) {
// There is an old bug in Chrome that causes onpopstate to fire even
// upon initial page load. Since the handler is run manually in init(),
// this would cause Chrome to run it twise. Currently the only
// workaround seems to be to set the handler after the initial page load
// http://code.google.com/p/chromium/issues/detail?id=63040
setTimeout(function() {
window.onpopstate = onchange;
}, 500);
}
else {
window.onhashchange = onchange;
}
this.mode = 'modern';
}
else {
//
// IE support, based on a concept by Erik Arvidson ...
//
var frame = document.createElement('iframe');
frame.id = 'state-frame';
frame.style.display = 'none';
document.body.appendChild(frame);
this.writeFrame('');
if ('onpropertychange' in document && 'attachEvent' in document) {
document.attachEvent('onpropertychange', function () {
if (event.propertyName === 'location') {
self.check();
}
});
}
window.setInterval(function () { self.check(); }, 50);
this.onHashChanged = onchange;
this.mode = 'legacy';
}
Router.listeners.push(fn);
return this.mode;
},
destroy: function (fn) {
if (!Router || !Router.listeners) {
return;
}
var listeners = Router.listeners;
for (var i = listeners.length - 1; i >= 0; i--) {
if (listeners[i] === fn) {
listeners.splice(i, 1);
}
}
},
setHash: function (s) {
// Mozilla always adds an entry to the history
if (this.mode === 'legacy') {
this.writeFrame(s);
}
if (this.history === true) {
window.history.pushState({}, document.title, s);
// Fire an onpopstate event manually since pushing does not obviously
// trigger the pop event.
this.fire();
} else {
dloc.hash = (s[0] === '/') ? s : '/' + s;
}
return this;
},
writeFrame: function (s) {
// IE support...
var f = document.getElementById('state-frame');
var d = f.contentDocument || f.contentWindow.document;
d.open();
d.write("<script>_hash = '" + s + "'; onload = parent.listener.syncHash;<script>");
d.close();
},
syncHash: function () {
// IE support...
var s = this._hash;
if (s != dloc.hash) {
dloc.hash = s;
}
return this;
},
onHashChanged: function () {}
};
var Router = exports.Router = function (routes) {
if (!(this instanceof Router)) return new Router(routes);
this.params = {};
this.routes = {};
this.methods = ['on', 'once', 'after', 'before'];
this.scope = [];
this._methods = {};
this._insert = this.insert;
this.insert = this.insertEx;
this.historySupport = (window.history != null ? window.history.pushState : null) != null
this.configure();
this.mount(routes || {});
};
Router.prototype.init = function (r) {
var self = this;
this.handler = function(onChangeEvent) {
var newURL = onChangeEvent && onChangeEvent.newURL || window.location.hash;
var url = self.history === true ? self.getPath() : newURL.replace(/.*#/, '');
self.dispatch('on', url);
};
listener.init(this.handler, this.history);
if (this.history === false) {
if (dlocHashEmpty() && r) {
dloc.hash = r;
} else if (!dlocHashEmpty()) {
self.dispatch('on', dloc.hash.replace(/^#/, ''));
}
}
else {
var routeTo = dlocHashEmpty() && r ? r : !dlocHashEmpty() ? dloc.hash.replace(/^#/, '') : null;
if (routeTo) {
window.history.replaceState({}, document.title, routeTo);
}
// Router has been initialized, but due to the chrome bug it will not
// yet actually route HTML5 history state changes. Thus, decide if should route.
if (routeTo || this.run_in_init === true) {
this.handler();
}
}
return this;
};
Router.prototype.explode = function () {
var v = this.history === true ? this.getPath() : dloc.hash;
if (v.charAt(1) === '/') { v=v.slice(1) }
return v.slice(1, v.length).split("/");
};
Router.prototype.setRoute = function (i, v, val) {
var url = this.explode();
if (typeof i === 'number' && typeof v === 'string') {
url[i] = v;
}
else if (typeof val === 'string') {
url.splice(i, v, s);
}
else {
url = [i];
}
listener.setHash(url.join('/'));
return url;
};
//
// ### function insertEx(method, path, route, parent)
// #### @method {string} Method to insert the specific `route`.
// #### @path {Array} Parsed path to insert the `route` at.
// #### @route {Array|function} Route handlers to insert.
// #### @parent {Object} **Optional** Parent "routes" to insert into.
// insert a callback that will only occur once per the matched route.
//
Router.prototype.insertEx = function(method, path, route, parent) {
if (method === "once") {
method = "on";
route = function(route) {
var once = false;
return function() {
if (once) return;
once = true;
return route.apply(this, arguments);
};
}(route);
}
return this._insert(method, path, route, parent);
};
Router.prototype.getRoute = function (v) {
var ret = v;
if (typeof v === "number") {
ret = this.explode()[v];
}
else if (typeof v === "string"){
var h = this.explode();
ret = h.indexOf(v);
}
else {
ret = this.explode();
}
return ret;
};
Router.prototype.destroy = function () {
listener.destroy(this.handler);
return this;
};
Router.prototype.getPath = function () {
var path = window.location.pathname;
if (path.substr(0, 1) !== '/') {
path = '/' + path;
}
return path;
};
function _every(arr, iterator) {
for (var i = 0; i < arr.length; i += 1) {
if (iterator(arr[i], i, arr) === false) {
return;
}
}
}
function _flatten(arr) {
var flat = [];
for (var i = 0, n = arr.length; i < n; i++) {
flat = flat.concat(arr[i]);
}
return flat;
}
function _asyncEverySeries(arr, iterator, callback) {
if (!arr.length) {
return callback();
}
var completed = 0;
(function iterate() {
iterator(arr[completed], function(err) {
if (err || err === false) {
callback(err);
callback = function() {};
} else {
completed += 1;
if (completed === arr.length) {
callback();
} else {
iterate();
}
}
});
})();
}
function paramifyString(str, params, mod) {
mod = str;
for (var param in params) {
if (params.hasOwnProperty(param)) {
mod = params[param](str);
if (mod !== str) {
break;
}
}
}
return mod === str ? "([._a-zA-Z0-9-]+)" : mod;
}
function regifyString(str, params) {
var matches, last = 0, out = "";
while (matches = str.substr(last).match(/[^\w\d\- %@&]*\*[^\w\d\- %@&]*/)) {
last = matches.index + matches[0].length;
matches[0] = matches[0].replace(/^\*/, "([_.()!\\ %@&a-zA-Z0-9-]+)");
out += str.substr(0, matches.index) + matches[0];
}
str = out += str.substr(last);
var captures = str.match(/:([^\/]+)/ig), length;
if (captures) {
length = captures.length;
for (var i = 0; i < length; i++) {
str = str.replace(captures[i], paramifyString(captures[i], params));
}
}
return str;
}
function terminator(routes, delimiter, start, stop) {
var last = 0, left = 0, right = 0, start = (start || "(").toString(), stop = (stop || ")").toString(), i;
for (i = 0; i < routes.length; i++) {
var chunk = routes[i];
if (chunk.indexOf(start, last) > chunk.indexOf(stop, last) || ~chunk.indexOf(start, last) && !~chunk.indexOf(stop, last) || !~chunk.indexOf(start, last) && ~chunk.indexOf(stop, last)) {
left = chunk.indexOf(start, last);
right = chunk.indexOf(stop, last);
if (~left && !~right || !~left && ~right) {
var tmp = routes.slice(0, (i || 1) + 1).join(delimiter);
routes = [ tmp ].concat(routes.slice((i || 1) + 1));
}
last = (right > left ? right : left) + 1;
i = 0;
} else {
last = 0;
}
}
return routes;
}
Router.prototype.configure = function(options) {
options = options || {};
for (var i = 0; i < this.methods.length; i++) {
this._methods[this.methods[i]] = true;
}
this.recurse = options.recurse || this.recurse || false;
this.async = options.async || false;
this.delimiter = options.delimiter || "/";
this.strict = typeof options.strict === "undefined" ? true : options.strict;
this.notfound = options.notfound;
this.resource = options.resource;
this.history = options.html5history && this.historySupport || false;
this.run_in_init = this.history === true && options.run_handler_in_init !== false;
this.every = {
after: options.after || null,
before: options.before || null,
on: options.on || null
};
return this;
};
Router.prototype.param = function(token, matcher) {
if (token[0] !== ":") {
token = ":" + token;
}
var compiled = new RegExp(token, "g");
this.params[token] = function(str) {
return str.replace(compiled, matcher.source || matcher);
};
};
Router.prototype.on = Router.prototype.route = function(method, path, route) {
var self = this;
if (!route && typeof path == "function") {
route = path;
path = method;
method = "on";
}
if (Array.isArray(path)) {
return path.forEach(function(p) {
self.on(method, p, route);
});
}
if (path.source) {
path = path.source.replace(/\\\//ig, "/");
}
if (Array.isArray(method)) {
return method.forEach(function(m) {
self.on(m.toLowerCase(), path, route);
});
}
path = path.split(new RegExp(this.delimiter));
path = terminator(path, this.delimiter);
this.insert(method, this.scope.concat(path), route);
};
Router.prototype.dispatch = function(method, path, callback) {
var self = this, fns = this.traverse(method, path, this.routes, ""), invoked = this._invoked, after;
this._invoked = true;
if (!fns || fns.length === 0) {
this.last = [];
if (typeof this.notfound === "function") {
this.invoke([ this.notfound ], {
method: method,
path: path
}, callback);
}
return false;
}
if (this.recurse === "forward") {
fns = fns.reverse();
}
function updateAndInvoke() {
self.last = fns.after;
self.invoke(self.runlist(fns), self, callback);
}
after = this.every && this.every.after ? [ this.every.after ].concat(this.last) : [ this.last ];
if (after && after.length > 0 && invoked) {
if (this.async) {
this.invoke(after, this, updateAndInvoke);
} else {
this.invoke(after, this);
updateAndInvoke();
}
return true;
}
updateAndInvoke();
return true;
};
Router.prototype.invoke = function(fns, thisArg, callback) {
var self = this;
if (this.async) {
_asyncEverySeries(fns, function apply(fn, next) {
if (Array.isArray(fn)) {
return _asyncEverySeries(fn, apply, next);
} else if (typeof fn == "function") {
fn.apply(thisArg, fns.captures.concat(next));
}
}, function() {
if (callback) {
callback.apply(thisArg, arguments);
}
});
} else {
_every(fns, function apply(fn) {
if (Array.isArray(fn)) {
return _every(fn, apply);
} else if (typeof fn === "function") {
return fn.apply(thisArg, fns.captures || []);
} else if (typeof fn === "string" && self.resource) {
self.resource[fn].apply(thisArg, fns.captures || []);
}
});
}
};
Router.prototype.traverse = function(method, path, routes, regexp, filter) {
var fns = [], current, exact, match, next, that;
function filterRoutes(routes) {
if (!filter) {
return routes;
}
function deepCopy(source) {
var result = [];
for (var i = 0; i < source.length; i++) {
result[i] = Array.isArray(source[i]) ? deepCopy(source[i]) : source[i];
}
return result;
}
function applyFilter(fns) {
for (var i = fns.length - 1; i >= 0; i--) {
if (Array.isArray(fns[i])) {
applyFilter(fns[i]);
if (fns[i].length === 0) {
fns.splice(i, 1);
}
} else {
if (!filter(fns[i])) {
fns.splice(i, 1);
}
}
}
}
var newRoutes = deepCopy(routes);
newRoutes.matched = routes.matched;
newRoutes.captures = routes.captures;
newRoutes.after = routes.after.filter(filter);
applyFilter(newRoutes);
return newRoutes;
}
if (path === this.delimiter && routes[method]) {
next = [ [ routes.before, routes[method] ].filter(Boolean) ];
next.after = [ routes.after ].filter(Boolean);
next.matched = true;
next.captures = [];
return filterRoutes(next);
}
for (var r in routes) {
if (routes.hasOwnProperty(r) && (!this._methods[r] || this._methods[r] && typeof routes[r] === "object" && !Array.isArray(routes[r]))) {
current = exact = regexp + this.delimiter + r;
if (!this.strict) {
exact += "[" + this.delimiter + "]?";
}
match = path.match(new RegExp("^" + exact));
if (!match) {
continue;
}
if (match[0] && match[0] == path && routes[r][method]) {
next = [ [ routes[r].before, routes[r][method] ].filter(Boolean) ];
next.after = [ routes[r].after ].filter(Boolean);
next.matched = true;
next.captures = match.slice(1);
if (this.recurse && routes === this.routes) {
next.push([ routes.before, routes.on ].filter(Boolean));
next.after = next.after.concat([ routes.after ].filter(Boolean));
}
return filterRoutes(next);
}
next = this.traverse(method, path, routes[r], current);
if (next.matched) {
if (next.length > 0) {
fns = fns.concat(next);
}
if (this.recurse) {
fns.push([ routes[r].before, routes[r].on ].filter(Boolean));
next.after = next.after.concat([ routes[r].after ].filter(Boolean));
if (routes === this.routes) {
fns.push([ routes["before"], routes["on"] ].filter(Boolean));
next.after = next.after.concat([ routes["after"] ].filter(Boolean));
}
}
fns.matched = true;
fns.captures = next.captures;
fns.after = next.after;
return filterRoutes(fns);
}
}
}
return false;
};
Router.prototype.insert = function(method, path, route, parent) {
var methodType, parentType, isArray, nested, part;
path = path.filter(function(p) {
return p && p.length > 0;
});
parent = parent || this.routes;
part = path.shift();
if (/\:|\*/.test(part) && !/\\d|\\w/.test(part)) {
part = regifyString(part, this.params);
}
if (path.length > 0) {
parent[part] = parent[part] || {};
return this.insert(method, path, route, parent[part]);
}
if (!part && !path.length && parent === this.routes) {
methodType = typeof parent[method];
switch (methodType) {
case "function":
parent[method] = [ parent[method], route ];
return;
case "object":
parent[method].push(route);
return;
case "undefined":
parent[method] = route;
return;
}
return;
}
parentType = typeof parent[part];
isArray = Array.isArray(parent[part]);
if (parent[part] && !isArray && parentType == "object") {
methodType = typeof parent[part][method];
switch (methodType) {
case "function":
parent[part][method] = [ parent[part][method], route ];
return;
case "object":
parent[part][method].push(route);
return;
case "undefined":
parent[part][method] = route;
return;
}
} else if (parentType == "undefined") {
nested = {};
nested[method] = route;
parent[part] = nested;
return;
}
throw new Error("Invalid route context: " + parentType);
};
Router.prototype.extend = function(methods) {
var self = this, len = methods.length, i;
function extend(method) {
self._methods[method] = true;
self[method] = function() {
var extra = arguments.length === 1 ? [ method, "" ] : [ method ];
self.on.apply(self, extra.concat(Array.prototype.slice.call(arguments)));
};
}
for (i = 0; i < len; i++) {
extend(methods[i]);
}
};
Router.prototype.runlist = function(fns) {
var runlist = this.every && this.every.before ? [ this.every.before ].concat(_flatten(fns)) : _flatten(fns);
if (this.every && this.every.on) {
runlist.push(this.every.on);
}
runlist.captures = fns.captures;
runlist.source = fns.source;
return runlist;
};
Router.prototype.mount = function(routes, path) {
if (!routes || typeof routes !== "object" || Array.isArray(routes)) {
return;
}
var self = this;
path = path || [];
if (!Array.isArray(path)) {
path = path.split(self.delimiter);
}
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;
if (isRoute) {
rename = rename.slice((rename.match(new RegExp(self.delimiter)) || [ "" ])[0].length);
parts.shift();
}
if (isRoute && routeType === "object" && !Array.isArray(routes[route])) {
local = local.concat(parts);
self.mount(routes[route], local);
return;
}
if (isRoute) {
local = local.concat(rename.split(self.delimiter));
local = terminator(local, self.delimiter);
}
self.insert(event, local, routes[route]);
}
for (var route in routes) {
if (routes.hasOwnProperty(route)) {
insertOrMount(route, path.slice(0));
}
}
};
}(typeof exports === "object" ? exports : window));
\ No newline at end of file
/** vim: et:ts=4:sw=4:sts=4
* @license RequireJS 2.1.5 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved.
* Available via the MIT or new BSD license.
* see: http://github.com/jrburke/requirejs for details
*/
//Not using strict: uneven strict support in browsers, #392, and causes
//problems with requirejs.exec()/transpiler plugins that may not be strict.
/*jslint regexp: true, nomen: true, sloppy: true */
/*global window, navigator, document, importScripts, setTimeout, opera */
var requirejs, require, define;
(function (global) {
var req, s, head, baseElement, dataMain, src,
interactiveScript, currentlyAddingScript, mainScript, subPath,
version = '2.1.5',
commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg,
cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,
jsSuffixRegExp = /\.js$/,
currDirRegExp = /^\.\//,
op = Object.prototype,
ostring = op.toString,
hasOwn = op.hasOwnProperty,
ap = Array.prototype,
apsp = ap.splice,
isBrowser = !!(typeof window !== 'undefined' && navigator && document),
isWebWorker = !isBrowser && typeof importScripts !== 'undefined',
//PS3 indicates loaded and complete, but need to wait for complete
//specifically. Sequence is 'loading', 'loaded', execution,
// then 'complete'. The UA check is unfortunate, but not sure how
//to feature test w/o causing perf issues.
readyRegExp = isBrowser && navigator.platform === 'PLAYSTATION 3' ?
/^complete$/ : /^(complete|loaded)$/,
defContextName = '_',
//Oh the tragedy, detecting opera. See the usage of isOpera for reason.
isOpera = typeof opera !== 'undefined' && opera.toString() === '[object Opera]',
contexts = {},
cfg = {},
globalDefQueue = [],
useInteractive = false;
function isFunction(it) {
return ostring.call(it) === '[object Function]';
}
function isArray(it) {
return ostring.call(it) === '[object Array]';
}
/**
* Helper function for iterating over an array. If the func returns
* a true value, it will break out of the loop.
*/
function each(ary, func) {
if (ary) {
var i;
for (i = 0; i < ary.length; i += 1) {
if (ary[i] && func(ary[i], i, ary)) {
break;
}
}
}
}
/**
* Helper function for iterating over an array backwards. If the func
* returns a true value, it will break out of the loop.
*/
function eachReverse(ary, func) {
if (ary) {
var i;
for (i = ary.length - 1; i > -1; i -= 1) {
if (ary[i] && func(ary[i], i, ary)) {
break;
}
}
}
}
function hasProp(obj, prop) {
return hasOwn.call(obj, prop);
}
function getOwn(obj, prop) {
return hasProp(obj, prop) && obj[prop];
}
/**
* Cycles over properties in an object and calls a function for each
* property value. If the function returns a truthy value, then the
* iteration is stopped.
*/
function eachProp(obj, func) {
var prop;
for (prop in obj) {
if (hasProp(obj, prop)) {
if (func(obj[prop], prop)) {
break;
}
}
}
}
/**
* Simple function to mix in properties from source into target,
* but only if target does not already have a property of the same name.
*/
function mixin(target, source, force, deepStringMixin) {
if (source) {
eachProp(source, function (value, prop) {
if (force || !hasProp(target, prop)) {
if (deepStringMixin && typeof value !== 'string') {
if (!target[prop]) {
target[prop] = {};
}
mixin(target[prop], value, force, deepStringMixin);
} else {
target[prop] = value;
}
}
});
}
return target;
}
//Similar to Function.prototype.bind, but the 'this' object is specified
//first, since it is easier to read/figure out what 'this' will be.
function bind(obj, fn) {
return function () {
return fn.apply(obj, arguments);
};
}
function scripts() {
return document.getElementsByTagName('script');
}
//Allow getting a global that expressed in
//dot notation, like 'a.b.c'.
function getGlobal(value) {
if (!value) {
return value;
}
var g = global;
each(value.split('.'), function (part) {
g = g[part];
});
return g;
}
/**
* Constructs an error with a pointer to an URL with more information.
* @param {String} id the error ID that maps to an ID on a web page.
* @param {String} message human readable error.
* @param {Error} [err] the original error, if there is one.
*
* @returns {Error}
*/
function makeError(id, msg, err, requireModules) {
var e = new Error(msg + '\nhttp://requirejs.org/docs/errors.html#' + id);
e.requireType = id;
e.requireModules = requireModules;
if (err) {
e.originalError = err;
}
return e;
}
if (typeof define !== 'undefined') {
//If a define is already in play via another AMD loader,
//do not overwrite.
return;
}
if (typeof requirejs !== 'undefined') {
if (isFunction(requirejs)) {
//Do not overwrite and existing requirejs instance.
return;
}
cfg = requirejs;
requirejs = undefined;
}
//Allow for a require config object
if (typeof require !== 'undefined' && !isFunction(require)) {
//assume it is a config object.
cfg = require;
require = undefined;
}
function newContext(contextName) {
var inCheckLoaded, Module, context, handlers,
checkLoadedTimeoutId,
config = {
//Defaults. Do not set a default for map
//config to speed up normalize(), which
//will run faster if there is no default.
waitSeconds: 7,
baseUrl: './',
paths: {},
pkgs: {},
shim: {},
config: {}
},
registry = {},
//registry of just enabled modules, to speed
//cycle breaking code when lots of modules
//are registered, but not activated.
enabledRegistry = {},
undefEvents = {},
defQueue = [],
defined = {},
urlFetched = {},
requireCounter = 1,
unnormalizedCounter = 1;
/**
* Trims the . and .. from an array of path segments.
* It will keep a leading path segment if a .. will become
* the first path segment, to help with module name lookups,
* which act like paths, but can be remapped. But the end result,
* all paths that use this function should look normalized.
* NOTE: this method MODIFIES the input array.
* @param {Array} ary the array of path segments.
*/
function trimDots(ary) {
var i, part;
for (i = 0; ary[i]; i += 1) {
part = ary[i];
if (part === '.') {
ary.splice(i, 1);
i -= 1;
} else if (part === '..') {
if (i === 1 && (ary[2] === '..' || ary[0] === '..')) {
//End of the line. Keep at least one non-dot
//path segment at the front so it can be mapped
//correctly to disk. Otherwise, there is likely
//no path mapping for a path starting with '..'.
//This can still fail, but catches the most reasonable
//uses of ..
break;
} else if (i > 0) {
ary.splice(i - 1, 2);
i -= 2;
}
}
}
}
/**
* Given a relative module name, like ./something, normalize it to
* a real name that can be mapped to a path.
* @param {String} name the relative name
* @param {String} baseName a real name that the name arg is relative
* to.
* @param {Boolean} applyMap apply the map config to the value. Should
* only be done if this normalization is for a dependency ID.
* @returns {String} normalized name
*/
function normalize(name, baseName, applyMap) {
var pkgName, pkgConfig, mapValue, nameParts, i, j, nameSegment,
foundMap, foundI, foundStarMap, starI,
baseParts = baseName && baseName.split('/'),
normalizedBaseParts = baseParts,
map = config.map,
starMap = map && map['*'];
//Adjust any relative paths.
if (name && name.charAt(0) === '.') {
//If have a base name, try to normalize against it,
//otherwise, assume it is a top-level require that will
//be relative to baseUrl in the end.
if (baseName) {
if (getOwn(config.pkgs, baseName)) {
//If the baseName is a package name, then just treat it as one
//name to concat the name with.
normalizedBaseParts = baseParts = [baseName];
} else {
//Convert baseName to array, and lop off the last part,
//so that . matches that 'directory' and not name of the baseName's
//module. For instance, baseName of 'one/two/three', maps to
//'one/two/three.js', but we want the directory, 'one/two' for
//this normalization.
normalizedBaseParts = baseParts.slice(0, baseParts.length - 1);
}
name = normalizedBaseParts.concat(name.split('/'));
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('/');
if (pkgConfig && name === pkgName + '/' + pkgConfig.main) {
name = pkgName;
}
} else if (name.indexOf('./') === 0) {
// No baseName, so this is ID is resolved relative
// to baseUrl, pull off the leading dot.
name = name.substring(2);
}
}
//Apply map config if available.
if (applyMap && map && (baseParts || starMap)) {
nameParts = name.split('/');
for (i = nameParts.length; i > 0; i -= 1) {
nameSegment = nameParts.slice(0, i).join('/');
if (baseParts) {
//Find the longest baseName segment match in the config.
//So, do joins on the biggest to smallest lengths of baseParts.
for (j = baseParts.length; j > 0; j -= 1) {
mapValue = getOwn(map, baseParts.slice(0, j).join('/'));
//baseName segment has config, find if it has one for
//this name.
if (mapValue) {
mapValue = getOwn(mapValue, nameSegment);
if (mapValue) {
//Match, update name to the new value.
foundMap = mapValue;
foundI = i;
break;
}
}
}
}
if (foundMap) {
break;
}
//Check for a star map match, but just hold on to it,
//if there is a shorter segment match later in a matching
//config, then favor over this star map.
if (!foundStarMap && starMap && getOwn(starMap, nameSegment)) {
foundStarMap = getOwn(starMap, nameSegment);
starI = i;
}
}
if (!foundMap && foundStarMap) {
foundMap = foundStarMap;
foundI = starI;
}
if (foundMap) {
nameParts.splice(0, foundI, foundMap);
name = nameParts.join('/');
}
}
return name;
}
function removeScript(name) {
if (isBrowser) {
each(scripts(), function (scriptNode) {
if (scriptNode.getAttribute('data-requiremodule') === name &&
scriptNode.getAttribute('data-requirecontext') === context.contextName) {
scriptNode.parentNode.removeChild(scriptNode);
return true;
}
});
}
}
function hasPathFallback(id) {
var pathConfig = getOwn(config.paths, id);
if (pathConfig && isArray(pathConfig) && pathConfig.length > 1) {
removeScript(id);
//Pop off the first array value, since it failed, and
//retry
pathConfig.shift();
context.require.undef(id);
context.require([id]);
return true;
}
}
//Turns a plugin!resource to [plugin, resource]
//with the plugin being undefined if the name
//did not have a plugin prefix.
function splitPrefix(name) {
var prefix,
index = name ? name.indexOf('!') : -1;
if (index > -1) {
prefix = name.substring(0, index);
name = name.substring(index + 1, name.length);
}
return [prefix, name];
}
/**
* Creates a module mapping that includes plugin prefix, module
* name, and path. If parentModuleMap is provided it will
* also normalize the name via require.normalize()
*
* @param {String} name the module name
* @param {String} [parentModuleMap] parent module map
* for the module name, used to resolve relative names.
* @param {Boolean} isNormalized: is the ID already normalized.
* This is true if this call is done for a define() module ID.
* @param {Boolean} applyMap: apply the map config to the ID.
* Should only be true if this map is for a dependency.
*
* @returns {Object}
*/
function makeModuleMap(name, parentModuleMap, isNormalized, applyMap) {
var url, pluginModule, suffix, nameParts,
prefix = null,
parentName = parentModuleMap ? parentModuleMap.name : null,
originalName = name,
isDefine = true,
normalizedName = '';
//If no name, then it means it is a require call, generate an
//internal name.
if (!name) {
isDefine = false;
name = '_@r' + (requireCounter += 1);
}
nameParts = splitPrefix(name);
prefix = nameParts[0];
name = nameParts[1];
if (prefix) {
prefix = normalize(prefix, parentName, applyMap);
pluginModule = getOwn(defined, prefix);
}
//Account for relative paths if there is a base name.
if (name) {
if (prefix) {
if (pluginModule && pluginModule.normalize) {
//Plugin is loaded, use its normalize method.
normalizedName = pluginModule.normalize(name, function (name) {
return normalize(name, parentName, applyMap);
});
} else {
normalizedName = normalize(name, parentName, applyMap);
}
} else {
//A regular module.
normalizedName = normalize(name, parentName, applyMap);
//Normalized name may be a plugin ID due to map config
//application in normalize. The map config values must
//already be normalized, so do not need to redo that part.
nameParts = splitPrefix(normalizedName);
prefix = nameParts[0];
normalizedName = nameParts[1];
isNormalized = true;
url = context.nameToUrl(normalizedName);
}
}
//If the id is a plugin id that cannot be determined if it needs
//normalization, stamp it with a unique ID so two matching relative
//ids that may conflict can be separate.
suffix = prefix && !pluginModule && !isNormalized ?
'_unnormalized' + (unnormalizedCounter += 1) :
'';
return {
prefix: prefix,
name: normalizedName,
parentMap: parentModuleMap,
unnormalized: !!suffix,
url: url,
originalName: originalName,
isDefine: isDefine,
id: (prefix ?
prefix + '!' + normalizedName :
normalizedName) + suffix
};
}
function getModule(depMap) {
var id = depMap.id,
mod = getOwn(registry, id);
if (!mod) {
mod = registry[id] = new context.Module(depMap);
}
return mod;
}
function on(depMap, name, fn) {
var id = depMap.id,
mod = getOwn(registry, id);
if (hasProp(defined, id) &&
(!mod || mod.defineEmitComplete)) {
if (name === 'defined') {
fn(defined[id]);
}
} else {
getModule(depMap).on(name, fn);
}
}
function onError(err, errback) {
var ids = err.requireModules,
notified = false;
if (errback) {
errback(err);
} else {
each(ids, function (id) {
var mod = getOwn(registry, id);
if (mod) {
//Set error on module, so it skips timeout checks.
mod.error = err;
if (mod.events.error) {
notified = true;
mod.emit('error', err);
}
}
});
if (!notified) {
req.onError(err);
}
}
}
/**
* Internal method to transfer globalQueue items to this context's
* defQueue.
*/
function takeGlobalQueue() {
//Push all the globalDefQueue items into the context's defQueue
if (globalDefQueue.length) {
//Array splice in the values since the context code has a
//local var ref to defQueue, so cannot just reassign the one
//on context.
apsp.apply(defQueue,
[defQueue.length - 1, 0].concat(globalDefQueue));
globalDefQueue = [];
}
}
handlers = {
'require': function (mod) {
if (mod.require) {
return mod.require;
} else {
return (mod.require = context.makeRequire(mod.map));
}
},
'exports': function (mod) {
mod.usingExports = true;
if (mod.map.isDefine) {
if (mod.exports) {
return mod.exports;
} else {
return (mod.exports = defined[mod.map.id] = {});
}
}
},
'module': function (mod) {
if (mod.module) {
return mod.module;
} else {
return (mod.module = {
id: mod.map.id,
uri: mod.map.url,
config: function () {
return (config.config && getOwn(config.config, mod.map.id)) || {};
},
exports: defined[mod.map.id]
});
}
}
};
function cleanRegistry(id) {
//Clean up machinery used for waiting modules.
delete registry[id];
delete enabledRegistry[id];
}
function breakCycle(mod, traced, processed) {
var id = mod.map.id;
if (mod.error) {
mod.emit('error', mod.error);
} else {
traced[id] = true;
each(mod.depMaps, function (depMap, i) {
var depId = depMap.id,
dep = getOwn(registry, depId);
//Only force things that have not completed
//being defined, so still in the registry,
//and only if it has not been matched up
//in the module already.
if (dep && !mod.depMatched[i] && !processed[depId]) {
if (getOwn(traced, depId)) {
mod.defineDep(i, defined[depId]);
mod.check(); //pass false?
} else {
breakCycle(dep, traced, processed);
}
}
});
processed[id] = true;
}
}
function checkLoaded() {
var map, modId, err, usingPathFallback,
waitInterval = config.waitSeconds * 1000,
//It is possible to disable the wait interval by using waitSeconds of 0.
expired = waitInterval && (context.startTime + waitInterval) < new Date().getTime(),
noLoads = [],
reqCalls = [],
stillLoading = false,
needCycleCheck = true;
//Do not bother if this call was a result of a cycle break.
if (inCheckLoaded) {
return;
}
inCheckLoaded = true;
//Figure out the state of all the modules.
eachProp(enabledRegistry, function (mod) {
map = mod.map;
modId = map.id;
//Skip things that are not enabled or in error state.
if (!mod.enabled) {
return;
}
if (!map.isDefine) {
reqCalls.push(mod);
}
if (!mod.error) {
//If the module should be executed, and it has not
//been inited and time is up, remember it.
if (!mod.inited && expired) {
if (hasPathFallback(modId)) {
usingPathFallback = true;
stillLoading = true;
} else {
noLoads.push(modId);
removeScript(modId);
}
} else if (!mod.inited && mod.fetched && map.isDefine) {
stillLoading = true;
if (!map.prefix) {
//No reason to keep looking for unfinished
//loading. If the only stillLoading is a
//plugin resource though, keep going,
//because it may be that a plugin resource
//is waiting on a non-plugin cycle.
return (needCycleCheck = false);
}
}
}
});
if (expired && noLoads.length) {
//If wait time expired, throw error of unloaded modules.
err = makeError('timeout', 'Load timeout for modules: ' + noLoads, null, noLoads);
err.contextName = context.contextName;
return onError(err);
}
//Not expired, check for a cycle.
if (needCycleCheck) {
each(reqCalls, function (mod) {
breakCycle(mod, {}, {});
});
}
//If still waiting on loads, and the waiting load is something
//other than a plugin resource, or there are still outstanding
//scripts, then just try back later.
if ((!expired || usingPathFallback) && stillLoading) {
//Something is still waiting to load. Wait for it, but only
//if a timeout is not already in effect.
if ((isBrowser || isWebWorker) && !checkLoadedTimeoutId) {
checkLoadedTimeoutId = setTimeout(function () {
checkLoadedTimeoutId = 0;
checkLoaded();
}, 50);
}
}
inCheckLoaded = false;
}
Module = function (map) {
this.events = getOwn(undefEvents, map.id) || {};
this.map = map;
this.shim = getOwn(config.shim, map.id);
this.depExports = [];
this.depMaps = [];
this.depMatched = [];
this.pluginMaps = {};
this.depCount = 0;
/* this.exports this.factory
this.depMaps = [],
this.enabled, this.fetched
*/
};
Module.prototype = {
init: function (depMaps, factory, errback, options) {
options = options || {};
//Do not do more inits if already done. Can happen if there
//are multiple define calls for the same module. That is not
//a normal, common case, but it is also not unexpected.
if (this.inited) {
return;
}
this.factory = factory;
if (errback) {
//Register for errors on this module.
this.on('error', errback);
} else if (this.events.error) {
//If no errback already, but there are error listeners
//on this module, set up an errback to pass to the deps.
errback = bind(this, function (err) {
this.emit('error', err);
});
}
//Do a copy of the dependency array, so that
//source inputs are not modified. For example
//"shim" deps are passed in here directly, and
//doing a direct modification of the depMaps array
//would affect that config.
this.depMaps = depMaps && depMaps.slice(0);
this.errback = errback;
//Indicate this module has be initialized
this.inited = true;
this.ignore = options.ignore;
//Could have option to init this module in enabled mode,
//or could have been previously marked as enabled. However,
//the dependencies are not known until init is called. So
//if enabled previously, now trigger dependencies as enabled.
if (options.enabled || this.enabled) {
//Enable this module and dependencies.
//Will call this.check()
this.enable();
} else {
this.check();
}
},
defineDep: function (i, depExports) {
//Because of cycles, defined callback for a given
//export can be called more than once.
if (!this.depMatched[i]) {
this.depMatched[i] = true;
this.depCount -= 1;
this.depExports[i] = depExports;
}
},
fetch: function () {
if (this.fetched) {
return;
}
this.fetched = true;
context.startTime = (new Date()).getTime();
var map = this.map;
//If the manager is for a plugin managed resource,
//ask the plugin to load it now.
if (this.shim) {
context.makeRequire(this.map, {
enableBuildCallback: true
})(this.shim.deps || [], bind(this, function () {
return map.prefix ? this.callPlugin() : this.load();
}));
} else {
//Regular dependency.
return map.prefix ? this.callPlugin() : this.load();
}
},
load: function () {
var url = this.map.url;
//Regular dependency.
if (!urlFetched[url]) {
urlFetched[url] = true;
context.load(this.map.id, url);
}
},
/**
* Checks if the module is ready to define itself, and if so,
* define it.
*/
check: function () {
if (!this.enabled || this.enabling) {
return;
}
var err, cjsModule,
id = this.map.id,
depExports = this.depExports,
exports = this.exports,
factory = this.factory;
if (!this.inited) {
this.fetch();
} else if (this.error) {
this.emit('error', this.error);
} else if (!this.defining) {
//The factory could trigger another require call
//that would result in checking this module to
//define itself again. If already in the process
//of doing that, skip this work.
this.defining = true;
if (this.depCount < 1 && !this.defined) {
if (isFunction(factory)) {
//If there is an error listener, favor passing
//to that instead of throwing an error.
if (this.events.error) {
try {
exports = context.execCb(id, factory, depExports, exports);
} catch (e) {
err = e;
}
} else {
exports = context.execCb(id, factory, depExports, exports);
}
if (this.map.isDefine) {
//If setting exports via 'module' is in play,
//favor that over return value and exports. After that,
//favor a non-undefined return value over exports use.
cjsModule = this.module;
if (cjsModule &&
cjsModule.exports !== undefined &&
//Make sure it is not already the exports value
cjsModule.exports !== this.exports) {
exports = cjsModule.exports;
} else if (exports === undefined && this.usingExports) {
//exports already set the defined value.
exports = this.exports;
}
}
if (err) {
err.requireMap = this.map;
err.requireModules = [this.map.id];
err.requireType = 'define';
return onError((this.error = err));
}
} else {
//Just a literal value
exports = factory;
}
this.exports = exports;
if (this.map.isDefine && !this.ignore) {
defined[id] = exports;
if (req.onResourceLoad) {
req.onResourceLoad(context, this.map, this.depMaps);
}
}
//Clean up
cleanRegistry(id);
this.defined = true;
}
//Finished the define stage. Allow calling check again
//to allow define notifications below in the case of a
//cycle.
this.defining = false;
if (this.defined && !this.defineEmitted) {
this.defineEmitted = true;
this.emit('defined', this.exports);
this.defineEmitComplete = true;
}
}
},
callPlugin: function () {
var map = this.map,
id = map.id,
//Map already normalized the prefix.
pluginMap = makeModuleMap(map.prefix);
//Mark this as a dependency for this plugin, so it
//can be traced for cycles.
this.depMaps.push(pluginMap);
on(pluginMap, 'defined', bind(this, function (plugin) {
var load, normalizedMap, normalizedMod,
name = this.map.name,
parentName = this.map.parentMap ? this.map.parentMap.name : null,
localRequire = context.makeRequire(map.parentMap, {
enableBuildCallback: true
});
//If current map is not normalized, wait for that
//normalized name to load instead of continuing.
if (this.map.unnormalized) {
//Normalize the ID if the plugin allows it.
if (plugin.normalize) {
name = plugin.normalize(name, function (name) {
return normalize(name, parentName, true);
}) || '';
}
//prefix and name should already be normalized, no need
//for applying map config again either.
normalizedMap = makeModuleMap(map.prefix + '!' + name,
this.map.parentMap);
on(normalizedMap,
'defined', bind(this, function (value) {
this.init([], function () { return value; }, null, {
enabled: true,
ignore: true
});
}));
normalizedMod = getOwn(registry, normalizedMap.id);
if (normalizedMod) {
//Mark this as a dependency for this plugin, so it
//can be traced for cycles.
this.depMaps.push(normalizedMap);
if (this.events.error) {
normalizedMod.on('error', bind(this, function (err) {
this.emit('error', err);
}));
}
normalizedMod.enable();
}
return;
}
load = bind(this, function (value) {
this.init([], function () { return value; }, null, {
enabled: true
});
});
load.error = bind(this, function (err) {
this.inited = true;
this.error = err;
err.requireModules = [id];
//Remove temp unnormalized modules for this module,
//since they will never be resolved otherwise now.
eachProp(registry, function (mod) {
if (mod.map.id.indexOf(id + '_unnormalized') === 0) {
cleanRegistry(mod.map.id);
}
});
onError(err);
});
//Allow plugins to load other code without having to know the
//context or how to 'complete' the load.
load.fromText = bind(this, function (text, textAlt) {
/*jslint evil: true */
var moduleName = map.name,
moduleMap = makeModuleMap(moduleName),
hasInteractive = useInteractive;
//As of 2.1.0, support just passing the text, to reinforce
//fromText only being called once per resource. Still
//support old style of passing moduleName but discard
//that moduleName in favor of the internal ref.
if (textAlt) {
text = textAlt;
}
//Turn off interactive script matching for IE for any define
//calls in the text, then turn it back on at the end.
if (hasInteractive) {
useInteractive = false;
}
//Prime the system by creating a module instance for
//it.
getModule(moduleMap);
//Transfer any config to this other module.
if (hasProp(config.config, id)) {
config.config[moduleName] = config.config[id];
}
try {
req.exec(text);
} catch (e) {
return onError(makeError('fromtexteval',
'fromText eval for ' + id +
' failed: ' + e,
e,
[id]));
}
if (hasInteractive) {
useInteractive = true;
}
//Mark this as a dependency for the plugin
//resource
this.depMaps.push(moduleMap);
//Support anonymous modules.
context.completeLoad(moduleName);
//Bind the value of that module to the value for this
//resource ID.
localRequire([moduleName], load);
});
//Use parentName here since the plugin's name is not reliable,
//could be some weird string with no path that actually wants to
//reference the parentName's path.
plugin.load(map.name, localRequire, load, config);
}));
context.enable(pluginMap, this);
this.pluginMaps[pluginMap.id] = pluginMap;
},
enable: function () {
enabledRegistry[this.map.id] = this;
this.enabled = true;
//Set flag mentioning that the module is enabling,
//so that immediate calls to the defined callbacks
//for dependencies do not trigger inadvertent load
//with the depCount still being zero.
this.enabling = true;
//Enable each dependency
each(this.depMaps, bind(this, function (depMap, i) {
var id, mod, handler;
if (typeof depMap === 'string') {
//Dependency needs to be converted to a depMap
//and wired up to this module.
depMap = makeModuleMap(depMap,
(this.map.isDefine ? this.map : this.map.parentMap),
false,
!this.skipMap);
this.depMaps[i] = depMap;
handler = getOwn(handlers, depMap.id);
if (handler) {
this.depExports[i] = handler(this);
return;
}
this.depCount += 1;
on(depMap, 'defined', bind(this, function (depExports) {
this.defineDep(i, depExports);
this.check();
}));
if (this.errback) {
on(depMap, 'error', this.errback);
}
}
id = depMap.id;
mod = registry[id];
//Skip special modules like 'require', 'exports', 'module'
//Also, don't call enable if it is already enabled,
//important in circular dependency cases.
if (!hasProp(handlers, id) && mod && !mod.enabled) {
context.enable(depMap, this);
}
}));
//Enable each plugin that is used in
//a dependency
eachProp(this.pluginMaps, bind(this, function (pluginMap) {
var mod = getOwn(registry, pluginMap.id);
if (mod && !mod.enabled) {
context.enable(pluginMap, this);
}
}));
this.enabling = false;
this.check();
},
on: function (name, cb) {
var cbs = this.events[name];
if (!cbs) {
cbs = this.events[name] = [];
}
cbs.push(cb);
},
emit: function (name, evt) {
each(this.events[name], function (cb) {
cb(evt);
});
if (name === 'error') {
//Now that the error handler was triggered, remove
//the listeners, since this broken Module instance
//can stay around for a while in the registry.
delete this.events[name];
}
}
};
function callGetModule(args) {
//Skip modules already defined.
if (!hasProp(defined, args[0])) {
getModule(makeModuleMap(args[0], null, true)).init(args[1], args[2]);
}
}
function removeListener(node, func, name, ieName) {
//Favor detachEvent because of IE9
//issue, see attachEvent/addEventListener comment elsewhere
//in this file.
if (node.detachEvent && !isOpera) {
//Probably IE. If not it will throw an error, which will be
//useful to know.
if (ieName) {
node.detachEvent(ieName, func);
}
} else {
node.removeEventListener(name, func, false);
}
}
/**
* Given an event from a script node, get the requirejs info from it,
* and then removes the event listeners on the node.
* @param {Event} evt
* @returns {Object}
*/
function getScriptData(evt) {
//Using currentTarget instead of target for Firefox 2.0's sake. Not
//all old browsers will be supported, but this one was easy enough
//to support and still makes sense.
var node = evt.currentTarget || evt.srcElement;
//Remove the listeners once here.
removeListener(node, context.onScriptLoad, 'load', 'onreadystatechange');
removeListener(node, context.onScriptError, 'error');
return {
node: node,
id: node && node.getAttribute('data-requiremodule')
};
}
function intakeDefines() {
var args;
//Any defined modules in the global queue, intake them now.
takeGlobalQueue();
//Make sure any remaining defQueue items get properly processed.
while (defQueue.length) {
args = defQueue.shift();
if (args[0] === null) {
return onError(makeError('mismatch', 'Mismatched anonymous define() module: ' + args[args.length - 1]));
} else {
//args are id, deps, factory. Should be normalized by the
//define() function.
callGetModule(args);
}
}
}
context = {
config: config,
contextName: contextName,
registry: registry,
defined: defined,
urlFetched: urlFetched,
defQueue: defQueue,
Module: Module,
makeModuleMap: makeModuleMap,
nextTick: req.nextTick,
onError: onError,
/**
* Set a configuration for the context.
* @param {Object} cfg config object to integrate.
*/
configure: function (cfg) {
//Make sure the baseUrl ends in a slash.
if (cfg.baseUrl) {
if (cfg.baseUrl.charAt(cfg.baseUrl.length - 1) !== '/') {
cfg.baseUrl += '/';
}
}
//Save off the paths and packages since they require special processing,
//they are additive.
var pkgs = config.pkgs,
shim = config.shim,
objs = {
paths: true,
config: true,
map: true
};
eachProp(cfg, function (value, prop) {
if (objs[prop]) {
if (prop === 'map') {
if (!config.map) {
config.map = {};
}
mixin(config[prop], value, true, true);
} else {
mixin(config[prop], value, true);
}
} else {
config[prop] = value;
}
});
//Merge shim
if (cfg.shim) {
eachProp(cfg.shim, function (value, id) {
//Normalize the structure
if (isArray(value)) {
value = {
deps: value
};
}
if ((value.exports || value.init) && !value.exportsFn) {
value.exportsFn = context.makeShimExports(value);
}
shim[id] = value;
});
config.shim = shim;
}
//Adjust packages if necessary.
if (cfg.packages) {
each(cfg.packages, function (pkgObj) {
var location;
pkgObj = typeof pkgObj === 'string' ? { name: pkgObj } : pkgObj;
location = pkgObj.location;
//Create a brand new object on pkgs, since currentPackages can
//be passed in again, and config.pkgs is the internal transformed
//state for all package configs.
pkgs[pkgObj.name] = {
name: pkgObj.name,
location: location || pkgObj.name,
//Remove leading dot in main, so main paths are normalized,
//and remove any trailing .js, since different package
//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,
//update the maps for them, since their info, like URLs to load,
//may have changed.
eachProp(registry, function (mod, id) {
//If module already has init called, since it is too
//late to modify them, and ignore unnormalized ones
//since they are transient.
if (!mod.inited && !mod.map.unnormalized) {
mod.map = makeModuleMap(id);
}
});
//If a deps array or a config callback is specified, then call
//require with those args. This is useful when require is defined as a
//config object before require.js is loaded.
if (cfg.deps || cfg.callback) {
context.require(cfg.deps || [], cfg.callback);
}
},
makeShimExports: function (value) {
function fn() {
var ret;
if (value.init) {
ret = value.init.apply(global, arguments);
}
return ret || (value.exports && getGlobal(value.exports));
}
return fn;
},
makeRequire: function (relMap, options) {
options = options || {};
function localRequire(deps, callback, errback) {
var id, map, requireMod;
if (options.enableBuildCallback && callback && isFunction(callback)) {
callback.__requireJsBuild = true;
}
if (typeof deps === 'string') {
if (isFunction(callback)) {
//Invalid call
return onError(makeError('requireargs', 'Invalid require call'), errback);
}
//If require|exports|module are requested, get the
//value for them from the special handlers. Caveat:
//this only works while module is being defined.
if (relMap && hasProp(handlers, deps)) {
return handlers[deps](registry[relMap.id]);
}
//Synchronous access to one module. If require.get is
//available (as in the Node adapter), prefer that.
if (req.get) {
return req.get(context, deps, relMap, localRequire);
}
//Normalize module name, if it contains . or ..
map = makeModuleMap(deps, relMap, false, true);
id = map.id;
if (!hasProp(defined, id)) {
return onError(makeError('notloaded', 'Module name "' +
id +
'" has not been loaded yet for context: ' +
contextName +
(relMap ? '' : '. Use require([])')));
}
return defined[id];
}
//Grab defines waiting in the global queue.
intakeDefines();
//Mark all the dependencies as needing to be loaded.
context.nextTick(function () {
//Some defines could have been added since the
//require call, collect them.
intakeDefines();
requireMod = getModule(makeModuleMap(null, relMap));
//Store if map config should be applied to this require
//call for dependencies.
requireMod.skipMap = options.skipMap;
requireMod.init(deps, callback, errback, {
enabled: true
});
checkLoaded();
});
return localRequire;
}
mixin(localRequire, {
isBrowser: isBrowser,
/**
* Converts a module name + .extension into an URL path.
* *Requires* the use of a module name. It does not support using
* plain URLs like nameToUrl.
*/
toUrl: function (moduleNamePlusExt) {
var ext,
index = moduleNamePlusExt.lastIndexOf('.'),
segment = moduleNamePlusExt.split('/')[0],
isRelative = segment === '.' || segment === '..';
//Have a file extension alias, and it is not the
//dots from a relative path.
if (index !== -1 && (!isRelative || index > 1)) {
ext = moduleNamePlusExt.substring(index, moduleNamePlusExt.length);
moduleNamePlusExt = moduleNamePlusExt.substring(0, index);
}
return context.nameToUrl(normalize(moduleNamePlusExt,
relMap && relMap.id, true), ext, true);
},
defined: function (id) {
return hasProp(defined, makeModuleMap(id, relMap, false, true).id);
},
specified: function (id) {
id = makeModuleMap(id, relMap, false, true).id;
return hasProp(defined, id) || hasProp(registry, id);
}
});
//Only allow undef on top level require calls
if (!relMap) {
localRequire.undef = function (id) {
//Bind any waiting define() calls to this context,
//fix for #408
takeGlobalQueue();
var map = makeModuleMap(id, relMap, true),
mod = getOwn(registry, id);
delete defined[id];
delete urlFetched[map.url];
delete undefEvents[id];
if (mod) {
//Hold on to listeners in case the
//module will be attempted to be reloaded
//using a different config.
if (mod.events.defined) {
undefEvents[id] = mod.events;
}
cleanRegistry(id);
}
};
}
return localRequire;
},
/**
* Called to enable a module if it is still in the registry
* awaiting enablement. A second arg, parent, the parent module,
* is passed in for context, when this method is overriden by
* the optimizer. Not shown here to keep code compact.
*/
enable: function (depMap) {
var mod = getOwn(registry, depMap.id);
if (mod) {
getModule(depMap).enable();
}
},
/**
* Internal method used by environment adapters to complete a load event.
* A load event could be a script load or just a load pass from a synchronous
* load call.
* @param {String} moduleName the name of the module to potentially complete.
*/
completeLoad: function (moduleName) {
var found, args, mod,
shim = getOwn(config.shim, moduleName) || {},
shExports = shim.exports;
takeGlobalQueue();
while (defQueue.length) {
args = defQueue.shift();
if (args[0] === null) {
args[0] = moduleName;
//If already found an anonymous module and bound it
//to this name, then this is some other anon module
//waiting for its completeLoad to fire.
if (found) {
break;
}
found = true;
} else if (args[0] === moduleName) {
//Found matching define call for this script!
found = true;
}
callGetModule(args);
}
//Do this after the cycle of callGetModule in case the result
//of those calls/init calls changes the registry.
mod = getOwn(registry, moduleName);
if (!found && !hasProp(defined, moduleName) && mod && !mod.inited) {
if (config.enforceDefine && (!shExports || !getGlobal(shExports))) {
if (hasPathFallback(moduleName)) {
return;
} else {
return onError(makeError('nodefine',
'No define call for ' + moduleName,
null,
[moduleName]));
}
} else {
//A script that does not call define(), so just simulate
//the call for it.
callGetModule([moduleName, (shim.deps || []), shim.exportsFn]);
}
}
checkLoaded();
},
/**
* Converts a module name to a file path. Supports cases where
* moduleName may actually be just an URL.
* Note that it **does not** call normalize on the moduleName,
* it is assumed to have already been normalized. This is an
* internal API, not a public one. Use toUrl for the public API.
*/
nameToUrl: function (moduleName, ext, skipExt) {
var paths, pkgs, pkg, pkgPath, syms, i, parentModule, url,
parentPath;
//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. ?)
//or ends with .js, then assume the user meant to use an url and not a module id.
//The slash is important for protocol-less URLs as well as full paths.
if (req.jsExtRegExp.test(moduleName)) {
//Just a plain path, not module name lookup, so just return it.
//Add extension if it is included. This is a bit wonky, only non-.js things pass
//an extension, this method probably needs to be reworked.
url = moduleName + (ext || '');
} else {
//A module that needs to be converted to a path.
paths = config.paths;
pkgs = config.pkgs;
syms = moduleName.split('/');
//For each module name segment, see if there is a path
//registered for it. Start with most specific name
//and work up from it.
for (i = syms.length; i > 0; i -= 1) {
parentModule = syms.slice(0, i).join('/');
pkg = getOwn(pkgs, parentModule);
parentPath = getOwn(paths, parentModule);
if (parentPath) {
//If an array, it means there are a few choices,
//Choose the one that is desired
if (isArray(parentPath)) {
parentPath = parentPath[0];
}
syms.splice(0, i, parentPath);
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.
url = syms.join('/');
url += (ext || (/\?/.test(url) || skipExt ? '' : '.js'));
url = (url.charAt(0) === '/' || url.match(/^[\w\+\.\-]+:/) ? '' : config.baseUrl) + url;
}
return config.urlArgs ? url +
((url.indexOf('?') === -1 ? '?' : '&') +
config.urlArgs) : url;
},
//Delegates to req.load. Broken out as a separate function to
//allow overriding in the optimizer.
load: function (id, url) {
req.load(context, id, url);
},
/**
* Executes a module callack function. Broken out as a separate function
* solely to allow the build system to sequence the files in the built
* layer in the right sequence.
*
* @private
*/
execCb: function (name, callback, args, exports) {
return callback.apply(exports, args);
},
/**
* callback for script loads, used to check status of loading.
*
* @param {Event} evt the event from the browser for the script
* that was loaded.
*/
onScriptLoad: function (evt) {
//Using currentTarget instead of target for Firefox 2.0's sake. Not
//all old browsers will be supported, but this one was easy enough
//to support and still makes sense.
if (evt.type === 'load' ||
(readyRegExp.test((evt.currentTarget || evt.srcElement).readyState))) {
//Reset interactive script so a script node is not held onto for
//to long.
interactiveScript = null;
//Pull out the name of the module and the context.
var data = getScriptData(evt);
context.completeLoad(data.id);
}
},
/**
* Callback for script errors.
*/
onScriptError: function (evt) {
var data = getScriptData(evt);
if (!hasPathFallback(data.id)) {
return onError(makeError('scripterror', 'Script error', evt, [data.id]));
}
}
};
context.require = context.makeRequire();
return context;
}
/**
* Main entry point.
*
* If the only argument to require is a string, then the module that
* is represented by that string is fetched for the appropriate context.
*
* If the first argument is an array, then it will be treated as an array
* of dependency string names to fetch. An optional function callback can
* be specified to execute when all of those dependencies are available.
*
* Make a local req variable to help Caja compliance (it assumes things
* on a require that are not standardized), and to give a short
* name for minification/local scope use.
*/
req = requirejs = function (deps, callback, errback, optional) {
//Find the right context, use default
var context, config,
contextName = defContextName;
// Determine if have config object in the call.
if (!isArray(deps) && typeof deps !== 'string') {
// deps is a config object
config = deps;
if (isArray(callback)) {
// Adjust args if there are dependencies
deps = callback;
callback = errback;
errback = optional;
} else {
deps = [];
}
}
if (config && config.context) {
contextName = config.context;
}
context = getOwn(contexts, contextName);
if (!context) {
context = contexts[contextName] = req.s.newContext(contextName);
}
if (config) {
context.configure(config);
}
return context.require(deps, callback, errback);
};
/**
* Support require.config() to make it easier to cooperate with other
* AMD loaders on globally agreed names.
*/
req.config = function (config) {
return req(config);
};
/**
* Execute something after the current tick
* of the event loop. Override for other envs
* that have a better solution than setTimeout.
* @param {Function} fn function to execute later.
*/
req.nextTick = typeof setTimeout !== 'undefined' ? function (fn) {
setTimeout(fn, 4);
} : function (fn) { fn(); };
/**
* Export require as a global, but only if it does not already exist.
*/
if (!require) {
require = req;
}
req.version = version;
//Used to filter out dependencies that are already paths.
req.jsExtRegExp = /^\/|:|\?|\.js$/;
req.isBrowser = isBrowser;
s = req.s = {
contexts: contexts,
newContext: newContext
};
//Create default context.
req({});
//Exports some context-sensitive methods on global require.
each([
'toUrl',
'undef',
'defined',
'specified'
], function (prop) {
//Reference from contexts instead of early binding to default context,
//so that during builds, the latest instance of the default context
//with its config gets used.
req[prop] = function () {
var ctx = contexts[defContextName];
return ctx.require[prop].apply(ctx, arguments);
};
});
if (isBrowser) {
head = s.head = document.getElementsByTagName('head')[0];
//If BASE tag is in play, using appendChild is a problem for IE6.
//When that browser dies, this can be removed. Details in this jQuery bug:
//http://dev.jquery.com/ticket/2709
baseElement = document.getElementsByTagName('base')[0];
if (baseElement) {
head = s.head = baseElement.parentNode;
}
}
/**
* Any errors that require explicitly generates will be passed to this
* function. Intercept/override it if you want custom error handling.
* @param {Error} err the error object.
*/
req.onError = function (err) {
throw err;
};
/**
* Does the request to load a module for the browser case.
* Make this a separate function to allow other environments
* to override it.
*
* @param {Object} context the require context to find state.
* @param {String} moduleName the name of the module.
* @param {Object} url the URL to the module.
*/
req.load = function (context, moduleName, url) {
var config = (context && context.config) || {},
node;
if (isBrowser) {
//In the browser so use a script tag
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;
node.setAttribute('data-requirecontext', context.contextName);
node.setAttribute('data-requiremodule', moduleName);
//Set up load listener. Test attachEvent first because IE9 has
//a subtle issue in its addEventListener and script onload firings
//that do not match the behavior of all other browsers with
//addEventListener support, which fire the onload event for a
//script right after the script execution. See:
//https://connect.microsoft.com/IE/feedback/details/648057/script-onload-event-is-not-fired-immediately-after-script-execution
//UNFORTUNATELY Opera implements attachEvent but does not follow the script
//script execution mode.
if (node.attachEvent &&
//Check if node.attachEvent is artificially added by custom script or
//natively supported by browser
//read https://github.com/jrburke/requirejs/issues/187
//if we can NOT find [native code] then it must NOT natively supported.
//in IE8, node.attachEvent does not have toString()
//Note the test for "[native code" with no closing brace, see:
//https://github.com/jrburke/requirejs/issues/273
!(node.attachEvent.toString && node.attachEvent.toString().indexOf('[native code') < 0) &&
!isOpera) {
//Probably IE. IE (at least 6-8) do not fire
//script onload right after executing the script, so
//we cannot tie the anonymous define call to a name.
//However, IE reports the script as being in 'interactive'
//readyState at the time of the define call.
useInteractive = true;
node.attachEvent('onreadystatechange', context.onScriptLoad);
//It would be great to add an error handler here to catch
//404s in IE9+. However, onreadystatechange will fire before
//the error handler, so that does not help. If addEventListener
//is used, then IE will fire error before load, but we cannot
//use that pathway given the connect.microsoft.com issue
//mentioned above about not doing the 'script execute,
//then fire the script load event listener before execute
//next script' that other browsers do.
//Best hope: IE10 fixes the issues,
//and then destroys all installs of IE 6-9.
//node.attachEvent('onerror', context.onScriptError);
} else {
node.addEventListener('load', context.onScriptLoad, false);
node.addEventListener('error', context.onScriptError, false);
}
node.src = url;
//For some cache cases in IE 6-8, the script executes before the end
//of the appendChild execution, so to tie an anonymous define
//call to the module name (which is stored on the node), hold on
//to a reference to this node, but clear after the DOM insertion.
currentlyAddingScript = node;
if (baseElement) {
head.insertBefore(node, baseElement);
} else {
head.appendChild(node);
}
currentlyAddingScript = null;
return node;
} else if (isWebWorker) {
try {
//In a web worker, use importScripts. This is not a very
//efficient use of importScripts, importScripts will block until
//its script is downloaded and evaluated. However, if web workers
//are in play, the expectation that a build has been done so that
//only one script needs to be loaded anyway. This may need to be
//reevaluated if other use cases become common.
importScripts(url);
//Account for anonymous modules
context.completeLoad(moduleName);
} catch (e) {
context.onError(makeError('importscripts',
'importScripts failed for ' +
moduleName + ' at ' + url,
e,
[moduleName]));
}
}
};
function getInteractiveScript() {
if (interactiveScript && interactiveScript.readyState === 'interactive') {
return interactiveScript;
}
eachReverse(scripts(), function (script) {
if (script.readyState === 'interactive') {
return (interactiveScript = script);
}
});
return interactiveScript;
}
//Look for a data-main script attribute, which could also adjust the baseUrl.
if (isBrowser) {
//Figure out baseUrl. Get it from the script tag with require.js in it.
eachReverse(scripts(), function (script) {
//Set the 'head' where we can append children by
//using the script's parent.
if (!head) {
head = script.parentNode;
}
//Look for a data-main attribute to set main script for the page
//to load. If it is there, the path to data main becomes the
//baseUrl, if it is not already set.
dataMain = script.getAttribute('data-main');
if (dataMain) {
//Set final baseUrl if there is not already an explicit one.
if (!cfg.baseUrl) {
//Pull off the directory of data-main for use as the
//baseUrl.
src = dataMain.split('/');
mainScript = src.pop();
subPath = src.length ? src.join('/') + '/' : './';
cfg.baseUrl = subPath;
dataMain = mainScript;
}
//Strip off any trailing .js since dataMain is now
//like a module name.
dataMain = dataMain.replace(jsSuffixRegExp, '');
//Put the data-main script in the files to load.
cfg.deps = cfg.deps ? cfg.deps.concat(dataMain) : [dataMain];
return true;
}
});
}
/**
* The function that handles definitions of modules. Differs from
* require() in that a string for the module should be the first argument,
* and the function to execute after dependencies are loaded should
* return a value to define the module corresponding to the first argument's
* name.
*/
define = function (name, deps, callback) {
var node, context;
//Allow for anonymous modules
if (typeof name !== 'string') {
//Adjust args appropriately
callback = deps;
deps = name;
name = null;
}
//This module may not have dependencies
if (!isArray(deps)) {
callback = deps;
deps = [];
}
//If no name, and callback is a function, then figure out if it a
//CommonJS thing with dependencies.
if (!deps.length && isFunction(callback)) {
//Remove comments from the callback string,
//look for require calls, and pull them into the dependencies,
//but only if there are function args.
if (callback.length) {
callback
.toString()
.replace(commentRegExp, '')
.replace(cjsRequireRegExp, function (match, dep) {
deps.push(dep);
});
//May be a CommonJS thing even without require calls, but still
//could use exports, and module. Avoid doing exports and module
//work though if it just needs require.
//REQUIRES the function to expect the CommonJS variables in the
//order listed below.
deps = (callback.length === 1 ? ['require'] : ['require', 'exports', 'module']).concat(deps);
}
}
//If in IE 6-8 and hit an anonymous define() call, do the interactive
//work.
if (useInteractive) {
node = currentlyAddingScript || getInteractiveScript();
if (node) {
if (!name) {
name = node.getAttribute('data-requiremodule');
}
context = contexts[node.getAttribute('data-requirecontext')];
}
}
//Always save off evaluating the def call until the script onload handler.
//This allows multiple modules to be in a file without prematurely
//tracing dependencies, and allows for anonymous module support,
//where the module name is not known until the script onload event
//occurs. If no context, use the global queue, and get it processed
//in the onscript load callback.
(context ? context.defQueue : globalDefQueue).push([name, deps, callback]);
};
define.amd = {
jQuery: true
};
/**
* Executes the text. Normally just uses eval, but can be modified
* to use a better, environment-specific call. Only used for transpiling
* loader plugins, not for plain JS modules.
* @param {String} text the text to execute/evaluate.
*/
req.exec = function (text) {
/*jslint evil: true */
return eval(text);
};
//Set up with config info.
req(cfg);
}(this));
;(function (soma, undefined) {
'use strict';
soma.template = soma.template || {};
soma.template.version = "0.1.8";
var 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_NO_PARAM: "Error in soma.template, a template requires at least 1 parameter - soma.template.create(element)"
};
var tokenStart = '{{';
var tokenEnd = '}}';
var helpersObject = {};
var helpersScopeObject = {};
var settings = soma.template.settings = soma.template.settings || {};
settings.autocreate = true;
var tokens = settings.tokens = {
start: function(value) {
if (isDefined(value) && value !== '') {
tokenStart = escapeRegExp(value);
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");
elm.className = elm.className.replace(reg, "$2");
}
}
removeClass(elm, className);
}
// jquery contains
var contains = typeof document !== 'object' ? function(){} : document.documentElement.contains ?
function( a, b ) {
var adown = a.nodeType === 9 ? a.documentElement : a,
bup = b && b.parentNode;
return a === bup || !!( bup && bup.nodeType === 1 && adown.contains && adown.contains(bup) );
} :
document.documentElement.compareDocumentPosition ?
function( a, b ) {
return b && !!( a.compareDocumentPosition( b ) & 16 );
} :
function( a, b ) {
while ( (b = b.parentNode) ) {
if ( b === a ) {
return true;
}
}
return false;
};
function HashMap() {
var items = {};
var id = 1;
//var uuid = function(a,b){for(b=a='';a++<36;b+=a*51&52?(a^15?8^Math.random()*(a^20?16:4):4).toString(16):'-');return b;}
function uuid() { return ++id; };
function getKey(target) {
if (!target) return;
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.hashkey ? target.hashkey : target.hashkey = 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) {
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) {
node.childrenRepeater.length = data.length;
}
}
else {
// process object
var count = -1;
for (var o in data) {
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;
}
if (node.element.parentNode) {
node.element.parentNode.removeChild(node.element);
}
}
function cloneRepeaterNode(element, node) {
var newNode = new Node(element, node.scope._createChild());
if (node.attributes) {
var attrs = [];
for (var i = 0, l = node.attributes.length; i < l; i++) {
newNode.renderAsHtml = node.renderAsHtml;
if (node.attributes[i].name === settings.attributes.skip) {
newNode.skip = (node.attributes[i].value === "" || node.attributes[i].value === "true");
}
if (node.attributes[i].name === settings.attributes.html) {
newNode.html = (node.attributes[i].value === "" || node.attributes[i].value === "true");
}
if (node.attributes[i].name !== attributes.repeat) {
var attribute = new Attribute(node.attributes[i].name, node.attributes[i].value, newNode);
attrs.push(attribute);
}
if (events[node.attributes[i].name]) {
newNode.addEvent(events[node.attributes[i].name], node.attributes[i].value);
}
}
newNode.attributes = attrs;
}
return newNode;
}
function createRepeaterChild(node, count, data, indexVar, indexVarValue, previousElement) {
var existingChild = node.childrenRepeater[count];
if (!existingChild) {
// no existing node
var newElement = node.element.cloneNode(true);
// can't recreate the node with a cloned element on IE7
// 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;
newNode.parent = node.parent;
newNode.template = node.template;
node.childrenRepeater[count] = newNode;
updateScopeWithRepeaterData(node.repeater, newNode.scope, data);
newNode.scope[indexVar] = indexVarValue;
compile(node.template, newElement, node.parent, newNode);
newNode.update();
newNode.render();
if (!previousElement) {
if (node.previousSibling) insertAfter(node.previousSibling, newElement);
else if (node.nextSibling) insertBefore(node.nextSibling, newElement);
else node.parent.element.appendChild(newElement);
}
else {
insertAfter(previousElement, newElement);
}
return newElement;
}
else {
// existing node
updateScopeWithRepeaterData(node.repeater, existingChild.scope, data);
existingChild.scope[indexVar] = indexVarValue;
existingChild.update();
existingChild.render();
return existingChild.element;
}
}
var Scope = function(data) {
function createChild(data) {
var obj = createObject(data);
obj._parent = this;
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) {
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.attributesHashMap = 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;
else 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;
}
}
else {
for (var i = 0, l = this.children.length; i < l; i++) {
node = this.children[i].getNode(element);
if (node) return node;
}
}
return null;
},
getAttribute: function(name) {
if (this.attributes) {
for (var i = 0, l = this.attributes.length; i < l; i++) {
var att = this.attributes[i];
if (att.interpolationName && att.interpolationName.value === name) {
return att;
}
}
}
},
update: function() {
if (childNodeIsTemplate(this)) 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();
}
}
updateNodeChildren(this);
},
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;
}
}
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) {
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);
}
};
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) {
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();
}
}
}
if (this.attributes) {
for (var i = 0, l = this.attributes.length; i < l; i++) {
this.attributes[i].render();
}
}
if (this.repeater) {
renderNodeRepeater(this);
}
else {
renderNodeChildren(this);
}
}
};
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() {
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 {
if (this.node.isRepeaterChild && ie === 7) {
// delete attributes on cloned elements crash IE7
}
else {
this.node.element.removeAttribute(this.interpolationName.value);
}
if (this.previousName) {
if (ie === 7 && this.previousName === 'class') {
// iE
this.node.element.className = "";
}
else {
if (this.node.isRepeaterChild && ie === 7) {
// delete attributes on cloned elements crash IE7
}
else {
this.node.element.removeAttribute(this.previousName);
}
}
}
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) {
element.style.display = isAttributeDefined(this.value) ? "none" : "block";
}
// show
if (this.name === attributes.show) {
element.style.display = isAttributeDefined(this.value) ? "block" : "none";
}
// checked
if (this.name === attributes.checked) {
renderSpecialAttribute(this.name, this.value, 'checked');
element.checked = isAttributeDefined(this.value) ? true : false;
}
// disabled
if (this.name === attributes.disabled) {
renderSpecialAttribute(this.name, this.value, 'disabled');
}
// multiple
if (this.name === attributes.multiple) {
renderSpecialAttribute(this.name, this.value, 'multiple');
}
// readonly
if (this.name === attributes.readonly) {
if (ie === 7) {
element.readOnly = isAttributeDefined(this.value) ? true : false;
}
else {
renderSpecialAttribute(this.name, this.value, 'readonly');
}
}
// selected
if (this.name === attributes.selected) {
renderSpecialAttribute(this.name, this.value, 'selected');
}
// normal attribute
function renderAttribute(name, value) {
if (ie === 7 && name === "class") {
element.className = value;
}
else {
element.setAttribute(name, value);
}
}
// special attribute
function renderSpecialAttribute(name, value, attrName) {
if (isAttributeDefined(value)) {
element.setAttribute(attrName, attrName);
}
else {
element.removeAttribute(attrName);
}
}
// src attribute
function renderSrc(name, value) {
element.setAttribute('src', value);
}
// href attribute
function renderHref(name, value) {
element.setAttribute('href', value);
}
}
};
var Interpolation = function(value, node, attribute) {
this.value = node && !isTextNode(node.element) ? trim(value) : value;
this.node = node;
this.attribute = attribute;
this.sequence = [];
this.expressions = [];
var parts = this.value.match(regex.sequence);
if (parts) {
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);
}
};
Interpolation.prototype = {
toString: function() {
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;
}
};
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) {
this.node.dispose();
}
this.element = null;
this.watchers = null;
this.node = null;
}
};
// written by Dean Edwards, 2005
// with input from Tino Zijdel, Matthias Miller, Diego Perini
// http://dean.edwards.name/weblog/2005/10/add-event/
function addEvent(element, type, handler) {
if (element.addEventListener) {
element.addEventListener(type, handler, false);
} else {
// assign each event handler a unique ID
if (!handler.$$guid) handler.$$guid = addEvent.guid++;
// create a hash table of event types for the element
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) {
var parts = pattern.match(regex.func);
if (parts) {
var func = parts[1];
if (isFunction(object[func])) {
return object[func];
}
}
}
function clearEvents(element) {
var i = eventStore.length, l = 0;
while (--i >= l) {
var item = eventStore[i];
if (element === item.element || contains(element, item.element)) {
removeEvent(item.element, item.type, item.handler);
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 parse = function(element) {
var child = !element ? document.body : element.firstChild;
while (child) {
if (child.nodeType === 1) {
parse(child);
var attrValue = child.getAttribute(attributes.template);
if (attrValue) {
var getFunction = new Function('return ' + attrValue + ';');
try {
var f = getFunction();
if (isFunction(f)) {
soma.template.bootstrap(attrValue, child, f);
}
} catch(err){};
}
}
child = child.nextSibling;
}
};
ready(parse);
}
function bootstrapTemplate(attrValue, element, func) {
var tpl = createTemplate(element);
func(tpl, tpl.scope, tpl.element, tpl.node);
}
function createTemplate(source, target) {
var element;
if (isString(source)) {
// string template
if (!isElement(target)) {
throw new Error(soma.template.errors.TEMPLATE_STRING_NO_ELEMENT);
}
target.innerHTML = source;
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 {
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) {
return templates.get(element);
}
function renderAllTemplates() {
for (var key in templates.getData()) {
templates.get(key).render();
}
}
function appendHelpers(obj) {
if (obj === null) {
helpersObject = {};
helpersScopeObject = {};
}
if (isDefined(obj) && isObject(obj)) {
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
helpersObject[key] = helpersScopeObject[key] = obj[key];
}
}
}
return helpersObject;
}
// set regex
tokens.start(tokenStart);
tokens.end(tokenEnd);
// plugins
soma.plugins = soma.plugins || {};
var TemplatePlugin = function(instance, injector) {
instance.constructor.prototype.createTemplate = function(cl, domElement) {
if (!cl || typeof cl !== "function") {
throw new Error("Error creating a template, the first parameter must be a function.");
}
if (domElement && isElement(domElement)) {
var template = soma.template.create(domElement);
for (var key in template) {
if (typeof template[key] === 'function') {
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);
}
return null;
}
soma.template.bootstrap = function(attrValue, element, func) {
instance.createTemplate(func, element);
}
}
if (soma.plugins && soma.plugins.add) {
soma.plugins.add(TemplatePlugin);
}
soma.template.Plugin = TemplatePlugin;
// exports
soma.template.create = createTemplate;
soma.template.get = getTemplate;
soma.template.renderAll = renderAllTemplates;
soma.template.helpers = appendHelpers;
soma.template.bootstrap = bootstrapTemplate;
soma.template.addEvent = addEvent;
soma.template.removeEvent = removeEvent;
soma.template.parseEvents = parseEvents;
soma.template.clearEvents = clearEvents;
soma.template.ready = ready;
// register for AMD module
if (typeof define === 'function' && define.amd) {
define("soma-template", soma.template);
}
// export for node.js
if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
exports = module.exports = soma.template;
}
exports = soma.template;
}
})(this['soma'] = this['soma'] || {});
\ No newline at end of file
/*
Copyright (c) | 2012 | 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
and associated documentation 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 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 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 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.
*/
;(function(infuse, undefined) {
"use strict";
infuse.version = "0.6.3";
// regex from angular JS (https://github.com/angular/angular.js)
var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
var FN_ARG_SPLIT = /,/;
var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
if(!Array.prototype.contains) {
Array.prototype.contains = function(value) {
var i = this.length;
while (i--) {
if (this[i] === value) return true;
}
return false;
};
}
infuse.InjectorError = {
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_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_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",
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_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) {
this.prop = prop;
this.value = value;
this.cl = cl;
this.singleton = singleton || false;
};
var validateProp = function(prop) {
if (typeof prop !== "string") {
throw new Error(infuse.InjectorError.MAPPING_BAD_PROP);
}
};
var validateValue = function(prop, val) {
if (val === undefined || val === null) {
throw new Error(infuse.InjectorError.MAPPING_BAD_VALUE + prop);
}
};
var validateClass = function(prop, val) {
if (typeof val !== "function") {
throw new Error(infuse.InjectorError.MAPPING_BAD_CLASS + prop);
}
};
var validateBooleanSingleton = function(prop, singleton) {
if (typeof singleton !== "boolean") {
throw new Error(infuse.InjectorError.MAPPING_BAD_SINGLETON + prop);
}
};
var validateConstructorInjectionLoop = function(name, cl) {
var params = infuse.getConstructorParams(cl);
if (params.contains(name)) {
throw new Error(infuse.InjectorError.INJECT_INSTANCE_IN_ITSELF_CONSTRUCTOR);
}
};
var validatePropertyInjectionLoop = function(name, target) {
if (target.hasOwnProperty(name)) {
throw new Error(infuse.InjectorError.INJECT_INSTANCE_IN_ITSELF_PROPERTY);
}
};
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() {
this.mappings = {};
this.parent = null;
};
infuse.getConstructorParams = function(cl) {
var args = [];
var clStr = cl.toString().replace(STRIP_COMMENTS, '');
var argsFlat = clStr.match(FN_ARGS);
var spl = argsFlat[1].split(FN_ARG_SPLIT);
for (var i=0; i<spl.length; i++) {
var arg = spl[i];
arg.replace(FN_ARG, function(all, underscore, name){
args.push(name);
});
}
return args;
};
infuse.Injector.prototype = {
createChild: function() {
var injector = new infuse.Injector();
injector.parent = this;
return injector;
},
getMappingVo: function(prop) {
if (!this.mappings) return null;
if (this.mappings[prop]) return this.mappings[prop];
if (this.parent) return this.parent.getMappingVo(prop);
return null;
},
mapValue: function(prop, val) {
if (this.mappings[prop]) {
throw new Error(infuse.InjectorError.MAPPING_ALREADY_EXISTS + prop);
}
validateProp(prop);
validateValue(prop, val);
this.mappings[prop] = new MappingVO(prop, val);
return this;
},
mapClass: function(prop, cl, singleton) {
if (this.mappings[prop]) {
throw new Error(infuse.InjectorError.MAPPING_ALREADY_EXISTS + prop);
}
validateProp(prop);
validateClass(prop, cl);
if (singleton) validateBooleanSingleton(prop, singleton);
this.mappings[prop] = new MappingVO(prop, null, cl, singleton);
return this;
},
removeMapping: function(prop) {
this.mappings[prop] = null;
delete this.mappings[prop];
return this;
},
hasMapping: function(prop) {
return !!this.mappings[prop];
},
hasInheritedMapping: function(prop) {
return !!this.getMappingVo(prop);
},
getMapping: function(value) {
for (var name in this.mappings) {
var vo = this.mappings[name];
if (vo.value === value || vo.cl === value) {
return vo.prop;
}
}
},
getValue: function(prop) {
var vo = this.mappings[prop];
if (!vo) {
if (this.parent) return this.parent.getValue.apply(this.parent, arguments);
else throw new Error(infuse.InjectorError.NO_MAPPING_FOUND);
}
if (vo.cl) {
arguments[0] = vo.cl;
return this.getValueFromClass.apply(this, arguments);
}
return vo.value;
},
getClass: function(prop) {
var vo = this.mappings[prop];
if (!vo) {
if (this.parent) return this.parent.getClass(prop);
else return undefined;
}
if (vo.cl) return vo.cl;
return undefined;
},
instantiate: function(TargetClass) {
if (typeof TargetClass !== "function") {
throw new Error(infuse.InjectorError.CREATE_INSTANCE_INVALID_PARAM);
}
var TargetClass = arguments[0];
var args = [null];
var params = infuse.getConstructorParams(TargetClass, this.mappings);
for (var i=0; i<params.length; i++) {
if (arguments[i+1] !== undefined && arguments[i+1] !== null) {
// argument found
args.push(arguments[i+1]);
}
else {
var name = params[i];
// no argument found
var vo = this.getMappingVo(name);
if (!!vo) {
// found mapping
var val = this.getInjectedValue(vo, name);
args.push(val);
}
else {
// no mapping found
args.push(undefined);
}
}
}
return new (Function.prototype.bind.apply(TargetClass, args));
},
inject: function (target, isParent) {
if (this.parent) {
this.parent.inject(target, true);
}
for (var name in this.mappings) {
var vo = this.getMappingVo(name);
if (target.hasOwnProperty(vo.prop)) {
var val = this.getInjectedValue(vo, name);
target[name] = val;
}
}
if (typeof target.postConstruct === 'function' && !isParent) {
target.postConstruct();
}
return this;
},
getInjectedValue: function(vo, name) {
var val = vo.value;
var injectee;
if (vo.cl) {
var params = infuse.getConstructorParams(vo.cl);
if (vo.singleton) {
if (!vo.value) {
validateConstructorInjectionLoop(name, vo.cl);
vo.value = this.instantiate(vo.cl);
injectee = vo.value;
}
val = vo.value;
}
else {
validateConstructorInjectionLoop(name, vo.cl);
val = this.instantiate(vo.cl);
injectee = val;
}
}
if (injectee) {
validatePropertyInjectionLoop(name, injectee);
this.inject(injectee);
}
return val;
},
createInstance: function() {
var instance = this.instantiate.apply(this, arguments);
this.inject(instance);
return instance;
},
getValueFromClass: function(cl) {
for (var name in this.mappings) {
var vo = this.mappings[name];
if (vo.cl == cl) {
if (vo.singleton) {
if (!vo.value) vo.value = this.createInstance.apply(this, arguments);
return vo.value;
}
else {
return this.createInstance.apply(this, arguments);
}
}
}
if (this.parent) {
return this.parent.getValueFromClass.apply(this.parent, arguments);
} else {
throw new Error(infuse.InjectorError.NO_MAPPING_FOUND);
}
},
dispose: function() {
this.mappings = {};
}
};
if (!Function.prototype.bind) {
Function.prototype.bind = function bind(that) {
var target = this;
if (typeof target != "function") {
throw new Error("Error, you must bind a function.");
}
var args = Array.prototype.slice.call(arguments, 1); // for normal call
var bound = function () {
if (this instanceof bound) {
var F = function(){};
F.prototype = target.prototype;
var self = new F;
var result = target.apply(
self,
args.concat(Array.prototype.slice.call(arguments))
);
if (Object(result) === result) {
return result;
}
return self;
} else {
return target.apply(
that,
args.concat(Array.prototype.slice.call(arguments))
);
}
};
return bound;
};
}
// register for AMD module
if (typeof define === 'function' && define.amd) {
define("infuse", infuse);
}
// export for node.js
if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
exports = module.exports = infuse;
}
exports = infuse;
}
})(this['infuse'] = this['infuse'] || {});
/*
Copyright (c) | 2012 | 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
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
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 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
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.
*/
;(function (soma, undefined) {
"use strict";
soma.events = {};
soma.events.version = "0.5.2";
if (!Function.prototype.bind) {
Function.prototype.bind = function bind(that) {
var target = this;
if (typeof target != "function") {
throw new Error("Error, you must bind a function.");
}
var args = Array.prototype.slice.call(arguments, 1); // for normal call
var bound = function () {
if (this instanceof bound) {
var F = function(){};
F.prototype = target.prototype;
var self = new F;
var result = target.apply(
self,
args.concat(Array.prototype.slice.call(arguments))
);
if (Object(result) === result) {
return result;
}
return self;
} else {
return target.apply(
that,
args.concat(Array.prototype.slice.call(arguments))
);
}
};
return bound;
};
};
soma.Event = function (type, params, bubbles, cancelable) {
var e = soma.Event.createGenericEvent(type, bubbles, cancelable);
if (params !== null && params !== undefined) {
e.params = params;
}
e.isCloned = false;
e.clone = this.clone.bind(e);
e.isIE9 = this.isIE9;
e.isDefaultPrevented = this.isDefaultPrevented;
if (this.isIE9() || !e.preventDefault || (e.getDefaultPrevented === undefined && e.defaultPrevented === undefined )) {
e.preventDefault = this.preventDefault.bind(e);
}
if (this.isIE9()) e.IE9PreventDefault = false;
return e;
};
soma.Event.prototype.clone = function () {
var e = soma.Event.createGenericEvent(this.type, this.bubbles, this.cancelable);
e.params = this.params;
e.isCloned = true;
e.clone = this.clone;
e.isDefaultPrevented = this.isDefaultPrevented;
e.isIE9 = this.isIE9;
if (this.isIE9()) e.IE9PreventDefault = this.IE9PreventDefault;
return e;
};
soma.Event.prototype.preventDefault = function () {
if (!this.cancelable) return false;
this.defaultPrevented = true;
if (this.isIE9()) this.IE9PreventDefault = true;
return this;
};
soma.Event.prototype.isDefaultPrevented = function () {
if (!this.cancelable) return false;
if (this.isIE9()) {
return this.IE9PreventDefault;
}
if (this.defaultPrevented !== undefined) {
return this.defaultPrevented;
} else if (this.getDefaultPrevented !== undefined) {
return this.getDefaultPrevented();
}
return false;
};
soma.Event.createGenericEvent = function (type, bubbles, cancelable) {
var event;
bubbles = bubbles !== undefined ? bubbles : true;
if (typeof document === "object" && document.createEvent) {
event = document.createEvent("Event");
event.initEvent(type, !!bubbles, !!cancelable);
} else if (typeof document === "object" && document.createEventObject) {
event = document.createEventObject();
event.type = type;
event.bubbles = !!bubbles;
event.cancelable = !!cancelable;
} else {
event = new EventObject(type, !!bubbles, !!cancelable);
}
return event;
};
soma.Event.prototype.isIE9 = function() {
if (typeof document !== "object") return false;
return document.body.style.scrollbar3dLightColor !== undefined && document.body.style.opacity !== undefined;
};
soma.Event.prototype.toString = function() {
return "[soma.Event]";
};
var EventObject = function(type, bubbles, cancelable) {
this.type = type;
this.bubbles = !!bubbles;
this.cancelable = !!cancelable;
this.defaultPrevented = false;
this.currentTarget = null;
this.target = null;
};
soma.EventDispatcher = function () {
this.listeners = [];
};
soma.EventDispatcher.prototype.addEventListener = function(type, listener, priority) {
if (!this.listeners || !type || !listener) return;
if (isNaN(priority)) priority = 0;
for (var i=0; i<this.listeners.length; i++) {
var eventObj = this.listeners[i];
if (eventObj.type === type && eventObj.listener === listener) {
return;
}
}
this.listeners.push({type: type, listener: listener, priority: priority, scope:this});
};
soma.EventDispatcher.prototype.removeEventListener = function(type, listener) {
if (!this.listeners || !type || !listener) return;
var i = this.listeners.length;
while(i-- > 0) {
var eventObj = this.listeners[i];
if (eventObj.type === type && eventObj.listener === listener) {
this.listeners.splice(i, 1);
}
}
};
soma.EventDispatcher.prototype.hasEventListener = function(type) {
if (!this.listeners || !type) return false;
var i = 0;
var l = this.listeners.length;
for (; i < l; ++i) {
var eventObj = this.listeners[i];
if (eventObj.type === type) {
return true;
}
}
return false;
};
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.");
var events = [];
var i;
for (i = 0; i < this.listeners.length; i++) {
var eventObj = this.listeners[i];
if (eventObj.type === event.type) {
events.push(eventObj);
}
}
events.sort(function(a, b) {
return b.priority - a.priority;
});
for (i = 0; i < events.length; i++) {
events[i].listener.apply((event.srcElement) ? event.srcElement : event.currentTarget, [event]);
}
return !event.isDefaultPrevented();
};
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.");
var event = new soma.Event(type, params, bubbles, cancelable);
this.dispatchEvent(event);
return event;
};
soma.EventDispatcher.prototype.dispose = function() {
this.listeners = null;
};
soma.EventDispatcher.prototype.toString = function() {
return "[soma.EventDispatcher]";
};
// register for AMD module
if (typeof define === 'function' && define.amd) {
define("soma-events", soma);
};
// export for node.js
if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
exports = module.exports = soma;
}
exports = soma;
};
})(this['soma'] = this['soma'] || {});
;(function (soma, infuse, undefined) {
'use strict';
soma.version = "2.0.0";
soma.applyProperties = function(target, extension, bindToExtension, list) {
if (Object.prototype.toString.apply(list) === '[object Array]') {
for (var i = 0, l = list.length; i < l; i++) {
if (target[list[i]] === undefined || target[list[i]] === null) {
if (bindToExtension && typeof extension[list[i]] === 'function') {
target[list[i]] = extension[list[i]].bind(extension);
}
else {
target[list[i]] = extension[list[i]];
}
}
}
}
else {
for (var prop in extension) {
if (bindToExtension && typeof extension[prop] === 'function') {
target[prop] = extension[prop].bind(extension);
}
else {
target[prop] = extension[prop];
}
}
}
};
soma.augment = function (target, extension, list) {
if (!extension.prototype || !target.prototype) {
return;
}
if (Object.prototype.toString.apply(list) === '[object Array]') {
for (var i = 0, l = list.length; i < l; i++) {
if (!target.prototype[list[i]]) {
target.prototype[list[i]] = extension.prototype[list[i]];
}
}
}
else {
for (var prop in extension.prototype) {
if (!target.prototype[prop]) {
target.prototype[prop] = extension.prototype[prop];
}
}
}
};
soma.inherit = function (parent, obj) {
var Subclass;
if (obj && obj.hasOwnProperty('constructor')) {
// use constructor if defined
Subclass = obj.constructor;
} else {
// call the super constructor
Subclass = function () {
return parent.apply(this, arguments);
};
}
// set the prototype chain to inherit from the parent without calling parent's constructor
var Chain = function(){};
Chain.prototype = parent.prototype;
Subclass.prototype = new Chain();
// add obj properties
if (obj) soma.applyProperties(Subclass.prototype, obj);
// point constructor to the Subclass
Subclass.prototype.constructor = Subclass;
// set super class reference
Subclass.parent = parent.prototype;
// add extend shortcut
Subclass.extend = function (obj) {
return soma.inherit(Subclass, obj);
};
return Subclass;
};
soma.extend = function (obj) {
return soma.inherit(function () {
}, obj);
};
// plugins
var plugins = [];
soma.plugins = soma.plugins || {};
soma.plugins.add = function(plugin) {
plugins.push(plugin);
};
soma.plugins.remove = function(plugin) {
for (var i = plugins.length-1, l = 0; i >= l; i--) {
if (plugin === plugins[i]) {
plugins.splice(i, 1);
}
}
};
// framework
soma.Application = soma.extend({
constructor: function() {
setup.bind(this)();
this.init();
this.start();
function setup() {
// injector
this.injector = new infuse.Injector(this.dispatcher);
// dispatcher
this.dispatcher = new soma.EventDispatcher();
// mapping
this.injector.mapValue('injector', this.injector);
this.injector.mapValue('instance', this);
this.injector.mapValue('dispatcher', this.dispatcher);
// mediator
this.injector.mapClass('mediators', Mediators, true);
this.mediators = this.injector.getValue('mediators');
// commands
this.injector.mapClass('commands', Commands, true);
this.commands = this.injector.getValue('commands');
// plugins
for (var i = 0, l = plugins.length; i < l; i++) {
this.createPlugin(plugins[i]);
}
}
},
createPlugin: function() {
if (arguments.length == 0 || !arguments[0]) {
throw new Error("Error creating a plugin, plugin class is missing.");
}
var params = infuse.getConstructorParams(arguments[0]);
var args = [arguments[0]];
for (var i=0; i<params.length; i++) {
if (this.injector.hasMapping(params[i]) || this.injector.hasInheritedMapping(params[i])) {
args.push(this.injector.getValue(params[i]));
}
}
for (var j=1; j<arguments.length; j++) {
args.push(arguments[j]);
}
return this.injector.createInstance.apply(this.injector, args);
},
init: function() {
},
start: function() {
},
dispose: function() {
// mapping
if (this.injector) {
this.injector.removeMapping('injector');
this.injector.removeMapping('dispatcher');
this.injector.removeMapping('mediators');
this.injector.removeMapping('commands');
this.injector.removeMapping('instance');
}
// variables
if (this.injector) {
this.injector.dispose();
}
if (this.dispatcher) {
this.dispatcher.dispose();
}
if (this.mediators) {
this.mediators.dispose();
}
if (this.commands) {
this.commands.dispose();
}
this.injector = undefined;
this.dispatcher = undefined;
this.mediators = undefined;
this.commands = undefined;
this.instance = undefined;
}
});
var Mediators = soma.extend({
constructor: function() {
this.injector = null;
this.dispatcher = null;
},
create: function(cl, target) {
if (!cl || typeof cl !== "function") {
throw new Error("Error creating a mediator, the first parameter must be a function.");
}
if (target === undefined || target === null) {
throw new Error("Error creating a mediator, the second parameter cannot be undefined or null.");
}
var targets = [];
var meds = [];
if (target.length > 0) {
targets = target;
}
else {
targets.push(target);
}
for (var i= 0, l=targets.length; i<l; i++) {
var injector = this.injector.createChild();
injector.mapValue("target", targets[i]);
//var mediator = injector.createInstance.apply(this.injector, params);
var mediator = injector.createInstance(cl);
// soma.applyProperties(mediator, this.dispatcher, true, ['dispatch', 'dispatchEvent', 'addEventListener', 'removeEventListener', 'hasEventListener']);
if (targets.length === 1) return mediator;
meds.push(mediator);
}
return meds;
},
dispose: function() {
this.injector = undefined;
this.dispatcher = undefined;
}
});
var Commands = soma.extend({
constructor: function() {
this.boundHandler = this.handler.bind(this);
this.dispatcher = null;
this.injector = null;
this.list = {};
},
has: function(commandName) {
return this.list[commandName] !== null && this.list[commandName] !== undefined;
},
get: function(commandName) {
if (this.has(commandName)) {
return this.list[commandName];
}
return undefined;
},
getAll: function() {
var copy = {};
for (var cmd in this.list) {
copy[cmd] = this.list[cmd];
}
return copy;
},
add: function(commandName, command) {
if (typeof commandName !== 'string') {
throw new Error("Error adding a command, the first parameter must be a string.");
}
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.");
}
if (this.has(commandName)) {
throw new Error("Error adding a command with the name: \"" + commandName + "\", already registered.");
}
this.list[ commandName ] = command;
this.addInterceptor(commandName);
},
remove: function(commandName) {
if (!this.has(commandName)) {
return;
}
this.list[commandName] = undefined;
delete this.list[commandName];
this.removeInterceptor(commandName);
},
addInterceptor: function(commandName) {
this.dispatcher.addEventListener(commandName, this.boundHandler, -Number.MAX_VALUE);
},
removeInterceptor: function(commandName) {
this.dispatcher.removeEventListener(commandName, this.boundHandler);
},
handler: function(event) {
if (event.isDefaultPrevented && !event.isDefaultPrevented()) {
this.executeCommand(event);
}
},
executeCommand: function(event) {
var commandName = event.type;
if (this.has(commandName)) {
var command = this.injector.createInstance(this.list[commandName]);
if (!command.hasOwnProperty('execute') && command['execute'] === 'function') {
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);
}
},
dispose: function() {
for (var cmd in this.list) {
this.remove(cmd);
}
this.boundHandler = undefined;
this.dispatcher = undefined;
this.injector = undefined;
this.list = undefined;
}
});
// event extend utils
soma.EventDispatcher.extend = function (obj) {
return soma.inherit(soma.EventDispatcher, obj);
};
soma.Event.extend = function (obj) {
return soma.inherit(soma.Event, obj);
};
infuse.Injector.extend = function(obj) {
return soma.inherit(infuse.Injector, obj);
};
// register for AMD module
if (typeof define === 'function' && define.amd) {
define("soma", soma);
}
// export for node.js
if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
exports = module.exports = soma;
}
exports = soma;
}
})(this['soma'] = this['soma'] || {}, this['infuse']);
\ No newline at end of file
html,
body {
margin: 0;
padding: 0;
}
button {
margin: 0;
padding: 0;
border: 0;
background: none;
font-size: 100%;
vertical-align: baseline;
font-family: inherit;
color: inherit;
-webkit-appearance: none;
/*-moz-appearance: none;*/
-ms-appearance: none;
-o-appearance: none;
appearance: none;
}
body {
font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif;
line-height: 1.4em;
background: #eaeaea url('bg.png');
color: #4d4d4d;
width: 550px;
margin: 0 auto;
-webkit-font-smoothing: antialiased;
-moz-font-smoothing: antialiased;
-ms-font-smoothing: antialiased;
-o-font-smoothing: antialiased;
font-smoothing: antialiased;
}
#todoapp {
background: #fff;
background: rgba(255, 255, 255, 0.9);
margin: 130px 0 40px 0;
border: 1px solid #ccc;
position: relative;
border-top-left-radius: 2px;
border-top-right-radius: 2px;
box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.2),
0 25px 50px 0 rgba(0, 0, 0, 0.15);
}
#todoapp:before {
content: '';
border-left: 1px solid #f5d6d6;
border-right: 1px solid #f5d6d6;
width: 2px;
position: absolute;
top: 0;
left: 40px;
height: 100%;
}
#todoapp input::-webkit-input-placeholder {
font-style: italic;
}
#todoapp input:-moz-placeholder {
font-style: italic;
color: #a9a9a9;
}
#todoapp h1 {
position: absolute;
top: -120px;
width: 100%;
font-size: 70px;
font-weight: bold;
text-align: center;
color: #b3b3b3;
color: rgba(255, 255, 255, 0.3);
text-shadow: -1px -1px rgba(0, 0, 0, 0.2);
-webkit-text-rendering: optimizeLegibility;
-moz-text-rendering: optimizeLegibility;
-ms-text-rendering: optimizeLegibility;
-o-text-rendering: optimizeLegibility;
text-rendering: optimizeLegibility;
}
#header {
padding-top: 15px;
border-radius: inherit;
}
#header:before {
content: '';
position: absolute;
top: 0;
right: 0;
left: 0;
height: 15px;
z-index: 2;
border-bottom: 1px solid #6c615c;
background: #8d7d77;
background: -webkit-gradient(linear, left top, left bottom, from(rgba(132, 110, 100, 0.8)),to(rgba(101, 84, 76, 0.8)));
background: -webkit-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8));
background: -moz-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8));
background: -o-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8));
background: -ms-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8));
background: linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8));
filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#9d8b83', EndColorStr='#847670');
border-top-left-radius: 1px;
border-top-right-radius: 1px;
}
#new-todo,
.edit {
position: relative;
margin: 0;
width: 100%;
font-size: 24px;
font-family: inherit;
line-height: 1.4em;
border: 0;
outline: none;
color: inherit;
padding: 6px;
border: 1px solid #999;
box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2);
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
-ms-box-sizing: border-box;
-o-box-sizing: border-box;
box-sizing: border-box;
-webkit-font-smoothing: antialiased;
-moz-font-smoothing: antialiased;
-ms-font-smoothing: antialiased;
-o-font-smoothing: antialiased;
font-smoothing: antialiased;
}
#new-todo {
padding: 16px 16px 16px 60px;
border: none;
background: rgba(0, 0, 0, 0.02);
z-index: 2;
box-shadow: none;
}
#main {
position: relative;
z-index: 2;
border-top: 1px dotted #adadad;
}
label[for='toggle-all'] {
display: none;
}
#toggle-all {
position: absolute;
top: -42px;
left: -4px;
width: 40px;
text-align: center;
border: none; /* Mobile Safari */
}
#toggle-all:before {
content: '»';
font-size: 28px;
color: #d9d9d9;
padding: 0 25px 7px;
}
#toggle-all:checked:before {
color: #737373;
}
#todo-list {
margin: 0;
padding: 0;
list-style: none;
}
#todo-list li {
position: relative;
font-size: 24px;
border-bottom: 1px dotted #ccc;
}
#todo-list li:last-child {
border-bottom: none;
}
#todo-list li.editing {
border-bottom: none;
padding: 0;
}
#todo-list li.editing .edit {
display: block;
width: 506px;
padding: 13px 17px 12px 17px;
margin: 0 0 0 43px;
}
#todo-list li.editing .view {
display: none;
}
#todo-list li .toggle {
text-align: center;
width: 40px;
/* auto, since non-WebKit browsers doesn't support input styling */
height: auto;
position: absolute;
top: 0;
bottom: 0;
margin: auto 0;
border: none; /* Mobile Safari */
-webkit-appearance: none;
/*-moz-appearance: none;*/
-ms-appearance: none;
-o-appearance: none;
appearance: none;
}
#todo-list li .toggle:after {
content: '✔';
line-height: 43px; /* 40 + a couple of pixels visual adjustment */
font-size: 20px;
color: #d9d9d9;
text-shadow: 0 -1px 0 #bfbfbf;
}
#todo-list li .toggle:checked:after {
color: #85ada7;
text-shadow: 0 1px 0 #669991;
bottom: 1px;
position: relative;
}
#todo-list li label {
word-break: break-word;
padding: 15px;
margin-left: 45px;
display: block;
line-height: 1.2;
-webkit-transition: color 0.4s;
-moz-transition: color 0.4s;
-ms-transition: color 0.4s;
-o-transition: color 0.4s;
transition: color 0.4s;
}
#todo-list li.completed label {
color: #a9a9a9;
text-decoration: line-through;
}
#todo-list li .destroy {
display: none;
position: absolute;
top: 0;
right: 10px;
bottom: 0;
width: 40px;
height: 40px;
margin: auto 0;
font-size: 22px;
color: #a88a8a;
-webkit-transition: all 0.2s;
-moz-transition: all 0.2s;
-ms-transition: all 0.2s;
-o-transition: all 0.2s;
transition: all 0.2s;
}
#todo-list li .destroy:hover {
text-shadow: 0 0 1px #000,
0 0 10px rgba(199, 107, 107, 0.8);
-webkit-transform: scale(1.3);
-moz-transform: scale(1.3);
-ms-transform: scale(1.3);
-o-transform: scale(1.3);
transform: scale(1.3);
}
#todo-list li .destroy:after {
content: '✖';
}
#todo-list li:hover .destroy {
display: block;
}
#todo-list li .edit {
display: none;
}
#todo-list li.editing:last-child {
margin-bottom: -1px;
}
#footer {
color: #777;
padding: 0 15px;
position: absolute;
right: 0;
bottom: -31px;
left: 0;
height: 20px;
z-index: 1;
text-align: center;
}
#footer:before {
content: '';
position: absolute;
right: 0;
bottom: 31px;
left: 0;
height: 50px;
z-index: -1;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.3),
0 6px 0 -3px rgba(255, 255, 255, 0.8),
0 7px 1px -3px rgba(0, 0, 0, 0.3),
0 43px 0 -6px rgba(255, 255, 255, 0.8),
0 44px 2px -6px rgba(0, 0, 0, 0.2);
}
#todo-count {
float: left;
text-align: left;
}
#filters {
margin: 0;
padding: 0;
list-style: none;
position: absolute;
right: 0;
left: 0;
}
#filters li {
display: inline;
}
#filters li a {
color: #83756f;
margin: 2px;
text-decoration: none;
}
#filters li a.selected {
font-weight: bold;
}
#clear-completed {
float: right;
position: relative;
line-height: 20px;
text-decoration: none;
background: rgba(0, 0, 0, 0.1);
font-size: 11px;
padding: 0 10px;
border-radius: 3px;
box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.2);
}
#clear-completed:hover {
background: rgba(0, 0, 0, 0.15);
box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.3);
}
#info {
margin: 65px auto 0;
color: #a6a6a6;
font-size: 12px;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.7);
text-align: center;
}
#info a {
color: inherit;
}
/*
Hack to remove background from Mobile Safari.
Can't use it globally since it destroys checkboxes in Firefox and Opera
*/
@media screen and (-webkit-min-device-pixel-ratio:0) {
#toggle-all,
#todo-list li .toggle {
background: none;
}
#todo-list li .toggle {
height: 40px;
}
#toggle-all {
top: -56px;
left: -15px;
width: 65px;
height: 41px;
-webkit-transform: rotate(90deg);
transform: rotate(90deg);
-webkit-appearance: none;
appearance: none;
}
}
.hidden{
display:none;
}
hr {
margin: 20px 0;
border: 0;
border-top: 1px dashed #C5C5C5;
border-bottom: 1px dashed #F7F7F7;
}
.learn a {
font-weight: normal;
text-decoration: none;
color: #b83f45;
}
.learn a:hover {
text-decoration: underline;
color: #787e7e;
}
.learn h3,
.learn h4,
.learn h5 {
margin: 10px 0;
font-weight: 500;
line-height: 1.2;
color: #000;
}
.learn h3 {
font-size: 24px;
}
.learn h4 {
font-size: 18px;
}
.learn h5 {
margin-bottom: 0;
font-size: 14px;
}
.learn ul {
padding: 0;
margin: 0 0 30px 25px;
}
.learn li {
line-height: 20px;
}
.learn p {
font-size: 15px;
font-weight: 300;
line-height: 1.3;
margin-top: 0;
margin-bottom: 0;
}
.quote {
border: none;
margin: 20px 0 60px 0;
}
.quote p {
font-style: italic;
}
.quote p:before {
content: '“';
font-size: 50px;
opacity: .15;
position: absolute;
top: -20px;
left: 3px;
}
.quote p:after {
content: '”';
font-size: 50px;
opacity: .15;
position: absolute;
bottom: -42px;
right: 3px;
}
.quote footer {
position: absolute;
bottom: -40px;
right: 0;
}
.quote footer img {
border-radius: 3px;
}
.quote footer a {
margin-left: 5px;
vertical-align: middle;
}
.speech-bubble {
position: relative;
padding: 10px;
background: rgba(0, 0, 0, .04);
border-radius: 5px;
}
.speech-bubble:after {
content: '';
position: absolute;
top: 100%;
right: 30px;
border: 13px solid transparent;
border-top-color: rgba(0, 0, 0, .04);
}
/**body*/.learn-bar > .learn {
position: absolute;
width: 272px;
top: 8px;
left: -300px;
padding: 10px;
border-radius: 5px;
background-color: rgba(255, 255, 255, .6);
transition-property: left;
transition-duration: 500ms;
}
@media (min-width: 899px) {
/**body*/.learn-bar {
width: auto;
margin: 0 0 0 300px;
}
/**body*/.learn-bar > .learn {
left: 8px;
}
/**body*/.learn-bar #todoapp {
width: 550px;
margin: 130px auto 40px auto;
}
}
(function () {
'use strict';
// Underscore's Template Module
// Courtesy of underscorejs.org
var _ = (function (_) {
_.defaults = function (object) {
if (!object) {
return object;
}
for (var argsIndex = 1, argsLength = arguments.length; argsIndex < argsLength; argsIndex++) {
var iterable = arguments[argsIndex];
if (iterable) {
for (var key in iterable) {
if (object[key] == null) {
object[key] = iterable[key];
}
}
}
}
return object;
}
// By default, Underscore uses ERB-style template delimiters, change the
// following template settings to use alternative delimiters.
_.templateSettings = {
evaluate : /<%([\s\S]+?)%>/g,
interpolate : /<%=([\s\S]+?)%>/g,
escape : /<%-([\s\S]+?)%>/g
};
// When customizing `templateSettings`, if you don't want to define an
// interpolation, evaluation or escaping regex, we need one that is
// guaranteed not to match.
var noMatch = /(.)^/;
// Certain characters need to be escaped so that they can be put into a
// string literal.
var escapes = {
"'": "'",
'\\': '\\',
'\r': 'r',
'\n': 'n',
'\t': 't',
'\u2028': 'u2028',
'\u2029': 'u2029'
};
var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g;
// JavaScript micro-templating, similar to John Resig's implementation.
// Underscore templating handles arbitrary delimiters, preserves whitespace,
// and correctly escapes quotes within interpolated code.
_.template = function(text, data, settings) {
var render;
settings = _.defaults({}, settings, _.templateSettings);
// Combine delimiters into one regular expression via alternation.
var matcher = new RegExp([
(settings.escape || noMatch).source,
(settings.interpolate || noMatch).source,
(settings.evaluate || noMatch).source
].join('|') + '|$', 'g');
// Compile the template source, escaping string literals appropriately.
var index = 0;
var source = "__p+='";
text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
source += text.slice(index, offset)
.replace(escaper, function(match) { return '\\' + escapes[match]; });
if (escape) {
source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
}
if (interpolate) {
source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
}
if (evaluate) {
source += "';\n" + evaluate + "\n__p+='";
}
index = offset + match.length;
return match;
});
source += "';\n";
// If a variable is not specified, place data values in local scope.
if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';
source = "var __t,__p='',__j=Array.prototype.join," +
"print=function(){__p+=__j.call(arguments,'');};\n" +
source + "return __p;\n";
try {
render = new Function(settings.variable || 'obj', '_', source);
} catch (e) {
e.source = source;
throw e;
}
if (data) return render(data, _);
var template = function(data) {
return render.call(this, data, _);
};
// Provide the compiled function source as a convenience for precompilation.
template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}';
return template;
};
return _;
})({});
if (location.hostname === 'todomvc.com') {
window._gaq = [['_setAccount','UA-31081062-1'],['_trackPageview']];(function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];g.src='//www.google-analytics.com/ga.js';s.parentNode.insertBefore(g,s)}(document,'script'));
}
function redirect() {
if (location.hostname === 'tastejs.github.io') {
location.href = location.href.replace('tastejs.github.io/todomvc', 'todomvc.com');
}
}
function findRoot() {
var base;
[/labs/, /\w*-examples/].forEach(function (href) {
var match = location.href.match(href);
if (!base && match) {
base = location.href.indexOf(match);
}
});
return location.href.substr(0, base);
}
function getFile(file, callback) {
var xhr = new XMLHttpRequest();
xhr.open('GET', findRoot() + file, true);
xhr.send();
xhr.onload = function () {
if (xhr.status === 200 && callback) {
callback(xhr.responseText);
}
};
}
function Learn(learnJSON, config) {
if (!(this instanceof Learn)) {
return new Learn(learnJSON, config);
}
var template, framework;
if (typeof learnJSON !== 'object') {
try {
learnJSON = JSON.parse(learnJSON);
} catch (e) {
return;
}
}
if (config) {
template = config.template;
framework = config.framework;
}
if (!template && learnJSON.templates) {
template = learnJSON.templates.todomvc;
}
if (!framework && document.querySelector('[data-framework]')) {
framework = document.querySelector('[data-framework]').getAttribute('data-framework');
}
if (template && learnJSON[framework]) {
this.frameworkJSON = learnJSON[framework];
this.template = template;
this.append();
}
}
Learn.prototype.append = function () {
var aside = document.createElement('aside');
aside.innerHTML = _.template(this.template, this.frameworkJSON);
aside.className = 'learn';
// Localize demo links
var demoLinks = aside.querySelectorAll('.demo-link');
Array.prototype.forEach.call(demoLinks, function (demoLink) {
demoLink.setAttribute('href', findRoot() + demoLink.getAttribute('href'));
});
document.body.className = (document.body.className + ' learn-bar').trim();
document.body.insertAdjacentHTML('afterBegin', aside.outerHTML);
};
redirect();
getFile('learn.json', Learn);
})();
{
"name": "todomvc-common",
"version": "0.1.7",
"gitHead": "42348a8146fe0be847b93cd98664813fbae62be9",
"_id": "todomvc-common@0.1.7",
"readme": "ERROR: No README.md file found!",
"description": "ERROR: No README.md file found!",
"repository": {
"type": "git",
"url": "git://github.com/tastejs/todomvc-common.git"
}
}
\ No newline at end of file
.data-cloak {
display: none;
}
\ No newline at end of file
<!doctype html>
<html lang="en" data-framework="somajs">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>soma.js • TodoMVC</title>
<link rel="stylesheet" href="components/todomvc-common/base.css">
<link rel="stylesheet" href="css/app.css">
</head>
<body>
<section id="todoapp">
<!-- HEADER VIEW, template: /views/header.js (todo.HeaderView) -->
<header id="header">
<h1>todos</h1>
<input id="new-todo" placeholder="What needs to be done?" autofocus data-keypress="add()" data-blur="clear()">
</header>
<!-- MAIN VIEW, template: /views/main.js (todo.MainView) -->
<section id="main" data-show="{{isVisible}}">
<input id="toggle-all" type="checkbox" data-click="toggleAll()" data-checked="{{allCompleted}}">
<label for="toggle-all">Mark all as complete</label>
<ul id="todo-list" class="data-cloak">
<li data-repeat="todo in items" class="{{completedClass(todo.completed)}} {{todo.editing}}">
<div class="view">
<input class="toggle" type="checkbox" data-checked="{{todo.completed}}" data-click="toggle(todo)">
<label data-dblclick="edit(todo)">{{todo.title}}</label>
<button class="destroy" data-click="remove(todo)"></button>
</div>
<input class="edit" value="{{todo.title}}" data-keypress="update(todo)" data-blur="update(todo)">
</li>
</ul>
</section>
<!-- FOOTER VIEW, template: /views/footer.js (todo.FooterView) -->
<footer id="footer" class="data-cloak" data-show="{{footerVisible}}">
<span id="todo-count"><strong>{{active}}</strong> {{itemLabel}} left</span>
<ul id="filters">
<li>
<a class="{{highlightFilter('')}}" href="#/">All</a>
</li>
<li>
<a class="{{highlightFilter('active')}}" href="#/active">Active</a>
</li>
<li>
<a class="{{highlightFilter('completed')}}" href="#/completed">Completed</a>
</li>
</ul>
<button id="clear-completed" data-show="{{clearCompletedVisible()}}" data-click="clearCompleted()">Clear completed ({{completed}})</button>
</footer>
</section>
<footer id="info">
<p>Double-click to edit a todo</p>
<p>Created by <a href="http://soundstep.com">Romuald Quantin</a> (<a href="http://somajs.github.io/somajs/">soma.js</a>)</p>
<p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
</footer>
<!-- TODO APP -->
<script src="components/todomvc-common/base.js"></script>
<script data-main="js/app" src="components/requirejs/require.js"></script>
</body>
</html>
/*global requirejs:false */
(function (requirejs) {
'use strict';
requirejs.config({
baseUrl: './js',
paths: {
// libs
soma: '../components/soma.js/build/soma',
template: '../components/soma-template/build/soma-template',
director: '../components/director/build/director',
// app paths
views: './views',
models: './models'
},
shim: {
'template': {
deps: ['soma']
},
'director': {
exports: 'Router'
}
}
});
requirejs([
'soma',
'template',
'models/todos',
'models/router',
'views/header',
'views/main',
'views/footer'
], function (soma, template, TodoModel, RouterModel, HeaderView, MainView, FooterView) {
var TodoApp = soma.Application.extend({
init: function () {
// mapping rules so the model and router can be injected
this.injector.mapClass('model', TodoModel, true);
this.injector.mapClass('router', RouterModel, true);
// create templates for DOM Elements (optional soma-template plugin)
this.createTemplate(HeaderView, document.getElementById('header'));
this.createTemplate(MainView, document.getElementById('main'));
this.createTemplate(FooterView, document.getElementById('footer'));
},
start: function () {
// dispatch a custom event to render the templates
this.dispatcher.dispatch('render');
}
});
// create the application
new TodoApp();
});
})(requirejs);
/*global define:false */
(function () {
'use strict';
define(['director'], function (Router) {
var RouterModel = function (dispatcher) {
// create the router (director.js)
var router = new Router().init();
// dispatch a custom event to render the template on a route change
router.on(/.*/, function () {
dispatcher.dispatch('render');
});
return {
getRoute: function () {
return router.getRoute()[0];
}
};
};
return RouterModel;
});
})();
/*global define:false */
(function () {
'use strict';
define([], function () {
var TodoModel = function () {
var storeKey = 'todos-somajs';
return {
get: function () {
// get the data from the local storage
return JSON.parse(localStorage.getItem(storeKey) || '[]');
},
set: function (items) {
// set the data to the local storage
localStorage.setItem(storeKey, JSON.stringify(items));
},
getActive: function () {
// returns items that are not completed
return this.get().filter(function (item) {
return !item.completed;
}).length;
}
};
};
return TodoModel;
});
})();
/*global define:false */
(function () {
'use strict';
define([], function () {
var FooterView = function (scope, template, model, router, dispatcher) {
// get data from the injected model
var items = model.get();
// template function: returns a css class for the current filter (all/active/completed)
scope.highlightFilter = function (filter) {
var route = router.getRoute();
return route === filter ? 'selected' : '';
};
// template function: returns the number of completed items
scope.clearCompleted = function () {
items = items.filter(function (item) {
return !item.completed;
});
update();
};
// save the changes to the model and dispatch a custom event to render the templates
function update() {
model.set(items);
dispatcher.dispatch('render');
}
// listen to a custom event to render the footer view
dispatcher.addEventListener('render', function () {
items = model.get();
scope.active = model.getActive();
scope.completed = items.length - scope.active;
scope.itemLabel = scope.active === 1 ? 'item' : 'items';
scope.footerVisible = items.length > 0 ? true : false;
scope.clearCompletedVisible = scope.completed > 0 ? true : false;
template.render();
});
};
return FooterView;
});
})();
/*global define:false */
(function () {
'use strict';
define([], function () {
var ENTER_KEY = 13;
var HeaderView = function (scope, template, model, dispatcher) {
// get data from the injected model
var items = model.get();
// template function: add a new item on an enter key press
scope.add = function (event) {
var value = event.currentTarget.value.trim();
if (event.which === ENTER_KEY && value !== '') {
items.push({
title: value,
completed: false
});
event.currentTarget.value = '';
update();
}
};
// template function: remove text from the input (used on blur event)
scope.clear = function (event) {
event.currentTarget.value = '';
};
// save the changes to the model and dispatch a custom event to render the templates
function update() {
model.set(items);
dispatcher.dispatch('render');
}
// listen to a custom event to render the header view
dispatcher.addEventListener('render', function () {
items = model.get();
template.render();
});
};
return HeaderView;
});
})();
/*global define:false */
(function () {
'use strict';
define([], function () {
var ENTER_KEY = 13;
var MainView = function (scope, template, model, router, dispatcher) {
// get data from the injected model
var items = model.get();
// template function: returns an array of items to display
// can be different depending on the filter selected
scope.items = function () {
var filter = router.getRoute();
if (filter === '') {
return items;
}
return items.filter(function (item) {
return filter === 'active' ? !item.completed : item.completed;
});
};
// template function: set all items to either completed or not completed
scope.toggleAll = function (event) {
items.forEach(function (item) {
item.completed = event.currentTarget.checked;
});
update();
};
// template function: set 1 item to either completed or not completed
scope.toggle = function (event, item) {
item.completed = !item.completed;
update();
};
// template function: returns a css class depending if the item is completed or not completed
scope.completedClass = function (completed) {
return completed ? 'completed' : '';
};
// template function: removes an item
scope.remove = function (event, item) {
if (item) {
items.splice(items.indexOf(item), 1);
update();
}
};
// template function: edit an item (used on a double click event)
scope.edit = function (event, item) {
item.editing = 'editing';
template.render();
};
// template function: during edit mode, changes the value of an item after an enter key press
scope.update = function (event, item) {
var value = event.currentTarget.value.trim();
if (event.which === ENTER_KEY || event.type === 'blur') {
if (value) {
item.title = value;
}
else {
items.splice(items.indexOf(item), 1);
}
item.editing = '';
event.currentTarget.value = value;
update();
}
};
// save the changes to the model and dispatch a custom event to render the templates
function update() {
model.set(items);
dispatcher.dispatch('render');
}
// listen to a custom event to render the main view
dispatcher.addEventListener('render', function () {
items = model.get();
scope.active = model.getActive();
scope.isVisible = scope.items().length > 0 ? true : false;
scope.allCompleted = items.length > 0 && scope.active === 0 ? true : false;
template.render();
});
};
return MainView;
});
})();
# soma.js TodoMVC App
This is a todo app built using [soma.js](http://somajs.github.io/somajs/).
## Framework
soma.js is a framework created to build scalable and maintainable javascript applications.
The success to build large-scale applications will rely on smaller single-purposed parts of a larger system. soma.js provides tools to create a loosely-coupled architecture broken down into smaller pieces.
The different components of an application can be modules, models, views, plugins or widgets. soma.js will not dictate its own structure, the framework can be used as a model-view-controller framework, can be used to create independent modules, sandboxed widgets or anything that will fit a specific need.
For a system to grow and be maintainable, its parts must have a very limited knowledge of their surrounding. The components must not be tied to each other (Law of Demeter), in order to be re-usable, interchangeable and allow the system to scale up.
soma.js is a set of tools and design patterns to build a long term architecture that are decoupled and easily testable. The tools provided by the framework are dependency injection, observer pattern, mediator pattern, facade pattern, command pattern, OOP utilities and a DOM manipulation template engine as an optional plugin.
## Implementation
The application starts by instantiating the application main entry point (/js/app.js).
The application contains two models:
* A router model ([Director](https://github.com/flatiron/director)) to handle the routes: all/active/completed.
* A todo model saving the todo items to the local storage.
The todo app DOM element has been cut into 3 views:
* A header view handling the input that creates todo items.
* A main view displaying the list of todo items and the buttons to manage them.
* A footer view displaying information, the filters and the clear completed button.
The application instance creates 2 mapping rules: "router" and "model", so the two models can be injected in the templates seamlessly using their injection names.
The application instance also creates three templates that represent DOM Elements using the optional plugin [soma-template](http://soundstep.github.io/soma-template/) (web template engine).
Finally, the application dispatches a custom event "render", that all templates are listening to, to render the DOM with the right content.
## Running
Install the libraries needed using bower ([soma.js](http://somajs.github.io/somajs/), [soma-template](http://soundstep.github.io/soma-template/), [director](https://github.com/flatiron/director), [require.js](http://requirejs.org/docs/api.html)):
bower install
To run the app, spin up an HTTP server and visit
http://localhost/todomvc/labs/dependency-examples/somajs_require/
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