includejs
Version:
IncludeJS Resource Builder Tool
794 lines (681 loc) • 21.5 kB
JavaScript
//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);
//});