muu

DEPRECATED lightweight JS framework
git clone https://git.ce9e.org/muu.git

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.js
    2    -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    36 
    9    -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                 '&': '&amp;',
   -1   237                 '<': '&lt;',
   -1   238                 '>': '&gt;',
   -1   239                 '"': '&quot;',
   -1   240                 "'": '&#39;',
   -1   241                 '/': '&#x2F;'
   -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={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","/":"&#x2F;"},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                 '&': '&amp;',
   -1   237                 '<': '&lt;',
   -1   238                 '>': '&gt;',
   -1   239                 '"': '&quot;',
   -1   240                 "'": '&#39;',
   -1   241                 '/': '&#x2F;'
   -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={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","/":"&#x2F;"},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     9 
   10    -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    12 
   13    -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    56 
   57    -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     3 
    4    -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     7 
    8    -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    17 
   18    -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    39 
   40    -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={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","/":"&#x2F;"},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.ma
    2    -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        '&': '&amp;',\n        '<': '&lt;',\n        '>': '&gt;',\n        '\"': '&quot;',\n        \"'\": '&#39;',\n        '/': '&#x2F;'\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 Directive
   31    -1      * @param {DOMElement} root
   -1    32      * @param {Element} root
   32    33      * @param {string} template
   33    -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} selector
   84    -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} selector
  107    -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 {*} detail
   40    -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} eventName
   58    -1      * @param {function} callback
   59    -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} fn
   70    -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} desc
   89    -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} element
  104    -1      * @param {function} fn
  105    -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} fn
   44    -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} array
   79    -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} array
   93    -1      * @param {function} fn
   94    -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} array
  110    -1      * @param {function} fn
  111    -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[]} arrays
  129    -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} a
  145    -1      * @param {array} b
  146    -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} a
  160    -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} eventName
  139    -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} eventName
  151    -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 Registry
   10    -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} template
   30    -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 a
   32    -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   108 
  108    -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} root
  119    -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} template
   61    -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-dom
   21    -1  * @param {DOMElement} target
   22    -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    12 
    8    -1     /**
    9    -1      * @constructs Registry
   10    -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 as
   14    -1      *   `element.directive`.
   15    -1      * - **renderer** - `{Function(string, object)}` - The template renderer
   16    -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    14 
   22    -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    18 
   25    -1         /**
   26    -1          * Register a new type of {@link Directive}
   27    -1          *
   28    -1          * @param {string} type
   29    -1          * @param {string} template
   30    -1          * @param {Function(Directive, DOMElement): function} link The link
   31    -1          *   function is called with an instance of {@link Directive} and a
   32    -1          *   DOMElement when {@link Registry#link} is executed.
   33    -1          *
   34    -1          *   It is the only place where you can access a directive and
   35    -1          *   therefore the place where you define its behavior.
   36    -1          *
   37    -1          *   This typically means to make an initial call to {@link
   38    -1          *   Directive#update} and to add some event listeners. You should also
   39    -1          *   return an *unlink* function that clears all external references in
   40    -1          *   order to avoid memory leaks.
   41    -1          * @return {Registry} this
   42    -1          */
   43    -1         this.registerDirective = function(type, template, link) {
   44    -1             directives[type] = {
   45    -1                 template: template,
   46    -1                 link: link
   47    -1             };
   48    -1             return self;
   49    -1         };
   50    -1 
   51    -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 a
   55    -1          * different module.
   56    -1          *
   57    -1          * ```.js
   58    -1          * define('foobar', [], function() {
   59    -1          *   return function(registry) {
   60    -1          *     registry
   61    -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} this
   74    -1          */
   75    -1         this.registerModule = function(module) {
   76    -1             module(self);
   77    -1             return self;
   78    -1         };
   79    -1 
   80    -1         /**
   81    -1          * Create and initialise a {@link Directive} for `element`.
   82    -1          *
   83    -1          * @param {DOMElement} element
   84    -1          * @param {string} type
   85    -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    -1 
   92    -1             if (!directives.hasOwnProperty(type)) {
   93    -1                 throw new Error('Unknown directive type: ' + type);
   94    -1             }
   95    -1 
   96    -1             var template = directives[type].template;
   97    -1             var link = directives[type].link;
   98    -1 
   99    -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    -1 
  104    -1             if (self.config.debug) {
  105    -1                 element.directive = directive;
  106    -1             }
  107    -1 
  108    -1             if (unlink !== void 0) {
  109    -1                 $.destroy(element, unlink);
  110    -1             }
  111    -1 
  112    -1             return directive;
  113    -1         };
  114    -1 
  115    -1         /**
  116    -1          * Link all directives that can be found inside `root`.
  117    -1          *
  118    -1          * @param {DOMElement} root
  119    -1          * @return {Directive[]}
  120    -1          */
  121    -1         this.linkAll = function(root) {
  122    -1             // NOTE: root may be a DOM Node or a directive
  123    -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    -1 
  132    -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() {