lighty
Version:
The tiny engine for your handy microframework
199 lines (149 loc) • 4.34 kB
JavaScript
/*!
* lighty v0.9.0
* https://github.com/demiazz/lighty
*
* Copyright 2016-present Alexey Plutalov <demiazz.py@gmail.com>
* Released under the MIT license
*/
(function(global, factory) {
typeof exports === "object" && typeof module !== "undefined"
? (module.exports = factory())
: typeof define === "function" && define.amd
? define("lighty", factory)
: (global.lighty = factory());
})(this, function() {
"use strict";
var doc = window.document;
var body = doc.body;
/* ----- Matches Helper ----- */
function getMatchesFn() {
var e = doc.createElement("div");
return (
e.matches ||
e.matchesSelector ||
e.msMatchesSelector ||
e.mozMatchesSelector ||
e.webkitMatchesSelector ||
e.oMatchesSelector
);
}
var matchesFn = getMatchesFn();
function matches(element, selector) {
return matchesFn.call(element, selector);
}
/* ----- Walk Helper ----- */
function filterElements(elements) {
return elements.filter(function(e) {
return e instanceof Element;
});
}
function toArray(nodeList) {
return [].slice.call(nodeList);
}
function walk(trees, selector, callback) {
var roots = void 0;
if (trees instanceof Element) {
roots = [trees];
} else if (trees instanceof NodeList) {
roots = filterElements(toArray(trees));
} else if (Array.isArray(trees)) {
roots = filterElements(trees);
} else if (typeof trees === "string") {
roots = toArray(doc.querySelectorAll(trees));
} else {
throw new TypeError("Unsupported type of tree root");
}
roots.forEach(function(root) {
if (matches(root, selector)) {
callback(root);
}
toArray(root.querySelectorAll(selector)).forEach(callback);
});
}
/* ----- Engine ID Helper ----- */
var getEngineId = (function() {
var engineId = -1;
return function() {
engineId += 1;
return "__lighty__" + engineId;
};
})();
/* ----- Binding Helpers ----- */
function hasBinding(element, key, id) {
var bindings = element[key];
return bindings ? bindings.indexOf(id) !== -1 : false;
}
function bind(element, key, id) {
var bindings = element[key];
element[key] = bindings ? bindings.concat(id) : [id];
}
/* ----- DOM Ready Helper ----- */
function onDOMContentLoaded(action) {
if (doc.readyState === "loading") {
doc.addEventListener("DOMContentLoaded", action);
} else {
// See https://connect.microsoft.com/IE/feedback/details/792880/document-readystat
setTimeout(action, 1);
}
}
/* ----- Engine Factory ----- */
function createEngine(builder, onStart) {
var engineId = getEngineId();
var components = [];
var isRunning = false;
/* ----- Components Instatiation ----- */
function build(trees, component) {
var id = component[0];
var selector = component[1];
var args = component[2];
walk(trees, selector, function(element) {
if (hasBinding(element, engineId, id)) {
return;
}
builder.apply(null, [element].concat(args));
bind(element, engineId, id);
});
}
function vitalize(trees) {
if (!isRunning) {
throw Error("Document is not ready yet.");
}
var roots = trees || body;
components.forEach(function(component) {
return build(roots, component);
});
}
/* ----- Components Registration ----- */
function register(selector) {
var id = components.length;
for (
var _len = arguments.length,
args = Array(_len > 1 ? _len - 1 : 0),
_key = 1;
_key < _len;
_key++
) {
args[_key - 1] = arguments[_key];
}
var component = [id, selector, args];
components.push(component);
if (isRunning) {
build(body, component);
}
}
/* ----- Initialization ----- */
function start() {
isRunning = true;
vitalize();
if (onStart) {
onStart();
}
}
if (!(builder instanceof Function)) {
throw new TypeError("Builder must be a function");
}
onDOMContentLoaded(start);
return { component: register, vitalize: vitalize };
}
return createEngine;
});