- commit
- 5de21ab882f0789d7d0914b215da4896f112c486
- parent
- e783e09691135b41033ce70d377c2fabc2364afd
- Author
- Tobias Bengfort <tobias.bengfort@gmx.net>
- Date
- 2015-08-28 10:55
build
Diffstat
| C | dist/muu.js -> dist/muu-core.js | 1627 | +++++++++++++++++++------------------------------------------ |
| A | dist/muu-core.min.js | 10 | ++++++++++ |
| M | dist/muu.js | 2102 | +++++++++++++++++++++++++++++++------------------------------ |
| M | dist/muu.min.js | 36 | ++++++++++++++++++------------------ |
4 files changed, 1592 insertions, 2183 deletions
diff --git a/dist/muu.js b/dist/muu-core.js
@@ -1,13 +1,19 @@ 1 1 (function(window, document, undefined) { -1 2 var name = 'muu'; -1 3 2 4 (function(factory) { 3 5 if (typeof define === 'function' && define.amd) {4 -1 define('muu', [], factory);-1 6 define(name, ['lodash'], factory); 5 7 } else {6 -1 window['muu'] = factory();-1 8 window[name] = factory(window._); 7 9 }8 -1 })(function() {-1 10 })(function(lodash) { 9 11 var modules = {}; 10 12 -1 13 modules['muu-js-helpers'] = { -1 14 instance: lodash -1 15 }; -1 16 11 17 var map = function(a, fn) { 12 18 var b = []; 13 19 for (var i = 0; i < a.length; i++) { @@ -38,1207 +44,594 @@ 38 44 return modules[name].instance; 39 45 }; 40 4641 -1 /**42 -1 * Exports the {@link Directive} class.43 -1 * @module muu-directive44 -1 * @ignore45 -1 */46 -1 _define('muu-directive', ['muu-dom-helpers', 'muu-js-helpers', 'muu-update-dom'], function($, _, updateDOM) {47 -1 "use strict";48 -149 47 /**50 -1 * A directive is linked to a Element and manages the DOM tree below51 -1 * that element (excluding any isolated subtrees, e.g. those managed by52 -1 * subdirectives).53 -1 *54 -1 * It provides a set of methods to interact with the managed part of the55 -1 * DOM. This is separated into three distinct parts:56 -1 *57 -1 * - You can push data to the DOM using the {@link Directive#update}58 -1 * method. The DOM will than be updated using the template that was59 -1 * provided at construction.60 -1 * - You can get data from the DOM using the {@link Directive#getModel}61 -1 * method. This is however restricted to form field by design.62 -1 * - You can react to DOM events by specifying an alias for them. In the63 -1 * template, you might for example add the attribute64 -1 * `data-onclick="custom"` to an element. When there is `click` event on65 -1 * that element, a `muu-custom` event will be triggered on the66 -1 * directive's root element.67 -1 *68 -1 * Directives are typically not created directly but via {@link69 -1 * Registry#link}.70 -1 *71 -1 * @constructs Directive72 -1 * @param {Element} root73 -1 * @param {string} template74 -1 * @param {Registry} registry-1 48 * Exports the {@link Directive} class. -1 49 * @module muu-directive -1 50 * @ignore 75 51 */76 -1 var Directive = function(root, template, registry) {77 -1 var self = this;78 -179 -1 root.innerHTML = '';80 -181 -1 var eventCallback = function(originalEvent) {82 -1 var attrName = 'data-on' + originalEvent.type;83 -1 if (originalEvent.target.hasAttribute(attrName)) {84 -1 var eventName = originalEvent.target.getAttribute(attrName);85 -1 var event = $.createEvent('muu-' + eventName, originalEvent);86 -1 root.dispatchEvent(event);87 -1 }88 -1 };-1 52 _define('muu-directive', ['muu-dom-helpers', 'muu-js-helpers', 'muu-update-dom'], function($, _, updateDOM) { -1 53 "use strict"; 89 54 90 55 /**91 -1 * Rerender `template` with `data` and push the changes to the DOM.-1 56 * A directive is linked to a Element and manages the DOM tree below -1 57 * that element (excluding any isolated subtrees, e.g. those managed by -1 58 * subdirectives). 92 59 *93 -1 * @param {Object.<string, *>} data94 -1 * @see {@link module:muu-update-dom} for details.95 -1 * @see The templating system can be defined in the {@link Registry}.96 -1 */97 -1 this.update = function(data) {98 -1 var tmp = document.createElement('div');99 -1 tmp.innerHTML = registry.renderer(template, data);100 -1101 -1 updateDOM(root, tmp);102 -1103 -1 _.forEach(['keydown', 'keyup', 'click', 'change', 'search'], function(eventType) {104 -1 var selector = '[data-on' + eventType + ']';105 -1 _.forEach(self.querySelectorAll(selector), function(element) {106 -1 element.addEventListener(eventType, eventCallback, false);107 -1 });108 -1 });109 -1110 -1 var updateEvent = $.createEvent('muu-parent-update');111 -1 var subDirectives = this.querySelectorAll('muu.muu-initialised');112 -1 _.forEach(subDirectives, function(element) {113 -1 element.dispatchEvent(updateEvent);114 -1 });115 -1116 -1 registry.linkAll(self);117 -1 };118 -1119 -1 /**120 -1 * A variant of `querySelectorAll` that returns only elements from121 -1 * the managed part of the DOM.-1 60 * It provides a set of methods to interact with the managed part of the -1 61 * DOM. This is separated into three distinct parts: 122 62 *123 -1 * @private124 -1 * @param {string} selector125 -1 * @return {Array.<Element>} All child elements that match the given126 -1 * selector and are not isolated.127 -1 */128 -1 this.querySelectorAll = function(selector) {129 -1 var hits = root.querySelectorAll(selector);130 -1131 -1 // NOTE: querySelectorAll returns all elements in the tree that132 -1 // match the given selector. findAll does the same with *relative133 -1 // selectors* but does not seem to be available yet.134 -1 var isolations = root.querySelectorAll('.muu-isolate');135 -1 var isolated = _.union(_.map(isolations, function(isolation) {136 -1 return isolation.querySelectorAll(selector);137 -1 }));138 -1139 -1 return _.difference(hits, isolated);140 -1 };141 -1142 -1 /**143 -1 * A variant of `querySelector` that returns only elements from the144 -1 * managed part of the DOM.-1 63 * - You can push data to the DOM using the {@link Directive#update} -1 64 * method. The DOM will than be updated using the template that was -1 65 * provided at construction. -1 66 * - You can get data from the DOM using the {@link Directive#getModel} -1 67 * method. This is however restricted to form field by design. -1 68 * - You can react to DOM events by specifying an alias for them. In the -1 69 * template, you might for example add the attribute -1 70 * `data-onclick="custom"` to an element. When there is `click` event on -1 71 * that element, a `muu-custom` event will be triggered on the -1 72 * directive's root element. 145 73 *146 -1 * @private147 -1 * @param {String} selector148 -1 * @return {Element} First child element that matches the given149 -1 * selector and is not isolated.150 -1 * @suppress {missingReturn}151 -1 */152 -1 this.querySelector = function(selector) {153 -1 var all = self.querySelectorAll(selector);154 -1 if (all.length > 0) {155 -1 return all[0];156 -1 }157 -1 };158 -1159 -1 /**160 -1 * Get all model data as a flat object.-1 74 * Directives are typically not created directly but via {@link -1 75 * Registry#link}. 161 76 *162 -1 * @return {Object.<string, string|number|boolean>}163 -1 *//**164 -1 * Get the value of a form input by name.165 -1 *166 -1 * In case of a checkbox, returns `boolean`.167 -1 * In case of radioboxes, returns the value of the selected box.168 -1 *169 -1 * @param {string} name170 -1 * @param {*} [_default]171 -1 * @return {string|number|boolean|*}-1 77 * @constructs Directive -1 78 * @param {Element} root -1 79 * @param {string} template -1 80 * @param {Registry} registry 172 81 */173 -1 this.getModel = function(name, _default) {174 -1 if (name === undefined) {175 -1 var model = {};176 -1 _.forEach(self.querySelectorAll('[name]'), function(element) {177 -1 model[element.name] = self.getModel(element.name);178 -1 });179 -1 return model;180 -1 } else {181 -1 var element = self.querySelector('[name=' + name + ']');182 -1 if (element === undefined) {183 -1 return _default;184 -1 } else if (element.type === 'checkbox') {185 -1 return element.checked;186 -1 } else if (element.type === 'radio') {187 -1 var options = self.querySelectorAll('[name=' + name + ']');188 -1 return $.getRadio(options) || _default;189 -1 } else {190 -1 return element.value;191 -1 }192 -1 }193 -1 };-1 82 var Directive = function(root, template, registry) { -1 83 var self = this; 194 84195 -1 /**196 -1 * Set the value of a form input by name.197 -1 *198 -1 * In case of a checkbox, sets `element.checked`.199 -1 * In case of radioboxes, selects the box with matching value.200 -1 *201 -1 * @param {string} name202 -1 * @param {string|number|boolean} value203 -1 */204 -1 this.setModel = function(name, value) {205 -1 var element = self.querySelector('[name=' + name + ']');206 -1 if (element.type === 'checkbox') {207 -1 element.checked = value;208 -1 } else if (element.type === 'radio') {209 -1 var options = self.querySelectorAll('[name=' + name + ']');210 -1 $.setRadio(options, value);211 -1 } else {212 -1 element.value = value;213 -1 }214 -1 };215 -1 };216 -1217 -1 return Directive;218 -1 });219 -1 /**220 -1 * DOM related helper functions221 -1 * @module muu-dom-helpers222 -1 */223 -1 _define("muu-dom-helpers", ['muu-js-helpers'], function(_) {224 -1 "use strict";225 -1226 -1 var entityMap = {227 -1 '&': '&',228 -1 '<': '<',229 -1 '>': '>',230 -1 '"': '"',231 -1 "'": ''',232 -1 '/': '/'233 -1 };234 -1235 -1 /** @lends module:muu-dom-helpers */236 -1 var $ = {};-1 85 root.innerHTML = ''; 237 86238 -1 $.DELAY = 1000;239 -1240 -1 /**241 -1 * @param {string} string242 -1 * @return {string} - escaped HTML243 -1 */244 -1 $.escapeHtml = function(string) {245 -1 return String(string).replace(/[&<>"'\/]/g, function(s) {246 -1 return entityMap[s];247 -1 });248 -1 };-1 87 var eventCallback = function(originalEvent) { -1 88 var attrName = 'data-on' + originalEvent.type; -1 89 if (originalEvent.target.hasAttribute(attrName)) { -1 90 var eventName = originalEvent.target.getAttribute(attrName); -1 91 var event = $.createEvent('muu-' + eventName, originalEvent); -1 92 root.dispatchEvent(event); -1 93 } -1 94 }; 249 95250 -1 /**251 -1 * Cross browser custom events.252 -1 *253 -1 * *Note*: IE does not seem to like it when you use existing event names254 -1 * with this.255 -1 *256 -1 * @param {string} type257 -1 * @param {*} detail258 -1 * @return {Event}259 -1 * @see https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events260 -1 */261 -1 $.createEvent = function(type, detail) {262 -1 if (typeof CustomEvent === 'function') {263 -1 return new CustomEvent(type, {264 -1 detail: detail265 -1 });266 -1 } else {267 -1 var event = document.createEvent('CustomEvent');268 -1 event.initCustomEvent(type, false, true, detail);269 -1 return event;270 -1 }271 -1 };-1 96 /** -1 97 * Rerender `template` with `data` and push the changes to the DOM. -1 98 * -1 99 * @param {Object.<string, *>} data -1 100 * @see {@link module:muu-update-dom} for details. -1 101 * @see The templating system can be defined in the {@link Registry}. -1 102 */ -1 103 this.update = function(data) { -1 104 var tmp = document.createElement('div'); -1 105 tmp.innerHTML = registry.renderer(template, data); -1 106 -1 107 updateDOM(root, tmp); -1 108 -1 109 _.forEach(['keydown', 'keyup', 'click', 'change', 'search'], function(eventType) { -1 110 var selector = '[data-on' + eventType + ']'; -1 111 _.forEach(self.querySelectorAll(selector), function(element) { -1 112 element.addEventListener(eventType, eventCallback, false); -1 113 }); -1 114 }); 272 115273 -1 /**274 -1 * @param {EventTarget} element275 -1 * @param {string} eventName276 -1 * @param {Function} callback277 -1 * @return {function()} An unregister function278 -1 */279 -1 $.on = function(element, eventName, callback) {280 -1 element.addEventListener(eventName, callback, false);281 -1 return function() {282 -1 element.removeEventListener(eventName, callback, false);283 -1 };284 -1 };-1 116 var updateEvent = $.createEvent('muu-parent-update'); -1 117 var subDirectives = this.querySelectorAll('muu.muu-initialised'); -1 118 _.forEach(subDirectives, function(element) { -1 119 element.dispatchEvent(updateEvent); -1 120 }); 285 121286 -1 /**287 -1 * @param {Function} fn288 -1 * @return {function()} An unregister function289 -1 */290 -1 $.ready = function(fn) {291 -1 var _fn = _.once(fn);292 -1 if (document.readyState === 'complete') {293 -1 _fn();294 -1 return function() {};295 -1 } else {296 -1 var u1 = $.on(document, 'DOMContentLoaded', _fn);297 -1 var u2 = $.on(window, 'load', _fn);298 -1 return function() {299 -1 u1();300 -1 u2();-1 122 registry.linkAll(self); 301 123 };302 -1 }303 -1 };304 124305 -1 /**306 -1 * @param {Node} desc307 -1 * @param {Node} root308 -1 * @return {boolean}309 -1 */310 -1 $.isDescendant = function(desc, root) {311 -1 return !!desc && (desc === root || $.isDescendant(desc.parentNode, root));312 -1 };-1 125 /** -1 126 * A variant of `querySelectorAll` that returns only elements from -1 127 * the managed part of the DOM. -1 128 * -1 129 * @private -1 130 * @param {string} selector -1 131 * @return {Array.<Element>} All child elements that match the given -1 132 * selector and are not isolated. -1 133 */ -1 134 this.querySelectorAll = function(selector) { -1 135 var hits = root.querySelectorAll(selector); -1 136 -1 137 // NOTE: querySelectorAll returns all elements in the tree that -1 138 // match the given selector. findAll does the same with *relative -1 139 // selectors* but does not seem to be available yet. -1 140 var isolations = root.querySelectorAll('.muu-isolate'); -1 141 var isolated = _.union(_.map(isolations, function(isolation) { -1 142 return isolation.querySelectorAll(selector); -1 143 })); -1 144 -1 145 return _.difference(hits, isolated); -1 146 }; 313 147314 -1 /**315 -1 * Execute a function when `element` is removed from the DOM.316 -1 *317 -1 * *Note*: The callback is not executed directly when (or even before) the318 -1 * element is removed but with a slight delay. So the only way to test this319 -1 * is to use a timeout in the test.320 -1 *321 -1 * @param {Element} element322 -1 * @param {Function} fn323 -1 * @return {function()} An unregister function324 -1 */325 -1 $.destroy = function(element, fn) {326 -1 var unregister;327 -1328 -1 if (!!window.MutationObserver) {329 -1 var observer = new MutationObserver(function() {330 -1 if (!$.isDescendant(element, document)) {331 -1 fn();332 -1 unregister();-1 148 /** -1 149 * A variant of `querySelector` that returns only elements from the -1 150 * managed part of the DOM. -1 151 * -1 152 * @private -1 153 * @param {String} selector -1 154 * @return {Element} First child element that matches the given -1 155 * selector and is not isolated. -1 156 * @suppress {missingReturn} -1 157 */ -1 158 this.querySelector = function(selector) { -1 159 var all = self.querySelectorAll(selector); -1 160 if (all.length > 0) { -1 161 return all[0]; 333 162 }334 -1 });335 -1336 -1 observer.observe(document, {337 -1 childList: true,338 -1 subtree: true339 -1 });-1 163 }; 340 164341 -1 unregister = _.once(function() {342 -1 observer.disconnect();343 -1 observer = undefined;344 -1 });345 -1 } else {346 -1 var intervalID = setInterval(function() {347 -1 if (!$.isDescendant(element, document)) {348 -1 fn();349 -1 unregister();-1 165 /** -1 166 * Get all model data as a flat object. -1 167 * -1 168 * @return {Object.<string, string|number|boolean>} -1 169 *//** -1 170 * Get the value of a form input by name. -1 171 * -1 172 * In case of a checkbox, returns `boolean`. -1 173 * In case of radioboxes, returns the value of the selected box. -1 174 * -1 175 * @param {string} name -1 176 * @param {*} [_default] -1 177 * @return {string|number|boolean|*} -1 178 */ -1 179 this.getModel = function(name, _default) { -1 180 if (name === undefined) { -1 181 var model = {}; -1 182 _.forEach(self.querySelectorAll('[name]'), function(element) { -1 183 model[element.name] = self.getModel(element.name); -1 184 }); -1 185 return model; -1 186 } else { -1 187 var element = self.querySelector('[name=' + name + ']'); -1 188 if (element === undefined) { -1 189 return _default; -1 190 } else if (element.type === 'checkbox') { -1 191 return element.checked; -1 192 } else if (element.type === 'radio') { -1 193 var options = self.querySelectorAll('[name=' + name + ']'); -1 194 return $.getRadio(options) || _default; -1 195 } else { -1 196 return element.value; -1 197 } 350 198 }351 -1 }, $.DELAY);352 -1353 -1 unregister = function() {354 -1 clearInterval(intervalID);355 199 };356 -1 }357 200358 -1 return unregister;359 -1 };360 -1361 -1 /**362 -1 * @param {Array.<Element>} options363 -1 * @return {string}364 -1 * @suppress {missingReturn}365 -1 */366 -1 $.getRadio = function(options) {367 -1 for (var i = 0; i < options.length; i++) {368 -1 if (options[i].checked) {369 -1 return options[i].value;370 -1 }371 -1 }372 -1 };373 -1374 -1 /**375 -1 * @param {Array.<Element>} options376 -1 * @param {string} value377 -1 */378 -1 $.setRadio = function(options, value) {379 -1 for (var i = 0; i < options.length; i++) {380 -1 if (options[i].value === value) {381 -1 options[i].checked = true;382 -1 } else {383 -1 options[i].checked = false;384 -1 }385 -1 }386 -1 };387 -1388 -1 return $;389 -1 });390 -1 /**391 -1 * Minimal implementation of an underscore/lodash subset.392 -1 * @module muu-js-helpers393 -1 */394 -1 _define('muu-js-helpers', [], function() {395 -1 "use strict";396 -1397 -1 /** @lends module:muu-js-helpers */398 -1 var _ = {};399 -1400 -1 /**401 -1 * @param {Object} value402 -1 * @return {string}403 -1 */404 -1 var objToString = function(value) {405 -1 return Object.prototype.toString.call(value);406 -1 };407 -1408 -1 /**409 -1 * @param {*} value410 -1 * @return {boolean}411 -1 */412 -1 _.isString = function(value) {413 -1 return typeof value === 'string' || objToString(value) === '[object String]';414 -1 };415 -1416 -1 /**417 -1 * @function418 -1 * @param {*} value419 -1 * @return {boolean}420 -1 */421 -1 _.isArray = Array.isArray;422 -1423 -1 /**424 -1 * @param {*} value425 -1 * @return {boolean}426 -1 */427 -1 _.isFunction = function(value) {428 -1 return typeof value === 'function';429 -1 };430 -1431 -1 /**432 -1 * @param {Function} fn433 -1 * @return {Function}434 -1 */435 -1 _.once = function(fn) {436 -1 var result;437 -1 var called = false;438 -1439 -1 return function() {440 -1 if (!called) {441 -1 result = fn.apply(this, arguments);442 -1 called = true;443 -1 }444 -1 return result;445 -1 };446 -1 };447 -1448 -1 /**449 -1 * @param {Array} array450 -1 * @param {*} value451 -1 * @return {number}452 -1 */453 -1 _.indexOf = function(array, value) {454 -1 if ('indexOf' in array) {455 -1 return array.indexOf(value);456 -1 }457 -1458 -1 for (var i = 0; i < array.length; i++) {459 -1 if (array[i] === value) {460 -1 return i;461 -1 }462 -1 }463 -1 return -1;464 -1 };465 -1466 -1 /**467 -1 * @param {Array} array468 -1 * @param {Function} fn469 -1 */470 -1 _.forEach = function(array, fn) {471 -1 if ('forEach' in array) {472 -1 return array.forEach(fn);473 -1 }474 -1475 -1 for (var i = 0; i < array.length; i++) {476 -1 fn(array[i]);477 -1 }478 -1 };479 -1480 -1 /**481 -1 * @param {Array} array482 -1 * @param {Function} fn483 -1 * @return {Array}484 -1 */485 -1 _.map = function(array, fn) {486 -1 if ('map' in array) {487 -1 return array.map(fn);488 -1 }489 -1490 -1 var results = [];491 -1 for (var i = 0; i < array.length; i++) {492 -1 results.push(fn(array[i]));493 -1 }494 -1 return results;495 -1 };496 -1497 -1 /**498 -1 * @param {Array} array499 -1 * @param {Function} fn500 -1 * @return {Array}501 -1 */502 -1 _.filter = function(array, fn) {503 -1 if ('filter' in array) {504 -1 return array.filter(fn);505 -1 }506 -1507 -1 var results = [];508 -1 for (var i = 0; i < array.length; i++) {509 -1 if (fn(array[i])) {510 -1 results.push(array[i]);511 -1 }512 -1 }513 -1 return results;514 -1 };515 -1516 -1 /**517 -1 * @param {Array.<Array>} arrays518 -1 * @return {Array}519 -1 */520 -1 _.union = function(arrays) {521 -1 var results = [];522 -1 for (var i = 0; i < arrays.length; i++) {523 -1 for (var j = 0; j < arrays[i].length; j++) {524 -1 if (_.indexOf(results, arrays[i][j]) === -1) {525 -1 results.push(arrays[i][j]);-1 201 /** -1 202 * Set the value of a form input by name. -1 203 * -1 204 * In case of a checkbox, sets `element.checked`. -1 205 * In case of radioboxes, selects the box with matching value. -1 206 * -1 207 * @param {string} name -1 208 * @param {string|number|boolean} value -1 209 */ -1 210 this.setModel = function(name, value) { -1 211 var element = self.querySelector('[name=' + name + ']'); -1 212 if (element.type === 'checkbox') { -1 213 element.checked = value; -1 214 } else if (element.type === 'radio') { -1 215 var options = self.querySelectorAll('[name=' + name + ']'); -1 216 $.setRadio(options, value); -1 217 } else { -1 218 element.value = value; 526 219 }527 -1 }528 -1 }529 -1 return results;530 -1 };531 -1532 -1 /**533 -1 * @param {Array} a534 -1 * @param {Array} b535 -1 * @return {Array}536 -1 */537 -1 _.difference = function(a, b) {538 -1 var results = [];539 -1 for (var i = 0; i < a.length; i++) {540 -1 if (_.indexOf(b, a[i]) === -1) {541 -1 results.push(a[i]);542 -1 }543 -1 }544 -1 return results;545 -1 };546 -1547 -1 /**548 -1 * @param {Array} a549 -1 * @return {Array}550 -1 */551 -1 _.flatten = function(a) {552 -1 var o = [];553 -1 _.forEach(a, function(item) {554 -1 if (_.isArray(item)) {555 -1 o = o.concat(_.flatten(item));556 -1 } else {557 -1 o.push(item);558 -1 }559 -1 });560 -1 return o;561 -1 };562 -1563 -1 return _;564 -1 });565 -1 /**566 -1 * angular inspired location service.567 -1 * @module muu-location568 -1 */569 -1 _define('muu-location', ['muu-search'], function(q) {570 -1 "use strict";571 -1572 -1 /** @lends module:muu-location */573 -1 var loc = {};574 -1575 -1 /**576 -1 * @return {string}577 -1 */578 -1 loc.absUrl = function() {579 -1 return location.href;580 -1 };581 -1582 -1 /**583 -1 * @return {string}584 -1 *//**585 -1 * @param {string} value586 -1 * @param {boolean} [replace]587 -1 * @return {muu-location}588 -1 */589 -1 loc.url = function(value, replace) {590 -1 if (value === undefined) {591 -1 return location.pathname + location.search + location.hash;592 -1 } else if (replace) {593 -1 history.replaceState(null, null, value);594 -1 } else {595 -1 history.pushState(null, null, value);596 -1 }597 -1 return loc;598 -1 };599 -1600 -1 /**601 -1 * @return {string}602 -1 */603 -1 loc.protocol = function() {604 -1 return location.protocol;605 -1 };-1 220 }; -1 221 }; 606 222 -1 223 return Directive; -1 224 }); 607 225 /**608 -1 * @return {string}-1 226 * DOM related helper functions -1 227 * @module muu-dom-helpers 609 228 */610 -1 loc.host = function() {611 -1 return location.host;612 -1 };-1 229 _define("muu-dom-helpers", ['muu-js-helpers'], function(_) { -1 230 "use strict"; 613 231614 -1 /**615 -1 * @return {string}616 -1 */617 -1 loc.port = function() {618 -1 return location.port;619 -1 };620 -1621 -1 /**622 -1 * @return {string}623 -1 *//**624 -1 * @param {string} value625 -1 * @param {boolean} [replace]626 -1 * @return {muu-location}627 -1 */628 -1 loc.path = function(value, replace) {629 -1 if (value === undefined) {630 -1 return location.pathname;631 -1 } else {632 -1 var url = value + location.search + location.hash;633 -1 loc.url(url, replace);634 -1 return loc;635 -1 }636 -1 };-1 232 var entityMap = { -1 233 '&': '&', -1 234 '<': '<', -1 235 '>': '>', -1 236 '"': '"', -1 237 "'": ''', -1 238 '/': '/' -1 239 }; 637 240638 -1 var _search = function(value, replace) {639 -1 if (value === undefined) {640 -1 return location.search;641 -1 } else {642 -1 if (value && value[0] !== '?') {643 -1 value = '?' + value;644 -1 }645 -1 if (value.length === 1) {646 -1 value = '';647 -1 }-1 241 /** @lends module:muu-dom-helpers */ -1 242 var $ = {}; 648 243649 -1 var url = location.pathname + value + location.hash;650 -1 loc.url(url, replace);651 -1 return loc;652 -1 }653 -1 };-1 244 $.DELAY = 1000; 654 245655 -1 /**656 -1 * @return {Object}657 -1 *//**658 -1 * @param {string|object} value659 -1 * @return {muu-location}660 -1 *//**661 -1 * @param {string} key662 -1 * @param {*} value663 -1 * @param {boolean} [replace]664 -1 * @return {muu-location}665 -1 */666 -1 loc.search = function(key, value, replace) {667 -1 if (key !== undefined) {668 -1 if (value !== undefined) {669 -1 var search = q.parse(_search());670 -1 search[key] = value;671 -1 return _search(q.unparse(search), replace);672 -1 } else {673 -1 return _search(q.unparse(key), replace);674 -1 }675 -1 } else {676 -1 return q.parse(_search());677 -1 }678 -1 };-1 246 /** -1 247 * @param {string} string -1 248 * @return {string} - escaped HTML -1 249 */ -1 250 $.escapeHtml = function(string) { -1 251 return String(string).replace(/[&<>"'\/]/g, function(s) { -1 252 return entityMap[s]; -1 253 }); -1 254 }; 679 255680 -1 /**681 -1 * @return {string}682 -1 *//**683 -1 * @param {string} value684 -1 * @param {boolean} [replace]685 -1 * @return {muu-location}686 -1 */687 -1 loc.hash = function(value, replace) {688 -1 if (value === undefined) {689 -1 if (location.hash) {690 -1 return location.hash.slice(1);-1 256 /** -1 257 * Cross browser custom events. -1 258 * -1 259 * *Note*: IE does not seem to like it when you use existing event names -1 260 * with this. -1 261 * -1 262 * @param {string} type -1 263 * @param {*} detail -1 264 * @return {Event} -1 265 * @see https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events -1 266 */ -1 267 $.createEvent = function(type, detail) { -1 268 if (typeof CustomEvent === 'function') { -1 269 return new CustomEvent(type, { -1 270 detail: detail -1 271 }); 691 272 } else {692 -1 return '';-1 273 var event = document.createEvent('CustomEvent'); -1 274 event.initCustomEvent(type, false, true, detail); -1 275 return event; 693 276 }694 -1 } else {695 -1 var url = location.pathname + location.search + '#' + value;696 -1 loc.url(url, replace);697 -1 return loc;698 -1 }699 -1 };700 -1701 -1 /**702 -1 * @param {string} eventName703 -1 * @param {Function} fn704 -1 * @return {muu-location}705 -1 */706 -1 loc.addEventListener = function(eventName, fn) {707 -1 if (eventName === 'change') {708 -1 window.addEventListener('popstate', fn, false);709 -1 }710 -1 return loc;711 -1 };712 -1713 -1 /**714 -1 * @param {string} eventName715 -1 * @param {Function} fn716 -1 * @return {muu-location}717 -1 */718 -1 loc.removeEventListener = function(eventName, fn) {719 -1 if (eventName === 'change') {720 -1 window.removeEventListener('popstate', fn, false);721 -1 }722 -1 return loc;723 -1 };724 -1725 -1 return loc;726 -1 });727 -1 /**728 -1 * Exports the {@link Registry} class.729 -1 * @module muu-registry730 -1 * @ignore731 -1 */732 -1 _define('muu-registry', ['muu-template', 'muu-directive', 'muu-js-helpers', 'muu-dom-helpers'], function(muuTemplate, Directive, _, $) {733 -1 "use strict";734 -1735 -1 /**736 -1 * @constructs Registry737 -1 * @param {Object} config The config object may have following properties:738 -1 *739 -1 * - **debug** - `{boolean}` - Enable debug mode. In debug mode,740 -1 * directive objects are available as properties from the DOM as741 -1 * `element.directive`.742 -1 * - **renderer** - `{function(string, Object)}` - The template renderer743 -1 * to be used. Defaults to {@link module:muu-template}.744 -1 */745 -1 var Registry = function(config) {746 -1 var self = this;747 -1 var directives = {};748 -1749 -1 this.config = config || {};750 -1 this.renderer = self.config.renderer || muuTemplate;-1 277 }; 751 278 752 279 /**753 -1 * Register a new type of {@link Directive}754 -1 *755 -1 * @param {string} type756 -1 * @param {string} template757 -1 * @param {function(Directive, Element): Function} link The link758 -1 * function is called with an instance of {@link Directive} and a759 -1 * Element when {@link Registry#link} is executed.760 -1 *761 -1 * It is the only place where you can access a directive and762 -1 * therefore the place where you define its behavior.763 -1 *764 -1 * This typically means to make an initial call to {@link765 -1 * Directive#update} and to add some event listeners. You should also766 -1 * return an *unlink* function that clears all external references in767 -1 * order to avoid memory leaks.768 -1 * @return {Registry} this-1 280 * @param {EventTarget} element -1 281 * @param {string} eventName -1 282 * @param {Function} callback -1 283 * @return {function()} An unregister function 769 284 */770 -1 this.registerDirective = function(type, template, link) {771 -1 directives[type] = {772 -1 template: template,773 -1 link: link-1 285 $.on = function(element, eventName, callback) { -1 286 element.addEventListener(eventName, callback, false); -1 287 return function() { -1 288 element.removeEventListener(eventName, callback, false); 774 289 };775 -1 return self;776 290 }; 777 291 778 292 /**779 -1 * Shortcut for wrapping calls to {@link Registry} in a function.780 -1 *781 -1 * This can be esepcially helpful if that function is defined in a782 -1 * different module.783 -1 *784 -1 * ```.js785 -1 * _define('foobar', [], function() {786 -1 * return function(registry) {787 -1 * registry788 -1 * .registerDirective('foo', '...', function() {...})789 -1 * .registerDirective('bar', '...', function() {...});790 -1 * };791 -1 * });792 -1 *793 -1 * require(['foobar'], function(foobar) {794 -1 * var registry = new Registry();795 -1 * registry.registerModule(foobar);796 -1 * });797 -1 * ```798 -1 *799 -1 * @param {function(Registry)} module800 -1 * @return {Registry} this-1 293 * @param {Function} fn -1 294 * @return {function()} An unregister function -1 295 */ -1 296 $.ready = function(fn) { -1 297 var _fn = _.once(fn); -1 298 if (document.readyState === 'complete') { -1 299 _fn(); -1 300 return function() {}; -1 301 } else { -1 302 var u1 = $.on(document, 'DOMContentLoaded', _fn); -1 303 var u2 = $.on(window, 'load', _fn); -1 304 return function() { -1 305 u1(); -1 306 u2(); -1 307 }; -1 308 } -1 309 }; -1 310 -1 311 /** -1 312 * @param {Node} desc -1 313 * @param {Node} root -1 314 * @return {boolean} 801 315 */802 -1 this.registerModule = function(module) {803 -1 module(self);804 -1 return self;-1 316 $.isDescendant = function(desc, root) { -1 317 return !!desc && (desc === root || $.isDescendant(desc.parentNode, root)); 805 318 }; 806 319 807 320 /**808 -1 * Create and initialise a {@link Directive} for `element`.-1 321 * Execute a function when `element` is removed from the DOM. -1 322 * -1 323 * *Note*: The callback is not executed directly when (or even before) the -1 324 * element is removed but with a slight delay. So the only way to test this -1 325 * is to use a timeout in the test. 809 326 * 810 327 * @param {Element} element811 -1 * @param {string} type812 -1 * @return {Directive}-1 328 * @param {Function} fn -1 329 * @return {function()} An unregister function 813 330 */814 -1 this.link = function(element, type) {815 -1 if (type === undefined) {816 -1 type = element.getAttribute('type');817 -1 }818 -1819 -1 if (!directives.hasOwnProperty(type)) {820 -1 throw new Error('Unknown directive type: ' + type);821 -1 }822 -1823 -1 var template = directives[type].template;824 -1 var link = directives[type].link;-1 331 $.destroy = function(element, fn) { -1 332 var unregister; -1 333 -1 334 if (!!window.MutationObserver) { -1 335 var observer = new MutationObserver(function() { -1 336 if (!$.isDescendant(element, document)) { -1 337 fn(); -1 338 unregister(); -1 339 } -1 340 }); 825 341826 -1 var directive = new Directive(element, template, self);827 -1 var unlink = link(directive, element);828 -1 element.classList.add('muu-isolate');829 -1 element.classList.add('muu-initialised');-1 342 observer.observe(document, { -1 343 childList: true, -1 344 subtree: true -1 345 }); 830 346831 -1 if (self.config.debug) {832 -1 element.directive = directive;833 -1 }-1 347 unregister = _.once(function() { -1 348 observer.disconnect(); -1 349 observer = undefined; -1 350 }); -1 351 } else { -1 352 var intervalID = setInterval(function() { -1 353 if (!$.isDescendant(element, document)) { -1 354 fn(); -1 355 unregister(); -1 356 } -1 357 }, $.DELAY); 834 358835 -1 if (unlink !== undefined) {836 -1 $.destroy(element, unlink);-1 359 unregister = function() { -1 360 clearInterval(intervalID); -1 361 }; 837 362 } 838 363839 -1 return directive;-1 364 return unregister; 840 365 }; 841 366 842 367 /**843 -1 * Link all directives that can be found inside `root`.844 -1 *845 -1 * @param {Element} root846 -1 * @return {Array.<Directive>}-1 368 * @param {Array.<Element>} options -1 369 * @return {string} -1 370 * @suppress {missingReturn} 847 371 */848 -1 this.linkAll = function(root) {849 -1 // NOTE: root may be a DOM Node or a directive850 -1 var elements = _.filter(root.querySelectorAll('muu'), function(element) {851 -1 return !element.classList.contains('muu-initialised');852 -1 });853 -1 return _.map(elements, function(element) {854 -1 return self.link(element);855 -1 });856 -1 };857 -1 };858 -1859 -1 return Registry;860 -1 });861 -1 _define('muu-search', ['muu-js-helpers'], function(_) {862 -1 "use strict";863 -1864 -1 var q = {};865 -1866 -1 q.parse = function(s) {867 -1 var q = {};868 -1869 -1 var set = function(key, value) {870 -1 if (!q.hasOwnProperty(key)) {871 -1 q[key] = value;872 -1 } else if (_.isArray(q[key])) {873 -1 q[key].push(value);874 -1 } else {875 -1 q[key] = [q[key], value];-1 372 $.getRadio = function(options) { -1 373 for (var i = 0; i < options.length; i++) { -1 374 if (options[i].checked) { -1 375 return options[i].value; -1 376 } 876 377 } 877 378 }; 878 379879 -1 _.forEach(s.substring(1).split('&'), function(item) {880 -1 var i = _.map(item.split('='), decodeURIComponent);881 -1 if (i.length === 2) {882 -1 set(i[0], i[1]);883 -1 } else if (i[0]) {884 -1 set(i[0], true);-1 380 /** -1 381 * @param {Array.<Element>} options -1 382 * @param {string} value -1 383 */ -1 384 $.setRadio = function(options, value) { -1 385 for (var i = 0; i < options.length; i++) { -1 386 if (options[i].value === value) { -1 387 options[i].checked = true; -1 388 } else { -1 389 options[i].checked = false; -1 390 } 885 391 }886 -1 });887 -1 return q;888 -1 };889 -1890 -1 var unparseItem = function(key, value) {891 -1 if (value === undefined || value === null || value === false) {892 -1 return [];893 -1 } else if (_.isArray(value)) {894 -1 return _.flatten(_.map(value, function(v) {895 -1 return unparseItem(key, v);896 -1 }));897 -1 } else if (value === true) {898 -1 return [encodeURIComponent(key)];899 -1 } else {900 -1 return [encodeURIComponent(key) + '=' + encodeURIComponent(value)];901 -1 }902 -1 };-1 392 }; 903 393904 -1 q.unparse = function(q) {905 -1 if (_.isString(q)) {906 -1 return q;907 -1 }-1 394 return $; -1 395 }); -1 396 /** -1 397 * This module gives access to the following objects: -1 398 * -1 399 * - `Registry` - {@link Registry} -1 400 * - `$` - {@link module:muu-dom-helpers} -1 401 * - `$location` - {@link module:muu-location} -1 402 * -1 403 * @module muu -1 404 */ -1 405 _define('muu', ['muu-registry', 'muu-dom-helpers', 'muu-location'], function(Registry, $, $location) { -1 406 "use strict"; 908 407909 -1 var a = []910 -1 for (var key in q) {911 -1 if (q.hasOwnProperty(key)) {912 -1 a = a.concat(unparseItem(key, q[key]));913 -1 }914 -1 }915 -1 if (a.length > 0) {916 -1 return '?' + a.join('&');917 -1 } else {918 -1 return '';919 -1 }920 -1 };-1 408 var module = {}; 921 409922 -1 return q;923 -1 });924 -1 /**925 -1 * minimal mustache insipred templating926 -1 *927 -1 * ## Variables928 -1 *929 -1 * Variables are created with a `{{name}}` tag. These are always escaped.930 -1 *931 -1 * ## Loops932 -1 *933 -1 * Loops render blocks of text a number of times, depending on the value of934 -1 * the key in the current context.935 -1 *936 -1 * A loop begins with a pound and ends with a slash. That is, `{{#person}}`937 -1 * begins a "person" section while `{{/person}}` ends it.938 -1 *939 -1 * If the value is an array, the block is repeated for each item in that array.940 -1 * In any other case, the block is rendered with the outer scope, but only if941 -1 * the value is truthy.942 -1 *943 -1 * ## Inverted loops944 -1 *945 -1 * Inverted loops render blocks of test if the value of the key is falsy. They946 -1 * begin with a caret.947 -1 *948 -1 * ## Comments949 -1 *950 -1 * Comments begin with a bang and are ignored.951 -1 *952 -1 * ## Pairs953 -1 *954 -1 * Pairs look like JSON objects. The result is a space separated list of all955 -1 * keys with truthy values.956 -1 *957 -1 * ```958 -1 * muuTemplate('{{foo: var1, bar: var2, baz: var3}}', {959 -1 * var1: true,960 -1 * var2: false,961 -1 * var3: true962 -1 * }); // 'foo baz'963 -1 * ```964 -1 *965 -1 * ## Special variable `this`966 -1 *967 -1 * `this` always refers to the current context. So the following expressions968 -1 * are equivalent:969 -1 *970 -1 * ```971 -1 * muuTemplate('{{#items}}{{content}}{{/items}}', {972 -1 * item: [{973 -1 * content: 1974 -1 * }, {975 -1 * content: 2976 -1 * }]977 -1 * });978 -1 *979 -1 * muuTemplate('{{#this}}{{this}}{{/this}}', [1, 2]);980 -1 * ```981 -1 *982 -1 * @module muu-template983 -1 * @param {string} template984 -1 * @param {Object} data985 -1 * @return {string}986 -1 */987 -1 _define('muu-template', ['muu-js-helpers', 'muu-dom-helpers'], function(_, $) {988 -1 "use strict";989 -1990 -1 var openTag = '{{';991 -1 var closeTag = '}}';992 -1993 -1 var getValue = function(key, data) {994 -1 return key === 'this' ? data : data[key];995 -1 };-1 410 module.Registry = Registry; -1 411 module.$ = $; -1 412 module.$location = $location; 996 413997 -1 var parseVariableTemplate = function(template) {998 -1 var content = template.slice(2, -2);-1 414 return module; -1 415 }); -1 416 /** -1 417 * Exports the {@link Registry} class. -1 418 * @module muu-registry -1 419 * @ignore -1 420 */ -1 421 _define('muu-registry', ['muu-template', 'muu-directive', 'muu-js-helpers', 'muu-dom-helpers'], function(muuTemplate, Directive, _, $) { -1 422 "use strict"; 999 4231000 -1 if (template.indexOf(':') === -1) {1001 -1 return function(data) {1002 -1 return $.escapeHtml(getValue(content, data) || '');-1 424 /** -1 425 * @constructs Registry -1 426 * @param {Object} config The config object may have following properties: -1 427 * -1 428 * - **debug** - `{boolean}` - Enable debug mode. In debug mode, -1 429 * directive objects are available as properties from the DOM as -1 430 * `element.directive`. -1 431 * - **renderer** - `{function(string, Object)}` - The template renderer -1 432 * to be used. Defaults to {@link module:muu-template}. -1 433 */ -1 434 var Registry = function(config) { -1 435 var self = this; -1 436 var directives = {}; -1 437 -1 438 this.config = config || {}; -1 439 this.renderer = self.config.renderer || muuTemplate; -1 440 -1 441 /** -1 442 * Register a new type of {@link Directive} -1 443 * -1 444 * @param {string} type -1 445 * @param {string} template -1 446 * @param {function(Directive, Element): Function} link The link -1 447 * function is called with an instance of {@link Directive} and a -1 448 * Element when {@link Registry#link} is executed. -1 449 * -1 450 * It is the only place where you can access a directive and -1 451 * therefore the place where you define its behavior. -1 452 * -1 453 * This typically means to make an initial call to {@link -1 454 * Directive#update} and to add some event listeners. You should also -1 455 * return an *unlink* function that clears all external references in -1 456 * order to avoid memory leaks. -1 457 * @return {Registry} this -1 458 */ -1 459 this.registerDirective = function(type, template, link) { -1 460 directives[type] = { -1 461 template: template, -1 462 link: link -1 463 }; -1 464 return self; 1003 465 };1004 -1 } else {1005 -1 var pairs = content.split(',').map(function(pair) {1006 -1 var v = pair.split(':');1007 -1 var key = v[0].trim();1008 -1 var value = v.slice(1).join(':').trim();1009 -1 return [key, value];1010 -1 });1011 -11012 -1 return function(data) {1013 -1 var results = [];1014 4661015 -1 for (var i = 0; i < pairs.length; i++) {1016 -1 var key = pairs[i][0];1017 -1 var value = pairs[i][1];-1 467 /** -1 468 * Shortcut for wrapping calls to {@link Registry} in a function. -1 469 * -1 470 * This can be esepcially helpful if that function is defined in a -1 471 * different module. -1 472 * -1 473 * ```.js -1 474 * _define('foobar', [], function() { -1 475 * return function(registry) { -1 476 * registry -1 477 * .registerDirective('foo', '...', function() {...}) -1 478 * .registerDirective('bar', '...', function() {...}); -1 479 * }; -1 480 * }); -1 481 * -1 482 * require(['foobar'], function(foobar) { -1 483 * var registry = new Registry(); -1 484 * registry.registerModule(foobar); -1 485 * }); -1 486 * ``` -1 487 * -1 488 * @param {function(Registry)} module -1 489 * @return {Registry} this -1 490 */ -1 491 this.registerModule = function(module) { -1 492 module(self); -1 493 return self; -1 494 }; 1018 4951019 -1 if (getValue(value, data)) {1020 -1 results.push(key);1021 -1 }-1 496 /** -1 497 * Create and initialise a {@link Directive} for `element`. -1 498 * -1 499 * @param {Element} element -1 500 * @param {string} type -1 501 * @return {Directive} -1 502 */ -1 503 this.link = function(element, type) { -1 504 if (type === undefined) { -1 505 type = element.getAttribute('type'); 1022 506 } 1023 5071024 -1 return $.escapeHtml(results.join(' '));1025 -1 };1026 -1 }1027 -1 };-1 508 if (!directives.hasOwnProperty(type)) { -1 509 throw new Error('Unknown directive type: ' + type); -1 510 } 1028 5111029 -1 var parseLoopTemplate = function(tag, afterTag, inverted) {1030 -1 var tagName = tag.slice(3, -2);-1 512 var template = directives[type].template; -1 513 var link = directives[type].link; 1031 5141032 -1 var v = parseTemplate(afterTag, tagName);1033 -1 var inner = v[0];1034 -1 var afterLoop = v[1];-1 515 var directive = new Directive(element, template, self); -1 516 var unlink = link(directive, element); -1 517 element.classList.add('muu-isolate'); -1 518 element.classList.add('muu-initialised'); 1035 5191036 -1 var render = function(data) {1037 -1 if (inverted) {1038 -1 if (getValue(tagName, data)) {1039 -1 return '';1040 -1 } else {1041 -1 return inner(data);-1 520 if (self.config.debug) { -1 521 element.directive = directive; 1042 522 }1043 -1 } else {1044 -1 if (_.isArray(getValue(tagName, data))) {1045 -1 var result = '';1046 -1 for (var i = 0; i < getValue(tagName, data).length; i++) {1047 -1 result += inner(getValue(tagName, data)[i]);1048 -1 }1049 -1 return result;1050 -1 } else if (getValue(tagName, data)) {1051 -1 return inner(data);1052 -1 } else {1053 -1 return '';-1 523 -1 524 if (unlink !== undefined) { -1 525 $.destroy(element, unlink); 1054 526 }1055 -1 }1056 -1 };1057 5271058 -1 return [render, afterLoop];1059 -1 };-1 528 return directive; -1 529 }; 1060 5301061 -1 var concat = function(a) {1062 -1 var last = a.pop();1063 -11064 -1 if (_.isArray(last)) {1065 -1 a.push(last[0]);1066 -1 return [concat(a), last[1]];1067 -1 } else {1068 -1 a.push(last);1069 -11070 -1 return function(data) {1071 -1 return a.map(function(item) {1072 -1 if (_.isString(item)) {1073 -1 return item;1074 -1 } else if (_.isFunction(item)) {1075 -1 return item(data);1076 -1 }1077 -1 }).join('');-1 531 /** -1 532 * Link all directives that can be found inside `root`. -1 533 * -1 534 * @param {Element} root -1 535 * @return {Array.<Directive>} -1 536 */ -1 537 this.linkAll = function(root) { -1 538 // NOTE: root may be a DOM Node or a directive -1 539 var elements = _.filter(root.querySelectorAll('muu'), function(element) { -1 540 return !element.classList.contains('muu-initialised'); -1 541 }); -1 542 return _.map(elements, function(element) { -1 543 return self.link(element); -1 544 }); 1078 545 };1079 -1 }1080 -1 };-1 546 }; 1081 5471082 -1 var parseTemplate = function(template, loopName) {1083 -1 var openIndex = template.indexOf(openTag);1084 -1 if (openIndex === -1) {1085 -1 if (loopName === undefined) {1086 -1 return function() {1087 -1 return template;1088 -1 };1089 -1 } else {1090 -1 throw new Error('unclosed loop: ' + loopName);1091 -1 }1092 -1 } else {1093 -1 var beforeTag = template.slice(0, openIndex);1094 -1 var tmp = template.slice(openIndex);-1 548 return Registry; -1 549 }); -1 550 /** -1 551 * Recreate children of `source` in `target` by making only small adjustments. -1 552 * -1 553 * *The following section explains details about the current implementation. -1 554 * These are likely to change in the future.* -1 555 * -1 556 * The algorithms is relatively simple. It just iterates through all top level -1 557 * nodes. If a node has a different `nodeType` (e.g. text or element) or a -1 558 * different `nodeName` (e.g. div or ul) it is replaced completely and the -1 559 * algorithm proceeds with the node's children recursively. Otherwise, only -1 560 * the nodes's attributes are updated. -1 561 * -1 562 * Note that non-attribute properties (e.g. value) are lost in the first case -1 563 * and preserved in the second. -1 564 * -1 565 * If the algorithm encounters an element with the class `muu-isolate` it does -1 566 * not recurse into its children. This way, you can protect dynamically -1 567 * generated content from being overwritten. -1 568 * -1 569 * @module muu-update-dom -1 570 * @param {Element} target -1 571 * @param {Element} source -1 572 */ -1 573 _define('muu-update-dom', ['muu-js-helpers'], function(_) { -1 574 "use strict"; 1095 5751096 -1 var closeIndex = tmp.indexOf(closeTag) + 2;1097 -1 if (closeIndex === 1) {1098 -1 throw new Error('unclosed tag: ' + tmp);1099 -1 }1100 -1 var tag = tmp.slice(0, closeIndex);1101 -1 var afterTag = tmp.slice(closeIndex);1102 -11103 -1 if (tag.lastIndexOf('{{#', 0) === 0) {1104 -1 var v = parseLoopTemplate(tag, afterTag);1105 -1 var loop = v[0];1106 -1 var after = parseTemplate(v[1], loopName);1107 -1 return concat([beforeTag, loop, after]);1108 -1 } else if (tag.lastIndexOf('{{^', 0) === 0) {1109 -1 var v = parseLoopTemplate(tag, afterTag, true);1110 -1 var loop = v[0];1111 -1 var after = parseTemplate(v[1], loopName);1112 -1 return concat([beforeTag, loop, after]);1113 -1 } else if (tag.lastIndexOf('{{!', 0) === 0) {1114 -1 var after = parseTemplate(afterTag, loopName);1115 -1 return concat([beforeTag, after]);1116 -1 } else if (tag.lastIndexOf('{{/', 0) === 0) {1117 -1 if (tag.slice(3, -2) === loopName) {1118 -1 var render = function() {1119 -1 return beforeTag;1120 -1 };1121 -1 return [render, afterTag];1122 -1 } else {1123 -1 throw new Error('unexpected closing loop: ' + tag);-1 576 var updateAttributes = function(target, source) { -1 577 var targetAttrNames = _.map(target.attributes, function(item) { -1 578 return item.name; -1 579 }); -1 580 var sourceAttrNames = _.map(source.attributes, function(item) { -1 581 return item.name; -1 582 }); -1 583 -1 584 _.forEach(targetAttrNames, function(name) { -1 585 // NOTE: ie8.js creates some attribute -1 586 if (!source.hasAttribute(name) && name.substr(0, 7) !== '__IE8__') { -1 587 target.removeAttribute(name); 1124 588 }1125 -1 } else {1126 -1 var render = parseVariableTemplate(tag);1127 -1 var after = parseTemplate(afterTag, loopName);1128 -1 return concat([beforeTag, render, after]);1129 -1 }1130 -1 }1131 -1 };-1 589 }); -1 590 _.forEach(sourceAttrNames, function(name) { -1 591 if (target.getAttribute(name) !== source.getAttribute(name)) { -1 592 target.setAttribute(name, source.getAttribute(name)); -1 593 } -1 594 }); -1 595 }; 1132 5961133 -1 return function(template, data) {1134 -1 return parseTemplate(template)(data);1135 -1 };1136 -1 });1137 -1 /**1138 -1 * Recreate children of `source` in `target` by making only small adjustments.1139 -1 *1140 -1 * *The following section explains details about the current implementation.1141 -1 * These are likely to change in the future.*1142 -1 *1143 -1 * The algorithms is relatively simple. It just iterates through all top level1144 -1 * nodes. If a node has a different `nodeType` (e.g. text or element) or a1145 -1 * different `nodeName` (e.g. div or ul) it is replaced completely and the1146 -1 * algorithm proceeds with the node's children recursively. Otherwise, only1147 -1 * the nodes's attributes are updated.1148 -1 *1149 -1 * Note that non-attribute properties (e.g. value) are lost in the first case1150 -1 * and preserved in the second.1151 -1 *1152 -1 * If the algorithm encounters an element with the class `muu-isolate` it does1153 -1 * not recurse into its children. This way, you can protect dynamically1154 -1 * generated content from being overwritten.1155 -1 *1156 -1 * @module muu-update-dom1157 -1 * @param {Element} target1158 -1 * @param {Element} source1159 -1 */1160 -1 _define('muu-update-dom', ['muu-js-helpers'], function(_) {1161 -1 "use strict";1162 -11163 -1 var updateAttributes = function(target, source) {1164 -1 var targetAttrNames = _.map(target.attributes, function(item) {1165 -1 return item.name;1166 -1 });1167 -1 var sourceAttrNames = _.map(source.attributes, function(item) {1168 -1 return item.name;1169 -1 });1170 -11171 -1 _.forEach(targetAttrNames, function(name) {1172 -1 // NOTE: ie8.js creates some attribute1173 -1 if (!source.hasAttribute(name) && name.substr(0, 7) !== '__IE8__') {1174 -1 target.removeAttribute(name);-1 597 var updateDOM = function(target, source) { -1 598 var nt = target.childNodes.length; -1 599 var ns = source.childNodes.length; -1 600 -1 601 for (var i = ns; i < nt; i++) { -1 602 target.removeChild(target.childNodes[ns]); 1175 603 }1176 -1 });1177 -1 _.forEach(sourceAttrNames, function(name) {1178 -1 if (target.getAttribute(name) !== source.getAttribute(name)) {1179 -1 target.setAttribute(name, source.getAttribute(name));-1 604 for (i = nt; i < ns; i++) { -1 605 target.appendChild(source.childNodes[nt]); 1180 606 }1181 -1 });1182 -1 };1183 -11184 -1 var updateDOM = function(target, source) {1185 -1 var nt = target.childNodes.length;1186 -1 var ns = source.childNodes.length;1187 -11188 -1 for (var i = ns; i < nt; i++) {1189 -1 target.removeChild(target.childNodes[ns]);1190 -1 }1191 -1 for (i = nt; i < ns; i++) {1192 -1 target.appendChild(source.childNodes[nt]);1193 -1 }1194 -1 for (i = 0; i < nt && i < ns; i++) {1195 -1 var tchild = target.childNodes[i];1196 -1 var schild = source.childNodes[i];1197 -11198 -1 if (tchild.nodeType === schild.nodeType && tchild.nodeName === schild.nodeName && tchild.type === schild.type) {1199 -1 if (tchild.nodeType === 1) {1200 -1 var muuClasses = _.filter(tchild.classList, function(cls) {1201 -1 return cls.lastIndexOf('muu-', 0) === 0;1202 -1 });1203 -1 updateAttributes(tchild, schild);1204 -1 _.forEach(muuClasses, function(cls) {1205 -1 tchild.classList.add(cls);1206 -1 });1207 -1 } else if (tchild.nodeType === 3) {1208 -1 tchild.nodeValue = schild.nodeValue;1209 -1 }1210 -1 if (tchild.nodeType !== 3 && !tchild.classList.contains('muu-isolate')) {1211 -1 updateDOM(tchild, schild);-1 607 for (i = 0; i < nt && i < ns; i++) { -1 608 var tchild = target.childNodes[i]; -1 609 var schild = source.childNodes[i]; -1 610 -1 611 if (tchild.nodeType === schild.nodeType && tchild.nodeName === schild.nodeName && tchild.type === schild.type) { -1 612 if (tchild.nodeType === 1) { -1 613 var muuClasses = _.filter(tchild.classList, function(cls) { -1 614 return cls.lastIndexOf('muu-', 0) === 0; -1 615 }); -1 616 updateAttributes(tchild, schild); -1 617 _.forEach(muuClasses, function(cls) { -1 618 tchild.classList.add(cls); -1 619 }); -1 620 } else if (tchild.nodeType === 3) { -1 621 tchild.nodeValue = schild.nodeValue; -1 622 } -1 623 if (tchild.nodeType !== 3 && !tchild.classList.contains('muu-isolate')) { -1 624 updateDOM(tchild, schild); -1 625 } -1 626 } else { -1 627 tchild.parentNode.replaceChild(schild, tchild); 1212 628 }1213 -1 } else {1214 -1 tchild.parentNode.replaceChild(schild, tchild);1215 629 }1216 -1 }1217 -1 };-1 630 }; 1218 6311219 -1 return updateDOM;1220 -1 });1221 -1 /**1222 -1 * This module gives access to the following objects:1223 -1 *1224 -1 * - `Registry` - {@link Registry}1225 -1 * - `$` - {@link module:muu-dom-helpers}1226 -1 * - `$location` - {@link module:muu-location}1227 -1 *1228 -1 * @module muu1229 -1 */1230 -1 _define('muu', ['muu-registry', 'muu-dom-helpers', 'muu-location'], function(Registry, $, $location) {1231 -1 "use strict";1232 -11233 -1 var module = {};1234 -11235 -1 module.Registry = Registry;1236 -1 module.$ = $;1237 -1 module.$location = $location;1238 -11239 -1 return module;1240 -1 });-1 632 return updateDOM; -1 633 }); 1241 6341242 -1 return _require('muu');-1 635 return _require(name); 1243 636 }); 1244 637 })(window, document, void 0);
diff --git a/dist/muu-core.min.js b/dist/muu-core.min.js
@@ -0,0 +1,10 @@
-1 1 (function(q,n,p){(function(l){"function"===typeof define&&define.amd?define("muu",["lodash"],l):q.muu=l(q._)})(function(l){var m={};m["muu-js-helpers"]={instance:l};var t=function(e,k){for(var g=[],a=0;a<e.length;a++)g.push(k(e[a]));return g};l=function(e,k,g){m[e]={deps:k,factory:g}};var r=function(e){if(!m[e])return p;m[e].instance||(m[e].instance=m[e].factory.apply(p,t(m[e].deps,r)));return m[e].instance};l("muu-directive",["muu-dom-helpers","muu-js-helpers","muu-update-dom"],function(e,k,g){return function(a,
-1 2 b,d){var h=this;a.innerHTML="";var c=function(f){var c="data-on"+f.type;f.target.hasAttribute(c)&&(c=f.target.getAttribute(c),f=e.createEvent("muu-"+c,f),a.dispatchEvent(f))};this.update=function(f){var s=n.createElement("div");s.innerHTML=d.renderer(b,f);g(a,s);k.forEach(["keydown","keyup","click","change","search"],function(a){k.forEach(h.querySelectorAll("[data-on"+a+"]"),function(f){f.addEventListener(a,c,!1)})});var u=e.createEvent("muu-parent-update");f=this.querySelectorAll("muu.muu-initialised");
-1 3 k.forEach(f,function(a){a.dispatchEvent(u)});d.linkAll(h)};this.querySelectorAll=function(f){var c=a.querySelectorAll(f),d=a.querySelectorAll(".muu-isolate"),d=k.union(k.map(d,function(a){return a.querySelectorAll(f)}));return k.difference(c,d)};this.querySelector=function(a){a=h.querySelectorAll(a);if(0<a.length)return a[0]};this.getModel=function(a,c){if(a===p){var d={};k.forEach(h.querySelectorAll("[name]"),function(a){d[a.name]=h.getModel(a.name)});return d}var b=h.querySelector("[name="+a+"]");
-1 4 return b===p?c:"checkbox"===b.type?b.checked:"radio"===b.type?(b=h.querySelectorAll("[name="+a+"]"),e.getRadio(b)||c):b.value};this.setModel=function(a,c){var b=h.querySelector("[name="+a+"]");"checkbox"===b.type?b.checked=c:"radio"===b.type?(b=h.querySelectorAll("[name="+a+"]"),e.setRadio(b,c)):b.value=c}}});l("muu-dom-helpers",["muu-js-helpers"],function(e){var k={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"},g={DELAY:1E3,escapeHtml:function(a){return String(a).replace(/[&<>"'\/]/g,
-1 5 function(a){return k[a]})},createEvent:function(a,b){if("function"===typeof CustomEvent)return new CustomEvent(a,{detail:b});var d=n.createEvent("CustomEvent");d.initCustomEvent(a,!1,!0,b);return d},on:function(a,b,d){a.addEventListener(b,d,!1);return function(){a.removeEventListener(b,d,!1)}},ready:function(a){a=e.once(a);if("complete"===n.readyState)return a(),function(){};var b=g.on(n,"DOMContentLoaded",a),d=g.on(q,"load",a);return function(){b();d()}},isDescendant:function(a,b){return!!a&&(a===
-1 6 b||g.isDescendant(a.parentNode,b))},destroy:function(a,b){var d;if(q.MutationObserver){var h=new MutationObserver(function(){g.isDescendant(a,n)||(b(),d())});h.observe(n,{childList:!0,subtree:!0});d=e.once(function(){h.disconnect();h=p})}else{var c=setInterval(function(){g.isDescendant(a,n)||(b(),d())},g.DELAY);d=function(){clearInterval(c)}}return d},getRadio:function(a){for(var b=0;b<a.length;b++)if(a[b].checked)return a[b].value},setRadio:function(a,b){for(var d=0;d<a.length;d++)a[d].checked=a[d].value===
-1 7 b?!0:!1}};return g});l("muu",["muu-registry","muu-dom-helpers","muu-location"],function(e,k,g){var a={};a.Registry=e;a.$=k;a.$location=g;return a});l("muu-registry",["muu-template","muu-directive","muu-js-helpers","muu-dom-helpers"],function(e,k,g,a){return function(b){var d=this,h={};this.config=b||{};this.renderer=d.config.renderer||e;this.registerDirective=function(a,b,e){h[a]={template:b,link:e};return d};this.registerModule=function(a){a(d);return d};this.link=function(c,b){b===p&&(b=c.getAttribute("type"));
-1 8 if(!h.hasOwnProperty(b))throw Error("Unknown directive type: "+b);var e=h[b].link,g=new k(c,h[b].template,d),e=e(g,c);c.classList.add("muu-isolate");c.classList.add("muu-initialised");d.config.debug&&(c.directive=g);e!==p&&a.destroy(c,e);return g};this.linkAll=function(a){a=g.filter(a.querySelectorAll("muu"),function(a){return!a.classList.contains("muu-initialised")});return g.map(a,function(a){return d.link(a)})}}});l("muu-update-dom",["muu-js-helpers"],function(e){var k=function(a,b){var d=e.map(a.attributes,
-1 9 function(a){return a.name}),g=e.map(b.attributes,function(a){return a.name});e.forEach(d,function(c){b.hasAttribute(c)||"__IE8__"===c.substr(0,7)||a.removeAttribute(c)});e.forEach(g,function(c){a.getAttribute(c)!==b.getAttribute(c)&&a.setAttribute(c,b.getAttribute(c))})},g=function(a,b){for(var d=a.childNodes.length,h=b.childNodes.length,c=h;c<d;c++)a.removeChild(a.childNodes[h]);for(c=d;c<h;c++)a.appendChild(b.childNodes[d]);for(c=0;c<d&&c<h;c++){var f=a.childNodes[c],l=b.childNodes[c];if(f.nodeType===
-1 10 l.nodeType&&f.nodeName===l.nodeName&&f.type===l.type){if(1===f.nodeType){var m=e.filter(f.classList,function(a){return 0===a.lastIndexOf("muu-",0)});k(f,l);e.forEach(m,function(a){f.classList.add(a)})}else 3===f.nodeType&&(f.nodeValue=l.nodeValue);3===f.nodeType||f.classList.contains("muu-isolate")||g(f,l)}else f.parentNode.replaceChild(l,f)}};return g});return r("muu")})})(window,document,void 0);
diff --git a/dist/muu.js b/dist/muu.js
@@ -1,13 +1,19 @@ 1 1 (function(window, document, undefined) { -1 2 var name = 'muu'; -1 3 2 4 (function(factory) { 3 5 if (typeof define === 'function' && define.amd) {4 -1 define('muu', [], factory);-1 6 define(name, [], factory); 5 7 } else {6 -1 window['muu'] = factory();-1 8 window[name] = factory(window._); 7 9 }8 -1 })(function() {-1 10 })(function(lodash) { 9 11 var modules = {}; 10 12 -1 13 modules['muu-js-helpers'] = { -1 14 instance: lodash -1 15 }; -1 16 11 17 var map = function(a, fn) { 12 18 var b = []; 13 19 for (var i = 0; i < a.length; i++) { @@ -38,1207 +44,1207 @@ 38 44 return modules[name].instance; 39 45 }; 40 4641 -1 /**42 -1 * Exports the {@link Directive} class.43 -1 * @module muu-directive44 -1 * @ignore45 -1 */46 -1 _define('muu-directive', ['muu-dom-helpers', 'muu-js-helpers', 'muu-update-dom'], function($, _, updateDOM) {47 -1 "use strict";48 -149 47 /**50 -1 * A directive is linked to a Element and manages the DOM tree below51 -1 * that element (excluding any isolated subtrees, e.g. those managed by52 -1 * subdirectives).53 -1 *54 -1 * It provides a set of methods to interact with the managed part of the55 -1 * DOM. This is separated into three distinct parts:56 -1 *57 -1 * - You can push data to the DOM using the {@link Directive#update}58 -1 * method. The DOM will than be updated using the template that was59 -1 * provided at construction.60 -1 * - You can get data from the DOM using the {@link Directive#getModel}61 -1 * method. This is however restricted to form field by design.62 -1 * - You can react to DOM events by specifying an alias for them. In the63 -1 * template, you might for example add the attribute64 -1 * `data-onclick="custom"` to an element. When there is `click` event on65 -1 * that element, a `muu-custom` event will be triggered on the66 -1 * directive's root element.67 -1 *68 -1 * Directives are typically not created directly but via {@link69 -1 * Registry#link}.70 -1 *71 -1 * @constructs Directive72 -1 * @param {Element} root73 -1 * @param {string} template74 -1 * @param {Registry} registry-1 48 * Exports the {@link Directive} class. -1 49 * @module muu-directive -1 50 * @ignore 75 51 */76 -1 var Directive = function(root, template, registry) {77 -1 var self = this;78 -179 -1 root.innerHTML = '';80 -181 -1 var eventCallback = function(originalEvent) {82 -1 var attrName = 'data-on' + originalEvent.type;83 -1 if (originalEvent.target.hasAttribute(attrName)) {84 -1 var eventName = originalEvent.target.getAttribute(attrName);85 -1 var event = $.createEvent('muu-' + eventName, originalEvent);86 -1 root.dispatchEvent(event);87 -1 }88 -1 };-1 52 _define('muu-directive', ['muu-dom-helpers', 'muu-js-helpers', 'muu-update-dom'], function($, _, updateDOM) { -1 53 "use strict"; 89 54 90 55 /**91 -1 * Rerender `template` with `data` and push the changes to the DOM.-1 56 * A directive is linked to a Element and manages the DOM tree below -1 57 * that element (excluding any isolated subtrees, e.g. those managed by -1 58 * subdirectives). -1 59 * -1 60 * It provides a set of methods to interact with the managed part of the -1 61 * DOM. This is separated into three distinct parts: -1 62 * -1 63 * - You can push data to the DOM using the {@link Directive#update} -1 64 * method. The DOM will than be updated using the template that was -1 65 * provided at construction. -1 66 * - You can get data from the DOM using the {@link Directive#getModel} -1 67 * method. This is however restricted to form field by design. -1 68 * - You can react to DOM events by specifying an alias for them. In the -1 69 * template, you might for example add the attribute -1 70 * `data-onclick="custom"` to an element. When there is `click` event on -1 71 * that element, a `muu-custom` event will be triggered on the -1 72 * directive's root element. -1 73 * -1 74 * Directives are typically not created directly but via {@link -1 75 * Registry#link}. 92 76 *93 -1 * @param {Object.<string, *>} data94 -1 * @see {@link module:muu-update-dom} for details.95 -1 * @see The templating system can be defined in the {@link Registry}.-1 77 * @constructs Directive -1 78 * @param {Element} root -1 79 * @param {string} template -1 80 * @param {Registry} registry 96 81 */97 -1 this.update = function(data) {98 -1 var tmp = document.createElement('div');99 -1 tmp.innerHTML = registry.renderer(template, data);-1 82 var Directive = function(root, template, registry) { -1 83 var self = this; 100 84101 -1 updateDOM(root, tmp);-1 85 root.innerHTML = ''; -1 86 -1 87 var eventCallback = function(originalEvent) { -1 88 var attrName = 'data-on' + originalEvent.type; -1 89 if (originalEvent.target.hasAttribute(attrName)) { -1 90 var eventName = originalEvent.target.getAttribute(attrName); -1 91 var event = $.createEvent('muu-' + eventName, originalEvent); -1 92 root.dispatchEvent(event); -1 93 } -1 94 }; 102 95103 -1 _.forEach(['keydown', 'keyup', 'click', 'change', 'search'], function(eventType) {104 -1 var selector = '[data-on' + eventType + ']';105 -1 _.forEach(self.querySelectorAll(selector), function(element) {106 -1 element.addEventListener(eventType, eventCallback, false);-1 96 /** -1 97 * Rerender `template` with `data` and push the changes to the DOM. -1 98 * -1 99 * @param {Object.<string, *>} data -1 100 * @see {@link module:muu-update-dom} for details. -1 101 * @see The templating system can be defined in the {@link Registry}. -1 102 */ -1 103 this.update = function(data) { -1 104 var tmp = document.createElement('div'); -1 105 tmp.innerHTML = registry.renderer(template, data); -1 106 -1 107 updateDOM(root, tmp); -1 108 -1 109 _.forEach(['keydown', 'keyup', 'click', 'change', 'search'], function(eventType) { -1 110 var selector = '[data-on' + eventType + ']'; -1 111 _.forEach(self.querySelectorAll(selector), function(element) { -1 112 element.addEventListener(eventType, eventCallback, false); -1 113 }); 107 114 });108 -1 });109 115110 -1 var updateEvent = $.createEvent('muu-parent-update');111 -1 var subDirectives = this.querySelectorAll('muu.muu-initialised');112 -1 _.forEach(subDirectives, function(element) {113 -1 element.dispatchEvent(updateEvent);114 -1 });-1 116 var updateEvent = $.createEvent('muu-parent-update'); -1 117 var subDirectives = this.querySelectorAll('muu.muu-initialised'); -1 118 _.forEach(subDirectives, function(element) { -1 119 element.dispatchEvent(updateEvent); -1 120 }); -1 121 -1 122 registry.linkAll(self); -1 123 }; -1 124 -1 125 /** -1 126 * A variant of `querySelectorAll` that returns only elements from -1 127 * the managed part of the DOM. -1 128 * -1 129 * @private -1 130 * @param {string} selector -1 131 * @return {Array.<Element>} All child elements that match the given -1 132 * selector and are not isolated. -1 133 */ -1 134 this.querySelectorAll = function(selector) { -1 135 var hits = root.querySelectorAll(selector); -1 136 -1 137 // NOTE: querySelectorAll returns all elements in the tree that -1 138 // match the given selector. findAll does the same with *relative -1 139 // selectors* but does not seem to be available yet. -1 140 var isolations = root.querySelectorAll('.muu-isolate'); -1 141 var isolated = _.union(_.map(isolations, function(isolation) { -1 142 return isolation.querySelectorAll(selector); -1 143 })); -1 144 -1 145 return _.difference(hits, isolated); -1 146 }; -1 147 -1 148 /** -1 149 * A variant of `querySelector` that returns only elements from the -1 150 * managed part of the DOM. -1 151 * -1 152 * @private -1 153 * @param {String} selector -1 154 * @return {Element} First child element that matches the given -1 155 * selector and is not isolated. -1 156 * @suppress {missingReturn} -1 157 */ -1 158 this.querySelector = function(selector) { -1 159 var all = self.querySelectorAll(selector); -1 160 if (all.length > 0) { -1 161 return all[0]; -1 162 } -1 163 }; 115 164116 -1 registry.linkAll(self);-1 165 /** -1 166 * Get all model data as a flat object. -1 167 * -1 168 * @return {Object.<string, string|number|boolean>} -1 169 *//** -1 170 * Get the value of a form input by name. -1 171 * -1 172 * In case of a checkbox, returns `boolean`. -1 173 * In case of radioboxes, returns the value of the selected box. -1 174 * -1 175 * @param {string} name -1 176 * @param {*} [_default] -1 177 * @return {string|number|boolean|*} -1 178 */ -1 179 this.getModel = function(name, _default) { -1 180 if (name === undefined) { -1 181 var model = {}; -1 182 _.forEach(self.querySelectorAll('[name]'), function(element) { -1 183 model[element.name] = self.getModel(element.name); -1 184 }); -1 185 return model; -1 186 } else { -1 187 var element = self.querySelector('[name=' + name + ']'); -1 188 if (element === undefined) { -1 189 return _default; -1 190 } else if (element.type === 'checkbox') { -1 191 return element.checked; -1 192 } else if (element.type === 'radio') { -1 193 var options = self.querySelectorAll('[name=' + name + ']'); -1 194 return $.getRadio(options) || _default; -1 195 } else { -1 196 return element.value; -1 197 } -1 198 } -1 199 }; -1 200 -1 201 /** -1 202 * Set the value of a form input by name. -1 203 * -1 204 * In case of a checkbox, sets `element.checked`. -1 205 * In case of radioboxes, selects the box with matching value. -1 206 * -1 207 * @param {string} name -1 208 * @param {string|number|boolean} value -1 209 */ -1 210 this.setModel = function(name, value) { -1 211 var element = self.querySelector('[name=' + name + ']'); -1 212 if (element.type === 'checkbox') { -1 213 element.checked = value; -1 214 } else if (element.type === 'radio') { -1 215 var options = self.querySelectorAll('[name=' + name + ']'); -1 216 $.setRadio(options, value); -1 217 } else { -1 218 element.value = value; -1 219 } -1 220 }; 117 221 }; 118 222119 -1 /**120 -1 * A variant of `querySelectorAll` that returns only elements from121 -1 * the managed part of the DOM.122 -1 *123 -1 * @private124 -1 * @param {string} selector125 -1 * @return {Array.<Element>} All child elements that match the given126 -1 * selector and are not isolated.127 -1 */128 -1 this.querySelectorAll = function(selector) {129 -1 var hits = root.querySelectorAll(selector);130 -1131 -1 // NOTE: querySelectorAll returns all elements in the tree that132 -1 // match the given selector. findAll does the same with *relative133 -1 // selectors* but does not seem to be available yet.134 -1 var isolations = root.querySelectorAll('.muu-isolate');135 -1 var isolated = _.union(_.map(isolations, function(isolation) {136 -1 return isolation.querySelectorAll(selector);137 -1 }));138 -1139 -1 return _.difference(hits, isolated);-1 223 return Directive; -1 224 }); -1 225 /** -1 226 * DOM related helper functions -1 227 * @module muu-dom-helpers -1 228 */ -1 229 _define("muu-dom-helpers", ['muu-js-helpers'], function(_) { -1 230 "use strict"; -1 231 -1 232 var entityMap = { -1 233 '&': '&', -1 234 '<': '<', -1 235 '>': '>', -1 236 '"': '"', -1 237 "'": ''', -1 238 '/': '/' 140 239 }; 141 240 -1 241 /** @lends module:muu-dom-helpers */ -1 242 var $ = {}; -1 243 -1 244 $.DELAY = 1000; -1 245 142 246 /**143 -1 * A variant of `querySelector` that returns only elements from the144 -1 * managed part of the DOM.145 -1 *146 -1 * @private147 -1 * @param {String} selector148 -1 * @return {Element} First child element that matches the given149 -1 * selector and is not isolated.150 -1 * @suppress {missingReturn}-1 247 * @param {string} string -1 248 * @return {string} - escaped HTML 151 249 */152 -1 this.querySelector = function(selector) {153 -1 var all = self.querySelectorAll(selector);154 -1 if (all.length > 0) {155 -1 return all[0];156 -1 }-1 250 $.escapeHtml = function(string) { -1 251 return String(string).replace(/[&<>"'\/]/g, function(s) { -1 252 return entityMap[s]; -1 253 }); 157 254 }; 158 255 159 256 /**160 -1 * Get all model data as a flat object.161 -1 *162 -1 * @return {Object.<string, string|number|boolean>}163 -1 *//**164 -1 * Get the value of a form input by name.-1 257 * Cross browser custom events. 165 258 *166 -1 * In case of a checkbox, returns `boolean`.167 -1 * In case of radioboxes, returns the value of the selected box.-1 259 * *Note*: IE does not seem to like it when you use existing event names -1 260 * with this. 168 261 *169 -1 * @param {string} name170 -1 * @param {*} [_default]171 -1 * @return {string|number|boolean|*}-1 262 * @param {string} type -1 263 * @param {*} detail -1 264 * @return {Event} -1 265 * @see https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events 172 266 */173 -1 this.getModel = function(name, _default) {174 -1 if (name === undefined) {175 -1 var model = {};176 -1 _.forEach(self.querySelectorAll('[name]'), function(element) {177 -1 model[element.name] = self.getModel(element.name);-1 267 $.createEvent = function(type, detail) { -1 268 if (typeof CustomEvent === 'function') { -1 269 return new CustomEvent(type, { -1 270 detail: detail 178 271 });179 -1 return model;180 272 } else {181 -1 var element = self.querySelector('[name=' + name + ']');182 -1 if (element === undefined) {183 -1 return _default;184 -1 } else if (element.type === 'checkbox') {185 -1 return element.checked;186 -1 } else if (element.type === 'radio') {187 -1 var options = self.querySelectorAll('[name=' + name + ']');188 -1 return $.getRadio(options) || _default;189 -1 } else {190 -1 return element.value;191 -1 }-1 273 var event = document.createEvent('CustomEvent'); -1 274 event.initCustomEvent(type, false, true, detail); -1 275 return event; 192 276 } 193 277 }; 194 278 195 279 /**196 -1 * Set the value of a form input by name.197 -1 *198 -1 * In case of a checkbox, sets `element.checked`.199 -1 * In case of radioboxes, selects the box with matching value.200 -1 *201 -1 * @param {string} name202 -1 * @param {string|number|boolean} value-1 280 * @param {EventTarget} element -1 281 * @param {string} eventName -1 282 * @param {Function} callback -1 283 * @return {function()} An unregister function -1 284 */ -1 285 $.on = function(element, eventName, callback) { -1 286 element.addEventListener(eventName, callback, false); -1 287 return function() { -1 288 element.removeEventListener(eventName, callback, false); -1 289 }; -1 290 }; -1 291 -1 292 /** -1 293 * @param {Function} fn -1 294 * @return {function()} An unregister function 203 295 */204 -1 this.setModel = function(name, value) {205 -1 var element = self.querySelector('[name=' + name + ']');206 -1 if (element.type === 'checkbox') {207 -1 element.checked = value;208 -1 } else if (element.type === 'radio') {209 -1 var options = self.querySelectorAll('[name=' + name + ']');210 -1 $.setRadio(options, value);-1 296 $.ready = function(fn) { -1 297 var _fn = _.once(fn); -1 298 if (document.readyState === 'complete') { -1 299 _fn(); -1 300 return function() {}; 211 301 } else {212 -1 element.value = value;-1 302 var u1 = $.on(document, 'DOMContentLoaded', _fn); -1 303 var u2 = $.on(window, 'load', _fn); -1 304 return function() { -1 305 u1(); -1 306 u2(); -1 307 }; 213 308 } 214 309 };215 -1 };216 310217 -1 return Directive;218 -1 });219 -1 /**220 -1 * DOM related helper functions221 -1 * @module muu-dom-helpers222 -1 */223 -1 _define("muu-dom-helpers", ['muu-js-helpers'], function(_) {224 -1 "use strict";225 -1226 -1 var entityMap = {227 -1 '&': '&',228 -1 '<': '<',229 -1 '>': '>',230 -1 '"': '"',231 -1 "'": ''',232 -1 '/': '/'233 -1 };-1 311 /** -1 312 * @param {Node} desc -1 313 * @param {Node} root -1 314 * @return {boolean} -1 315 */ -1 316 $.isDescendant = function(desc, root) { -1 317 return !!desc && (desc === root || $.isDescendant(desc.parentNode, root)); -1 318 }; 234 319235 -1 /** @lends module:muu-dom-helpers */236 -1 var $ = {};-1 320 /** -1 321 * Execute a function when `element` is removed from the DOM. -1 322 * -1 323 * *Note*: The callback is not executed directly when (or even before) the -1 324 * element is removed but with a slight delay. So the only way to test this -1 325 * is to use a timeout in the test. -1 326 * -1 327 * @param {Element} element -1 328 * @param {Function} fn -1 329 * @return {function()} An unregister function -1 330 */ -1 331 $.destroy = function(element, fn) { -1 332 var unregister; -1 333 -1 334 if (!!window.MutationObserver) { -1 335 var observer = new MutationObserver(function() { -1 336 if (!$.isDescendant(element, document)) { -1 337 fn(); -1 338 unregister(); -1 339 } -1 340 }); 237 341238 -1 $.DELAY = 1000;-1 342 observer.observe(document, { -1 343 childList: true, -1 344 subtree: true -1 345 }); 239 346240 -1 /**241 -1 * @param {string} string242 -1 * @return {string} - escaped HTML243 -1 */244 -1 $.escapeHtml = function(string) {245 -1 return String(string).replace(/[&<>"'\/]/g, function(s) {246 -1 return entityMap[s];247 -1 });248 -1 };-1 347 unregister = _.once(function() { -1 348 observer.disconnect(); -1 349 observer = undefined; -1 350 }); -1 351 } else { -1 352 var intervalID = setInterval(function() { -1 353 if (!$.isDescendant(element, document)) { -1 354 fn(); -1 355 unregister(); -1 356 } -1 357 }, $.DELAY); 249 358250 -1 /**251 -1 * Cross browser custom events.252 -1 *253 -1 * *Note*: IE does not seem to like it when you use existing event names254 -1 * with this.255 -1 *256 -1 * @param {string} type257 -1 * @param {*} detail258 -1 * @return {Event}259 -1 * @see https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events260 -1 */261 -1 $.createEvent = function(type, detail) {262 -1 if (typeof CustomEvent === 'function') {263 -1 return new CustomEvent(type, {264 -1 detail: detail265 -1 });266 -1 } else {267 -1 var event = document.createEvent('CustomEvent');268 -1 event.initCustomEvent(type, false, true, detail);269 -1 return event;270 -1 }271 -1 };-1 359 unregister = function() { -1 360 clearInterval(intervalID); -1 361 }; -1 362 } 272 363273 -1 /**274 -1 * @param {EventTarget} element275 -1 * @param {string} eventName276 -1 * @param {Function} callback277 -1 * @return {function()} An unregister function278 -1 */279 -1 $.on = function(element, eventName, callback) {280 -1 element.addEventListener(eventName, callback, false);281 -1 return function() {282 -1 element.removeEventListener(eventName, callback, false);-1 364 return unregister; 283 365 };284 -1 };285 -1286 -1 /**287 -1 * @param {Function} fn288 -1 * @return {function()} An unregister function289 -1 */290 -1 $.ready = function(fn) {291 -1 var _fn = _.once(fn);292 -1 if (document.readyState === 'complete') {293 -1 _fn();294 -1 return function() {};295 -1 } else {296 -1 var u1 = $.on(document, 'DOMContentLoaded', _fn);297 -1 var u2 = $.on(window, 'load', _fn);298 -1 return function() {299 -1 u1();300 -1 u2();301 -1 };302 -1 }303 -1 };304 366305 -1 /**306 -1 * @param {Node} desc307 -1 * @param {Node} root308 -1 * @return {boolean}309 -1 */310 -1 $.isDescendant = function(desc, root) {311 -1 return !!desc && (desc === root || $.isDescendant(desc.parentNode, root));312 -1 };313 -1314 -1 /**315 -1 * Execute a function when `element` is removed from the DOM.316 -1 *317 -1 * *Note*: The callback is not executed directly when (or even before) the318 -1 * element is removed but with a slight delay. So the only way to test this319 -1 * is to use a timeout in the test.320 -1 *321 -1 * @param {Element} element322 -1 * @param {Function} fn323 -1 * @return {function()} An unregister function324 -1 */325 -1 $.destroy = function(element, fn) {326 -1 var unregister;327 -1328 -1 if (!!window.MutationObserver) {329 -1 var observer = new MutationObserver(function() {330 -1 if (!$.isDescendant(element, document)) {331 -1 fn();332 -1 unregister();-1 367 /** -1 368 * @param {Array.<Element>} options -1 369 * @return {string} -1 370 * @suppress {missingReturn} -1 371 */ -1 372 $.getRadio = function(options) { -1 373 for (var i = 0; i < options.length; i++) { -1 374 if (options[i].checked) { -1 375 return options[i].value; 333 376 }334 -1 });335 -1336 -1 observer.observe(document, {337 -1 childList: true,338 -1 subtree: true339 -1 });-1 377 } -1 378 }; 340 379341 -1 unregister = _.once(function() {342 -1 observer.disconnect();343 -1 observer = undefined;344 -1 });345 -1 } else {346 -1 var intervalID = setInterval(function() {347 -1 if (!$.isDescendant(element, document)) {348 -1 fn();349 -1 unregister();-1 380 /** -1 381 * @param {Array.<Element>} options -1 382 * @param {string} value -1 383 */ -1 384 $.setRadio = function(options, value) { -1 385 for (var i = 0; i < options.length; i++) { -1 386 if (options[i].value === value) { -1 387 options[i].checked = true; -1 388 } else { -1 389 options[i].checked = false; 350 390 }351 -1 }, $.DELAY);352 -1353 -1 unregister = function() {354 -1 clearInterval(intervalID);355 -1 };356 -1 }357 -1358 -1 return unregister;359 -1 };360 -1361 -1 /**362 -1 * @param {Array.<Element>} options363 -1 * @return {string}364 -1 * @suppress {missingReturn}365 -1 */366 -1 $.getRadio = function(options) {367 -1 for (var i = 0; i < options.length; i++) {368 -1 if (options[i].checked) {369 -1 return options[i].value;370 391 }371 -1 }372 -1 };-1 392 }; 373 393 -1 394 return $; -1 395 }); 374 396 /**375 -1 * @param {Array.<Element>} options376 -1 * @param {string} value-1 397 * Minimal implementation of an underscore/lodash subset. -1 398 * @module muu-js-helpers 377 399 */378 -1 $.setRadio = function(options, value) {379 -1 for (var i = 0; i < options.length; i++) {380 -1 if (options[i].value === value) {381 -1 options[i].checked = true;382 -1 } else {383 -1 options[i].checked = false;384 -1 }385 -1 }386 -1 };-1 400 _define('muu-js-helpers', [], function() { -1 401 "use strict"; 387 402388 -1 return $;389 -1 });390 -1 /**391 -1 * Minimal implementation of an underscore/lodash subset.392 -1 * @module muu-js-helpers393 -1 */394 -1 _define('muu-js-helpers', [], function() {395 -1 "use strict";-1 403 /** @lends module:muu-js-helpers */ -1 404 var _ = {}; 396 405397 -1 /** @lends module:muu-js-helpers */398 -1 var _ = {};-1 406 /** -1 407 * @param {Object} value -1 408 * @return {string} -1 409 */ -1 410 var objToString = function(value) { -1 411 return Object.prototype.toString.call(value); -1 412 }; 399 413400 -1 /**401 -1 * @param {Object} value402 -1 * @return {string}403 -1 */404 -1 var objToString = function(value) {405 -1 return Object.prototype.toString.call(value);406 -1 };-1 414 /** -1 415 * @param {*} value -1 416 * @return {boolean} -1 417 */ -1 418 _.isString = function(value) { -1 419 return typeof value === 'string' || objToString(value) === '[object String]'; -1 420 }; 407 421408 -1 /**409 -1 * @param {*} value410 -1 * @return {boolean}411 -1 */412 -1 _.isString = function(value) {413 -1 return typeof value === 'string' || objToString(value) === '[object String]';414 -1 };-1 422 /** -1 423 * @function -1 424 * @param {*} value -1 425 * @return {boolean} -1 426 */ -1 427 _.isArray = Array.isArray; 415 428416 -1 /**417 -1 * @function418 -1 * @param {*} value419 -1 * @return {boolean}420 -1 */421 -1 _.isArray = Array.isArray;-1 429 /** -1 430 * @param {*} value -1 431 * @return {boolean} -1 432 */ -1 433 _.isFunction = function(value) { -1 434 return typeof value === 'function'; -1 435 }; 422 436423 -1 /**424 -1 * @param {*} value425 -1 * @return {boolean}426 -1 */427 -1 _.isFunction = function(value) {428 -1 return typeof value === 'function';429 -1 };-1 437 /** -1 438 * @param {Function} fn -1 439 * @return {Function} -1 440 */ -1 441 _.once = function(fn) { -1 442 var result; -1 443 var called = false; 430 444431 -1 /**432 -1 * @param {Function} fn433 -1 * @return {Function}434 -1 */435 -1 _.once = function(fn) {436 -1 var result;437 -1 var called = false;438 -1439 -1 return function() {440 -1 if (!called) {441 -1 result = fn.apply(this, arguments);442 -1 called = true;443 -1 }444 -1 return result;-1 445 return function() { -1 446 if (!called) { -1 447 result = fn.apply(this, arguments); -1 448 called = true; -1 449 } -1 450 return result; -1 451 }; 445 452 };446 -1 };447 -1448 -1 /**449 -1 * @param {Array} array450 -1 * @param {*} value451 -1 * @return {number}452 -1 */453 -1 _.indexOf = function(array, value) {454 -1 if ('indexOf' in array) {455 -1 return array.indexOf(value);456 -1 }457 453458 -1 for (var i = 0; i < array.length; i++) {459 -1 if (array[i] === value) {460 -1 return i;-1 454 /** -1 455 * @param {Array} array -1 456 * @param {*} value -1 457 * @return {number} -1 458 */ -1 459 _.indexOf = function(array, value) { -1 460 if ('indexOf' in array) { -1 461 return array.indexOf(value); 461 462 }462 -1 }463 -1 return -1;464 -1 };465 463466 -1 /**467 -1 * @param {Array} array468 -1 * @param {Function} fn469 -1 */470 -1 _.forEach = function(array, fn) {471 -1 if ('forEach' in array) {472 -1 return array.forEach(fn);473 -1 }-1 464 for (var i = 0; i < array.length; i++) { -1 465 if (array[i] === value) { -1 466 return i; -1 467 } -1 468 } -1 469 return -1; -1 470 }; 474 471475 -1 for (var i = 0; i < array.length; i++) {476 -1 fn(array[i]);477 -1 }478 -1 };-1 472 /** -1 473 * @param {Array} array -1 474 * @param {Function} fn -1 475 */ -1 476 _.forEach = function(array, fn) { -1 477 if ('forEach' in array) { -1 478 return array.forEach(fn); -1 479 } 479 480480 -1 /**481 -1 * @param {Array} array482 -1 * @param {Function} fn483 -1 * @return {Array}484 -1 */485 -1 _.map = function(array, fn) {486 -1 if ('map' in array) {487 -1 return array.map(fn);488 -1 }-1 481 for (var i = 0; i < array.length; i++) { -1 482 fn(array[i]); -1 483 } -1 484 }; 489 485490 -1 var results = [];491 -1 for (var i = 0; i < array.length; i++) {492 -1 results.push(fn(array[i]));493 -1 }494 -1 return results;495 -1 };-1 486 /** -1 487 * @param {Array} array -1 488 * @param {Function} fn -1 489 * @return {Array} -1 490 */ -1 491 _.map = function(array, fn) { -1 492 if ('map' in array) { -1 493 return array.map(fn); -1 494 } 496 495497 -1 /**498 -1 * @param {Array} array499 -1 * @param {Function} fn500 -1 * @return {Array}501 -1 */502 -1 _.filter = function(array, fn) {503 -1 if ('filter' in array) {504 -1 return array.filter(fn);505 -1 }-1 496 var results = []; -1 497 for (var i = 0; i < array.length; i++) { -1 498 results.push(fn(array[i])); -1 499 } -1 500 return results; -1 501 }; 506 502507 -1 var results = [];508 -1 for (var i = 0; i < array.length; i++) {509 -1 if (fn(array[i])) {510 -1 results.push(array[i]);-1 503 /** -1 504 * @param {Array} array -1 505 * @param {Function} fn -1 506 * @return {Array} -1 507 */ -1 508 _.filter = function(array, fn) { -1 509 if ('filter' in array) { -1 510 return array.filter(fn); 511 511 }512 -1 }513 -1 return results;514 -1 };515 512516 -1 /**517 -1 * @param {Array.<Array>} arrays518 -1 * @return {Array}519 -1 */520 -1 _.union = function(arrays) {521 -1 var results = [];522 -1 for (var i = 0; i < arrays.length; i++) {523 -1 for (var j = 0; j < arrays[i].length; j++) {524 -1 if (_.indexOf(results, arrays[i][j]) === -1) {525 -1 results.push(arrays[i][j]);-1 513 var results = []; -1 514 for (var i = 0; i < array.length; i++) { -1 515 if (fn(array[i])) { -1 516 results.push(array[i]); 526 517 } 527 518 }528 -1 }529 -1 return results;530 -1 };-1 519 return results; -1 520 }; 531 521532 -1 /**533 -1 * @param {Array} a534 -1 * @param {Array} b535 -1 * @return {Array}536 -1 */537 -1 _.difference = function(a, b) {538 -1 var results = [];539 -1 for (var i = 0; i < a.length; i++) {540 -1 if (_.indexOf(b, a[i]) === -1) {541 -1 results.push(a[i]);-1 522 /** -1 523 * @param {Array.<Array>} arrays -1 524 * @return {Array} -1 525 */ -1 526 _.union = function(arrays) { -1 527 var results = []; -1 528 for (var i = 0; i < arrays.length; i++) { -1 529 for (var j = 0; j < arrays[i].length; j++) { -1 530 if (_.indexOf(results, arrays[i][j]) === -1) { -1 531 results.push(arrays[i][j]); -1 532 } -1 533 } 542 534 }543 -1 }544 -1 return results;545 -1 };-1 535 return results; -1 536 }; 546 537547 -1 /**548 -1 * @param {Array} a549 -1 * @return {Array}550 -1 */551 -1 _.flatten = function(a) {552 -1 var o = [];553 -1 _.forEach(a, function(item) {554 -1 if (_.isArray(item)) {555 -1 o = o.concat(_.flatten(item));556 -1 } else {557 -1 o.push(item);-1 538 /** -1 539 * @param {Array} a -1 540 * @param {Array} b -1 541 * @return {Array} -1 542 */ -1 543 _.difference = function(a, b) { -1 544 var results = []; -1 545 for (var i = 0; i < a.length; i++) { -1 546 if (_.indexOf(b, a[i]) === -1) { -1 547 results.push(a[i]); -1 548 } 558 549 }559 -1 });560 -1 return o;561 -1 };562 -1563 -1 return _;564 -1 });565 -1 /**566 -1 * angular inspired location service.567 -1 * @module muu-location568 -1 */569 -1 _define('muu-location', ['muu-search'], function(q) {570 -1 "use strict";-1 550 return results; -1 551 }; 571 552572 -1 /** @lends module:muu-location */573 -1 var loc = {};-1 553 /** -1 554 * @param {Array} a -1 555 * @return {Array} -1 556 */ -1 557 _.flatten = function(a) { -1 558 var o = []; -1 559 _.forEach(a, function(item) { -1 560 if (_.isArray(item)) { -1 561 o = o.concat(_.flatten(item)); -1 562 } else { -1 563 o.push(item); -1 564 } -1 565 }); -1 566 return o; -1 567 }; 574 568 -1 569 return _; -1 570 }); 575 571 /**576 -1 * @return {string}-1 572 * angular inspired location service. -1 573 * @module muu-location 577 574 */578 -1 loc.absUrl = function() {579 -1 return location.href;580 -1 };-1 575 _define('muu-location', ['muu-search'], function(q) { -1 576 "use strict"; 581 577582 -1 /**583 -1 * @return {string}584 -1 *//**585 -1 * @param {string} value586 -1 * @param {boolean} [replace]587 -1 * @return {muu-location}588 -1 */589 -1 loc.url = function(value, replace) {590 -1 if (value === undefined) {591 -1 return location.pathname + location.search + location.hash;592 -1 } else if (replace) {593 -1 history.replaceState(null, null, value);594 -1 } else {595 -1 history.pushState(null, null, value);596 -1 }597 -1 return loc;598 -1 };-1 578 /** @lends module:muu-location */ -1 579 var loc = {}; 599 580600 -1 /**601 -1 * @return {string}602 -1 */603 -1 loc.protocol = function() {604 -1 return location.protocol;605 -1 };-1 581 /** -1 582 * @return {string} -1 583 */ -1 584 loc.absUrl = function() { -1 585 return location.href; -1 586 }; 606 587607 -1 /**608 -1 * @return {string}609 -1 */610 -1 loc.host = function() {611 -1 return location.host;612 -1 };-1 588 /** -1 589 * @return {string} -1 590 *//** -1 591 * @param {string} value -1 592 * @param {boolean} [replace] -1 593 * @return {muu-location} -1 594 */ -1 595 loc.url = function(value, replace) { -1 596 if (value === undefined) { -1 597 return location.pathname + location.search + location.hash; -1 598 } else if (replace) { -1 599 history.replaceState(null, null, value); -1 600 } else { -1 601 history.pushState(null, null, value); -1 602 } -1 603 return loc; -1 604 }; 613 605614 -1 /**615 -1 * @return {string}616 -1 */617 -1 loc.port = function() {618 -1 return location.port;619 -1 };-1 606 /** -1 607 * @return {string} -1 608 */ -1 609 loc.protocol = function() { -1 610 return location.protocol; -1 611 }; 620 612621 -1 /**622 -1 * @return {string}623 -1 *//**624 -1 * @param {string} value625 -1 * @param {boolean} [replace]626 -1 * @return {muu-location}627 -1 */628 -1 loc.path = function(value, replace) {629 -1 if (value === undefined) {630 -1 return location.pathname;631 -1 } else {632 -1 var url = value + location.search + location.hash;633 -1 loc.url(url, replace);634 -1 return loc;635 -1 }636 -1 };-1 613 /** -1 614 * @return {string} -1 615 */ -1 616 loc.host = function() { -1 617 return location.host; -1 618 }; 637 619638 -1 var _search = function(value, replace) {639 -1 if (value === undefined) {640 -1 return location.search;641 -1 } else {642 -1 if (value && value[0] !== '?') {643 -1 value = '?' + value;644 -1 }645 -1 if (value.length === 1) {646 -1 value = '';-1 620 /** -1 621 * @return {string} -1 622 */ -1 623 loc.port = function() { -1 624 return location.port; -1 625 }; -1 626 -1 627 /** -1 628 * @return {string} -1 629 *//** -1 630 * @param {string} value -1 631 * @param {boolean} [replace] -1 632 * @return {muu-location} -1 633 */ -1 634 loc.path = function(value, replace) { -1 635 if (value === undefined) { -1 636 return location.pathname; -1 637 } else { -1 638 var url = value + location.search + location.hash; -1 639 loc.url(url, replace); -1 640 return loc; 647 641 } -1 642 }; 648 643649 -1 var url = location.pathname + value + location.hash;650 -1 loc.url(url, replace);651 -1 return loc;652 -1 }653 -1 };-1 644 var _search = function(value, replace) { -1 645 if (value === undefined) { -1 646 return location.search; -1 647 } else { -1 648 if (value && value[0] !== '?') { -1 649 value = '?' + value; -1 650 } -1 651 if (value.length === 1) { -1 652 value = ''; -1 653 } 654 654655 -1 /**656 -1 * @return {Object}657 -1 *//**658 -1 * @param {string|object} value659 -1 * @return {muu-location}660 -1 *//**661 -1 * @param {string} key662 -1 * @param {*} value663 -1 * @param {boolean} [replace]664 -1 * @return {muu-location}665 -1 */666 -1 loc.search = function(key, value, replace) {667 -1 if (key !== undefined) {668 -1 if (value !== undefined) {669 -1 var search = q.parse(_search());670 -1 search[key] = value;671 -1 return _search(q.unparse(search), replace);-1 655 var url = location.pathname + value + location.hash; -1 656 loc.url(url, replace); -1 657 return loc; -1 658 } -1 659 }; -1 660 -1 661 /** -1 662 * @return {Object} -1 663 *//** -1 664 * @param {string|object} value -1 665 * @return {muu-location} -1 666 *//** -1 667 * @param {string} key -1 668 * @param {*} value -1 669 * @param {boolean} [replace] -1 670 * @return {muu-location} -1 671 */ -1 672 loc.search = function(key, value, replace) { -1 673 if (key !== undefined) { -1 674 if (value !== undefined) { -1 675 var search = q.parse(_search()); -1 676 search[key] = value; -1 677 return _search(q.unparse(search), replace); -1 678 } else { -1 679 return _search(q.unparse(key), replace); -1 680 } 672 681 } else {673 -1 return _search(q.unparse(key), replace);-1 682 return q.parse(_search()); 674 683 }675 -1 } else {676 -1 return q.parse(_search());677 -1 }678 -1 };-1 684 }; 679 685680 -1 /**681 -1 * @return {string}682 -1 *//**683 -1 * @param {string} value684 -1 * @param {boolean} [replace]685 -1 * @return {muu-location}686 -1 */687 -1 loc.hash = function(value, replace) {688 -1 if (value === undefined) {689 -1 if (location.hash) {690 -1 return location.hash.slice(1);-1 686 /** -1 687 * @return {string} -1 688 *//** -1 689 * @param {string} value -1 690 * @param {boolean} [replace] -1 691 * @return {muu-location} -1 692 */ -1 693 loc.hash = function(value, replace) { -1 694 if (value === undefined) { -1 695 if (location.hash) { -1 696 return location.hash.slice(1); -1 697 } else { -1 698 return ''; -1 699 } 691 700 } else {692 -1 return '';-1 701 var url = location.pathname + location.search + '#' + value; -1 702 loc.url(url, replace); -1 703 return loc; -1 704 } -1 705 }; -1 706 -1 707 /** -1 708 * @param {string} eventName -1 709 * @param {Function} fn -1 710 * @return {muu-location} -1 711 */ -1 712 loc.addEventListener = function(eventName, fn) { -1 713 if (eventName === 'change') { -1 714 window.addEventListener('popstate', fn, false); 693 715 }694 -1 } else {695 -1 var url = location.pathname + location.search + '#' + value;696 -1 loc.url(url, replace);697 716 return loc;698 -1 }699 -1 };-1 717 }; 700 718701 -1 /**702 -1 * @param {string} eventName703 -1 * @param {Function} fn704 -1 * @return {muu-location}705 -1 */706 -1 loc.addEventListener = function(eventName, fn) {707 -1 if (eventName === 'change') {708 -1 window.addEventListener('popstate', fn, false);709 -1 }710 -1 return loc;711 -1 };-1 719 /** -1 720 * @param {string} eventName -1 721 * @param {Function} fn -1 722 * @return {muu-location} -1 723 */ -1 724 loc.removeEventListener = function(eventName, fn) { -1 725 if (eventName === 'change') { -1 726 window.removeEventListener('popstate', fn, false); -1 727 } -1 728 return loc; -1 729 }; 712 730713 -1 /**714 -1 * @param {string} eventName715 -1 * @param {Function} fn716 -1 * @return {muu-location}717 -1 */718 -1 loc.removeEventListener = function(eventName, fn) {719 -1 if (eventName === 'change') {720 -1 window.removeEventListener('popstate', fn, false);721 -1 }722 731 return loc;723 -1 };724 -1725 -1 return loc;726 -1 });727 -1 /**728 -1 * Exports the {@link Registry} class.729 -1 * @module muu-registry730 -1 * @ignore731 -1 */732 -1 _define('muu-registry', ['muu-template', 'muu-directive', 'muu-js-helpers', 'muu-dom-helpers'], function(muuTemplate, Directive, _, $) {733 -1 "use strict";734 -1-1 732 }); 735 733 /**736 -1 * @constructs Registry737 -1 * @param {Object} config The config object may have following properties:738 -1 *739 -1 * - **debug** - `{boolean}` - Enable debug mode. In debug mode,740 -1 * directive objects are available as properties from the DOM as741 -1 * `element.directive`.742 -1 * - **renderer** - `{function(string, Object)}` - The template renderer743 -1 * to be used. Defaults to {@link module:muu-template}.-1 734 * Exports the {@link Registry} class. -1 735 * @module muu-registry -1 736 * @ignore 744 737 */745 -1 var Registry = function(config) {746 -1 var self = this;747 -1 var directives = {};748 -1749 -1 this.config = config || {};750 -1 this.renderer = self.config.renderer || muuTemplate;-1 738 _define('muu-registry', ['muu-template', 'muu-directive', 'muu-js-helpers', 'muu-dom-helpers'], function(muuTemplate, Directive, _, $) { -1 739 "use strict"; 751 740 752 741 /**753 -1 * Register a new type of {@link Directive}-1 742 * @constructs Registry -1 743 * @param {Object} config The config object may have following properties: 754 744 *755 -1 * @param {string} type756 -1 * @param {string} template757 -1 * @param {function(Directive, Element): Function} link The link758 -1 * function is called with an instance of {@link Directive} and a759 -1 * Element when {@link Registry#link} is executed.760 -1 *761 -1 * It is the only place where you can access a directive and762 -1 * therefore the place where you define its behavior.763 -1 *764 -1 * This typically means to make an initial call to {@link765 -1 * Directive#update} and to add some event listeners. You should also766 -1 * return an *unlink* function that clears all external references in767 -1 * order to avoid memory leaks.768 -1 * @return {Registry} this-1 745 * - **debug** - `{boolean}` - Enable debug mode. In debug mode, -1 746 * directive objects are available as properties from the DOM as -1 747 * `element.directive`. -1 748 * - **renderer** - `{function(string, Object)}` - The template renderer -1 749 * to be used. Defaults to {@link module:muu-template}. 769 750 */770 -1 this.registerDirective = function(type, template, link) {771 -1 directives[type] = {772 -1 template: template,773 -1 link: link-1 751 var Registry = function(config) { -1 752 var self = this; -1 753 var directives = {}; -1 754 -1 755 this.config = config || {}; -1 756 this.renderer = self.config.renderer || muuTemplate; -1 757 -1 758 /** -1 759 * Register a new type of {@link Directive} -1 760 * -1 761 * @param {string} type -1 762 * @param {string} template -1 763 * @param {function(Directive, Element): Function} link The link -1 764 * function is called with an instance of {@link Directive} and a -1 765 * Element when {@link Registry#link} is executed. -1 766 * -1 767 * It is the only place where you can access a directive and -1 768 * therefore the place where you define its behavior. -1 769 * -1 770 * This typically means to make an initial call to {@link -1 771 * Directive#update} and to add some event listeners. You should also -1 772 * return an *unlink* function that clears all external references in -1 773 * order to avoid memory leaks. -1 774 * @return {Registry} this -1 775 */ -1 776 this.registerDirective = function(type, template, link) { -1 777 directives[type] = { -1 778 template: template, -1 779 link: link -1 780 }; -1 781 return self; 774 782 };775 -1 return self;776 -1 };777 783778 -1 /**779 -1 * Shortcut for wrapping calls to {@link Registry} in a function.780 -1 *781 -1 * This can be esepcially helpful if that function is defined in a782 -1 * different module.783 -1 *784 -1 * ```.js785 -1 * _define('foobar', [], function() {786 -1 * return function(registry) {787 -1 * registry788 -1 * .registerDirective('foo', '...', function() {...})789 -1 * .registerDirective('bar', '...', function() {...});790 -1 * };791 -1 * });792 -1 *793 -1 * require(['foobar'], function(foobar) {794 -1 * var registry = new Registry();795 -1 * registry.registerModule(foobar);796 -1 * });797 -1 * ```798 -1 *799 -1 * @param {function(Registry)} module800 -1 * @return {Registry} this801 -1 */802 -1 this.registerModule = function(module) {803 -1 module(self);804 -1 return self;805 -1 };-1 784 /** -1 785 * Shortcut for wrapping calls to {@link Registry} in a function. -1 786 * -1 787 * This can be esepcially helpful if that function is defined in a -1 788 * different module. -1 789 * -1 790 * ```.js -1 791 * _define('foobar', [], function() { -1 792 * return function(registry) { -1 793 * registry -1 794 * .registerDirective('foo', '...', function() {...}) -1 795 * .registerDirective('bar', '...', function() {...}); -1 796 * }; -1 797 * }); -1 798 * -1 799 * require(['foobar'], function(foobar) { -1 800 * var registry = new Registry(); -1 801 * registry.registerModule(foobar); -1 802 * }); -1 803 * ``` -1 804 * -1 805 * @param {function(Registry)} module -1 806 * @return {Registry} this -1 807 */ -1 808 this.registerModule = function(module) { -1 809 module(self); -1 810 return self; -1 811 }; 806 812807 -1 /**808 -1 * Create and initialise a {@link Directive} for `element`.809 -1 *810 -1 * @param {Element} element811 -1 * @param {string} type812 -1 * @return {Directive}813 -1 */814 -1 this.link = function(element, type) {815 -1 if (type === undefined) {816 -1 type = element.getAttribute('type');817 -1 }-1 813 /** -1 814 * Create and initialise a {@link Directive} for `element`. -1 815 * -1 816 * @param {Element} element -1 817 * @param {string} type -1 818 * @return {Directive} -1 819 */ -1 820 this.link = function(element, type) { -1 821 if (type === undefined) { -1 822 type = element.getAttribute('type'); -1 823 } 818 824819 -1 if (!directives.hasOwnProperty(type)) {820 -1 throw new Error('Unknown directive type: ' + type);821 -1 }-1 825 if (!directives.hasOwnProperty(type)) { -1 826 throw new Error('Unknown directive type: ' + type); -1 827 } 822 828823 -1 var template = directives[type].template;824 -1 var link = directives[type].link;-1 829 var template = directives[type].template; -1 830 var link = directives[type].link; 825 831826 -1 var directive = new Directive(element, template, self);827 -1 var unlink = link(directive, element);828 -1 element.classList.add('muu-isolate');829 -1 element.classList.add('muu-initialised');-1 832 var directive = new Directive(element, template, self); -1 833 var unlink = link(directive, element); -1 834 element.classList.add('muu-isolate'); -1 835 element.classList.add('muu-initialised'); 830 836831 -1 if (self.config.debug) {832 -1 element.directive = directive;833 -1 }-1 837 if (self.config.debug) { -1 838 element.directive = directive; -1 839 } 834 840835 -1 if (unlink !== undefined) {836 -1 $.destroy(element, unlink);837 -1 }-1 841 if (unlink !== undefined) { -1 842 $.destroy(element, unlink); -1 843 } 838 844839 -1 return directive;840 -1 };-1 845 return directive; -1 846 }; 841 847842 -1 /**843 -1 * Link all directives that can be found inside `root`.844 -1 *845 -1 * @param {Element} root846 -1 * @return {Array.<Directive>}847 -1 */848 -1 this.linkAll = function(root) {849 -1 // NOTE: root may be a DOM Node or a directive850 -1 var elements = _.filter(root.querySelectorAll('muu'), function(element) {851 -1 return !element.classList.contains('muu-initialised');852 -1 });853 -1 return _.map(elements, function(element) {854 -1 return self.link(element);855 -1 });-1 848 /** -1 849 * Link all directives that can be found inside `root`. -1 850 * -1 851 * @param {Element} root -1 852 * @return {Array.<Directive>} -1 853 */ -1 854 this.linkAll = function(root) { -1 855 // NOTE: root may be a DOM Node or a directive -1 856 var elements = _.filter(root.querySelectorAll('muu'), function(element) { -1 857 return !element.classList.contains('muu-initialised'); -1 858 }); -1 859 return _.map(elements, function(element) { -1 860 return self.link(element); -1 861 }); -1 862 }; 856 863 };857 -1 };858 -1859 -1 return Registry;860 -1 });861 -1 _define('muu-search', ['muu-js-helpers'], function(_) {862 -1 "use strict";863 864864 -1 var q = {};-1 865 return Registry; -1 866 }); -1 867 _define('muu-search', ['muu-js-helpers'], function(_) { -1 868 "use strict"; 865 869866 -1 q.parse = function(s) {867 870 var q = {}; 868 871869 -1 var set = function(key, value) {870 -1 if (!q.hasOwnProperty(key)) {871 -1 q[key] = value;872 -1 } else if (_.isArray(q[key])) {873 -1 q[key].push(value);-1 872 q.parse = function(s) { -1 873 var q = {}; -1 874 -1 875 var set = function(key, value) { -1 876 if (!q.hasOwnProperty(key)) { -1 877 q[key] = value; -1 878 } else if (_.isArray(q[key])) { -1 879 q[key].push(value); -1 880 } else { -1 881 q[key] = [q[key], value]; -1 882 } -1 883 }; -1 884 -1 885 _.forEach(s.substring(1).split('&'), function(item) { -1 886 var i = _.map(item.split('='), decodeURIComponent); -1 887 if (i.length === 2) { -1 888 set(i[0], i[1]); -1 889 } else if (i[0]) { -1 890 set(i[0], true); -1 891 } -1 892 }); -1 893 return q; -1 894 }; -1 895 -1 896 var unparseItem = function(key, value) { -1 897 if (value === undefined || value === null || value === false) { -1 898 return []; -1 899 } else if (_.isArray(value)) { -1 900 return _.flatten(_.map(value, function(v) { -1 901 return unparseItem(key, v); -1 902 })); -1 903 } else if (value === true) { -1 904 return [encodeURIComponent(key)]; 874 905 } else {875 -1 q[key] = [q[key], value];-1 906 return [encodeURIComponent(key) + '=' + encodeURIComponent(value)]; 876 907 } 877 908 }; 878 909879 -1 _.forEach(s.substring(1).split('&'), function(item) {880 -1 var i = _.map(item.split('='), decodeURIComponent);881 -1 if (i.length === 2) {882 -1 set(i[0], i[1]);883 -1 } else if (i[0]) {884 -1 set(i[0], true);-1 910 q.unparse = function(q) { -1 911 if (_.isString(q)) { -1 912 return q; -1 913 } -1 914 -1 915 var a = [] -1 916 for (var key in q) { -1 917 if (q.hasOwnProperty(key)) { -1 918 a = a.concat(unparseItem(key, q[key])); -1 919 } 885 920 }886 -1 });-1 921 if (a.length > 0) { -1 922 return '?' + a.join('&'); -1 923 } else { -1 924 return ''; -1 925 } -1 926 }; -1 927 887 928 return q;888 -1 };-1 929 }); -1 930 /** -1 931 * minimal mustache insipred templating -1 932 * -1 933 * ## Variables -1 934 * -1 935 * Variables are created with a `{{name}}` tag. These are always escaped. -1 936 * -1 937 * ## Loops -1 938 * -1 939 * Loops render blocks of text a number of times, depending on the value of -1 940 * the key in the current context. -1 941 * -1 942 * A loop begins with a pound and ends with a slash. That is, `{{#person}}` -1 943 * begins a "person" section while `{{/person}}` ends it. -1 944 * -1 945 * If the value is an array, the block is repeated for each item in that array. -1 946 * In any other case, the block is rendered with the outer scope, but only if -1 947 * the value is truthy. -1 948 * -1 949 * ## Inverted loops -1 950 * -1 951 * Inverted loops render blocks of test if the value of the key is falsy. They -1 952 * begin with a caret. -1 953 * -1 954 * ## Comments -1 955 * -1 956 * Comments begin with a bang and are ignored. -1 957 * -1 958 * ## Pairs -1 959 * -1 960 * Pairs look like JSON objects. The result is a space separated list of all -1 961 * keys with truthy values. -1 962 * -1 963 * ``` -1 964 * muuTemplate('{{foo: var1, bar: var2, baz: var3}}', { -1 965 * var1: true, -1 966 * var2: false, -1 967 * var3: true -1 968 * }); // 'foo baz' -1 969 * ``` -1 970 * -1 971 * ## Special variable `this` -1 972 * -1 973 * `this` always refers to the current context. So the following expressions -1 974 * are equivalent: -1 975 * -1 976 * ``` -1 977 * muuTemplate('{{#items}}{{content}}{{/items}}', { -1 978 * item: [{ -1 979 * content: 1 -1 980 * }, { -1 981 * content: 2 -1 982 * }] -1 983 * }); -1 984 * -1 985 * muuTemplate('{{#this}}{{this}}{{/this}}', [1, 2]); -1 986 * ``` -1 987 * -1 988 * @module muu-template -1 989 * @param {string} template -1 990 * @param {Object} data -1 991 * @return {string} -1 992 */ -1 993 _define('muu-template', ['muu-js-helpers', 'muu-dom-helpers'], function(_, $) { -1 994 "use strict"; 889 995890 -1 var unparseItem = function(key, value) {891 -1 if (value === undefined || value === null || value === false) {892 -1 return [];893 -1 } else if (_.isArray(value)) {894 -1 return _.flatten(_.map(value, function(v) {895 -1 return unparseItem(key, v);896 -1 }));897 -1 } else if (value === true) {898 -1 return [encodeURIComponent(key)];899 -1 } else {900 -1 return [encodeURIComponent(key) + '=' + encodeURIComponent(value)];901 -1 }902 -1 };-1 996 var openTag = '{{'; -1 997 var closeTag = '}}'; 903 998904 -1 q.unparse = function(q) {905 -1 if (_.isString(q)) {906 -1 return q;907 -1 }-1 999 var getValue = function(key, data) { -1 1000 return key === 'this' ? data : data[key]; -1 1001 }; 908 1002909 -1 var a = []910 -1 for (var key in q) {911 -1 if (q.hasOwnProperty(key)) {912 -1 a = a.concat(unparseItem(key, q[key]));913 -1 }914 -1 }915 -1 if (a.length > 0) {916 -1 return '?' + a.join('&');917 -1 } else {918 -1 return '';919 -1 }920 -1 };-1 1003 var parseVariableTemplate = function(template) { -1 1004 var content = template.slice(2, -2); 921 1005922 -1 return q;923 -1 });924 -1 /**925 -1 * minimal mustache insipred templating926 -1 *927 -1 * ## Variables928 -1 *929 -1 * Variables are created with a `{{name}}` tag. These are always escaped.930 -1 *931 -1 * ## Loops932 -1 *933 -1 * Loops render blocks of text a number of times, depending on the value of934 -1 * the key in the current context.935 -1 *936 -1 * A loop begins with a pound and ends with a slash. That is, `{{#person}}`937 -1 * begins a "person" section while `{{/person}}` ends it.938 -1 *939 -1 * If the value is an array, the block is repeated for each item in that array.940 -1 * In any other case, the block is rendered with the outer scope, but only if941 -1 * the value is truthy.942 -1 *943 -1 * ## Inverted loops944 -1 *945 -1 * Inverted loops render blocks of test if the value of the key is falsy. They946 -1 * begin with a caret.947 -1 *948 -1 * ## Comments949 -1 *950 -1 * Comments begin with a bang and are ignored.951 -1 *952 -1 * ## Pairs953 -1 *954 -1 * Pairs look like JSON objects. The result is a space separated list of all955 -1 * keys with truthy values.956 -1 *957 -1 * ```958 -1 * muuTemplate('{{foo: var1, bar: var2, baz: var3}}', {959 -1 * var1: true,960 -1 * var2: false,961 -1 * var3: true962 -1 * }); // 'foo baz'963 -1 * ```964 -1 *965 -1 * ## Special variable `this`966 -1 *967 -1 * `this` always refers to the current context. So the following expressions968 -1 * are equivalent:969 -1 *970 -1 * ```971 -1 * muuTemplate('{{#items}}{{content}}{{/items}}', {972 -1 * item: [{973 -1 * content: 1974 -1 * }, {975 -1 * content: 2976 -1 * }]977 -1 * });978 -1 *979 -1 * muuTemplate('{{#this}}{{this}}{{/this}}', [1, 2]);980 -1 * ```981 -1 *982 -1 * @module muu-template983 -1 * @param {string} template984 -1 * @param {Object} data985 -1 * @return {string}986 -1 */987 -1 _define('muu-template', ['muu-js-helpers', 'muu-dom-helpers'], function(_, $) {988 -1 "use strict";989 -1990 -1 var openTag = '{{';991 -1 var closeTag = '}}';992 -1993 -1 var getValue = function(key, data) {994 -1 return key === 'this' ? data : data[key];995 -1 };-1 1006 if (template.indexOf(':') === -1) { -1 1007 return function(data) { -1 1008 return $.escapeHtml(getValue(content, data) || ''); -1 1009 }; -1 1010 } else { -1 1011 var pairs = content.split(',').map(function(pair) { -1 1012 var v = pair.split(':'); -1 1013 var key = v[0].trim(); -1 1014 var value = v.slice(1).join(':').trim(); -1 1015 return [key, value]; -1 1016 }); 996 1017997 -1 var parseVariableTemplate = function(template) {998 -1 var content = template.slice(2, -2);-1 1018 return function(data) { -1 1019 var results = []; 999 10201000 -1 if (template.indexOf(':') === -1) {1001 -1 return function(data) {1002 -1 return $.escapeHtml(getValue(content, data) || '');1003 -1 };1004 -1 } else {1005 -1 var pairs = content.split(',').map(function(pair) {1006 -1 var v = pair.split(':');1007 -1 var key = v[0].trim();1008 -1 var value = v.slice(1).join(':').trim();1009 -1 return [key, value];1010 -1 });-1 1021 for (var i = 0; i < pairs.length; i++) { -1 1022 var key = pairs[i][0]; -1 1023 var value = pairs[i][1]; -1 1024 -1 1025 if (getValue(value, data)) { -1 1026 results.push(key); -1 1027 } -1 1028 } -1 1029 -1 1030 return $.escapeHtml(results.join(' ')); -1 1031 }; -1 1032 } -1 1033 }; 1011 10341012 -1 return function(data) {1013 -1 var results = [];-1 1035 var parseLoopTemplate = function(tag, afterTag, inverted) { -1 1036 var tagName = tag.slice(3, -2); 1014 10371015 -1 for (var i = 0; i < pairs.length; i++) {1016 -1 var key = pairs[i][0];1017 -1 var value = pairs[i][1];-1 1038 var v = parseTemplate(afterTag, tagName); -1 1039 var inner = v[0]; -1 1040 var afterLoop = v[1]; 1018 10411019 -1 if (getValue(value, data)) {1020 -1 results.push(key);-1 1042 var render = function(data) { -1 1043 if (inverted) { -1 1044 if (getValue(tagName, data)) { -1 1045 return ''; -1 1046 } else { -1 1047 return inner(data); -1 1048 } -1 1049 } else { -1 1050 if (_.isArray(getValue(tagName, data))) { -1 1051 var result = ''; -1 1052 for (var i = 0; i < getValue(tagName, data).length; i++) { -1 1053 result += inner(getValue(tagName, data)[i]); -1 1054 } -1 1055 return result; -1 1056 } else if (getValue(tagName, data)) { -1 1057 return inner(data); -1 1058 } else { -1 1059 return ''; 1021 1060 } 1022 1061 }1023 -11024 -1 return $.escapeHtml(results.join(' '));1025 1062 };1026 -1 }1027 -1 };1028 10631029 -1 var parseLoopTemplate = function(tag, afterTag, inverted) {1030 -1 var tagName = tag.slice(3, -2);-1 1064 return [render, afterLoop]; -1 1065 }; 1031 10661032 -1 var v = parseTemplate(afterTag, tagName);1033 -1 var inner = v[0];1034 -1 var afterLoop = v[1];-1 1067 var concat = function(a) { -1 1068 var last = a.pop(); 1035 10691036 -1 var render = function(data) {1037 -1 if (inverted) {1038 -1 if (getValue(tagName, data)) {1039 -1 return '';-1 1070 if (_.isArray(last)) { -1 1071 a.push(last[0]); -1 1072 return [concat(a), last[1]]; -1 1073 } else { -1 1074 a.push(last); -1 1075 -1 1076 return function(data) { -1 1077 return a.map(function(item) { -1 1078 if (_.isString(item)) { -1 1079 return item; -1 1080 } else if (_.isFunction(item)) { -1 1081 return item(data); -1 1082 } -1 1083 }).join(''); -1 1084 }; -1 1085 } -1 1086 }; -1 1087 -1 1088 var parseTemplate = function(template, loopName) { -1 1089 var openIndex = template.indexOf(openTag); -1 1090 if (openIndex === -1) { -1 1091 if (loopName === undefined) { -1 1092 return function() { -1 1093 return template; -1 1094 }; 1040 1095 } else {1041 -1 return inner(data);-1 1096 throw new Error('unclosed loop: ' + loopName); 1042 1097 } 1043 1098 } else {1044 -1 if (_.isArray(getValue(tagName, data))) {1045 -1 var result = '';1046 -1 for (var i = 0; i < getValue(tagName, data).length; i++) {1047 -1 result += inner(getValue(tagName, data)[i]);-1 1099 var beforeTag = template.slice(0, openIndex); -1 1100 var tmp = template.slice(openIndex); -1 1101 -1 1102 var closeIndex = tmp.indexOf(closeTag) + 2; -1 1103 if (closeIndex === 1) { -1 1104 throw new Error('unclosed tag: ' + tmp); -1 1105 } -1 1106 var tag = tmp.slice(0, closeIndex); -1 1107 var afterTag = tmp.slice(closeIndex); -1 1108 -1 1109 if (tag.lastIndexOf('{{#', 0) === 0) { -1 1110 var v = parseLoopTemplate(tag, afterTag); -1 1111 var loop = v[0]; -1 1112 var after = parseTemplate(v[1], loopName); -1 1113 return concat([beforeTag, loop, after]); -1 1114 } else if (tag.lastIndexOf('{{^', 0) === 0) { -1 1115 var v = parseLoopTemplate(tag, afterTag, true); -1 1116 var loop = v[0]; -1 1117 var after = parseTemplate(v[1], loopName); -1 1118 return concat([beforeTag, loop, after]); -1 1119 } else if (tag.lastIndexOf('{{!', 0) === 0) { -1 1120 var after = parseTemplate(afterTag, loopName); -1 1121 return concat([beforeTag, after]); -1 1122 } else if (tag.lastIndexOf('{{/', 0) === 0) { -1 1123 if (tag.slice(3, -2) === loopName) { -1 1124 var render = function() { -1 1125 return beforeTag; -1 1126 }; -1 1127 return [render, afterTag]; -1 1128 } else { -1 1129 throw new Error('unexpected closing loop: ' + tag); 1048 1130 }1049 -1 return result;1050 -1 } else if (getValue(tagName, data)) {1051 -1 return inner(data);1052 1131 } else {1053 -1 return '';-1 1132 var render = parseVariableTemplate(tag); -1 1133 var after = parseTemplate(afterTag, loopName); -1 1134 return concat([beforeTag, render, after]); 1054 1135 } 1055 1136 } 1056 1137 }; 1057 11381058 -1 return [render, afterLoop];1059 -1 };-1 1139 return function(template, data) { -1 1140 return parseTemplate(template)(data); -1 1141 }; -1 1142 }); -1 1143 /** -1 1144 * Recreate children of `source` in `target` by making only small adjustments. -1 1145 * -1 1146 * *The following section explains details about the current implementation. -1 1147 * These are likely to change in the future.* -1 1148 * -1 1149 * The algorithms is relatively simple. It just iterates through all top level -1 1150 * nodes. If a node has a different `nodeType` (e.g. text or element) or a -1 1151 * different `nodeName` (e.g. div or ul) it is replaced completely and the -1 1152 * algorithm proceeds with the node's children recursively. Otherwise, only -1 1153 * the nodes's attributes are updated. -1 1154 * -1 1155 * Note that non-attribute properties (e.g. value) are lost in the first case -1 1156 * and preserved in the second. -1 1157 * -1 1158 * If the algorithm encounters an element with the class `muu-isolate` it does -1 1159 * not recurse into its children. This way, you can protect dynamically -1 1160 * generated content from being overwritten. -1 1161 * -1 1162 * @module muu-update-dom -1 1163 * @param {Element} target -1 1164 * @param {Element} source -1 1165 */ -1 1166 _define('muu-update-dom', ['muu-js-helpers'], function(_) { -1 1167 "use strict"; 1060 11681061 -1 var concat = function(a) {1062 -1 var last = a.pop();1063 -11064 -1 if (_.isArray(last)) {1065 -1 a.push(last[0]);1066 -1 return [concat(a), last[1]];1067 -1 } else {1068 -1 a.push(last);1069 -11070 -1 return function(data) {1071 -1 return a.map(function(item) {1072 -1 if (_.isString(item)) {1073 -1 return item;1074 -1 } else if (_.isFunction(item)) {1075 -1 return item(data);1076 -1 }1077 -1 }).join('');1078 -1 };1079 -1 }1080 -1 };-1 1169 var updateAttributes = function(target, source) { -1 1170 var targetAttrNames = _.map(target.attributes, function(item) { -1 1171 return item.name; -1 1172 }); -1 1173 var sourceAttrNames = _.map(source.attributes, function(item) { -1 1174 return item.name; -1 1175 }); 1081 11761082 -1 var parseTemplate = function(template, loopName) {1083 -1 var openIndex = template.indexOf(openTag);1084 -1 if (openIndex === -1) {1085 -1 if (loopName === undefined) {1086 -1 return function() {1087 -1 return template;1088 -1 };1089 -1 } else {1090 -1 throw new Error('unclosed loop: ' + loopName);1091 -1 }1092 -1 } else {1093 -1 var beforeTag = template.slice(0, openIndex);1094 -1 var tmp = template.slice(openIndex);-1 1177 _.forEach(targetAttrNames, function(name) { -1 1178 // NOTE: ie8.js creates some attribute -1 1179 if (!source.hasAttribute(name) && name.substr(0, 7) !== '__IE8__') { -1 1180 target.removeAttribute(name); -1 1181 } -1 1182 }); -1 1183 _.forEach(sourceAttrNames, function(name) { -1 1184 if (target.getAttribute(name) !== source.getAttribute(name)) { -1 1185 target.setAttribute(name, source.getAttribute(name)); -1 1186 } -1 1187 }); -1 1188 }; -1 1189 -1 1190 var updateDOM = function(target, source) { -1 1191 var nt = target.childNodes.length; -1 1192 var ns = source.childNodes.length; 1095 11931096 -1 var closeIndex = tmp.indexOf(closeTag) + 2;1097 -1 if (closeIndex === 1) {1098 -1 throw new Error('unclosed tag: ' + tmp);-1 1194 for (var i = ns; i < nt; i++) { -1 1195 target.removeChild(target.childNodes[ns]); 1099 1196 }1100 -1 var tag = tmp.slice(0, closeIndex);1101 -1 var afterTag = tmp.slice(closeIndex);1102 -11103 -1 if (tag.lastIndexOf('{{#', 0) === 0) {1104 -1 var v = parseLoopTemplate(tag, afterTag);1105 -1 var loop = v[0];1106 -1 var after = parseTemplate(v[1], loopName);1107 -1 return concat([beforeTag, loop, after]);1108 -1 } else if (tag.lastIndexOf('{{^', 0) === 0) {1109 -1 var v = parseLoopTemplate(tag, afterTag, true);1110 -1 var loop = v[0];1111 -1 var after = parseTemplate(v[1], loopName);1112 -1 return concat([beforeTag, loop, after]);1113 -1 } else if (tag.lastIndexOf('{{!', 0) === 0) {1114 -1 var after = parseTemplate(afterTag, loopName);1115 -1 return concat([beforeTag, after]);1116 -1 } else if (tag.lastIndexOf('{{/', 0) === 0) {1117 -1 if (tag.slice(3, -2) === loopName) {1118 -1 var render = function() {1119 -1 return beforeTag;1120 -1 };1121 -1 return [render, afterTag];-1 1197 for (i = nt; i < ns; i++) { -1 1198 target.appendChild(source.childNodes[nt]); -1 1199 } -1 1200 for (i = 0; i < nt && i < ns; i++) { -1 1201 var tchild = target.childNodes[i]; -1 1202 var schild = source.childNodes[i]; -1 1203 -1 1204 if (tchild.nodeType === schild.nodeType && tchild.nodeName === schild.nodeName && tchild.type === schild.type) { -1 1205 if (tchild.nodeType === 1) { -1 1206 var muuClasses = _.filter(tchild.classList, function(cls) { -1 1207 return cls.lastIndexOf('muu-', 0) === 0; -1 1208 }); -1 1209 updateAttributes(tchild, schild); -1 1210 _.forEach(muuClasses, function(cls) { -1 1211 tchild.classList.add(cls); -1 1212 }); -1 1213 } else if (tchild.nodeType === 3) { -1 1214 tchild.nodeValue = schild.nodeValue; -1 1215 } -1 1216 if (tchild.nodeType !== 3 && !tchild.classList.contains('muu-isolate')) { -1 1217 updateDOM(tchild, schild); -1 1218 } 1122 1219 } else {1123 -1 throw new Error('unexpected closing loop: ' + tag);-1 1220 tchild.parentNode.replaceChild(schild, tchild); 1124 1221 }1125 -1 } else {1126 -1 var render = parseVariableTemplate(tag);1127 -1 var after = parseTemplate(afterTag, loopName);1128 -1 return concat([beforeTag, render, after]);1129 1222 }1130 -1 }1131 -1 };-1 1223 }; 1132 12241133 -1 return function(template, data) {1134 -1 return parseTemplate(template)(data);1135 -1 };1136 -1 });1137 -1 /**1138 -1 * Recreate children of `source` in `target` by making only small adjustments.1139 -1 *1140 -1 * *The following section explains details about the current implementation.1141 -1 * These are likely to change in the future.*1142 -1 *1143 -1 * The algorithms is relatively simple. It just iterates through all top level1144 -1 * nodes. If a node has a different `nodeType` (e.g. text or element) or a1145 -1 * different `nodeName` (e.g. div or ul) it is replaced completely and the1146 -1 * algorithm proceeds with the node's children recursively. Otherwise, only1147 -1 * the nodes's attributes are updated.1148 -1 *1149 -1 * Note that non-attribute properties (e.g. value) are lost in the first case1150 -1 * and preserved in the second.1151 -1 *1152 -1 * If the algorithm encounters an element with the class `muu-isolate` it does1153 -1 * not recurse into its children. This way, you can protect dynamically1154 -1 * generated content from being overwritten.1155 -1 *1156 -1 * @module muu-update-dom1157 -1 * @param {Element} target1158 -1 * @param {Element} source1159 -1 */1160 -1 _define('muu-update-dom', ['muu-js-helpers'], function(_) {1161 -1 "use strict";1162 -11163 -1 var updateAttributes = function(target, source) {1164 -1 var targetAttrNames = _.map(target.attributes, function(item) {1165 -1 return item.name;1166 -1 });1167 -1 var sourceAttrNames = _.map(source.attributes, function(item) {1168 -1 return item.name;1169 -1 });1170 -11171 -1 _.forEach(targetAttrNames, function(name) {1172 -1 // NOTE: ie8.js creates some attribute1173 -1 if (!source.hasAttribute(name) && name.substr(0, 7) !== '__IE8__') {1174 -1 target.removeAttribute(name);1175 -1 }1176 -1 });1177 -1 _.forEach(sourceAttrNames, function(name) {1178 -1 if (target.getAttribute(name) !== source.getAttribute(name)) {1179 -1 target.setAttribute(name, source.getAttribute(name));1180 -1 }1181 -1 });1182 -1 };-1 1225 return updateDOM; -1 1226 }); -1 1227 /** -1 1228 * This module gives access to the following objects: -1 1229 * -1 1230 * - `Registry` - {@link Registry} -1 1231 * - `$` - {@link module:muu-dom-helpers} -1 1232 * - `$location` - {@link module:muu-location} -1 1233 * -1 1234 * @module muu -1 1235 */ -1 1236 _define('muu', ['muu-registry', 'muu-dom-helpers', 'muu-location'], function(Registry, $, $location) { -1 1237 "use strict"; 1183 12381184 -1 var updateDOM = function(target, source) {1185 -1 var nt = target.childNodes.length;1186 -1 var ns = source.childNodes.length;-1 1239 var module = {}; 1187 12401188 -1 for (var i = ns; i < nt; i++) {1189 -1 target.removeChild(target.childNodes[ns]);1190 -1 }1191 -1 for (i = nt; i < ns; i++) {1192 -1 target.appendChild(source.childNodes[nt]);1193 -1 }1194 -1 for (i = 0; i < nt && i < ns; i++) {1195 -1 var tchild = target.childNodes[i];1196 -1 var schild = source.childNodes[i];1197 -11198 -1 if (tchild.nodeType === schild.nodeType && tchild.nodeName === schild.nodeName && tchild.type === schild.type) {1199 -1 if (tchild.nodeType === 1) {1200 -1 var muuClasses = _.filter(tchild.classList, function(cls) {1201 -1 return cls.lastIndexOf('muu-', 0) === 0;1202 -1 });1203 -1 updateAttributes(tchild, schild);1204 -1 _.forEach(muuClasses, function(cls) {1205 -1 tchild.classList.add(cls);1206 -1 });1207 -1 } else if (tchild.nodeType === 3) {1208 -1 tchild.nodeValue = schild.nodeValue;1209 -1 }1210 -1 if (tchild.nodeType !== 3 && !tchild.classList.contains('muu-isolate')) {1211 -1 updateDOM(tchild, schild);1212 -1 }1213 -1 } else {1214 -1 tchild.parentNode.replaceChild(schild, tchild);1215 -1 }1216 -1 }1217 -1 };-1 1241 module.Registry = Registry; -1 1242 module.$ = $; -1 1243 module.$location = $location; 1218 12441219 -1 return updateDOM;1220 -1 });1221 -1 /**1222 -1 * This module gives access to the following objects:1223 -1 *1224 -1 * - `Registry` - {@link Registry}1225 -1 * - `$` - {@link module:muu-dom-helpers}1226 -1 * - `$location` - {@link module:muu-location}1227 -1 *1228 -1 * @module muu1229 -1 */1230 -1 _define('muu', ['muu-registry', 'muu-dom-helpers', 'muu-location'], function(Registry, $, $location) {1231 -1 "use strict";1232 -11233 -1 var module = {};1234 -11235 -1 module.Registry = Registry;1236 -1 module.$ = $;1237 -1 module.$location = $location;1238 -11239 -1 return module;1240 -1 });-1 1245 return module; -1 1246 }); 1241 12471242 -1 return _require('muu');-1 1248 return _require(name); 1243 1249 }); 1244 1250 })(window, document, void 0);
diff --git a/dist/muu.min.js b/dist/muu.min.js
@@ -1,19 +1,19 @@1 -1 (function(r,p,h){(function(h){"function"===typeof define&&define.amd?define("muu",[],h):r.muu=h()})(function(){var q={},v=function(c,b){for(var e=[],a=0;a<c.length;a++)e.push(b(c[a]));return e},m=function(c,b,e){q[c]={deps:b,factory:e}},t=function(c){if(!q[c])return h;q[c].instance||(q[c].instance=q[c].factory.apply(h,v(q[c].deps,t)));return q[c].instance};m("muu-directive",["muu-dom-helpers","muu-js-helpers","muu-update-dom"],function(c,b,e){return function(a,d,g){var f=this;a.innerHTML="";var k=2 -1 function(d){var b="data-on"+d.type;d.target.hasAttribute(b)&&(b=d.target.getAttribute(b),d=c.createEvent("muu-"+b,d),a.dispatchEvent(d))};this.update=function(s){var u=p.createElement("div");u.innerHTML=g.renderer(d,s);e(a,u);b.forEach(["keydown","keyup","click","change","search"],function(a){b.forEach(f.querySelectorAll("[data-on"+a+"]"),function(d){d.addEventListener(a,k,!1)})});var n=c.createEvent("muu-parent-update");s=this.querySelectorAll("muu.muu-initialised");b.forEach(s,function(a){a.dispatchEvent(n)});3 -1 g.linkAll(f)};this.querySelectorAll=function(d){var k=a.querySelectorAll(d),c=a.querySelectorAll(".muu-isolate"),c=b.union(b.map(c,function(a){return a.querySelectorAll(d)}));return b.difference(k,c)};this.querySelector=function(a){a=f.querySelectorAll(a);if(0<a.length)return a[0]};this.getModel=function(a,d){if(a===h){var k={};b.forEach(f.querySelectorAll("[name]"),function(a){k[a.name]=f.getModel(a.name)});return k}var e=f.querySelector("[name="+a+"]");return e===h?d:"checkbox"===e.type?e.checked:4 -1 "radio"===e.type?(e=f.querySelectorAll("[name="+a+"]"),c.getRadio(e)||d):e.value};this.setModel=function(a,d){var b=f.querySelector("[name="+a+"]");"checkbox"===b.type?b.checked=d:"radio"===b.type?(b=f.querySelectorAll("[name="+a+"]"),c.setRadio(b,d)):b.value=d}}});m("muu-dom-helpers",["muu-js-helpers"],function(c){var b={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"},e={DELAY:1E3,escapeHtml:function(a){return String(a).replace(/[&<>"'\/]/g,function(a){return b[a]})},createEvent:function(a,5 -1 d){if("function"===typeof CustomEvent)return new CustomEvent(a,{detail:d});var b=p.createEvent("CustomEvent");b.initCustomEvent(a,!1,!0,d);return b},on:function(a,d,b){a.addEventListener(d,b,!1);return function(){a.removeEventListener(d,b,!1)}},ready:function(a){a=c.once(a);if("complete"===p.readyState)return a(),function(){};var d=e.on(p,"DOMContentLoaded",a),b=e.on(r,"load",a);return function(){d();b()}},isDescendant:function(a,d){return!!a&&(a===d||e.isDescendant(a.parentNode,d))},destroy:function(a,6 -1 d){var b;if(r.MutationObserver){var f=new MutationObserver(function(){e.isDescendant(a,p)||(d(),b())});f.observe(p,{childList:!0,subtree:!0});b=c.once(function(){f.disconnect();f=h})}else{var k=setInterval(function(){e.isDescendant(a,p)||(d(),b())},e.DELAY);b=function(){clearInterval(k)}}return b},getRadio:function(a){for(var d=0;d<a.length;d++)if(a[d].checked)return a[d].value},setRadio:function(a,d){for(var b=0;b<a.length;b++)a[b].checked=a[b].value===d?!0:!1}};return e});m("muu-js-helpers",[],7 -1 function(){var c={isString:function(b){return"string"===typeof b||"[object String]"===Object.prototype.toString.call(b)}};c.isArray=Array.isArray;c.isFunction=function(b){return"function"===typeof b};c.once=function(b){var e,a=!1;return function(){a||(e=b.apply(this,arguments),a=!0);return e}};c.indexOf=function(b,e){if("indexOf"in b)return b.indexOf(e);for(var a=0;a<b.length;a++)if(b[a]===e)return a;return-1};c.forEach=function(b,e){if("forEach"in b)return b.forEach(e);for(var a=0;a<b.length;a++)e(b[a])};8 -1 c.map=function(b,e){if("map"in b)return b.map(e);for(var a=[],d=0;d<b.length;d++)a.push(e(b[d]));return a};c.filter=function(b,e){if("filter"in b)return b.filter(e);for(var a=[],d=0;d<b.length;d++)e(b[d])&&a.push(b[d]);return a};c.union=function(b){for(var e=[],a=0;a<b.length;a++)for(var d=0;d<b[a].length;d++)-1===c.indexOf(e,b[a][d])&&e.push(b[a][d]);return e};c.difference=function(b,e){for(var a=[],d=0;d<b.length;d++)-1===c.indexOf(e,b[d])&&a.push(b[d]);return a};c.flatten=function(b){var e=[];9 -1 c.forEach(b,function(a){c.isArray(a)?e=e.concat(c.flatten(a)):e.push(a)});return e};return c});m("muu-location",["muu-search"],function(c){var b={absUrl:function(){return location.href},url:function(a,d){if(a===h)return location.pathname+location.search+location.hash;d?history.replaceState(null,null,a):history.pushState(null,null,a);return b},protocol:function(){return location.protocol},host:function(){return location.host},port:function(){return location.port},path:function(a,d){if(a===h)return location.pathname;10 -1 b.url(a+location.search+location.hash,d);return b}},e=function(a,d){if(a===h)return location.search;a&&"?"!==a[0]&&(a="?"+a);1===a.length&&(a="");b.url(location.pathname+a+location.hash,d);return b};b.search=function(a,b,g){if(a!==h){if(b!==h){var f=c.parse(e());f[a]=b;return e(c.unparse(f),g)}return e(c.unparse(a),g)}return c.parse(e())};b.hash=function(a,d){if(a===h)return location.hash?location.hash.slice(1):"";b.url(location.pathname+location.search+"#"+a,d);return b};b.addEventListener=function(a,11 -1 d){"change"===a&&r.addEventListener("popstate",d,!1);return b};b.removeEventListener=function(a,d){"change"===a&&r.removeEventListener("popstate",d,!1);return b};return b});m("muu-registry",["muu-template","muu-directive","muu-js-helpers","muu-dom-helpers"],function(c,b,e,a){return function(d){var g=this,f={};this.config=d||{};this.renderer=g.config.renderer||c;this.registerDirective=function(a,b,d){f[a]={template:b,link:d};return g};this.registerModule=function(a){a(g);return g};this.link=function(d,12 -1 e){e===h&&(e=d.getAttribute("type"));if(!f.hasOwnProperty(e))throw Error("Unknown directive type: "+e);var c=f[e].link,n=new b(d,f[e].template,g),c=c(n,d);d.classList.add("muu-isolate");d.classList.add("muu-initialised");g.config.debug&&(d.directive=n);c!==h&&a.destroy(d,c);return n};this.linkAll=function(a){a=e.filter(a.querySelectorAll("muu"),function(a){return!a.classList.contains("muu-initialised")});return e.map(a,function(a){return g.link(a)})}}});m("muu-search",["muu-js-helpers"],function(c){var b=13 -1 {parse:function(a){var d={},b=function(a,b){d.hasOwnProperty(a)?c.isArray(d[a])?d[a].push(b):d[a]=[d[a],b]:d[a]=b};c.forEach(a.substring(1).split("&"),function(a){a=c.map(a.split("="),decodeURIComponent);2===a.length?b(a[0],a[1]):a[0]&&b(a[0],!0)});return d}},e=function(a,b){return b===h||null===b||!1===b?[]:c.isArray(b)?c.flatten(c.map(b,function(b){return e(a,b)})):!0===b?[encodeURIComponent(a)]:[encodeURIComponent(a)+"="+encodeURIComponent(b)]};b.unparse=function(a){if(c.isString(a))return a;var b=14 -1 [],g;for(g in a)a.hasOwnProperty(g)&&(b=b.concat(e(g,a[g])));return 0<b.length?"?"+b.join("&"):""};return b});m("muu-template",["muu-js-helpers","muu-dom-helpers"],function(c,b){var e=function(a,b){return"this"===a?b:b[a]},a=function(a){var d=a.slice(2,-2);if(-1===a.indexOf(":"))return function(a){return b.escapeHtml(e(d,a)||"")};var c=d.split(",").map(function(a){var b=a.split(":");a=b[0].trim();b=b.slice(1).join(":").trim();return[a,b]});return function(a){for(var d=[],k=0;k<c.length;k++){var f=15 -1 c[k][0];e(c[k][1],a)&&d.push(f)}return b.escapeHtml(d.join(" "))}},d=function(a,b,d){var g=a.slice(3,-2);a=f(b,g);var l=a[0];return[function(a){if(d)return e(g,a)?"":l(a);if(c.isArray(e(g,a))){for(var b="",k=0;k<e(g,a).length;k++)b+=l(e(g,a)[k]);return b}return e(g,a)?l(a):""},a[1]]},g=function(a){var b=a.pop();if(c.isArray(b))return a.push(b[0]),[g(a),b[1]];a.push(b);return function(b){return a.map(function(a){if(c.isString(a))return a;if(c.isFunction(a))return a(b)}).join("")}},f=function(b,e){var c=16 -1 b.indexOf("{{");if(-1===c){if(e===h)return function(){return b};throw Error("unclosed loop: "+e);}var n=b.slice(0,c),l=b.slice(c),m=l.indexOf("}}")+2;if(1===m)throw Error("unclosed tag: "+l);c=l.slice(0,m);l=l.slice(m);if(0===c.lastIndexOf("{{#",0))return l=d(c,l),c=l[0],l=f(l[1],e),g([n,c,l]);if(0===c.lastIndexOf("{{^",0))return l=d(c,l,!0),c=l[0],l=f(l[1],e),g([n,c,l]);if(0===c.lastIndexOf("{{!",0))return l=f(l,e),g([n,l]);if(0===c.lastIndexOf("{{/",0)){if(c.slice(3,-2)===e)return[function(){return n},17 -1 l];throw Error("unexpected closing loop: "+c);}c=a(c);l=f(l,e);return g([n,c,l])};return function(a,b){return f(a)(b)}});m("muu-update-dom",["muu-js-helpers"],function(c){var b=function(a,b){var e=c.map(a.attributes,function(a){return a.name}),f=c.map(b.attributes,function(a){return a.name});c.forEach(e,function(c){b.hasAttribute(c)||"__IE8__"===c.substr(0,7)||a.removeAttribute(c)});c.forEach(f,function(c){a.getAttribute(c)!==b.getAttribute(c)&&a.setAttribute(c,b.getAttribute(c))})},e=function(a,18 -1 d){for(var g=a.childNodes.length,f=d.childNodes.length,k=f;k<g;k++)a.removeChild(a.childNodes[f]);for(k=g;k<f;k++)a.appendChild(d.childNodes[g]);for(k=0;k<g&&k<f;k++){var h=a.childNodes[k],m=d.childNodes[k];if(h.nodeType===m.nodeType&&h.nodeName===m.nodeName&&h.type===m.type){if(1===h.nodeType){var n=c.filter(h.classList,function(a){return 0===a.lastIndexOf("muu-",0)});b(h,m);c.forEach(n,function(a){h.classList.add(a)})}else 3===h.nodeType&&(h.nodeValue=m.nodeValue);3===h.nodeType||h.classList.contains("muu-isolate")||-1 1 (function(r,q,h){(function(h){"function"===typeof define&&define.amd?define("muu",[],h):r.muu=h(r._)})(function(m){var p={};p["muu-js-helpers"]={instance:m};var v=function(c,b){for(var e=[],a=0;a<c.length;a++)e.push(b(c[a]));return e};m=function(c,b,e){p[c]={deps:b,factory:e}};var t=function(c){if(!p[c])return h;p[c].instance||(p[c].instance=p[c].factory.apply(h,v(p[c].deps,t)));return p[c].instance};m("muu-directive",["muu-dom-helpers","muu-js-helpers","muu-update-dom"],function(c,b,e){return function(a, -1 2 d,g){var f=this;a.innerHTML="";var k=function(d){var b="data-on"+d.type;d.target.hasAttribute(b)&&(b=d.target.getAttribute(b),d=c.createEvent("muu-"+b,d),a.dispatchEvent(d))};this.update=function(s){var u=q.createElement("div");u.innerHTML=g.renderer(d,s);e(a,u);b.forEach(["keydown","keyup","click","change","search"],function(a){b.forEach(f.querySelectorAll("[data-on"+a+"]"),function(d){d.addEventListener(a,k,!1)})});var n=c.createEvent("muu-parent-update");s=this.querySelectorAll("muu.muu-initialised"); -1 3 b.forEach(s,function(a){a.dispatchEvent(n)});g.linkAll(f)};this.querySelectorAll=function(d){var k=a.querySelectorAll(d),c=a.querySelectorAll(".muu-isolate"),c=b.union(b.map(c,function(a){return a.querySelectorAll(d)}));return b.difference(k,c)};this.querySelector=function(a){a=f.querySelectorAll(a);if(0<a.length)return a[0]};this.getModel=function(a,d){if(a===h){var k={};b.forEach(f.querySelectorAll("[name]"),function(a){k[a.name]=f.getModel(a.name)});return k}var e=f.querySelector("[name="+a+"]"); -1 4 return e===h?d:"checkbox"===e.type?e.checked:"radio"===e.type?(e=f.querySelectorAll("[name="+a+"]"),c.getRadio(e)||d):e.value};this.setModel=function(a,d){var b=f.querySelector("[name="+a+"]");"checkbox"===b.type?b.checked=d:"radio"===b.type?(b=f.querySelectorAll("[name="+a+"]"),c.setRadio(b,d)):b.value=d}}});m("muu-dom-helpers",["muu-js-helpers"],function(c){var b={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"},e={DELAY:1E3,escapeHtml:function(a){return String(a).replace(/[&<>"'\/]/g, -1 5 function(a){return b[a]})},createEvent:function(a,d){if("function"===typeof CustomEvent)return new CustomEvent(a,{detail:d});var b=q.createEvent("CustomEvent");b.initCustomEvent(a,!1,!0,d);return b},on:function(a,d,b){a.addEventListener(d,b,!1);return function(){a.removeEventListener(d,b,!1)}},ready:function(a){a=c.once(a);if("complete"===q.readyState)return a(),function(){};var d=e.on(q,"DOMContentLoaded",a),b=e.on(r,"load",a);return function(){d();b()}},isDescendant:function(a,d){return!!a&&(a=== -1 6 d||e.isDescendant(a.parentNode,d))},destroy:function(a,d){var b;if(r.MutationObserver){var f=new MutationObserver(function(){e.isDescendant(a,q)||(d(),b())});f.observe(q,{childList:!0,subtree:!0});b=c.once(function(){f.disconnect();f=h})}else{var k=setInterval(function(){e.isDescendant(a,q)||(d(),b())},e.DELAY);b=function(){clearInterval(k)}}return b},getRadio:function(a){for(var d=0;d<a.length;d++)if(a[d].checked)return a[d].value},setRadio:function(a,d){for(var b=0;b<a.length;b++)a[b].checked=a[b].value=== -1 7 d?!0:!1}};return e});m("muu-js-helpers",[],function(){var c={isString:function(b){return"string"===typeof b||"[object String]"===Object.prototype.toString.call(b)}};c.isArray=Array.isArray;c.isFunction=function(b){return"function"===typeof b};c.once=function(b){var e,a=!1;return function(){a||(e=b.apply(this,arguments),a=!0);return e}};c.indexOf=function(b,e){if("indexOf"in b)return b.indexOf(e);for(var a=0;a<b.length;a++)if(b[a]===e)return a;return-1};c.forEach=function(b,e){if("forEach"in b)return b.forEach(e); -1 8 for(var a=0;a<b.length;a++)e(b[a])};c.map=function(b,e){if("map"in b)return b.map(e);for(var a=[],d=0;d<b.length;d++)a.push(e(b[d]));return a};c.filter=function(b,e){if("filter"in b)return b.filter(e);for(var a=[],d=0;d<b.length;d++)e(b[d])&&a.push(b[d]);return a};c.union=function(b){for(var e=[],a=0;a<b.length;a++)for(var d=0;d<b[a].length;d++)-1===c.indexOf(e,b[a][d])&&e.push(b[a][d]);return e};c.difference=function(b,e){for(var a=[],d=0;d<b.length;d++)-1===c.indexOf(e,b[d])&&a.push(b[d]);return a}; -1 9 c.flatten=function(b){var e=[];c.forEach(b,function(a){c.isArray(a)?e=e.concat(c.flatten(a)):e.push(a)});return e};return c});m("muu-location",["muu-search"],function(c){var b={absUrl:function(){return location.href},url:function(a,d){if(a===h)return location.pathname+location.search+location.hash;d?history.replaceState(null,null,a):history.pushState(null,null,a);return b},protocol:function(){return location.protocol},host:function(){return location.host},port:function(){return location.port},path:function(a, -1 10 d){if(a===h)return location.pathname;b.url(a+location.search+location.hash,d);return b}},e=function(a,d){if(a===h)return location.search;a&&"?"!==a[0]&&(a="?"+a);1===a.length&&(a="");b.url(location.pathname+a+location.hash,d);return b};b.search=function(a,b,g){if(a!==h){if(b!==h){var f=c.parse(e());f[a]=b;return e(c.unparse(f),g)}return e(c.unparse(a),g)}return c.parse(e())};b.hash=function(a,d){if(a===h)return location.hash?location.hash.slice(1):"";b.url(location.pathname+location.search+"#"+a, -1 11 d);return b};b.addEventListener=function(a,d){"change"===a&&r.addEventListener("popstate",d,!1);return b};b.removeEventListener=function(a,d){"change"===a&&r.removeEventListener("popstate",d,!1);return b};return b});m("muu-registry",["muu-template","muu-directive","muu-js-helpers","muu-dom-helpers"],function(c,b,e,a){return function(d){var g=this,f={};this.config=d||{};this.renderer=g.config.renderer||c;this.registerDirective=function(a,b,d){f[a]={template:b,link:d};return g};this.registerModule= -1 12 function(a){a(g);return g};this.link=function(d,e){e===h&&(e=d.getAttribute("type"));if(!f.hasOwnProperty(e))throw Error("Unknown directive type: "+e);var c=f[e].link,n=new b(d,f[e].template,g),c=c(n,d);d.classList.add("muu-isolate");d.classList.add("muu-initialised");g.config.debug&&(d.directive=n);c!==h&&a.destroy(d,c);return n};this.linkAll=function(a){a=e.filter(a.querySelectorAll("muu"),function(a){return!a.classList.contains("muu-initialised")});return e.map(a,function(a){return g.link(a)})}}}); -1 13 m("muu-search",["muu-js-helpers"],function(c){var b={parse:function(a){var d={},b=function(a,b){d.hasOwnProperty(a)?c.isArray(d[a])?d[a].push(b):d[a]=[d[a],b]:d[a]=b};c.forEach(a.substring(1).split("&"),function(a){a=c.map(a.split("="),decodeURIComponent);2===a.length?b(a[0],a[1]):a[0]&&b(a[0],!0)});return d}},e=function(a,b){return b===h||null===b||!1===b?[]:c.isArray(b)?c.flatten(c.map(b,function(b){return e(a,b)})):!0===b?[encodeURIComponent(a)]:[encodeURIComponent(a)+"="+encodeURIComponent(b)]}; -1 14 b.unparse=function(a){if(c.isString(a))return a;var b=[],g;for(g in a)a.hasOwnProperty(g)&&(b=b.concat(e(g,a[g])));return 0<b.length?"?"+b.join("&"):""};return b});m("muu-template",["muu-js-helpers","muu-dom-helpers"],function(c,b){var e=function(a,b){return"this"===a?b:b[a]},a=function(a){var d=a.slice(2,-2);if(-1===a.indexOf(":"))return function(a){return b.escapeHtml(e(d,a)||"")};var c=d.split(",").map(function(a){var b=a.split(":");a=b[0].trim();b=b.slice(1).join(":").trim();return[a,b]});return function(a){for(var d= -1 15 [],k=0;k<c.length;k++){var f=c[k][0];e(c[k][1],a)&&d.push(f)}return b.escapeHtml(d.join(" "))}},d=function(a,b,d){var g=a.slice(3,-2);a=f(b,g);var l=a[0];return[function(a){if(d)return e(g,a)?"":l(a);if(c.isArray(e(g,a))){for(var b="",k=0;k<e(g,a).length;k++)b+=l(e(g,a)[k]);return b}return e(g,a)?l(a):""},a[1]]},g=function(a){var b=a.pop();if(c.isArray(b))return a.push(b[0]),[g(a),b[1]];a.push(b);return function(b){return a.map(function(a){if(c.isString(a))return a;if(c.isFunction(a))return a(b)}).join("")}}, -1 16 f=function(b,e){var c=b.indexOf("{{");if(-1===c){if(e===h)return function(){return b};throw Error("unclosed loop: "+e);}var n=b.slice(0,c),l=b.slice(c),m=l.indexOf("}}")+2;if(1===m)throw Error("unclosed tag: "+l);c=l.slice(0,m);l=l.slice(m);if(0===c.lastIndexOf("{{#",0))return l=d(c,l),c=l[0],l=f(l[1],e),g([n,c,l]);if(0===c.lastIndexOf("{{^",0))return l=d(c,l,!0),c=l[0],l=f(l[1],e),g([n,c,l]);if(0===c.lastIndexOf("{{!",0))return l=f(l,e),g([n,l]);if(0===c.lastIndexOf("{{/",0)){if(c.slice(3,-2)=== -1 17 e)return[function(){return n},l];throw Error("unexpected closing loop: "+c);}c=a(c);l=f(l,e);return g([n,c,l])};return function(a,b){return f(a)(b)}});m("muu-update-dom",["muu-js-helpers"],function(c){var b=function(a,b){var e=c.map(a.attributes,function(a){return a.name}),f=c.map(b.attributes,function(a){return a.name});c.forEach(e,function(c){b.hasAttribute(c)||"__IE8__"===c.substr(0,7)||a.removeAttribute(c)});c.forEach(f,function(c){a.getAttribute(c)!==b.getAttribute(c)&&a.setAttribute(c,b.getAttribute(c))})}, -1 18 e=function(a,d){for(var g=a.childNodes.length,f=d.childNodes.length,k=f;k<g;k++)a.removeChild(a.childNodes[f]);for(k=g;k<f;k++)a.appendChild(d.childNodes[g]);for(k=0;k<g&&k<f;k++){var h=a.childNodes[k],m=d.childNodes[k];if(h.nodeType===m.nodeType&&h.nodeName===m.nodeName&&h.type===m.type){if(1===h.nodeType){var n=c.filter(h.classList,function(a){return 0===a.lastIndexOf("muu-",0)});b(h,m);c.forEach(n,function(a){h.classList.add(a)})}else 3===h.nodeType&&(h.nodeValue=m.nodeValue);3===h.nodeType||h.classList.contains("muu-isolate")|| 19 19 e(h,m)}else h.parentNode.replaceChild(m,h)}};return e});m("muu",["muu-registry","muu-dom-helpers","muu-location"],function(c,b,e){var a={};a.Registry=c;a.$=b;a.$location=e;return a});return t("muu")})})(window,document,void 0);