lemonadejs
Advanced tools
Comparing version 3.5.0 to 4.0.0-beta.0
/** | ||
* Lemonadejs v3.5.0 (ESM build) | ||
* LemonadeJS v4.0.0 (ESM build) | ||
* | ||
@@ -7,3 +7,3 @@ * Website: https://lemonadejs.net | ||
* | ||
* This software is distribute under MIT License | ||
* This software is distributed under MIT License | ||
*/ | ||
@@ -37,24 +37,32 @@ function Lemonade() { | ||
/** | ||
* Path string to object | ||
* @param {string} str - path to the value as a string | ||
* @param {boolean} onlyObject - get the last valid object in the nested object | ||
* @return {array|boolean} - return the object and the property | ||
* Extract a property from a nested object using a string address | ||
* @param {string} str address inside the nested object | ||
* @param {boolean} config get the configuration obj => property | ||
*/ | ||
const Path = function(str, onlyObject) { | ||
let t = str.split('.'); | ||
if (t.length) { | ||
const extractFromPath = function(str, config) { | ||
try { | ||
let t = str.toString().replace(/[\[\]]/g, '.').split('.'); | ||
if (t[0] === 'self') { | ||
t.shift(); | ||
} | ||
// Remove blanks | ||
t = t.filter(item => item !== ''); | ||
// Object | ||
let o = this; | ||
// Property | ||
let p = null; | ||
while (t.length > 1) { | ||
let lastObject; | ||
while (t.length) { | ||
// Get the property | ||
p = t.shift(); | ||
let p = t.shift(); | ||
// Process config | ||
if (config) { | ||
if (typeof(o) === 'object' && ! Array.isArray(o)) { | ||
lastObject = [o,p]; | ||
} | ||
if (t.length === 0) { | ||
return lastObject; | ||
} | ||
} | ||
// Check if the property exists | ||
if (o.hasOwnProperty(p)) { | ||
if (! onlyObject || typeof(o[p]) === 'object' && ! Array.isArray(o[p])) { | ||
o = o[p]; | ||
} else { | ||
return [o,p]; | ||
} | ||
o = o[p]; | ||
} else { | ||
@@ -64,14 +72,52 @@ return undefined; | ||
} | ||
// Get the property | ||
p = t.shift(); | ||
// Return the value | ||
if (o) { | ||
return [o,p]; | ||
if (typeof(o) !== 'undefined') { | ||
return o; | ||
} | ||
} | ||
} catch (e) {} | ||
// Something went wrong | ||
return false; | ||
return undefined; | ||
} | ||
/** | ||
* Cast the value of an attribute | ||
*/ | ||
const castProperty = function(attr) { | ||
// Parse type | ||
try { | ||
if (typeof(attr) === 'string' && attr) { | ||
// Remove any white spaces | ||
attr = attr.trim(); | ||
if (attr === 'true') { | ||
return true; | ||
} else if (attr === 'false') { | ||
return false; | ||
} else if (! isNaN(attr)) { | ||
return Number(attr); | ||
} else if (attr.substring(0, 1) === '{') { | ||
if (attr.slice(-1) === '}') { | ||
return JSON.parse(attr); | ||
} | ||
} else if (attr.substring(0, 5) === 'self.') { | ||
let v = extractFromPath.call(this, attr); | ||
if (typeof(v) !== 'undefined') { | ||
return v; | ||
} | ||
} | ||
} | ||
} catch (e) {} | ||
return attr; | ||
} | ||
/** | ||
* This allows to run inline script on legacy system. Inline script can lead to security issues so use carefully. | ||
* @param {string} s string to function | ||
*/ | ||
const run = function(s) { | ||
return Function('self', '"use strict";return (' + s + ')')(this); | ||
} | ||
/** | ||
* Create a new HTML element | ||
@@ -266,3 +312,3 @@ * @param {string} type - create a new HTML element as a type | ||
let a = this.attributes; | ||
if (a && a.length) { | ||
if (a && a.length) { | ||
for (let i = 0; i < a.length; i++) { | ||
@@ -281,15 +327,2 @@ k = a[i].name; | ||
/** | ||
* Parse javascript | ||
* @param {string} s - string | ||
* @return {any} | ||
*/ | ||
const run = function(s) { | ||
return Function('self', '"use strict";return (' + s + ')')(this); | ||
} | ||
const removeMark = function(v) { | ||
return v.replace(isScript, '$1'); | ||
} | ||
/** | ||
* Run a loop in the data for the element {e} | ||
@@ -299,4 +332,4 @@ * @param {object} o - tracking content object | ||
const loop = function(o) { | ||
let s = Path.call(o.s, o.v.replace('self.','')); | ||
if (s) { | ||
let data = extractFromPath.call(o.s, o.v); | ||
if (data) { | ||
// Template for the render | ||
@@ -308,6 +341,4 @@ let t; | ||
let d = []; | ||
// Get the data from the self based on the property | ||
let data = (s[0])[s[1]]; | ||
// If data exists render each element of the array | ||
if (data && data.length) { | ||
if (data.length) { | ||
for (let i = 0; i < data.length; i++) { | ||
@@ -361,13 +392,15 @@ let e = data[i].el; | ||
if (o.reference) { | ||
v = run.call(o.s, o.v); | ||
v = castProperty.call(o.s, o.v); | ||
} else { | ||
let s = o.v.split('}}')[0]; | ||
if (s.substr(0, 2) === '{{' && s.length === o.v.length - 2) { | ||
s = removeMark(o.v); | ||
v = run.call(o.s, s); | ||
} else { | ||
v = o.v.replace(isScript, function (a, b) { | ||
return run.call(o.s, b); | ||
}); | ||
} | ||
v = o.v.replace(isScript, function(a,b) { | ||
let r = extractFromPath.call(o.s, b); | ||
if (typeof(r) === 'function') { | ||
return r.call(o.s); | ||
} else { | ||
if (typeof(r) === 'undefined') { | ||
r = run.call(o.s, b); | ||
} | ||
return r; | ||
} | ||
}); | ||
} | ||
@@ -425,9 +458,11 @@ | ||
const register = function(s, p, v) { | ||
Object.defineProperty(s, p, { | ||
enumerable: false, | ||
configurable: true, | ||
get: function() { | ||
return v; | ||
} | ||
}); | ||
if (typeof(s) === 'object') { | ||
Object.defineProperty(s, p, { | ||
enumerable: false, | ||
configurable: true, | ||
get: function () { | ||
return v; | ||
} | ||
}); | ||
} | ||
} | ||
@@ -442,23 +477,24 @@ | ||
let s = this; | ||
let value = this[p]; | ||
// Do not allow undefined | ||
if (value === undefined) { | ||
value = ''; | ||
if (typeof(s) === 'object') { | ||
let value = this[p]; | ||
// Do not allow undefined | ||
if (typeof (value) === 'undefined') { | ||
value = ''; | ||
} | ||
// Create the observer | ||
Object.defineProperty(s, p, { | ||
set: function (v) { | ||
// Update val | ||
value = v; | ||
// Refresh bound elements | ||
dispatch.call(this, p); | ||
}, | ||
get: function () { | ||
// Get value | ||
return value; | ||
}, | ||
configurable: true, | ||
enumerable: true, | ||
}); | ||
} | ||
// Create the observer | ||
Object.defineProperty(s, p, { | ||
set: function(v) { | ||
// Update val | ||
value = v; | ||
// Refresh bound elements | ||
dispatch.call(this, p); | ||
}, | ||
get: function() { | ||
// Get value | ||
return value; | ||
}, | ||
configurable: true, | ||
enumerable: true, | ||
}); | ||
} | ||
@@ -472,25 +508,24 @@ | ||
// Get all self tokens in use | ||
let tokens = content.v.match(/self.([.a-zA-Z1-9_]+)*/gm); | ||
if (tokens) { | ||
for (let i = 0; i < tokens.length; i++) { | ||
// Property found | ||
let p = tokens[i].replace('self.', ''); | ||
// Get path to the object | ||
p = Path.call(this, p, true); | ||
// Register | ||
let t = R.tracking.get(p[0]); | ||
if (! t) { | ||
t = {}; | ||
R.tracking.set(p[0], t); | ||
} | ||
// Do not include self.__ref in the tracking system | ||
if (p[1] !== '__r') { | ||
// Register the properties of the self | ||
if (!t[p[1]]) { | ||
t[p[1]] = []; | ||
if (typeof(content.v) === 'string') { | ||
let tokens = content.v.match(/self([.a-zA-Z0-9_\[\]]+)*/gm); | ||
if (tokens) { | ||
for (let i = 0; i < tokens.length; i++) { | ||
// Get path to the object | ||
let p = extractFromPath.call(this, tokens[i], true); | ||
if (p) { | ||
// Register | ||
let t = R.tracking.get(p[0]); | ||
if (!t) { | ||
t = {}; | ||
R.tracking.set(p[0], t); | ||
} | ||
// Register the properties of the self | ||
if (! t[p[1]]) { | ||
t[p[1]] = []; | ||
} | ||
// Save relationship between the self and the tag attributes. TODO: avoid double call when {{self.value*self.value}} | ||
t[p[1]].push(content); | ||
// Create the necessary observers for this property | ||
observers.call(p[0], p[1]); | ||
} | ||
// Save relationship between the self and the tag attributes. TODO: avoid double call when {{self.value*self.value}} | ||
t[p[1]].push(content); | ||
// Create the necessary observers for this property | ||
observers.call(p[0], p[1]); | ||
} | ||
@@ -506,9 +541,11 @@ } | ||
* Parse the content object to see if is necessary to start tracking | ||
* @param {object} content: content tracking object | ||
* @param {object} content the tracking object | ||
*/ | ||
const parseExpression = function(content) { | ||
// Check if the content has script marks {{}} | ||
if (content.v.match(isScript)) { | ||
// Get all self tokens in use | ||
parseTokens.call(this, content); | ||
if (typeof(content.v) === 'string') { | ||
if (content.v.match(isScript)) { | ||
// Get all self tokens in use | ||
parseTokens.call(this, content); | ||
} | ||
} | ||
@@ -521,3 +558,3 @@ } | ||
* @param {string} name - attribute name | ||
* @param {string|number} value? - value to be attributed | ||
* @param {string|number?} value - value to be attributed | ||
*/ | ||
@@ -536,4 +573,5 @@ const parseAttribute = function(e, name, value) { | ||
* @param {HTMLElement} e - element | ||
* @param {array} injection | ||
*/ | ||
const parseContent = function(e) { | ||
const parseContent = function(e, injection) { | ||
// Get the content of the property | ||
@@ -551,2 +589,9 @@ let text = e.textContent; | ||
text = result[j]; | ||
// Injected values | ||
if (text && injection) { | ||
let r = text.match(/__lm=(\d+)/); | ||
if (r && r[1]) { | ||
text = injection[r[1]].v; | ||
} | ||
} | ||
// Create text node | ||
@@ -562,2 +607,4 @@ let node = document.createTextNode(text); | ||
const shouldBeReference = 'should be a reference to a function on'; | ||
/** | ||
@@ -567,4 +614,5 @@ * Parse all attributes from one element | ||
* @param {object} components | ||
* @param {array} injection | ||
*/ | ||
const parse = function(element, components) { | ||
const parse = function(element, components, injection) { | ||
// Self for this parser | ||
@@ -628,16 +676,27 @@ let self = this; | ||
for (let i = 0; i < k.length; i++) { | ||
let value = attr[k[i]]; | ||
if (injection) { | ||
let r = value.match(/{{__lm=(\d+)}}/); | ||
if (r && r[1]) { | ||
value = injection[r[1]].v; | ||
element.setAttribute(k[i], value); | ||
} | ||
} | ||
// Create input event to monitor changes in the HTML element | ||
let prop = attr[k[i]].replace('self.', ''); | ||
// Parse events | ||
if (! handler && k[i].substring(0,2) === 'on') { | ||
// Naturally on attributes already expects scripts, so no marks is necessary. But this is just for make sure there is no marks. | ||
let value = removeMark(attr[k[i]]); | ||
// References | ||
if (value.indexOf('self.__r') === 0) { | ||
value = run.call(self, value); | ||
// Remove any inline javascript from the template | ||
element.removeAttribute(k[i]); | ||
// If not a method, should be converted to a method | ||
if (typeof(value) !== 'function') { | ||
let t = extractFromPath.call(self, prop); | ||
if (t) { | ||
value = t; | ||
} | ||
} | ||
// Get action | ||
element.removeAttribute(k[i]); | ||
element.addEventListener(k[i].substring(2), function(e) { | ||
if (typeof(value) == 'function') { | ||
element.addEventListener(k[i].substring(2), (e) => { | ||
if (typeof(value) === 'function') { | ||
value.call(this, e, self); | ||
@@ -658,4 +717,11 @@ } else { | ||
if (type === 'ready') { | ||
// Call this method when the element is ready and appended to the DOM | ||
q.method = Function('self', attr[k[i]]).bind(element, self); | ||
// If not a method, should be converted to a method | ||
if (typeof(value) !== 'function') { | ||
value = extractFromPath.call(self, prop); | ||
} | ||
if (typeof(value) === 'function') { | ||
q.method = value.bind(element, element, self); | ||
} else { | ||
q.method = Function('self', attr[k[i]]).bind(element, self); | ||
} | ||
} else if (type === 'ref') { | ||
@@ -673,3 +739,3 @@ // Create a reference to the HTML element or to the self of the custom element | ||
} else { | ||
// Add event oninput for the two way binding | ||
// Add event oninput for the two-way binding | ||
let h = function() { | ||
@@ -697,3 +763,3 @@ // Get the reference to the object | ||
// Parse attributes | ||
parseTokens.call(self, { e: element, a: type, v: attr[k[i]], s: self, reference: true }) | ||
parseTokens.call(self, { e: element, a: type, v: value, s: self, reference: true }) | ||
} | ||
@@ -714,3 +780,3 @@ | ||
// Check the children | ||
if (element.children.length) { | ||
if (element.children && element.children.length) { | ||
t = []; | ||
@@ -721,3 +787,3 @@ for (let i = 0; i < element.children.length; i++) { | ||
for (let i = 0; i < t.length; i++) { | ||
parse.call(self, t[i], components); | ||
parse.call(self, t[i], components, injection); | ||
} | ||
@@ -727,3 +793,3 @@ } else { | ||
// Parse textual content | ||
parseContent.call(self, element); | ||
parseContent.call(self, element, injection); | ||
} | ||
@@ -749,19 +815,25 @@ } | ||
* Extract variables from the dynamic and append to the self | ||
* @return {string} t - converted template from ${} to {{self}} | ||
* @return {[string, array]} grab the literal injection | ||
*/ | ||
const dynamic = function() { | ||
let i = 0; | ||
// Replace the scripts for the self marks | ||
let t = this.c.toString().split('`')[1].replace(/\${.*?}/gm, function () { | ||
return '{{self.__r[' + (i++) + ']}}'; | ||
}); | ||
// Get all arguments but the first | ||
let a = Array.from(arguments); | ||
a.shift(); | ||
this.s.__r = a; | ||
let d = []; | ||
let a = arguments; | ||
let total = a[0].length - 1; | ||
const result = a[0].map((value, index) => { | ||
if (index < total) { | ||
d.push({ | ||
v: a[index+1] | ||
}); | ||
return value + '{{__lm=' + (index) + '}}'; | ||
} else { | ||
return value; | ||
} | ||
}).join(''); | ||
// Return the final template | ||
return t; | ||
return [result,d]; | ||
} | ||
// Lemonadejs object | ||
// LemonadeJS object | ||
const L = {}; | ||
@@ -790,5 +862,5 @@ | ||
// Flexible element (class or method) | ||
if (typeof (o) == 'function') { | ||
if (typeof(o) == 'function') { | ||
if (isClass(o)) { | ||
if (! self) { | ||
if (typeof(self) === 'undefined') { | ||
self = new o({}); | ||
@@ -798,3 +870,3 @@ } | ||
} else { | ||
if (! self) { | ||
if (typeof(self) === 'undefined') { | ||
self = {}; | ||
@@ -804,8 +876,8 @@ } | ||
o = o.call(self, template, components); | ||
// Process return | ||
if (typeof (o) === 'function') { | ||
o = L.element(o(dynamic.bind({c: o, s: self})), self, components); | ||
// Remove dynamic references | ||
delete self.__r; | ||
} else if (typeof (o) === 'string') { | ||
if (typeof(o) === 'function') { | ||
let d = o(dynamic.bind({ c: o, s: self })); | ||
o = L.element(d[0], self, components, d[1]); | ||
} else if (typeof(o) === 'string') { | ||
o = L.element(o, self, components); | ||
@@ -815,3 +887,3 @@ } | ||
if (!isDOM(o)) { | ||
if (! isDOM(o)) { | ||
console.error('Invalid DOM return'); | ||
@@ -868,5 +940,6 @@ return false; | ||
} else { | ||
let s = Path.call(this, p); | ||
// Refresh a loop | ||
dispatch.call(s[0], s[1]); | ||
let s = extractFromPath.call(this, p, true); | ||
if (s) { | ||
dispatch.call(s[0], s[1]); | ||
} | ||
} | ||
@@ -890,5 +963,6 @@ }); | ||
* @param {object?} components - all custom components references | ||
* @param {object?} injection - arguments from a string literal injection | ||
* @return {HTMLElement|null} el - result of the DOM parse | ||
*/ | ||
L.element = function(t, self, components) { | ||
L.element = function(t, self, components, injection) { | ||
// Element | ||
@@ -898,3 +972,3 @@ let el; | ||
// Lemonade handler | ||
if (! self) { | ||
if (typeof(self) === 'undefined') { | ||
self = {}; | ||
@@ -945,3 +1019,3 @@ } | ||
// Parse the content | ||
parse.call(self, el, components); | ||
parse.call(self, el, components, injection); | ||
@@ -994,5 +1068,5 @@ // Create the el bound to the self | ||
* Set the values from {o} to {this} | ||
* @param {object} o: set the values of {this} when the this[property] is found in {o}, or when flag force is true | ||
* @param {boolean} f: create a new property when that does not exists yet, but is found in {o} | ||
* @return {object} - this is redundant since object {this} is a reference and is already available in the caller | ||
* @param {object} o set the values of {this} when the this[property] is found in {o}, or when flag force is true | ||
* @param {boolean} f create a new property when that does not exists yet, but is found in {o} | ||
* @return {object} this is redundant since object {this} is a reference and is already available in the caller | ||
*/ | ||
@@ -1020,3 +1094,3 @@ L.setProperties = function(o, f) { | ||
* Lemonade CC (common container) helps you share a self or function through the whole application | ||
* @param {string} name: alias for your declared object(self) or function | ||
* @param {string} name alias for your declared object(self) or function | ||
* @returns {Object | Function} - registered element | ||
@@ -1023,0 +1097,0 @@ */ |
/** | ||
* LemonadeJS v3.5.0 | ||
* LemonadeJS v4.0.0 | ||
* | ||
@@ -7,9 +7,3 @@ * Website: https://lemonadejs.net | ||
* | ||
* This software is distribute under MIT License | ||
* | ||
* Roadmap | ||
* - @bind jSuites.dropdown initial value is not set when properties has a value | ||
* - setComponents for local variables | ||
* - {{self.test*self.test}} - avoid duplication in the monitoring | ||
* - Classes are not detect with https://codesandbox.io/s/lemonadejs-examples-sebfeo | ||
* This software is distributed under MIT License | ||
*/ | ||
@@ -48,24 +42,32 @@ | ||
/** | ||
* Path string to object | ||
* @param {string} str - path to the value as a string | ||
* @param {boolean} onlyObject - get the last valid object in the nested object | ||
* @return {array|boolean} - return the object and the property | ||
* Extract a property from a nested object using a string address | ||
* @param {string} str address inside the nested object | ||
* @param {boolean} config get the configuration obj => property | ||
*/ | ||
const Path = function(str, onlyObject) { | ||
let t = str.split('.'); | ||
if (t.length) { | ||
const extractFromPath = function(str, config) { | ||
try { | ||
let t = str.toString().replace(/[\[\]]/g, '.').split('.'); | ||
if (t[0] === 'self') { | ||
t.shift(); | ||
} | ||
// Remove blanks | ||
t = t.filter(item => item !== ''); | ||
// Object | ||
let o = this; | ||
// Property | ||
let p = null; | ||
while (t.length > 1) { | ||
let lastObject; | ||
while (t.length) { | ||
// Get the property | ||
p = t.shift(); | ||
let p = t.shift(); | ||
// Process config | ||
if (config) { | ||
if (typeof(o) === 'object' && ! Array.isArray(o)) { | ||
lastObject = [o,p]; | ||
} | ||
if (t.length === 0) { | ||
return lastObject; | ||
} | ||
} | ||
// Check if the property exists | ||
if (o.hasOwnProperty(p)) { | ||
if (! onlyObject || typeof(o[p]) === 'object' && ! Array.isArray(o[p])) { | ||
o = o[p]; | ||
} else { | ||
return [o,p]; | ||
} | ||
o = o[p]; | ||
} else { | ||
@@ -75,14 +77,52 @@ return undefined; | ||
} | ||
// Get the property | ||
p = t.shift(); | ||
// Return the value | ||
if (o) { | ||
return [o,p]; | ||
if (typeof(o) !== 'undefined') { | ||
return o; | ||
} | ||
} | ||
} catch (e) {} | ||
// Something went wrong | ||
return false; | ||
return undefined; | ||
} | ||
/** | ||
* Cast the value of an attribute | ||
*/ | ||
const castProperty = function(attr) { | ||
// Parse type | ||
try { | ||
if (typeof(attr) === 'string' && attr) { | ||
// Remove any white spaces | ||
attr = attr.trim(); | ||
if (attr === 'true') { | ||
return true; | ||
} else if (attr === 'false') { | ||
return false; | ||
} else if (! isNaN(attr)) { | ||
return Number(attr); | ||
} else if (attr.substring(0, 1) === '{') { | ||
if (attr.slice(-1) === '}') { | ||
return JSON.parse(attr); | ||
} | ||
} else if (attr.substring(0, 5) === 'self.') { | ||
let v = extractFromPath.call(this, attr); | ||
if (typeof(v) !== 'undefined') { | ||
return v; | ||
} | ||
} | ||
} | ||
} catch (e) {} | ||
return attr; | ||
} | ||
/** | ||
* This allows to run inline script on legacy system. Inline script can lead to security issues so use carefully. | ||
* @param {string} s string to function | ||
*/ | ||
const run = function(s) { | ||
return Function('self', '"use strict";return (' + s + ')')(this); | ||
} | ||
/** | ||
* Create a new HTML element | ||
@@ -291,15 +331,2 @@ * @param {string} type - create a new HTML element as a type | ||
/** | ||
* Parse javascript | ||
* @param {string} s - string | ||
* @return {any} | ||
*/ | ||
const run = function(s) { | ||
return Function('self', '"use strict";return (' + s + ')')(this); | ||
} | ||
const removeMark = function(v) { | ||
return v.replace(isScript, '$1'); | ||
} | ||
/** | ||
* Run a loop in the data for the element {e} | ||
@@ -309,4 +336,4 @@ * @param {object} o - tracking content object | ||
const loop = function(o) { | ||
let s = Path.call(o.s, o.v.replace('self.','')); | ||
if (s) { | ||
let data = extractFromPath.call(o.s, o.v); | ||
if (data) { | ||
// Template for the render | ||
@@ -318,6 +345,4 @@ let t; | ||
let d = []; | ||
// Get the data from the self based on the property | ||
let data = (s[0])[s[1]]; | ||
// If data exists render each element of the array | ||
if (data && data.length) { | ||
if (data.length) { | ||
for (let i = 0; i < data.length; i++) { | ||
@@ -371,13 +396,15 @@ let e = data[i].el; | ||
if (o.reference) { | ||
v = run.call(o.s, o.v); | ||
v = castProperty.call(o.s, o.v); | ||
} else { | ||
let s = o.v.split('}}')[0]; | ||
if (s.substr(0, 2) === '{{' && s.length === o.v.length - 2) { | ||
s = removeMark(o.v); | ||
v = run.call(o.s, s); | ||
} else { | ||
v = o.v.replace(isScript, function (a, b) { | ||
return run.call(o.s, b); | ||
}); | ||
} | ||
v = o.v.replace(isScript, function(a,b) { | ||
let r = extractFromPath.call(o.s, b); | ||
if (typeof(r) === 'function') { | ||
return r.call(o.s); | ||
} else { | ||
if (typeof(r) === 'undefined') { | ||
r = run.call(o.s, b); | ||
} | ||
return r; | ||
} | ||
}); | ||
} | ||
@@ -435,9 +462,11 @@ | ||
const register = function(s, p, v) { | ||
Object.defineProperty(s, p, { | ||
enumerable: false, | ||
configurable: true, | ||
get: function() { | ||
return v; | ||
} | ||
}); | ||
if (typeof(s) === 'object') { | ||
Object.defineProperty(s, p, { | ||
enumerable: false, | ||
configurable: true, | ||
get: function () { | ||
return v; | ||
} | ||
}); | ||
} | ||
} | ||
@@ -452,23 +481,24 @@ | ||
let s = this; | ||
let value = this[p]; | ||
// Do not allow undefined | ||
if (value === undefined) { | ||
value = ''; | ||
if (typeof(s) === 'object') { | ||
let value = this[p]; | ||
// Do not allow undefined | ||
if (typeof (value) === 'undefined') { | ||
value = ''; | ||
} | ||
// Create the observer | ||
Object.defineProperty(s, p, { | ||
set: function (v) { | ||
// Update val | ||
value = v; | ||
// Refresh bound elements | ||
dispatch.call(this, p); | ||
}, | ||
get: function () { | ||
// Get value | ||
return value; | ||
}, | ||
configurable: true, | ||
enumerable: true, | ||
}); | ||
} | ||
// Create the observer | ||
Object.defineProperty(s, p, { | ||
set: function(v) { | ||
// Update val | ||
value = v; | ||
// Refresh bound elements | ||
dispatch.call(this, p); | ||
}, | ||
get: function() { | ||
// Get value | ||
return value; | ||
}, | ||
configurable: true, | ||
enumerable: true, | ||
}); | ||
} | ||
@@ -482,25 +512,24 @@ | ||
// Get all self tokens in use | ||
let tokens = content.v.match(/self.([.a-zA-Z1-9_]+)*/gm); | ||
if (tokens) { | ||
for (let i = 0; i < tokens.length; i++) { | ||
// Property found | ||
let p = tokens[i].replace('self.', ''); | ||
// Get path to the object | ||
p = Path.call(this, p, true); | ||
// Register | ||
let t = R.tracking.get(p[0]); | ||
if (! t) { | ||
t = {}; | ||
R.tracking.set(p[0], t); | ||
} | ||
// Do not include self.__ref in the tracking system | ||
if (p[1] !== '__r') { | ||
// Register the properties of the self | ||
if (!t[p[1]]) { | ||
t[p[1]] = []; | ||
if (typeof(content.v) === 'string') { | ||
let tokens = content.v.match(/self([.a-zA-Z0-9_\[\]]+)*/gm); | ||
if (tokens) { | ||
for (let i = 0; i < tokens.length; i++) { | ||
// Get path to the object | ||
let p = extractFromPath.call(this, tokens[i], true); | ||
if (p) { | ||
// Register | ||
let t = R.tracking.get(p[0]); | ||
if (!t) { | ||
t = {}; | ||
R.tracking.set(p[0], t); | ||
} | ||
// Register the properties of the self | ||
if (! t[p[1]]) { | ||
t[p[1]] = []; | ||
} | ||
// Save relationship between the self and the tag attributes. TODO: avoid double call when {{self.value*self.value}} | ||
t[p[1]].push(content); | ||
// Create the necessary observers for this property | ||
observers.call(p[0], p[1]); | ||
} | ||
// Save relationship between the self and the tag attributes. TODO: avoid double call when {{self.value*self.value}} | ||
t[p[1]].push(content); | ||
// Create the necessary observers for this property | ||
observers.call(p[0], p[1]); | ||
} | ||
@@ -516,9 +545,11 @@ } | ||
* Parse the content object to see if is necessary to start tracking | ||
* @param {object} content: content tracking object | ||
* @param {object} content the tracking object | ||
*/ | ||
const parseExpression = function(content) { | ||
// Check if the content has script marks {{}} | ||
if (content.v.match(isScript)) { | ||
// Get all self tokens in use | ||
parseTokens.call(this, content); | ||
if (typeof(content.v) === 'string') { | ||
if (content.v.match(isScript)) { | ||
// Get all self tokens in use | ||
parseTokens.call(this, content); | ||
} | ||
} | ||
@@ -531,3 +562,3 @@ } | ||
* @param {string} name - attribute name | ||
* @param {string|number} value? - value to be attributed | ||
* @param {string|number?} value - value to be attributed | ||
*/ | ||
@@ -546,4 +577,5 @@ const parseAttribute = function(e, name, value) { | ||
* @param {HTMLElement} e - element | ||
* @param {array} injection | ||
*/ | ||
const parseContent = function(e) { | ||
const parseContent = function(e, injection) { | ||
// Get the content of the property | ||
@@ -561,2 +593,9 @@ let text = e.textContent; | ||
text = result[j]; | ||
// Injected values | ||
if (text && injection) { | ||
let r = text.match(/__lm=(\d+)/); | ||
if (r && r[1]) { | ||
text = injection[r[1]].v; | ||
} | ||
} | ||
// Create text node | ||
@@ -572,2 +611,4 @@ let node = document.createTextNode(text); | ||
const shouldBeReference = 'should be a reference to a function on'; | ||
/** | ||
@@ -577,4 +618,5 @@ * Parse all attributes from one element | ||
* @param {object} components | ||
* @param {array} injection | ||
*/ | ||
const parse = function(element, components) { | ||
const parse = function(element, components, injection) { | ||
// Self for this parser | ||
@@ -638,16 +680,27 @@ let self = this; | ||
for (let i = 0; i < k.length; i++) { | ||
let value = attr[k[i]]; | ||
if (injection) { | ||
let r = value.match(/{{__lm=(\d+)}}/); | ||
if (r && r[1]) { | ||
value = injection[r[1]].v; | ||
element.setAttribute(k[i], value); | ||
} | ||
} | ||
// Create input event to monitor changes in the HTML element | ||
let prop = attr[k[i]].replace('self.', ''); | ||
// Parse events | ||
if (! handler && k[i].substring(0,2) === 'on') { | ||
// Naturally on attributes already expects scripts, so no marks is necessary. But this is just for make sure there is no marks. | ||
let value = removeMark(attr[k[i]]); | ||
// References | ||
if (value.indexOf('self.__r') === 0) { | ||
value = run.call(self, value); | ||
// Remove any inline javascript from the template | ||
element.removeAttribute(k[i]); | ||
// If not a method, should be converted to a method | ||
if (typeof(value) !== 'function') { | ||
let t = extractFromPath.call(self, prop); | ||
if (t) { | ||
value = t; | ||
} | ||
} | ||
// Get action | ||
element.removeAttribute(k[i]); | ||
element.addEventListener(k[i].substring(2), function(e) { | ||
if (typeof(value) == 'function') { | ||
element.addEventListener(k[i].substring(2), (e) => { | ||
if (typeof(value) === 'function') { | ||
value.call(this, e, self); | ||
@@ -668,4 +721,11 @@ } else { | ||
if (type === 'ready') { | ||
// Call this method when the element is ready and appended to the DOM | ||
q.method = Function('self', attr[k[i]]).bind(element, self); | ||
// If not a method, should be converted to a method | ||
if (typeof(value) !== 'function') { | ||
value = extractFromPath.call(self, prop); | ||
} | ||
if (typeof(value) === 'function') { | ||
q.method = value.bind(element, element, self); | ||
} else { | ||
q.method = Function('self', attr[k[i]]).bind(element, self); | ||
} | ||
} else if (type === 'ref') { | ||
@@ -683,3 +743,3 @@ // Create a reference to the HTML element or to the self of the custom element | ||
} else { | ||
// Add event oninput for the two way binding | ||
// Add event oninput for the two-way binding | ||
let h = function() { | ||
@@ -707,3 +767,3 @@ // Get the reference to the object | ||
// Parse attributes | ||
parseTokens.call(self, { e: element, a: type, v: attr[k[i]], s: self, reference: true }) | ||
parseTokens.call(self, { e: element, a: type, v: value, s: self, reference: true }) | ||
} | ||
@@ -724,3 +784,3 @@ | ||
// Check the children | ||
if (element.children.length) { | ||
if (element.children && element.children.length) { | ||
t = []; | ||
@@ -731,3 +791,3 @@ for (let i = 0; i < element.children.length; i++) { | ||
for (let i = 0; i < t.length; i++) { | ||
parse.call(self, t[i], components); | ||
parse.call(self, t[i], components, injection); | ||
} | ||
@@ -737,3 +797,3 @@ } else { | ||
// Parse textual content | ||
parseContent.call(self, element); | ||
parseContent.call(self, element, injection); | ||
} | ||
@@ -759,19 +819,25 @@ } | ||
* Extract variables from the dynamic and append to the self | ||
* @return {string} t - converted template from ${} to {{self}} | ||
* @return {[string, array]} grab the literal injection | ||
*/ | ||
const dynamic = function() { | ||
let i = 0; | ||
// Replace the scripts for the self marks | ||
let t = this.c.toString().split('`')[1].replace(/\${.*?}/gm, function () { | ||
return '{{self.__r[' + (i++) + ']}}'; | ||
}); | ||
// Get all arguments but the first | ||
let a = Array.from(arguments); | ||
a.shift(); | ||
this.s.__r = a; | ||
let d = []; | ||
let a = arguments; | ||
let total = a[0].length - 1; | ||
const result = a[0].map((value, index) => { | ||
if (index < total) { | ||
d.push({ | ||
v: a[index+1] | ||
}); | ||
return value + '{{__lm=' + (index) + '}}'; | ||
} else { | ||
return value; | ||
} | ||
}).join(''); | ||
// Return the final template | ||
return t; | ||
return [result,d]; | ||
} | ||
// Lemonadejs object | ||
// LemonadeJS object | ||
const L = {}; | ||
@@ -800,5 +866,5 @@ | ||
// Flexible element (class or method) | ||
if (typeof (o) == 'function') { | ||
if (typeof(o) == 'function') { | ||
if (isClass(o)) { | ||
if (! self) { | ||
if (typeof(self) === 'undefined') { | ||
self = new o({}); | ||
@@ -808,3 +874,3 @@ } | ||
} else { | ||
if (! self) { | ||
if (typeof(self) === 'undefined') { | ||
self = {}; | ||
@@ -814,8 +880,8 @@ } | ||
o = o.call(self, template, components); | ||
// Process return | ||
if (typeof (o) === 'function') { | ||
o = L.element(o(dynamic.bind({c: o, s: self})), self, components); | ||
// Remove dynamic references | ||
delete self.__r; | ||
} else if (typeof (o) === 'string') { | ||
if (typeof(o) === 'function') { | ||
let d = o(dynamic.bind({ c: o, s: self })); | ||
o = L.element(d[0], self, components, d[1]); | ||
} else if (typeof(o) === 'string') { | ||
o = L.element(o, self, components); | ||
@@ -825,3 +891,3 @@ } | ||
if (!isDOM(o)) { | ||
if (! isDOM(o)) { | ||
console.error('Invalid DOM return'); | ||
@@ -878,5 +944,6 @@ return false; | ||
} else { | ||
let s = Path.call(this, p); | ||
// Refresh a loop | ||
dispatch.call(s[0], s[1]); | ||
let s = extractFromPath.call(this, p, true); | ||
if (s) { | ||
dispatch.call(s[0], s[1]); | ||
} | ||
} | ||
@@ -900,5 +967,6 @@ }); | ||
* @param {object?} components - all custom components references | ||
* @param {object?} injection - arguments from a string literal injection | ||
* @return {HTMLElement|null} el - result of the DOM parse | ||
*/ | ||
L.element = function(t, self, components) { | ||
L.element = function(t, self, components, injection) { | ||
// Element | ||
@@ -908,3 +976,3 @@ let el; | ||
// Lemonade handler | ||
if (! self) { | ||
if (typeof(self) === 'undefined') { | ||
self = {}; | ||
@@ -955,3 +1023,3 @@ } | ||
// Parse the content | ||
parse.call(self, el, components); | ||
parse.call(self, el, components, injection); | ||
@@ -1004,5 +1072,5 @@ // Create the el bound to the self | ||
* Set the values from {o} to {this} | ||
* @param {object} o: set the values of {this} when the this[property] is found in {o}, or when flag force is true | ||
* @param {boolean} f: create a new property when that does not exists yet, but is found in {o} | ||
* @return {object} - this is redundant since object {this} is a reference and is already available in the caller | ||
* @param {object} o set the values of {this} when the this[property] is found in {o}, or when flag force is true | ||
* @param {boolean} f create a new property when that does not exists yet, but is found in {o} | ||
* @return {object} this is redundant since object {this} is a reference and is already available in the caller | ||
*/ | ||
@@ -1030,3 +1098,3 @@ L.setProperties = function(o, f) { | ||
* Lemonade CC (common container) helps you share a self or function through the whole application | ||
* @param {string} name: alias for your declared object(self) or function | ||
* @param {string} name alias for your declared object(self) or function | ||
* @returns {Object | Function} - registered element | ||
@@ -1033,0 +1101,0 @@ */ |
@@ -42,3 +42,3 @@ { | ||
"types": "dist/lemonade.d.ts", | ||
"version": "3.5.0" | ||
"version": "4.0.0-beta.0" | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
91903
2175
2