From c0a1e6044741c9847efa701f4b03c950640fb359 Mon Sep 17 00:00:00 2001 From: Dev_Bon_Bon Date: Tue, 29 Jan 2019 18:34:06 +0200 Subject: [PATCH 01/10] Tests: Don't check if the Proxy API is defined --- test/index.js | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-) diff --git a/test/index.js b/test/index.js index 5671352..6a10c1c 100644 --- a/test/index.js +++ b/test/index.js @@ -222,32 +222,21 @@ test('Test that `isElement` is defined', function (t) { }); // -- Test the Proxy API -- -test('Test that the Proxy API is defined', function (t) { +test('Test that the Proxy API works', function (t) { if (typeof Proxy === 'undefined') { t.plan(1) t.pass('Proxies are not supported in the current environment'); } else { - var proxyCrel = crel.proxy; + // I'm not proficient with proxies, so + // TODO: Add #moar-tests + t.plan(4); - t.plan(proxyCrel ? 2 : 1); + var testElement = crel.proxy.div({'class': 'test'}, + crel.proxy.span('test')); - t.ok(proxyCrel, 'The Proxy API is defined'); - - if (proxyCrel) { - // Do further tests - t.test('Test that the Proxy API works', function (ts) { - // I'm not proficient with proxies, so - // TODO: Add #moar-tests - ts.plan(4); - - var testElement = proxyCrel.div({'class': 'test'}, - proxyCrel.span('test')); - - ts.equal(testElement.className, 'test'); - ts.equal(testElement.childNodes.length, 1); - ts.equal(testElement.childNodes[0].tagName, 'SPAN'); - ts.equal(testElement.childNodes[0].textContent, 'test'); - }); - } + t.equal(testElement.className, 'test'); + t.equal(testElement.childNodes.length, 1); + t.equal(testElement.childNodes[0].tagName, 'SPAN'); + t.equal(testElement.childNodes[0].textContent, 'test'); } }); From aefaae37d58758e071b71cc6ff70e4d720a6d82a Mon Sep 17 00:00:00 2001 From: Dev_Bon_Bon Date: Thu, 31 Jan 2019 02:44:22 +0200 Subject: [PATCH 02/10] Tests: Import Crel to global context for manual testing --- test/test.html | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test.html b/test/test.html index d6b99f3..cb90462 100644 --- a/test/test.html +++ b/test/test.html @@ -12,6 +12,7 @@ }; + From e58a3f848466619a53812f7ba60ebb97fb5026dd Mon Sep 17 00:00:00 2001 From: Dev_Bon_Bon Date: Thu, 31 Jan 2019 02:48:16 +0200 Subject: [PATCH 03/10] Crel: Use `target` instead of `crel` in proxy interface --- crel.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crel.js b/crel.js index 9cba0f3..6d80426 100644 --- a/crel.js +++ b/crel.js @@ -74,8 +74,8 @@ This might make it harder to read at times, but the code's intention should be t // Expose proxy interface crel.proxy = new Proxy(crel, { get: (target, key) => { - !(key in crel) && (crel[key] = crel.bind(null, key)); - return crel[key]; + !(key in target) && (target[key] = target.bind(null, key)); + return target[key]; } }); // Export crel From 3bb94e90dd3f29f435cc6f68034f5787326519bc Mon Sep 17 00:00:00 2001 From: Dev_Bon_Bon Date: Thu, 31 Jan 2019 02:49:54 +0200 Subject: [PATCH 04/10] Crel: Turn `crel` into a `Proxy` --- crel.js | 86 +++++++++++++++++++++++++++------------------------------ 1 file changed, 41 insertions(+), 45 deletions(-) diff --git a/crel.js b/crel.js index 6d80426..62d6dae 100644 --- a/crel.js +++ b/crel.js @@ -9,7 +9,6 @@ This might make it harder to read at times, but the code's intention should be t // IIFE our function ((exporter) => { - // Define our function and its properties // These strings are used multiple times, so this makes things smaller once compiled const func = 'function', isNodeString = 'isNode', @@ -28,56 +27,53 @@ This might make it harder to read at times, but the code's intention should be t element.appendChild(child); } } - }; - // - function crel (element, settings) { - // Define all used variables / shortcuts here, to make things smaller once compiled - let args = arguments, // Note: assigned to a variable to assist compilers. - index = 1, - key, - attribute; - // If first argument is an element, use it as is, otherwise treat it as a tagname - element = crel.isElement(element) ? element : d.createElement(element); - // Check if second argument is a settings object - if (isType(settings, 'object') && !crel[isNodeString](settings) && !Array.isArray(settings)) { - // Don't treat settings as a child - index++; - // Go through settings / attributes object, if it exists - for (key in settings) { - // Store the attribute into a variable, before we potentially modify the key - attribute = settings[key]; - // Get mapped key / function, if one exists - key = crel.attrMap[key] || key; - // Note: We want to prioritise mapping over properties - if (isType(key, func)) { - key(element, attribute); - } else if (isType(attribute, func)) { // ex. onClick property - element[key] = attribute; - } else { - // Set the element attribute - element.setAttribute(key, attribute); + }, + // Define our function as a proxy interface + crel = new Proxy(function (element, settings) { + // Define all used variables / shortcuts here, to make things smaller once compiled + let args = arguments, // Note: assigned to a variable to assist compilers. + index = 1, + key, + attribute; + // If first argument is an element, use it as is, otherwise treat it as a tagname + element = crel.isElement(element) ? element : d.createElement(element); + // Check if second argument is a settings object + if (isType(settings, 'object') && !crel[isNodeString](settings) && !Array.isArray(settings)) { + // Don't treat settings as a child + index++; + // Go through settings / attributes object, if it exists + for (key in settings) { + // Store the attribute into a variable, before we potentially modify the key + attribute = settings[key]; + // Get mapped key / function, if one exists + key = crel.attrMap[key] || key; + // Note: We want to prioritise mapping over properties + if (isType(key, func)) { + key(element, attribute); + } else if (isType(attribute, func)) { // ex. onClick property + element[key] = attribute; + } else { + // Set the element attribute + element.setAttribute(key, attribute); + } } } - } - // Loop through all arguments, if any, and append them to our element if they're not `null` - for (; index < args.length; index++) { - appendChild(element, args[index]); - } - - return element; - } + // Loop through all arguments, if any, and append them to our element if they're not `null` + for (; index < args.length; index++) { + appendChild(element, args[index]); + } - // Used for mapping attribute keys to supported versions in bad browsers, or to custom functionality + return element; + }, {// Binds specific tagnames to crel function calls with that tag as the first argument + get: (target, key) => { + !(key in target) && (target[key] = target.bind(null, key)); + return target[key]; + } + }); + // Used for mapping attribute keys to custom functionality, or to supported versions in bad browsers crel.attrMap = {}; crel.isElement = object => object instanceof Element; crel[isNodeString] = node => node instanceof Node; - // Expose proxy interface - crel.proxy = new Proxy(crel, { - get: (target, key) => { - !(key in target) && (target[key] = target.bind(null, key)); - return target[key]; - } - }); // Export crel exporter(crel, func); })((product, func) => { From c65e364c7a476fb738165dce4691f856e3a95a30 Mon Sep 17 00:00:00 2001 From: Dev_Bon_Bon Date: Thu, 31 Jan 2019 03:11:42 +0200 Subject: [PATCH 05/10] Crel: Support old `crel.proxy` calls --- crel.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/crel.js b/crel.js index 62d6dae..13e0342 100644 --- a/crel.js +++ b/crel.js @@ -12,6 +12,7 @@ This might make it harder to read at times, but the code's intention should be t // These strings are used multiple times, so this makes things smaller once compiled const func = 'function', isNodeString = 'isNode', + proxyString = 'proxy', d = document, // Helper functions used throughout the script isType = (object, type) => typeof object === type, @@ -66,14 +67,21 @@ This might make it harder to read at times, but the code's intention should be t return element; }, {// Binds specific tagnames to crel function calls with that tag as the first argument get: (target, key) => { - !(key in target) && (target[key] = target.bind(null, key)); - return target[key]; + if (key in target) { + return target[key]; + } + if (!(key in target[proxyString])) { + target[proxyString][key] = target.bind(null, key); + } + return target[proxyString][key]; } }); // Used for mapping attribute keys to custom functionality, or to supported versions in bad browsers crel.attrMap = {}; crel.isElement = object => object instanceof Element; crel[isNodeString] = node => node instanceof Node; + // Bound functions are "cached" here for legacy support and to keep Crels internal structure clean + crel[proxyString] = new Proxy({}, { get: (target, key) => target[key] || crel[key] }); // Export crel exporter(crel, func); })((product, func) => { From 09881c46198ee01c294c59d25149932384ef57dc Mon Sep 17 00:00:00 2001 From: Dev_Bon_Bon Date: Thu, 31 Jan 2019 03:34:35 +0200 Subject: [PATCH 06/10] Crel: Add tag / key transformations --- crel.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crel.js b/crel.js index 13e0342..09482ce 100644 --- a/crel.js +++ b/crel.js @@ -13,6 +13,7 @@ This might make it harder to read at times, but the code's intention should be t const func = 'function', isNodeString = 'isNode', proxyString = 'proxy', + tagTransformString = 'tagTransform', d = document, // Helper functions used throughout the script isType = (object, type) => typeof object === type, @@ -70,6 +71,7 @@ This might make it harder to read at times, but the code's intention should be t if (key in target) { return target[key]; } + key = target[tagTransformString](key); if (!(key in target[proxyString])) { target[proxyString][key] = target.bind(null, key); } @@ -82,6 +84,8 @@ This might make it harder to read at times, but the code's intention should be t crel[isNodeString] = node => node instanceof Node; // Bound functions are "cached" here for legacy support and to keep Crels internal structure clean crel[proxyString] = new Proxy({}, { get: (target, key) => target[key] || crel[key] }); + // Transforms tags on call, to for example allow dashes in tags + crel[tagTransformString] = key => key; // Export crel exporter(crel, func); })((product, func) => { From ff16ebcc0f153d2923d7db6c67dc4d7d41a508f5 Mon Sep 17 00:00:00 2001 From: Dev_Bon_Bon Date: Thu, 31 Jan 2019 04:09:24 +0200 Subject: [PATCH 07/10] Crel: Turn handler into an arrow function --- crel.js | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/crel.js b/crel.js index 09482ce..9a6e4a4 100644 --- a/crel.js +++ b/crel.js @@ -17,7 +17,7 @@ This might make it harder to read at times, but the code's intention should be t d = document, // Helper functions used throughout the script isType = (object, type) => typeof object === type, - // Recursively appends children to given element. As a text node if not already an element + // Recursively appends children to given element if they're not `null`. As a text node if not already an element appendChild = (element, child) => { if (child !== null) { if (Array.isArray(child)) { // Support (deeply) nested child elements @@ -31,10 +31,9 @@ This might make it harder to read at times, but the code's intention should be t } }, // Define our function as a proxy interface - crel = new Proxy(function (element, settings) { + crel = new Proxy((element, ...children) => { // Define all used variables / shortcuts here, to make things smaller once compiled - let args = arguments, // Note: assigned to a variable to assist compilers. - index = 1, + let settings = children[0], key, attribute; // If first argument is an element, use it as is, otherwise treat it as a tagname @@ -42,7 +41,7 @@ This might make it harder to read at times, but the code's intention should be t // Check if second argument is a settings object if (isType(settings, 'object') && !crel[isNodeString](settings) && !Array.isArray(settings)) { // Don't treat settings as a child - index++; + children.shift(); // Go through settings / attributes object, if it exists for (key in settings) { // Store the attribute into a variable, before we potentially modify the key @@ -60,11 +59,8 @@ This might make it harder to read at times, but the code's intention should be t } } } - // Loop through all arguments, if any, and append them to our element if they're not `null` - for (; index < args.length; index++) { - appendChild(element, args[index]); - } - + // Append remaining children to element and return it + appendChild(element, children); return element; }, {// Binds specific tagnames to crel function calls with that tag as the first argument get: (target, key) => { From e578b38c04228bfbf6260d90b5250ffa06910dd1 Mon Sep 17 00:00:00 2001 From: Dev_Bon_Bon Date: Sat, 2 Feb 2019 16:57:00 +0200 Subject: [PATCH 08/10] Tests: Add test for tag / key transformation --- test/index.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/index.js b/test/index.js index 6a10c1c..84ad205 100644 --- a/test/index.js +++ b/test/index.js @@ -240,3 +240,17 @@ test('Test that the Proxy API works', function (t) { t.equal(testElement.childNodes[0].textContent, 'test'); } }); + +// -- Test the Proxy APIs features -- +test('Test the proxy APIs tag transformations', (t) => { + t.plan(4); + + crel.tagTransform = (key) => key.replace(/([0-9a-z])([A-Z])/g, '$1-$2').toLowerCase(); + let testElement = crel.myTable(crel.span('test')); + + t.equal(testElement.tagName, 'MY-TABLE', + 'tagname had dashes added to it'); + t.equal(testElement.childNodes.length, 1); + t.equal(testElement.childNodes[0].tagName, 'SPAN'); + t.equal(testElement.childNodes[0].textContent, 'test'); +}); From 0e6c2820ebbabdf3eff13a8547d9e90716ef23b2 Mon Sep 17 00:00:00 2001 From: Dev_Bon_Bon Date: Sun, 3 Feb 2019 00:11:25 +0200 Subject: [PATCH 09/10] README: Update proxy example --- README.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4ef8818..626b308 100644 --- a/README.md +++ b/README.md @@ -124,8 +124,6 @@ _But don't._ If you are using Crel in an environment that supports Proxies, you can also use the new API: ```javascript -let crel = require('crel').proxy; - let element = crel.div( crel.h1('Crello World!'), crel.p('This is crel'), @@ -133,6 +131,17 @@ let element = crel.div( ); ``` +If you want to transform tags to for example get dashes in them, you can define a `tagTransform` function: + +```javascript +// Adds dashes on camelCase, ex: `camelCase` -> `camel-case` +crel.tagTransform = key => key.replace(/([0-9a-z])([A-Z])/g, '$1-$2'); + +let element = crel.myTag('Crello World!'); + +console.log(element.tagName); // my-tag +``` + # Browser support Crel uses ES6 features, so it'll work in all evergreen browsers. From d55a3f19f6b4b0da9074781224557dd85913aff1 Mon Sep 17 00:00:00 2001 From: Dev_Bon_Bon Date: Sun, 3 Feb 2019 00:22:26 +0200 Subject: [PATCH 10/10] Rebuild crel.min.js --- crel.min.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crel.min.js b/crel.min.js index d17ea8c..98b8387 100644 --- a/crel.min.js +++ b/crel.min.js @@ -1 +1 @@ -(e=>{const t="function",n="isNode",r=document,o=(e,t)=>typeof e===t,i=(e,t)=>{null!==t&&(Array.isArray(t)?t.map(t=>i(e,t)):(a[n](t)||(t=r.createTextNode(t)),e.appendChild(t)))};function a(e,f){let l,d,s=arguments,c=1;if(e=a.isElement(e)?e:r.createElement(e),o(f,"object")&&!a[n](f)&&!Array.isArray(f))for(l in c++,f)d=f[l],l=a.attrMap[l]||l,o(l,t)?l(e,d):o(d,t)?e[l]=d:e.setAttribute(l,d);for(;ce instanceof Element),a[n]=(e=>e instanceof Node),a.proxy=new Proxy(a,{get:(e,t)=>(!(t in a)&&(a[t]=a.bind(null,t)),a[t])}),e(a,t)})((e,t)=>{"object"==typeof exports?module.exports=e:typeof define===t&&define.amd?define(e):this.crel=e}); \ No newline at end of file +(e=>{const t="function",n="isNode",r="proxy",o="tagTransform",i=document,a=(e,t)=>typeof e===t,s=(e,t)=>{null!==t&&(Array.isArray(t)?t.map(t=>s(e,t)):(f[n](t)||(t=i.createTextNode(t)),e.appendChild(t)))},f=new Proxy((e,...r)=>{let o,d,l=r[0];if(e=f.isElement(e)?e:i.createElement(e),a(l,"object")&&!f[n](l)&&!Array.isArray(l))for(o in r.shift(),l)d=l[o],a(o=f.attrMap[o]||o,t)?o(e,d):a(d,t)?e[o]=d:e.setAttribute(o,d);return s(e,r),e},{get:(e,t)=>t in e?e[t]:((t=e[o](t))in e[r]||(e[r][t]=e.bind(null,t)),e[r][t])});f.attrMap={},f.isElement=(e=>e instanceof Element),f[n]=(e=>e instanceof Node),f[r]=new Proxy({},{get:(e,t)=>e[t]||f[t]}),f[o]=(e=>e),e(f,t)})((e,t)=>{"object"==typeof exports?module.exports=e:typeof define===t&&define.amd?define(()=>e):this.crel=e}); \ No newline at end of file