Page MenuHomePhabricator

D564.id1780.diff
No OneTemporary

D564.id1780.diff

diff --git a/demo/lysine.demo.html b/demo/lysine.demo.html
--- a/demo/lysine.demo.html
+++ b/demo/lysine.demo.html
@@ -16,6 +16,8 @@
<option value="notell">I'd rather you didn't</option>
</select>
</div>
+
+ <div data-condition="value(gender) == male">Oppressive pig!</div>
</div>
</template>
diff --git a/src/Condition.js b/src/Condition.js
new file mode 100644
--- /dev/null
+++ b/src/Condition.js
@@ -0,0 +1,94 @@
+
+import collect from "collect.js";
+
+/**
+ * Lysine allows users to write simple expressions that then can be used
+ * to determine whether a element should be rendered or not.
+ *
+ * Making use of these allows your application to easily modify the the
+ * visibility of components from the data in the view, and save resources
+ * by removing components that are currently unneeded.
+ *
+ * @param {string} expression An expression to evaluate
+ * @param {HTMLElement} element An HTML element to show / hide if the condition applies
+ * @param {Adapter[]} adapters The adapters that underlie this condition, they won't be used if unavailable
+ */
+function Condition(expression, element, adapters)
+{
+ var exp = /([a-zA-Z_0-9]+)\(([a-zA-Z_0-9\-]+)\)\s?(\=\=|\!\=)\s?(.+)/g;
+ var res = exp.exec(expression);
+
+ if (res === null) {
+ throw 'Malformed expression: ' + expression;
+ }
+
+ var fn = res[1];
+ var id = res[2];
+ var comp = res[3];
+ var tgt = res[4];
+
+ var view = undefined;
+
+ var parent = element.parentNode;
+ var nextSib = element.nextSibling;
+
+ this.isVisible = function () {
+ var val = undefined;
+
+ switch(fn) {
+ case 'null':
+ val = view.get(id) === null? 'true' : 'false';
+ break;
+ case 'bool':
+ val = view.get(id) === true? 'true' : 'false';
+ break;
+ case 'count':
+ val = !view.get(id)? 0 : view.get(id).length;
+ break;
+ case 'value':
+ val = view.get(id);
+ break;
+ }
+
+ return comp === '=='? val == tgt : val != tgt;
+ };
+
+ this.test = function () {
+ var visible = this.isVisible();
+
+ if (visible === (element.parentNode === parent)) {
+ return;
+ }
+
+ if (visible) {
+ parent.insertBefore(element, nextSib);
+ }
+ else {
+ parent.removeChild(element);
+ }
+ };
+
+ this.for = function() {
+ var c = collect([]);
+ adapters.each(function (e) { c.merge(e.for()); });
+ c.push(id);
+
+ return c.toArray();
+ };
+
+ this.parent = function(v) {
+ view = v;
+ adapters.each(function(e) { e.parent(v); });
+ return this;
+ };
+
+ this.refresh = function () {
+ this.test();
+
+ if (this.isVisible()) {
+ adapters.each(function(e) { e.refresh(); });
+ }
+ };
+}
+
+export default Condition;
diff --git a/src/adapter/ArrayAdapter.js b/src/adapter/ArrayAdapter.js
new file mode 100644
--- /dev/null
+++ b/src/adapter/ArrayAdapter.js
@@ -0,0 +1,138 @@
+function ArrayAdapter(view)
+{
+ this.views = [];
+ this.base = view;
+ this.parentView = undefined;
+ this.listeners = collect([]);
+ this.writeProtect = false;
+
+ this._setup = collect([]);
+ this._tearDown = collect([]);
+
+ this.getValue = function () {
+ var ret = [],
+ i;
+
+ /*
+ * Ensure there's no destroyed views being used to read the data
+ */
+ this.views = this.views.filter(function (e) { return !e.isDestroyed();});
+
+ for (i = 0; i < this.views.length; i+=1) {
+ ret.push(this.views[i].getValue());
+ }
+ return ret;
+ };
+
+ this.setValue = function (val) {
+ /**
+ * While the code is propagating, as in: writing to the parent, it should
+ * expect the parent to try an return the data back to the element.
+ *
+ * Since we're currently writing data, we can safely reject it, since it
+ * will provide the same data we are sending.
+ */
+ if (this.writeProtect) {
+ return;
+ }
+
+ var i, v;
+
+ if (val === undefined) {
+ return;
+ }
+
+ val = val.filter(function (e) { return !!e;});
+ this.views = this.views.filter(function (e) { return e.reset() && !e.isDestroyed();});
+
+ /*
+ * In this scenario, we have more views than necessary and need to get
+ * rid of some. We first loop over the array to remove them from the
+ * HTML (destroy them). Then we slice the array with them in it.
+ */
+ while (val.length < this.views.length) {
+ this.views[val.length].destroy();
+ }
+
+ this.views = this.views.slice(0, val.length);
+
+ /*
+ * Loop over the left views and call teardown on them
+ */
+ var t = this._tearDown;
+ collect(this.views).each(function (view) {
+ t.each(function (fn) { fn(view); });
+ });
+
+ /*
+ * In the event of the views not being enough to hold the data, we will
+ * add new views.
+ */
+ for (i = this.views.length; i < val.length; i+=1) {
+ v = new View(this.base);
+ this.views.push(v);
+
+ v.setParent(this);
+ this.listeners.each(function (e) { v.on.apply(v, e); })
+ }
+
+ var t = this._setup;
+ collect(this.views).each(function (view) {
+ t.each(function (fn) { fn(view); });
+ });
+
+ for (i = 0; i < val.length; i++) {
+ this.views[i].setValue(val[i]);
+ }
+
+ };
+
+ this.for = function() {
+ return [this.base.getAttribute('data-for')];
+ };
+
+ this.on = function (selector, event, callback) {
+ this.listeners.push([selector, event, callback]);
+
+ this.views.forEach(function (e) { e.on(selector, event, callback); })
+ };
+
+ this.setUp = function (fn) {
+ this._setup.push(fn);
+ };
+
+ this.tearDown = function (fn) {
+ this._tearDown.push(fn);
+ };
+
+ this.parent = function(v) {
+ this.parentView = v;
+ return this;
+ };
+
+ this.push = function(d) {
+
+ var v = new View(this.base);
+ this.views.push(v);
+
+ v.setValue(d);
+ v.setParent(this);
+
+ this.listeners.each(function (e) { v.on.apply(v, e); })
+ this.propagate();
+
+ return v;
+ };
+
+ this.refresh = function () {
+ this.setValue(this.parentView.get(this.for()[0]));
+ };
+
+ this.propagate = function () {
+ this.writeProtect = true;
+ this.parentView.set(this.for()[0], this.getValue());
+ this.writeProtect = false;
+ };
+}
+
+export default ArrayAdapter;
\ No newline at end of file
diff --git a/src/adapter/inputAdapter.js b/src/adapter/inputAdapter.js
--- a/src/adapter/inputAdapter.js
+++ b/src/adapter/inputAdapter.js
@@ -40,7 +40,7 @@
/**
* If the user alters the data, we immediately inform the view.
*/
- this.element.addEventListener('onkeyup', function() {
+ this.element.addEventListener('input', function() {
self.view.set(self.for()[0], this.value);
});
diff --git a/src/adapter/selectAdapter.js b/src/adapter/selectAdapter.js
--- a/src/adapter/selectAdapter.js
+++ b/src/adapter/selectAdapter.js
@@ -32,8 +32,17 @@
* @param {type} element
* @returns {lysine_L11.InputAdapter}
*/
- function SelectAdapter(element) {
+ function SelectAdapter(element)
+ {
this.view = undefined;
+ this.element = element;
+
+ var self = this;
+
+ this.element.addEventListener('change', function() {
+ var options = Array.prototype.slice.call(element.options, 0);
+ self.view.set(self.for()[0], options[this.selectedIndex].value);
+ });
/**
* Gets the value of the input being managed. It will therefore just read
diff --git a/src/collection.js b/src/collection.js
deleted file mode 100644
--- a/src/collection.js
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * The MIT License
- *
- * Copyright 2018 César de la Cal Bretschneider <cesar@magic3w.com>.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-
-
-"use strict";
-
-var Collection = function(elements) {
- if (elements instanceof NodeList) {
- elements = Array.prototype.slice.call(elements, 0);
- }
-
- this.elements = elements;
-};
-
-Collection.prototype = {
- each: function(fn) {
- var ret = (this.elements instanceof Array)? [] : {};
-
- if (!this.elements instanceof Array) {
- for (var i in this.elements) {
- if (!this.elements.hasOwnProperty(i)) { continue; }
- ret[i] = fn(this.elements[i], i);
- }
- }
- else {
- for (var i = 0; i < this.elements.length; i++) {
- ret[i] = fn(this.elements[i], i);
- }
- }
-
- return new Collection(ret);
- },
-
- filter: function(fn) {
- var result = new Collection([]);
-
- this.each(function(e) {
- if (fn(e)) {
- result.push(e);
- }
- });
-
- return result;
- },
-
- merge: function (elements) {
- var self = this;
-
- if (!(elements instanceof Collection)) {
- elements = new Collection(elements);
- }
-
- elements.each(function (value, idx) {
- if (!(self.elements instanceof Array)) {
- self.elements[idx] = value;
- }
- else {
- self.elements.push(value);
- }
- });
-
- return this;
- },
-
- reduce: function(cb) {
- return this.elements.reduce(cb);
- },
-
- push: function (v) {
- this.elements.push(v);
- },
-
- pop: function () {
- return this.elements.pop();
- },
-
- set: function (idx, v) {
- this.elements[idx] = v;
- },
-
- get: function (idx) {
- return this.elements[idx];
- },
-
- raw : function () {
- return this.elements;
- },
-
- length : function () {
- if (this.elements instanceof Array) {
- return this.elements.length;
- }
- else {
- var c = 0;
- this.each(function () { c++; });
- return c;
- }
- }
-};
-
-export default function (e) { return new Collection(e); }
\ No newline at end of file
diff --git a/src/lysine.js b/src/lysine.js
--- a/src/lysine.js
+++ b/src/lysine.js
@@ -8,6 +8,8 @@
import select from "./adapter/selectAdapter";
import htmlAdapter from "./adapter/htmlAdapter";
import attributeAdapter from "./adapter/attributeAdapter";
+import ArrayAdapter from "./adapter/ArrayAdapter";
+import Condition from "./Condition";
import * as parsel from "parsel-js";
@@ -19,218 +21,6 @@
if (window === undefined) { throw 'Lysine requires a browser to work. Window variable was not found'; }
- function ArrayAdapter(view) {
- this.views = [];
- this.base = view;
- this.parentView = undefined;
- this.listeners = collect([]);
- this.writeProtect = false;
-
- this._setup = collect([]);
- this._tearDown = collect([]);
-
- this.getValue = function () {
- var ret = [],
- i;
-
- /*
- * Ensure there's no destroyed views being used to read the data
- */
- this.views = this.views.filter(function (e) { return !e.isDestroyed();});
-
- for (i = 0; i < this.views.length; i+=1) {
- ret.push(this.views[i].getValue());
- }
- return ret;
- };
-
- this.setValue = function (val) {
- /**
- * While the code is propagating, as in: writing to the parent, it should
- * expect the parent to try an return the data back to the element.
- *
- * Since we're currently writing data, we can safely reject it, since it
- * will provide the same data we are sending.
- */
- if (this.writeProtect) {
- return;
- }
-
- var i, v;
-
- if (val === undefined) {
- return;
- }
-
- val = val.filter(function (e) { return !!e;});
- this.views = this.views.filter(function (e) { return e.reset() && !e.isDestroyed();});
-
- /*
- * In this scenario, we have more views than necessary and need to get
- * rid of some. We first loop over the array to remove them from the
- * HTML (destroy them). Then we slice the array with them in it.
- */
- while (val.length < this.views.length) {
- this.views[val.length].destroy();
- }
-
- this.views = this.views.slice(0, val.length);
-
- /*
- * Loop over the left views and call teardown on them
- */
- var t = this._tearDown;
- collect(this.views).each(function (view) {
- t.each(function (fn) { fn(view); });
- });
-
- /*
- * In the event of the views not being enough to hold the data, we will
- * add new views.
- */
- for (i = this.views.length; i < val.length; i+=1) {
- v = new View(this.base);
- this.views.push(v);
-
- v.setParent(this);
- this.listeners.each(function (e) { v.on.apply(v, e); })
- }
-
- var t = this._setup;
- collect(this.views).each(function (view) {
- t.each(function (fn) { fn(view); });
- });
-
- for (i = 0; i < val.length; i++) {
- this.views[i].setValue(val[i]);
- }
-
- };
-
- this.for = function() {
- return [this.base.getAttribute('data-for')];
- };
-
- this.on = function (selector, event, callback) {
- this.listeners.push([selector, event, callback]);
-
- this.views.forEach(function (e) { e.on(selector, event, callback); })
- };
-
- this.setUp = function (fn) {
- this._setup.push(fn);
- };
-
- this.tearDown = function (fn) {
- this._tearDown.push(fn);
- };
-
- this.parent = function(v) {
- this.parentView = v;
- return this;
- };
-
- this.push = function(d) {
-
- var v = new View(this.base);
- this.views.push(v);
-
- v.setValue(d);
- v.setParent(this);
-
- this.listeners.each(function (e) { v.on.apply(v, e); })
- this.propagate();
-
- return v;
- };
-
- this.refresh = function () {
- this.setValue(this.parentView.get(this.for()[0]));
- };
-
- this.propagate = function () {
- this.writeProtect = true;
- this.parentView.set(this.for()[0], this.getValue());
- this.writeProtect = false;
- };
- }
-
- function Condition(expression, element, adapters) {
- var exp = /([a-zA-Z_0-9]+)\(([a-zA-Z_0-9\-]+)\)\s?(\=\=|\!\=)\s?(.+)/g;
- var res = exp.exec(expression);
-
- if (res === null) {
- throw 'Malformed expression: ' + expression;
- }
-
- var fn = res[1];
- var id = res[2];
- var comp = res[3];
- var tgt = res[4];
-
- var view = undefined;
-
- var parent = element.parentNode;
- var nextSib = element.nextSibling;
-
- this.isVisible = function () {
- var val = undefined;
-
- switch(fn) {
- case 'null':
- val = view.get(id) === null? 'true' : 'false';
- break;
- case 'bool':
- val = view.get(id) === true? 'true' : 'false';
- break;
- case 'count':
- val = !view.get(id)? 0 : view.get(id).length;
- break;
- case 'value':
- val = view.get(id);
- break;
- }
-
- return comp === '=='? val == tgt : val != tgt;
- };
-
- this.test = function () {
- var visible = this.isVisible();
-
- if (visible === (element.parentNode === parent)) {
- return;
- }
-
- if (visible) {
- parent.insertBefore(element, nextSib);
- }
- else {
- parent.removeChild(element);
- }
- };
-
- this.for = function() {
- var c = collect([]);
- adapters.each(function (e) { c.merge(e.for()); });
- c.push(id);
-
- return c.raw();
- };
-
- this.parent = function(v) {
- view = v;
- adapters.each(function(e) { e.parent(v); });
- return this;
- };
-
- this.refresh = function () {
- this.test();
-
- if (this.isVisible()) {
- adapters.each(function(e) { e.refresh(); });
- }
- };
- }
/**
* Creates a new Lysine view that handles the user's HTML and accepts objects as
@@ -358,7 +148,7 @@
var adapters = collect([]), self = this;
- collect(parent.childNodes).each(function (e) {
+ collect(parent.childNodes).map(function (e) {
var extracted = collect([]);
if (e.nodeType === 3) {
@@ -372,7 +162,7 @@
* makes little to no sense to have that feature.
*/
if (e.hasAttribute('data-lysine-view')) {
- extracted.merge(collect([(new ArrayAdapter(e)).parent(self)]));
+ extracted = extracted.push((new ArrayAdapter(e)).parent(self));
}
else {
/*
@@ -381,29 +171,33 @@
* the system cannot handle having multiple adapters for one
* property.
*/
- var adapter = collect([]).merge(input.findAdapters(e)).merge(select.findAdapters(e)).merge(htmlAdapter.findAdapters(e));
- extracted.merge(adapter.each(function (e) { return e.parent(self); }));
+ var adapter = collect([]);
+ collect(input.findAdapters(e)).each(e => adapter.push(e));
+ collect(select.findAdapters(e)).each(e => adapter.push(e));
+ collect(htmlAdapter.findAdapters(e)).each(e => adapter.push(e));
+
+ extracted = extracted.merge(adapter.map(function (e) { return e.parent(self); }).toArray());
}
}
else {
- extracted.merge(self.fetchAdapters(e));
+ extracted = extracted.merge(self.fetchAdapters(e).toArray());
}
/*
* Get the adapters for the attributes, then informt them that the parent
* for them is this view and attach them to the attributes.
*/
- extracted.merge(attributeAdapter.findAdapters(e).each(function (e) { return e.parent(self); }));
+ extracted = extracted.merge(attributeAdapter.findAdapters(e).map(function (e) { return e.parent(self); }).toArray());
if (e.getAttribute && e.getAttribute('data-condition')) {
var c = new Condition(e.getAttribute('data-condition'), e, extracted);
adapters.push(c.parent(self));
}
else {
- adapters.merge(extracted);
+ adapters = adapters.merge(extracted.toArray());
}
});
-
+
return adapters;
};
@@ -485,7 +279,6 @@
}
- console.log(actual);
return actual;
}

File Metadata

Mime Type
text/plain
Expires
Tue, Apr 13, 6:41 PM (3 w, 6 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5671
Default Alt Text
D564.id1780.diff (17 KB)

Event Timeline