UNPKG

includejs

Version:

IncludeJS Resource Builder Tool

1,873 lines (1,597 loc) 100 kB
; (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