123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578 |
- (function($root) {
- var Membrane = {
-
- inject: function($host, gene, namespace, replace) {
-
- var $node = null;
- var $replacement;
- if (replace && $host) {
-
- $replacement = Phenotype.$type(gene, namespace);
- if (gene.hasOwnProperty('$cell')) {
- $node = $host;
- if ($node.parentNode) $node.parentNode.replaceChild($replacement, $node);
- }
- $node = $replacement;
- } else if (gene.$type && (gene.$type === 'head' || gene.$type === 'body') && $root.document.getElementsByTagName(gene.$type)) {
-
- $node = $root.document.getElementsByTagName(gene.$type)[0];
- } else if (gene.id && $root.document.getElementById(gene.id)) {
-
- $node = $root.document.getElementById(gene.id);
- if ($node.nodeName.toLowerCase() !== (gene.$type || 'div')) {
- $replacement = Phenotype.$type(gene, namespace);
- $node.parentNode.replaceChild($replacement, $node);
- $node = $replacement;
- }
- }
- if ($node && !$node.Meta) $node.Meta = {};
- return $node;
- },
- add: function($parent, gene, index, namespace) {
-
- var $node = Phenotype.$type(gene, namespace);
- if (index !== null && index !== undefined && $parent.childNodes && $parent.childNodes[index]) {
-
- $parent.insertBefore($node, $parent.childNodes[index]);
- } else {
-
- $parent.appendChild($node);
- }
- return $node;
- },
- build: function($parent, gene, index, namespace, replace) {
-
-
- var $existing = Membrane.inject($parent, gene, namespace, replace);
- if ($existing) return $existing;
-
- else return Membrane.add($parent, gene, index, namespace);
- },
- };
- var Genotype = {
-
- set: function($node, key, val) {
- if (['$init'].indexOf(key) === -1) {
- $node.Genotype[key] = Nucleus.bind($node, val);
- } else {
- val.snapshot = val;
- $node.Genotype[key] = val;
- }
- },
- update: function($node, key, val) {
- Nucleus.queue($node, key, 'w');
- Genotype.set($node, key, val);
- },
- build: function($node, gene, inheritance) {
- $node.Genotype = {};
- $node.Inheritance = inheritance;
- for (var key in gene) {
- Genotype.set($node, key, gene[key]);
- }
- },
- };
- var Gene = {
-
- freeze: function(gene) {
- var cache = [];
- var res = JSON.stringify(gene, function(key, val) {
- if (typeof val === 'function') { return val.toString(); }
- if (typeof val === 'object' && val !== null) {
- if (cache.indexOf(val) !== -1) { return '[Circular]'; }
- cache.push(val);
- }
- return val;
- });
- cache = null;
- return res;
- },
- LCS: function(a, b) {
- var m = a.length, n = b.length, C = [], i, j, af = [], bf = [];
- for (i = 0; i < m; i++) af.push(Gene.freeze(a[i]));
- for (j = 0; j < n; j++) bf.push(Gene.freeze(b[j]));
- for (i = 0; i <= m; i++) C.push([0]);
- for (j = 0; j < n; j++) C[0].push(0);
- for (i = 0; i < m; i++) for (j = 0; j < n; j++) C[i+1][j+1] = af[i] === bf[j] ? C[i][j]+1 : Math.max(C[i+1][j], C[i][j+1]);
- return (function bt(i, j) {
- if (i*j === 0) { return []; }
- if (af[i-1] === bf[j-1]) { return bt(i-1, j-1).concat([{ item: a[i-1], _old: i-1, _new: j-1 }]); }
- return C[i][j-1] > C[i-1][j] ? bt(i, j-1) : bt(i-1, j);
- }(m, n));
- },
- diff: function(_old, _new) {
- var lcs = Gene.LCS(_old, _new);
- var old_common = lcs.map(function(i) { return i._old; });
- var minus = _old.map(function(item, index) {
- return { item: item, index: index };
- }).filter(function(item, index) {
- return old_common.indexOf(index) === -1;
- });
- var new_common = lcs.map(function(i) { return i._new; });
- var plus = _new.map(function(item, index) {
- return { item: item, index: index };
- }).filter(function(item, index) {
- return new_common.indexOf(index) === -1;
- });
- return { '-': minus, '+': plus };
- },
- };
- var Phenotype = {
-
- build: function($node, genotype) {
- Phenotype.$init($node);
- for (var key in genotype) {
- if (genotype[key] !== null && genotype[key] !== undefined) {
- Phenotype.set($node, key, genotype[key]);
- }
- }
- },
- multiline: function(fn) { return /\/\*!?(?:@preserve)?[ \t]*(?:\r\n|\n)([\s\S]*?)(?:\r\n|\n)[ \t]*\*\//.exec(fn.toString())[1]; },
- get: function(key) {
- return Object.getOwnPropertyDescriptor($root.HTMLElement.prototype, key) || Object.getOwnPropertyDescriptor($root.Element.prototype, key);
- },
- set: function($node, key, val) {
- if (key[0] === '$') {
- if (key === '$type') {
-
- var tag = $node.tagName ? $node.tagName.toLowerCase() : 'text';
- if (val.toLowerCase() !== tag) {
- var fragment = Phenotype.$type({ $type: 'fragment' });
- var replacement = fragment.$build($node.Genotype, $node.Inheritance, null, $node.Meta.namespace);
- $node.parentNode.replaceChild(replacement, $node);
- $node = replacement;
- }
- } else if (key === '$text') {
- if (typeof val === 'function') val = Phenotype.multiline(val);
- $node.textContent = val;
- } else if (key === '$html') {
- if (typeof val === 'function') val = Phenotype.multiline(val);
- $node.innerHTML = val;
- } else if (key === '$components') {
- Phenotype.$components($node, val);
- }
- } else if (key[0] === '_') {
-
- } else if (key === 'value') {
- $node[key] = val;
- } else if (key === 'style' && typeof val === 'object') {
- var CSSStyleDeclaration = Phenotype.get(key).get.call($node);
- for (var attr in val) { CSSStyleDeclaration[attr] = val[attr]; }
- } else if (typeof val === 'number' || typeof val === 'string' || typeof val === 'boolean') {
- if ($node.setAttribute) $node.setAttribute(key, val);
- } else if (typeof val === 'function') {
-
- var prop = Phenotype.get(key);
- if (prop) prop.set.call($node, val);
- }
- },
- $type: function(model, namespace) {
- var meta = {};
- var $node;
- if (model.$type === 'text') {
- if (model.$text && typeof model.$text === 'function') model.$text = Phenotype.multiline(model.$text);
- $node = $root.document.createTextNode(model.$text);
- } else if (model.$type === 'svg') {
- $node = $root.document.createElementNS('http://www.w3.org/2000/svg', model.$type);
- meta.namespace = $node.namespaceURI;
- } else if (namespace) {
- $node = $root.document.createElementNS(namespace, model.$type);
- meta.namespace = $node.namespaceURI;
- } else if (model.$type === 'fragment') {
- $node = $root.document.createDocumentFragment();
- } else {
- $node = $root.document.createElement(model.$type || 'div');
- }
- $node.Meta = meta;
- return $node;
- },
- $components: function($parent, components) {
- if (!components) components = [];
- var old = [].map.call($parent.childNodes, function($node) {
- return $node.Genotype;
- }).filter(function(item) {
- return item;
- });
- if (old.length > 0) {
-
- var diff = Gene.diff(old, components);
- diff['-'].forEach(function(item) { $parent.childNodes[item.index].Kill = true; });
- [].filter.call($parent.childNodes, function($node) {
- return $node.Kill;
- }).forEach(function($node) {
- $parent.removeChild($node);
- });
- diff['+'].forEach(function(item) {
- var inheritance = $parent.Inheritance;
- for (var key in $parent.Genotype) {
- if (key[0] === '_') inheritance = inheritance.concat([key]);
- }
- $parent.$build(item.item, inheritance, item.index, $parent.Meta.namespace);
- $parent.$components[item.index] = $parent.childNodes[item.index].Genotype;
- });
- } else {
-
- var $fragment = Phenotype.$type({ $type: 'fragment' });
- var inheritance = $parent.Inheritance;
- for (var key in $parent.Genotype) {
- if (key[0] === '_') inheritance = inheritance.concat([key]);
- }
- while ($parent.firstChild) { $parent.removeChild($parent.firstChild); }
- components.forEach(function(component) {
- $fragment.$build(component, inheritance, null, $parent.Meta.namespace);
- });
- $parent.appendChild($fragment);
- $parent.$components = [].map.call($parent.childNodes, function($node) { return $node.Genotype; });
- }
- },
- $init: function($node) {
- Nucleus.tick.call($root, function() {
- if ($node.Genotype && $node.Genotype.$init) Nucleus.bind($node, $node.Genotype.$init)();
- });
- },
- $update: function($node) {
- if ($node.parentNode && !$node.Meta.$updated && $node.$update) {
- $node.Meta.$updated = true;
- $node.$update();
- for (var key in $node.Dirty) { Phenotype.set($node, key, $node.Genotype[key]); }
- $node.Meta.$updated = false;
- $node.Dirty = null;
- }
- },
- };
- var Nucleus = {
-
- tick: $root.requestAnimationFrame || $root.webkitRequestAnimationFrame || $root.mozRequestAnimationFrame || $root.msRequestAnimationFrame || function(cb) { return $root.setTimeout(cb, 1000/60); },
- set: function($node, key) {
-
- try {
- Object.defineProperty($node, key, {
- configurable: true,
- get: function() {
-
-
- if (key[0] === '$' || key[0] === '_') {
- if (key in $node.Genotype) {
- Nucleus.queue($node, key, 'r');
- return $node.Genotype[key];
- } else if (key[0] === '_') {
-
- var $current = $node;
- while ($current = $current.parentNode) {
- if ($current && $current.Genotype && (key in $current.Genotype)) {
- Nucleus.queue($current, key, 'r');
- return $current.Genotype[key];
- }
- }
- } else {
- return null;
- }
- } else {
-
- if (key === 'value') {
-
- return Object.getOwnPropertyDescriptor(Object.getPrototypeOf($node), key).get.call($node);
- } else if (key === 'style') {
- return Phenotype.get(key).get.call($node);
- } else if (key in $node.Genotype) {
-
- return $node.Genotype[key];
- } else {
-
-
-
-
- return Phenotype.get(key).get.call($node);
- }
- }
- },
- set: function(val) {
-
-
-
- var $current = $node;
- if (!(key in $node.Genotype) && key[0] === '_') {
- while ($current = $current.parentNode) {
- if ($current && $current.Genotype && (key in $current.Genotype)) {
- break;
- }
- }
- }
-
- Genotype.update($current, key, val);
-
- if (key[0] !== '$' && key[0] !== '_') {
- if (key === 'value') {
- return Object.getOwnPropertyDescriptor(Object.getPrototypeOf($node), key).set.call($node, val);
- } else if (key === 'style' && typeof val === 'object') {
- Phenotype.get(key).set.call($node, val);
- } else if (typeof val === 'number' || typeof val === 'string' || typeof val === 'boolean') {
- $node.setAttribute(key, val);
- } else if (typeof val === 'function') {
- Phenotype.get(key).set.call($node, val);
- }
- }
- },
- });
- } catch (e) { }
- },
- build: function($node) {
-
- ['$type', '$text', '$html', '$components'].forEach(function(key) {
- if (!(key in $node.Genotype)) Nucleus.set($node, key);
- });
-
- if ($node.Inheritance) {
- $node.Inheritance.forEach(function(key) {
- Nucleus.set($node, key);
- });
- }
-
- for (var key in $node.Genotype) {
- Nucleus.set($node, key);
- }
- },
- _queue: [],
- bind: function($node, v) {
-
-
-
- if (typeof v === 'function') {
- var fun = function() {
-
-
- Nucleus.tick.call($root, function() {
-
-
-
-
- Nucleus._queue.forEach(function($node) {
- var needs_update = false;
-
- for (var key in $node.Dirty) {
- if (Gene.freeze($node.Genotype[key]) !== $node.Dirty[key]) {
- Phenotype.set($node, key, $node.Genotype[key]);
- if (key[0] === '_') { needs_update = true; }
- }
- }
- if (needs_update && '$update' in $node.Genotype && (typeof $node.Genotype.$update === 'function')) {
- Phenotype.$update($node);
- } else { $node.Dirty = null; }
- });
-
- var index = Nucleus._queue.indexOf($node);
- if (index !== -1) Nucleus._queue.splice(index, 1);
- });
-
- return v.apply($node, arguments);
- };
- fun.snapshot = v;
- return fun;
- } else {
- return v;
- }
- },
- queue: function($node, key, mode) {
- var val = $node.Genotype[key];
- if (mode === 'r') {
-
- if (typeof val !== 'object' && !Array.isArray(val)) return;
- }
- if (Nucleus._queue.indexOf($node) === -1) { Nucleus._queue.push($node); }
- if (!$node.Dirty) $node.Dirty = {};
- if (!(key in $node.Dirty)) {
-
- $node.Dirty[key] = Gene.freeze($node.Genotype[key]);
- }
- },
- };
- var God = {
-
- detect: function($context) {
-
- if ($context === undefined) $context = this;
- return Object.keys($context).filter(function(k) {
- try {
- if (/webkitStorageInfo|webkitIndexedDB/.test(k) || $context[k] instanceof $root.Element) return false;
- return $context[k] && Object.prototype.hasOwnProperty.call($context[k], '$cell');
- } catch (e) { return false; }
- }).map(function(k) {
- return $context[k];
- });
- },
- plan: function($context) {
-
-
- if ($context === undefined) $context = $root;
- else $root = $context;
- $context.DocumentFragment.prototype.$build = $context.Element.prototype.$build = function(gene, inheritance, index, namespace, replace) {
- var $node = Membrane.build(this, gene, index, namespace, replace);
- Genotype.build($node, gene, inheritance || [], index);
- Nucleus.build($node);
- Phenotype.build($node, $node.Genotype);
- return $node;
- };
- $context.DocumentFragment.prototype.$cell = $context.Element.prototype.$cell = function(gene, options) {
- return this.$build(gene, [], null, (options && options.namespace) || null, true);
- };
- $context.DocumentFragment.prototype.$snapshot = $context.Element.prototype.$snapshot = function() {
- var json = JSON.stringify(this.Genotype, function(k, v) {
- if (typeof v === 'function' && v.snapshot) { return '(' + v.snapshot.toString() + ')'; }
- return v;
- });
- return JSON.parse(json, function(k, v) {
- if (typeof v === 'string' && v.indexOf('function') >= 0) { return eval(v); }
- return v;
- });
- };
- if ($root.NodeList && !$root.NodeList.prototype.forEach) $root.NodeList.prototype.forEach = Array.prototype.forEach;
- },
- create: function($context) {
-
- return God.detect($context).map(function(gene) {
- return $context.document.body.$build(gene, []);
- });
- },
- };
-
- if (typeof exports !== 'undefined') {
- var x = {
- Phenotype: Phenotype,
- Genotype: Genotype,
- Nucleus: Nucleus,
- Gene: Gene,
- Membrane: Membrane,
- God: God,
- plan: God.plan.bind(God),
- create: God.create.bind(God),
- };
- if (typeof module !== 'undefined' && module.exports) { exports = module.exports = x; }
- exports = x;
- } else {
- God.plan(this);
- if (this.addEventListener) {
-
- this.addEventListener('load', function() {
- God.create(this);
- });
- }
- }
- }(this));
|