- commit
- 824e80a4738158b4135e7e528c1b54d74fd75abf
- parent
- c797dae8987cf8ebd117c4a4126c36854ef06a06
- Author
- Tobias Bengfort <tobias.bengfort@gmx.net>
- Date
- 2015-08-28 12:10
Merge branch 'feature-dist-layout'
Diffstat
| A | .build/externs.js | 19 | +++++++++++++++++++ |
| A | .build/template.js | 49 | +++++++++++++++++++++++++++++++++++++++++++++++++ |
| M | .doc/tutorials/phonecat.md | 7 | +++---- |
| M | Makefile | 36 | ++++++++++++++++++++++++++++++++---- |
| M | README.md | 24 | ++++++++++++++++++++++++ |
| D | build.js | 8 | -------- |
| A | dist/muu-core.js | 644 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | dist/muu-core.min.js | 10 | ++++++++++ |
| A | dist/muu.js | 1277 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | dist/muu.min.js | 19 | +++++++++++++++++++ |
| M | examples/example/example.js | 16 | ++++++++-------- |
| M | examples/example/index.html | 1 | - |
| M | examples/example/muu-moment.js | 4 | ++-- |
| M | examples/phonecat/index.html | 1 | - |
| M | examples/phonecat/phonecat.js | 12 | ++++++------ |
| D | muu.min.js | 3 | --- |
| D | muu.min.js.map | 2 | -- |
| M | src/muu-directive.js | 21 | +++++++++++++-------- |
| M | src/muu-dom-helpers.js | 33 | +++++++++++++++++++-------------- |
| M | src/muu-js-helpers.js | 51 | +++++++++++++++++++++++++++++++-------------------- |
| M | src/muu-location.js | 28 | ++++++++++++++++++---------- |
| C | src/muu.js -> src/muu-registry.js | 25 | +++++++++++++------------ |
| M | src/muu-search.js | 4 | ++-- |
| M | src/muu-template.js | 7 | ++++--- |
| M | src/muu-update-dom.js | 6 | +++--- |
| M | src/muu.js | 137 | ++++++------------------------------------------------------- |
| M | test/test-registry.js | 2 | +- |
27 files changed, 2209 insertions, 237 deletions
diff --git a/.build/externs.js b/.build/externs.js
@@ -0,0 +1,19 @@
-1 1 /** @type {Object} */
-1 2 var history;
-1 3
-1 4 var _ = {
-1 5 once: function(fn) {},
-1 6 difference: function(a, b) {},
-1 7 union: function(a) {}
-1 8 };
-1 9
-1 10 /**
-1 11 * @param {string} name
-1 12 * @param {Array.<string>} deps
-1 13 * @param {Function} factory
-1 14 * @return {*}
-1 15 */
-1 16 var define = function(name, deps, factory) {};
-1 17
-1 18 /** @type {boolean} */
-1 19 define.amd = false;
diff --git a/.build/template.js b/.build/template.js
@@ -0,0 +1,49 @@
-1 1 (function(window, document, undefined) {
-1 2 var name = 'muu';
-1 3
-1 4 (function(factory) {
-1 5 if (typeof define === 'function' && define.amd) {
-1 6 define(name, ['lodash'], factory);
-1 7 } else {
-1 8 window[name] = factory(window._);
-1 9 }
-1 10 })(function(lodash) {
-1 11 var modules = {};
-1 12
-1 13 modules['muu-js-helpers'] = {
-1 14 instance: lodash
-1 15 };
-1 16
-1 17 var map = function(a, fn) {
-1 18 var b = [];
-1 19 for (var i = 0; i < a.length; i++) {
-1 20 b.push(fn(a[i]));
-1 21 }
-1 22 return b;
-1 23 };
-1 24
-1 25 var _define = function(name, deps, factory) {
-1 26 modules[name] = {
-1 27 deps: deps,
-1 28 factory: factory
-1 29 };
-1 30 };
-1 31
-1 32 var _require = function(name) {
-1 33 if (!modules[name]) {
-1 34 return undefined;
-1 35 }
-1 36
-1 37 if (!modules[name].instance) {
-1 38 var deps = modules[name].deps;
-1 39 var factory = modules[name].factory;
-1 40
-1 41 modules[name].instance = factory.apply(undefined, map(deps, _require));
-1 42 }
-1 43
-1 44 return modules[name].instance;
-1 45 };
-1 46
-1 47 return _require(name);
-1 48 });
-1 49 })(window, document, void 0);
diff --git a/.doc/tutorials/phonecat.md b/.doc/tutorials/phonecat.md
@@ -8,13 +8,12 @@ only muu related object you will ever have to create yourself is a *{@link
8 8 Registry}*. It will take care of creating directives for you. Here is how it
9 9 is done:
10 10
11 -1 require(['muu'], function(Muu) {
12 -1 var registry = new Muu();
-1 11 require(['muu'], function(muu) {
-1 12 var registry = new muu.Registry();
13 13 });
14 14
15 15 *For simplicaty, the calls to require will be left out of all following
16 -1 examples. `Muu` always refers to the `muu` module, `_` to `muu-js-helpers` and
17 -1 `$` to `muu-dom-helpers`.*
-1 16 examples.*
18 17
19 18 Once you have a registry, you can register *directives* with it. In order to do
20 19 that, you will need a *name*, a *template*, and a *link* function.
diff --git a/Makefile b/Makefile
@@ -1,13 +1,41 @@1 -1 muu.min.js: build.js src/*.js node_modules/requirejs/bin/r.js2 -1 ./node_modules/requirejs/bin/r.js -o build.js-1 1 dist/muu.js: JS := src/*.js -1 2 dist/muu-core.js: LODASH := 1 -1 3 dist/muu-core.js: JS := src/muu-directive.js src/muu-dom-helpers.js src/muu.js src/muu-registry.js src/muu-update-dom.js -1 4 -1 5 all: dist/muu.min.js dist/muu-core.min.js -1 6 -1 7 dist/%.min.js: dist/%.js node_modules/closure-compiler-jar/compiler.jar .build/externs.js -1 8 java -jar node_modules/closure-compiler-jar/compiler.jar \ -1 9 --compilation_level SIMPLE_OPTIMIZATIONS \ -1 10 --use_types_for_optimization \ -1 11 --warning_level=VERBOSE \ -1 12 --jscomp_warning=missingProperties \ -1 13 --jscomp_warning=checkTypes \ -1 14 --externs .build/externs.js \ -1 15 --js $< \ -1 16 --js_output_file $@ -1 17 -1 18 dist/%.js: .build/template.js src/*.js -1 19 mkdir -p dist -1 20 head -n -3 $< > .build/head.js -1 21 if [ -z ${LODASH} ]; then sed -i "s/'lodash'//g" .build/head.js; fi -1 22 tail -n 4 $< > .build/tail.js -1 23 cat ${JS} |\ -1 24 sed 's/^/ /g' |\ -1 25 sed 's/ *$$//g' |\ -1 26 sed 's/define(/_define(/g' > .build/modules.js -1 27 cat .build/head.js .build/modules.js .build/tail.js > $@ -1 28 rm .build/head.js -1 29 rm .build/tail.js -1 30 rm .build/modules.js 3 31 4 32 doc: doc/.touch 5 33 6 34 doc/.touch: src/*.js node_modules/jsdoc/jsdoc.js .doc/conf.json .doc/styles/* .doc/tutorials/*.md bower.json README.md 7 35 ./node_modules/jsdoc/jsdoc.js --pedantic --package bower.json -u .doc/tutorials -c .doc/conf.json README.md src/*.js && touch doc/.touch 8 369 -1 node_modules/requirejs/bin/r.js:10 -1 npm install requirejs-1 37 node_modules/closure-compiler-jar/compiler.jar: -1 38 npm install closure-compiler-jar 11 39 12 40 node_modules/jsdoc/jsdoc.js: 13 41 npm install jsdoc
diff --git a/README.md b/README.md
@@ -6,6 +6,30 @@ of angular may be added later on. 6 6 7 7 For an introduction to the concepts, see the {@tutorial phonecat} tutorial. 8 8 -1 9 # Getting started -1 10 -1 11 Muu can be installed with bower: -1 12 -1 13 bower install xi/muu -1 14 -1 15 It can be used either a AMD module or global variable. -1 16 -1 17 See [this -1 18 tutorial](https://github.com/xi/muu/blob/master/.doc/tutorials/phonecat.md) for -1 19 what you can do once muu is loaded. -1 20 -1 21 ## custom builds -1 22 -1 23 Not all parts of muu are required. This repository contains a minimal build -1 24 in `dist/muu-core.js`. It requires [lodash](https://lodash.com) and an external -1 25 templating system, e.g. [mustache.js](https://github.com/janl/mustache.js): -1 26 -1 27 var registry = new muu.Registry({ -1 28 renderer: Mustache.render -1 29 }); -1 30 -1 31 In addition, `muu.$location` is not available. -1 32 9 33 # history 10 34 11 35 I have worked a bit with [angular](https://angularjs.org/) and found it to be a
diff --git a/build.js b/build.js
@@ -1,8 +0,0 @@1 -1 ({2 -1 "baseUrl": "src",3 -1 "optimize": "uglify2",4 -1 "generateSourceMaps": true,5 -1 "preserveLicenseComments": false,6 -1 "name": "muu",7 -1 "out": "muu.min.js"8 -1 })
diff --git a/dist/muu-core.js b/dist/muu-core.js
@@ -0,0 +1,644 @@
-1 1 (function(window, document, undefined) {
-1 2 var name = 'muu';
-1 3
-1 4 (function(factory) {
-1 5 if (typeof define === 'function' && define.amd) {
-1 6 define(name, ['lodash'], factory);
-1 7 } else {
-1 8 window[name] = factory(window._);
-1 9 }
-1 10 })(function(lodash) {
-1 11 var modules = {};
-1 12
-1 13 modules['muu-js-helpers'] = {
-1 14 instance: lodash
-1 15 };
-1 16
-1 17 var map = function(a, fn) {
-1 18 var b = [];
-1 19 for (var i = 0; i < a.length; i++) {
-1 20 b.push(fn(a[i]));
-1 21 }
-1 22 return b;
-1 23 };
-1 24
-1 25 var _define = function(name, deps, factory) {
-1 26 modules[name] = {
-1 27 deps: deps,
-1 28 factory: factory
-1 29 };
-1 30 };
-1 31
-1 32 var _require = function(name) {
-1 33 if (!modules[name]) {
-1 34 return undefined;
-1 35 }
-1 36
-1 37 if (!modules[name].instance) {
-1 38 var deps = modules[name].deps;
-1 39 var factory = modules[name].factory;
-1 40
-1 41 modules[name].instance = factory.apply(undefined, map(deps, _require));
-1 42 }
-1 43
-1 44 return modules[name].instance;
-1 45 };
-1 46
-1 47 /**
-1 48 * Exports the {@link Directive} class.
-1 49 * @module muu-directive
-1 50 * @ignore
-1 51 */
-1 52 _define('muu-directive', ['muu-dom-helpers', 'muu-js-helpers', 'muu-update-dom'], function($, _, updateDOM) {
-1 53 "use strict";
-1 54
-1 55 /**
-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}.
-1 76 *
-1 77 * @constructs Directive
-1 78 * @param {Element} root
-1 79 * @param {string} template
-1 80 * @param {Registry} registry
-1 81 */
-1 82 var Directive = function(root, template, registry) {
-1 83 var self = this;
-1 84
-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 };
-1 95
-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 });
-1 115
-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 * @nosideeffects
-1 134 */
-1 135 this.querySelectorAll = function(selector) {
-1 136 var hits = root.querySelectorAll(selector);
-1 137
-1 138 // NOTE: querySelectorAll returns all elements in the tree that
-1 139 // match the given selector. findAll does the same with *relative
-1 140 // selectors* but does not seem to be available yet.
-1 141 var isolations = root.querySelectorAll('.muu-isolate');
-1 142 var isolated = _.union(_.map(isolations, function(isolation) {
-1 143 return isolation.querySelectorAll(selector);
-1 144 }));
-1 145
-1 146 return _.difference(hits, isolated);
-1 147 };
-1 148
-1 149 /**
-1 150 * A variant of `querySelector` that returns only elements from the
-1 151 * managed part of the DOM.
-1 152 *
-1 153 * @private
-1 154 * @param {String} selector
-1 155 * @return {Element} First child element that matches the given
-1 156 * selector and is not isolated.
-1 157 * @nosideeffects
-1 158 * @suppress {missingReturn}
-1 159 */
-1 160 this.querySelector = function(selector) {
-1 161 var all = self.querySelectorAll(selector);
-1 162 if (all.length > 0) {
-1 163 return all[0];
-1 164 }
-1 165 };
-1 166
-1 167 /**
-1 168 * Get all model data as a flat object.
-1 169 *
-1 170 * @return {Object.<string, string|number|boolean>}
-1 171 *//**
-1 172 * Get the value of a form input by name.
-1 173 *
-1 174 * In case of a checkbox, returns `boolean`.
-1 175 * In case of radioboxes, returns the value of the selected box.
-1 176 *
-1 177 * @param {string} name
-1 178 * @param {*} [_default]
-1 179 * @return {string|number|boolean|*}
-1 180 * @nosideeffects
-1 181 */
-1 182 this.getModel = function(name, _default) {
-1 183 if (name === undefined) {
-1 184 var model = {};
-1 185 _.forEach(self.querySelectorAll('[name]'), function(element) {
-1 186 model[element.name] = self.getModel(element.name);
-1 187 });
-1 188 return model;
-1 189 } else {
-1 190 var element = self.querySelector('[name=' + name + ']');
-1 191 if (element === undefined) {
-1 192 return _default;
-1 193 } else if (element.type === 'checkbox') {
-1 194 return element.checked;
-1 195 } else if (element.type === 'radio') {
-1 196 var options = self.querySelectorAll('[name=' + name + ']');
-1 197 return $.getRadio(options) || _default;
-1 198 } else {
-1 199 return element.value;
-1 200 }
-1 201 }
-1 202 };
-1 203
-1 204 /**
-1 205 * Set the value of a form input by name.
-1 206 *
-1 207 * In case of a checkbox, sets `element.checked`.
-1 208 * In case of radioboxes, selects the box with matching value.
-1 209 *
-1 210 * @param {string} name
-1 211 * @param {string|number|boolean} value
-1 212 */
-1 213 this.setModel = function(name, value) {
-1 214 var element = self.querySelector('[name=' + name + ']');
-1 215 if (element.type === 'checkbox') {
-1 216 element.checked = value;
-1 217 } else if (element.type === 'radio') {
-1 218 var options = self.querySelectorAll('[name=' + name + ']');
-1 219 $.setRadio(options, value);
-1 220 } else {
-1 221 element.value = value;
-1 222 }
-1 223 };
-1 224 };
-1 225
-1 226 return Directive;
-1 227 });
-1 228 /**
-1 229 * DOM related helper functions
-1 230 * @module muu-dom-helpers
-1 231 */
-1 232 _define("muu-dom-helpers", ['muu-js-helpers'], function(_) {
-1 233 "use strict";
-1 234
-1 235 var entityMap = {
-1 236 '&': '&',
-1 237 '<': '<',
-1 238 '>': '>',
-1 239 '"': '"',
-1 240 "'": ''',
-1 241 '/': '/'
-1 242 };
-1 243
-1 244 /** @lends module:muu-dom-helpers */
-1 245 var $ = {};
-1 246
-1 247 $.DELAY = 1000;
-1 248
-1 249 /**
-1 250 * @param {string} string
-1 251 * @return {string} - escaped HTML
-1 252 * @nosideeffects
-1 253 */
-1 254 $.escapeHtml = function(string) {
-1 255 return String(string).replace(/[&<>"'\/]/g, function(s) {
-1 256 return entityMap[s];
-1 257 });
-1 258 };
-1 259
-1 260 /**
-1 261 * Cross browser custom events.
-1 262 *
-1 263 * *Note*: IE does not seem to like it when you use existing event names
-1 264 * with this.
-1 265 *
-1 266 * @param {string} type
-1 267 * @param {*} detail
-1 268 * @return {Event}
-1 269 * @see https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events
-1 270 * @nosideeffects
-1 271 */
-1 272 $.createEvent = function(type, detail) {
-1 273 if (typeof CustomEvent === 'function') {
-1 274 return new CustomEvent(type, {
-1 275 detail: detail
-1 276 });
-1 277 } else {
-1 278 var event = document.createEvent('CustomEvent');
-1 279 event.initCustomEvent(type, false, true, detail);
-1 280 return event;
-1 281 }
-1 282 };
-1 283
-1 284 /**
-1 285 * @param {EventTarget} element
-1 286 * @param {string} eventName
-1 287 * @param {Function} callback
-1 288 * @return {function()} An unregister function
-1 289 */
-1 290 $.on = function(element, eventName, callback) {
-1 291 element.addEventListener(eventName, callback, false);
-1 292 return function() {
-1 293 element.removeEventListener(eventName, callback, false);
-1 294 };
-1 295 };
-1 296
-1 297 /**
-1 298 * @param {Function} fn
-1 299 * @return {function()} An unregister function
-1 300 */
-1 301 $.ready = function(fn) {
-1 302 var _fn = _.once(fn);
-1 303 if (document.readyState === 'complete') {
-1 304 _fn();
-1 305 return function() {};
-1 306 } else {
-1 307 var u1 = $.on(document, 'DOMContentLoaded', _fn);
-1 308 var u2 = $.on(window, 'load', _fn);
-1 309 return function() {
-1 310 u1();
-1 311 u2();
-1 312 };
-1 313 }
-1 314 };
-1 315
-1 316 /**
-1 317 * @param {Node} desc
-1 318 * @param {Node} root
-1 319 * @return {boolean}
-1 320 * @nosideeffects
-1 321 */
-1 322 $.isDescendant = function(desc, root) {
-1 323 return !!desc && (desc === root || $.isDescendant(desc.parentNode, root));
-1 324 };
-1 325
-1 326 /**
-1 327 * Execute a function when `element` is removed from the DOM.
-1 328 *
-1 329 * *Note*: The callback is not executed directly when (or even before) the
-1 330 * element is removed but with a slight delay. So the only way to test this
-1 331 * is to use a timeout in the test.
-1 332 *
-1 333 * @param {Element} element
-1 334 * @param {Function} fn
-1 335 * @return {function()} An unregister function
-1 336 */
-1 337 $.destroy = function(element, fn) {
-1 338 var unregister;
-1 339
-1 340 if (!!window.MutationObserver) {
-1 341 var observer = new MutationObserver(function() {
-1 342 if (!$.isDescendant(element, document)) {
-1 343 fn();
-1 344 unregister();
-1 345 }
-1 346 });
-1 347
-1 348 observer.observe(document, {
-1 349 childList: true,
-1 350 subtree: true
-1 351 });
-1 352
-1 353 unregister = _.once(function() {
-1 354 observer.disconnect();
-1 355 observer = undefined;
-1 356 });
-1 357 } else {
-1 358 var intervalID = setInterval(function() {
-1 359 if (!$.isDescendant(element, document)) {
-1 360 fn();
-1 361 unregister();
-1 362 }
-1 363 }, $.DELAY);
-1 364
-1 365 unregister = function() {
-1 366 clearInterval(intervalID);
-1 367 };
-1 368 }
-1 369
-1 370 return unregister;
-1 371 };
-1 372
-1 373 /**
-1 374 * @param {Array.<Element>} options
-1 375 * @return {string}
-1 376 * @suppress {missingReturn}
-1 377 * @nosideeffects
-1 378 */
-1 379 $.getRadio = function(options) {
-1 380 for (var i = 0; i < options.length; i++) {
-1 381 if (options[i].checked) {
-1 382 return options[i].value;
-1 383 }
-1 384 }
-1 385 };
-1 386
-1 387 /**
-1 388 * @param {Array.<Element>} options
-1 389 * @param {string} value
-1 390 */
-1 391 $.setRadio = function(options, value) {
-1 392 for (var i = 0; i < options.length; i++) {
-1 393 if (options[i].value === value) {
-1 394 options[i].checked = true;
-1 395 } else {
-1 396 options[i].checked = false;
-1 397 }
-1 398 }
-1 399 };
-1 400
-1 401 return $;
-1 402 });
-1 403 /**
-1 404 * This module gives access to the following objects:
-1 405 *
-1 406 * - `Registry` - {@link Registry}
-1 407 * - `$` - {@link module:muu-dom-helpers}
-1 408 * - `$location` - {@link module:muu-location}
-1 409 *
-1 410 * @module muu
-1 411 */
-1 412 _define('muu', ['muu-registry', 'muu-dom-helpers', 'muu-location'], function(Registry, $, $location) {
-1 413 "use strict";
-1 414
-1 415 var module = {};
-1 416
-1 417 module.Registry = Registry;
-1 418 module.$ = $;
-1 419 module.$location = $location;
-1 420
-1 421 return module;
-1 422 });
-1 423 /**
-1 424 * Exports the {@link Registry} class.
-1 425 * @module muu-registry
-1 426 * @ignore
-1 427 */
-1 428 _define('muu-registry', ['muu-template', 'muu-directive', 'muu-js-helpers', 'muu-dom-helpers'], function(muuTemplate, Directive, _, $) {
-1 429 "use strict";
-1 430
-1 431 /**
-1 432 * @constructs Registry
-1 433 * @param {Object} config The config object may have following properties:
-1 434 *
-1 435 * - **debug** - `{boolean}` - Enable debug mode. In debug mode,
-1 436 * directive objects are available as properties from the DOM as
-1 437 * `element.directive`.
-1 438 * - **renderer** - `{function(string, Object)}` - The template renderer
-1 439 * to be used. Defaults to {@link module:muu-template}.
-1 440 */
-1 441 var Registry = function(config) {
-1 442 var self = this;
-1 443 var directives = {};
-1 444
-1 445 this.config = config || {};
-1 446 this.renderer = self.config.renderer || muuTemplate;
-1 447
-1 448 /**
-1 449 * Register a new type of {@link Directive}
-1 450 *
-1 451 * @param {string} type
-1 452 * @param {string} template
-1 453 * @param {function(Directive, Element): Function} link The link
-1 454 * function is called with an instance of {@link Directive} and a
-1 455 * Element when {@link Registry#link} is executed.
-1 456 *
-1 457 * It is the only place where you can access a directive and
-1 458 * therefore the place where you define its behavior.
-1 459 *
-1 460 * This typically means to make an initial call to {@link
-1 461 * Directive#update} and to add some event listeners. You should also
-1 462 * return an *unlink* function that clears all external references in
-1 463 * order to avoid memory leaks.
-1 464 * @return {Registry} this
-1 465 */
-1 466 this.registerDirective = function(type, template, link) {
-1 467 directives[type] = {
-1 468 template: template,
-1 469 link: link
-1 470 };
-1 471 return self;
-1 472 };
-1 473
-1 474 /**
-1 475 * Shortcut for wrapping calls to {@link Registry} in a function.
-1 476 *
-1 477 * This can be esepcially helpful if that function is defined in a
-1 478 * different module.
-1 479 *
-1 480 * ```.js
-1 481 * _define('foobar', [], function() {
-1 482 * return function(registry) {
-1 483 * registry
-1 484 * .registerDirective('foo', '...', function() {...})
-1 485 * .registerDirective('bar', '...', function() {...});
-1 486 * };
-1 487 * });
-1 488 *
-1 489 * require(['foobar'], function(foobar) {
-1 490 * var registry = new Registry();
-1 491 * registry.registerModule(foobar);
-1 492 * });
-1 493 * ```
-1 494 *
-1 495 * @param {function(Registry)} module
-1 496 * @return {Registry} this
-1 497 */
-1 498 this.registerModule = function(module) {
-1 499 module(self);
-1 500 return self;
-1 501 };
-1 502
-1 503 /**
-1 504 * Create and initialise a {@link Directive} for `element`.
-1 505 *
-1 506 * @param {Element} element
-1 507 * @param {string} type
-1 508 * @return {Directive}
-1 509 */
-1 510 this.link = function(element, type) {
-1 511 if (type === undefined) {
-1 512 type = element.getAttribute('type');
-1 513 }
-1 514
-1 515 if (!directives.hasOwnProperty(type)) {
-1 516 throw new Error('Unknown directive type: ' + type);
-1 517 }
-1 518
-1 519 var template = directives[type].template;
-1 520 var link = directives[type].link;
-1 521
-1 522 var directive = new Directive(element, template, self);
-1 523 var unlink = link(directive, element);
-1 524 element.classList.add('muu-isolate');
-1 525 element.classList.add('muu-initialised');
-1 526
-1 527 if (self.config.debug) {
-1 528 element.directive = directive;
-1 529 }
-1 530
-1 531 if (unlink !== undefined) {
-1 532 $.destroy(element, unlink);
-1 533 }
-1 534
-1 535 return directive;
-1 536 };
-1 537
-1 538 /**
-1 539 * Link all directives that can be found inside `root`.
-1 540 *
-1 541 * @param {Element} root
-1 542 * @return {Array.<Directive>}
-1 543 */
-1 544 this.linkAll = function(root) {
-1 545 // NOTE: root may be a DOM Node or a directive
-1 546 var elements = _.filter(root.querySelectorAll('muu'), function(element) {
-1 547 return !element.classList.contains('muu-initialised');
-1 548 });
-1 549 return _.map(elements, function(element) {
-1 550 return self.link(element);
-1 551 });
-1 552 };
-1 553 };
-1 554
-1 555 return Registry;
-1 556 });
-1 557 /**
-1 558 * Recreate children of `source` in `target` by making only small adjustments.
-1 559 *
-1 560 * *The following section explains details about the current implementation.
-1 561 * These are likely to change in the future.*
-1 562 *
-1 563 * The algorithms is relatively simple. It just iterates through all top level
-1 564 * nodes. If a node has a different `nodeType` (e.g. text or element) or a
-1 565 * different `nodeName` (e.g. div or ul) it is replaced completely and the
-1 566 * algorithm proceeds with the node's children recursively. Otherwise, only
-1 567 * the nodes's attributes are updated.
-1 568 *
-1 569 * Note that non-attribute properties (e.g. value) are lost in the first case
-1 570 * and preserved in the second.
-1 571 *
-1 572 * If the algorithm encounters an element with the class `muu-isolate` it does
-1 573 * not recurse into its children. This way, you can protect dynamically
-1 574 * generated content from being overwritten.
-1 575 *
-1 576 * @module muu-update-dom
-1 577 * @param {Element} target
-1 578 * @param {Element} source
-1 579 */
-1 580 _define('muu-update-dom', ['muu-js-helpers'], function(_) {
-1 581 "use strict";
-1 582
-1 583 var updateAttributes = function(target, source) {
-1 584 var targetAttrNames = _.map(target.attributes, function(item) {
-1 585 return item.name;
-1 586 });
-1 587 var sourceAttrNames = _.map(source.attributes, function(item) {
-1 588 return item.name;
-1 589 });
-1 590
-1 591 _.forEach(targetAttrNames, function(name) {
-1 592 // NOTE: ie8.js creates some attribute
-1 593 if (!source.hasAttribute(name) && name.substr(0, 7) !== '__IE8__') {
-1 594 target.removeAttribute(name);
-1 595 }
-1 596 });
-1 597 _.forEach(sourceAttrNames, function(name) {
-1 598 if (target.getAttribute(name) !== source.getAttribute(name)) {
-1 599 target.setAttribute(name, source.getAttribute(name));
-1 600 }
-1 601 });
-1 602 };
-1 603
-1 604 var updateDOM = function(target, source) {
-1 605 var nt = target.childNodes.length;
-1 606 var ns = source.childNodes.length;
-1 607
-1 608 for (var i = ns; i < nt; i++) {
-1 609 target.removeChild(target.childNodes[ns]);
-1 610 }
-1 611 for (i = nt; i < ns; i++) {
-1 612 target.appendChild(source.childNodes[nt]);
-1 613 }
-1 614 for (i = 0; i < nt && i < ns; i++) {
-1 615 var tchild = target.childNodes[i];
-1 616 var schild = source.childNodes[i];
-1 617
-1 618 if (tchild.nodeType === schild.nodeType && tchild.nodeName === schild.nodeName && tchild.type === schild.type) {
-1 619 if (tchild.nodeType === 1) {
-1 620 var muuClasses = _.filter(tchild.classList, function(cls) {
-1 621 return cls.lastIndexOf('muu-', 0) === 0;
-1 622 });
-1 623 updateAttributes(tchild, schild);
-1 624 _.forEach(muuClasses, function(cls) {
-1 625 tchild.classList.add(cls);
-1 626 });
-1 627 } else if (tchild.nodeType === 3) {
-1 628 tchild.nodeValue = schild.nodeValue;
-1 629 }
-1 630 if (tchild.nodeType !== 3 && !tchild.classList.contains('muu-isolate')) {
-1 631 updateDOM(tchild, schild);
-1 632 }
-1 633 } else {
-1 634 tchild.parentNode.replaceChild(schild, tchild);
-1 635 }
-1 636 }
-1 637 };
-1 638
-1 639 return updateDOM;
-1 640 });
-1 641
-1 642 return _require(name);
-1 643 });
-1 644 })(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
@@ -0,0 +1,1277 @@
-1 1 (function(window, document, undefined) {
-1 2 var name = 'muu';
-1 3
-1 4 (function(factory) {
-1 5 if (typeof define === 'function' && define.amd) {
-1 6 define(name, [], factory);
-1 7 } else {
-1 8 window[name] = factory(window._);
-1 9 }
-1 10 })(function(lodash) {
-1 11 var modules = {};
-1 12
-1 13 modules['muu-js-helpers'] = {
-1 14 instance: lodash
-1 15 };
-1 16
-1 17 var map = function(a, fn) {
-1 18 var b = [];
-1 19 for (var i = 0; i < a.length; i++) {
-1 20 b.push(fn(a[i]));
-1 21 }
-1 22 return b;
-1 23 };
-1 24
-1 25 var _define = function(name, deps, factory) {
-1 26 modules[name] = {
-1 27 deps: deps,
-1 28 factory: factory
-1 29 };
-1 30 };
-1 31
-1 32 var _require = function(name) {
-1 33 if (!modules[name]) {
-1 34 return undefined;
-1 35 }
-1 36
-1 37 if (!modules[name].instance) {
-1 38 var deps = modules[name].deps;
-1 39 var factory = modules[name].factory;
-1 40
-1 41 modules[name].instance = factory.apply(undefined, map(deps, _require));
-1 42 }
-1 43
-1 44 return modules[name].instance;
-1 45 };
-1 46
-1 47 /**
-1 48 * Exports the {@link Directive} class.
-1 49 * @module muu-directive
-1 50 * @ignore
-1 51 */
-1 52 _define('muu-directive', ['muu-dom-helpers', 'muu-js-helpers', 'muu-update-dom'], function($, _, updateDOM) {
-1 53 "use strict";
-1 54
-1 55 /**
-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}.
-1 76 *
-1 77 * @constructs Directive
-1 78 * @param {Element} root
-1 79 * @param {string} template
-1 80 * @param {Registry} registry
-1 81 */
-1 82 var Directive = function(root, template, registry) {
-1 83 var self = this;
-1 84
-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 };
-1 95
-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 });
-1 115
-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 * @nosideeffects
-1 134 */
-1 135 this.querySelectorAll = function(selector) {
-1 136 var hits = root.querySelectorAll(selector);
-1 137
-1 138 // NOTE: querySelectorAll returns all elements in the tree that
-1 139 // match the given selector. findAll does the same with *relative
-1 140 // selectors* but does not seem to be available yet.
-1 141 var isolations = root.querySelectorAll('.muu-isolate');
-1 142 var isolated = _.union(_.map(isolations, function(isolation) {
-1 143 return isolation.querySelectorAll(selector);
-1 144 }));
-1 145
-1 146 return _.difference(hits, isolated);
-1 147 };
-1 148
-1 149 /**
-1 150 * A variant of `querySelector` that returns only elements from the
-1 151 * managed part of the DOM.
-1 152 *
-1 153 * @private
-1 154 * @param {String} selector
-1 155 * @return {Element} First child element that matches the given
-1 156 * selector and is not isolated.
-1 157 * @nosideeffects
-1 158 * @suppress {missingReturn}
-1 159 */
-1 160 this.querySelector = function(selector) {
-1 161 var all = self.querySelectorAll(selector);
-1 162 if (all.length > 0) {
-1 163 return all[0];
-1 164 }
-1 165 };
-1 166
-1 167 /**
-1 168 * Get all model data as a flat object.
-1 169 *
-1 170 * @return {Object.<string, string|number|boolean>}
-1 171 *//**
-1 172 * Get the value of a form input by name.
-1 173 *
-1 174 * In case of a checkbox, returns `boolean`.
-1 175 * In case of radioboxes, returns the value of the selected box.
-1 176 *
-1 177 * @param {string} name
-1 178 * @param {*} [_default]
-1 179 * @return {string|number|boolean|*}
-1 180 * @nosideeffects
-1 181 */
-1 182 this.getModel = function(name, _default) {
-1 183 if (name === undefined) {
-1 184 var model = {};
-1 185 _.forEach(self.querySelectorAll('[name]'), function(element) {
-1 186 model[element.name] = self.getModel(element.name);
-1 187 });
-1 188 return model;
-1 189 } else {
-1 190 var element = self.querySelector('[name=' + name + ']');
-1 191 if (element === undefined) {
-1 192 return _default;
-1 193 } else if (element.type === 'checkbox') {
-1 194 return element.checked;
-1 195 } else if (element.type === 'radio') {
-1 196 var options = self.querySelectorAll('[name=' + name + ']');
-1 197 return $.getRadio(options) || _default;
-1 198 } else {
-1 199 return element.value;
-1 200 }
-1 201 }
-1 202 };
-1 203
-1 204 /**
-1 205 * Set the value of a form input by name.
-1 206 *
-1 207 * In case of a checkbox, sets `element.checked`.
-1 208 * In case of radioboxes, selects the box with matching value.
-1 209 *
-1 210 * @param {string} name
-1 211 * @param {string|number|boolean} value
-1 212 */
-1 213 this.setModel = function(name, value) {
-1 214 var element = self.querySelector('[name=' + name + ']');
-1 215 if (element.type === 'checkbox') {
-1 216 element.checked = value;
-1 217 } else if (element.type === 'radio') {
-1 218 var options = self.querySelectorAll('[name=' + name + ']');
-1 219 $.setRadio(options, value);
-1 220 } else {
-1 221 element.value = value;
-1 222 }
-1 223 };
-1 224 };
-1 225
-1 226 return Directive;
-1 227 });
-1 228 /**
-1 229 * DOM related helper functions
-1 230 * @module muu-dom-helpers
-1 231 */
-1 232 _define("muu-dom-helpers", ['muu-js-helpers'], function(_) {
-1 233 "use strict";
-1 234
-1 235 var entityMap = {
-1 236 '&': '&',
-1 237 '<': '<',
-1 238 '>': '>',
-1 239 '"': '"',
-1 240 "'": ''',
-1 241 '/': '/'
-1 242 };
-1 243
-1 244 /** @lends module:muu-dom-helpers */
-1 245 var $ = {};
-1 246
-1 247 $.DELAY = 1000;
-1 248
-1 249 /**
-1 250 * @param {string} string
-1 251 * @return {string} - escaped HTML
-1 252 * @nosideeffects
-1 253 */
-1 254 $.escapeHtml = function(string) {
-1 255 return String(string).replace(/[&<>"'\/]/g, function(s) {
-1 256 return entityMap[s];
-1 257 });
-1 258 };
-1 259
-1 260 /**
-1 261 * Cross browser custom events.
-1 262 *
-1 263 * *Note*: IE does not seem to like it when you use existing event names
-1 264 * with this.
-1 265 *
-1 266 * @param {string} type
-1 267 * @param {*} detail
-1 268 * @return {Event}
-1 269 * @see https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events
-1 270 * @nosideeffects
-1 271 */
-1 272 $.createEvent = function(type, detail) {
-1 273 if (typeof CustomEvent === 'function') {
-1 274 return new CustomEvent(type, {
-1 275 detail: detail
-1 276 });
-1 277 } else {
-1 278 var event = document.createEvent('CustomEvent');
-1 279 event.initCustomEvent(type, false, true, detail);
-1 280 return event;
-1 281 }
-1 282 };
-1 283
-1 284 /**
-1 285 * @param {EventTarget} element
-1 286 * @param {string} eventName
-1 287 * @param {Function} callback
-1 288 * @return {function()} An unregister function
-1 289 */
-1 290 $.on = function(element, eventName, callback) {
-1 291 element.addEventListener(eventName, callback, false);
-1 292 return function() {
-1 293 element.removeEventListener(eventName, callback, false);
-1 294 };
-1 295 };
-1 296
-1 297 /**
-1 298 * @param {Function} fn
-1 299 * @return {function()} An unregister function
-1 300 */
-1 301 $.ready = function(fn) {
-1 302 var _fn = _.once(fn);
-1 303 if (document.readyState === 'complete') {
-1 304 _fn();
-1 305 return function() {};
-1 306 } else {
-1 307 var u1 = $.on(document, 'DOMContentLoaded', _fn);
-1 308 var u2 = $.on(window, 'load', _fn);
-1 309 return function() {
-1 310 u1();
-1 311 u2();
-1 312 };
-1 313 }
-1 314 };
-1 315
-1 316 /**
-1 317 * @param {Node} desc
-1 318 * @param {Node} root
-1 319 * @return {boolean}
-1 320 * @nosideeffects
-1 321 */
-1 322 $.isDescendant = function(desc, root) {
-1 323 return !!desc && (desc === root || $.isDescendant(desc.parentNode, root));
-1 324 };
-1 325
-1 326 /**
-1 327 * Execute a function when `element` is removed from the DOM.
-1 328 *
-1 329 * *Note*: The callback is not executed directly when (or even before) the
-1 330 * element is removed but with a slight delay. So the only way to test this
-1 331 * is to use a timeout in the test.
-1 332 *
-1 333 * @param {Element} element
-1 334 * @param {Function} fn
-1 335 * @return {function()} An unregister function
-1 336 */
-1 337 $.destroy = function(element, fn) {
-1 338 var unregister;
-1 339
-1 340 if (!!window.MutationObserver) {
-1 341 var observer = new MutationObserver(function() {
-1 342 if (!$.isDescendant(element, document)) {
-1 343 fn();
-1 344 unregister();
-1 345 }
-1 346 });
-1 347
-1 348 observer.observe(document, {
-1 349 childList: true,
-1 350 subtree: true
-1 351 });
-1 352
-1 353 unregister = _.once(function() {
-1 354 observer.disconnect();
-1 355 observer = undefined;
-1 356 });
-1 357 } else {
-1 358 var intervalID = setInterval(function() {
-1 359 if (!$.isDescendant(element, document)) {
-1 360 fn();
-1 361 unregister();
-1 362 }
-1 363 }, $.DELAY);
-1 364
-1 365 unregister = function() {
-1 366 clearInterval(intervalID);
-1 367 };
-1 368 }
-1 369
-1 370 return unregister;
-1 371 };
-1 372
-1 373 /**
-1 374 * @param {Array.<Element>} options
-1 375 * @return {string}
-1 376 * @suppress {missingReturn}
-1 377 * @nosideeffects
-1 378 */
-1 379 $.getRadio = function(options) {
-1 380 for (var i = 0; i < options.length; i++) {
-1 381 if (options[i].checked) {
-1 382 return options[i].value;
-1 383 }
-1 384 }
-1 385 };
-1 386
-1 387 /**
-1 388 * @param {Array.<Element>} options
-1 389 * @param {string} value
-1 390 */
-1 391 $.setRadio = function(options, value) {
-1 392 for (var i = 0; i < options.length; i++) {
-1 393 if (options[i].value === value) {
-1 394 options[i].checked = true;
-1 395 } else {
-1 396 options[i].checked = false;
-1 397 }
-1 398 }
-1 399 };
-1 400
-1 401 return $;
-1 402 });
-1 403 /**
-1 404 * Minimal implementation of an underscore/lodash subset.
-1 405 * @module muu-js-helpers
-1 406 */
-1 407 _define('muu-js-helpers', [], function() {
-1 408 "use strict";
-1 409
-1 410 /** @lends module:muu-js-helpers */
-1 411 var _ = {};
-1 412
-1 413 /**
-1 414 * @param {Object} value
-1 415 * @return {string}
-1 416 * @nosideeffects
-1 417 */
-1 418 var objToString = function(value) {
-1 419 return Object.prototype.toString.call(value);
-1 420 };
-1 421
-1 422 /**
-1 423 * @param {*} value
-1 424 * @return {boolean}
-1 425 * @nosideeffects
-1 426 */
-1 427 _.isString = function(value) {
-1 428 return typeof value === 'string' || objToString(value) === '[object String]';
-1 429 };
-1 430
-1 431 /**
-1 432 * @function
-1 433 * @param {*} value
-1 434 * @return {boolean}
-1 435 * @nosideeffects
-1 436 */
-1 437 _.isArray = Array.isArray;
-1 438
-1 439 /**
-1 440 * @param {*} value
-1 441 * @return {boolean}
-1 442 * @nosideeffects
-1 443 */
-1 444 _.isFunction = function(value) {
-1 445 return typeof value === 'function';
-1 446 };
-1 447
-1 448 /**
-1 449 * @param {Function} fn
-1 450 * @return {Function}
-1 451 * @nosideeffects
-1 452 */
-1 453 _.once = function(fn) {
-1 454 var result;
-1 455 var called = false;
-1 456
-1 457 return function() {
-1 458 if (!called) {
-1 459 result = fn.apply(this, arguments);
-1 460 called = true;
-1 461 }
-1 462 return result;
-1 463 };
-1 464 };
-1 465
-1 466 /**
-1 467 * @param {Array} array
-1 468 * @param {*} value
-1 469 * @return {number}
-1 470 * @nosideeffects
-1 471 */
-1 472 _.indexOf = function(array, value) {
-1 473 if ('indexOf' in array) {
-1 474 return array.indexOf(value);
-1 475 }
-1 476
-1 477 for (var i = 0; i < array.length; i++) {
-1 478 if (array[i] === value) {
-1 479 return i;
-1 480 }
-1 481 }
-1 482 return -1;
-1 483 };
-1 484
-1 485 /**
-1 486 * @param {Array} array
-1 487 * @param {Function} fn
-1 488 */
-1 489 _.forEach = function(array, fn) {
-1 490 if ('forEach' in array) {
-1 491 return array.forEach(fn);
-1 492 }
-1 493
-1 494 for (var i = 0; i < array.length; i++) {
-1 495 fn(array[i]);
-1 496 }
-1 497 };
-1 498
-1 499 /**
-1 500 * @param {Array} array
-1 501 * @param {Function} fn
-1 502 * @return {Array}
-1 503 * @nosideeffects
-1 504 */
-1 505 _.map = function(array, fn) {
-1 506 if ('map' in array) {
-1 507 return array.map(fn);
-1 508 }
-1 509
-1 510 var results = [];
-1 511 for (var i = 0; i < array.length; i++) {
-1 512 results.push(fn(array[i]));
-1 513 }
-1 514 return results;
-1 515 };
-1 516
-1 517 /**
-1 518 * @param {Array} array
-1 519 * @param {Function} fn
-1 520 * @return {Array}
-1 521 * @nosideeffects
-1 522 */
-1 523 _.filter = function(array, fn) {
-1 524 if ('filter' in array) {
-1 525 return array.filter(fn);
-1 526 }
-1 527
-1 528 var results = [];
-1 529 for (var i = 0; i < array.length; i++) {
-1 530 if (fn(array[i])) {
-1 531 results.push(array[i]);
-1 532 }
-1 533 }
-1 534 return results;
-1 535 };
-1 536
-1 537 /**
-1 538 * @param {Array.<Array>} arrays
-1 539 * @return {Array}
-1 540 * @nosideeffects
-1 541 */
-1 542 _.union = function(arrays) {
-1 543 var results = [];
-1 544 for (var i = 0; i < arrays.length; i++) {
-1 545 for (var j = 0; j < arrays[i].length; j++) {
-1 546 if (_.indexOf(results, arrays[i][j]) === -1) {
-1 547 results.push(arrays[i][j]);
-1 548 }
-1 549 }
-1 550 }
-1 551 return results;
-1 552 };
-1 553
-1 554 /**
-1 555 * @param {Array} a
-1 556 * @param {Array} b
-1 557 * @return {Array}
-1 558 * @nosideeffects
-1 559 */
-1 560 _.difference = function(a, b) {
-1 561 var results = [];
-1 562 for (var i = 0; i < a.length; i++) {
-1 563 if (_.indexOf(b, a[i]) === -1) {
-1 564 results.push(a[i]);
-1 565 }
-1 566 }
-1 567 return results;
-1 568 };
-1 569
-1 570 /**
-1 571 * @param {Array} a
-1 572 * @return {Array}
-1 573 * @nosideeffects
-1 574 */
-1 575 _.flatten = function(a) {
-1 576 var o = [];
-1 577 _.forEach(a, function(item) {
-1 578 if (_.isArray(item)) {
-1 579 o = o.concat(_.flatten(item));
-1 580 } else {
-1 581 o.push(item);
-1 582 }
-1 583 });
-1 584 return o;
-1 585 };
-1 586
-1 587 return _;
-1 588 });
-1 589 /**
-1 590 * angular inspired location service.
-1 591 * @module muu-location
-1 592 */
-1 593 _define('muu-location', ['muu-search'], function(q) {
-1 594 "use strict";
-1 595
-1 596 /** @lends module:muu-location */
-1 597 var loc = {};
-1 598
-1 599 /**
-1 600 * @return {string}
-1 601 * @nosideeffects
-1 602 */
-1 603 loc.absUrl = function() {
-1 604 return location.href;
-1 605 };
-1 606
-1 607 /**
-1 608 * @return {string}
-1 609 * @nosideeffects
-1 610 *//**
-1 611 * @param {string} value
-1 612 * @param {boolean} [replace]
-1 613 * @return {muu-location}
-1 614 */
-1 615 loc.url = function(value, replace) {
-1 616 if (value === undefined) {
-1 617 return location.pathname + location.search + location.hash;
-1 618 } else if (replace) {
-1 619 history.replaceState(null, null, value);
-1 620 } else {
-1 621 history.pushState(null, null, value);
-1 622 }
-1 623 return loc;
-1 624 };
-1 625
-1 626 /**
-1 627 * @return {string}
-1 628 * @nosideeffects
-1 629 */
-1 630 loc.protocol = function() {
-1 631 return location.protocol;
-1 632 };
-1 633
-1 634 /**
-1 635 * @return {string}
-1 636 * @nosideeffects
-1 637 */
-1 638 loc.host = function() {
-1 639 return location.host;
-1 640 };
-1 641
-1 642 /**
-1 643 * @return {string}
-1 644 * @nosideeffects
-1 645 */
-1 646 loc.port = function() {
-1 647 return location.port;
-1 648 };
-1 649
-1 650 /**
-1 651 * @return {string}
-1 652 * @nosideeffects
-1 653 *//**
-1 654 * @param {string} value
-1 655 * @param {boolean} [replace]
-1 656 * @return {muu-location}
-1 657 */
-1 658 loc.path = function(value, replace) {
-1 659 if (value === undefined) {
-1 660 return location.pathname;
-1 661 } else {
-1 662 var url = value + location.search + location.hash;
-1 663 loc.url(url, replace);
-1 664 return loc;
-1 665 }
-1 666 };
-1 667
-1 668 var _search = function(value, replace) {
-1 669 if (value === undefined) {
-1 670 return location.search;
-1 671 } else {
-1 672 if (value && value[0] !== '?') {
-1 673 value = '?' + value;
-1 674 }
-1 675 if (value.length === 1) {
-1 676 value = '';
-1 677 }
-1 678
-1 679 var url = location.pathname + value + location.hash;
-1 680 loc.url(url, replace);
-1 681 return loc;
-1 682 }
-1 683 };
-1 684
-1 685 /**
-1 686 * @return {Object}
-1 687 * @nosideeffects
-1 688 *//**
-1 689 * @param {string|object} value
-1 690 * @return {muu-location}
-1 691 *//**
-1 692 * @param {string} key
-1 693 * @param {*} value
-1 694 * @param {boolean} [replace]
-1 695 * @return {muu-location}
-1 696 */
-1 697 loc.search = function(key, value, replace) {
-1 698 if (key !== undefined) {
-1 699 if (value !== undefined) {
-1 700 var search = q.parse(_search());
-1 701 search[key] = value;
-1 702 return _search(q.unparse(search), replace);
-1 703 } else {
-1 704 return _search(q.unparse(key), replace);
-1 705 }
-1 706 } else {
-1 707 return q.parse(_search());
-1 708 }
-1 709 };
-1 710
-1 711 /**
-1 712 * @return {string}
-1 713 * @nosideeffects
-1 714 *//**
-1 715 * @param {string} value
-1 716 * @param {boolean} [replace]
-1 717 * @return {muu-location}
-1 718 */
-1 719 loc.hash = function(value, replace) {
-1 720 if (value === undefined) {
-1 721 if (location.hash) {
-1 722 return location.hash.slice(1);
-1 723 } else {
-1 724 return '';
-1 725 }
-1 726 } else {
-1 727 var url = location.pathname + location.search + '#' + value;
-1 728 loc.url(url, replace);
-1 729 return loc;
-1 730 }
-1 731 };
-1 732
-1 733 /**
-1 734 * @param {string} eventName
-1 735 * @param {Function} fn
-1 736 * @return {muu-location}
-1 737 */
-1 738 loc.addEventListener = function(eventName, fn) {
-1 739 if (eventName === 'change') {
-1 740 window.addEventListener('popstate', fn, false);
-1 741 }
-1 742 return loc;
-1 743 };
-1 744
-1 745 /**
-1 746 * @param {string} eventName
-1 747 * @param {Function} fn
-1 748 * @return {muu-location}
-1 749 */
-1 750 loc.removeEventListener = function(eventName, fn) {
-1 751 if (eventName === 'change') {
-1 752 window.removeEventListener('popstate', fn, false);
-1 753 }
-1 754 return loc;
-1 755 };
-1 756
-1 757 return loc;
-1 758 });
-1 759 /**
-1 760 * Exports the {@link Registry} class.
-1 761 * @module muu-registry
-1 762 * @ignore
-1 763 */
-1 764 _define('muu-registry', ['muu-template', 'muu-directive', 'muu-js-helpers', 'muu-dom-helpers'], function(muuTemplate, Directive, _, $) {
-1 765 "use strict";
-1 766
-1 767 /**
-1 768 * @constructs Registry
-1 769 * @param {Object} config The config object may have following properties:
-1 770 *
-1 771 * - **debug** - `{boolean}` - Enable debug mode. In debug mode,
-1 772 * directive objects are available as properties from the DOM as
-1 773 * `element.directive`.
-1 774 * - **renderer** - `{function(string, Object)}` - The template renderer
-1 775 * to be used. Defaults to {@link module:muu-template}.
-1 776 */
-1 777 var Registry = function(config) {
-1 778 var self = this;
-1 779 var directives = {};
-1 780
-1 781 this.config = config || {};
-1 782 this.renderer = self.config.renderer || muuTemplate;
-1 783
-1 784 /**
-1 785 * Register a new type of {@link Directive}
-1 786 *
-1 787 * @param {string} type
-1 788 * @param {string} template
-1 789 * @param {function(Directive, Element): Function} link The link
-1 790 * function is called with an instance of {@link Directive} and a
-1 791 * Element when {@link Registry#link} is executed.
-1 792 *
-1 793 * It is the only place where you can access a directive and
-1 794 * therefore the place where you define its behavior.
-1 795 *
-1 796 * This typically means to make an initial call to {@link
-1 797 * Directive#update} and to add some event listeners. You should also
-1 798 * return an *unlink* function that clears all external references in
-1 799 * order to avoid memory leaks.
-1 800 * @return {Registry} this
-1 801 */
-1 802 this.registerDirective = function(type, template, link) {
-1 803 directives[type] = {
-1 804 template: template,
-1 805 link: link
-1 806 };
-1 807 return self;
-1 808 };
-1 809
-1 810 /**
-1 811 * Shortcut for wrapping calls to {@link Registry} in a function.
-1 812 *
-1 813 * This can be esepcially helpful if that function is defined in a
-1 814 * different module.
-1 815 *
-1 816 * ```.js
-1 817 * _define('foobar', [], function() {
-1 818 * return function(registry) {
-1 819 * registry
-1 820 * .registerDirective('foo', '...', function() {...})
-1 821 * .registerDirective('bar', '...', function() {...});
-1 822 * };
-1 823 * });
-1 824 *
-1 825 * require(['foobar'], function(foobar) {
-1 826 * var registry = new Registry();
-1 827 * registry.registerModule(foobar);
-1 828 * });
-1 829 * ```
-1 830 *
-1 831 * @param {function(Registry)} module
-1 832 * @return {Registry} this
-1 833 */
-1 834 this.registerModule = function(module) {
-1 835 module(self);
-1 836 return self;
-1 837 };
-1 838
-1 839 /**
-1 840 * Create and initialise a {@link Directive} for `element`.
-1 841 *
-1 842 * @param {Element} element
-1 843 * @param {string} type
-1 844 * @return {Directive}
-1 845 */
-1 846 this.link = function(element, type) {
-1 847 if (type === undefined) {
-1 848 type = element.getAttribute('type');
-1 849 }
-1 850
-1 851 if (!directives.hasOwnProperty(type)) {
-1 852 throw new Error('Unknown directive type: ' + type);
-1 853 }
-1 854
-1 855 var template = directives[type].template;
-1 856 var link = directives[type].link;
-1 857
-1 858 var directive = new Directive(element, template, self);
-1 859 var unlink = link(directive, element);
-1 860 element.classList.add('muu-isolate');
-1 861 element.classList.add('muu-initialised');
-1 862
-1 863 if (self.config.debug) {
-1 864 element.directive = directive;
-1 865 }
-1 866
-1 867 if (unlink !== undefined) {
-1 868 $.destroy(element, unlink);
-1 869 }
-1 870
-1 871 return directive;
-1 872 };
-1 873
-1 874 /**
-1 875 * Link all directives that can be found inside `root`.
-1 876 *
-1 877 * @param {Element} root
-1 878 * @return {Array.<Directive>}
-1 879 */
-1 880 this.linkAll = function(root) {
-1 881 // NOTE: root may be a DOM Node or a directive
-1 882 var elements = _.filter(root.querySelectorAll('muu'), function(element) {
-1 883 return !element.classList.contains('muu-initialised');
-1 884 });
-1 885 return _.map(elements, function(element) {
-1 886 return self.link(element);
-1 887 });
-1 888 };
-1 889 };
-1 890
-1 891 return Registry;
-1 892 });
-1 893 _define('muu-search', ['muu-js-helpers'], function(_) {
-1 894 "use strict";
-1 895
-1 896 var q = {};
-1 897
-1 898 q.parse = function(s) {
-1 899 var q = {};
-1 900
-1 901 var set = function(key, value) {
-1 902 if (!q.hasOwnProperty(key)) {
-1 903 q[key] = value;
-1 904 } else if (_.isArray(q[key])) {
-1 905 q[key].push(value);
-1 906 } else {
-1 907 q[key] = [q[key], value];
-1 908 }
-1 909 };
-1 910
-1 911 _.forEach(s.substring(1).split('&'), function(item) {
-1 912 var i = _.map(item.split('='), decodeURIComponent);
-1 913 if (i.length === 2) {
-1 914 set(i[0], i[1]);
-1 915 } else if (i[0]) {
-1 916 set(i[0], true);
-1 917 }
-1 918 });
-1 919 return q;
-1 920 };
-1 921
-1 922 var unparseItem = function(key, value) {
-1 923 if (value === undefined || value === null || value === false) {
-1 924 return [];
-1 925 } else if (_.isArray(value)) {
-1 926 return _.flatten(_.map(value, function(v) {
-1 927 return unparseItem(key, v);
-1 928 }));
-1 929 } else if (value === true) {
-1 930 return [encodeURIComponent(key)];
-1 931 } else {
-1 932 return [encodeURIComponent(key) + '=' + encodeURIComponent(value)];
-1 933 }
-1 934 };
-1 935
-1 936 q.unparse = function(q) {
-1 937 if (_.isString(q)) {
-1 938 return q;
-1 939 }
-1 940
-1 941 var a = []
-1 942 for (var key in q) {
-1 943 if (q.hasOwnProperty(key)) {
-1 944 a = a.concat(unparseItem(key, q[key]));
-1 945 }
-1 946 }
-1 947 if (a.length > 0) {
-1 948 return '?' + a.join('&');
-1 949 } else {
-1 950 return '';
-1 951 }
-1 952 };
-1 953
-1 954 return q;
-1 955 });
-1 956 /**
-1 957 * minimal mustache insipred templating
-1 958 *
-1 959 * ## Variables
-1 960 *
-1 961 * Variables are created with a `{{name}}` tag. These are always escaped.
-1 962 *
-1 963 * ## Loops
-1 964 *
-1 965 * Loops render blocks of text a number of times, depending on the value of
-1 966 * the key in the current context.
-1 967 *
-1 968 * A loop begins with a pound and ends with a slash. That is, `{{#person}}`
-1 969 * begins a "person" section while `{{/person}}` ends it.
-1 970 *
-1 971 * If the value is an array, the block is repeated for each item in that array.
-1 972 * In any other case, the block is rendered with the outer scope, but only if
-1 973 * the value is truthy.
-1 974 *
-1 975 * ## Inverted loops
-1 976 *
-1 977 * Inverted loops render blocks of test if the value of the key is falsy. They
-1 978 * begin with a caret.
-1 979 *
-1 980 * ## Comments
-1 981 *
-1 982 * Comments begin with a bang and are ignored.
-1 983 *
-1 984 * ## Pairs
-1 985 *
-1 986 * Pairs look like JSON objects. The result is a space separated list of all
-1 987 * keys with truthy values.
-1 988 *
-1 989 * ```
-1 990 * muuTemplate('{{foo: var1, bar: var2, baz: var3}}', {
-1 991 * var1: true,
-1 992 * var2: false,
-1 993 * var3: true
-1 994 * }); // 'foo baz'
-1 995 * ```
-1 996 *
-1 997 * ## Special variable `this`
-1 998 *
-1 999 * `this` always refers to the current context. So the following expressions
-1 1000 * are equivalent:
-1 1001 *
-1 1002 * ```
-1 1003 * muuTemplate('{{#items}}{{content}}{{/items}}', {
-1 1004 * item: [{
-1 1005 * content: 1
-1 1006 * }, {
-1 1007 * content: 2
-1 1008 * }]
-1 1009 * });
-1 1010 *
-1 1011 * muuTemplate('{{#this}}{{this}}{{/this}}', [1, 2]);
-1 1012 * ```
-1 1013 *
-1 1014 * @module muu-template
-1 1015 * @param {string} template
-1 1016 * @param {Object} data
-1 1017 * @return {string}
-1 1018 * @nosideeffects
-1 1019 */
-1 1020 _define('muu-template', ['muu-js-helpers', 'muu-dom-helpers'], function(_, $) {
-1 1021 "use strict";
-1 1022
-1 1023 var openTag = '{{';
-1 1024 var closeTag = '}}';
-1 1025
-1 1026 var getValue = function(key, data) {
-1 1027 return key === 'this' ? data : data[key];
-1 1028 };
-1 1029
-1 1030 var parseVariableTemplate = function(template) {
-1 1031 var content = template.slice(2, -2);
-1 1032
-1 1033 if (template.indexOf(':') === -1) {
-1 1034 return function(data) {
-1 1035 return $.escapeHtml(getValue(content, data) || '');
-1 1036 };
-1 1037 } else {
-1 1038 var pairs = content.split(',').map(function(pair) {
-1 1039 var v = pair.split(':');
-1 1040 var key = v[0].trim();
-1 1041 var value = v.slice(1).join(':').trim();
-1 1042 return [key, value];
-1 1043 });
-1 1044
-1 1045 return function(data) {
-1 1046 var results = [];
-1 1047
-1 1048 for (var i = 0; i < pairs.length; i++) {
-1 1049 var key = pairs[i][0];
-1 1050 var value = pairs[i][1];
-1 1051
-1 1052 if (getValue(value, data)) {
-1 1053 results.push(key);
-1 1054 }
-1 1055 }
-1 1056
-1 1057 return $.escapeHtml(results.join(' '));
-1 1058 };
-1 1059 }
-1 1060 };
-1 1061
-1 1062 var parseLoopTemplate = function(tag, afterTag, inverted) {
-1 1063 var tagName = tag.slice(3, -2);
-1 1064
-1 1065 var v = parseTemplate(afterTag, tagName);
-1 1066 var inner = v[0];
-1 1067 var afterLoop = v[1];
-1 1068
-1 1069 var render = function(data) {
-1 1070 if (inverted) {
-1 1071 if (getValue(tagName, data)) {
-1 1072 return '';
-1 1073 } else {
-1 1074 return inner(data);
-1 1075 }
-1 1076 } else {
-1 1077 if (_.isArray(getValue(tagName, data))) {
-1 1078 var result = '';
-1 1079 for (var i = 0; i < getValue(tagName, data).length; i++) {
-1 1080 result += inner(getValue(tagName, data)[i]);
-1 1081 }
-1 1082 return result;
-1 1083 } else if (getValue(tagName, data)) {
-1 1084 return inner(data);
-1 1085 } else {
-1 1086 return '';
-1 1087 }
-1 1088 }
-1 1089 };
-1 1090
-1 1091 return [render, afterLoop];
-1 1092 };
-1 1093
-1 1094 var concat = function(a) {
-1 1095 var last = a.pop();
-1 1096
-1 1097 if (_.isArray(last)) {
-1 1098 a.push(last[0]);
-1 1099 return [concat(a), last[1]];
-1 1100 } else {
-1 1101 a.push(last);
-1 1102
-1 1103 return function(data) {
-1 1104 return a.map(function(item) {
-1 1105 if (_.isString(item)) {
-1 1106 return item;
-1 1107 } else if (_.isFunction(item)) {
-1 1108 return item(data);
-1 1109 }
-1 1110 }).join('');
-1 1111 };
-1 1112 }
-1 1113 };
-1 1114
-1 1115 var parseTemplate = function(template, loopName) {
-1 1116 var openIndex = template.indexOf(openTag);
-1 1117 if (openIndex === -1) {
-1 1118 if (loopName === undefined) {
-1 1119 return function() {
-1 1120 return template;
-1 1121 };
-1 1122 } else {
-1 1123 throw new Error('unclosed loop: ' + loopName);
-1 1124 }
-1 1125 } else {
-1 1126 var beforeTag = template.slice(0, openIndex);
-1 1127 var tmp = template.slice(openIndex);
-1 1128
-1 1129 var closeIndex = tmp.indexOf(closeTag) + 2;
-1 1130 if (closeIndex === 1) {
-1 1131 throw new Error('unclosed tag: ' + tmp);
-1 1132 }
-1 1133 var tag = tmp.slice(0, closeIndex);
-1 1134 var afterTag = tmp.slice(closeIndex);
-1 1135
-1 1136 if (tag.lastIndexOf('{{#', 0) === 0) {
-1 1137 var v = parseLoopTemplate(tag, afterTag);
-1 1138 var loop = v[0];
-1 1139 var after = parseTemplate(v[1], loopName);
-1 1140 return concat([beforeTag, loop, after]);
-1 1141 } else if (tag.lastIndexOf('{{^', 0) === 0) {
-1 1142 var v = parseLoopTemplate(tag, afterTag, true);
-1 1143 var loop = v[0];
-1 1144 var after = parseTemplate(v[1], loopName);
-1 1145 return concat([beforeTag, loop, after]);
-1 1146 } else if (tag.lastIndexOf('{{!', 0) === 0) {
-1 1147 var after = parseTemplate(afterTag, loopName);
-1 1148 return concat([beforeTag, after]);
-1 1149 } else if (tag.lastIndexOf('{{/', 0) === 0) {
-1 1150 if (tag.slice(3, -2) === loopName) {
-1 1151 var render = function() {
-1 1152 return beforeTag;
-1 1153 };
-1 1154 return [render, afterTag];
-1 1155 } else {
-1 1156 throw new Error('unexpected closing loop: ' + tag);
-1 1157 }
-1 1158 } else {
-1 1159 var render = parseVariableTemplate(tag);
-1 1160 var after = parseTemplate(afterTag, loopName);
-1 1161 return concat([beforeTag, render, after]);
-1 1162 }
-1 1163 }
-1 1164 };
-1 1165
-1 1166 return function(template, data) {
-1 1167 return parseTemplate(template)(data);
-1 1168 };
-1 1169 });
-1 1170 /**
-1 1171 * Recreate children of `source` in `target` by making only small adjustments.
-1 1172 *
-1 1173 * *The following section explains details about the current implementation.
-1 1174 * These are likely to change in the future.*
-1 1175 *
-1 1176 * The algorithms is relatively simple. It just iterates through all top level
-1 1177 * nodes. If a node has a different `nodeType` (e.g. text or element) or a
-1 1178 * different `nodeName` (e.g. div or ul) it is replaced completely and the
-1 1179 * algorithm proceeds with the node's children recursively. Otherwise, only
-1 1180 * the nodes's attributes are updated.
-1 1181 *
-1 1182 * Note that non-attribute properties (e.g. value) are lost in the first case
-1 1183 * and preserved in the second.
-1 1184 *
-1 1185 * If the algorithm encounters an element with the class `muu-isolate` it does
-1 1186 * not recurse into its children. This way, you can protect dynamically
-1 1187 * generated content from being overwritten.
-1 1188 *
-1 1189 * @module muu-update-dom
-1 1190 * @param {Element} target
-1 1191 * @param {Element} source
-1 1192 */
-1 1193 _define('muu-update-dom', ['muu-js-helpers'], function(_) {
-1 1194 "use strict";
-1 1195
-1 1196 var updateAttributes = function(target, source) {
-1 1197 var targetAttrNames = _.map(target.attributes, function(item) {
-1 1198 return item.name;
-1 1199 });
-1 1200 var sourceAttrNames = _.map(source.attributes, function(item) {
-1 1201 return item.name;
-1 1202 });
-1 1203
-1 1204 _.forEach(targetAttrNames, function(name) {
-1 1205 // NOTE: ie8.js creates some attribute
-1 1206 if (!source.hasAttribute(name) && name.substr(0, 7) !== '__IE8__') {
-1 1207 target.removeAttribute(name);
-1 1208 }
-1 1209 });
-1 1210 _.forEach(sourceAttrNames, function(name) {
-1 1211 if (target.getAttribute(name) !== source.getAttribute(name)) {
-1 1212 target.setAttribute(name, source.getAttribute(name));
-1 1213 }
-1 1214 });
-1 1215 };
-1 1216
-1 1217 var updateDOM = function(target, source) {
-1 1218 var nt = target.childNodes.length;
-1 1219 var ns = source.childNodes.length;
-1 1220
-1 1221 for (var i = ns; i < nt; i++) {
-1 1222 target.removeChild(target.childNodes[ns]);
-1 1223 }
-1 1224 for (i = nt; i < ns; i++) {
-1 1225 target.appendChild(source.childNodes[nt]);
-1 1226 }
-1 1227 for (i = 0; i < nt && i < ns; i++) {
-1 1228 var tchild = target.childNodes[i];
-1 1229 var schild = source.childNodes[i];
-1 1230
-1 1231 if (tchild.nodeType === schild.nodeType && tchild.nodeName === schild.nodeName && tchild.type === schild.type) {
-1 1232 if (tchild.nodeType === 1) {
-1 1233 var muuClasses = _.filter(tchild.classList, function(cls) {
-1 1234 return cls.lastIndexOf('muu-', 0) === 0;
-1 1235 });
-1 1236 updateAttributes(tchild, schild);
-1 1237 _.forEach(muuClasses, function(cls) {
-1 1238 tchild.classList.add(cls);
-1 1239 });
-1 1240 } else if (tchild.nodeType === 3) {
-1 1241 tchild.nodeValue = schild.nodeValue;
-1 1242 }
-1 1243 if (tchild.nodeType !== 3 && !tchild.classList.contains('muu-isolate')) {
-1 1244 updateDOM(tchild, schild);
-1 1245 }
-1 1246 } else {
-1 1247 tchild.parentNode.replaceChild(schild, tchild);
-1 1248 }
-1 1249 }
-1 1250 };
-1 1251
-1 1252 return updateDOM;
-1 1253 });
-1 1254 /**
-1 1255 * This module gives access to the following objects:
-1 1256 *
-1 1257 * - `Registry` - {@link Registry}
-1 1258 * - `$` - {@link module:muu-dom-helpers}
-1 1259 * - `$location` - {@link module:muu-location}
-1 1260 *
-1 1261 * @module muu
-1 1262 */
-1 1263 _define('muu', ['muu-registry', 'muu-dom-helpers', 'muu-location'], function(Registry, $, $location) {
-1 1264 "use strict";
-1 1265
-1 1266 var module = {};
-1 1267
-1 1268 module.Registry = Registry;
-1 1269 module.$ = $;
-1 1270 module.$location = $location;
-1 1271
-1 1272 return module;
-1 1273 });
-1 1274
-1 1275 return _require(name);
-1 1276 });
-1 1277 })(window, document, void 0);
diff --git a/dist/muu.min.js b/dist/muu.min.js
@@ -0,0 +1,19 @@
-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")||
-1 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);
diff --git a/examples/example/example.js b/examples/example/example.js
@@ -1,16 +1,16 @@ 1 1 requirejs.config({2 -1 baseUrl: '../../src/',3 2 paths: {4 -1 xhr: '../lib/promise-xhr/promise-xhr',5 -1 moment: '../lib/moment/moment',6 -1 'muu-moment': '../examples/example/muu-moment'-1 3 muu: '../../dist/muu.min', -1 4 xhr: '../../lib/promise-xhr/promise-xhr', -1 5 moment: '../../lib/moment/moment', -1 6 'muu-moment': './muu-moment' 7 7 } 8 8 }); 9 910 -1 require(['xhr', 'muu', 'muu-dom-helpers', 'muu-moment'], function(xhr, Muu, $, muuMoment) {-1 10 require(['xhr', 'muu', 'muu-moment'], function(xhr, muu, muuMoment) { 11 11 "use strict"; 12 1213 -1 var muu = new Muu({debug: true})-1 13 var registry = new muu.Registry({debug: true}) 14 14 .registerModule(muuMoment) 15 15 .registerDirective( 16 16 'test', @@ -54,7 +54,7 @@ require(['xhr', 'muu', 'muu-dom-helpers', 'muu-moment'], function(xhr, Muu, $, m 54 54 self.update(data); 55 55 }); 56 5657 -1 $.ready(function() {58 -1 muu.linkAll(document);-1 57 muu.$.ready(function() { -1 58 registry.linkAll(document); 59 59 }); 60 60 });
diff --git a/examples/example/index.html b/examples/example/index.html
@@ -1,7 +1,6 @@ 1 1 <html> 2 2 <head> 3 3 <script src="../../lib/requirejs/require.js"></script>4 -1 <script src="../../muu.min.js"></script>5 4 <script src="example.js"></script> 6 5 </head> 7 6 <body>
diff --git a/examples/example/muu-moment.js b/examples/example/muu-moment.js
@@ -1,8 +1,8 @@ 1 1 define(['moment'], function(moment) { 2 2 "use strict"; 3 34 -1 return function(muu) {5 -1 muu.registerDirective('moment', '<time aria-live datetime="{{machine}}">{{human}}</time>', function(self) {-1 4 return function(registry) { -1 5 registry.registerDirective('moment', '<time aria-live datetime="{{machine}}">{{human}}</time>', function(self) { 6 6 var update = function() { 7 7 self.update({ 8 8 machine: moment().format(),
diff --git a/examples/phonecat/index.html b/examples/phonecat/index.html
@@ -1,7 +1,6 @@ 1 1 <html> 2 2 <head> 3 3 <script src="../../lib/requirejs/require.js"></script>4 -1 <script src="../../muu.min.js"></script>5 4 <script src="phonecat.js"></script> 6 5 </head> 7 6 <body>
diff --git a/examples/phonecat/phonecat.js b/examples/phonecat/phonecat.js
@@ -1,11 +1,11 @@ 1 1 requirejs.config({2 -1 baseUrl: '../../src/',3 2 paths: {4 -1 xhr: '../lib/promise-xhr/promise-xhr'-1 3 muu: '../../dist/muu.min', -1 4 xhr: '../../lib/promise-xhr/promise-xhr' 5 5 } 6 6 }); 7 78 -1 require(['xhr', 'muu', 'muu-dom-helpers'], function(xhr, Muu, $) {-1 8 require(['xhr', 'muu'], function(xhr, muu) { 9 9 "use strict"; 10 10 11 11 Promise.all([ @@ -15,7 +15,7 @@ require(['xhr', 'muu', 'muu-dom-helpers'], function(xhr, Muu, $) { 15 15 var template = args[0]; 16 16 var phones = args[1]; 17 1718 -1 var muu = new Muu()-1 18 var registry = new muu.Registry() 19 19 .registerDirective('phonecat', template, function(self, element) { 20 20 element.addEventListener('muu-filter', function() { 21 21 var query = self.getModel('query', '').toLowerCase(); @@ -37,8 +37,8 @@ require(['xhr', 'muu', 'muu-dom-helpers'], function(xhr, Muu, $) { 37 37 self.setModel('orderProp', 'age'); 38 38 }); 39 3940 -1 $.ready(function() {41 -1 muu.linkAll(document);-1 40 muu.$.ready(function() { -1 41 registry.linkAll(document); 42 42 }); 43 43 }); 44 44 });
diff --git a/muu.min.js b/muu.min.js
@@ -1,2 +0,0 @@1 -1 define("muu-js-helpers",[],function(){"use strict";var e={},t=function(e){return Object.prototype.toString.call(e)};return e.isString=function(e){return"string"==typeof e||"[object String]"===t(e)},e.isArray=Array.isArray,e.isFunction=function(e){return"function"==typeof e},e.once=function(e){var t,n=!1;return function(){return n||(t=e.apply(this,arguments),n=!0),t}},e.indexOf=function(e,t){if("indexOf"in e)return e.indexOf(t);for(var n=0;n<e.length;n++)if(e[n]===t)return n;return-1},e.forEach=function(e,t){if("forEach"in e)return e.forEach(t);for(var n=0;n<e.length;n++)t(e[n])},e.map=function(e,t){if("map"in e)return e.map(t);for(var n=[],r=0;r<e.length;r++)n.push(t(e[r]));return n},e.filter=function(e,t){if("filter"in e)return e.filter(t);for(var n=[],r=0;r<e.length;r++)t(e[r])&&n.push(e[r]);return n},e.union=function(t){for(var n=[],r=0;r<t.length;r++)for(var i=0;i<t[r].length;i++)-1===e.indexOf(n,t[r][i])&&n.push(t[r][i]);return n},e.difference=function(t,n){for(var r=[],i=0;i<t.length;i++)-1===e.indexOf(n,t[i])&&r.push(t[i]);return r},e.flatten=function(t){var n=[];return e.forEach(t,function(t){e.isArray(t)?n=n.concat(e.flatten(t)):n.push(t)}),n},e}),define("muu-dom-helpers",["muu-js-helpers"],function(e){"use strict";var t={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"},n={};return n.DELAY=1e3,n.escapeHtml=function(e){return String(e).replace(/[&<>"'\/]/g,function(e){return t[e]})},n.createEvent=function(e,t){if("function"==typeof CustomEvent)return new CustomEvent(e,{detail:t});var n=document.createEvent("CustomEvent");return n.initCustomEvent(e,!1,!0,t),n},n.on=function(e,t,n){return e.addEventListener(t,n,!1),function(){e.removeEventListener(t,n,!1)}},n.ready=function(t){var r=e.once(t);if("complete"===document.readyState)return r(),function(){};var i=n.on(document,"DOMContentLoaded",r),u=n.on(window,"load",r);return function(){i(),u()}},n.isDescendant=function(e,t){return!!e&&(e===t||n.isDescendant(e.parentNode,t))},n.destroy=function(e,t){var r,i=setInterval(function(){n.isDescendant(e,document)||(t(),r())},n.DELAY);return r=function(){clearInterval(i)}},n.getRadio=function(e){for(var t=0;t<e.length;t++)if(e[t].checked)return e[t].value},n.setRadio=function(e,t){for(var n=0;n<e.length;n++)e[n].value===t?e[n].checked=!0:e[n].checked=!1},n}),define("muu-template",["muu-js-helpers","muu-dom-helpers"],function(e,t){"use strict";var n="{{",r="}}",i=function(e){var n=e.slice(2,-2);if(-1===e.indexOf(":"))return function(e){return t.escapeHtml(e[n]||"")};var r=n.split(",").map(function(e){var t=e.split(":"),n=t[0].trim(),r=t.slice(1).join(":").trim();return[n,r]});return function(e){for(var n=[],i=0;i<r.length;i++){var u=r[i][0],o=r[i][1];e[o]&&n.push(u)}return t.escapeHtml(n.join(" "))}},u=function(t,n){var r=t.slice(3,-2),i=c(n,r),u=i[0],o=i[1],a=function(t){if(e.isArray(t[r])){for(var n="",i=0;i<t[r].length;i++)n+=u(t[r][i]);return n}return t[r]?u(t):""};return[a,o]},o=function(t){var n=t.pop();return e.isArray(n)?(t.push(n[0]),[o(t),n[1]]):(t.push(n),function(n){return t.map(function(t){return e.isString(t)?t:e.isFunction(t)?t(n):void 0}).join("")})},c=function(e,t){var a=e.indexOf(n);if(-1===a){if(void 0===t)return function(){return e};throw new Error("unclosed loop: "+t)}var f=e.slice(0,a),l=e.slice(a),s=l.indexOf(r)+2;if(1===s)throw new Error("unclosed tag: "+l);var d=l.slice(0,s),v=l.slice(s);if(0===d.lastIndexOf("{{#",0)){var h=u(d,v),m=h[0],p=c(h[1],t);return o([f,m,p])}if(0===d.lastIndexOf("{{!",0)){var p=c(v,t);return o([f,p])}if(0===d.lastIndexOf("{{/",0)){if(d.slice(3,-2)===t){var y=function(){return f};return[y,v]}throw new Error("unexpected closing loop: "+d)}var y=i(d),p=c(v,t);return o([f,y,p])};return function(e,t){return c(e)(t)}}),define("muu-update-dom",["muu-js-helpers"],function(e){"use strict";var t=function(t,n){var r=e.map(t.attributes,function(e){return e.name}),i=e.map(n.attributes,function(e){return e.name});e.forEach(r,function(e){n.hasAttribute(e)||"__IE8__"===e.substr(0,7)||t.removeAttribute(e)}),e.forEach(i,function(e){t.getAttribute(e)!==n.getAttribute(e)&&t.setAttribute(e,n.getAttribute(e))})},n=function(r,i){var u=r.childNodes.length,o=i.childNodes.length;if(r.nodeType===i.nodeType&&r.nodeName===i.nodeName&&r.type===i.type){if(1===r.nodeType){var c=e.filter(r.classList,function(e){return 0===e.lastIndexOf("muu-",0)});t(r,i),e.forEach(c,function(e){r.classList.add(e)})}else 3===r.nodeType&&(r.nodeValue=i.nodeValue);if(1!==r.nodeType||!r.classList.contains("muu-isolate")){for(var a=o;u>a;a++)r.removeChild(r.childNodes[o]);for(a=u;o>a;a++)r.appendChild(i.childNodes[u]);for(a=0;u>a&&o>a;a++)n(r.childNodes[a],i.childNodes[a])}}else r.parentNode.replaceChild(i,r)};return n}),define("muu-directive",["muu-dom-helpers","muu-js-helpers","muu-update-dom"],function(e,t,n){"use strict";var r=function(r,i,u){var o=this;r.innerHTML="<div></div>";var c=function(t){var n="data-on"+t.type;if(t.target.hasAttribute(n)){var i=t.target.getAttribute(n),u=e.createEvent("muu-"+i,t);r.dispatchEvent(u)}};this.update=function(a){var f=document.createElement("div");f.innerHTML=u.renderer(i,a),n(r.children[0],f),t.forEach(["keydown","keyup","click","change","search"],function(e){var n="[data-on"+e+"]";t.forEach(o.querySelectorAll(n),function(t){t.addEventListener(e,c,!1)})});var l=e.createEvent("muu-parent-update"),s=this.querySelectorAll("muu.muu-initialised");t.forEach(s,function(e){e.dispatchEvent(l)}),u.linkAll(o)},this.querySelectorAll=function(e){var n=r.querySelectorAll(e),i=r.querySelectorAll(".muu-isolate"),u=t.union(t.map(i,function(t){return t.querySelectorAll(e)}));return t.difference(n,u)},this.querySelector=function(e){var t=o.querySelectorAll(e);return t.length>0?t[0]:void 0},this.getModel=function(n,r){if(void 0===n){var i={};return t.forEach(o.querySelectorAll("[name]"),function(e){i[e.name]=o.getModel(e.name)}),i}var u=o.querySelector("[name="+n+"]");if(void 0===u)return r;if("checkbox"===u.type)return u.checked;if("radio"===u.type){var c=o.querySelectorAll("[name="+n+"]");return e.getRadio(c)||r}return u.value},this.setModel=function(t,n){var r=o.querySelector("[name="+t+"]");if("checkbox"===r.type)r.checked=n;else if("radio"===r.type){var i=o.querySelectorAll("[name="+t+"]");e.setRadio(i,n)}else r.value=n}};return r}),define("muu",["muu-template","muu-directive","muu-js-helpers","muu-dom-helpers"],function(e,t,n,r){"use strict";var i=function(i){var u=this,o={};this.config=i||{},this.renderer=u.config.renderer||e,this.registerDirective=function(e,t,n){return o[e]={template:t,link:n},u},this.registerModule=function(e){return e(u),u},this.link=function(e,n){if(void 0===n&&(n=e.getAttribute("type")),!o.hasOwnProperty(n))throw new Error("Unknown directive type: "+n);var i=o[n].template,c=o[n].link,a=new t(e,i,u),f=c(a,e);return e.classList.add("muu-isolate"),e.classList.add("muu-initialised"),u.config.debug&&(e.directive=a),void 0!==f&&r.destroy(e,f),a},this.linkAll=function(e){var t=n.filter(e.querySelectorAll("muu"),function(e){return!e.classList.contains("muu-initialised")});return n.map(t,function(e){return u.link(e)})}};return i});2 -1 //# sourceMappingURL=muu.min.js.ma2 -1 \ No newline at end of file
diff --git a/muu.min.js.map b/muu.min.js.map
@@ -1 +0,0 @@1 -1 {"version":3,"file":"muu.min.js","sources":["muu-js-helpers.js","muu-dom-helpers.js","muu-template.js","muu-update-dom.js","muu-directive.js","muu.js"],"names":["define","_","objToString","value","Object","prototype","toString","call","isString","isArray","Array","isFunction","once","fn","result","called","apply","this","arguments","indexOf","array","i","length","forEach","map","results","push","filter","union","arrays","j","difference","a","b","flatten","o","item","concat","entityMap","&","<",">","\"","'","/","$","DELAY","escapeHtml","string","String","replace","s","createEvent","type","detail","CustomEvent","event","document","initCustomEvent","on","element","eventName","callback","addEventListener","removeEventListener","ready","_fn","readyState","u1","u2","window","isDescendant","desc","root","parentNode","destroy","unregister","intervalID","setInterval","clearInterval","getRadio","options","checked","setRadio","openTag","closeTag","parseVariableTemplate","template","content","slice","data","pairs","split","pair","v","key","trim","join","parseLoopTemplate","tag","afterTag","tagName","parseTemplate","inner","afterLoop","render","last","pop","loopName","openIndex","Error","beforeTag","tmp","closeIndex","lastIndexOf","loop","after","updateAttributes","target","source","targetAttrNames","attributes","name","sourceAttrNames","hasAttribute","substr","removeAttribute","getAttribute","setAttribute","updateDOM","nt","childNodes","ns","nodeType","nodeName","muuClasses","classList","cls","add","nodeValue","contains","removeChild","appendChild","replaceChild","Directive","registry","self","innerHTML","eventCallback","originalEvent","attrName","dispatchEvent","update","createElement","renderer","children","eventType","selector","querySelectorAll","updateEvent","subDirectives","linkAll","hits","isolations","isolated","isolation","querySelector","all","getModel","_default","model","setModel","muuTemplate","Registry","config","directives","registerDirective","link","registerModule","module","hasOwnProperty","directive","unlink","debug","elements"],"mappings":"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B,KAAArH,MAKA,OAAAiH;ALlIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AC/KA,ADgLA;AC/KA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,ACnKA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,AC/KA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AClFA,ADmFA;AClFA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,AClLA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","sourcesContent":["/**\n * Minimal implementation of an underscore/lodash subset.\n * @module muu-js-helpers\n */\ndefine('muu-js-helpers',[],function() {\n \"use strict\";\n\n /** @lends module:muu-js-helpers */\n var _ = {};\n\n /**\n * @param {object} value\n * @return {string}\n */\n var objToString = function(value) {\n return Object.prototype.toString.call(value);\n };\n\n /**\n * @param {*} value\n * @return {boolean}\n */\n _.isString = function(value) {\n return typeof value === 'string' || objToString(value) === '[object String]';\n };\n\n /**\n * @function\n * @param {*} value\n * @return {boolean}\n */\n _.isArray = Array.isArray;\n\n /**\n * @param {*} value\n * @return {boolean}\n */\n _.isFunction = function(value) {\n return typeof value === 'function';\n };\n\n /**\n * @param {function} fn\n * @return {function}\n */\n _.once = function(fn) {\n var result;\n var called = false;\n\n return function() {\n if (!called) {\n result = fn.apply(this, arguments);\n called = true;\n }\n return result;\n };\n };\n\n /**\n * @param {array} array\n * @param {*} value\n * @return {number}\n */\n _.indexOf = function(array, value) {\n if ('indexOf' in array) {\n return array.indexOf(value);\n }\n\n for (var i = 0; i < array.length; i++) {\n if (array[i] === value) {\n return i;\n }\n }\n return -1;\n };\n\n /**\n * @param {array} array\n * @param {function} fn\n */\n _.forEach = function(array, fn) {\n if ('forEach' in array) {\n return array.forEach(fn);\n }\n\n for (var i = 0; i < array.length; i++) {\n fn(array[i]);\n }\n };\n\n /**\n * @param {array} array\n * @param {function} fn\n * @return {array}\n */\n _.map = function(array, fn) {\n if ('map' in array) {\n return array.map(fn);\n }\n\n var results = [];\n for (var i = 0; i < array.length; i++) {\n results.push(fn(array[i]));\n }\n return results;\n };\n\n /**\n * @param {array} array\n * @param {function} fn\n * @return {array}\n */\n _.filter = function(array, fn) {\n if ('filter' in array) {\n return array.filter(fn);\n }\n\n var results = [];\n for (var i = 0; i < array.length; i++) {\n if (fn(array[i])) {\n results.push(array[i]);\n }\n }\n return results;\n };\n\n /**\n * @param {array[]} arrays\n * @return {array}\n */\n _.union = function(arrays) {\n var results = [];\n for (var i = 0; i < arrays.length; i++) {\n for (var j = 0; j < arrays[i].length; j++) {\n if (_.indexOf(results, arrays[i][j]) === -1) {\n results.push(arrays[i][j]);\n }\n }\n }\n return results;\n };\n\n /**\n * @param {array} a\n * @param {array} b\n * @return {array}\n */\n _.difference = function(a, b) {\n var results = [];\n for (var i = 0; i < a.length; i++) {\n if (_.indexOf(b, a[i]) === -1) {\n results.push(a[i]);\n }\n }\n return results;\n };\n\n /**\n * @param {array} a\n * @return {array}\n */\n _.flatten = function(a) {\n var o = [];\n _.forEach(a, function(item) {\n if (_.isArray(item)) {\n o = o.concat(_.flatten(item));\n } else {\n o.push(item);\n }\n });\n return o;\n };\n\n return _;\n});\n\n","/**\n * DOM related helper functions\n * @module muu-dom-helpers\n */\ndefine('muu-dom-helpers',['muu-js-helpers'], function(_) {\n \"use strict\";\n\n var entityMap = {\n '&': '&',\n '<': '<',\n '>': '>',\n '\"': '"',\n \"'\": ''',\n '/': '/'\n };\n\n /** @lends module:muu-dom-helpers */\n var $ = {};\n\n $.DELAY = 1000;\n\n /**\n * @param {string} string\n * @return {string} - escaped HTML\n */\n $.escapeHtml = function(string) {\n return String(string).replace(/[&<>\"'\\/]/g, function(s) {\n return entityMap[s];\n });\n };\n\n /**\n * Cross browser custom events.\n *\n * See https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events\n *\n * *Note*: IE does not seem to like it when you use existing event names\n * with this.\n *\n * @param {string} type\n * @param {*} detail\n * @return {DOMEvent}\n */\n $.createEvent = function(type, detail) {\n if (typeof CustomEvent === 'function') {\n return new CustomEvent(type, {\n detail: detail\n });\n } else {\n var event = document.createEvent('CustomEvent');\n event.initCustomEvent(type, false, true, detail);\n return event;\n }\n };\n\n /**\n * @param {DOMElement} element\n * @param {string} eventName\n * @param {function} callback\n * @return {Function()} An unregister function\n */\n $.on = function(element, eventName, callback) {\n element.addEventListener(eventName, callback, false);\n return function() {\n element.removeEventListener(eventName, callback, false);\n };\n };\n\n /**\n * @param {function} fn\n * @return {Function()} An unregister function\n */\n $.ready = function(fn) {\n var _fn = _.once(fn);\n if (document.readyState === 'complete') {\n _fn();\n return function() {};\n } else {\n var u1 = $.on(document, 'DOMContentLoaded', _fn);\n var u2 = $.on(window, 'load', _fn);\n return function() {\n u1();\n u2();\n };\n }\n };\n\n $.isDescendant = function(desc, root) {\n return !!desc && (desc === root || $.isDescendant(desc.parentNode, root));\n };\n\n /**\n * Execute a function when `element` is removed from the DOM.\n *\n * @param {DOMElement} element\n * @param {function} fn\n * @return {Function()} An unregister function\n */\n $.destroy = function(element, fn) {\n var unregister;\n\n if (false) {\n var observer = new MutationObserver(function() {\n if (!$.isDescendant(element, document)) {\n fn();\n unregister();\n }\n });\n\n observer.observe(document, {\n childList: true,\n subtree: true\n });\n\n unregister = _.once(function() {\n observer.disconnect();\n observer = undefined;\n });\n } else {\n var intervalID = setInterval(function() {\n if (!$.isDescendant(element, document)) {\n fn();\n unregister();\n }\n }, $.DELAY);\n\n unregister = function() {\n clearInterval(intervalID);\n };\n }\n\n return unregister;\n };\n\n /**\n * @param {DOMElement[]} options\n * @return {string}\n */\n $.getRadio = function(options) {\n for (var i = 0; i < options.length; i++) {\n if (options[i].checked) {\n return options[i].value;\n }\n }\n };\n\n /**\n * @param {DOMElement[]} options\n * @param {string} value\n */\n $.setRadio = function(options, value) {\n for (var i = 0; i < options.length; i++) {\n if (options[i].value === value) {\n options[i].checked = true;\n } else {\n options[i].checked = false;\n }\n }\n };\n\n return $;\n});\n\n","/**\n * minimal mustache insipred templating\n *\n * ## Variables\n *\n * Variables are created with a `{{name}}` tag. These are always escaped.\n *\n * ## Loops\n *\n * Loops render blocks of text a number of times, depending on the value of\n * the key in the current context.\n *\n * A loop begins with a pound and ends with a slash. That is, {{#person}}\n * begins a \"person\" section while {{/person}} ends it.\n *\n * If the value is an array, the block is repeated for each item in that array.\n * In any other case, the block is rendered with the outer scope, but only if\n * the value is truthy.\n *\n * ## Comments\n *\n * Comments begin with a bang and are ignored.\n *\n * ## Pairs\n *\n * Pairs look like JSON objects. The result is a space separated list of all\n * keys with truthy values.\n *\n * ```\n * muuTemplate('{{foo: var1, bar: var2, baz: var3}}', {\n * var1: true,\n * var2: false,\n * var3: true\n * }); // 'foo baz'\n * ```\n *\n * @module muu-template\n * @param {string} template\n * @param {object} data\n * @return {string}\n */\ndefine('muu-template',['muu-js-helpers', 'muu-dom-helpers'], function(_, $) {\n \"use strict\";\n\n var openTag = '{{';\n var closeTag = '}}';\n\n var parseVariableTemplate = function(template) {\n var content = template.slice(2, -2);\n\n if (template.indexOf(':') === -1) {\n return function(data) {\n return $.escapeHtml(data[content] || '');\n };\n } else {\n var pairs = content.split(',').map(function(pair) {\n var v = pair.split(':');\n var key = v[0].trim();\n var value = v.slice(1).join(':').trim();\n return [key, value];\n });\n\n return function(data) {\n var results = [];\n\n for (var i = 0; i < pairs.length; i++) {\n var key = pairs[i][0];\n var value = pairs[i][1];\n\n if (data[value]) {\n results.push(key);\n }\n }\n\n return $.escapeHtml(results.join(' '));\n };\n }\n };\n\n var parseLoopTemplate = function(tag, afterTag) {\n var tagName = tag.slice(3, -2);\n\n var v = parseTemplate(afterTag, tagName);\n var inner = v[0];\n var afterLoop = v[1];\n\n var render = function(data) {\n if (_.isArray(data[tagName])) {\n var result = '';\n for (var i = 0; i < data[tagName].length; i++) {\n result += inner(data[tagName][i]);\n }\n return result;\n } else if (data[tagName]) {\n return inner(data);\n } else {\n return '';\n }\n };\n\n return [render, afterLoop];\n };\n\n var concat = function(a) {\n var last = a.pop();\n\n if (_.isArray(last)) {\n a.push(last[0]);\n return [concat(a), last[1]];\n } else {\n a.push(last);\n\n return function(data) {\n return a.map(function(item) {\n if (_.isString(item)) {\n return item;\n } else if (_.isFunction(item)) {\n return item(data);\n }\n }).join('');\n };\n }\n };\n\n var parseTemplate = function(template, loopName) {\n var openIndex = template.indexOf(openTag);\n if (openIndex === -1) {\n if (loopName === void 0) {\n return function() {\n return template;\n };\n } else {\n throw new Error('unclosed loop: ' + loopName);\n }\n } else {\n var beforeTag = template.slice(0, openIndex);\n var tmp = template.slice(openIndex);\n\n var closeIndex = tmp.indexOf(closeTag) + 2;\n if (closeIndex === 1) {\n throw new Error('unclosed tag: ' + tmp);\n }\n var tag = tmp.slice(0, closeIndex);\n var afterTag = tmp.slice(closeIndex);\n\n if (tag.lastIndexOf('{{#', 0) === 0) {\n var v = parseLoopTemplate(tag, afterTag);\n var loop = v[0];\n var after = parseTemplate(v[1], loopName);\n return concat([beforeTag, loop, after]);\n } else if (tag.lastIndexOf('{{!', 0) === 0) {\n var after = parseTemplate(afterTag, loopName);\n return concat([beforeTag, after]);\n } else if (tag.lastIndexOf('{{/', 0) === 0) {\n if (tag.slice(3, -2) === loopName) {\n var render = function() {\n return beforeTag;\n };\n return [render, afterTag];\n } else {\n throw new Error('unexpected closing loop: ' + tag);\n }\n } else {\n var render = parseVariableTemplate(tag);\n var after = parseTemplate(afterTag, loopName);\n return concat([beforeTag, render, after]);\n }\n }\n };\n\n return function(template, data) {\n return parseTemplate(template)(data);\n };\n});\n\n","/**\n * Recreate DOM `source` in `target` by making only small adjustments.\n *\n * *The following section explains details about the current implementation.\n * These are likely to change in the future.*\n *\n * The algorithms is relatively simple. It just iterates through all top level\n * nodes. If a node has a different `nodeType` (e.g. text or element) or a\n * different `nodeName` (e.g. div or ul) it is replaced completely and the\n * algorithm proceeds with the node's children recursively. Otherwise, only\n * the nodes's attributes are updated.\n *\n * Note that non-attribute properties (e.g. value) are lost in the first case\n * and preserved in the second.\n *\n * If the algorithm encounters an element with the class `muu-isolate` it does\n * not recurse into its children. This way, you can protect dynamically\n * generated content from being overwritten.\n *\n * @module muu-update-dom\n * @param {DOMElement} target\n * @param {DOMElement} source\n */\ndefine('muu-update-dom',['muu-js-helpers'], function(_) {\n \"use strict\";\n\n var updateAttributes = function(target, source) {\n var targetAttrNames = _.map(target.attributes, function(item) {\n return item.name;\n });\n var sourceAttrNames = _.map(source.attributes, function(item) {\n return item.name;\n });\n\n _.forEach(targetAttrNames, function(name) {\n // NOTE: ie8.js creates some attribute\n if (!source.hasAttribute(name) && name.substr(0, 7) !== '__IE8__') {\n target.removeAttribute(name);\n }\n });\n _.forEach(sourceAttrNames, function(name) {\n if (target.getAttribute(name) !== source.getAttribute(name)) {\n target.setAttribute(name, source.getAttribute(name));\n }\n });\n };\n\n var updateDOM = function(target, source) {\n var nt = target.childNodes.length;\n var ns = source.childNodes.length;\n\n if (target.nodeType === source.nodeType && target.nodeName === source.nodeName && target.type === source.type) {\n if (target.nodeType === 1) {\n var muuClasses = _.filter(target.classList, function(cls) {\n return cls.lastIndexOf('muu-', 0) === 0;\n });\n updateAttributes(target, source);\n _.forEach(muuClasses, function(cls) {\n target.classList.add(cls);\n });\n } else if (target.nodeType === 3) {\n target.nodeValue = source.nodeValue;\n }\n\n if (target.nodeType !== 1 || !target.classList.contains('muu-isolate')) {\n for (var i = ns; i < nt; i++) {\n target.removeChild(target.childNodes[ns]);\n }\n for (i = nt; i < ns; i++) {\n target.appendChild(source.childNodes[nt]);\n }\n for (i = 0; i < nt && i < ns; i++) {\n updateDOM(target.childNodes[i], source.childNodes[i]);\n }\n }\n } else {\n target.parentNode.replaceChild(source, target);\n }\n };\n\n return updateDOM;\n});\n\n","/**\n * Exports the {@link Directive} class.\n * @module muu-directive\n */\ndefine('muu-directive',['muu-dom-helpers', 'muu-js-helpers', 'muu-update-dom'], function($, _, updateDOM) {\n \"use strict\";\n\n /**\n * A directive is linked to a DOMElement and manages the DOM tree below\n * that element (excluding any isolated subtrees, e.g. those managed by\n * subdirectives).\n *\n * It provides a set of methods to interact with the managed part of the\n * DOM. This is separated into three distinct parts:\n *\n * - You can push data to the DOM using the {@link Directive#update}\n * method. The DOM will than be updated using the template that was\n * provided at construction.\n * - You can get data from the DOM using the {@link Directive#getModel}\n * method. This is however restricted to form field by design.\n * - You can react to DOM events by specifying an alias for them. In the\n * template, you might for example add the attribute\n * `data-onclick=\"custom\"` to an element. When there is `click` event on\n * that element, a `muu-custom` event will be triggered on the\n * directive's root element.\n *\n * Directives are typically not created directly but via {@link\n * Registry#link}.\n *\n * @constructs Directive\n * @param {DOMElement} root\n * @param {string} template\n * @param {Muu} registry\n */\n var Directive = function(root, template, registry) {\n var self = this;\n\n root.innerHTML = '<div></div>';\n\n var eventCallback = function(originalEvent) {\n var attrName = 'data-on' + originalEvent.type;\n if (originalEvent.target.hasAttribute(attrName)) {\n var eventName = originalEvent.target.getAttribute(attrName);\n var event = $.createEvent('muu-' + eventName, originalEvent);\n root.dispatchEvent(event);\n }\n };\n\n /**\n * Rerender `template` with `data` and push the changes to the DOM.\n *\n * See {@link module:muu-update-dom} for details. The templating system\n * can be defined in the {@link Registry}.\n *\n * @param {Object.<string, *>} data\n */\n this.update = function(data) {\n var tmp = document.createElement('div');\n tmp.innerHTML = registry.renderer(template, data);\n\n updateDOM(root.children[0], tmp);\n\n _.forEach(['keydown', 'keyup', 'click', 'change', 'search'], function(eventType) {\n var selector = '[data-on' + eventType + ']';\n _.forEach(self.querySelectorAll(selector), function(element) {\n element.addEventListener(eventType, eventCallback, false);\n });\n });\n\n var updateEvent = $.createEvent('muu-parent-update');\n var subDirectives = this.querySelectorAll('muu.muu-initialised');\n _.forEach(subDirectives, function(element) {\n element.dispatchEvent(updateEvent);\n });\n\n registry.linkAll(self);\n };\n\n /**\n * A variant of `querySelectorAll` that returns only elements from\n * the managed part of the DOM.\n *\n * @private\n * @param {string} selector\n * @return {DOMElement[]} All child elements that match the given\n * selector and are not isolated.\n */\n this.querySelectorAll = function(selector) {\n var hits = root.querySelectorAll(selector);\n\n // NOTE: querySelectorAll returns all elements in the tree that\n // match the given selector. findAll does the same with *relative\n // selectors* but does not seem to be available yet.\n var isolations = root.querySelectorAll('.muu-isolate');\n var isolated = _.union(_.map(isolations, function(isolation) {\n return isolation.querySelectorAll(selector);\n }));\n\n return _.difference(hits, isolated);\n };\n\n /**\n * A variant of `querySelector` that returns only elements from the\n * managed part of the DOM.\n *\n * @private\n * @param {String} selector\n * @return {DOMElement} First child element that matches the given\n * selector and is not isolated.\n */\n this.querySelector = function(selector) {\n var all = self.querySelectorAll(selector);\n if (all.length > 0) {\n return all[0];\n }\n };\n\n /**\n * Get all model data as a flat object.\n *\n * @return {Object.<string, string|number|boolean>}\n *//**\n * Get the value of a form input by name.\n *\n * In case of a checkbox, returns `boolean`.\n * In case of radioboxes, returns the value of the selected box.\n *\n * @param {string} name\n * @param {*} [_default]\n * @return {string|number|boolean|*}\n */\n this.getModel = function(name, _default) {\n if (name === void 0) {\n var model = {};\n _.forEach(self.querySelectorAll('[name]'), function(element) {\n model[element.name] = self.getModel(element.name);\n });\n return model;\n } else {\n var element = self.querySelector('[name=' + name + ']');\n if (element === void 0) {\n return _default;\n } else if (element.type === 'checkbox') {\n return element.checked;\n } else if (element.type === 'radio') {\n var options = self.querySelectorAll('[name=' + name + ']');\n return $.getRadio(options) || _default;\n } else {\n return element.value;\n }\n }\n };\n\n /**\n * Set the value of a form input by name.\n *\n * In case of a checkbox, sets `element.checked`.\n * In case of radioboxes, selects the box with matching value.\n *\n * @param {string} name\n * @param {string|number|boolean} value\n */\n this.setModel = function(name, value) {\n var element = self.querySelector('[name=' + name + ']');\n if (element.type === 'checkbox') {\n element.checked = value;\n } else if (element.type === 'radio') {\n var options = self.querySelectorAll('[name=' + name + ']');\n $.setRadio(options, value);\n } else {\n element.value = value;\n }\n };\n };\n\n return Directive;\n});\n\n","/**\n * Exports the {@link Registry} class.\n * @module muu\n */\ndefine('muu',['muu-template', 'muu-directive', 'muu-js-helpers', 'muu-dom-helpers'], function(muuTemplate, Directive, _, $) {\n \"use strict\";\n\n /**\n * @constructs Registry\n * @param {object} config The config object may have following properties:\n *\n * - **debug** - `{boolean}` - Enable debug mode. In debug mode,\n * directive objects are available as properties from the DOM as\n * `element.directive`.\n * - **renderer** - `{Function(string, object)}` - The template renderer\n * to be used. Defaults to {@link module:muu-template}.\n */\n var Registry = function(config) {\n var self = this;\n var directives = {};\n\n this.config = config || {};\n this.renderer = self.config.renderer || muuTemplate;\n\n /**\n * Register a new type of {@link Directive}\n *\n * @param {string} type\n * @param {string} template\n * @param {Function(Directive, DOMElement): function} link The link\n * function is called with an instance of {@link Directive} and a\n * DOMElement when {@link Registry#link} is executed.\n *\n * It is the only place where you can access a directive and\n * therefore the place where you define its behavior.\n *\n * This typically means to make an initial call to {@link\n * Directive#update} and to add some event listeners. You should also\n * return an *unlink* function that clears all external references in\n * order to avoid memory leaks.\n * @return {Registry} this\n */\n this.registerDirective = function(type, template, link) {\n directives[type] = {\n template: template,\n link: link\n };\n return self;\n };\n\n /**\n * Shortcut for wrapping calls to {@link Registry} in a function.\n *\n * This can be esepcially helpful if that function is defined in a\n * different module.\n *\n * ```.js\n * define('foobar', [], function() {\n * return function(registry) {\n * registry\n * .registerDirective('foo', '...', function() {...})\n * .registerDirective('bar', '...', function() {...});\n * };\n * });\n *\n * require(['foobar'], function(foobar) {\n * var registry = new Registry();\n * registry.registerModule(foobar);\n * });\n * ```\n *\n * @param {Function(Registry)}\n * @return {Registry} this\n */\n this.registerModule = function(module) {\n module(self);\n return self;\n };\n\n /**\n * Create and initialise a {@link Directive} for `element`.\n *\n * @param {DOMElement} element\n * @param {string} type\n * @return {Directive}\n */\n this.link = function(element, type) {\n if (type === void 0) {\n type = element.getAttribute('type');\n }\n\n if (!directives.hasOwnProperty(type)) {\n throw new Error('Unknown directive type: ' + type);\n }\n\n var template = directives[type].template;\n var link = directives[type].link;\n\n var directive = new Directive(element, template, self);\n var unlink = link(directive, element);\n element.classList.add('muu-isolate');\n element.classList.add('muu-initialised');\n\n if (self.config.debug) {\n element.directive = directive;\n }\n\n if (unlink !== void 0) {\n $.destroy(element, unlink);\n }\n\n return directive;\n };\n\n /**\n * Link all directives that can be found inside `root`.\n *\n * @param {DOMElement} root\n * @return {Directive[]}\n */\n this.linkAll = function(root) {\n // NOTE: root may be a DOM Node or a directive\n var elements = _.filter(root.querySelectorAll('muu'), function(element) {\n return !element.classList.contains('muu-initialised');\n });\n return _.map(elements, function(element) {\n return self.link(element);\n });\n };\n };\n\n return Registry;\n});\n\n"]1 -1 \ No newline at end of file
diff --git a/src/muu-directive.js b/src/muu-directive.js
@@ -1,12 +1,13 @@ 1 1 /** 2 2 * Exports the {@link Directive} class. 3 3 * @module muu-directive -1 4 * @ignore 4 5 */5 -1 define(['muu-dom-helpers', 'muu-js-helpers', 'muu-update-dom'], function($, _, updateDOM) {-1 6 define('muu-directive', ['muu-dom-helpers', 'muu-js-helpers', 'muu-update-dom'], function($, _, updateDOM) { 6 7 "use strict"; 7 8 8 9 /**9 -1 * A directive is linked to a DOMElement and manages the DOM tree below-1 10 * A directive is linked to a Element and manages the DOM tree below 10 11 * that element (excluding any isolated subtrees, e.g. those managed by 11 12 * subdirectives). 12 13 * @@ -28,9 +29,9 @@ define(['muu-dom-helpers', 'muu-js-helpers', 'muu-update-dom'], function($, _, u 28 29 * Registry#link}. 29 30 * 30 31 * @constructs Directive31 -1 * @param {DOMElement} root-1 32 * @param {Element} root 32 33 * @param {string} template33 -1 * @param {Muu} registry-1 34 * @param {Registry} registry 34 35 */ 35 36 var Directive = function(root, template, registry) { 36 37 var self = this; @@ -81,8 +82,9 @@ define(['muu-dom-helpers', 'muu-js-helpers', 'muu-update-dom'], function($, _, u 81 82 * 82 83 * @private 83 84 * @param {string} selector84 -1 * @return {DOMElement[]} All child elements that match the given-1 85 * @return {Array.<Element>} All child elements that match the given 85 86 * selector and are not isolated. -1 87 * @nosideeffects 86 88 */ 87 89 this.querySelectorAll = function(selector) { 88 90 var hits = root.querySelectorAll(selector); @@ -104,8 +106,10 @@ define(['muu-dom-helpers', 'muu-js-helpers', 'muu-update-dom'], function($, _, u 104 106 * 105 107 * @private 106 108 * @param {String} selector107 -1 * @return {DOMElement} First child element that matches the given-1 109 * @return {Element} First child element that matches the given 108 110 * selector and is not isolated. -1 111 * @nosideeffects -1 112 * @suppress {missingReturn} 109 113 */ 110 114 this.querySelector = function(selector) { 111 115 var all = self.querySelectorAll(selector); @@ -127,9 +131,10 @@ define(['muu-dom-helpers', 'muu-js-helpers', 'muu-update-dom'], function($, _, u 127 131 * @param {string} name 128 132 * @param {*} [_default] 129 133 * @return {string|number|boolean|*} -1 134 * @nosideeffects 130 135 */ 131 136 this.getModel = function(name, _default) {132 -1 if (name === void 0) {-1 137 if (name === undefined) { 133 138 var model = {}; 134 139 _.forEach(self.querySelectorAll('[name]'), function(element) { 135 140 model[element.name] = self.getModel(element.name); @@ -137,7 +142,7 @@ define(['muu-dom-helpers', 'muu-js-helpers', 'muu-update-dom'], function($, _, u 137 142 return model; 138 143 } else { 139 144 var element = self.querySelector('[name=' + name + ']');140 -1 if (element === void 0) {-1 145 if (element === undefined) { 141 146 return _default; 142 147 } else if (element.type === 'checkbox') { 143 148 return element.checked;
diff --git a/src/muu-dom-helpers.js b/src/muu-dom-helpers.js
@@ -2,7 +2,7 @@ 2 2 * DOM related helper functions 3 3 * @module muu-dom-helpers 4 4 */5 -1 define(['muu-js-helpers'], function(_) {-1 5 define("muu-dom-helpers", ['muu-js-helpers'], function(_) { 6 6 "use strict"; 7 7 8 8 var entityMap = { @@ -22,6 +22,7 @@ define(['muu-js-helpers'], function(_) { 22 22 /** 23 23 * @param {string} string 24 24 * @return {string} - escaped HTML -1 25 * @nosideeffects 25 26 */ 26 27 $.escapeHtml = function(string) { 27 28 return String(string).replace(/[&<>"'\/]/g, function(s) { @@ -37,8 +38,9 @@ define(['muu-js-helpers'], function(_) { 37 38 * 38 39 * @param {string} type 39 40 * @param {*} detail40 -1 * @return {DOMEvent}-1 41 * @return {Event} 41 42 * @see https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events -1 43 * @nosideeffects 42 44 */ 43 45 $.createEvent = function(type, detail) { 44 46 if (typeof CustomEvent === 'function') { @@ -53,10 +55,10 @@ define(['muu-js-helpers'], function(_) { 53 55 }; 54 56 55 57 /**56 -1 * @param {DOMElement} element-1 58 * @param {EventTarget} element 57 59 * @param {string} eventName58 -1 * @param {function} callback59 -1 * @return {Function()} An unregister function-1 60 * @param {Function} callback -1 61 * @return {function()} An unregister function 60 62 */ 61 63 $.on = function(element, eventName, callback) { 62 64 element.addEventListener(eventName, callback, false); @@ -66,8 +68,8 @@ define(['muu-js-helpers'], function(_) { 66 68 }; 67 69 68 70 /**69 -1 * @param {function} fn70 -1 * @return {Function()} An unregister function-1 71 * @param {Function} fn -1 72 * @return {function()} An unregister function 71 73 */ 72 74 $.ready = function(fn) { 73 75 var _fn = _.once(fn); @@ -85,9 +87,10 @@ define(['muu-js-helpers'], function(_) { 85 87 }; 86 88 87 89 /**88 -1 * @param {DOMNode} desc89 -1 * @param {DOMNode} root-1 90 * @param {Node} desc -1 91 * @param {Node} root 90 92 * @return {boolean} -1 93 * @nosideeffects 91 94 */ 92 95 $.isDescendant = function(desc, root) { 93 96 return !!desc && (desc === root || $.isDescendant(desc.parentNode, root)); @@ -100,9 +103,9 @@ define(['muu-js-helpers'], function(_) { 100 103 * element is removed but with a slight delay. So the only way to test this 101 104 * is to use a timeout in the test. 102 105 *103 -1 * @param {DOMElement} element104 -1 * @param {function} fn105 -1 * @return {Function()} An unregister function-1 106 * @param {Element} element -1 107 * @param {Function} fn -1 108 * @return {function()} An unregister function 106 109 */ 107 110 $.destroy = function(element, fn) { 108 111 var unregister; @@ -141,8 +144,10 @@ define(['muu-js-helpers'], function(_) { 141 144 }; 142 145 143 146 /**144 -1 * @param {DOMElement[]} options-1 147 * @param {Array.<Element>} options 145 148 * @return {string} -1 149 * @suppress {missingReturn} -1 150 * @nosideeffects 146 151 */ 147 152 $.getRadio = function(options) { 148 153 for (var i = 0; i < options.length; i++) { @@ -153,7 +158,7 @@ define(['muu-js-helpers'], function(_) { 153 158 }; 154 159 155 160 /**156 -1 * @param {DOMElement[]} options-1 161 * @param {Array.<Element>} options 157 162 * @param {string} value 158 163 */ 159 164 $.setRadio = function(options, value) {
diff --git a/src/muu-js-helpers.js b/src/muu-js-helpers.js
@@ -2,15 +2,16 @@ 2 2 * Minimal implementation of an underscore/lodash subset. 3 3 * @module muu-js-helpers 4 4 */5 -1 define(function() {-1 5 define('muu-js-helpers', [], function() { 6 6 "use strict"; 7 7 8 8 /** @lends module:muu-js-helpers */ 9 9 var _ = {}; 10 10 11 11 /**12 -1 * @param {object} value-1 12 * @param {Object} value 13 13 * @return {string} -1 14 * @nosideeffects 14 15 */ 15 16 var objToString = function(value) { 16 17 return Object.prototype.toString.call(value); @@ -19,6 +20,7 @@ define(function() { 19 20 /** 20 21 * @param {*} value 21 22 * @return {boolean} -1 23 * @nosideeffects 22 24 */ 23 25 _.isString = function(value) { 24 26 return typeof value === 'string' || objToString(value) === '[object String]'; @@ -28,20 +30,23 @@ define(function() { 28 30 * @function 29 31 * @param {*} value 30 32 * @return {boolean} -1 33 * @nosideeffects 31 34 */ 32 35 _.isArray = Array.isArray; 33 36 34 37 /** 35 38 * @param {*} value 36 39 * @return {boolean} -1 40 * @nosideeffects 37 41 */ 38 42 _.isFunction = function(value) { 39 43 return typeof value === 'function'; 40 44 }; 41 45 42 46 /**43 -1 * @param {function} fn44 -1 * @return {function}-1 47 * @param {Function} fn -1 48 * @return {Function} -1 49 * @nosideeffects 45 50 */ 46 51 _.once = function(fn) { 47 52 var result; @@ -57,9 +62,10 @@ define(function() { 57 62 }; 58 63 59 64 /**60 -1 * @param {array} array-1 65 * @param {Array} array 61 66 * @param {*} value 62 67 * @return {number} -1 68 * @nosideeffects 63 69 */ 64 70 _.indexOf = function(array, value) { 65 71 if ('indexOf' in array) { @@ -75,8 +81,8 @@ define(function() { 75 81 }; 76 82 77 83 /**78 -1 * @param {array} array79 -1 * @param {function} fn-1 84 * @param {Array} array -1 85 * @param {Function} fn 80 86 */ 81 87 _.forEach = function(array, fn) { 82 88 if ('forEach' in array) { @@ -89,9 +95,10 @@ define(function() { 89 95 }; 90 96 91 97 /**92 -1 * @param {array} array93 -1 * @param {function} fn94 -1 * @return {array}-1 98 * @param {Array} array -1 99 * @param {Function} fn -1 100 * @return {Array} -1 101 * @nosideeffects 95 102 */ 96 103 _.map = function(array, fn) { 97 104 if ('map' in array) { @@ -106,9 +113,10 @@ define(function() { 106 113 }; 107 114 108 115 /**109 -1 * @param {array} array110 -1 * @param {function} fn111 -1 * @return {array}-1 116 * @param {Array} array -1 117 * @param {Function} fn -1 118 * @return {Array} -1 119 * @nosideeffects 112 120 */ 113 121 _.filter = function(array, fn) { 114 122 if ('filter' in array) { @@ -125,8 +133,9 @@ define(function() { 125 133 }; 126 134 127 135 /**128 -1 * @param {array[]} arrays129 -1 * @return {array}-1 136 * @param {Array.<Array>} arrays -1 137 * @return {Array} -1 138 * @nosideeffects 130 139 */ 131 140 _.union = function(arrays) { 132 141 var results = []; @@ -141,9 +150,10 @@ define(function() { 141 150 }; 142 151 143 152 /**144 -1 * @param {array} a145 -1 * @param {array} b146 -1 * @return {array}-1 153 * @param {Array} a -1 154 * @param {Array} b -1 155 * @return {Array} -1 156 * @nosideeffects 147 157 */ 148 158 _.difference = function(a, b) { 149 159 var results = []; @@ -156,8 +166,9 @@ define(function() { 156 166 }; 157 167 158 168 /**159 -1 * @param {array} a160 -1 * @return {array}-1 169 * @param {Array} a -1 170 * @return {Array} -1 171 * @nosideeffects 161 172 */ 162 173 _.flatten = function(a) { 163 174 var o = [];
diff --git a/src/muu-location.js b/src/muu-location.js
@@ -2,7 +2,7 @@ 2 2 * angular inspired location service. 3 3 * @module muu-location 4 4 */5 -1 define(['muu-search'], function(q) {-1 5 define('muu-location', ['muu-search'], function(q) { 6 6 "use strict"; 7 7 8 8 /** @lends module:muu-location */ @@ -10,6 +10,7 @@ define(['muu-search'], function(q) { 10 10 11 11 /** 12 12 * @return {string} -1 13 * @nosideeffects 13 14 */ 14 15 loc.absUrl = function() { 15 16 return location.href; @@ -17,13 +18,14 @@ define(['muu-search'], function(q) { 17 18 18 19 /** 19 20 * @return {string} -1 21 * @nosideeffects 20 22 *//** 21 23 * @param {string} value 22 24 * @param {boolean} [replace] 23 25 * @return {muu-location} 24 26 */ 25 27 loc.url = function(value, replace) {26 -1 if (value === void 0) {-1 28 if (value === undefined) { 27 29 return location.pathname + location.search + location.hash; 28 30 } else if (replace) { 29 31 history.replaceState(null, null, value); @@ -35,6 +37,7 @@ define(['muu-search'], function(q) { 35 37 36 38 /** 37 39 * @return {string} -1 40 * @nosideeffects 38 41 */ 39 42 loc.protocol = function() { 40 43 return location.protocol; @@ -42,6 +45,7 @@ define(['muu-search'], function(q) { 42 45 43 46 /** 44 47 * @return {string} -1 48 * @nosideeffects 45 49 */ 46 50 loc.host = function() { 47 51 return location.host; @@ -49,6 +53,7 @@ define(['muu-search'], function(q) { 49 53 50 54 /** 51 55 * @return {string} -1 56 * @nosideeffects 52 57 */ 53 58 loc.port = function() { 54 59 return location.port; @@ -56,13 +61,14 @@ define(['muu-search'], function(q) { 56 61 57 62 /** 58 63 * @return {string} -1 64 * @nosideeffects 59 65 *//** 60 66 * @param {string} value 61 67 * @param {boolean} [replace] 62 68 * @return {muu-location} 63 69 */ 64 70 loc.path = function(value, replace) {65 -1 if (value === void 0) {-1 71 if (value === undefined) { 66 72 return location.pathname; 67 73 } else { 68 74 var url = value + location.search + location.hash; @@ -72,7 +78,7 @@ define(['muu-search'], function(q) { 72 78 }; 73 79 74 80 var _search = function(value, replace) {75 -1 if (value === void 0) {-1 81 if (value === undefined) { 76 82 return location.search; 77 83 } else { 78 84 if (value && value[0] !== '?') { @@ -89,7 +95,8 @@ define(['muu-search'], function(q) { 89 95 }; 90 96 91 97 /**92 -1 * @return {object}-1 98 * @return {Object} -1 99 * @nosideeffects 93 100 *//** 94 101 * @param {string|object} value 95 102 * @return {muu-location} @@ -100,8 +107,8 @@ define(['muu-search'], function(q) { 100 107 * @return {muu-location} 101 108 */ 102 109 loc.search = function(key, value, replace) {103 -1 if (key !== void 0) {104 -1 if (value !== void 0) {-1 110 if (key !== undefined) { -1 111 if (value !== undefined) { 105 112 var search = q.parse(_search()); 106 113 search[key] = value; 107 114 return _search(q.unparse(search), replace); @@ -115,13 +122,14 @@ define(['muu-search'], function(q) { 115 122 116 123 /** 117 124 * @return {string} -1 125 * @nosideeffects 118 126 *//** 119 127 * @param {string} value 120 128 * @param {boolean} [replace] 121 129 * @return {muu-location} 122 130 */ 123 131 loc.hash = function(value, replace) {124 -1 if (value === void 0) {-1 132 if (value === undefined) { 125 133 if (location.hash) { 126 134 return location.hash.slice(1); 127 135 } else { @@ -136,7 +144,7 @@ define(['muu-search'], function(q) { 136 144 137 145 /** 138 146 * @param {string} eventName139 -1 * @param {function} fn-1 147 * @param {Function} fn 140 148 * @return {muu-location} 141 149 */ 142 150 loc.addEventListener = function(eventName, fn) { @@ -148,7 +156,7 @@ define(['muu-search'], function(q) { 148 156 149 157 /** 150 158 * @param {string} eventName151 -1 * @param {function} fn-1 159 * @param {Function} fn 152 160 * @return {muu-location} 153 161 */ 154 162 loc.removeEventListener = function(eventName, fn) {
diff --git a/src/muu.js b/src/muu-registry.js
@@ -1,18 +1,19 @@ 1 1 /** 2 2 * Exports the {@link Registry} class.3 -1 * @module muu-1 3 * @module muu-registry -1 4 * @ignore 4 5 */5 -1 define(['muu-template', 'muu-directive', 'muu-js-helpers', 'muu-dom-helpers'], function(muuTemplate, Directive, _, $) {-1 6 define('muu-registry', ['muu-template', 'muu-directive', 'muu-js-helpers', 'muu-dom-helpers'], function(muuTemplate, Directive, _, $) { 6 7 "use strict"; 7 8 8 9 /** 9 10 * @constructs Registry10 -1 * @param {object} config The config object may have following properties:-1 11 * @param {Object} config The config object may have following properties: 11 12 * 12 13 * - **debug** - `{boolean}` - Enable debug mode. In debug mode, 13 14 * directive objects are available as properties from the DOM as 14 15 * `element.directive`.15 -1 * - **renderer** - `{Function(string, object)}` - The template renderer-1 16 * - **renderer** - `{function(string, Object)}` - The template renderer 16 17 * to be used. Defaults to {@link module:muu-template}. 17 18 */ 18 19 var Registry = function(config) { @@ -27,9 +28,9 @@ define(['muu-template', 'muu-directive', 'muu-js-helpers', 'muu-dom-helpers'], f 27 28 * 28 29 * @param {string} type 29 30 * @param {string} template30 -1 * @param {Function(Directive, DOMElement): function} link The link-1 31 * @param {function(Directive, Element): Function} link The link 31 32 * function is called with an instance of {@link Directive} and a32 -1 * DOMElement when {@link Registry#link} is executed.-1 33 * Element when {@link Registry#link} is executed. 33 34 * 34 35 * It is the only place where you can access a directive and 35 36 * therefore the place where you define its behavior. @@ -69,7 +70,7 @@ define(['muu-template', 'muu-directive', 'muu-js-helpers', 'muu-dom-helpers'], f 69 70 * }); 70 71 * ``` 71 72 *72 -1 * @param {Function(Registry)}-1 73 * @param {function(Registry)} module 73 74 * @return {Registry} this 74 75 */ 75 76 this.registerModule = function(module) { @@ -80,12 +81,12 @@ define(['muu-template', 'muu-directive', 'muu-js-helpers', 'muu-dom-helpers'], f 80 81 /** 81 82 * Create and initialise a {@link Directive} for `element`. 82 83 *83 -1 * @param {DOMElement} element-1 84 * @param {Element} element 84 85 * @param {string} type 85 86 * @return {Directive} 86 87 */ 87 88 this.link = function(element, type) {88 -1 if (type === void 0) {-1 89 if (type === undefined) { 89 90 type = element.getAttribute('type'); 90 91 } 91 92 @@ -105,7 +106,7 @@ define(['muu-template', 'muu-directive', 'muu-js-helpers', 'muu-dom-helpers'], f 105 106 element.directive = directive; 106 107 } 107 108108 -1 if (unlink !== void 0) {-1 109 if (unlink !== undefined) { 109 110 $.destroy(element, unlink); 110 111 } 111 112 @@ -115,8 +116,8 @@ define(['muu-template', 'muu-directive', 'muu-js-helpers', 'muu-dom-helpers'], f 115 116 /** 116 117 * Link all directives that can be found inside `root`. 117 118 *118 -1 * @param {DOMElement} root119 -1 * @return {Directive[]}-1 119 * @param {Element} root -1 120 * @return {Array.<Directive>} 120 121 */ 121 122 this.linkAll = function(root) { 122 123 // NOTE: root may be a DOM Node or a directive
diff --git a/src/muu-search.js b/src/muu-search.js
@@ -1,4 +1,4 @@1 -1 define(['muu-js-helpers'], function(_) {-1 1 define('muu-search', ['muu-js-helpers'], function(_) { 2 2 "use strict"; 3 3 4 4 var q = {}; @@ -28,7 +28,7 @@ define(['muu-js-helpers'], function(_) { 28 28 }; 29 29 30 30 var unparseItem = function(key, value) {31 -1 if (value === void 0 || value === null || value === false) {-1 31 if (value === undefined || value === null || value === false) { 32 32 return []; 33 33 } else if (_.isArray(value)) { 34 34 return _.flatten(_.map(value, function(v) {
diff --git a/src/muu-template.js b/src/muu-template.js
@@ -58,10 +58,11 @@ 58 58 * 59 59 * @module muu-template 60 60 * @param {string} template61 -1 * @param {object} data-1 61 * @param {Object} data 62 62 * @return {string} -1 63 * @nosideeffects 63 64 */64 -1 define(['muu-js-helpers', 'muu-dom-helpers'], function(_, $) {-1 65 define('muu-template', ['muu-js-helpers', 'muu-dom-helpers'], function(_, $) { 65 66 "use strict"; 66 67 67 68 var openTag = '{{'; @@ -159,7 +160,7 @@ define(['muu-js-helpers', 'muu-dom-helpers'], function(_, $) { 159 160 var parseTemplate = function(template, loopName) { 160 161 var openIndex = template.indexOf(openTag); 161 162 if (openIndex === -1) {162 -1 if (loopName === void 0) {-1 163 if (loopName === undefined) { 163 164 return function() { 164 165 return template; 165 166 };
diff --git a/src/muu-update-dom.js b/src/muu-update-dom.js
@@ -18,10 +18,10 @@ 18 18 * generated content from being overwritten. 19 19 * 20 20 * @module muu-update-dom21 -1 * @param {DOMElement} target22 -1 * @param {DOMElement} source-1 21 * @param {Element} target -1 22 * @param {Element} source 23 23 */24 -1 define(['muu-js-helpers'], function(_) {-1 24 define('muu-update-dom', ['muu-js-helpers'], function(_) { 25 25 "use strict"; 26 26 27 27 var updateAttributes = function(target, source) {
diff --git a/src/muu.js b/src/muu.js
@@ -1,133 +1,20 @@ 1 1 /**2 -1 * Exports the {@link Registry} class.-1 2 * This module gives access to the following objects: -1 3 * -1 4 * - `Registry` - {@link Registry} -1 5 * - `$` - {@link module:muu-dom-helpers} -1 6 * - `$location` - {@link module:muu-location} -1 7 * 3 8 * @module muu 4 9 */5 -1 define(['muu-template', 'muu-directive', 'muu-js-helpers', 'muu-dom-helpers'], function(muuTemplate, Directive, _, $) {-1 10 define('muu', ['muu-registry', 'muu-dom-helpers', 'muu-location'], function(Registry, $, $location) { 6 11 "use strict"; 7 128 -1 /**9 -1 * @constructs Registry10 -1 * @param {object} config The config object may have following properties:11 -1 *12 -1 * - **debug** - `{boolean}` - Enable debug mode. In debug mode,13 -1 * directive objects are available as properties from the DOM as14 -1 * `element.directive`.15 -1 * - **renderer** - `{Function(string, object)}` - The template renderer16 -1 * to be used. Defaults to {@link module:muu-template}.17 -1 */18 -1 var Registry = function(config) {19 -1 var self = this;20 -1 var directives = {};-1 13 var module = {}; 21 1422 -1 this.config = config || {};23 -1 this.renderer = self.config.renderer || muuTemplate;-1 15 module.Registry = Registry; -1 16 module.$ = $; -1 17 module.$location = $location; 24 1825 -1 /**26 -1 * Register a new type of {@link Directive}27 -1 *28 -1 * @param {string} type29 -1 * @param {string} template30 -1 * @param {Function(Directive, DOMElement): function} link The link31 -1 * function is called with an instance of {@link Directive} and a32 -1 * DOMElement when {@link Registry#link} is executed.33 -1 *34 -1 * It is the only place where you can access a directive and35 -1 * therefore the place where you define its behavior.36 -1 *37 -1 * This typically means to make an initial call to {@link38 -1 * Directive#update} and to add some event listeners. You should also39 -1 * return an *unlink* function that clears all external references in40 -1 * order to avoid memory leaks.41 -1 * @return {Registry} this42 -1 */43 -1 this.registerDirective = function(type, template, link) {44 -1 directives[type] = {45 -1 template: template,46 -1 link: link47 -1 };48 -1 return self;49 -1 };50 -151 -1 /**52 -1 * Shortcut for wrapping calls to {@link Registry} in a function.53 -1 *54 -1 * This can be esepcially helpful if that function is defined in a55 -1 * different module.56 -1 *57 -1 * ```.js58 -1 * define('foobar', [], function() {59 -1 * return function(registry) {60 -1 * registry61 -1 * .registerDirective('foo', '...', function() {...})62 -1 * .registerDirective('bar', '...', function() {...});63 -1 * };64 -1 * });65 -1 *66 -1 * require(['foobar'], function(foobar) {67 -1 * var registry = new Registry();68 -1 * registry.registerModule(foobar);69 -1 * });70 -1 * ```71 -1 *72 -1 * @param {Function(Registry)}73 -1 * @return {Registry} this74 -1 */75 -1 this.registerModule = function(module) {76 -1 module(self);77 -1 return self;78 -1 };79 -180 -1 /**81 -1 * Create and initialise a {@link Directive} for `element`.82 -1 *83 -1 * @param {DOMElement} element84 -1 * @param {string} type85 -1 * @return {Directive}86 -1 */87 -1 this.link = function(element, type) {88 -1 if (type === void 0) {89 -1 type = element.getAttribute('type');90 -1 }91 -192 -1 if (!directives.hasOwnProperty(type)) {93 -1 throw new Error('Unknown directive type: ' + type);94 -1 }95 -196 -1 var template = directives[type].template;97 -1 var link = directives[type].link;98 -199 -1 var directive = new Directive(element, template, self);100 -1 var unlink = link(directive, element);101 -1 element.classList.add('muu-isolate');102 -1 element.classList.add('muu-initialised');103 -1104 -1 if (self.config.debug) {105 -1 element.directive = directive;106 -1 }107 -1108 -1 if (unlink !== void 0) {109 -1 $.destroy(element, unlink);110 -1 }111 -1112 -1 return directive;113 -1 };114 -1115 -1 /**116 -1 * Link all directives that can be found inside `root`.117 -1 *118 -1 * @param {DOMElement} root119 -1 * @return {Directive[]}120 -1 */121 -1 this.linkAll = function(root) {122 -1 // NOTE: root may be a DOM Node or a directive123 -1 var elements = _.filter(root.querySelectorAll('muu'), function(element) {124 -1 return !element.classList.contains('muu-initialised');125 -1 });126 -1 return _.map(elements, function(element) {127 -1 return self.link(element);128 -1 });129 -1 };130 -1 };131 -1132 -1 return Registry;-1 19 return module; 133 20 });
diff --git a/test/test-registry.js b/test/test-registry.js
@@ -1,5 +1,5 @@ 1 1 /* global define, describe, it, expect, beforeEach, sinon */2 -1 define(['muu', 'muu-directive', 'muu-js-helpers'], function(Registry, Directive, _) {-1 2 define(['muu-registry', 'muu-directive', 'muu-js-helpers'], function(Registry, Directive, _) { 3 3 "use strict"; 4 4 5 5 describe('Registry', function() {