diff --git a/dist/moon.js b/dist/moon.js index 118031e3..e5bbcebc 100644 --- a/dist/moon.js +++ b/dist/moon.js @@ -42,6 +42,8 @@ var createTextNode = function (content) { return ("m.ctn(" + content + ");"); }; + var createComment = function () { return "m.cc();"; }; + var appendChild = function (element, parent) { return ("m.ac(m[" + element + "],m[" + parent + "]);"); }; var removeChild = function (element, parent) { return ("m.rc(m[" + element + "],m[" + parent + "]);"); }; @@ -54,11 +56,11 @@ var mOn = { order: 0, - create: function (code, directive, element) { - return code + addEventListener(element.index, directive.argument, ("function($event){" + (attributeValue(directive)) + "}")); + create: function (createCode, mountCode, directive, element) { + return [createCode + addEventListener(element.index, directive.argument, ("function($event){" + (attributeValue(directive)) + "}")), mountCode]; }, - mount: function (elementCode, childrenCode) { return [elementCode, childrenCode]; }, - update: function (code) { return code; } + update: function (updateCode) { return updateCode; }, + destroy: function (destroyCode) { return destroyCode; } }; var mIf = { @@ -284,7 +286,15 @@ pushChild({ index: stack[0].nextIndex++, type: "m-text", - content: content.replace(escapeRE, function (match) { return escapeMap[match]; }) + attributes: [{ + key: "content", + value: content.replace(escapeRE, function (match) { return escapeMap[match]; }), + argument: "", + expression: false, + dynamic: false + }], + directives: [], + children: [] }, stack); } @@ -307,9 +317,16 @@ pushChild({ index: stack[0].nextIndex++, - type: "m-expression", - content: expression, - dynamic: parseTemplate(expression, dependencies) + type: "m-text", + attributes: [{ + key: "content", + value: expression, + argument: "", + expression: true, + dynamic: parseTemplate(expression, dependencies) + }], + directives: [], + children: [] }, stack); return index; @@ -353,72 +370,72 @@ }; var generateCreate = function (element, parent, root) { + var createCode; + var mountCode = appendChild(element.index, parent.index); + switch (element.type) { - case "m-expression": - return assignElement(element.index, createTextNode(element.content)); + case "m-comment": + createCode = assignElement(element.index, createComment()); break; case "m-text": - return assignElement(element.index, createTextNode(("\"" + (element.content) + "\""))); + createCode = assignElement(element.index, createTextNode(attributeValue(element.attributes[0]))); break; default: - var elementDirectives = element.directives; - var code = assignElement(element.index, createElement(element.type) + mapReduce(element.attributes, function (attribute) { return setAttribute(element.index, attribute); }) + mapReduce(element.children, function (child) { return generateCreate(child, element, root); })); - - for (var i = 0; i < elementDirectives.length; i++) { - var elementDirective = elementDirectives[i]; - code = directives[elementDirective.key].create(code, elementDirective, element, parent, root); - } - - return code; + createCode = assignElement(element.index, createElement(element.type)) + mapReduce(element.attributes, function (attribute) { return setAttribute(element.index, attribute); }) + mapReduce(element.children, function (child) { return generateCreate(child, element, root); }); } - }; - var generateMount = function (element, parent, root) { - switch (element.type) { - case "m-expression": - case "m-text": - return appendChild(element.index, parent.index); - break; - default: - var elementDirectives = element.directives; - var elementCode = appendChild(element.index, parent.index); - var childrenCode = mapReduce(element.children, function (child) { return generateMount(child, element, root); }); - - for (var i = 0; i < elementDirectives.length; i++) { - var elementDirective = elementDirectives[i]; - var code = directives[elementDirective.key].mount(elementCode, childrenCode, elementDirective, element, parent, root); - elementCode = code[0]; - childrenCode = code[1]; - } + var elementDirectives = element.directives; - return elementCode + childrenCode; + for (var i = 0; i < elementDirectives.length; i++) { + var elementDirective = elementDirectives[i]; + var code = directives[elementDirective.key].create(createCode, mountCode, elementDirective, element, parent, root); + createCode = code[0]; + mountCode = code[1]; } + + return createCode + mountCode; }; var generateUpdate = function (element, parent, root) { + var updateCode; + switch (element.type) { - case "m-expression": - return element.dynamic ? setTextContent(element.index, element.content) : ""; + case "m-comment": + updateCode = ""; break; case "m-text": - return ""; + var content = element.attributes[0]; + updateCode = content.dynamic ? setTextContent(element.index, content.value) : ""; break; default: - var elementDirectives = element.directives; - var code = mapReduce(element.attributes, function (attribute) { return attribute.dynamic ? setAttribute(element.index, attribute) : ""; }) + mapReduce(element.children, function (child) { return generateUpdate(child, element, root); }); + updateCode = mapReduce(element.attributes, function (attribute) { return attribute.dynamic ? setAttribute(element.index, attribute) : ""; }) + mapReduce(element.children, function (child) { return generateUpdate(child, element, root); }); + } - for (var i = 0; i < elementDirectives.length; i++) { - var elementDirective = elementDirectives[i]; - code = directives[elementDirective.key].update(code, elementDirective, element, parent, root); - } + var elementDirectives = element.directives; - return code; + for (var i = 0; i < elementDirectives.length; i++) { + var elementDirective = elementDirectives[i]; + updateCode = directives[elementDirective.key].update(updateCode, elementDirective, element, parent, root); } + + return updateCode; + }; + + var generateDestroy = function (element, parent, root) { + var elementDirectives = element.directives; + var destroyCode = removeChild(element.index, parent.index); + + for (var i = 0; i < elementDirectives.length; i++) { + var elementDirective = elementDirectives[i]; + destroyCode = directives[elementDirective.key].destroy(destroyCode, elementDirective, element, parent, root); + } + + return destroyCode; }; var generate = function (tree) { var prelude = mapReduce(tree.dependencies, function (dependency) { return ("var " + dependency + "=this.data." + dependency + ";"); }); - return new Function(("return [function(m){this.m[0]=m;m=this.m;" + prelude + (mapReduce(tree.children, function (child) { return generateCreate(child, tree, tree); })) + (mapReduce(tree.children, function (child) { return generateMount(child, tree, tree); })) + "},function(){var m=this.m;" + prelude + (mapReduce(tree.children, function (child) { return generateUpdate(child, tree, tree); })) + "},function(){var m=this.m;" + (mapReduce(tree.children, function (child) { return removeChild(child.index, tree.index); })) + "m=[m[0]];}];"))(); + return new Function(("return [function(m){this.m[0]=m;m=this.m;" + prelude + (mapReduce(tree.children, function (child) { return generateCreate(child, tree, tree); })) + "},function(){var m=this.m;" + prelude + (mapReduce(tree.children, function (child) { return generateUpdate(child, tree, tree); })) + "},function(){var m=this.m;" + (mapReduce(tree.children, function (child) { return generateDestroy(child, tree, tree); })) + "m=[m[0]];}];"))(); }; var compile = function (input) { @@ -431,6 +448,8 @@ var createTextNode$1 = function (content) { return document.createTextNode(content); }; + var createComment$1 = function () { return document.createComment(""); }; + var appendChild$1 = function (element, parent) { parent.appendChild(element); }; @@ -460,6 +479,7 @@ m.c = components; m.ce = createElement$1; m.ctn = createTextNode$1; + m.cc = createComment$1; m.ac = appendChild$1; m.rc = removeChild$1; m.in = insertNode$1; diff --git a/dist/moon.min.js b/dist/moon.min.js index 7629fc47..09ad9c18 100644 --- a/dist/moon.min.js +++ b/dist/moon.min.js @@ -4,4 +4,4 @@ * Released under the MIT License * https://kbrsh.github.io/moon */ -!function(t,e){"undefined"==typeof module?t.Moon=e():module.exports=e()}(this,function(){"use strict";var c=function(t,e,n){for(;t"===u){t+=3;break}t+=1}}return t},f=function(t,n){return t.reduce(function(t,e){return t+n(e)},"")},a=function(t,e){return"m["+t+"]="+e},o=function(t){return t.expression?t.value:'"'+t.value+'"'},d=function(t){return"m.ctn("+t+");"},s=function(t,e){return"m.ac(m["+t+"],m["+e+"]);"},v=function(t,e){return"m.sa(m["+t+'],"'+e.key+'",'+o(e)+");"},m={"m-on":{order:0,create:function(t,e,n){return t+(i=n.index,r=e.argument,u="function($event){"+o(e)+"}","m.ael(m["+i+'],"'+r+'",'+u+");");var i,r,u},mount:function(t,e){return[t,e]},update:function(t){return t}},"m-if":{}},u=/"[^"]*"|'[^']*'|\d+[a-zA-Z$_]\w*|\.[a-zA-Z$_]\w*|[a-zA-Z$_]\w*:|([a-zA-Z$_]\w*)/g,h=["NaN","event","false","in","m","null","this","true","typeof","undefined"],p=function(t,e){for(var n,i=!1;null!==(n=u.exec(t));){var r=n[1];void 0!==r&&-1===h.indexOf(r)&&"$"!==r[0]&&(i=!0,-1===e.indexOf(r)&&e.push(r))}return i},t={silent:!0},l=/\s+/,x=function(t,e){e[e.length-1].children.push(t)},y=function(t){return t.sort(function(t,e){return m[t.key].order-m[e.key].order})},b=function(t,e,n,i,r,u){for(;t"===o)break;if(l.test(o))t+=1;else{for(var c="",a="",f="",d=!1;t"===o||l.test(o)){f=c;break}if("="===o){t+=1;break}":"===o&&"m"===c[0]&&"-"===c[1]?(a+=e[t+1],t+=2):(0!==a.length?a+=o:c+=o,t+=1)}if(0===f.length){var s=void 0;for('"'===(o=e[t])||"'"===o?(s=o,t+=1):"{"===o?(s="}",d=!0,t+=1):s=l;t"!==o;){if("object"==typeof s&&s.test(o)||o===s){t+=1;break}f+=o,t+=1}}("m"===c[0]&&"-"===c[1]?r:i).push({key:c,value:f,argument:a,expression:d,dynamic:d&&p(f,u)})}}return t},g=function(t,e,n,i,r){for(var u="",o=[],c=[];t"===a){var f={index:i[0].nextIndex++,type:u,attributes:o,directives:y(c),children:[]};x(f,i),i.push(f),t+=1;break}if("/"===a&&">"===e[t+1]){x({index:i[0].nextIndex++,type:u,attributes:o,directives:y(c),children:[]},i),t+=2;break}l.test(a)?t=b(t+1,e,n,o=[],c=[],r):(u+=a,t+=1)}return t},w=function(t,e,n,i){for(;t"===r){t+=1;break}r}var u=i.pop();return u.type,t},k=/(?:(?:&(?:amp|gt|lt|nbsp|quot);)|"|\\|\n)/g,q={"&":"&",">":">","<":"<"," ":" ",""":'\\"',"\\":"\\\\",'"':'\\"',"\n":"\\n"},$=function(t,e,n,i){for(var r="";t"===u){t+=3;break}t+=1}}return t},s=function(t,n){return t.reduce(function(t,e){return t+n(e)},"")},v=function(t,e){return"m["+t+"]="+e},m=function(t){return t.expression?t.value:'"'+t.value+'"'},h=function(t,e){return"m.sa(m["+t+'],"'+e.key+'",'+m(e)+");"},l={"m-on":{order:0,create:function(t,e,n,i){return[t+(r=i.index,u=n.argument,o="function($event){"+m(n)+"}","m.ael(m["+r+'],"'+u+'",'+o+");"),e];var r,u,o},update:function(t){return t},destroy:function(t){return t}},"m-if":{}},u=/"[^"]*"|'[^']*'|\d+[a-zA-Z$_]\w*|\.[a-zA-Z$_]\w*|[a-zA-Z$_]\w*:|([a-zA-Z$_]\w*)/g,o=["NaN","event","false","in","m","null","this","true","typeof","undefined"],p=function(t,e){for(var n,i=!1;null!==(n=u.exec(t));){var r=n[1];void 0!==r&&-1===o.indexOf(r)&&"$"!==r[0]&&(i=!0,-1===e.indexOf(r)&&e.push(r))}return i},t={silent:!0},x=/\s+/,d=function(t,e){e[e.length-1].children.push(t)},y=function(t){return t.sort(function(t,e){return l[t.key].order-l[e.key].order})},b=function(t,e,n,i,r,u){for(;t"===o)break;if(x.test(o))t+=1;else{for(var a="",c="",f="",d=!1;t"===o||x.test(o)){f=a;break}if("="===o){t+=1;break}":"===o&&"m"===a[0]&&"-"===a[1]?(c+=e[t+1],t+=2):(0!==c.length?c+=o:a+=o,t+=1)}if(0===f.length){var s=void 0;for('"'===(o=e[t])||"'"===o?(s=o,t+=1):"{"===o?(s="}",d=!0,t+=1):s=x;t"!==o;){if("object"==typeof s&&s.test(o)||o===s){t+=1;break}f+=o,t+=1}}("m"===a[0]&&"-"===a[1]?r:i).push({key:a,value:f,argument:c,expression:d,dynamic:d&&p(f,u)})}}return t},c=function(t,e,n,i,r){for(var u="",o=[],a=[];t"===c){var f={index:i[0].nextIndex++,type:u,attributes:o,directives:y(a),children:[]};d(f,i),i.push(f),t+=1;break}if("/"===c&&">"===e[t+1]){d({index:i[0].nextIndex++,type:u,attributes:o,directives:y(a),children:[]},i),t+=2;break}x.test(c)?t=b(t+1,e,n,o=[],a=[],r):(u+=c,t+=1)}return t},f=function(t,e,n,i){for(;t"===r){t+=1;break}r}var u=i.pop();return u.type,t},k=/(?:(?:&(?:amp|gt|lt|nbsp|quot);)|"|\\|\n)/g,g={"&":"&",">":">","<":"<"," ":" ",""":'\\"',"\\":"\\\\",'"':'\\"',"\n":"\\n"},w=function(t,e,n,i){for(var r="";t { - return code + addEventListener(element.index, directive.argument, `function($event){${attributeValue(directive)}}`); + create: (createCode, mountCode, directive, element) => { + return [createCode + addEventListener(element.index, directive.argument, `function($event){${attributeValue(directive)}}`), mountCode]; }, - mount: (elementCode, childrenCode) => [elementCode, childrenCode], - update: (code) => code + update: (updateCode) => updateCode, + destroy: (destroyCode) => destroyCode }; diff --git a/src/compiler/generator/create.js b/src/compiler/generator/create.js index 3818f8e8..8d8ed6bc 100644 --- a/src/compiler/generator/create.js +++ b/src/compiler/generator/create.js @@ -1,23 +1,29 @@ import { directives } from "../directives/directives"; -import { assignElement, createElement, createTextNode, setAttribute, mapReduce } from "./util"; +import { assignElement, attributeValue, createElement, createTextNode, createComment, appendChild, setAttribute, mapReduce } from "./util"; export const generateCreate = (element, parent, root) => { + let createCode; + let mountCode = appendChild(element.index, parent.index); + switch (element.type) { - case "m-expression": - return assignElement(element.index, createTextNode(element.content)); + case "m-comment": + createCode = assignElement(element.index, createComment()); break; case "m-text": - return assignElement(element.index, createTextNode(`"${element.content}"`)); + createCode = assignElement(element.index, createTextNode(attributeValue(element.attributes[0]))); break; default: - const elementDirectives = element.directives; - let code = assignElement(element.index, createElement(element.type) + mapReduce(element.attributes, (attribute) => setAttribute(element.index, attribute)) + mapReduce(element.children, (child) => generateCreate(child, element, root))); + createCode = assignElement(element.index, createElement(element.type)) + mapReduce(element.attributes, (attribute) => setAttribute(element.index, attribute)) + mapReduce(element.children, (child) => generateCreate(child, element, root)); + } - for (let i = 0; i < elementDirectives.length; i++) { - const elementDirective = elementDirectives[i]; - code = directives[elementDirective.key].create(code, elementDirective, element, parent, root); - } + const elementDirectives = element.directives; - return code; + for (let i = 0; i < elementDirectives.length; i++) { + const elementDirective = elementDirectives[i]; + const code = directives[elementDirective.key].create(createCode, mountCode, elementDirective, element, parent, root); + createCode = code[0]; + mountCode = code[1]; } + + return createCode + mountCode; }; diff --git a/src/compiler/generator/destroy.js b/src/compiler/generator/destroy.js new file mode 100644 index 00000000..fe64d803 --- /dev/null +++ b/src/compiler/generator/destroy.js @@ -0,0 +1,14 @@ +import { directives } from "../directives/directives"; +import { removeChild } from "./util"; + +export const generateDestroy = (element, parent, root) => { + const elementDirectives = element.directives; + let destroyCode = removeChild(element.index, parent.index); + + for (let i = 0; i < elementDirectives.length; i++) { + const elementDirective = elementDirectives[i]; + destroyCode = directives[elementDirective.key].destroy(destroyCode, elementDirective, element, parent, root); + } + + return destroyCode; +}; diff --git a/src/compiler/generator/generator.js b/src/compiler/generator/generator.js index bca81873..507d22b1 100644 --- a/src/compiler/generator/generator.js +++ b/src/compiler/generator/generator.js @@ -1,9 +1,9 @@ -import { removeChild, mapReduce } from "./util"; import { generateCreate } from "./create"; -import { generateMount } from "./mount"; import { generateUpdate } from "./update"; +import { generateDestroy } from "./destroy"; +import { mapReduce } from "./util"; export const generate = (tree) => { const prelude = mapReduce(tree.dependencies, (dependency) => `var ${dependency}=this.data.${dependency};`); - return new Function(`return [function(m){this.m[0]=m;m=this.m;${prelude}${mapReduce(tree.children, (child) => generateCreate(child, tree, tree))}${mapReduce(tree.children, (child) => generateMount(child, tree, tree))}},function(){var m=this.m;${prelude}${mapReduce(tree.children, (child) => generateUpdate(child, tree, tree))}},function(){var m=this.m;${mapReduce(tree.children, (child) => removeChild(child.index, tree.index))}m=[m[0]];}];`)(); + return new Function(`return [function(m){this.m[0]=m;m=this.m;${prelude}${mapReduce(tree.children, (child) => generateCreate(child, tree, tree))}},function(){var m=this.m;${prelude}${mapReduce(tree.children, (child) => generateUpdate(child, tree, tree))}},function(){var m=this.m;${mapReduce(tree.children, (child) => generateDestroy(child, tree, tree))}m=[m[0]];}];`)(); }; diff --git a/src/compiler/generator/mount.js b/src/compiler/generator/mount.js deleted file mode 100644 index 3f57be19..00000000 --- a/src/compiler/generator/mount.js +++ /dev/null @@ -1,24 +0,0 @@ -import { directives } from "../directives/directives"; -import { appendChild, mapReduce } from "./util"; - -export const generateMount = (element, parent, root) => { - switch (element.type) { - case "m-expression": - case "m-text": - return appendChild(element.index, parent.index); - break; - default: - const elementDirectives = element.directives; - let elementCode = appendChild(element.index, parent.index); - let childrenCode = mapReduce(element.children, (child) => generateMount(child, element, root)); - - for (let i = 0; i < elementDirectives.length; i++) { - const elementDirective = elementDirectives[i]; - const code = directives[elementDirective.key].mount(elementCode, childrenCode, elementDirective, element, parent, root); - elementCode = code[0]; - childrenCode = code[1]; - } - - return elementCode + childrenCode; - } -}; diff --git a/src/compiler/generator/update.js b/src/compiler/generator/update.js index 3c1a1171..b377e0af 100644 --- a/src/compiler/generator/update.js +++ b/src/compiler/generator/update.js @@ -2,22 +2,26 @@ import { directives } from "../directives/directives"; import { setAttribute, setTextContent, mapReduce } from "./util"; export const generateUpdate = (element, parent, root) => { + let updateCode; + switch (element.type) { - case "m-expression": - return element.dynamic ? setTextContent(element.index, element.content) : ""; + case "m-comment": + updateCode = ""; break; case "m-text": - return ""; + const content = element.attributes[0]; + updateCode = content.dynamic ? setTextContent(element.index, content.value) : ""; break; default: - const elementDirectives = element.directives; - let code = mapReduce(element.attributes, (attribute) => attribute.dynamic ? setAttribute(element.index, attribute) : "") + mapReduce(element.children, (child) => generateUpdate(child, element, root)); + updateCode = mapReduce(element.attributes, (attribute) => attribute.dynamic ? setAttribute(element.index, attribute) : "") + mapReduce(element.children, (child) => generateUpdate(child, element, root)); + } - for (let i = 0; i < elementDirectives.length; i++) { - const elementDirective = elementDirectives[i]; - code = directives[elementDirective.key].update(code, elementDirective, element, parent, root); - } + const elementDirectives = element.directives; - return code; + for (let i = 0; i < elementDirectives.length; i++) { + const elementDirective = elementDirectives[i]; + updateCode = directives[elementDirective.key].update(updateCode, elementDirective, element, parent, root); } + + return updateCode; }; diff --git a/src/compiler/generator/util.js b/src/compiler/generator/util.js index 7ad4d6ef..1a720b56 100644 --- a/src/compiler/generator/util.js +++ b/src/compiler/generator/util.js @@ -8,6 +8,8 @@ export const createElement = (type) => `m.ce("${type}");`; export const createTextNode = (content) => `m.ctn(${content});`; +export const createComment = () => `m.cc();`; + export const appendChild = (element, parent) => `m.ac(m[${element}],m[${parent}]);`; export const removeChild = (element, parent) => `m.rc(m[${element}],m[${parent}]);`; diff --git a/src/compiler/parser/expression.js b/src/compiler/parser/expression.js index 45041d3f..64413708 100644 --- a/src/compiler/parser/expression.js +++ b/src/compiler/parser/expression.js @@ -17,9 +17,16 @@ export const parseExpression = (index, input, length, stack, dependencies) => { pushChild({ index: stack[0].nextIndex++, - type: "m-expression", - content: expression, - dynamic: parseTemplate(expression, dependencies) + type: "m-text", + attributes: [{ + key: "content", + value: expression, + argument: "", + expression: true, + dynamic: parseTemplate(expression, dependencies) + }], + directives: [], + children: [] }, stack); return index; diff --git a/src/compiler/parser/text.js b/src/compiler/parser/text.js index 60ce9f1c..d4a50ff6 100644 --- a/src/compiler/parser/text.js +++ b/src/compiler/parser/text.js @@ -29,7 +29,15 @@ export const parseText = (index, input, length, stack) => { pushChild({ index: stack[0].nextIndex++, type: "m-text", - content: content.replace(escapeRE, (match) => escapeMap[match]) + attributes: [{ + key: "content", + value: content.replace(escapeRE, (match) => escapeMap[match]), + argument: "", + expression: false, + dynamic: false + }], + directives: [], + children: [] }, stack); } diff --git a/src/util/m.js b/src/util/m.js index a4a9dda1..aa6db641 100644 --- a/src/util/m.js +++ b/src/util/m.js @@ -4,6 +4,8 @@ const createElement = (type) => document.createElement(type); const createTextNode = (content) => document.createTextNode(content); +const createComment = () => document.createComment(""); + const appendChild = (element, parent) => { parent.appendChild(element); }; @@ -33,6 +35,7 @@ export const m = () => { m.c = components; m.ce = createElement; m.ctn = createTextNode; + m.cc = createComment; m.ac = appendChild; m.rc = removeChild; m.in = insertNode;