includejs
Version:
IncludeJS Resource Builder Tool
1,873 lines (1,597 loc) • 100 kB
JavaScript
;
(function(w) {
var helper = {
each: function(arr, fn) {
if (arr instanceof Array) {
for (var i = 0; i < arr.length; i++) {
fn(arr[i]);
}
return;
}
fn(arr);
},
extendProto: function(proto, x) {
var prototype;
if (x == null) return;
switch (typeof x) {
case 'function':
prototype = x.prototype;
break;
case 'object':
prototype = x;
break;
default:
return;
}
for (var key in prototype) {
proto[key] = prototype[key];
}
},
extendClass: function(_class, _base, _extends, original) {
if (typeof original !== 'object') return;
this.extendPrototype = original.__proto__ == null ? this.protoLess : this.proto;
this.extendPrototype(_class, _base, _extends, original);
},
proto: function(_class, _base, _extends, original) {
var prototype = original,
proto = original;
if (_extends != null) {
proto.__proto__ = {};
helper.each(_extends, function(x) {
helper.extendProto(proto.__proto__, x);
});
proto = proto.__proto__;
}
if (_base != null) {
proto.__proto__ = _base.prototype;
}
_class.prototype = prototype;
},
/** browser that doesnt support __proto__ */
protoLess: function(_class, _base, _extends, original) {
if (_base != null) {
var proto = {},
tmp = new Function;
tmp.prototype = _base.prototype;
_class.prototype = new tmp();
_class.constructor = _base;
}
helper.extendProto(_class.prototype, original);
if (_extends != null) {
helper.each(_extends, function(x){
helper.extendProto(_class.prototype, x);
});
}
}
}
w.Class = function(data) {
var _base = data.Base,
_extends = data.Extends,
_static = data.Static,
_construct = data.Construct,
_class = null;
if (_base != null) delete data.Base;
if (_extends != null) delete data.Extends;
if (_static != null) delete data.Static;
if (_construct != null) delete data.Construct;
if (_base == null && _extends == null) {
if (_construct == null) _class = function() {};
else _class = _construct;
if (_static != null) {
for (var key in _static) _class[key] = _static[key];
}
_class.prototype = data;
return _class;
}
_class = function() {
if (_extends != null){
var isarray = _extends instanceof Array,
length = isarray ? _extends.length : 1,
x = null;
for (var i = 0; x = isarray ? _extends[i] : _extends, isarray ? i < length : i < 1; i++) {
if (typeof x === 'function') x.apply(this, arguments);
}
}
if (_base != null) {
_base.apply(this, arguments);
}
if (_construct != null) {
var r = _construct.apply(this, arguments);
if (r != null) return r;
}
return this;
}
if (_static != null) for (var key in _static) _class[key] = _static[key];
helper.extendClass(_class, _base, _extends, data);
data = null;
_static = null;
return _class;
}
})(window);
;;
void
function(w, d) {
var cfg = {},
bin = {},
isWeb = !! (w.location && w.location.protocol && /^https?:/.test(w.location.protocol)),
handler = {},
regexp = {
name: new RegExp('\\{name\\}', 'g')
},
helper = { /** TODO: improve url handling*/
uri: {
getDir: function(url) {
var index = url.lastIndexOf('/');
return index == -1 ? '' : url.substring(index + 1, -index);
},
/** @obsolete */
resolveCurrent: function() {
var scripts = d.querySelectorAll('script');
return scripts[scripts.length - 1].getAttribute('src');
},
resolveUrl: function(url, parent) {
if (cfg.path && url[0] == '/') {
url = cfg.path + url.substring(1);
}
if (url[0] == '/') {
if (isWeb == false || cfg.lockedToFolder == true) return url.substring(1);
return url;
}
switch (url.substring(0, 4)) {
case 'file':
case 'http':
return url;
}
if (parent != null && parent.location != null) return parent.location + url;
return url;
}
},
extend: function(target, source) {
for (var key in source) target[key] = source[key];
return target;
},
/**
* @arg x :
* 1. string - URL to resource
* 2. array - URLs to resources
* 3. object - {route: x} - route defines the route template to resource,
* it must be set before in include.cfg.
* example:
* include.cfg('net','scripts/net/{name}.js')
* include.js({net: 'downloader'}) // -> will load scipts/net/downloader.js
* @arg namespace - route in case of resource url template, or namespace in case of LazyModule
*
* @arg fn - callback function, which receives namespace|route, url to resource and ?id in case of not relative url
* @arg xpath - xpath string of a lazy object 'object.sub.and.othersub';
*/
eachIncludeItem: function(type, x, fn, namespace, xpath) {
if (x == null) {
console.error('Include Item has no Data', type, namespace);
return;
}
if (type == 'lazy' && xpath == null) {
for (var key in x) this.eachIncludeItem(type, x[key], fn, null, key);
return;
}
if (x instanceof Array) {
for (var i = 0; i < x.length; i++) this.eachIncludeItem(type, x[i], fn, namespace, xpath);
return;
}
if (typeof x === 'object') {
for (var key in x) this.eachIncludeItem(type, x[key], fn, key, xpath);
return;
}
if (typeof x === 'string') {
var route = namespace && cfg[namespace];
if (route) {
namespace += '.' + x;
x = route.replace(regexp.name, x);
}
fn(namespace, x, xpath);
return;
}
console.error('Include Package is invalid', arguments);
},
invokeEach: function(arr, args) {
if (arr == null) return;
if (arr instanceof Array) {
for (var i = 0, x, length = arr.length; x = arr[i], i < length; i++) {
if (typeof x === 'function')(args != null ? x.apply(this, args) : x());
}
}
},
doNothing: function(fn) {
typeof fn == 'function' && fn()
},
reportError: function(e) {
console.error('IncludeJS Error:', e, e.message, e.url);
typeof handler.onerror == 'function' && handler.onerror(e);
},
ensureArray: function(obj, xpath) {
if (!xpath) return obj;
var arr = xpath.split('.');
while (arr.length - 1) {
var key = arr.shift();
obj = obj[key] || (obj[key] = {});
}
return (obj[arr.shift()] = []);
},
xhr: function(url, callback) {
var xhr = new XMLHttpRequest(),
s = Date.now();
xhr.onreadystatechange = function() {
xhr.readyState == 4 && callback && callback(url, xhr.responseText);
}
xhr.open('GET', url, true);
xhr.send();
}
},
events = (function(w, d) {
if (d == null) {
return {
ready: helper.doNothing,
load: helper.doNothing
};
}
var readycollection = [],
loadcollection = null,
readyqueue = null,
timer = Date.now();
d.onreadystatechange = function() {
if (/complete|interactive/g.test(d.readyState) == false) return;
if (timer) console.log('DOMContentLoader', d.readyState, Date.now() - timer, 'ms');
events.ready = (events.readyQueue = helper.doNothing);
helper.invokeEach(readyqueue);
helper.invokeEach(readycollection);
readycollection = null;
readyqueue = null;
if (d.readyState == 'complete') {
events.load = helper.doNothing;
helper.invokeEach(loadcollection);
loadcollection = null;
}
};
return {
ready: function(callback) {
readycollection.unshift(callback);
},
readyQueue: function(callback){
(readyqueue || (readyqueue = [])).push(callback);
},
load: function(callback) {
(loadcollection || (loadcollection = [])).unshift(callback);
}
}
})(w, d);
var IncludeDeferred = Class({
ready: function(callback) {
return this.on(4, function() {
events.ready(callback);
});
},
/** assest loaded and window is loaded */
loaded: function(callback) {
return this.on(4, function() {
events.load(callback);
});
},
/** assest loaded */
done: function(callback) {
return this.on(4, this.resolve.bind(this, callback));
},
resolve: function(callback) {
var r = callback(this.response);
if (r != null) this.obj = r;
}
});
var StateObservable = Class({
Construct: function() {
this.callbacks = [];
},
on: function(state, callback) {
state <= this.state ? callback(this) : this.callbacks.unshift({
state: state,
callback: callback
});
return this;
},
readystatechanged: function(state) {
this.state = state;
for (var i = 0, x, length = this.callbacks.length; x = this.callbacks[i], i < length; i++) {
if (x.state > this.state || x.callback == null) continue;
x.callback(this);
x.callback = null;
}
}
});
var currentParent;
var Include = Class({
setCurrent: function(data) {
currentParent = data;
},
incl: function(type, pckg) {
if (this instanceof Resource) return this.include(type, pckg);
var r = new Resource;
if (currentParent) {
r.id = currentParent.id;
//-r.url = currentParent.url;
r.namespace = currentParent.namespace;
//-currentParent = null;
}
return r.include(type, pckg);
//-return (this instanceof Resource ? this : new Resource).include(type, pckg);
},
js: function(pckg) {
return this.incl('js', pckg);
},
css: function(pckg) {
return this.incl('css', pckg);
},
load: function(pckg) {
return this.incl('load', pckg);
},
ajax: function(pckg) {
return this.incl('ajax', pckg);
},
embed: function(pckg) {
return this.incl('embed', pckg);
},
lazy: function(pckg) {
return this.incl('lazy', pckg);
},
cfg: function(arg) {
switch (typeof arg) {
case 'object':
for (var key in arg) cfg[key] = arg[key];
break;
case 'string':
if (arguments.length == 1) return cfg[arg];
if (arguments.length == 2) cfg[arg] = arguments[1];
break;
case 'undefined':
return cfg;
}
return this;
},
promise: function(namespace) {
var arr = namespace.split('.'),
obj = w;
while (arr.length) {
var key = arr.shift();
obj = obj[key] || (obj[key] = {});
}
return obj;
},
register: function(_bin) {
var onready = [];
for (var key in _bin) {
for (var i = 0; i < _bin[key].length; i++) {
var id = _bin[key][i].id,
url = _bin[key][i].url,
namespace = _bin[key][i].namespace,
resource = new Resource;
resource.state = 4;
resource.namespace = namespace;
resource.type = key;
if (url) {
if (url[0] == '/') url = url.substring(1);
resource.location = helper.uri.getDir(url);
}
switch (key) {
case 'load':
case 'lazy':
resource.state = 0;
events.readyQueue(function(_r, _id) {
var container = d.querySelector('script[data-id="' + _id + '"]');
if (container == null) {
console.error('"%s" Data was not embedded into html', _id);
return;
}
_r.obj = container.innerHTML;
_r.readystatechanged(4);
}.bind(this, resource, id));
break;
};
(bin[key] || (bin[key] = {}))[id] = resource;
}
}
}
});
var hasRewrites = typeof IncludeRewrites != 'undefined',
rewrites = hasRewrites ? IncludeRewrites : null;
var Resource = Class({
Base: Include,
Extends: [IncludeDeferred, StateObservable],
Construct: function(type, url, namespace, xpath, parent, id) {
if (type == null) {
return this;
}
this.namespace = namespace;
this.type = type;
this.xpath = xpath;
this.url = url;
if (url != null) {
this.url = helper.uri.resolveUrl(url, parent);
}
if (id) void(0);
else if (namespace) id = namespace;
else if (url[0] == '/') id = url;
else if (parent && parent.namespace) id = parent.namespace + '/' + url;
else if (parent && parent.location) id = '/' + parent.location.replace(/^[\/]+/, '') + url;
else id = '/' + url;
if (bin[type] && bin[type][id]) {
return bin[type][id];
}
if (hasRewrites == true && rewrites[id] != null) {
url = rewrites[id];
} else {
url = this.url;
}
this.location = helper.uri.getDir(url);
//-console.log('includejs. Load Resource:', id, url);
;
(bin[type] || (bin[type] = {}))[id] = this;
var tag;
switch (type) {
case 'js':
helper.xhr(url, this.onload.bind(this));
if (d != null) {
tag = d.createElement('script');
tag.type = "application/x-included-placeholder";
tag.src = url;
}
break;
case 'ajax':
case 'load':
case 'lazy':
helper.xhr(url, this.onload.bind(this));
break;
case 'css':
this.state = 4;
tag = d.createElement('link');
tag.href = url;
tag.rel = "stylesheet";
tag.type = "text/css";
break;
case 'embed':
tag = d.createElement('script');
tag.type = 'application/javascript';
tag.src = url;
tag.onload = function() {
this.readystatechanged(4);
}.bind(this);
tag.onerror = tag.onload;
break;
}
if (tag != null) {
d.querySelector('head').appendChild(tag);
tag = null;
}
return this;
},
include: function(type, pckg) {
this.state = 0;
if (this.includes == null) this.includes = [];
helper.eachIncludeItem(type, pckg, function(namespace, url, xpath) {
var resource = new Resource(type, url, namespace, xpath, this);
this.includes.push(resource);
resource.index = this.calcIndex(type, namespace);
resource.on(4, this.resourceLoaded.bind(this));
}.bind(this));
return this;
},
calcIndex: function(type, namespace) {
if (this.response == null) this.response = {};
switch (type) {
case 'js':
case 'load':
case 'ajax':
if (this.response[type + 'Index'] == null) this.response[type + 'Index'] = -1;
return ++this.response[type + 'Index'];
}
return -1;
},
wait: function() {
if (this.waits == null) this.waits = [];
if (this._include == null) this._include = this.include;
var data;
this.waits.push((data = []));
this.include = function(type, pckg) {
data.push({
type: type,
pckg: pckg
});
return this;
}
return this;
},
resourceLoaded: function(resource) {
if (this.parsing) return;
if (resource != null && resource.obj != null && resource.obj instanceof Include === false) {
switch (resource.type) {
case 'js':
case 'load':
case 'ajax':
var obj = (this.response[resource.type] || (this.response[resource.type] = []));
if (resource.namespace != null) {
obj = helper.ensureArray(obj, resource.namespace);
}
obj[resource.index] = resource.obj;
break;
}
}
if (this.includes != null && this.includes.length) {
for (var i = 0; i < this.includes.length; i++) if (this.includes[i].state != 4) return;
}
if (this.waits && this.waits.length) {
var data = this.waits.shift();
this.include = this._include;
for (var i = 0; i < data.length; i++) this.include(data[i].type, data[i].pckg);
return;
}
this.readystatechanged((this.state = 4));
},
onload: function(url, response) {
if (!response) {
console.warn('Resource cannt be loaded', this.url);
this.readystatechanged(4);
return;
}
switch (this.type) {
case 'load':
case 'ajax':
this.obj = response;
break;
case 'lazy':
LazyModule.create(this.xpath, response);
break;
case 'js':
this.parsing = true;
try {
__includeEval(response, this);
} catch (error) {
error.url = this.url;
helper.reportError(error);
}
break;
};
this.parsing = false;
this.resourceLoaded(null);
}
});
var LazyModule = {
create: function(xpath, code) {
var arr = xpath.split('.'),
obj = window,
module = arr[arr.length - 1];
while (arr.length > 1) {
var prop = arr.shift();
obj = obj[prop] || (obj[prop] = {});
}
arr = null;
obj.__defineGetter__(module, function() {
delete obj[module];
try {
var r = __includeEval(code, window.include);
if (r != null && r instanceof Resource == false) obj[module] = r;
} catch (error) {
error.xpath = xpath;
helper.reportError(e);
} finally {
code = null;
xpath = null;
return obj[module];
}
});
}
}
w.include = new Include();
w.include.helper = helper;
w.IncludeResource = Resource;
}(window, window.document);
window.__includeEval = function(source, include) {
return eval(source);
}
;include.cfg({"lib":"/.import/libjs/{name}/lib/{name}.js","framework":"/.import/libjs/framework/lib/{name}.js","compo":"/.import/libjs/compos/{name}/lib/{name}.js"}); include.register({"css":[{"id":"/style/main.css","url":"style/main.css","namespace":""}],"js":[{"id":"/.import/libjs/class/lib/class.js","url":".import/libjs/class/lib/class.js","namespace":""},{"id":"/.import/libjs/include/lib/include.js","url":".import/libjs/include/lib/include.js","namespace":""},{"id":"/include.routes.js","url":"include.routes.js","namespace":""},{"id":"lib.mask","url":"/.import/libjs/mask/lib/mask.js","namespace":"lib.mask"},{"id":"lib.compo","url":"/.import/libjs/compo/lib/compo.js","namespace":"lib.compo"},{"id":"framework.dom/zepto","url":"/.import/libjs/framework/lib/dom/zepto.js","namespace":"framework.dom/zepto"},{"id":"/script/main.js","url":"script/main.js","namespace":""}]})
;include.setCurrent({ id: '/include.routes.js', namespace: '', url: '{url}'});
;include.cfg({
"lib": "/.import/libjs/{name}/lib/{name}.js",
"framework": "/.import/libjs/framework/lib/{name}.js",
"compo": "/.import/libjs/compos/{name}/lib/{name}.js"
});
;include.setCurrent({ id: 'lib.mask', namespace: 'lib.mask', url: '{url}'});
;//include('script/ruqq/class.js', function() {
window.mask = (function(w, d) {
var regexp = {
noWhitespace: /[^\s]/g,
whitespace: /\s/g,
attributes: /(([\w_-]+)='([^']+))|(\.([\w-_]+)[# \.;{])|(#([\w-_]+)[ \.])/g,
linearCondition: /([!]?[\w\.-]+)([!<>=]{1,2})?([^\|\&]+)?([\|\&]{2})?/g,
escapedChar: {
"'": /\\'/g,
'"': /\\"/g,
'{': /\\\{/g,
'>': /\\>/g,
';': /\\>/g
},
attrEnd: /[\.#>\{ ;]/g
},
singleTags = {
img: 1,
input: 1,
br: 1,
hr: 1,
link: 1
};
var Helper = {
extend: function(target, source) {
if (source == null) return target;
if (target == null) target = {};
for (var key in source) target[key] = source[key];
return target;
},
getProperty: function(o, chain) {
if (typeof o !== 'object' || chain == null) return o;
if (typeof chain === 'string') chain = chain.split('.');
if (chain.length === 1) return o[chain[0]];
return this.getProperty(o[chain.shift()], chain);
},
templateFunction: function(arr, o) {
var output = '';
for (var i = 0; i < arr.length; i++) {
if (i % 2 === 0) {
output += arr[i];
} else {
var key = arr[i],
value = null,
index = key.indexOf(':');
if (index > -1) {
var utility = index > 0 ? key.substring(0, index).replace(regexp.whitespace, '') : '';
if (utility === '') utility = 'condition';
key = key.substring(index + 1);
value = typeof ValueUtilities[utility] === 'function' ? ValueUtilities[utility](key, o) : null;
} else {
value = Helper.getProperty(o, arr[i]);
}
output += value == null ? '' : value;
}
}
return output;
}
}
var Template = function(template) {
this.template = template;
this.index = 0;
this.length = template.length;
}
Template.prototype = {
next: function() {
this.index++;
return this;
},
skipWhitespace: function() {
//regexp.noWhitespace.lastIndex = this.index;
//var result = regexp.noWhitespace.exec(this.template);
//if (result){
// this.index = result.index;
//}
//return this;
for (; this.index < this.length; this.index++) {
if (this.template.charCodeAt(this.index) !== 32 /*' '*/ ) return this;
}
return this;
},
skipToChar: function(c) {
var index = this.template.indexOf(c, this.index);
if (index > -1) {
this.index = index;
if (this.template.charCodeAt(index - 1) !== 92 /*'\\'*/ ) {
return this;
}
this.next().skipToChar(c);
}
return this;
},
skipToAny: function(chars) {
var r = regexp[chars];
if (r == null) {
console.error('Unknown regexp %s: Create', chars);
r = (regexp[chars] = new RegExp('[' + chars + ']', 'g'));
}
r.lastIndex = this.index;
var result = r.exec(this.template);
if (result != null) {
this.index = result.index;
}
return this;
},
skipToAttributeBreak: function() {
//regexp.attrEnd.lastIndex = ++this.index;
//var result;
//do{
// result = regexp.attrEnd.exec(this.template);
// if (result != null){
// if (result[0] == '#' && this.template.charCodeAt(this.index + 1) === 123) {
// regexp.attrEnd.lastIndex += 2;
// continue;
// }
// this.index = result.index;
// break;
// }
//}while(result != null)
//return this;
var c;
do {
c = this.template.charCodeAt(++this.index);
// if c == # && next() == { - continue */
if (c === 35 && this.template.charCodeAt(this.index + 1) === 123) {
this.index++;
c = null;
}
}
while (c !== 46 && c !== 35 && c !== 62 && c !== 123 && c !== 32 && c !== 59 && this.index < this.length);
//while(!== ".#>{ ;");
return this;
},
sliceToChar: function(c) {
var start = this.index,
isEscaped, index;
while ((index = this.template.indexOf(c, this.index)) > -1) {
this.index = index;
if (this.template.charCodeAt(index - 1) !== 92 /*'\\'*/ ) {
break;
}
isEscaped = true;
this.index++;
}
var value = this.template.substring(start, this.index);
return isEscaped ? value.replace(regexp.escapedChar[c], c) : value;
//-return this.skipToChar(c).template.substring(start, this.index);
},
sliceToAny: function(chars) {
var start = this.index;
return this.skipToAny(chars).template.substring(start, this.index);
}
}
var ICustomTag = function() {
if (this.attr == null) this.attr = {};
};
ICustomTag.prototype.render = function(values, stream) {
return stream instanceof Array ? Builder.buildHtml(this.noes, values, stream) : Builder.buildDom(this.nodes, values, stream);
}
var CustomTags = (function(ICustomTag) {
var List = function() {
this.attr = {}
}
List.prototype.render = function(values, container, cntx) {
values = Helper.getProperty(values, this.attr.value);
if (values instanceof Array === false) return container;
if (this.attr.template != null) {
var template = document.querySelector(this.attr.template).innerHTML;
this.nodes = mask.compile(template);
}
if (this.nodes == null) return container;
//-var fn = container instanceof Array ? 'buildHtml' : 'buildDom';
var fn = container.buffer != null ? 'buildHtml' : 'buildDom';
for (var i = 0, length = values.length; i < length; i++) {
Builder[fn](this.nodes, values[i], container, cntx);
}
return container;
}
var Visible = function() {
this.attr = {}
}
Visible.prototype.render = function(values, container, cntx) {
if (ValueUtilities.out.isCondition(this.attr.check, values) === false) return container;
return ICustomTag.prototype.render.call(this, values, container, cntx);
}
var Binding = function() {
this.attr = {}
}
Binding.prototype.render = function(values, container) {
var value = values[this.attr.value];
Object.defineProperty(values, this.attr.value, {
get: function() {
return value;
},
set: function(x) {
container.innerHTML = (value = x);
}
})
container.innerHTML = value;
return container;
}
return {
all: {
list: List,
visible: Visible,
bind: Binding
}
}
})(ICustomTag);
var ValueUtilities = (function(H, regexp) {
//condition1 == condition2 ? case1 : case2
var parseLinearCondition = function(line) {
var c = {
assertions: []
},
buffer = {
data: line.replace(regexp.whitespace, '')
};
buffer.index = buffer.data.indexOf('?');
if (buffer.index == -1) console.error('Invalid Linear Condition: ? - no found');
var match, expr = buffer.data.substring(0, buffer.index);
while ((match = regexp.linearCondition.exec(expr)) != null) {
c.assertions.push({
join: match[4],
left: match[1],
sign: match[2],
right: match[3]
});
}
buffer.index++;
parseCase(buffer, c, 'case1');
buffer.index++;
parseCase(buffer, c, 'case2');
return c;
},
parseCase = function(buffer, obj, key) {
var c = buffer.data[buffer.index],
end = null;
if (c == null) return;
if (c === '"' || c === "'") {
end = buffer.data.indexOf(c, ++buffer.index);
obj[key] = buffer.data.substring(buffer.index, end);
} else {
end = buffer.data.indexOf(':', buffer.index);
if (end == -1) end = buffer.data.length;
obj[key] = {
value: buffer.data.substring(buffer.index, end)
};
}
if (end != null) buffer.index = ++end;
},
isCondition = function(con, values) {
if (typeof con === 'string') con = parseLinearCondition(con);
var current = false;
for (var i = 0; i < con.assertions.length; i++) {
var a = con.assertions[i],
value1, value2;
if (a.right == null) {
current = a.left.charCodeAt(0) === 33 ? !H.getProperty(values, a.left.substring(1)) : !! H.getProperty(values, a.left);
if (current == true) {
if (a.join == '&&') continue;
break;
}
if (a.join == '||') continue;
break;
}
var c = a.right.charCodeAt(0);
if (c === 34 || c === 39) {
value2 = a.right.substring(1, a.right.length - 1);
} else if (c > 47 && c < 58) {
value2 = a.right;
} else {
value2 = H.getProperty(values, a.right);
}
value1 = H.getProperty(values, a.left);
switch (a.sign) {
case '<':
current = value1 < value2;
break;
case '<=':
current = value1 <= value2;
break;
case '>':
current = value1 > value2;
break;
case '>=':
current = value1 >= value2;
break;
case '!=':
current = value1 != value2;
break;
case '==':
current = value1 == value2;
break;
}
if (current == true) {
if (a.join == '&&') continue;
break;
}
if (a.join == '||') continue;
break;
}
return current;
};
return {
condition: function(line, values) {
con = parseLinearCondition(line);
var result = isCondition(con, values) ? con.case1 : con.case2;;
if (result == null) return '';
if (typeof result === 'string') return result;
return H.getProperty(values, result.value);
},
out: {
isCondition: isCondition,
parse: parseLinearCondition
}
}
})(Helper, regexp);
var Parser = {
toFunction: function(template) {
var arr = template.split('#{'),
length = arr.length;
for (var i = 1; i < length; i++) {
var key = arr[i],
index = key.indexOf('}');
arr.splice(i, 0, key.substring(0, index));
i++;
length++;
arr[i] = key.substring(index + 1);
}
template = null;
return function(o) {
return Helper.templateFunction(arr, o);
}
},
parseAttributes: function(T, node) {
var key, value, _classNames, quote;
if (node.attr == null) node.attr = {};
for (; T.index < T.length; T.index++) {
key = null;
value = null;
var c = T.template.charCodeAt(T.index);
switch (c) {
case 32:
//case 9: was replaced while compiling
//case 10:
continue;
//case '{;>':
case 123:
case 59:
case 62:
if (_classNames != null) {
node.attr['class'] = _classNames.indexOf('#{') > -1 ? (T.serialize !== true ? this.toFunction(_classNames) : {
template: _classNames
}) : _classNames;
}
return;
case 46:
/* '.' */
var start = T.index + 1;
T.skipToAttributeBreak();
value = T.template.substring(start, T.index);
_classNames = _classNames != null ? _classNames + ' ' + value : value;
T.index--;
break;
case 35:
/* '#' */
key = 'id';
var start = T.index + 1;
T.skipToAttributeBreak();
value = T.template.substring(start, T.index);
T.index--;
break;
default:
key = T.sliceToChar('=');
do(quote = T.template.charAt(++T.index))
while (quote == ' ');
T.index++;
value = T.sliceToChar(quote);
break;
}
if (key != null) {
//console.log('key', key, value);
if (value.indexOf('#{') > -1) {
value = T.serialize !== true ? this.toFunction(value) : {
template: value
};
}
node.attr[key] = value;
}
}
},
/** @out : nodes */
parse: function(T, nodes) {
var current = T;
for (; T.index < T.length; T.index++) {
var c = T.template.charCodeAt(T.index);
switch (c) {
case 32:
continue;
case 39:
/* ' */
case 34:
/* " */
T.index++;
var content = T.sliceToChar(c == 39 ? "'" : '"');
if (content.indexOf('#{') > -1) content = T.serialize !== true ? this.toFunction(content) : {
template: content
};
var t = {
content: content
}
if (current.nodes == null) current.nodes = t;
else if (current.nodes.push == null) current.nodes = [current.nodes, t];
else current.nodes.push(t);
//-current.nodes.push(t);
if (current.__single) {
if (current == null) continue;
current = current.parent;
while (current != null && current.__single != null) {
current = current.parent;
}
}
continue;
case 62:
/* '>' */
current.__single = true;
continue;
case 123:
/* '{' */
continue;
case 59:
/* ';' */
case 125:
/* '}' */
if (current == null) continue;
do(current = current.parent)
while (current != null && current.__single != null);
continue;
}
var start = T.index;
do(c = T.template.charCodeAt(++T.index))
while (c !== 32 && c !== 35 && c !== 46 && c !== 59 && c !== 123 && c !== 62); /** while !: ' ', # , . , ; , { <*/
var tagName = T.template.substring(start, T.index);
if (tagName === '') {
console.error('Parse Error: Undefined tag Name %d/%d %s', T.index, T.length, T.template.substring(T.index, T.index + 10));
}
var tag = {
tagName: tagName,
parent: current
};
if (current == null) {
console.log('T', T, 'rest', T.template.substring(T.index));
}
if (current.nodes == null) current.nodes = tag;
else if (current.nodes.push == null) current.nodes = [current.nodes, tag];
else current.nodes.push(tag);
//-if (current.nodes == null) current.nodes = [];
//-current.nodes.push(tag);
current = tag;
this.parseAttributes(T, current);
T.index--;
}
return T.nodes;
},
cleanObject: function(obj) {
if (obj instanceof Array) {
for (var i = 0; i < obj.length; i++) this.cleanObject(obj[i]);
return obj;
}
delete obj.parent;
delete obj.__single;
if (obj.nodes != null) this.cleanObject(obj.nodes);
return obj;
}
};
var Builder = {
buildDom: function(nodes, values, container, cntx) {
if (nodes == null) return container;
if (container == null) {
container = d.createDocumentFragment();
}
if (cntx == null) {
cntx = {
//events: {}
};
}
var isarray = nodes instanceof Array,
length = isarray ? nodes.length : 1,
node = null;
for (var i = 0; node = isarray ? nodes[i] : nodes, isarray ? i < length : i < 1; i++) {
if (CustomTags.all[node.tagName] != null) {
var custom = new CustomTags.all[node.tagName](values);
custom.compoName = node.tagName;
custom.nodes = node.nodes;
custom.attr = custom.attr == null ? node.attr : Helper.extend(custom.attr, node.attr);
(cntx.components || (cntx.components = [])).push(custom);
//cntx = custom;
custom.parent = cntx;
custom.render(values, container, custom);
continue;
}
if (node.content != null) {
container.appendChild(d.createTextNode(typeof node.content == 'function' ? node.content(values) : node.content));
continue;
}
var tag = d.createElement(node.tagName);
for (var key in node.attr) {
var value = typeof node.attr[key] == 'function' ? node.attr[key](values) : node.attr[key];
if (value) tag.setAttribute(key, value);
}
if (node.nodes != null) {
this.buildDom(node.nodes, values, tag, cntx);
}
container.appendChild(tag);
}
return container;
},
//////////buildHtml: function(node, values, stream) {
//////////
////////// if (stream == null) stream = [];
////////// if (node instanceof Array) {
////////// for (var i = 0, length = node.length; i < length; i++) this.buildHtml(node[i], values, stream);
////////// return stream;
////////// }
//////////
////////// if (CustomTags.all[node.tagName] != null) {
////////// var custom = new CustomTags.all[node.tagName]();
////////// for (var key in node) custom[key] = node[key];
////////// custom.render(values, stream);
////////// return stream;
////////// }
////////// if (node.content != null) {
////////// stream.push(typeof node.content === 'function' ? node.content(values) : node.content);
////////// return stream;
////////// }
//////////
////////// stream.push('<' + node.tagName);
////////// for (var key in node.attr) {
////////// var value = typeof node.attr[key] == 'function' ? node.attr[key](values) : node.attr[key];
////////// if (value) {
////////// stream.push(' ' + key + "='");
////////// stream.push(value);
////////// stream.push("'");
////////// }
////////// }
////////// if (singleTags[node.tagName] != null) {
////////// stream.push('/>');
////////// if (node.nodes != null) console.error('Html could be invalid: Single Tag Contains children:', node);
////////// } else {
////////// stream.push('>');
////////// if (node.nodes != null) {
////////// this.buildHtml(node.nodes, values, stream);
////////// }
////////// stream.push('</' + node.tagName + '>');
////////// }
////////// return stream;
//////////},
buildHtml: function(nodes, values, writer) {
if (writer == null) {
writer = {
buffer: ''
}
}
var isarray = nodes instanceof Array,
length = isarray ? nodes.length : 1,
node = null;
for (var i = 0; node = isarray ? nodes[i] : nodes, isarray ? i < length : i < 1; i++) {
if (CustomTags.all[node.tagName] != null) {
var custom = new CustomTags.all[node.tagName]();
for (var key in node) custom[key] = node[key];
custom.render(values, writer);
return writer;
}
if (node.content != null) {
writer.buffer += typeof node.content === 'function' ? node.content(values) : node.content;
return writer;
}
writer.buffer += '<' + node.tagName;
for (var key in node.attr) {
var value = typeof node.attr[key] == 'function' ? node.attr[key](values) : node.attr[key];
if (value) {
writer.buffer += ' ' + key + "='" + value + "'";
}
}
if (singleTags[node.tagName] != null) {
writer.buffer += '/>';
if (node.nodes != null) console.error('Html could be invalid: Single Tag Contains children:', node);
} else {
writer.buffer += '>';
if (node.nodes != null) {
this.buildHtml(node.nodes, values, writer);
}
writer.buffer += '</' + node.tagName + '>';
}
}
return writer;
}
}
return {
/**
* @see renderDom
* @description - normally you should use renderDom, as this function is slower
* @return html {string}
*/
renderHtml: function(template, values) {
if (typeof template === 'string') {
template = this.compile(template);
}
return Builder.buildHtml(template, values).buffer //-join('');
},
/**
* @arg template - {template{string} | maskDOM{array}}
* @arg values - template values
* @arg container - optional, - place to renderDOM, @default - DocumentFragment
* @return container {@default DocumentFragment}
*/
renderDom: function(template, values, container, cntx) {
//////try {
if (typeof template === 'string') {
template = this.compile(template);
}
return Builder.buildDom(template, values, container, cntx);
//////} catch (e) {
////// console.error('maskJS', e.message, template);
//////}
//////return null;
},
/**
*@arg template - string to be parsed into maskDOM
*@arg serializeDOM - build raw maskDOM json, without template functions - used for storing compiled template
*@return maskDOM
*/
compile: function(template, serializeOnly) {
/** remove unimportant whitespaces */
template = template.replace(/[\t\n\r]|[ ]{2,}/g, ' ');
var T = new Template(template);
if (serializeOnly == true) T.serialize = true;
return Parser.parse(T, []);
},
registerHandler: function(tagName, TagHandler) {
CustomTags.all[tagName] = TagHandler;
},
getHandler: function(tagName) {
return CustomTags.all[tagName]
},
registerUtility: function(utilityName, fn) {
ValueUtilities[utilityName] = fn;
},
serialize: function(template) {
return Parser.cleanObject(this.compile(template, true));
},
deserialize: function(serialized) {
if (serialized instanceof Array) {
for (var i = 0; i < serialized.length; i++) {
this.deserialize(serialized[i]);
}
return serialized;
}
if (serialized.content != null) {
if (serialized.content.template != null) {
serialized.content = Parser.toFunction(serialized.content.template);
}
return serialized;
}
if (serialized.attr != null) {
for (var key in serialized.attr) {
if (serialized.attr[key].template == null) continue;
serialized.attr[key] = Parser.toFunction(serialized.attr[key].template);
}
}
if (serialized.nodes != null) {
this.deserialize(serialized.nodes);
}
return serialized;
},
ICustomTag: ICustomTag,
ValueUtils: ValueUtilities
}
})(window, document);
//});
;include.setCurrent({ id: 'lib.compo', namespace: 'lib.compo', url: '{url}'});
;include.js({
lib: 'mask'
}).done(function() {
var w = window,
regexp = {
trailingSpaces: /^\s+/
},
Helper = {
resolveDom: function(compo, values) {
if (compo.nodes != null) {
if (compo.tagName != null) return compo;
return mask.renderDom(compo.nodes, values);
}
if (compo.attr.template != null) {
var e;
if (compo.attr.template[0] === '#') {
e = document.getElementById(compo.attr.template.substring(1));
if (e == null) {
console.error('Template Element not Found:', arg);
return null;
}
}
return mask.renderDom(e != null ? e.innerHTML : compo.attr.template, values);
}
return null;
},
ensureTemplate: function(compo) {
if (compo.nodes != null) return;
var template;
if (compo.attr.template != null) {
if (compo.attr.template[0] === '#') template = this.templateById(compo.attr.template.substring(1));
else template = compo.attr.template;
delete compo.attr.template;
}
if (typeof template == 'string') {
template = mask.compile(template);
}
if (template != null) {
compo.nodes = template;
return;
}
return;
},
templateById: function(id) {
var e = document.getElementById(id);
if (e == null) console.error('Template Element not Found:', id);
else
return e.innerHTML;
return '';
},
containerArray: function() {
var arr = [];
arr.appendChild = function(child) {
this.push(child);
}
return arr;
},
parseSelector: function(selector, type, direction) {
var key, prop, nextKey;
if (key == null) {
switch (selector[0]) {
case '#':
key = 'id';
selector = selector.substring(1);
prop = 'attr';
break;
case '.':
key = 'class';
selector = new RegExp('\\b' + selector.substring(1) + '\\b');
prop = 'attr';
break;
default:
key = type == 'node' ? 'tagName' : 'compoName';
break;
}
}
if (direction == 'up') nextKey = 'parent';
else nextKey = type == 'node' ? 'nodes' : 'components';
return {
key: key,
prop: prop,
selector: selector,
nextKey: nextKey
}
}
},
/**
* Component Events. Fires only once.
* Used for component Initialization.
* Supported events:
* DOMInsert
* +custom
* UI-Eevent exchange must be made over DOMLibrary
*/
Shots = { /** from parent to childs */
emit: function(component, event, args) {
if (component.listeners != null && event in component.listeners) {
component.listeners[event].apply(component, args);
delete component.listeners[event];
}
if (component.components instanceof Array) {
for (var i = 0; i < component.components.length; i++) {
Shots.emit(component.components[i], event, args);
}
}
},
on: function(component, event, fn) {
if (component.listeners == null) component.listeners = {};
component.listeners[event] = fn;
}
},
Events_ = {
on: function(component, events) {
var isarray = events instanceof Array,
length = isarray ? events.length : 1,
x = null;
for (var i = 0; x = isarray ? events[i] : events, isarray ? i < length : i < 1; i++) {
if (x instanceof Array) {
component.$.on.apply(component.$, x);
continue;
}
for (var key in x) {
var fn = typeof x[key] === 'string' ? component[x[key]] : x[key],
parts = key.split(':');
component.$.on(parts[0] || 'click', parts.splice(1).join(':'), fn.bind(component));
}
}
}
};
w.Compo = Class({
/**
* @param - arg -
* 1. object - model object, receive from mask.renderDom
* Custom Initialization:
* 2. String - template
* @param cntx
* 1. maskDOM context
*/
Construct: function(arg) {
if (typeof arg === 'string') {
var template = arg;
if (template[0] == '#') template = Helper.templateById(template.substring(1));
this.nodes = mask.compile(template);
}
},
render: function(values, container, cntx) {
this.create(values, cntx);
if (container != null) {
for (var i = 0; i < this.$.length; i++) container.appendChild(this.$[i]);
}
return this;
},
insert: function(parent) {
for (var i = 0; i < this.$.length; i++) parent.appendChild(this.$[i]);
Shots.emit(this, 'DOMInsert');
return this;
},
append: function(template, values, selector) {
if (this.$ == null) {
var dom = typeof template == 'string' ? mask.compile(template) : template,
parent = selector ? Compo.findNode(this, selector) : this;
if (parent.nodes == null) this.nodes = dom;
else if (parent.nodes instanceof Array) parent.nodes.push(dom);
else parent.nodes = [this.nodes, dom];
return this;
}
var array = mask.renderDom(template, values, Helper.containerArray(), this),
parent = selector ? this.$.find(selector) : this.$;
for (var i = 0; i < array.length; i++) parent.append(array[i]);
Shots.emit(this, 'DOMInsert');
return this;
},
create: function(values, cntx) {
if (cntx == null) cntx = this;
Helper.ensureTemplate(this);
var elements = mask.renderDom(this.tagName == null ? this.nodes : this, values, Helper.containerArray(), cntx);
this.$ = $(elements);
if (this.events != null) {
Events_.on(this, this.events);
}
if (this.compos != null) {
for (var key in this.compos) {
if (typeof this.compos[key] !== 'string') continue;
var selector = this.compos[key],
index = selector.indexOf(':'),
engine = selector.substring(0, index);
engine = Compo.config.selectors[engine];
if (engine == null) {
this.compos[key] = this.$.get(0).querySelector(selector);
continue;
}
selector = selector.substring(++index).replace(regexp.trailingSpaces, '');
this.compos[key] = engine(this, selector);
}
}
return this;
},
on: function() {
var x = Array.prototype.slice.call(arguments)
switch (arguments.length) {
case 1:
case 2:
x.unshift('click');
break;
}
if (this.events == null) this.events = [x];
else if (this.events instanceof Array) this.events.push(x)
else this.events = [x, this.events];
return this;
},
remove: function() {
this.$ && this.$.remove();
Compo.dispose(this);
if (this.parent != null) {
var i = this.parent.components.indexOf(compo);
this.parent.components.splice(i, 1);
}
return this;
},
Static: {
config: {
selectors: {
'$': function(compo, selector) {
var r = compo.$.find(selector);
return r.length > 0 ? r : compo.$.filter(selector);
},
'compo': function(compo, selector) {
var r = Compo.findCompo(compo, selector);
return r;
}
},
/**
@default, global $ is used
IDOMLibrary = {
{fn}(elements) - create dom-elements wrapper,
on(event, selector, fn) - @see jQuery 'on'
}
*/
setDOMLibrary: function(lib) {
$ = lib;
}
},
match: function(compo, selector, type) {
if (typeof selector === 'string') {
if (type == null) type = compo.compoName ? 'compo' : 'node';
selector = Helper.parseSelector(selector, type, direction);
}
var obj = selector.prop ? compo[selector.prop] : compo;
if (obj == null) return false;
if (selector.selector.test != null) {
if (selector.selector.test(obj[selector.key])) return true;
} else {
if (obj[selector.key] == selector.selector) return true;
}
return false;
},
find: function(compo, selector, direction, type) {
if (compo == null) return null;
if (typeof selector === 'string') {
if (type == null) type = compo.compoName ? 'compo' : 'node';
selector = Helper.parseSelector(selector, type, direction);
}
if (compo instanceof Array) {
for (var i = 0, x, length = compo.length; x = compo[i], i < length; i++) {
var r = Compo.find(x, selector);
if (r != null) return r;
}
return null;
}
if (Compo.match(compo, selector) == true) return compo;
return Compo.find(compo[selector.nextKey], selector);
},
findCompo: function(compo, selector, direction) {
return Compo.find(compo, selector, direction, 'compo');
},
findNode: function(compo, selector, direction) {
return Compo.find(compo, selector, direction, 'node');
},
dispose: function(compo) {
compo.dispose && compo.dispose();
if (this.components == null) return;
for (var i = 0, x, length = compo.components.length; x = compo.components[i], i < length; i++) {
Compo.dispose(x);
}
},
events: Shots
}
});
/** CompoUtils */
var Traversing = {
find: function(selector, type) {
return Compo.find(this, selector, null, type || 'compo');
},
closest: function(selector, type) {
return Compo.find(this, selector, 'up', type || 'compo');
},
all: function(selector, type) {
var current = arguments[2] || this,
arr = arguments[3] || []
if (typeof selector === 'string') selector = Helper.parseSelector(selector, type);
if (Compo.match(current, selector)) {
arr.push(current);
}
var childs = current[selector.nextKey];
if (childs != null) {
for (var i = 0; i < childs.length; i++) {
this.all(selector, null, childs[i], arr);
}
}
return arr;
}
}
var Manipulate