'))
);
} else {
+ console.debug('KEEGAN: itemType', item.type);
var apiItem = {};
var itemType = itemTypes[item.type];
- var templateData = $.extend({}, item, {
- attributes: $.map(item.attributes || [], function(item, key) {
- console.log(index);
- return {
- key: key,
- value: item.toString(),
- index: index++
- };
- })
- });
+ var templateData = {
+ label: '',
+ options: [],
+ attributes: {}
+ };
- apiItem.$element = $(mustache.render(itemType.template, templateData));
- apiItem.$manipulatorTarget = apiItem.$element.find('[data-manipulator-target]');
+ console.debug('KEEGAN: templateData', templateData);
+
+ _.extend(templateData, item);
+ apiItem.$element = HTML(_.formatHtml(itemType.template, templateData));
+ apiItem.$manipulatorTarget =
+ apiItem.$element.select('[data-manipulator-target]');
// this caters for situations where the manipulator target is the root element
if (!apiItem.$manipulatorTarget.length) {
@@ -93,26 +92,44 @@ function processConfigItem(item, $parent) {
}
// proxy event related methods
+ var eventProxies = {};
apiItem.on = function(events, handler) {
- return apiItem.$manipulatorTarget.on(events, $.proxy(handler, apiItem));
+ eventProxies[handler] = function(event) {
+ handler.call(apiItem, event);
+ };
+ return apiItem.$manipulatorTarget.on(events, eventProxies[handler]);
};
apiItem.one = function(events, handler) {
- return apiItem.$manipulatorTarget.one(events, $.proxy(handler, apiItem));
+ eventProxies[handler] = function(event) {
+ handler.call(apiItem, event);
+ $.off(eventProxies[handler]);
+ };
+ return apiItem.$manipulatorTarget.on(events, eventProxies[handler]);
};
- apiItem.off = apiItem.$manipulatorTarget.off.bind(apiItem.$manipulatorTarget);
- apiItem.triggerHandler =
- apiItem.$manipulatorTarget.triggerHandler.bind(apiItem.$manipulatorTarget);
+ apiItem.off = function(handler) {
+ return $.off(eventProxies[handler]);
+ };
+
+ apiItem.trigger =
+ apiItem.$manipulatorTarget.trigger.bind(apiItem.$manipulatorTarget);
// attach the manipulator methods to the apiItem
- $.each(itemType.manipulator, function(methodName, method) {
+ _.eachObj(itemType.manipulator, function(methodName, method) {
apiItem[methodName] = method.bind(apiItem);
});
+ apiItem.config = item;
+
+ // attach the initialize method to the API.
+ apiItem.iniialize = typeof itemType.initialize === 'function' ?
+ itemType.initialize :
+ function() {};
+ apiItem.iniialize.bind(apiItem);
+ apiItem.iniialize();
+
// set the value of the item via the manipulator to ensure consistency
apiItem.set(getSetting(item.app_key, item.value));
- apiItem.config = item;
-
if (item.id) {
api.itemsById[item.id] = apiItem;
}
@@ -123,7 +140,7 @@ function processConfigItem(item, $parent) {
api.items.push(apiItem);
- $parent.append(apiItem.$element);
+ $parent.add(apiItem.$element);
}
}
diff --git a/src/scripts/lib/item-types.js b/src/scripts/lib/item-types.js
deleted file mode 100755
index a93fc3e..0000000
--- a/src/scripts/lib/item-types.js
+++ /dev/null
@@ -1,42 +0,0 @@
-'use strict';
-
-var manipulators = require('./manipulators');
-
-module.exports = {
- heading: {
- template: require('../../templates/components/heading.mustache'),
- manipulator: manipulators.html
- },
- subheading: {
- template: require('../../templates/components/subheading.mustache'),
- manipulator: manipulators.html
- },
- footer: {
- template: require('../../templates/components/footer.mustache'),
- manipulator: manipulators.html
- },
- input: {
- template: require('../../templates/components/input.mustache'),
- manipulator: manipulators.val
- },
- color: {
- template: require('../../templates/components/color.mustache'),
- manipulator: manipulators.val
- },
- select: {
- template: require('../../templates/components/select.mustache'),
- manipulator: manipulators.val
- },
- toggle: {
- template: require('../../templates/components/toggle.mustache'),
- manipulator: manipulators.checked
- },
- radiogroup: {
- template: require('../../templates/components/radiogroup.mustache'),
- manipulator: manipulators.radiogroup
- },
- submit: {
- template: require('../../templates/components/submit.mustache'),
- manipulator: manipulators.val
- }
-};
diff --git a/src/scripts/lib/items.js b/src/scripts/lib/items.js
new file mode 100755
index 0000000..ae96c67
--- /dev/null
+++ b/src/scripts/lib/items.js
@@ -0,0 +1,13 @@
+'use strict';
+
+module.exports = {
+ heading: require('./items/heading'),
+ subheading: require('./items/subheading'),
+ footer: require('./items/footer'),
+ input: require('./items/input'),
+ color: require('./items/color'),
+ select: require('./items/select'),
+ toggle: require('./items/toggle'),
+ radiogroup: require('./items/radiogroup'),
+ submit: require('./items/submit')
+};
diff --git a/src/scripts/lib/items/color.js b/src/scripts/lib/items/color.js
new file mode 100644
index 0000000..1c56496
--- /dev/null
+++ b/src/scripts/lib/items/color.js
@@ -0,0 +1,87 @@
+'use strict';
+
+var HTML = require('../../vendor/minified/minified').HTML;
+
+module.exports = {
+ template: require('../../../templates/items/color.tpl'),
+ manipulator: require('../manipulators').val,
+ initialize: function() {
+ var self = this;
+ console.debug('KEEGAN: initializer', self);
+
+ /* eslint-disable comma-spacing, no-multi-spaces, max-len,
+ standard/array-bracket-even-spacing */
+ var layout = self.config.layout || [
+ [false , false , '#55FF00', '#AAFF55', false , '#FFFF55', '#FFFFAA', false , false ],
+ [false , '#AAFFAA', '#55FF55', '#00FF00', '#AAFF00', '#FFFF00', '#FFAA55', '#FFAAAA', false ],
+ ['#55FFAA', '#00FF55', '#00AA00', '#55AA00', '#AAAA55', '#AAAA00', '#FFAA00', '#FF5500', '#FF5555'],
+ ['#AAFFFF', '#00FFAA', '#00AA55', '#55AA55', '#005500', '#555500', '#AA5500', '#FF0000', '#FF0055'],
+ [false , '#55AAAA', '#00AAAA', '#005555', '#FFFFFF', '#000000', '#AA5555', '#AA0000', false ],
+ ['#55FFFF', '#00FFFF', '#00AAFF', '#0055AA', '#AAAAAA', '#555555', '#550000', '#AA0055', '#FF55AA'],
+ ['#55AAFF', '#0055FF', '#0000FF', '#0000AA', '#000055', '#550055', '#AA00AA', '#FF00AA', '#FFAAFF'],
+ [false , '#5555AA', '#5555FF', '#5500FF', '#5500AA', '#AA00FF', '#FF00FF', '#FF55FF', false ],
+ [false , false , false , '#AAAAFF', '#AA55FF', '#AA55AA', false , false , false ]
+ ];
+ /* eslint-enable */
+
+ var grid = '';
+ var itemWidth = 100 / layout[0].length;
+ var itemHeight = 100 / layout.length;
+ var boxHeight = itemWidth * layout.length;
+ var $elem = self.$element;
+
+ for (var i = 0; i < layout.length; i++) {
+ for (var j = 0; j < layout[i].length; j++) {
+
+ var color = layout[i][j] || 'transparent';
+ var selectable = (color !== 'transparent' ? ' selectable' : '');
+
+ var roundedTL = (i === 0 && j === 0) || i === 0 && !layout[i][j - 1] ||
+ !layout[i][j - 1] && !layout[i - 1][j] ?
+ ' rounded-tl' :
+ '';
+
+ var roundedTR = i === 0 && !layout[i][j + 1] ||
+ !layout[i][j + 1] && !layout[i - 1][j] ?
+ ' rounded-tr ' :
+ '';
+
+ var roundedBL = (i === layout.length - 1 && j === 0) ||
+ i === layout.length - 1 && !layout[i][j - 1] ||
+ !layout[i][j - 1] && !layout[i + 1][j] ?
+ ' rounded-bl' :
+ '';
+
+ var roundedBR = i === layout.length - 1 && !layout[i][j + 1] ||
+ !layout[i][j + 1] && !layout[i + 1][j] ?
+ ' rounded-br' :
+ '';
+
+ grid += '' +
+ '';
+ }
+ }
+
+ $elem.select('.color-box-container').add(HTML(grid));
+
+ var $injectedColor = $elem.select('.color-box-container')
+ var $valueDisplay = $injectedColor.select('.value');
+
+ $elem.on('click', function(ev) {
+ $elem.select('.color-box-wrap').set('show');
+ });
+
+ self.on('change', function(ev) {
+ var value = self.get().replace(/^0x/, '').toLowerCase();
+ $valueDisplay.set('$background-color', '#' + value);
+ });
+
+ }
+}
diff --git a/src/scripts/lib/items/footer.js b/src/scripts/lib/items/footer.js
new file mode 100644
index 0000000..4313443
--- /dev/null
+++ b/src/scripts/lib/items/footer.js
@@ -0,0 +1,6 @@
+'use strict';
+
+module.exports = {
+ template: require('../../../templates/items/footer.tpl'),
+ manipulator: require('../manipulators').html
+};
diff --git a/src/scripts/lib/items/heading.js b/src/scripts/lib/items/heading.js
new file mode 100644
index 0000000..37d72e8
--- /dev/null
+++ b/src/scripts/lib/items/heading.js
@@ -0,0 +1,6 @@
+'use strict';
+
+module.exports = {
+ template: require('../../../templates/items/heading.tpl'),
+ manipulator: require('../manipulators').html
+};
diff --git a/src/scripts/lib/items/input.js b/src/scripts/lib/items/input.js
new file mode 100644
index 0000000..c6c0fad
--- /dev/null
+++ b/src/scripts/lib/items/input.js
@@ -0,0 +1,6 @@
+'use strict';
+
+module.exports = {
+ template: require('../../../templates/items/input.tpl'),
+ manipulator: require('../manipulators').val
+};
diff --git a/src/scripts/lib/items/radiogroup.js b/src/scripts/lib/items/radiogroup.js
new file mode 100644
index 0000000..f581230
--- /dev/null
+++ b/src/scripts/lib/items/radiogroup.js
@@ -0,0 +1,6 @@
+'use strict';
+
+module.exports = {
+ template: require('../../../templates/items/radiogroup.tpl'),
+ manipulator: require('../manipulators').radiogroup
+};
diff --git a/src/scripts/lib/items/select.js b/src/scripts/lib/items/select.js
new file mode 100644
index 0000000..07a5c8d
--- /dev/null
+++ b/src/scripts/lib/items/select.js
@@ -0,0 +1,6 @@
+'use strict';
+
+module.exports = {
+ template: require('../../../templates/items/select.tpl'),
+ manipulator: require('../manipulators').val
+};
diff --git a/src/scripts/lib/items/subheading.js b/src/scripts/lib/items/subheading.js
new file mode 100644
index 0000000..c99b7ef
--- /dev/null
+++ b/src/scripts/lib/items/subheading.js
@@ -0,0 +1,6 @@
+'use strict';
+
+module.exports = {
+ template: require('../../../templates/items/subheading.tpl'),
+ manipulator: require('../manipulators').html
+};
diff --git a/src/scripts/lib/items/submit.js b/src/scripts/lib/items/submit.js
new file mode 100644
index 0000000..3082c23
--- /dev/null
+++ b/src/scripts/lib/items/submit.js
@@ -0,0 +1,6 @@
+'use strict';
+
+module.exports = {
+ template: require('../../../templates/items/submit.tpl'),
+ manipulator: require('../manipulators').val
+};
diff --git a/src/scripts/lib/items/toggle.js b/src/scripts/lib/items/toggle.js
new file mode 100644
index 0000000..6c33742
--- /dev/null
+++ b/src/scripts/lib/items/toggle.js
@@ -0,0 +1,6 @@
+'use strict';
+
+module.exports = {
+ template: require('../../../templates/items/toggle.tpl'),
+ manipulator: require('../manipulators').checked
+};
diff --git a/src/scripts/lib/manipulators.js b/src/scripts/lib/manipulators.js
index ddc583a..e6be686 100755
--- a/src/scripts/lib/manipulators.js
+++ b/src/scripts/lib/manipulators.js
@@ -3,45 +3,45 @@
module.exports = {
html: {
get: function() {
- return this.$manipulatorTarget.html();
+ return this.$manipulatorTarget.get('innerHTML');
},
set: function(value) {
- return this.$manipulatorTarget.html(value)
- .triggerHandler('change');
+ return this.$manipulatorTarget.set('innerHTML', value)
+ .trigger('change');
}
},
val: {
get: function() {
- return this.$manipulatorTarget.val();
+ return this.$manipulatorTarget.get('value');
},
set: function(value) {
- return this.$manipulatorTarget.val(value)
- .triggerHandler('change');
+ return this.$manipulatorTarget.set('value', value)
+ .trigger('change');
},
disable: function() {
- return this.$manipulatorTarget.prop('disabled', true)
- .triggerHandler('change');
+ return this.$manipulatorTarget.set('disabled', true)
+ .trigger('change');
},
enable: function() {
- return this.$manipulatorTarget.prop('disabled', false)
- .triggerHandler('change');
+ return this.$manipulatorTarget.set('disabled', false)
+ .trigger('change');
}
},
checked: {
get: function() {
- return this.$manipulatorTarget.prop('checked');
+ return this.$manipulatorTarget.get('checked');
},
set: function(value) {
- return this.$manipulatorTarget.prop('checked', value)
- .triggerHandler('change');
+ return this.$manipulatorTarget.set('checked', value)
+ .trigger('change');
},
disable: function() {
- return this.$manipulatorTarget.prop('disabled', true)
- .triggerHandler('change');
+ return this.$manipulatorTarget.set('disabled', true)
+ .trigger('change');
},
enable: function() {
- return this.$manipulatorTarget.prop('disabled', false)
- .triggerHandler('change');
+ return this.$manipulatorTarget.set('disabled', false)
+ .trigger('change');
}
},
radiogroup: {
@@ -51,15 +51,15 @@ module.exports = {
set: function(value) {
this.$manipulatorTarget.find('[value="' + value + '"]')
.prop('checked', true);
- return this.$manipulatorTarget.triggerHandler('change');
+ return this.$manipulatorTarget.trigger('change');
},
disable: function() {
- return this.$manipulatorTarget.prop('disabled', true)
- .triggerHandler('change');
+ return this.$manipulatorTarget.set('disabled', true)
+ .trigger('change');
},
enable: function() {
- return this.$manipulatorTarget.prop('disabled', false)
- .triggerHandler('change');
+ return this.$manipulatorTarget.set('disabled', false)
+ .trigger('change');
}
}
};
diff --git a/src/scripts/vendor/minified/minified.js b/src/scripts/vendor/minified/minified.js
new file mode 100644
index 0000000..114a125
--- /dev/null
+++ b/src/scripts/vendor/minified/minified.js
@@ -0,0 +1,3676 @@
+// minified.js config start -- use this comment to re-create a configuration in the Builder
+// - Only sections add, always, amdsupport, dollardollar, each,
+// - eachobj, error, extend, filter, find, format, formathtml, get, html, isobject,
+// - map, mapobj, off, on, ready, request, select, set, template, trigger,
+// - underscore, wait.
+
+
+// WARNING! This file is autogenerated from minified-master.js and others.
+
+/*
+ * Minified.js - Lightweight Client-Side JavaScript Library (full package)
+ * Version: Version 2014 beta 5 b2
+ *
+ * Public Domain. Use, modify and distribute it any way you like. No attribution required.
+ * To the extent possible under law, Tim Jansen has waived all copyright and related or neighboring rights to Minified.
+ * Please see http://creativecommons.org/publicdomain/zero/1.0/.
+ * NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
+ *
+ * Contains code based on https://github.com/douglascrockford/JSON-js (also Public Domain).
+ *
+ * https://github.com/timjansen/minified.js
+ */
+// ==ClosureCompiler==
+// @output_file_name minified.js
+// @compilation_level ADVANCED_OPTIMIZATIONS
+// ==/ClosureCompiler==
+
+/*$
+ * @id ALL
+ * @doc no
+ * @required
+ * This id allows identifying whether both Web and Util are available.
+ */
+
+///#snippet commonAmdStart
+
+/*$
+ * @id require
+ * @name require()
+ * @syntax require(name)
+ * @group OPTIONS
+ * @module WEB, UTIL
+ * Returns a reference to a module. If you do not use an AMD loader to load Minified, just call require() with the
+ * argument 'minified' to get a reference to Minified. You can also access all modules defined using ##define().
+ *
+ * If you do use an AMD loader, Minified will not define this function and you can use the AMD loader to obtain the
+ * reference to Minified.
+ * Minified's version of require is very simple and will only support Minified and other libraries designed
+ * for Minfied, but no real AMD libraries. If you need to work with libraries requiring AMD, you need a real AMD loader.
+ *
+ * @param name the name of the module to request. Minified is available as 'minified'.
+ * @return the reference to the module. Use the name 'minified' to get Minified. You can also access any modules defined using
+ * ##define(). If the name is unknown, it returns undefined.
+ *
+ * @see ##define() allows you to define modules that can be obtained using require().
+ */
+
+/*$
+ * @id define
+ * @name define()
+ * @syntax define(name, factoryFunction)
+ * @group OPTIONS
+ * @module WEB, UTIL
+ * Defines a module that can be returned by ##require(), in case you don't have a AMD loader. If you have a AMD loader before you include Minified,
+ * define() will not be set and you can use the AMD loader's (more powerful) variant.
+ *
+ * Minified's versions of require() and define() are very simple and can not resolve things like circular references.
+ * Also, they are not AMD-compatible and only useful for simple modules. If you need to work with real AMD libraries that are not written
+ * for Minified, you need a real AMD loader.
+ *
+ * @example Creates a simple module and uses it:
+ *
+ * define('makeGreen', function(require) {
+ * var MINI = require('minified'), $ = MINI.$; // obtain own ref to Minified
+ * return function(list) {
+ * $(list).set({$color: '#0f0', $backgroundColor: '#050'});
+ * });
+ * });
+ *
+ * var makeGreen = require('makeGreen');
+ * makeGreen('.notGreenEnough');
+ *
+ *
+ * @param name the name of the module to request. In Minified's implementation, only 'minified' is supported.
+ * @param factoryFunction is a function(require) will be called the first time the name is defined to obtain the module
+ * reference. It received a reference to ##require() (which is required for AMD backward-compatibility) and
+ * must return the value that is returned by ##require(). The function will only be called once, its result will
+ * be cached.
+ *
require
A reference to ##require(). While you could use require() from the global
+ * context, this would prevent backward compatibility with AMD.
+ *
(callback return value)
The reference to be returned by ##require().
+ *
+ * @see ##require() can be used to obtain references defined with ##define().
+ */
+
+/*$
+ * @id amdsupport
+ * @name AMD stubs
+ * @configurable default
+ * @group OPTIONS
+ * @doc no
+ * @module WEB, UTIL
+ * If enabled, Minified will create stubs so you can use it without an AMD framework.
+ * It requires AMD's define() function.
+ */
+if (/^u/.test(typeof define)) { // no AMD support available ? define a minimal version
+ (function(def){
+ var require = this['require'] = function(name) { return def[name]; };
+ this['define'] = function(name, f) { def[name] = def[name] || f(require); };
+ })({});
+}
+/*$
+ * @stop
+ */
+
+define('minified', function() {
+
+ ///#/snippet commonAmdStart
+ ///#snippet webVars
+ /*$
+ * @id WEB
+ * @doc no
+ * @required
+ * This id allows identifying whether the Web module is available.
+ */
+
+ /**
+ * @const
+ */
+ var _window = window;
+
+ /**
+ * @const
+ * @type {!string}
+ */
+ var MINIFIED_MAGIC_NODEID = 'Nia';
+
+ /**
+ * @const
+ * @type {!string}
+ */
+ var MINIFIED_MAGIC_PREV = 'NiaP';
+
+ var setter = {}, getter = {};
+
+ var idSequence = 1; // used as node id to identify nodes, and as general id for other maps
+
+
+ /*$
+ * @id ready_vars
+ * @dependency
+ */
+ /** @type {!Array.} */
+ var DOMREADY_HANDLER = /^[ic]/.test(document['readyState']) ? _null : []; // check for 'interactive' and 'complete'
+ /*$
+ * @stop
+ */
+
+ ///#/snippet webVars
+ ///#snippet utilVars
+ /*$
+ * @id UTIL
+ * @doc no
+ * @required
+ * This id allows identifying whether the Util module is available.
+ */
+
+ var _null = null;
+
+ /** @const */
+ var undef;
+
+ /*$
+ * @id date_constants
+ * @dependency
+ */
+ function val3(v) {return v.substr(0,3);}
+ var MONTH_LONG_NAMES = split('January,February,March,April,May,June,July,August,September,October,November,December', /,/g);
+ var MONTH_SHORT_NAMES = map(MONTH_LONG_NAMES, val3); // ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
+ var WEEK_LONG_NAMES = split('Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday', /,/g);
+ var WEEK_SHORT_NAMES = map(WEEK_LONG_NAMES, val3);
+ var MERIDIAN_NAMES = split('am,pm', /,/g);
+ var MERIDIAN_NAMES_FULL = split('am,am,am,am,am,am,am,am,am,am,am,am,pm,pm,pm,pm,pm,pm,pm,pm,pm,pm,pm,pm', /,/g);
+
+ var FORMAT_DATE_MAP = {
+ 'y': ['FullYear', nonOp],
+ 'Y': ['FullYear', function(d) { return d % 100; }],
+ 'M': ['Month', plusOne],
+ 'n': ['Month', MONTH_SHORT_NAMES],
+ 'N': ['Month', MONTH_LONG_NAMES],
+ 'd': ['Date', nonOp],
+ 'm': ['Minutes', nonOp],
+ 'H': ['Hours', nonOp],
+ 'h': ['Hours', function(d) { return (d % 12) || 12; }],
+ 'k': ['Hours', plusOne],
+ 'K': ['Hours', function(d) { return d % 12; }],
+ 's': ['Seconds', nonOp],
+ 'S': ['Milliseconds', nonOp],
+ 'a': ['Hours', MERIDIAN_NAMES_FULL],
+ 'w': ['Day', WEEK_SHORT_NAMES],
+ 'W': ['Day', WEEK_LONG_NAMES],
+ 'z': ['TimezoneOffset', function(d, dummy, timezone) {
+ if (timezone)
+ return timezone;
+
+ var sign = d > 0 ? '-' : '+';
+ var off = d < 0 ? -d : d;
+ return sign + pad(2, Math.floor(off/60)) + pad(2, off%60);
+ }]
+ };
+
+ var PARSE_DATE_MAP = {
+ 'y': 0, // placeholder -> ctorIndex
+ 'Y': [0, -2000],
+ 'M': [1,1], // placeholder -> [ctorIndex, offset|value array]
+ 'n': [1, MONTH_SHORT_NAMES],
+ 'N': [1, MONTH_LONG_NAMES],
+ 'd': 2,
+ 'm': 4,
+ 'H': 3,
+ 'h': 3,
+ 'K': [3,1],
+ 'k': [3,1],
+ 's': 5,
+ 'S': 6,
+ 'a': [3, MERIDIAN_NAMES]
+ };
+
+ /*$
+ * @stop
+ */
+
+ /** @const */
+ var MAX_CACHED_TEMPLATES = 99;
+ var templateCache={}; // template -> function
+ var templates = []; // list of MAX_CACHED_TEMPLATES templates
+
+ ///#/snippet utilVars
+ ///#snippet commonFunctions
+
+ /** @param s {?} */
+ function toString(s) {
+ return s!=_null ? ''+s : '';
+ }
+ /**
+ * @param s {?}
+ * @param o {string}
+ */
+ function isType(s,o) {
+ return typeof s == o;
+ }
+ /** @param s {?} */
+ function isString(s) {
+ return isType(s, 'string');
+ }
+ function isObject(f) {
+ return !!f && isType(f, 'object');
+ }
+ function isNode(n) {
+ return n && n['nodeType'];
+ }
+ function isNumber(n) {
+ return isType(n, 'number');
+ }
+ function isDate(n) {
+ return isObject(n) && !!n['getDay'];
+ }
+ function isBool(n) {
+ return n === true || n === false;
+ }
+ function isValue(n) {
+ var type = typeof n;
+ return type == 'object' ? !!(n && n['getDay']) : (type == 'string' || type == 'number' || isBool(n));
+ }
+ function nonOp(v) {
+ return v;
+ }
+ function plusOne(d) {
+ return d+1;
+ }
+ function replace(s, regexp, sub) {
+ return toString(s).replace(regexp, sub != _null ? sub : '');
+ }
+ function escapeRegExp(s) {
+ return replace(s, /[\\\[\]\/{}()*+?.$|^-]/g, "\\$&");
+ }
+ function trim(s) {
+ return replace(s, /^\s+|\s+$/g);
+ }
+ function eachObj(obj, cb, ctx) {
+ for (var n in obj)
+ if (obj.hasOwnProperty(n))
+ cb.call(ctx || obj, n, obj[n]);
+ return obj;
+ }
+ function each(list, cb, ctx) {
+ if (list)
+ for (var i = 0; i < list.length; i++)
+ cb.call(ctx || list, list[i], i);
+ return list;
+ }
+ function filter(list, filterFuncOrObject, ctx) {
+ var r = [];
+ var f = isFunction(filterFuncOrObject) ? filterFuncOrObject : function(value) { return filterFuncOrObject != value; };
+ each(list, function(value, index) {
+ if (f.call(ctx || list, value, index))
+ r.push(value);
+ });
+ return r;
+ }
+ function collector(iterator, obj, collectFunc, ctx) {
+ var result = [];
+ iterator(obj, function (a, b) {
+ if (isList(a = collectFunc.call(ctx || obj, a, b))) // extreme variable reusing: a is now the callback result
+ each(a, function(rr) { result.push(rr); });
+ else if (a != _null)
+ result.push(a);
+ });
+ return result;
+ }
+ function collectObj(obj, collectFunc, ctx) {
+ return collector(eachObj, obj, collectFunc, ctx);
+ }
+ function collect(list, collectFunc, ctx) {
+ return collector(each, list, collectFunc, ctx);
+ }
+ function keyCount(obj) {
+ var c = 0;
+ eachObj(obj, function(key) { c++; });
+ return c;
+ }
+ function keys(obj) { // use Object.keys? in IE>=9
+ var list = [];
+ eachObj(obj, function(key) { list.push(key); });
+ return list;
+ }
+ function map(list, mapFunc, ctx) {
+ var result = [];
+ each(list, function(item, index) {
+ result.push(mapFunc.call(ctx || list, item, index));
+ });
+ return result;
+ }
+ function startsWith(base, start) {
+ if (isList(base)) {
+ var s2 = _(start); // convert start as we don't know whether it is a list yet
+ return equals(sub(base, 0, s2.length), s2);
+ }
+ else
+ return start != _null && base.substr(0, start.length) == start;
+ }
+ function endsWith(base, end) {
+ if (isList(base)) {
+ var e2 = _(end);
+ return equals(sub(base, -e2.length), e2) || !e2.length;
+ }
+ else
+ return end != _null && base.substr(base.length - end.length) == end;
+ }
+ function reverse(list) {
+ var len = list.length;
+ if (isList(list))
+ return new M(map(list, function() { return list[--len]; }));
+ else
+ return replace(list, /[\s\S]/g, function() { return list.charAt(--len); });
+ }
+ function toObject(list, value) {
+ var obj = {};
+ each(list, function(item, index) {
+ obj[item] = value;
+ });
+ return obj;
+ }
+ function copyObj(from, to) {
+ var dest = to || {};
+ for (var name in from)
+ dest[name] = from[name];
+ return dest;
+ }
+ function merge(list, target) {
+ var o = target;
+ for (var i = 0; i < list.length; i++)
+ o = copyObj(list[i], o);
+ return o;
+ }
+ function getFindFunc(findFunc) {
+ return isFunction(findFunc) ? findFunc : function(obj, index) { if (findFunc === obj) return index; };
+ }
+ function getFindIndex(list, index, defaultIndex) {
+ return index == _null ? defaultIndex : index < 0 ? Math.max(list.length+index, 0) : Math.min(list.length, index);
+ }
+ function find(list, findFunc, startIndex, endIndex) {
+ var f = getFindFunc(findFunc);
+ var e = getFindIndex(list, endIndex, list.length);
+ var r;
+ for (var i = getFindIndex(list, startIndex, 0); i < e; i++)
+ if ((r = f.call(list, list[i], i)) != _null)
+ return r;
+ }
+ function findLast(list, findFunc, startIndex, endIndex) {
+ var f = getFindFunc(findFunc);
+ var e = getFindIndex(list, endIndex, -1);
+ var r;
+ for (var i = getFindIndex(list, startIndex, list.length-1); i > e; i--)
+ if ((r = f.call(list, list[i], i)) != _null)
+ return r;
+ }
+ function sub(list, startIndex, endIndex) {
+ var r = [];
+ if (list) {
+ var e = getFindIndex(list, endIndex, list.length);
+ for (var i = getFindIndex(list, startIndex, 0); i < e; i++)
+ r.push(list[i]);
+ }
+ return r;
+ }
+ function array(list) {
+ return map(list, nonOp);
+ }
+ function unite(list) {
+ return function() {
+ return new M(callList(list, arguments));
+ };
+ }
+ function uniq(list) {
+ var found = {};
+ return filter(list, function(item) {
+ if (found[item])
+ return false;
+ else
+ return found[item] = 1;
+ });
+ }
+ function intersection(list, otherList) {
+ var keys = toObject(otherList, 1);
+ return filter(list, function(item) {
+ var r = keys[item];
+ keys[item] = 0;
+ return r;
+ });
+ }
+ function contains(list, value) { // TODO: can Array.indexOf be used in >IE8?
+ for (var i = 0; i < list.length; i++)
+ if (list[i] == value)
+ return true;
+ return false;
+ }
+ // equals if a and b have the same elements and all are equal. Supports getters.
+ function equals(x, y) {
+ var a = isFunction(x) ? x() : x;
+ var b = isFunction(y) ? y() : y;
+ var aKeys;
+ if (a == b)
+ return true;
+ else if (a == _null || b == _null)
+ return false;
+ else if (isValue(a) || isValue(b))
+ return isDate(a) && isDate(b) && +a==+b;
+ else if (isList(a)) {
+ return (a.length == b.length) &&
+ !find(a, function(val, index) {
+ if (!equals(val, b[index]))
+ return true;
+ });
+ }
+ else {
+ return !isList(b) &&
+ ((aKeys = keys(a)).length == keyCount(b)) &&
+ !find(aKeys, function(key) {
+ if (!equals(a[key],b[key]))
+ return true;
+ });
+ }
+ }
+
+ function call(f, fThisOrArgs, args) {
+ if (isFunction(f))
+ return f.apply(args && fThisOrArgs, map(args || fThisOrArgs, nonOp));
+ }
+ function callList(list, fThisOrArgs, args) {
+ return map(list, function(f) { return call(f, fThisOrArgs, args);});
+ }
+ function bind(f, fThis, beforeArgs, afterArgs) {
+ return function() {
+ return call(f, fThis, collect([beforeArgs, arguments, afterArgs], nonOp));
+ };
+ }
+ function partial(f, beforeArgs, afterArgs) {
+ return bind(f, this, beforeArgs, afterArgs);
+ }
+ function pad(digits, number) {
+ var signed = number < 0 ? '-' : '';
+ var preDecimal = (signed?-number:number).toFixed(0);
+ while (preDecimal.length < digits)
+ preDecimal = '0' + preDecimal;
+ return signed + preDecimal;
+ }
+
+ function processNumCharTemplate(tpl, input, fwd) {
+ var inHash;
+ var inputPos = 0;
+ var rInput = fwd ? input : reverse(input);
+ var s = (fwd ? tpl : reverse(tpl)).replace(/./g, function(tplChar) {
+ if (tplChar == '0') {
+ inHash = false;
+ return rInput.charAt(inputPos++) || '0';
+ }
+ else if (tplChar == '#') {
+ inHash = true;
+ return rInput.charAt(inputPos++) || '';
+ }
+ else
+ return inHash && !rInput.charAt(inputPos) ? '' : tplChar;
+ });
+ return fwd ? s : (input.substr(0, input.length - inputPos) + reverse(s));
+ }
+
+ function getTimezone(match, idx, refDate) { // internal helper, see below
+ if (idx == _null || !match)
+ return 0;
+ return parseFloat(match[idx]+match[idx+1])*60 + parseFloat(match[idx]+match[idx+2]) + refDate.getTimezoneOffset();
+ }
+
+ // formats number with format string (e.g. "#.000", "#,#", "00000", "000.00", "000.000.000,00", "000,000,000.##")
+ // choice syntax: :|:|...
+ // e.g. 0:no item|1:one item|>=2:# items
+ // ="null" used to compare with nulls.
+ // choice also works with strings or bools, e.g. ERR:error|WAR:warning|FAT:fatal|ok
+ function formatValue(fmt, value) {
+ var format = replace(fmt, /^\?/);
+ if (isDate(value)) {
+ var timezone, match;
+
+ if (match = /^\[(([+-])(\d\d)(\d\d))\]\s*(.*)/.exec(format)) {
+ timezone = match[1];
+ value = dateAdd(value, 'minutes', getTimezone(match, 2, value));
+ format = match[5];
+ }
+
+ return replace(format, /(\w)(\1*)(?:\[([^\]]+)\])?/g, function(s, placeholderChar, placeholderDigits, params) {
+ var val = FORMAT_DATE_MAP[placeholderChar];
+ if (val) {
+ var d = value['get' + val[0]]();
+ var optionArray = (params && params.split(','));
+
+ if (isList(val[1]))
+ d = (optionArray || val[1])[d];
+ else
+ d = val[1](d, optionArray, timezone);
+ if (d != _null && !isString(d))
+ d = pad(placeholderDigits.length+1, d);
+ return d;
+ }
+ else
+ return s;
+ });
+
+ }
+ else
+ return find(format.split(/\s*\|\s*/), function(fmtPart) {
+ var match, numFmtOrResult;
+ if (match = /^([<>]?)(=?)([^:]*?)\s*:\s*(.*)$/.exec(fmtPart)) {
+ var cmpVal1 = value, cmpVal2 = +(match[3]);
+ if (isNaN(cmpVal2) || !isNumber(cmpVal1)) {
+ cmpVal1 = (cmpVal1==_null) ? "null" : toString(cmpVal1); // not ""+value, because undefined is treated as null here
+ cmpVal2 = match[3];
+ }
+ if (match[1]) {
+ if ((!match[2] && cmpVal1 == cmpVal2 ) ||
+ (match[1] == '<' && cmpVal1 > cmpVal2) ||
+ (match[1] == '>' && cmpVal1 < cmpVal2))
+ return _null;
+ }
+ else if (cmpVal1 != cmpVal2)
+ return _null;
+ numFmtOrResult = match[4];
+ }
+ else
+ numFmtOrResult = fmtPart;
+
+ if (isNumber(value))
+ return numFmtOrResult.replace(/[0#](.*[0#])?/, function(numFmt) {
+ var decimalFmt = /^([^.]+)(\.)([^.]+)$/.exec(numFmt) || /^([^,]+)(,)([^,]+)$/.exec(numFmt);
+ var signed = value < 0 ? '-' : '';
+ var numData = /(\d+)(\.(\d+))?/.exec((signed?-value:value).toFixed(decimalFmt ? decimalFmt[3].length:0));
+ var preDecimalFmt = decimalFmt ? decimalFmt[1] : numFmt;
+ var postDecimal = decimalFmt ? processNumCharTemplate(decimalFmt[3], replace(numData[3], /0+$/), true) : '';
+
+ return (signed ? '-' : '') +
+ (preDecimalFmt == '#' ? numData[1] : processNumCharTemplate(preDecimalFmt, numData[1])) +
+ (postDecimal.length ? decimalFmt[2] : '') +
+ postDecimal;
+ });
+ else
+ return numFmtOrResult;
+ });
+ }
+ // returns date; null if optional and not set; undefined if parsing failed
+ function parseDate(fmt, date) {
+ var indexMap = {}; // contains reGroupPosition -> typeLetter or [typeLetter, value array]
+ var reIndex = 1;
+ var timezoneOffsetMatch;
+ var timezoneIndex;
+ var match;
+
+ var format = replace(fmt, /^\?/);
+ if (format!=fmt && !trim(date))
+ return _null;
+
+ if (match = /^\[([+-])(\d\d)(\d\d)\]\s*(.*)/.exec(format)) {
+ timezoneOffsetMatch = match;
+ format = match[4];
+ }
+
+ var parser = new RegExp(format.replace(/(.)(\1*)(?:\[([^\]]*)\])?/g, function(wholeMatch, placeholderChar, placeholderDigits, param) {
+ if (/[dmhkyhs]/i.test(placeholderChar)) {
+ indexMap[reIndex++] = placeholderChar;
+ var plen = placeholderDigits.length+1;
+ return "(\\d"+(plen<2?"+":("{1,"+plen+"}"))+")";
+ }
+ else if (placeholderChar == 'z') {
+ timezoneIndex = reIndex;
+ reIndex += 3;
+ return "([+-])(\\d\\d)(\\d\\d)";
+ }
+ else if (/[Nna]/.test(placeholderChar)) {
+ indexMap[reIndex++] = [placeholderChar, param && param.split(',')];
+ return "([a-zA-Z\\u0080-\\u1fff]+)";
+ }
+ else if (/w/i.test(placeholderChar))
+ return "[a-zA-Z\\u0080-\\u1fff]+";
+ else if (/\s/.test(placeholderChar))
+ return "\\s+";
+ else
+ return escapeRegExp(wholeMatch);
+ }));
+
+ if (!(match = parser.exec(date)))
+ return undef;
+
+ var ctorArgs = [0, 0, 0, 0, 0, 0, 0];
+ for (var i = 1; i < reIndex; i++) {
+ var matchVal = match[i];
+ var indexEntry = indexMap[i];
+ if (isList(indexEntry)) { // for a, n or N
+ var placeholderChar = indexEntry[0];
+ var mapEntry = PARSE_DATE_MAP[placeholderChar];
+ var ctorIndex = mapEntry[0];
+ var valList = indexEntry[1] || mapEntry[1];
+ var listValue = find(valList, function(v, index) { if (startsWith(matchVal.toLowerCase(), v.toLowerCase())) return index; });
+ if (listValue == _null)
+ return undef;
+ if (placeholderChar == 'a')
+ ctorArgs[ctorIndex] += listValue * 12;
+ else
+ ctorArgs[ctorIndex] = listValue;
+ }
+ else if (indexEntry) { // for numeric values (yHmMs)
+ var value = parseFloat(matchVal);
+ var mapEntry = PARSE_DATE_MAP[indexEntry];
+ if (isList(mapEntry))
+ ctorArgs[mapEntry[0]] += value - mapEntry[1];
+ else
+ ctorArgs[mapEntry] += value;
+ }
+ }
+ var d = new Date(ctorArgs[0], ctorArgs[1], ctorArgs[2], ctorArgs[3], ctorArgs[4], ctorArgs[5], ctorArgs[6]);
+ return dateAdd(d, 'minutes', -getTimezone(timezoneOffsetMatch, 1, d) - getTimezone(match, timezoneIndex, d));
+ }
+ // format ?##00,00##
+ // returns number; null if optional and not set; undefined if parsing failed
+ function parseNumber(fmt, value) {
+ var format = replace(fmt, /^\?/);
+ if (format!=fmt && !trim(value))
+ return _null;
+ var decSep = (/(^|[^0#.,])(,|[0#.]*,[0#]+|[0#]+\.[0#]+\.[0#.,]*)($|[^0#.,])/.test(format)) ? ',' : '.';
+ var r = parseFloat(replace(replace(replace(value, decSep == ',' ? /\./g : /,/g), decSep, '.'), /^[^\d-]*(-?\d)/, '$1'));
+ return isNaN(r) ? undef : r;
+ }
+ function now() {
+ return new Date();
+ }
+ function dateClone(date) {
+ return new Date(+date);
+ }
+ function capWord(w) {
+ return w.charAt(0).toUpperCase() + w.substr(1);
+ }
+ function dateAddInline(d, cProp, value) {
+ d['set'+cProp](d['get'+cProp]() + value);
+ return d;
+ }
+ function dateAdd(date, property, value) {
+ if (value == _null)
+ return dateAdd(now(), date, property);
+ return dateAddInline(dateClone(date), capWord(property), value);
+ }
+ function dateMidnight(date) {
+ var od = date || now();
+ return new Date(od.getFullYear(), od.getMonth(), od.getDate());
+ }
+ function dateDiff(property, date1, date2) {
+ var d1t = +date1;
+ var d2t = +date2;
+ var dt = d2t - d1t;
+ if (dt < 0)
+ return -dateDiff(property, date2, date1);
+
+ var propValues = {'milliseconds': 1, 'seconds': 1000, 'minutes': 60000, 'hours': 3600000};
+ var ft = propValues[property];
+ if (ft)
+ return dt / ft;
+
+ var cProp = capWord(property);
+ var calApproxValues = {'fullYear': 8.64e7*365, 'month': 8.64e7*365/12, 'date': 8.64e7}; // minimum values, a little bit below avg values
+ var minimumResult = Math.floor((dt / calApproxValues[property])-2); // -2 to remove the imperfections caused by the values above
+
+ var d = dateAddInline(new Date(d1t), cProp, minimumResult);
+ for (var i = minimumResult; i < minimumResult*1.2+4; i++) { // try out 20% more than needed, just to be sure
+ if (+dateAddInline(d, cProp, 1) > d2t)
+ return i;
+ }
+ // should never ever be reached
+ }
+
+ function ucode(a) {
+ return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+ }
+
+ function escapeJavaScriptString(s) {
+ return replace(s, /[\x00-\x1f'"\u2028\u2029]/g, ucode);
+ }
+
+ // reimplemented split for IE8
+ function split(str, regexp) {
+
+ return str.split(regexp);
+ }
+
+ function template(template, escapeFunction) {
+ if (templateCache[template])
+ return templateCache[template];
+ else {
+ var funcBody = 'with(_.isObject(obj)?obj:{}){'+
+ map(split(template, /{{|}}}?/g), function(chunk, index) {
+ var match, c1 = trim(chunk), c2 = replace(c1, /^{/), escapeSnippet = (c1==c2) ? 'esc(' : '';
+ if (index%2) { // odd means JS code
+ if (match = /^each\b(\s+([\w_]+(\s*,\s*[\w_]+)?)\s*:)?(.*)/.exec(c2))
+ return 'each('+(trim(match[4])?match[4]:'this')+', function('+match[2]+'){';
+ else if (match = /^if\b(.*)/.exec(c2))
+ return 'if('+match[1]+'){';
+ else if (match = /^else\b\s*(if\b(.*))?/.exec(c2))
+ return '}else ' + (match[1] ? 'if('+match[2] +')' : '')+'{';
+ else if (match = /^\/(if)?/.exec(c2))
+ return match[1] ? '}\n' : '});\n';
+ else if (match = /^(var\s.*)/.exec(c2))
+ return match[1]+';';
+ else if (match = /^#(.*)/.exec(c2))
+ return match[1];
+ else if (match = /(.*)::\s*(.*)/.exec(c2))
+ return 'print('+escapeSnippet+'_.formatValue("'+escapeJavaScriptString(match[2])+'",'+(trim(match[1])?match[1]:'this')+(escapeSnippet&&')')+'));\n';
+ else
+ return 'print('+escapeSnippet+(trim(c2)?c2:'this')+(escapeSnippet&&')')+');\n';
+ }
+ else if (chunk){
+ return 'print("'+escapeJavaScriptString(chunk)+'");\n';
+ }
+ }).join('')+'}';
+ var f = (new Function('obj', 'each', 'esc', 'print', '_', funcBody));
+ var t = function(obj, thisContext) {
+ var result = [];
+ f.call(thisContext || obj, obj, function(obj, func) {
+ if (isList(obj))
+ each(obj, function(value, index) { func.call(value, value, index); });
+ else
+ eachObj(obj, function(key, value) { func.call(value, key, value); });
+ }, escapeFunction || nonOp, function() {call(result['push'], result, arguments);}, _);
+ return result.join('');
+ };
+ if (templates.push(t) > MAX_CACHED_TEMPLATES)
+ delete templateCache[templates.shift()];
+ return templateCache[template] = t;
+ }
+ }
+
+ function escapeHtml(s) {
+ return replace(s, /[<>'"&]/g, function(s) {
+ return ''+s.charCodeAt(0)+';';
+ });
+ }
+
+ function formatHtml(tpl, obj) {
+ return template(tpl, escapeHtml)(obj);
+ }
+
+ function listBindArray(func) {
+ return function(arg1, arg2) {
+ return new M(func(this, arg1, arg2));
+ };
+ }
+ function listBind(func) {
+ return function(arg1, arg2, arg3) {
+ return func(this, arg1, arg2, arg3);
+ };
+ }
+ function funcArrayBind(func) {
+ return function(arg1, arg2, arg3) {
+ return new M(func(arg1, arg2, arg3));
+ };
+ }
+
+ ///#/snippet commonFunctions
+ ///#snippet webFunctions
+
+ // note: only the web version has the f.item check
+ function isFunction(f) {
+ return typeof f == 'function' && !f['item']; // item check as work-around for webkit bug 14547
+ }
+
+ function isList(v) {
+ return v && v.length != _null && !isString(v) && !isNode(v) && !isFunction(v) && v !== _window;
+ }
+
+ // used by IE impl of on() only
+ function push(obj, prop, value) {
+ (obj[prop] = (obj[prop] || [])).push(value);
+ }
+ // used by IE impl of on()/off() only
+ function removeFromArray(array, value) {
+ for (var i = 0; array && i < array.length; i++)
+ if (array[i] === value)
+ array['splice'](i--, 1);
+ }
+
+ function extractNumber(v) {
+ return parseFloat(replace(v, /^[^\d-]+/));
+ }
+
+ // retrieves the node id of the element, create one if needed.
+ function getNodeId(el) {
+ return (el[MINIFIED_MAGIC_NODEID] = (el[MINIFIED_MAGIC_NODEID] || ++idSequence));
+ }
+
+ // collect variant that filters out duplicate nodes from the given list, returns a new array
+ function collectUniqNodes(list, func) {
+ var result = [];
+ var nodeIds = {};
+ var currentNodeId;
+
+ flexiEach(list, function(value) {
+ flexiEach(func(value), function(node) {
+ if (!nodeIds[currentNodeId = getNodeId(node)]) {
+ result.push(node);
+ nodeIds[currentNodeId] = true;
+ }
+ });
+ });
+ return result;
+ }
+
+ // finds out the 'natural' height of the first element, the one if $$slide=1
+ function getNaturalHeight(elementList, factor) {
+ var q = {'$position': 'absolute', '$visibility': 'hidden', '$display': 'block', '$height': _null};
+ var oldStyles = elementList['get'](q);
+ var h = elementList['set'](q)['get']('clientHeight');
+ elementList['set'](oldStyles);
+ return h*factor + 'px';
+ }
+
+
+
+
+ // @condblock !ie8compatibility
+ function on(subSelector, eventSpec, handler, args, bubbleSelector) {
+ if (isFunction(eventSpec))
+ return this['on'](_null, subSelector, eventSpec, handler, args);
+ else if (isString(args))
+ return this['on'](subSelector, eventSpec, handler, _null, args);
+ else
+ return this['each'](function(baseElement, index) {
+ flexiEach(subSelector ? dollarRaw(subSelector, baseElement) : baseElement, function(registeredOn) {
+ flexiEach(toString(eventSpec).split(/\s/), function(namePrefixed) {
+ var name = replace(namePrefixed, /[?|]/g);
+ var prefix = replace(namePrefixed, /[^?|]/g);
+ var capture = (name == 'blur' || name == 'focus') && !!bubbleSelector; // bubble selectors for 'blur' and 'focus' registered as capuring!
+ var triggerId = idSequence++;
+
+ // returns true if processing should be continued
+ function triggerHandler(eventName, event, target) {
+ var match = !bubbleSelector;
+ var el = bubbleSelector ? target : registeredOn;
+ if (bubbleSelector) {
+ var selectorFilter = getFilterFunc(bubbleSelector, registeredOn);
+ while (el && el != registeredOn && !(match = selectorFilter(el)))
+ el = el['parentNode'];
+ }
+ return (!match) || (name != eventName) || ((handler.apply($(el), args || [event, index]) && prefix=='?') || prefix == '|');
+ };
+
+ function eventHandler(event) {
+ if (!triggerHandler(name, event, event['target'])) {
+ event['preventDefault']();
+ event['stopPropagation']();
+ }
+ };
+
+ registeredOn.addEventListener(name, eventHandler, capture);
+
+ if (!registeredOn['M'])
+ registeredOn['M'] = {};
+ registeredOn['M'][triggerId] = triggerHandler; // to be called by trigger()
+
+ handler['M'] = collector(flexiEach, [handler['M'], function () { // this function will be called by off()
+ registeredOn.removeEventListener(name, eventHandler, capture);
+ delete registeredOn['M'][triggerId];
+ }], nonOp);
+
+ });
+ });
+ });
+ }
+ // @condend !ie8compatibility
+
+
+ // @condblock !ie8compatibility
+ function off(handler) {
+ callList(handler['M']);
+ handler['M'] = _null;
+ }
+ // @condend !ie8compatibility
+
+ // for remove & window.unload, IE only
+ function detachHandlerList(dummy, handlerList) {
+ flexiEach(handlerList, function(h) {
+ h.element.detachEvent('on'+h.eventType, h.handlerFunc);
+ });
+ }
+
+ function ready(handler) {
+ if (DOMREADY_HANDLER)
+ DOMREADY_HANDLER.push(handler);
+ else
+ setTimeout(handler, 0);
+ }
+
+ function $$(selector, context, childrenOnly) {
+ return dollarRaw(selector, context, childrenOnly)[0];
+ }
+
+ function EE(elementName, attributes, children) {
+ var e = $(document.createElement(elementName));
+ // @condblock UTIL
+ // this attributes != null check is only required with Util's isObject() implementation. Web's isObject() is simpler.
+ return (isList(attributes) || (attributes != _null && !isObject(attributes)) ) ? e['add'](attributes) : e['set'](attributes)['add'](children);
+ // @condend UTIL
+ // @cond !UTIL return (isList(attributes) || (!isObject(attributes)) ) ? e['add'](attributes) : e['set'](attributes)['add'](children);
+ }
+
+ function clone(listOrNode) {
+ return collector(flexiEach, listOrNode, function(e) {
+ var c;
+ if (isList(e))
+ return clone(e);
+ else if (isNode(e)) {
+ c = e['cloneNode'](true);
+ c['removeAttribute'] && c['removeAttribute']('id');
+ return c;
+ }
+ else
+ return e;
+ });
+ }
+
+ /*$
+ * @stop
+ */
+
+ function $(selector, context, childOnly) {
+ // @condblock ready
+ return isFunction(selector) ? ready(selector) : new M(dollarRaw(selector, context, childOnly));
+ // @condend
+ // @cond !ready return new M(dollarRaw(selector, context));
+ }
+
+ // implementation of $ that does not produce a Minified list, but just an array
+
+
+
+
+
+
+
+
+
+
+ // @condblock !ie7compatibility
+ function dollarRaw(selector, context, childOnly) {
+ function flatten(a) { // flatten list, keep non-lists, remove nulls
+ return isList(a) ? collector(flexiEach, a, flatten) : a;
+ }
+ function filterElements(list) { // converts into array, makes sure context is respected
+ return filter(collector(flexiEach, list, flatten), function(node) {
+ var a = node;
+ while (a = a['parentNode'])
+ if (a == context[0] || childOnly)
+ return a == context[0];
+ // fall through to return undef
+ });
+ }
+
+ if (context) {
+ if ((context = dollarRaw(context)).length != 1)
+ return collectUniqNodes(context, function(ci) { return dollarRaw(selector, ci, childOnly);});
+ else if (isString(selector)) {
+ if (isNode(context[0]) != 1)
+ return [];
+ else
+ return childOnly ? filterElements(context[0].querySelectorAll(selector)) : context[0].querySelectorAll(selector);
+ }
+ else
+ return filterElements(selector);
+
+ }
+ else if (isString(selector))
+ return document.querySelectorAll(selector);
+ else
+ return collector(flexiEach, selector, flatten);
+ };
+ // @condend !ie7compatibility
+
+ // If context is set, live updates will be possible.
+ // Please note that the context is not evaluated for the '*' and 'tagname.classname' patterns, because context is used only
+ // by on(), and in on() only nodes in the right context will be checked
+ function getFilterFunc(selector, context) {
+ function wordRegExpTester(name, prop) {
+ var re = RegExp('(^|\\s+)' + name + '(?=$|\\s)', 'i');
+ return function(obj) {return name ? re.test(obj[prop]) : true;};
+ }
+
+ var nodeSet = {};
+ var dotPos = nodeSet;
+ if (isFunction(selector))
+ return selector;
+ else if (isNumber(selector))
+ return function(v, index) { return index == selector; };
+ else if (!selector || selector == '*' ||
+ (isString(selector) && (dotPos = /^([\w-]*)\.?([\w-]*)$/.exec(selector)))) {
+ var nodeNameFilter = wordRegExpTester(dotPos[1], 'tagName');
+ var classNameFilter = wordRegExpTester(dotPos[2], 'className');
+ return function(v) {
+ return isNode(v) == 1 && nodeNameFilter(v) && classNameFilter(v);
+ };
+ }
+ else if (context)
+ return function(v) {
+ return $(selector, context)['find'](v)!=_null; // live search instead of node set, for on()
+ };
+ else {
+ $(selector)['each'](function(node) {
+ nodeSet[getNodeId(node)] = true;
+ });
+ return function(v) {
+ return nodeSet[getNodeId(v)];
+ };
+ }
+ }
+
+ function getInverseFilterFunc(selector) {
+ var f = getFilterFunc(selector);
+ return function(v) {return f(v) ? _null : true;};
+ }
+ ///#/snippet webFunctions
+
+ ///#snippet extrasFunctions
+ function flexiEach(list, cb) {
+ if (isList(list))
+ each(list, cb);
+ else if (list != _null)
+ cb(list, 0);
+ return list;
+ }
+
+ function Promise() {
+ this['state'] = null;
+ this['values'] = [];
+ this['parent'] = null;
+ }
+
+ /*$
+ * @id promise
+ * @name _.promise()
+ * @syntax _.promise()
+ * @syntax _.promise(otherPromises...)
+ * @module WEB+UTIL
+ *
+ * Creates a new ##promiseClass#Promise##, optionally assimilating other promises. If no other promise is given,
+ * a fresh new promise is returned.
+ *
+ * The returned promise provides the methods ##fulfill() and ##reject() that can be called directly to change the promise's state,
+ * as well as the more powerful ##fire().
+ *
+ * If one promise is given as parameter, the new promise assimilates the given promise as-is, and just forwards
+ * fulfillment and rejection with the original values.
+ *
+ * If more than one promise are given, it will assimilate all of them with slightly different rules:
+ *
the new promise is fulfilled if all assimilated promises have been fulfilled. The fulfillment values
+ * of all assimilated promises are given to the handler as arguments. Note that the fulfillment values themselves are always
+ * arrays, as a promise can have several fulfillment values in Minified's implementation.
+ *
when one of the promises is rejected, the new promise is rejected immediately. The rejection handler gets the
+ * promises rejection value (first argument if it got several) as first argument, an array of the result values
+ * of all promises as a second (that means one array of arguments for each promise), and the index of the failed
+ * promise as third.
+ *
+ *
+ * @example A simple promise that is fulfilled after 1 second, using Minified's invocation syntax:
+ *
var p = _.promise();
+ * setTimeout(function() {
+ * p.fire(true);
+ * }, 1000);
+ *
+ *
+ * @example Request three files in parallel. When all three have been downloaded, concatenate them into a single string.
+ *
+ *
+ * @param otherPromises one or more promises to assimilate (varargs). You can also pass lists of promises.
+ * @return the new promise.
+ */
+ function promise() {
+ var deferred = []; // this function calls the functions supplied by then()
+
+ var assimilatedPromises = arguments;
+ var assimilatedNum = assimilatedPromises.length;
+ var numCompleted = 0; // number of completed, assimilated promises
+ var rejectionHandlerNum = 0;
+
+ var obj = new Promise();
+
+ obj['errHandled'] = function() {
+ rejectionHandlerNum++;
+ if (obj['parent'])
+ obj['parent']['errHandled']();
+ };
+
+ /*$
+ * @id fire
+ * @name promise.fire()
+ * @syntax _.fire(newState)
+ * @syntax _.fire(newState, values)
+ * @module WEB+UTIL
+ *
+ * Changes the state of the promise into either fulfilled or rejected. This will also notify all ##then() handlers. If the promise
+ * already has a state, the call will be ignored.
+ *
+ * fire() can be invoked as a function without context ('this'). Every promise has its own instance.
+ *
+ * @example A simple promise that is fulfilled after 1 second, using Minified's invocation syntax:
+ *
var p = _.promise();
+ * setTimeout(function() {
+ * p.fire(true, []);
+ * }, 1000);
+ *
+ *
+ * @example Call fire() without a context:
+ *
var p = _.promise(function(resolve, reject) {
+ * setTimeout(resolve.fire, 1000);
+ * });
+ *
+ *
+ * @param newState true to set the Promise to fulfilled, false to set the state as rejected. If you pass null or
+ * undefined, the promise's state does not change.
+ * @param values optional an array of values to pass to ##then() handlers as arguments. You can also pass a non-list argument, which will then
+ * be passed as only argument.
+ * @return the promise
+ */
+ var fire = obj['fire'] = function(newState, newValues) {
+ if (obj['state'] == null && newState != null) {
+ obj['state'] = !!newState;
+ obj['values'] = isList(newValues) ? newValues : [newValues];
+ setTimeout(function() {
+ each(deferred, function(f) {f();});
+ }, 0);
+ }
+ return obj;
+ };
+
+ // use promise varargs
+ each(assimilatedPromises, function assimilate(promise, index) {
+ try {
+ if (promise['then'])
+ promise['then'](function(v) {
+ var then;
+ if ((isObject(v) || isFunction(v)) && isFunction(then = v['then']))
+ assimilate(v, index);
+ else {
+ obj['values'][index] = array(arguments);
+ if (++numCompleted == assimilatedNum)
+ fire(true, assimilatedNum < 2 ? obj['values'][index] : obj['values']);
+ }
+ },
+ function(e) {
+ obj['values'][index] = array(arguments);
+ fire(false, assimilatedNum < 2 ? obj['values'][index] : [obj['values'][index][0], obj['values'], index]);
+ });
+ else
+ promise(function() {fire(true, array(arguments));}, function() {fire(false, array(arguments)); });
+ }
+ catch (e) {
+ fire(false, [e, obj['values'], index]);
+ }
+ });
+
+ /*$
+ * @id stop
+ * @name promise.stop()
+ * @syntax promise.stop()
+ * @module WEB+UTIL
+ * Stops an ongoing operation, if supported. Currently the only promises supporting this are those returned by ##request(), ##animate(), ##wait() and
+ * ##asyncEach().
+ * stop() invocation will be propagated over promises returned by ##then() and promises assimilated by ##promise(). You only need to invoke stop
+ * with the last promise, and all dependent promises will automatically stop as well.
+ *
+ * stop() can be invoked as a function without context ('this'). Every promise has its own instance.
+ *
+ * @return In some cases, the stop() can return a value. This is currently only done by ##animate() and ##wait(), which will return the actual duration.
+ * ##asyncEach()'s promise will also return any value it got from the promise that it stopped.
+ *
+ * @example Animation chain that can be stopped.
+ *
+ */
+ obj['stop'] = function() {
+ each(assimilatedPromises, function(promise) {
+ if (promise['stop'])
+ promise['stop']();
+ });
+
+ return obj['stop0'] && call(obj['stop0']);
+ };
+
+ /*$
+ * @id then
+ * @name promise.then()
+ * @syntax promise.then()
+ * @syntax promise.then(onSuccess)
+ * @syntax promise.then(onSuccess, onError)
+ *
+ * @module WEB
+ * Registers two callbacks that will be invoked when the ##promise#Promise##'s asynchronous operation finished
+ * successfully (onSuccess) or an error occurred (onError). The callbacks will be called after
+ * then() returned, from the browser's event loop.
+ * You can chain then() invocations, as then() returns another Promise object that you can attach to.
+ *
+ * The full distribution of Minified implements the Promises/A+ specification, allowing interoperability with other Promises frameworks.
+ *
+ * Note: If you use the Web module, you will get a simplified Promises implementation that cuts some corners. The most notable
+ * difference is that when a then() handler throws an exception, this will not be caught and the promise returned by
+ * then will not be automatically rejected.
+ *
+ * @example Simple handler for an HTTP request. Handles only success and ignores errors.
+ *
+ *
+ * @param onSuccess optional a callback function to be called when the operation has been completed successfully. The exact arguments it receives depend on the operation.
+ * If the function returns a ##promise#Promise##, that Promise will be evaluated to determine the state of the promise returned by then(). If it returns any other value, the
+ * returned Promise will also succeed. If the function throws an error, the returned Promise will be in error state.
+ * Pass null or undefined if you do not need the success handler.
+ * @param onError optional a callback function to be called when the operation failed. The exact arguments it receives depend on the operation. If the function returns a ##promise#Promise##, that promise will
+ * be evaluated to determine the state of the Promise returned by then(). If it returns anything else, the returned Promise will
+ * have success status. If the function throws an error, the returned Promise will be in the error state.
+ * You can pass null or undefined if you do not need the error handler.
+ * @return a new ##promise#Promise## object. If you specified a callback for success or error, the new Promises's state will be determined by that callback if it is called.
+ * If no callback has been provided and the original Promise changes to that state, the new Promise will change to that state as well.
+ */
+ var then = obj['then'] = function (onFulfilled, onRejected) {
+ var promise2 = promise();
+ var callCallbacks = function() {
+ try {
+ var f = (obj['state'] ? onFulfilled : onRejected);
+ if (isFunction(f)) {
+ (function resolve(x) {
+ try {
+ var then, cbCalled = 0;
+ if ((isObject(x) || isFunction(x)) && isFunction(then = x['then'])) {
+ if (x === promise2)
+ throw new TypeError();
+ then.call(x, function(x) { if (!cbCalled++) resolve(x); }, function(value) { if (!cbCalled++) promise2['fire'](false, [value]);});
+ promise2['stop0'] = x['stop'];
+ }
+ else
+ promise2['fire'](true, [x]);
+ }
+ catch(e) {
+ if (!(cbCalled++)) {
+ promise2['fire'](false, [e]);
+ if (!rejectionHandlerNum)
+ throw e;
+ }
+ }
+ })(call(f, undef, obj['values']));
+ }
+ else
+ promise2['fire'](obj['state'], obj['values']);
+ }
+ catch (e) {
+ promise2['fire'](false, [e]);
+ if (!rejectionHandlerNum)
+ throw e;
+ }
+ };
+ if (isFunction(onRejected))
+ obj['errHandled']();
+ promise2['stop0'] = obj['stop'];
+ promise2['parent'] = obj;
+ if (obj['state'] != null)
+ setTimeout(callCallbacks, 0);
+ else
+ deferred.push(callCallbacks);
+ return promise2;
+ };
+
+ /*$
+ * @id always
+ * @group REQUEST
+ * @name promise.always()
+ * @syntax promise.always(callback)
+ * @configurable default
+ * @module WEB+UTIL
+ * Registers a callback that will always be called when the ##promise#Promise##'s operation ended, no matter whether the operation succeeded or not.
+ * This is a convenience function that will call ##then() with the same function for both arguments. It shares all of its semantics.
+ *
+ * @example Simple handler for a HTTP request.
+ *
+ *
+ * @param callback a function to be called when the operation has been finished, no matter what its result was. The exact arguments depend on the operation and may
+ * vary depending on whether it succeeded or not. If the function returns a ##promise#Promise##, that Promise will
+ * be evaluated to determine the state of the returned Promise. If provided and it returns regularly, the returned promise will
+ * have success status. If it throws an error, the returned Promise will be in the error state.
+ * @return a new ##promise#Promise## object. Its state is determined by the callback.
+ */
+ obj['always'] = function(func) { return then(func, func); };
+
+ /*$
+ * @id error
+ * @group REQUEST
+ * @name promise.error()
+ * @syntax promise.error(callback)
+ * @configurable default
+ * @module WEB, UTIL
+ * Registers a callback that will be called when the operation failed.
+ * This is a convenience function that will invoke ##then() with only the second argument set. It shares all of its semantics.
+ *
+ * @example Simple handler for a HTTP request.
+ *
+ *
+ * @param callback a function to be called when the operation has failed. The exact arguments depend on the operation. If the function returns a ##promise#Promise##, that Promise will
+ * be evaluated to determine the state of the returned Promise. If it returns regularly, the returned Promise will
+ * have success status. If it throws an error, the returned Promise will be in error state.
+ * @return a new ##promise#Promise## object. Its state is determined by the callback.
+ */
+ obj['error'] = function(func) { return then(0, func); };
+
+ return obj;
+ }
+
+ ///#/snippet extrasFunctions
+ ///#snippet extrasDocs
+ /*$
+ * @id length
+ * @group SELECTORS
+ * @requires dollar
+ * @name list.length
+ * @syntax length
+ * @module WEB, UTIL
+ *
+ * Contains the number of elements in the ##list#Minified list##.
+ *
+ * @example With Web module:
+ *
+ * var list = $('input');
+ * var myValues = {};
+ * for (var i = 0; i < list.length; i++)
+ * myValues[list[i].name] = list[i].value;
+ *
+ *
+ * @example With Util module:
+ *
+ * var list = _(1, 2, 3);
+ * var sum = 0;
+ * for (var i = 0; i < list.length; i++)
+ * sum += list[i];
+ *
+ */
+ /*$
+ * @stop
+ */
+ ///#/snippet extrasDocs
+
+ ///#snippet utilM
+
+ /*
+ * syntax: M(list, assimilateSublists)
+ * M(null, singleElement)
+ *
+ *
+ */
+ /** @constructor */
+ function M(list, assimilateSublists) {
+ var self = this, idx = 0;
+ if (list)
+ for (var i = 0, len = list.length; i < len; i++) {
+ var item = list[i];
+ if (assimilateSublists && isList(item))
+ for (var j = 0, len2 = item.length; j < len2; j++)
+ self[idx++] = item[j];
+ else
+ self[idx++] = item;
+ }
+ else
+ self[idx++] = assimilateSublists;
+
+ self['length'] = idx;
+ self['_'] = true;
+ }
+
+ function _() {
+ return new M(arguments, true);
+ }
+
+ ///#/snippet utilM
+
+ //// LIST FUNCTIONS ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ copyObj({
+ ///#snippet utilListFuncs
+ /*$
+ * @id each
+ * @group LIST
+ * @requires
+ * @configurable default
+ * @name .each()
+ * @altname _.each()
+ * @syntax list.each(callback)
+ * @syntax list.each(callback, ctx)
+ * @syntax _.each(list, callback)
+ * @syntax _.each(list, callback, ctx)
+ * @module UTIL, WEB
+ * Invokes the given function once for each item in the list. The function will be called with the item as first parameter and
+ * the zero-based index as second. Unlike JavaScript's built-in forEach() it will be invoked for each item in the list,
+ * even if it is undefined.
+ *
+ * @example Creates the sum of all list entries.
+ *
+ * var sum = 0;
+ * _(17, 4, 22).each(function(item, index) {
+ * sum += item;
+ * });
+ *
+ *
+ * @example The previous example with a native array:
+ *
+ * var sum = 0;
+ * _.each([17, 4, 22], function(item, index) {
+ * sum += item;
+ * });
+ *
+ *
+ * @example This goes through all h2 elements of the class 'section' on a web page and changes their content:
+ *
+ *
+ * @param list a list to iterate. Can be an array, a ##list#Minified list## or any other array-like structure with
+ * length property.
+ * @param callback The callback function(item, index) to invoke for each list element.
+ *
item
The current list element.
+ *
index
The second the zero-based index of the current element.
+ *
this
The given context if not null. Otherwise the list.
+ * The callback's return value will be ignored.
+ * @param ctx optional a context to pass to the callback as 'this'. Only supported in UTIL module.
+ * @return the list
+ *
+ * @see ##per() works like each(), but wraps the list elements in a list.
+ * @see ##find() can be used instead of each() if you need to abort the loop.
+ * @see ##eachObj() iterates through the properties of an object.
+ */
+ 'each': listBind(each),
+
+ /*$
+ * @id filter
+ * @group LIST
+ * @requires
+ * @configurable default
+ * @name .filter()
+ * @altname _.filter()
+ * @syntax list.filter(filterFunc)
+ * @syntax list.filter(filterFunc, ctx)
+ * @syntax list.filter(value)
+ * @syntax _.filter(list, filterFunc)
+ * @syntax _.filter(list, filterFunc, ctx)
+ * @syntax _.filter(list, value)
+ * @module WEB, UTIL
+ * Creates a new ##list#Minified list## by taking an existing list and omitting certain elements from it. You
+ * can either specify a callback function to approve those items that will be in the new list (all modules), or
+ * you can pass a value to remove from the new list (Util module only).
+ *
+ * If the callback function returns true, the item is shallow-copied in the new list, otherwise it will be removed.
+ * For values, a simple equality operation (==) will be used.
+ *
+ * @example Removing all instances of the number 10 from a list:
+ *
+ * var list = _([4, 10, 22, 7, 2, 19, 10]).filter(10);
+ *
+ *
+ * @example Removing all numbers over 10 from a list:
+ *
+ *
+ * @param list a list to filter. A list to use as input. Can be an array, a ##list#Minified list## or any other array-like structure with
+ * length property.
+ * @param filterFunc The filter callback function(item, index) that decides which elements to include:
+ *
item
The current list element.
+ *
index
The second the zero-based index of the current element.
+ *
this
The given context if not null. Otherwise the list.
+ *
(callback return value)
true to include the item in the new list, false to omit it.
+ * @param ctx optional a context to pass to the callback as 'this'. Only supported in UTIL module.
+ * @param value a value to remove from the list. It will be determined which elements to remove using ==. Must not
+ * be a function. Requires Util module.
+ * @return the new, filtered ##list#list##
+ *
+ * @see ##only() offers selector-based filtering.
+ */
+ 'filter': listBindArray(filter),
+
+ /*$
+ * @id map
+ * @group LIST
+ * @requires
+ * @configurable default
+ * @name .map()
+ * @altname _.map()
+ * @syntax list.map(mapFunc)
+ * @syntax list.map(mapFunc, ctx)
+ * @syntax _.map(list, mapFunc)
+ * @syntax _.map(list, mapFunc, ctx)
+ * @module UTIL
+ * Creates a new ##list#Minified list## from the current list using the given callback function.
+ * The callback is invoked once for each element of the current list. The callback results will be added to the result list.
+ *
+ * map() is a simpler version of ##collect(). Unlike collect(), it always creates lists of the same size as the input list, but
+ * it is easier to use if the resulting list should contain nulls or nested list.
+ *
+ * @example Goes through a list of numbers and creates a new list with each value increased by 1:
+ *
+ *
+ * @param list a list to transform. Can be an array, a ##list#Minified list## or any other array-like structure with
+ * length property.
+ * @param mapFunc The callback function(item, index) to invoke for each item:
+ *
item
The current list element.
index
The second the zero-based index of the current element.
+ *
this
The given context if not null. Otherwise the list.
+ *
(callback return value)
This value will replace the original value in the new list.
+ * @param ctx optional a context to pass to the callback as 'this'.
+ * @return the new ##list#list##
+ *
+ * @see ##collect() is a more powerful version of map().
+ */
+ 'map': listBindArray(map),
+
+ /*$
+ * @id find
+ * @group LIST
+ * @requires
+ * @configurable default
+ * @name .find()
+ * @altname _.find()
+ * @syntax list.find(findFunc)
+ * @syntax list.find(element)
+ * @syntax list.find(findFunc, startIndex)
+ * @syntax list.find(element, startIndex)
+ * @syntax _.find(list, findFunc)
+ * @syntax _.find(list, element)
+ * @syntax _.find(list, findFunc, startIndex)
+ * @syntax _.find(list, element, startIndex)
+ * @module WEB, UTIL
+ * Finds a specific value in the list. There are two ways of calling find():
+ *
+ *
With a value as argument. Then find() will search for the first occurrence of an identical value in the list,
+ * using the '===' operator for comparisons, and return the index. If it is not found,
+ * find() returns undefined.
+ *
With a callback function. find() will then call the given function for each list element until the function
+ * returns a value that is not null or undefined. This value will be returned.
+ *
+ *
+ * find() can also be used as an alternative to ##each() if you need to abort the loop.
+ *
+ * @example Finds the first negative number in the list:
+ *
+ * var i = _(1, 2, -4, 5, 2, -1).find(function(value, index) { if (value < 0) return index; }); // returns 2
+ *
+
+ * @example Finds the index of the first 5 in the array:
+ *
+ * var i = _.find([3, 6, 7, 6, 5, 4, 5], 5); // returns 4 (index of first 5)
+ *
+ *
+ * @example Determines the position of the element with the id '#wanted' among all li elements:
+ *
+ * var elementIndex = $('li').find($$('#wanted'));
+ *
+ *
+ * @example Goes through the elements to find the first div that has the class 'myClass', and returns this element:
+ *
+ * var myClassElement = $('div').find(function(e) { if ($(e).is('.myClass')) return e; });
+ *
+ *
+ * @param list A list to use as input. Can be an array, a ##list#Minified list## or any other array-like structure with
+ * length property.
+ * @param findFunc The callback function(item, index) that will be invoked for every list item until it returns a non-null value:
+ *
item
The current list element.
index
The second the zero-based index of the current element.
+ *
this
This list.
+ *
(callback return value)
If the callback returns something other than null or
+ * undefined, find() will return it directly. Otherwise it will continue.
+ * @param element the element to search for
+ * @param startIndex optional the 0-based index of the first element to search.
+ * @return if called with an element, either the element's index in the list or undefined if not found. If called with a callback function,
+ * it returns either the value returned by the callback or undefined.
+ *
+ * @see ##findLast() is the equivalent to find() for the list's end.
+ */
+ 'find': listBind(find),
+
+ /*$
+ * @stop
+ */
+ dummySort:0
+ ,
+ ///#/snippet utilListFuncs
+ ///#snippet webListFuncs
+
+ /*$
+ * @id select
+ * @group SELECTORS
+ * @requires dollar
+ * @configurable default
+ * @name .select()
+ * @syntax list.select(selector)
+ * @syntax list.select(selector, childrenOnly)
+ * @module WEB
+ * Executes a selector with the list as context. list.select(selector, childrenOnly) is equivalent
+ * to $(selector, list, childrenOnly).
+ *
+ * @example Returns a list of all list elements:
+ *
+ * var parents = $('ol.myList').select('li', true);
+ *
+ *
+ * @example Returns a list of all child elements:
+ *
+ * var children = $('.myElements').select('*', true);
+ *
+ *
+ * @param selector a selector or any other valid first argument for #dollar#$().
+ * @param childrenOnly optional if set, only direct children of the context nodes are included in the list. Children of children will be filtered out. If omitted or not
+ * true, all descendants of the context will be included.
+ * @return the new list containing the selected descendants.
+ *
+ * @see ##only() executes a selector on the list elements, instead of their descendants.
+ */
+ 'select': function(selector, childOnly) {
+ return $(selector, this, childOnly);
+ },
+
+ /*$
+ * @id get
+ * @group SELECTORS
+ * @requires dollar
+ * @configurable default
+ * @name .get()
+ * @syntax list.get(name)
+ * @syntax list.get(name, toNumber)
+ * @syntax list.get(list)
+ * @syntax list.get(list, toNumber)
+ * @syntax list.get(map)
+ * @syntax list.get(map, toNumber)
+ * @module WEB
+ * Retrieves properties, attributes and styles from the list's first element. The syntax to request those values is mostly identical with ##set(). You can either
+ * get a single value if you specify only one name, or get an object map when you specify several names using an array or an object map.
+ *
+ * The name parameter defines what kind of data you are reading. The following name schemes are supported:
+ *
+ *
Name Schema
Example
Sets what?
Description
+ *
name
innerHTML
Property
A name without prefix of '$' or '@' gets a property of the object.
+ *
@name
@href
Attribute
Gets the HTML attribute using getAttribute().
+ *
%name
%phone
Data-Attribute
Gets a data attribute using getAttribute(). Data attributes are
+ * attributes whose names start with 'data-'. '%myattr' and '@data-myattr' are equivalent.
+ *
$name
$fontSize
CSS Property
Gets a style using the element's style object.
+ * The syntax for the CSS styles is camel-case (e.g. "$backgroundColor", not "$background-color"). Shorthand properties like "border" or "margin" are
+ * not supported. You must use the full name, e.g. "$marginTop". Minified will try to determine the effective style
+ * and thus will return the value set in style sheets if not overwritten using a regular style.
+ *
$
$
CSS Classes
A simple $ returns the CSS classes of the element and is identical with "className".
+ *
$$
$$
Style
Reads the element's style attribute in a browser-independent way. On legacy IEs it uses
+ * style.cssText, and on everything else just the "style" attribute.
+ *
$$show
$$show
Show/Hide
Returns 1 if the element is visible and 0 if it is not visible. An element counts as
+ * visible if '$visibility' is not 'hidden' and '$display' is not 'none'. Other properties will be ignored, even if they can also be used to hide the element.
+ *
$$fade
$$fade
Fade Effect
The name '$$fade' returns the opacity of the element as a value between 0 and 1.
+ * '$$fade' will also automatically evaluate the element's 'visibility' and 'display' styles to find out whether the element is actually visible.
+ *
$$slide
$$slide
Slide Effect
'$$slide' returns the height of the element in pixels with a 'px' suffix and is equivalent to '$height'.
+ * Please note that you can pass that 'px' value to '$$slide' in ##set(), which will then set the according '$height'.
+ *
$$scrollX, $$scrollY
$$scrollY
Scroll Coordinates
The names '$$scrollX' and
+ * '$$scrollY' can be used on $(window) to retrieve the scroll coordinates of the document.
+ * The coordinates are specified in pixels without a 'px' unit postfix.
+ *
+ *
+ * @example Retrieves the id, title attribute and the background color of the element '#myElement':
+ *
+ * var id = $('#myElement).get('id');
+ * var title = $('#myElement).get('@title');
+ * var bgColor = $('#myElement).get('$backgroundColor');
+ *
+ *
+ * @example Retrieves the id, title attribute and the background color of the element '#myElement' as a map:
+ *
+ * var m = $('#myElement).get(['id', '@title', '$backgroundColor']);
+ * var id = m.id;
+ * var title = m['@title'];
+ * var bgColor = m.$backgroundColor;
+ *
+ *
+ * @example Uses ##get() and ##set() to reposition an element:
+ *
+ * Please note that the values of $top and $left in the get() invocation do not matter and will be ignored!
+ *
+ * @param name the name of a single property or attribute to modify. Unprefixed names set properties, a '$' prefix sets CSS styles and
+ * '@' sets attributes. Please see the table above for special properties and other options.
+ * @param list in order to retrieve more than one value, you can specify several names in an array or list. get() will then return an object map
+ * containing the values.
+ * @param map if you specify an object that is neither list nor string, get() will use it as a map of property names. Each property name will be requested.
+ * The values of the properties in the map will be ignored. get() will then return a new object map containing of results.
+ * @param toNumber if 'true', get() converts all returned values into numbers. If they are strings,
+ * get() removes any non-numeric characters before the conversion. This is useful when you request
+ * a CSS property such as '$marginTop' that returns a value with a unit suffix, like "21px". get() will convert it
+ * into a number and return 21. If the returned value is not parsable as a number, NaN will be returned.
+ * @return if get() was called with a single name, it returns the corresponding value.
+ * If a list or map was given, get() returns a new object map with the names as keys and the values as values.
+ * It returns undefined if the list is empty.
+ *
+ * @see ##set() sets values using the same property syntax.
+ */
+ 'get': function(spec, toNumber) {
+ var self = this;
+ var element = self[0];
+
+ if (element) {
+ if (isString(spec)) {
+ var match = /^(\W*)(.*)/.exec(replace(spec, /^%/,'@data-'));
+ var prefix = match[1];
+ var s;
+
+ if (getter[prefix])
+ s = getter[prefix](this, match[2]);
+ else if (spec == '$')
+ s = self['get']('className');
+ else if (spec == '$$') {
+ s = self['get']('@style');
+ }
+ else if (spec == '$$slide')
+ s = self['get']('$height');
+ else if (spec == '$$fade' || spec == '$$show') {
+ if (self['get']('$visibility') == 'hidden' || self['get']('$display') == 'none')
+ s = 0;
+ else if (spec == '$$fade') {
+ s =
+ isNaN(self['get']('$opacity', true)) ? 1 : self['get']('$opacity', true);
+ }
+ else // $$show
+ s = 1;
+ }
+ else if (prefix == '$') {
+ s = _window['getComputedStyle'](element, _null)['getPropertyValue'](replace(match[2], /[A-Z]/g, function (match2) { return '-' + match2.toLowerCase(); }));
+ }
+ else if (prefix == '@')
+ s = element.getAttribute(match[2]);
+ else
+ s = element[match[2]];
+ return toNumber ? extractNumber(s) : s;
+ }
+ else {
+ var r = {};
+ (isList(spec) ? flexiEach : eachObj)(spec, function(name) {
+ r[name] = self['get'](name, toNumber);
+ });
+ return r;
+ }
+ }
+ },
+
+ /*$
+ * @id set
+ * @group SELECTORS
+ * @requires dollar get
+ * @configurable default
+ * @name .set()
+ * @syntax list.set(name, value)
+ * @syntax list.set(properties)
+ * @syntax list.set(cssClasses)
+ * @module WEB
+ *
+ * Modifies the list's elements by setting their properties, attributes, CSS styles and/or CSS classes. You can either supply a
+ * single name and value to set only one property, or you can provide an object that contains name/value pairs to describe more than one property.
+ * More complex operations can be accomplished by supplying functions as values. They will then be called for each element that will
+ * be set.
+ *
+ * The name parameter defines what kind of data you are setting. The following name schemes are supported:
+ *
+ *
+ *
Name Schema
Example
Sets what?
Description
+ *
name
innerHTML
Property
A name without prefix of '$' or '@' sets a property of the object.
+ *
@name
@href
Attribute
Sets the HTML attribute using setAttribute(). In order to stay compatible with Internet Explorer 7 and earlier,
+ * you should not set the attributes '@class' and '@style'. Instead use '$' and '$$' as shown below.
+ *
%name
%phone
Data-Attribute
Sets a data attribute using setAttribute(). Data attributes are
+ * attributes whose names start with 'data-'. '%myattr' and '@data-myattr' are equivalent.
+ *
$name
$fontSize
CSS Property
Sets a style using the element's style object.
+ * The syntax for the CSS styles is camel-case (e.g. "$backgroundColor", not "$background-color").
+ *
$
$
CSS Classes
A simple $ modifies the element's CSS classes using the object's className property. The value is a
+ * space-separated list of class names. If prefixed with '-' the class is removed, a '+' prefix adds the class and a class name without prefix toggles the class.
+ * The name '$' can also be omitted if set is called with class names as only argument.
+ *
$$
$$
Style
Sets the element's style attribute in a browser-independent way.
+ *
$$show
$$show
Show/Hide
If true or a number not 0, it will make sure the element is visible by
+ * making sure '$display' is not 'none' and by setting '$visibility' to 'visible'. Please see ##show() for details. If the value is false or 0, it
+ * will be hidden by setting '$display' to 'none'.
+ *
$$fade
$$fade
Fade Effect
The name '$$fade' sets the opacity of the element in a browser-independent way. The value must be a number
+ * between 0 and 1. '$$fade' will also automatically control the element's 'visibility' style. If the value is 0,
+ * the element's visibility will automatically be set to 'hidden'. If the value is larger, the visibility will be set to
+ * 'visible'. '$$fade' only works with block elements.
+ *
$$slide
$$slide
Slide Effect
The name '$$slide' allows a vertical slide-out or slide-in effect. The value must be a number
+ * between 0 and 1 and will be used to set the element's '$height'. '$$slide' will also automatically control the element's 'visibility'
+ * style. If the value is 0, the element's visibility will automatically be set to 'hidden'. If the value is larger,
+ * the visibility will be set to 'visible'. '$$slide' only works with block elements and will not set the
+ * element's margin or padding. If you need a margin or padding, you should wrap the elements in a simple <div>.
+ *
$$scrollX, $$scrollY
$$scrollY
Scroll Coordinates
The names '$$scrollX' and
+ * '$$scrollY' can be used on $(window) to set the scroll coordinates of the document.
+ * The coordinates are specified in pixels, but must not use a 'px' unit postfix.
+ *
+ * @param name the name of a single property or attribute to modify. Unprefixed names set properties, a '$' prefix sets CSS styles and
+ * '@' sets attributes. Please see the table above for special properties and other options.
+ * @param value the value to set. If value is null and name specified an attribute, the attribute will be removed.
+ * If dollar ('$') has been passed as name, the value can contain space-separated CSS class names. If prefixed with a '+' the class will be added,
+ * with a '-' prefix the class will be removed. Without prefix, the class will be toggled.
+ * If value is a function, the function(oldValue, index, obj) will be invoked for each list element
+ * to evaluate the new value:
+ *
oldValue
The old value of the property to be changed, as returned by ##get().
+ * For the CSS style names, this is the computed style of the property
+ *
index
The list index of the object owning the property
+ *
obj
The list element owning the property.
+ *
(callback return value)
The value to be set.
+ * Functions are not supported by '$'.
+ * @param properties a Object as map containing names as keys and the values to set as map values. See above for the name and value syntax.
+ * @param cssClasses if set() is invoked with a string as single argument, the name "$" (CSS classes) is assumed and the argument is the
+ * value. See above for CSS syntax.
+ * Instead of a string, you can also specify a function(oldValue, index, obj) to modify the existing classes.
+ * @return the list
+ *
+ * @see ##get() retrieves values using the same property syntax.
+ * @see ##animate() animates values using the same property syntax.
+ * @see ##toggle() can toggle between two sets of values.
+ * @see ##dial() allows smooth transitions between two sets of values.
+ */
+ 'set': function (name, value) {
+ var self = this;
+ if (value !== undef) {
+ var match = /^(\W*)(.*)/.exec(replace(replace(name, /^\$float$/, 'cssFloat'), /^%/,'@data-'));
+ var prefix = match[1];
+
+ if (setter[prefix])
+ setter[prefix](this, match[2], value);
+ else if (name == '$$fade') {
+ this['set']({'$visibility': value ? 'visible' : 'hidden', '$opacity': value});
+ }
+ else if (name == '$$slide') {
+ self['set']({'$visibility': value ? 'visible' : 'hidden', '$overflow': 'hidden',
+ '$height': /px/.test(value) ? value : function(oldValue, idx, element) { return getNaturalHeight($(element), value);}
+ });
+ }
+ else if (name == '$$show') {
+ if (value)
+ self['set']({'$visibility': value ? 'visible' : 'hidden', '$display': ''}) // that value? part is only for gzip
+ ['set']({'$display': function(oldVal) { // set for 2nd time: now we get the stylesheet's $display
+ return oldVal == 'none' ? 'block' : oldVal;
+ }});
+ else
+ self['set']({'$display': 'none'});
+ }
+ else if (name == '$$') {
+ self['set']('@style', value);
+ }
+ else
+ flexiEach(this, function(obj, c) {
+ var newValue = isFunction(value) ? value($(obj)['get'](name), c, obj) : value;
+ if (prefix == '$') {
+ if (match[2])
+ obj['style'][match[2]] = newValue;
+ else {
+ flexiEach(newValue && newValue.split(/\s+/), function(clzz) {
+ var cName = replace(clzz, /^[+-]/);
+
+ if (/^\+/.test(clzz))
+ obj['classList'].add(cName);
+ else if (/^-/.test(clzz))
+ obj['classList'].remove(cName);
+ else
+ obj['classList'].toggle(cName);
+ });
+ }
+ }
+ else if (name == '$$scrollX')
+ obj['scroll'](newValue, $(obj)['get']('$$scrollY'));
+ else if (name == '$$scrollY')
+ obj['scroll']($(obj)['get']('$$scrollX'), newValue);
+ else if (prefix == '@') {
+ if (newValue == _null)
+ obj.removeAttribute(match[2]);
+ else
+ obj.setAttribute(match[2], newValue);
+ }
+ else
+ obj[match[2]] = newValue;
+ });
+ }
+ else if (isString(name) || isFunction(name))
+ self['set']('$', name);
+ else
+ eachObj(name, function(n,v) { self['set'](n, v); });
+ return self;
+ },
+
+ /*$
+ * @id add
+ * @group ELEMENT
+ * @requires dollar each
+ * @configurable default
+ * @name .add()
+ * @syntax list.add(text)
+ * @syntax list.add(node)
+ * @syntax list.add(list)
+ * @syntax list.add(factoryFunction)
+ * @module WEB
+ * Adds the given node(s) as children to the list's HTML elements. If a string has been given, it will be added as text node.
+ * DOM nodes will be added directly. If you pass a list, all its elements will be added using the rules above.
+ *
+ * When you pass a DOM node and the target list has more than one element, the original node will be added to the first list element,
+ * and ##clone#clones## to all following list elements.
+ *
+ * ##EE(), ##HTML() and ##clone() are compatible with add() and can help you create new HTML nodes.
+ *
+ * @example Using the following HTML:
+ *
+ * <div id="comments">Here is some text.<br/></div>
+ *
+ * The next line appends a text node to the div:
+ *
+ *
+ * @param text a string or number to add as text node
+ * @param node a DOM node to add to the list. If the list has more than one element, the given node will be added to the first element.
+ * For all additional elements, the node will be cloned using ##clone().
+ * @param list a list containing text and/or nodes. May also contain nested lists with nodes or text..
+ * @param factoryFunction a function(listItem, listIndex) that will be invoked for each list element to create the nodes:
+ *
listItem
The list element that will receive the new children.
+ *
listIndex
The index of the list element that will receive the new children.
+ *
(callback return value)
The node(s) to be added to the list element.
+ * Can be either a string for a text node, an HTML element or a list containing strings and/or DOM nodes.
+ * If a function is returned, it will be invoked recursively with the same arguments.
+ * @return the current list
+ *
+ * @see ##fill() works like add(), but deletes all children before adding the new nodes.
+ * @see ##addFront() adds nodes as first child, not as last.
+ * @see ##addAfter() adds nodes not as children but as siblings.
+ * @see ##addBefore() also adds nodes not as children but as siblings.
+ * @see ##replace() replaces existing nodes.
+ */
+ 'add': function (children, addFunction) {
+ return this['each'](function(e, index) {
+ var lastAdded;
+ function appendChildren(c) {
+ if (isList(c))
+ flexiEach(c, appendChildren);
+ else if (isFunction(c))
+ appendChildren(c(e, index));
+ else if (c != _null) { // must check null, as 0 is a valid parameter
+ var n = isNode(c) ? c : document.createTextNode(c);
+ if (lastAdded)
+ lastAdded['parentNode']['insertBefore'](n, lastAdded['nextSibling']);
+ else if (addFunction)
+ addFunction(n, e, e['parentNode']);
+ else
+ e.appendChild(n);
+ lastAdded = n;
+ }
+ }
+ appendChildren(index &&!isFunction(children) ? clone(children) : children);
+ });
+ },
+
+ /*$
+ * @id on
+ * @group EVENTS
+ * @requires dollar each
+ * @configurable default
+ * @name .on()
+ * @syntax list.on(names, eventHandler)
+ * @syntax list.on(selector, names, eventHandler)
+ * @syntax list.on(names, customFunc, args)
+ * @syntax list.on(selector, names, customFunc, args)
+ * @syntax list.on(names, eventHandler, bubbleSelector)
+ * @syntax list.on(names, customFunc, args, bubbleSelector)
+ * @module WEB
+ * Registers the function as event handler for all items in the list.
+ *
+ * By default, Minified cancels event propagation and disables element's default behavior for all elements that have an event handler.
+ * You can override this, either by prefixing the event name with a '|', or by prefixing them with '?' and returning a true
+ * in the handler. Both will reinstate the original JavaScript behavior.
+ *
+ * Handlers are called with the original event object as first argument, the index of the source element in the
+ * list as second argument and 'this' set to the source element of the event (e.g. the button that has been clicked).
+ *
+ * Instead of the event objects, you can also pass an array of arguments that will be passed instead of event object and index.
+ *
+ * Optionally you can specify two a selector strings to qualify only certain events. The first one is a selector
+ * that allows you to select only specific children of the list elements. This is mostly useful for adding events to DOM trees
+ * generated using ##HTML() or ##EE().
+ *
+ * The second type of selector is the bubble selector that allows you to receive only events that bubbled up from
+ * elements matching the selector. The selector is executed in the context of the element you registered on to identify whether the
+ * original target of the event qualifies. If not, the handler is not called.
+ *
+ * Minified always registers event handlers with event bubbling enabled. Event capture is not supported.
+ *
+ * Event handlers can be unregistered using #off#$.off().
+ *
+ * @example Adds a handler to all divs which paints the div background color to red when clicked.
+ *
+ * $('div').on('click', function() {
+ * this.style.backgroundColor = 'red'; // 'this' contains the element that caused the event
+ * });
+ *
+ *
+ * @example Registers a handler to call a method setStatus('running') using an inline function:
+ *
+ * Please note that bubble selectors will even listen to events for
+ * table rows that have been added after you registered for the events.
+ *
+ * @param selector optional a selector string for ##dollar#$()## to register the event only on those children of the list elements that
+ * match the selector.
+ * Supports all valid parameters for $() except functions.
+ * @param names the space-separated names of the events to register for, e.g. 'click'. Case-sensitive. The 'on' prefix in front of
+ * the name must not used. You can register the handler for more than one event by specifying several
+ * space-separated event names. If the name is prefixed
+ * with '|' (pipe), the event will be passed through and the event's default actions will be executed by the browser.
+ * If the name is prefixed with '?', the event will only be passed through if the handler returns true.
+ * @param eventHandler the callback function(event, index) to invoke when the event has been triggered:
+ *
+ *
event
The original DOM event object.
+ *
index
The index of the target object in the ##list#Minified list## .
+ *
this
A ##list#Minified list## containing the target element as only item (same as event.target).
+ *
(callback return value)
The return value will only be used if the event name prefix was '?'.
+ * Then, a return value false will stop all further processing of the event and disable event bubbling.
+ * true will keep the event alive.
+ *
+ * @param customFunc a function to be called instead of a regular event handler with the arguments given in args.
+ * 'this' will be a ##list#Minified list## containing the target element as only item (same element as event.target).
+ * @param args optional an array of arguments to pass to the custom callback function instead of the event objects. If omitted, the function is
+ * called as event handler with the event object as argument.
+ * @param bubbleSelector optional a selector string for ##dollar#$()## to receive only events that bubbled up from an
+ * element that matches this selector.
+ * Supports all valid parameters for $() except functions. Analog to ##is(),
+ * the selector is optimized for the simple patterns '.classname', 'tagname' and 'tagname.classname'.
+ * @return the list
+ * @see ##off() allows you to unregister an event handler.
+ * @see ##onClick() as a shortcut for 'click' events.
+ * @see ##onOver() to simplify mouseover/mouseout events.
+ * @see ##onFocus() as convenient way to register for focus events.
+ * @see ##onChange() to get notified when an input's content changes.
+ */
+ 'on': on,
+
+ /*$
+ * @id trigger
+ * @group EVENTS
+ * @requires on each
+ * @configurable default
+ * @name .trigger()
+ * @syntax list.trigger(name)
+ * @syntax list.trigger(name, eventObject)
+ * @module WEB
+ *
+ * Triggers event handlers registered with ##on().
+ * Any event that has been previously registered using ##on() can be invoked with trigger(). Please note that
+ * it will not simulate the default behavior on the elements, such as a form submit when you click on a submit button. Event bubbling
+ * is supported, thus unless there's an event handler that cancels the event, the event will be triggered on all parent elements.
+ *
+ *
+ * @example Simulates a 'click' event on the button.
+ *
+ * $('#myButton').trigger('click');
+ *
+ *
+ * @param name a single event name to trigger
+ * @param eventObj optional an object to pass to the event handler, provided the handler does not have custom arguments.
+ * Anything you pass here will be directly given to event handlers as event object, so you need to know what
+ * they expect.
+ * @return the list
+ * @see ##on() registers events that can be triggered.
+ */
+ 'trigger': function (eventName, eventObj) {
+ return this['each'](function(element, index) {
+ var bubbleOn = true, el = element;
+ while(el && bubbleOn) {
+ eachObj(el['M'], function(id, f) {
+ bubbleOn = bubbleOn && f(eventName, eventObj, element);
+ });
+ el = el['parentNode'];
+ }
+ });
+ }
+
+ /*$
+ * @stop
+ */
+ // @cond !trigger dummyTrigger:0
+ ,
+ ///#/snippet webListFuncs
+ ///#snippet extrasListFuncs
+
+ /*$
+ * @id ht
+ * @group ELEMENT
+ * @requires set template
+ * @configurable default
+ * @name .ht()
+ * @syntax list.ht(templateString, object...)
+ * @syntax list.ht(templateFunction, object...)
+ * @syntax list.ht(idSelector, object...)
+ * @module WEB+UTIL
+ * Replaces the content of the list elements with the HTML generated using the given template. The template uses
+ * ##template() syntax and HTML-escaped its output using ##escapeHtml().
+ *
+ * @example When you have a HTML snippet like this:
+ *
+ * <div id="price"></div>
+ *
+ * Then you can format the price value like this:
+ *
+ *
+ * @example You can store templates in <script> tags. First you need to create a <script> tag with a type not
+ * supported by the browser and put your template in there, like this:
+ *
<script id="myTimeTpl" type="minified-template">The time is {{HH:mm:ss}}.</script>
+ * Then you can specify the tag's id directly to access it:
+ *
$('#timeDisplay').ht('#myTimeTpl', new Date());
+ *
+ * @param templateString the template using ##template() syntax. Please note, because this is a template, you should
+ * avoid creating the template itself dynamically, as compiling templates is expensive and
+ * Minified will cache only a limited number of templates. Exception: If the template string does not use
+ * any template functionality (no {{}}), it does not need to be compiled and won't be cached.
+ * The template will use ##escapeHtml() as escape function, so all template substitutions will be HTML-escaped,
+ * unless you use triple curly-braces.
+ * @param templateFunction instead of a HTML template, ht() can also use a template function, e.g. one
+ * created by ##template(). It will be invoked with the object as only argument.
+ * @param idSelector if you pass an ID CSS selector in the form "#myScript", Minified will recognize this and use the content
+ * of the specified <script> element as template. This allows you to put your template into
+ * a <script> tag with a non-JavaScript type (see example). Any string that starts with '#' and does not
+ * contain any spaces is used as selector.
+ * @param object optional one or more objects to pass to the template. If object is not set, the template is called with undefined
+ * as object. If exactly one object is given, it is passed directly to the template. If you specify more than one
+ * object, they are ##merge#merged##.
+ * @return the current list
+ *
+ * @see ##HTML() creates only the nodes and can be used with ##add() and other methods to add the nodes to the DOM, giving you more flexibility than ht().
+ */
+ 'ht': function(htmlTemplate, object) {
+ var o = arguments.length > 2 ? merge(sub(arguments, 1)) : object;
+ return this['set']('innerHTML', isFunction(htmlTemplate) ? htmlTemplate(o) :
+ /{{/.test(htmlTemplate) ? formatHtml(htmlTemplate, o) :
+ /^#\S+$/.test(htmlTemplate) ? formatHtml($$(htmlTemplate)['text'], o) : htmlTemplate);
+ }
+ /*$
+ * @stop
+ */
+ // @cond !ht dummyHt:0
+ ///#/snippet extrasListFuncs
+ }, M.prototype);
+
+ //// DOLLAR FUNCTIONS ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ copyObj({
+ ///#snippet webDollarFuncs
+ /*$
+ * @id request
+ * @group REQUEST
+ * @requires
+ * @configurable default
+ * @name $.request()
+ * @syntax $.request(method, url)
+ * @syntax $.request(method, url, data)
+ * @syntax $.request(method, url, data, settings)
+ * @module WEB
+ * Initiates a HTTP request to the given URL, using XMLHttpRequest. It returns a ##promiseClass#Promise## object that allows you to obtain the result.
+ *
+ * @example Invokes a REST web service and parses the resulting document using JSON:
+ *
+ * $.request('get', 'http://service.example.com/weather', {zipcode: 90210})
+ * .then(function(txt) {
+ * var json = $.parseJSON(txt);
+ * $('#weatherResult').fill('Today's forecast is is: ' + json.today.forecast);
+ * })
+ * .error(function(status, statusText, responseText) {
+ * $('#weatherResult').fill('The weather service was not available.');
+ * });
+ *
+ *
+ * @example Sending a JSON object to a REST web service:
+ *
+ *
+ *
+ * @param method the HTTP method, e.g. 'get', 'post' or 'head' (rule of thumb: use 'post' for requests that change data
+ * on the server, and 'get' to request data). Not case sensitive.
+ * @param url the server URL to request. May be a relative URL (relative to the document) or an absolute URL. Note that unless you do something
+ * fancy on the server (keyword to google: Access-Control-Allow-Origin), you can only call URLs on the server your script originates from.
+ * @param data optional data to send in the request, either as POST body or as URL parameters. It can be either a plain object as map of
+ * parameters (for all HTTP methods), a string (for all HTTP methods), a DOM document ('post' only) or a FormData object ('post' only).
+ * If the method is 'post', it will be sent as body, otherwise parameters are appended to the URL. In order to send several parameters with the
+ * same name, use an array of values in the map. Use null as value for a parameter without value.
+ * @param settings optional a map of additional parameters. Supports the following properties (all optional):
+ *
headers
a map of HTTP headers to add to the request. Note that you should use the proper capitalization for the
+ * header 'Content-Type', if you set it, because otherwise it may be overwritten.
+ *
xhr
a map of properties to set in the XMLHttpRequest object before the request is sent, for example {withCredentials: true}.
+ *
user
username for HTTP authentication, together with the pass parameter
+ *
pass
password for HTTP authentication, together with the user parameter
+ *
+ * @return a ##promiseClass#Promise## containing the request's status. If the request has successfully completed with a HTTP status 2xx,
+ * the promise's completion handler will be called as function(text, xhr):
+ *
text
The response sent by the server as text.
+ *
xhr
The XMLHttpRequest used for the request. This allows you to retrieve the response in different
+ * formats (e.g. responseXml for an XML document), to retrieve headers and more.
+ * The failure handler will be called as function(statusCode, statusText, text):
+ *
statusCode
The HTTP status (never 200; 0 if no HTTP request took place).
+ *
text
The response's body text, if there was any, or the exception as string if the browser threw one.
+ *
xhr
The XMLHttpRequest used for the request. This allows you to retrieve the response in different
+ * formats (e.g. responseXml for an XML document), to retrieve headers and more..
+ * The returned promise supports ##stop(). Calling stop() will invoke the XHR's abort() method.
+ * The underlying XmlHttpRequest can also be obtained from the promise's xhr property.
+ *
+ * @see ##values() serializes an HTML form in a format ready to be sent by $.request.
+ * @see ##$.parseJSON() can be used to parse JSON responses.
+ * @see ##$.toJSON() can create JSON messages.
+ * @see ##_.format() can be useful for creating REST-like URLs, if you use JavaScript's built-in escape() function.
+ */
+ 'request': function (method, url, data, settings0) {
+ var settings = settings0 || {};
+ var xhr, callbackCalled = 0, prom = promise(), dataIsMap = data && (data['constructor'] == settings['constructor']);
+ try {
+ prom['xhr'] = xhr = new XMLHttpRequest();
+
+ prom['stop0'] = function() { xhr['abort'](); };
+ // @condend
+
+ if (dataIsMap) { // if data is parameter map...
+ data = collector(eachObj, data, function processParam(paramName, paramValue) {
+ return collector(flexiEach, paramValue, function(v) {
+ return encodeURIComponent(paramName) + ((v != _null) ? '=' + encodeURIComponent(v) : '');
+ });
+ }).join('&');
+ }
+
+ if (data != _null && !/post/i.test(method)) {
+ url += '?' + data;
+ data = _null;
+ }
+
+ xhr['open'](method, url, true, settings['user'], settings['pass']);
+ if (dataIsMap && /post/i.test(method))
+ xhr['setRequestHeader']('Content-Type', 'application/x-www-form-urlencoded');
+
+ eachObj(settings['headers'], function(hdrName, hdrValue) {
+ xhr['setRequestHeader'](hdrName, hdrValue);
+ });
+ eachObj(settings['xhr'], function(name, value) {
+ xhr[name] = value;
+ });
+
+ xhr['onreadystatechange'] = function() {
+ if (xhr['readyState'] == 4 && !callbackCalled++) {
+ if (xhr['status'] >= 200 && xhr['status'] < 300)
+ prom['fire'](true, [xhr['responseText'], xhr]);
+ else
+ prom['fire'](false, [xhr['status'], xhr['responseText'], xhr]);
+ }
+ };
+
+ xhr['send'](data);
+ }
+ catch (e) {
+ if (!callbackCalled)
+ prom['fire'](false, [0, _null, toString(e)]);
+ }
+
+ return prom;
+ },
+
+ /*
+ * JSON Module. Uses browser built-ins or json.org implementation if available. Otherwise its own implementation,
+ * originally based on public domain implementation http://www.JSON.org/json2.js / http://www.JSON.org/js.html.
+ * Extremely simplified code, made variables local, removed all side-effects (especially new properties for String, Date and Number).
+ */
+
+ /*$
+ * @id ready
+ * @group EVENTS
+ * @requires ready_vars ready_init
+ * @configurable default
+ * @name $.ready()
+ * @syntax $.ready(handler)
+ * @module WEB
+ * Registers a handler to be called as soon as the HTML has been fully loaded in the browser. Does not necessarily wait for images and other elements,
+ * only the main HTML document needs to be complete. On older browsers it is the same as window.onload.
+ *
+ * If you call ready() after the page is completed, the handler is scheduled for invocation in the event loop as soon as possible.
+ *
+ * A shortcut for ready() is to call ##dollar#$()## with the handler function. It does the same with fewer characters.
+ *
+ * @example Registers a handler that sets some text in an element:
+ *
+ *
+ * @param handler the function() to be called when the HTML is ready.
+ * @see ##dollar#$()## calls ready() when invoked with a function, offering a more convenient syntax.
+ */
+ 'ready': ready,
+
+ /*$
+ * @id off
+ * @group EVENTS
+ * @requires on
+ * @configurable default
+ * @name $.off()
+ * @syntax $.off(handler)
+ * @module WEB
+ * Removes the given event handler. The call will be ignored if the given handler has not been registered using ##on().
+ * If the handler has been registered for more than one element or event, it will be removed from all instances.
+ *
+ * Please note that you can not unregister event handlers registered using ##onOver() or ##onChange().
+ *
+ * @example Adds a handler to an element:
+ *
+ * function myEventHandler() {
+ * this.style.backgroundColor = 'red'; // 'this' contains the element that caused the event
+ * }
+ * $('#myElement').on('click', myEventHandler); // add event handler
+ *
+ * window.setInterval(function() { // after 5s, remove event handler
+ * $.off(myEventHandler);
+ * }, 5000);
+ *
+ *
+ * @param handler the handler to unregister, as given to ##on(). It must be a handler that has previously been registered using ##on().
+ * If the handler is not registered as event handler, the function does nothing.
+ *
+ * @see ##on() registers an event handler.
+ */
+ 'off': off
+
+ /*$
+ * @stop
+ */
+ // @cond !off dummyOff:null
+ ,
+ ///#/snippet webDollarFuncs
+ ///#snippet extrasDollarFuncs
+
+ /*$
+ * @id wait
+ * @group EVENTS
+ * @configurable default
+ * @requires promise
+ * @name $.wait()
+ * @syntax $.wait()
+ * @syntax $.wait(durationMs)
+ * @syntax $.wait(durationMs, args)
+ * @module WEB+UTIL
+ *
+ * Creates a new ##promise#Promise## that will be fulfilled as soon as the specified number of milliseconds have passed. This is mainly useful for animation,
+ * because it allows you to chain delays into your animation chain.
+ *
+ * The operation can be interrupted by calling the promise's ##stop() function.
+ *
+ * @example Chained animation using Promise callbacks. The element is first moved to the position 200/0, then to 200/200, waits for 50ms
+ * and finally moves to 100/100.
+ *
+ *
+ *
+ * @param durationMs optional the number of milliseconds to wait. If omitted, the promise will be fulfilled as soon as the browser can run it
+ * from the event loop.
+ * @param args optional an array or list of arguments to pass to the promise handler
+ * @return a ##promise#Promise## object that will be fulfilled when the time is over, or fail when the promise's ##stop() has been called.
+ * The promise argument of a fulfilled promise is the args parameter as given to wait(). The returned promise supports ##stop()
+ * to interrupt the promise.
+ */
+ 'wait': function(durationMs, args) {
+ var p = promise();
+ var id = setTimeout(function() {
+ p['fire'](true, args);
+ }, durationMs);
+ p['stop0'] = function() { p['fire'](false); clearTimeout(id); };
+ return p;
+ }
+
+ /*$
+ * @stop
+ */
+ // @cond !wait dummyWait:0
+
+ ///#/snippet extrasDollarFuncs
+ }, $);
+
+ //// UNDERSCORE FUNCTIONS ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ copyObj({
+ ///#snippet utilUnderscoreFuncs
+ // @condblock filter
+ 'filter': funcArrayBind(filter),
+ // @condend
+ // @condblock map
+ 'map': funcArrayBind(map),
+ // @condend
+ // @condblock each
+ 'each': each,
+ // @condend
+ // @condblock each
+ 'toObject': toObject,
+ // @condend
+ // @condblock find
+ 'find': find,
+ // @condend
+
+ /*$
+ * @id extend
+ * @group OBJECT
+ * @requires
+ * @configurable default
+ * @name _.extend()
+ * @syntax _.extend(target, src...)
+ * @module UTIL
+ * Copies every property of the source objects into the first object. The source objects are specified using variable arguments.
+ * There can be more than one.
+ * The properties are copied as shallow-copies.
+ *
+ * Please note: Unlike jQuery, extend does not directly add a function to extend Minified, although
+ * you can use it to for this. To add a function to ##list#Minified lists##, add a property to
+ * ##M#MINI.M##. If you want to extend $ or _, just assign the new function(s) as property.
+ *
+ * @example Copying properties:
+ *
var target = {a:3, c: 3};
+ * _.extend(target, {a: 1, b: 2}); // target is now {a: 1, b: 2, c: 3}
+ *
+ * @example Using several source values:
+ *
var extend = _.extend({a: 1, b: 2}, {a:3, c: 3}, {d: 5}); // target is now {a: 1, b: 2, c: 3, d: 5}
+ *
+ * @param target the object to copy to
+ * @param src the object(s) to copy from. Variable argument, there can be any number of sources. Nulls and undefined
+ * parameters will be ignored.
+ * @return the target
+ *
+ * @see ##_.copyObj() is very similar to extend(), but with a slightly different and more straightforward syntax.
+ * @see ##_.merge() copies a list of objects into a new object.
+ */
+ 'extend': function(target) {
+ return merge(sub(arguments, 1), target);
+ },
+
+ /*$
+ * @id eachobj
+ * @group OBJECT
+ * @requires
+ * @configurable default
+ * @name _.eachObj()
+ * @syntax _.eachObj(obj, callback)
+ * @syntax _.eachObj(obj, callback, ctx)
+ * @module UTIL
+ * Invokes the given function once for each property of the given object. The callback is not invoked for inherited properties.
+ *
+ * @example Dumps all properties of an object.
+ *
+ * var s = '';
+ * _.eachObj({a: 1, b: 5, c: 2}, function(key, value) {
+ * s += 'key=' + key + ' value=' + value + '\n';
+ * });
+ *
+ *
+ * @param obj the object to use
+ * @param callback The callback function(key, value) to invoke for each property.
+ *
key
The name of the current property.
+ *
value
The value of the current property.
+ *
this
The given context. If not set, the object itself.
+ * The callback's return value will be ignored.
+ * @param ctx optional a context to pass to the callback as 'this'.
+ * @return the object
+ *
+ * @see ##_.each() iterates through a list.
+ */
+ 'eachObj': eachObj,
+
+ /*$
+ * @id mapobj
+ * @group OBJECT
+ * @requires
+ * @configurable default
+ * @name _.mapObj()
+ * @syntax _.mapObj(obj, callback)
+ * @syntax _.mapObj(obj, callback, ctx)
+ * @module UTIL
+ * Creates a new object with the same properties but different values using the given callback function. The function is called
+ * for each property of the input object to provice a new value for the property.
+ *
+ * @example Increases the values of all properties.
+ *
+ * var r = _.mapObj({a: 1, b: 5, c: 2}, function(key, value) {
+ * return value + 1;
+ * });
+ * // r is now {a: 2, b: 6, c: 2}
+ *
+ *
+ * @param obj the object to use
+ * @param callback The callback function(key, value) to invoke for each property.
+ *
key
The name of the current property.
+ *
value
The value of the current property.
+ *
this
The given context. If not set, the object itself.
+ *
(callback return value)
This value will replace the original value in the new object.
+ * @param ctx optional a context to pass to the callback as 'this'.
+ * @return the new object
+ *
+ * @see ##_.filterObj() filters an object.
+ * @see ##map() maps a list.
+ */
+ 'mapObj': function(obj, mapFunc, ctx) {
+ var result = {};
+ eachObj(obj, function(key, value) {
+ result[key] = mapFunc.call(ctx || obj, key, value);
+ });
+ return result;
+ },
+
+ /*$
+ * @id isobject
+ * @group TYPE
+ * @requires
+ * @configurable default
+ * @name _.isObject()
+ * @syntax _.isObject(obj)
+ * @module UTIL
+ * Checks whether the given reference is an object as defined by typeof.
+ *
+ * @param obj the object to test
+ * @return true if the object is an object, false otherwise.
+ */
+ 'isObject': isObject,
+
+ /*$
+ * @id format
+ * @group FORMAT
+ * @requires template
+ * @configurable default
+ * @name _.format()
+ * @syntax _.format()
+ * @syntax _.format(template, object)
+ * @syntax _.format(template, object, escapeFunction)
+ * @module UTIL
+ * Formats an object using a ##template#template##. The template syntax is shared with ##_.template(). The only difference is that
+ * format() frees you from the extra step of creating the template. In any case, whether you use
+ * format() or ##_.template(), the template will be cached. Be careful when you create templates dynamically, as
+ * every template is cached and consumes memory.
+ * If you only want to format a single value, use ##_.formatValue().
+ *
+ * @example Format a name:
+ *
var s = _.formatHtml("{{first}} {{last}}", {first: 'Tim', last: 'Taylor'});
+ *
+ * @example Format a list of dates:
+ *
var s = _.format("{{each}}{{this :: yyyy-MM-dd}}{{/each}}", dateList);
+ *
+ * @param template The ##template#template## as a string. The template, once created, will be cached.
+ * @param object the object to format
+ * @param escapeFunction optional The callback function(inputString) that will be used
+ * to escape all output:
+ *
inputString
The string to escape.
+ *
(callback return value)
The escaped string.
+ * If no escapeFunction has been given, the output will not be escaped.
+ * ##_.escapeHtml() can be used as an escape function for HTML, and ##_.escapeRegExp() for regular expressions.
+ * JavaScript's built-in escape() function can escape URL components.
+ * See ##_.htmlFormat() for a version of format() that already includes HTML escaping.
+ * @return the string created by the template
+ *
+ * @see ##_.template() creates a template function, using the same syntax.
+ * @see ##_.formatHtml() is a variant of format() with HTML-escpaping built it.
+ * @see ##_.formatValue() formats a single number or date.
+ * @see ##_.escapeRegExp() can be used by format() to escape regular expressions.
+ */
+ 'format': function(tpl, object, escapeFunction) {
+ return template(tpl, escapeFunction)(object);
+ },
+
+ /*$
+ * @id template
+ * @group FORMAT
+ * @requires date_constants
+ * @configurable default
+ * @name _.template()
+ * @syntax _.template(template)
+ * @syntax _.template(template, escapeFunction)
+ * @module UTIL
+ * Parses a Handlebars-like template to create a reusable template function.
+ *
+ * The syntax of the template uses a syntax that superficially looks like
+ * Handlebars. Unlike Handlebars, it is based on raw JavaScript expressions and thus gives you
+ * complete freedom, but also offers you shortcuts for formatting, iteration and conditionals.
+ *
+ * Every template can receive exactly one object as input. If you need more than one value as input, put all required values
+ * into an object.
+ *
+ * Use double curly braces to embed a JavaScript expression and insert its result:
+ *
{{a}} plus {{b}} is {{a+b}}
+ *
+ * To use such a template, create it with template() and then execute the resulting function:
+ *
var myTemplate = _.template('{{a}} plus {{b}} is {{a+b}}');
+ * var result = myTemplate({a: 5, b: 7});
+ * If you pass an object as input, its properties will be mapped using JavaScript's with
+ * statement and are available as variables throughout the template.
+ *
+ * If you have only a simple value to render, you can pass it directly and access it through the pre-defined
+ * variable obj:
+ *
var myTemplate = _.template('The result is {{obj}}.');
+ * var result = myTemplate(17);
+ * Alternatively, you could also access the input as this, but be aware that JavaScript wraps simples types
+ * such as Number and Boolean. this is the default, so you can omit it to get the same result:
+ *
var myTemplate = _.template('The result is {{ }}.');
+ * var result = myTemplate(17);
+ *
+ * Minified templates can use ##_.formatValue() formats directly. Just separate them from the expression by
+ * a double-colon:
+ *
The price is {{obj::#.00}}.
+ *
+ * Conditions can be expressed using if and else:
+ *
Hello {{if visits==0}}New{{else if visits<10}}Returning{{else}}Regular{{/if}} Customer.
+ * You can use any JavaScript expression as condition.
+ *
+ * Use each to iterate through a list:
+ *
var myTemplate = _.template(
+ * '{{each names}}{{this.firstName}} {{this.lastName}}{{/each}}');
+ * var result = myTemplate({names: [{firstName: 'Joe', lastName: 'Jones'},
+ * {firstName: 'Marc', lastName: 'Meyer'}]});
+ * each will iterate through the members of the given object. It
+ * calls its body for each item and put a reference to the item into this.
+ * Optionally, you can specify up to two variables to store the value in and
+ * the zero-based index of the current item:
+ *
+ *
+ * If you do not pass an expression to each, it will take the list from this:
+ *
var myTemplate = _.template('{{each value:}}{{value}};{{/each}}');
+ * var result = myTemplate([1, 2, 3]);
+ *
+ * Beside lists, you can also iterate through the properties of an object. The property name will be stored
+ * in the first given parameter and the value in this and the second parameter:
+ *
var myTemplate = _.template('{{each key, value: nicknames}}{{key}}: {{value}}{{/each}}');
+ * var result = myTemplate({nicknames: {Matt: 'Matthew', John: 'Jonathan'} });
+ *
+ * Shorter version of the previous example that uses this for the value:
+ *
var myTemplate = _.template('{{each key: nicknames}}{{key}}: {{this}}{{/each}}');
+ *
+ * If you do not need the key, you can omit the variable specification:
+ *
var myTemplate = _.template('{{each nicknames}}{{this}}{{/each}}');
+ *
+ * You can define your own variables, using the regular JavaScript syntax, with 'var':
+ *
var myTemplate = _.template('{{var s=very.long.name, sum=a+b;}}{{s.desc}}, {{sum}}');
+ *
+ * In some situations, it may be inevitable to embed raw JavaScript in the template.
+ * To embed JavaScript code, prefix the code with a '#':
+ *
var myTemplate = _.template(
+ * '{{each}}{{#var sum = 0; for (var i = 0; i < 3; i++) sum += this.numbers[i]; }}{{sum}}{{/each}}');
+ * var result = myTemplate([['Foreword', 'Intro'], ['Something', 'Something else']]);
+ *
+ *
+ * By default, all output will be escaped. You can prevent this by using triple-curly-braces:
+ *
Here's the original: {{{rawText}}}
.
+ *
+ * The template's JavaScript code is executed in a sandbox without access to global variables. Minified defines the
+ * following variables for you:
+ *
+ *
Name
Desciption
+ *
this
The template object outside of each. Inside eachs, the current value.
+ *
obj
The parameter given to the template function.
+ *
_
A reference to Minified Util.
+ *
esc
The escape function given when the template has been defined. If no function has been given,
+ * a default function that returns the input unmodified.
+ *
print
A function(text,...) that appends one or more strings to the template result.
+ *
each
A function(listOrObject, eachCallback) that can iterate over lists or object properties.
+ * The eachCallback is a function(key, value) for objects or function(value, index)
+ * for arrays that will be invoked for each item.
+ *
+ *
+ * Every template you create is already cached, so it not an expensive operation to call ##_.template() a second
+ * time with the same template. However, because of caching, you should be careful when creating templates
+ * dynamically, as this will fill the cache up quickly.
+ *
+ * @param template The template as a string using the syntax described below.
+ * @param escapeFunction optional The callback function(inputString) that will be used
+ * to escape all output:
+ *
inputString
The string to escape.
+ *
(callback return value)
The escaped string.
+ * If no escapeFunction has been given, the output will not be escaped.
+ * ##_.escapeHtml() can be used as an escape function for HTML, and ##_.escapeRegExp() for regular expressions.
+ * JavaScript's built-in escape() function can escape URL components.
+ * @return the value returned by the last invocation of func
+ *
+ * @see ##_.format() shares template()'s syntax but returns the result directly.
+ * @see ##_.formatHtml() is a variant of format() with HTML escaping.
+ * @see ##_.escapeHtml() can be used by template() to escape HTML.
+ * @see ##_.escapeRegExp() can be used by template() to escape regular expressions.
+ * @see ##HTML() creates a HTML element tree from a template.
+ */
+ 'template': template,
+
+ /*$
+ * @id formathtml
+ * @group FORMAT
+ * @requires template
+ * @configurable default
+ * @name _.formatHtml()
+ * @syntax _.formatHtml()
+ * @syntax _.formatHtml(template, object)
+ * @module UTIL
+ * Formats an object using a ##template#template## with HTML escaping for the output.
+ * The template syntax is shared with ##_.template(). Output in double curly braces is automatically escaped using ##_.escapeHtml().
+ * formatHtml() just creates a new template with HTML escaping and invokes it immediately.
+ * The template will be cached. Be careful when you create templates dynamically, as
+ * every template is cached and consumes memory.
+ * If you only want to format a single value, use ##_.formatValue().
+ *
+ * @example Format a name:
+ *
var s = _.formatHtml("{{first}} {{last}}", {first: 'Tim', last: 'Taylor'});
+ *
+ * @example Format a list of dates:
+ *
var s = _.formatHtml("{{each}}{{::yyyy-MM-dd}}{{/each}}", dateList);
+ *
+ * @param template The ##template#template## as a string. The template, once created, will be cached.
+ * @param object the object to format
+ * @return the string created by the template
+ *
+ * @see ##ht() works uses formatHtml to set element's innerHTML.
+ * @see ##HTML() create HTML nodes using formatHtml.
+ * @see ##_.template() creates a template function, using the same syntax.
+ * @see ##_.format() allows you to specify alternative escape mechanisms.
+ */
+ 'formatHtml': formatHtml
+ /*$
+ * @stop
+ */
+
+ // @cond !format dummyFormatHtml:0
+ ,
+
+ ///#/snippet utilUnderscoreFuncs
+ ///#snippet extrasUnderscoreFuncs
+ // @condblock promise
+ 'promise': promise
+ // @condend promise
+
+ /*$
+ * @stop
+ */
+ // @cond !promise dummyPromise:0
+
+ ///#/snippet extrasUnderscoreFuncs
+ }, _);
+
+ ////INITIALIZATION ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ ///#snippet webInit
+ /*$
+ * @id ready_init
+ * @dependency
+ */
+ document.addEventListener("DOMContentLoaded", function() {
+ callList(DOMREADY_HANDLER);
+ DOMREADY_HANDLER = _null;
+ }, false);
+ /*$
+ @stop
+ */
+
+
+ ///#/snippet webInit
+
+ return {
+ ///#snippet extrasExports
+
+ /*$
+ * @id html
+ * @group ELEMENT
+ * @requires template ht
+ * @configurable default
+ * @name HTML()
+ * @syntax HTML(templateString, object...)
+ * @syntax HTML(templateFunction, object...)
+ * @syntax HTML(idSelector, object...)
+ * @module WEB
+ * Creates a ##list#list## of HTML nodes from the given HTML template. The list is compatible with ##add(), ##fill() and related methods.
+ * The template uses the ##template() syntax with ##escapeHtml() escaping for values.
+ *
+ * Please note that the function HTML will not be automatically exported by Minified. You should always import it
+ * using the recommended import statement:
+ *
+ * var MINI = require('minified'), $ = MINI.$, $$ = MINI.$$, EE = MINI.EE, HTML = MINI.HTML;
+ *
+ *
+ * @example Creating a HTML element showing a number:
+ *
+ *
+ * @example You can store templates in <script> tags. First you need to create a <script> tag with a type not
+ * supported by the browser and put your template in there, like this:
+ *
<script id="myTimeTpl" type="minified-template">The time is {{HH:mm:ss}}.</script>
+ * Then you can specify the tag's id directly to access it:
+ *
$('#timeDisplay').fill(HTML('#myTimeTpl', new Date()));
+ *
+ * @param templateString the template using ##template() syntax. Please note, because this is a template, you should
+ * avoid creating the template itself dynamically, as compiling templates is expensive and
+ * Minified will cache only a limited number of templates. Exception: If the template string does not use
+ * any template functionality (no {{}}), it does not need to be compiled and won't be cached.
+ * The template will use ##escapeHtml() as escape function, so all template substitutions will be HTML-escaped,
+ * unless you use triple curly-braces.
+ * @param templateFunction instead of a HTML template HTML() also accepts a template function, e.g. one
+ * created by ##template(). It will be invoked with the object as only argument.
+ * @param idSelector if you pass an ID CSS selector in the form "#myScript", Minified will recognize this and use the content
+ * of the specified <script> element as template. This allows you to put your template into
+ * a <script> tag with a non-JavaScript type (see example). Any string that starts with '#' and does not
+ * contain any spaces is used as selector.
+ * @param object optional one or more objects to pass to the template. If object is not set, the template is called with undefined
+ * as object. If exactly one object is given, it is passed directly to the template. If you specify more than one
+ * object, they are ##merge#merged##.
+ * @return the list containing the new HTML nodes
+ *
+ * @see ##ht() is a shortcut for fill(HTML()).
+ * @see ##EE() is a different way of creating HTML nodes.
+ */
+ 'HTML': function () {
+ var div = EE('div');
+ return _(call(div['ht'], div, arguments)[0].childNodes);
+ },
+ /*$
+ * @stop
+ */
+
+ ///#/snippet extrasExports
+ ///#snippet utilExports
+ /*$
+ * @id underscore
+ * @group LIST
+ * @name _()
+ * @syntax _(item...)
+ * @configurable default
+ * @module UTIL
+ * Creates a new Minified list. Supports variable arguments so you can add items directly to the list. For arguments that are lists
+ * (as defined by ##_.isList()), the list content will be added to the new list. Unlike #dollar#$()#, this is not done recursively
+ * and thus you can create a list of lists by wrapping arguments in a list. Another difference between _() and $()
+ * is that $() will automatically remove null values while _() will keep them.
+ *
+ * @example Creating an empty list:
+ *
_()
+ *
+ * @example Creating a list with three items:
+ *
_(1, 2, 3)
+ *
+ * @example Creating the same list, but by passing an array. One array level will be flattened:
+ *
_([1, 2, 3])
+ *
+ * @example Creating a list containing the arrays [1, 2] and [3, 4].
+ *
_([[1, 2], [3, 4]])
+ *
+ * @example Merging two lists:
+ *
var a = _("a", "b", "c");
+ * var b = _("x", "y", "z");
+ * var merged = _(a, b); // contains _("a", "b", "c", "x", "y", "z")
+ *
+ *
+ * @example Adding two elements to a list:
+ *
var a = _(1, 2, 3);
+ * var a4 = _(a, 4); // contains _(1, 2, 3, 4)
+ *
+ *
+ * @example Mixing different list types and single elements:
+ *
_(1, [], [2, 3], _(), _(4, 5)); // same content as _(1, 2, 3, 4, 5)
+ *
+ * @param item an item to add to the new list. If it is a list (as defined by ##_.isList()), its content will be to the new
+ * ##Minified list#list## (but NOT recursively).
+ */
+ '_': _,
+ /*$
+ * @stop
+ */
+ ///#/snippet utilExports
+ ///#snippet webExports
+
+ /*$
+ * @id dollar
+ * @group SELECTORS
+ * @requires
+ * @dependency yes
+ * @name $()
+ * @syntax $()
+ * @syntax $(selector)
+ * @syntax $(selector, context)
+ * @syntax $(selector, context, childOnly)
+ * @syntax $(list)
+ * @syntax $(list, context)
+ * @syntax $(list, context, childOnly)
+ * @syntax $(object)
+ * @syntax $(object, context)
+ * @syntax $(object, context, childOnly)
+ * @syntax $(domreadyFunction)
+ * @module WEB
+ * Creates a new ##list#Minified list##, or register a DOMReady-handler.
+ * The most common usage is with a CSS-like selector. $() will then create a list containing all elements of the current HTML
+ * document that fulfill the filter conditions. Alternatively you can also specify a list of objects or a single object.
+ * Nested lists will automatically be flattened, and nulls will automatically be removed from the resulting list.
+ * If you call $() without any arguments, it will return an empty list.
+ *
+ * Additionally, you can specify a second argument to provide a context. Contexts only make sense if you selected
+ * HTML nodes with the first parameter. Then the context limits the resulting list to include only those nodes
+ * that are descendants of the context nodes. The context can be either a selector, a list or a single HTML node, and will be
+ * processed like the first argument. A third arguments allows you to limit the list to
+ * only those elements that are direct children of the context nodes (so a child of a child would be filtered out).
+ *
+ * The lists created by $() are the same type as the ##list#Minified lists## created by Util's #underscore#_() constructor and other
+ * Util methods. All Util methods work on lists created by $(). If you want to add your own methods to those lists,
+ * use ##M#MINI.M##.
+ *
+ * As a special shortcut, if you pass a function to $(), it will be registered using #ready#$.ready() to be executed
+ * when the DOM model is complete.
+ *
+ * @example A simple selector to find an element by id.
+ *
+ * var l0 = $('#myElementId');
+ *
+ *
+ * @example You can pass an object reference to create a list containing only this element:
+ *
+ * var l1 = $(document.getElementById('myElementId'));
+ *
+ *
+ * @example Lists and arrays will be copied:
+ *
+ * var l2 = $([elementA, elementB, elementC]);
+ *
+ *
+ * @example Lists will be automatically flattened and nulls removed. So this list l3 has the same content as l2:
+ *
+ *
+ * @example This is a simple selector to find all elements with the given class.
+ *
+ * var l4 = $('.myClass');
+ *
+ *
+ * @example A selector to find all elements of the given type.
+ *
+ * var l5 = $('input'); // finds all input elements
+ *
+ *
+ * @example A selector to find all elements with the given type and class.
+ *
+ * var l6 = $('input.myRadio'); // finds all input elements with class 'myRadio'
+ *
+ *
+ * @example A selector to find all elements that are descendants of the given element.
+ *
+ * var l7 = $('#myForm input'); // finds all input elements contained in the element myForm
+ *
+ *
+ * @example A selector to find all elements that have either a CSS class 'a' or class 'b':
+ *
+ * var l8 = $('.a, .b'); // finds all elements that have class a or class b
+ *
+ *
+ * @example A selector that finds all elements that are descendants of the element myDivision, are inside an element with the
+ * class .myForm and are input elements:
+ *
+ * var l9 = $('#myDivision .myForm input');
+ *
+ *
+ * @example Contexts can make it easier to specify ancestors:
+ *
+ *
+ * @example Using one of the list functions, ##set(), on the list, and setting the element's text color. '$' at the beginning of the property name sets a CSS value.
+ *
+ * $('#myElementId').set('$color', 'red');
+ *
+ *
+ * @example Most list methods return the list you invoked them on, allowing you to chain them:
+ *
+ *
+ * @example Using $() as a #ready#$.ready() shortcut:
+ *
+ * $(function() {
+ * // in here you can safely work with the HTML document
+ * });
+ *
+ *
+ * @param selector a simple, CSS-like selector for HTML elements. It supports '#id' (lookup by id), '.class' (lookup by class),
+ * 'element' (lookup by elements) and 'element.class' (combined class and element). Use commas to combine several selectors.
+ * You can also join two or more selectors by space to find elements which are descendants of the previous selectors.
+ * For example, use 'div' to find all div elements, '.header' to find all elements containing a class name called 'header', and
+ * 'a.popup' for all a elements with the class 'popup'. To find all elements with 'header' or 'footer' class names,
+ * write '.header, .footer'. To find all divs elements below the element with the id 'main', use '#main div'.
+ * The selector "*" will return all elements.
+ * @param list a list to copy. It can be an array, another Minified list, a DOM nodelist or anything else that has a length property and
+ * allows read access by index. A shallow copy of the list will be returned. Nulls will be automatically removed from the copy. Nested lists
+ * will be flattened, so the result only contains nodes.
+ * @param object an object to create a single-element list containing only the object. If the argument is null, an empty list will be returned.
+ * @param domreadyFunction a function to be registered using #ready#$.ready().
+ * @param context optional an optional selector, node or list of nodes which specifies one or more common ancestor nodes for the selection. The context can be specified as
+ * a selector, a list or using a single object, just like the first argument.
+ * The returned list will contain only descendants of the context nodes. All others will be filtered out.
+ * @param childOnly optional if set, only direct children of the context nodes are included in the list. Children of children will be filtered out. If omitted or not
+ * true, all descendants of the context will be included.
+ * @return the array-like ##list#Minified list## object containing the content specified by the selector.
+ * Please note that if the first argument was a list, the existing order will be kept. If the first argument was a simple selector, the nodes are in document order.
+ * If you combined several selectors using commas, only the individual results of the selectors will keep the document order,
+ * but will then be joined to form a single list. This list will
+ * not be in document order anymore, unless you use a build without legacy IE support.
+ * Duplicate nodes will be removed from selectors, but not from lists.
+ *
+ * @see #underscore#_() is Util's alternative constructor for ##list#Minified lists##
+ * @see ##dollardollar#$$()## works like $(), but returns the resulting list's first element.
+ */
+ '$': $,
+
+ /*$
+ * @id dollardollar
+ * @group SELECTORS
+ * @requires
+ * @configurable default
+ * @name $$()
+ * @syntax $(selector)
+ * @syntax $(selector, context)
+ * @syntax $(selector, context, childOnly)
+ * @shortcut $$() - It is recommended that you assign MINI.$$ to a variable $$.
+ * @module WEB
+ * Returns a DOM object containing the first match of the given selector, or undefined if no match was found.
+ * $$ allows you to easily access an element directly. It is the equivalent to writing $(selector)[0].
+ *
+ * Please note that the function $$ will not be automatically exported by Minified. You should always import it
+ * using the recommended import statement:
+ *
+ * var MINI = require('minified'), $ = MINI.$, $$ = MINI.$$, EE = MINI.EE;
+ *
+ *
+ * @param selector a simple, CSS-like selector for the element. Uses the same syntax as #dollar#$(). The most common
+ * parameter for this function is the id selector with the syntax "#id".
+ * @param context optional an optional selector, node or list of nodes which specifies one or more common ancestor nodes for the selection. The context can be specified as
+ * a selector, a list or using a single object, just like the first argument.
+ * The returned list will contain only descendants of the context nodes. All others will be filtered out.
+ * @param childOnly optional if set, only direct children of the context nodes are included in the list. Children of children will be filtered out. If omitted or not
+ * true, all descendants of the context will be included.
+ * @return a DOM object of the first match, or undefined if the selector did not return at least one match
+ *
+ * @see ##dollar#$()## creates a list using the selector, instead of returning only the first result.
+ */
+ '$$': $$,
+
+ /*$
+ * @id M
+ * @name M
+ * @syntax MINI.M
+ * @module WEB, UTIL
+ *
+ * Exposes the internal class used by all ##list#Minified lists##. This is mainly intended to allow you adding your
+ * own functions.
+ *
+ * @example Adding a function printLength() to M:
+ *
+ */
+ 'M': M,
+
+ /*$
+ * @id getter
+ * @requires get
+ * @name MINI.getter
+ * @syntax MINI.getter
+ * @module WEB
+ *
+ * Exposes a map of prefix handlers used by ##get(). You can add support for a new prefix in get()
+ * by adding a function to this map. The prefix can be any string consisting solely of non-alphanumeric characters
+ * that's not already used by Minified.
+ *
+ * You must not replace getters by a new map, but must always modify the existing map.
+ *
+ * The function's signature is function(list, name) where
+ *
list
Is the Minified list to get the value from. By convention you should always use only the first element. The list is
+ * non-empty and the first elememt can't be null or undefined (get() automatically returns undefined in
+ * all other case).
+ *
name
The name of the property. That's the part AFTER the prefix.
+ *
(callback return value)
The value to return to the user.
+ *
+ * @example Adding a shortcut '||' for accessing border style properties:
+ *
+ * MINI.getter['||'] = function(list, name) {
+ * return list.get('$border' + name.replace(/^[a-z]/, function(a) { return a.toUpperCase()});
+ * };
+ *
+ * var borderColor = $('#box').get('||color'); // same as '$borderColor'
+ * var borderLeftRadius = $('#box').get('||leftRadius'); // same as '$borderLeftRadius'
+ *
+ *
+ * @example Adding XLink attribute support to get(). This is useful if you work with SVG. The prefix is '>'.
+ *
+ */
+ 'getter': getter,
+
+ /*$
+ * @id setter
+ * @requires set
+ * @name MINI.setter
+ * @syntax MINI.setter
+ * @module WEB
+ *
+ * Exposes a map of prefix handlers used by ##set(). You can add support for a new prefix in set()
+ * by adding a function to this map. The prefix can be any string consisting solely of non-alphanumeric characters
+ * that's not already used by Minified.
+ *
+ * You must not replace setters by a new map, but must always modify the existing map.
+ *
+ * The function's signature is function(list, name, value) where
+ *
list
Is the Minified list to use.
+ *
name
The name of the property. That's the part AFTER the prefix.
+ *
value
Either the value to set, or a callback function to create the value that you must call for each
+ * value (see ##set() ).
+ *
+ *
+ * If you provide complete ##get() and ##set() support for a prefix, you are also able to use it in other Minified
+ * function such as ##animate() and ##toggle().
+ *
+ * @example Adding a shortcut '||' for accessing border style properties. As it's just calling ##set() for an existing
+ * property, it is not required to extra code for the callback.
+ *
+ */
+ 'setter': setter
+ /*$
+ * @stop
+ */
+ ///#/snippet webExports
+ };
+
+ ///#snippet commonAmdEnd
+});
+///#/snippet commonAmdEnd
+///#snippet webDocs
+
+/*$
+ * @id list
+ * @name Minified Lists
+ * @module WEB, UTIL
+ *
+ * Minified lists are Array-like objects provided by Minified. Like a regular JavaScript array,
+ * they provide a length property and you can access their content using the index operator (a[5]).
+ * However, they do not provide the same methods as JavaScript's native array and are designed to be immutable, so
+ * there is no direct way to add something to a Minified list. Instead Minified provides a number of functions and methods
+ * that take a list and create a modified copy which, for example, may contain additional elements.
+ *
+ * Minified lists are typically created either using the Web module's #dollar#$() function or with the Util module's
+ * #underscore#_() function, but many functions in the Util module also return a Minified list.
+ *
+ * The Util module provides a function ##_.array() that converts a Minified list to a regular JavaScript array.
+ */
+
+/*$
+ * @id promiseClass
+ * @name Promise
+ * @module WEB, UTIL
+ *
+ * Promises are objects that represent the future result of an asynchronous operation. When you start such an operation, using #request#$.request(),
+ * ##animate(), or ##wait(), you will get a Promise object that allows you to get the result as soon as the operation is finished.
+ *
+ * Minified's full distribution ships with a Promises/A+-compliant implementation of Promises that should
+ * be able to interoperate with most other Promises implementations. Minified's Web module in stand-alone distribution comes with a limited implementation.
+ * See below for details.
+ *
+ * What may be somewhat surprising about this Promises specification is that the only standard-compliant way to access the result is to
+ * register callbacks. They will be invoked as soon as the operation is finished.
+ * If the operation already ended when you register the callbacks, the callback will then just be called from the event loop as soon
+ * as possible (but never while the ##then() you register them with is still running).
+ * This design forces you to handle the operation result asynchronously and disencourages 'bad' techniques such as polling.
+ *
+ * The central method of a Promise, and indeed the only required function in Promises/A+, is ##then(). It allows you to register
+ * two callback methods, one for success (called 'fulfillment' in Promises/A+ terminology) and one for failures (called 'rejection' in Promises/A+).
+ *
+ * This example shows you how to use then():
+ *
+ *
+ * What makes Promises so special is that ##then() itself returns a new Promise, which is based on the Promise then() was called on, but can be
+ * modified by the outcome of callbacks. Both arguments to then() are optional, and you can also write the code like this:
+ *
+ *
+ * Because the first ##then() returns a new Promise based on the original Promise, the second then() will handle errors of the request just like
+ * the first one did. There is only one subtle difference in the second example: the error handler will not only be called if the request failed,
+ * but also when the request succeded but the success handler threw an exception. That's one of the two differences between the original Promise and
+ * the Promise returned by then(). Any exception thrown in a callback causes the new Promise to be in error state.
+ *
+ * Before I show you the second difference between the original Promise and the new Promise, let me make the example a bit more readable
+ * by using ##error(), which is not part of Promises/A+, but a simple extension by Minified. It just registers the failure callback without
+ * forcing you to specify null as first argument:
+ *
+ *
+ * A very powerful capability of Promises is that you can easily chain them. If a ##then() callback returns a value, the new Promise returned
+ * by then() will be marked as success (fulfilled) and this value is the result of the operation. If a callback returns a Promise,
+ * the new Promise will assume the state of the returned Promise. You can use the latter to create chains of asynchronous operations,
+ * but you still need only a single error handler for all of them and you do not need to nest functions to achieve this:
+ *
+ *
+ * Only the full Minified distribution allows you to create promises yourself, using the ##promise() function. The Promises/A+
+ * specification does not specify how to fulfill a promise, but in Minified's implementation every Promise object has a function fire()
+ * that needs to be called when the promise result is ready. It requires two arguments.
+ * The first is a boolean, true for a successful operation and false for a failure. The second is an array or list containing the
+ * arguments to call the corresponding ##then() handler with.
+ *
+ * The following example is a function, similar to ##wait(), that returns a Promise which succeeds after the given amount
+ * of milliseconds has passed.
+ * It then fulfills the promise with the number of milliseconds as argument.
+ *
+ *
+ * function timeout(durationMs) {
+ * var p = _.promise();
+ * setTimeout(function() { p.fire(true, [durationMs]); }, durationMs);
+ * return p;
+ * }
+ *
+ * If you use only the Web module, instead of the full implementation, the promises implementation is not fully Promises/A+ compliant.
+ * One major difference is that it does not allow you create promises yourself. The only way to get a promise in the Web module
+ * is from functions like ##animate() and ##request(). The other difference is that the interoperability with other promises frameworks
+ * is limited, even though it should be good enough most of the time.
+ *
+ * There are two things you may run into when you use Web's simplified implementation with a complete implementation:
+ *
The simplified implementation does not support recursive thenables. So when you register callbacks with ##then(),
+ * you can return a promise or a thenable, but only if that promise is not also returning a promise.
+ *
Many corner cases required by the Promises/A+ specification are not handled. When interoperating using
+ * reasonable implementations, you may never run into this, but Promises/A+ has detailed rules for things like ##then()
+ * methods implemented as dynamic getter and returning a new value on each invocation or throwing exceptions. If you need
+ * a water-proof implementation, you need to use the complete implementation in Minified's full package.
+ */
+/*$
+ * @stop
+ */
+
+///#/snippet webDocs
+
diff --git a/src/scripts/vendor/minified/minified.min.js b/src/scripts/vendor/minified/minified.min.js
new file mode 100644
index 0000000..584fc53
--- /dev/null
+++ b/src/scripts/vendor/minified/minified.min.js
@@ -0,0 +1,2923 @@
+// minified.js config start -- use this comment to re-create a configuration in the Builder
+// - Only sections add, always, amdsupport, each, error, extend,
+// - filter, find, map, on, ready, request, template, trigger, underscore, wait.
+
+
+// WARNING! This file is autogenerated from minified-master.js and others.
+
+/*
+ * Minified.js - Lightweight Client-Side JavaScript Library (full package)
+ * Version: Version 2014 beta 5 b2
+ *
+ * Public Domain. Use, modify and distribute it any way you like. No attribution required.
+ * To the extent possible under law, Tim Jansen has waived all copyright and related or neighboring rights to Minified.
+ * Please see http://creativecommons.org/publicdomain/zero/1.0/.
+ * NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
+ *
+ * Contains code based on https://github.com/douglascrockford/JSON-js (also Public Domain).
+ *
+ * https://github.com/timjansen/minified.js
+ */
+// ==ClosureCompiler==
+// @output_file_name minified.js
+// @compilation_level ADVANCED_OPTIMIZATIONS
+// ==/ClosureCompiler==
+
+/*$
+ * @id ALL
+ * @doc no
+ * @required
+ * This id allows identifying whether both Web and Util are available.
+ */
+
+///#snippet commonAmdStart
+
+/*$
+ * @id require
+ * @name require()
+ * @syntax require(name)
+ * @group OPTIONS
+ * @module WEB, UTIL
+ * Returns a reference to a module. If you do not use an AMD loader to load Minified, just call require() with the
+ * argument 'minified' to get a reference to Minified. You can also access all modules defined using ##define().
+ *
+ * If you do use an AMD loader, Minified will not define this function and you can use the AMD loader to obtain the
+ * reference to Minified.
+ * Minified's version of require is very simple and will only support Minified and other libraries designed
+ * for Minfied, but no real AMD libraries. If you need to work with libraries requiring AMD, you need a real AMD loader.
+ *
+ * @param name the name of the module to request. Minified is available as 'minified'.
+ * @return the reference to the module. Use the name 'minified' to get Minified. You can also access any modules defined using
+ * ##define(). If the name is unknown, it returns undefined.
+ *
+ * @see ##define() allows you to define modules that can be obtained using require().
+ */
+
+/*$
+ * @id define
+ * @name define()
+ * @syntax define(name, factoryFunction)
+ * @group OPTIONS
+ * @module WEB, UTIL
+ * Defines a module that can be returned by ##require(), in case you don't have a AMD loader. If you have a AMD loader before you include Minified,
+ * define() will not be set and you can use the AMD loader's (more powerful) variant.
+ *
+ * Minified's versions of require() and define() are very simple and can not resolve things like circular references.
+ * Also, they are not AMD-compatible and only useful for simple modules. If you need to work with real AMD libraries that are not written
+ * for Minified, you need a real AMD loader.
+ *
+ * @example Creates a simple module and uses it:
+ *
+ * define('makeGreen', function(require) {
+ * var MINI = require('minified'), $ = MINI.$; // obtain own ref to Minified
+ * return function(list) {
+ * $(list).set({$color: '#0f0', $backgroundColor: '#050'});
+ * });
+ * });
+ *
+ * var makeGreen = require('makeGreen');
+ * makeGreen('.notGreenEnough');
+ *
+ *
+ * @param name the name of the module to request. In Minified's implementation, only 'minified' is supported.
+ * @param factoryFunction is a function(require) will be called the first time the name is defined to obtain the module
+ * reference. It received a reference to ##require() (which is required for AMD backward-compatibility) and
+ * must return the value that is returned by ##require(). The function will only be called once, its result will
+ * be cached.
+ *
require
A reference to ##require(). While you could use require() from the global
+ * context, this would prevent backward compatibility with AMD.
+ *
(callback return value)
The reference to be returned by ##require().
+ *
+ * @see ##require() can be used to obtain references defined with ##define().
+ */
+
+/*$
+ * @id amdsupport
+ * @name AMD stubs
+ * @configurable default
+ * @group OPTIONS
+ * @doc no
+ * @module WEB, UTIL
+ * If enabled, Minified will create stubs so you can use it without an AMD framework.
+ * It requires AMD's define() function.
+ */
+if (/^u/.test(typeof define)) { // no AMD support available ? define a minimal version
+ (function(def){
+ var require = this['require'] = function(name) { return def[name]; };
+ this['define'] = function(name, f) { def[name] = def[name] || f(require); };
+ })({});
+}
+/*$
+ * @stop
+ */
+
+define('minified', function() {
+
+ ///#/snippet commonAmdStart
+ ///#snippet webVars
+ /*$
+ * @id WEB
+ * @doc no
+ * @required
+ * This id allows identifying whether the Web module is available.
+ */
+
+ /**
+ * @const
+ */
+ var _window = window;
+
+ /**
+ * @const
+ * @type {!string}
+ */
+ var MINIFIED_MAGIC_NODEID = 'Nia';
+
+ /**
+ * @const
+ * @type {!string}
+ */
+ var MINIFIED_MAGIC_PREV = 'NiaP';
+
+ var setter = {}, getter = {};
+
+ var idSequence = 1; // used as node id to identify nodes, and as general id for other maps
+
+
+ /*$
+ * @id ready_vars
+ * @dependency
+ */
+ /** @type {!Array.} */
+ var DOMREADY_HANDLER = /^[ic]/.test(document['readyState']) ? _null : []; // check for 'interactive' and 'complete'
+ /*$
+ * @stop
+ */
+
+ ///#/snippet webVars
+ ///#snippet utilVars
+ /*$
+ * @id UTIL
+ * @doc no
+ * @required
+ * This id allows identifying whether the Util module is available.
+ */
+
+ var _null = null;
+
+ /** @const */
+ var undef;
+
+ /*$
+ * @id date_constants
+ * @dependency
+ */
+ function val3(v) {return v.substr(0,3);}
+ var MONTH_LONG_NAMES = split('January,February,March,April,May,June,July,August,September,October,November,December', /,/g);
+ var MONTH_SHORT_NAMES = map(MONTH_LONG_NAMES, val3); // ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
+ var WEEK_LONG_NAMES = split('Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday', /,/g);
+ var WEEK_SHORT_NAMES = map(WEEK_LONG_NAMES, val3);
+ var MERIDIAN_NAMES = split('am,pm', /,/g);
+ var MERIDIAN_NAMES_FULL = split('am,am,am,am,am,am,am,am,am,am,am,am,pm,pm,pm,pm,pm,pm,pm,pm,pm,pm,pm,pm', /,/g);
+
+ var FORMAT_DATE_MAP = {
+ 'y': ['FullYear', nonOp],
+ 'Y': ['FullYear', function(d) { return d % 100; }],
+ 'M': ['Month', plusOne],
+ 'n': ['Month', MONTH_SHORT_NAMES],
+ 'N': ['Month', MONTH_LONG_NAMES],
+ 'd': ['Date', nonOp],
+ 'm': ['Minutes', nonOp],
+ 'H': ['Hours', nonOp],
+ 'h': ['Hours', function(d) { return (d % 12) || 12; }],
+ 'k': ['Hours', plusOne],
+ 'K': ['Hours', function(d) { return d % 12; }],
+ 's': ['Seconds', nonOp],
+ 'S': ['Milliseconds', nonOp],
+ 'a': ['Hours', MERIDIAN_NAMES_FULL],
+ 'w': ['Day', WEEK_SHORT_NAMES],
+ 'W': ['Day', WEEK_LONG_NAMES],
+ 'z': ['TimezoneOffset', function(d, dummy, timezone) {
+ if (timezone)
+ return timezone;
+
+ var sign = d > 0 ? '-' : '+';
+ var off = d < 0 ? -d : d;
+ return sign + pad(2, Math.floor(off/60)) + pad(2, off%60);
+ }]
+ };
+
+ var PARSE_DATE_MAP = {
+ 'y': 0, // placeholder -> ctorIndex
+ 'Y': [0, -2000],
+ 'M': [1,1], // placeholder -> [ctorIndex, offset|value array]
+ 'n': [1, MONTH_SHORT_NAMES],
+ 'N': [1, MONTH_LONG_NAMES],
+ 'd': 2,
+ 'm': 4,
+ 'H': 3,
+ 'h': 3,
+ 'K': [3,1],
+ 'k': [3,1],
+ 's': 5,
+ 'S': 6,
+ 'a': [3, MERIDIAN_NAMES]
+ };
+
+ /*$
+ * @stop
+ */
+
+ /** @const */
+ var MAX_CACHED_TEMPLATES = 99;
+ var templateCache={}; // template -> function
+ var templates = []; // list of MAX_CACHED_TEMPLATES templates
+
+ ///#/snippet utilVars
+ ///#snippet commonFunctions
+
+ /** @param s {?} */
+ function toString(s) {
+ return s!=_null ? ''+s : '';
+ }
+ /**
+ * @param s {?}
+ * @param o {string}
+ */
+ function isType(s,o) {
+ return typeof s == o;
+ }
+ /** @param s {?} */
+ function isString(s) {
+ return isType(s, 'string');
+ }
+ function isObject(f) {
+ return !!f && isType(f, 'object');
+ }
+ function isNode(n) {
+ return n && n['nodeType'];
+ }
+ function isNumber(n) {
+ return isType(n, 'number');
+ }
+ function isDate(n) {
+ return isObject(n) && !!n['getDay'];
+ }
+ function isBool(n) {
+ return n === true || n === false;
+ }
+ function isValue(n) {
+ var type = typeof n;
+ return type == 'object' ? !!(n && n['getDay']) : (type == 'string' || type == 'number' || isBool(n));
+ }
+ function nonOp(v) {
+ return v;
+ }
+ function plusOne(d) {
+ return d+1;
+ }
+ function replace(s, regexp, sub) {
+ return toString(s).replace(regexp, sub != _null ? sub : '');
+ }
+ function escapeRegExp(s) {
+ return replace(s, /[\\\[\]\/{}()*+?.$|^-]/g, "\\$&");
+ }
+ function trim(s) {
+ return replace(s, /^\s+|\s+$/g);
+ }
+ function eachObj(obj, cb, ctx) {
+ for (var n in obj)
+ if (obj.hasOwnProperty(n))
+ cb.call(ctx || obj, n, obj[n]);
+ return obj;
+ }
+ function each(list, cb, ctx) {
+ if (list)
+ for (var i = 0; i < list.length; i++)
+ cb.call(ctx || list, list[i], i);
+ return list;
+ }
+ function filter(list, filterFuncOrObject, ctx) {
+ var r = [];
+ var f = isFunction(filterFuncOrObject) ? filterFuncOrObject : function(value) { return filterFuncOrObject != value; };
+ each(list, function(value, index) {
+ if (f.call(ctx || list, value, index))
+ r.push(value);
+ });
+ return r;
+ }
+ function collector(iterator, obj, collectFunc, ctx) {
+ var result = [];
+ iterator(obj, function (a, b) {
+ if (isList(a = collectFunc.call(ctx || obj, a, b))) // extreme variable reusing: a is now the callback result
+ each(a, function(rr) { result.push(rr); });
+ else if (a != _null)
+ result.push(a);
+ });
+ return result;
+ }
+ function collectObj(obj, collectFunc, ctx) {
+ return collector(eachObj, obj, collectFunc, ctx);
+ }
+ function collect(list, collectFunc, ctx) {
+ return collector(each, list, collectFunc, ctx);
+ }
+ function keyCount(obj) {
+ var c = 0;
+ eachObj(obj, function(key) { c++; });
+ return c;
+ }
+ function keys(obj) { // use Object.keys? in IE>=9
+ var list = [];
+ eachObj(obj, function(key) { list.push(key); });
+ return list;
+ }
+ function map(list, mapFunc, ctx) {
+ var result = [];
+ each(list, function(item, index) {
+ result.push(mapFunc.call(ctx || list, item, index));
+ });
+ return result;
+ }
+ function startsWith(base, start) {
+ if (isList(base)) {
+ var s2 = _(start); // convert start as we don't know whether it is a list yet
+ return equals(sub(base, 0, s2.length), s2);
+ }
+ else
+ return start != _null && base.substr(0, start.length) == start;
+ }
+ function endsWith(base, end) {
+ if (isList(base)) {
+ var e2 = _(end);
+ return equals(sub(base, -e2.length), e2) || !e2.length;
+ }
+ else
+ return end != _null && base.substr(base.length - end.length) == end;
+ }
+ function reverse(list) {
+ var len = list.length;
+ if (isList(list))
+ return new M(map(list, function() { return list[--len]; }));
+ else
+ return replace(list, /[\s\S]/g, function() { return list.charAt(--len); });
+ }
+ function toObject(list, value) {
+ var obj = {};
+ each(list, function(item, index) {
+ obj[item] = value;
+ });
+ return obj;
+ }
+ function copyObj(from, to) {
+ var dest = to || {};
+ for (var name in from)
+ dest[name] = from[name];
+ return dest;
+ }
+ function merge(list, target) {
+ var o = target;
+ for (var i = 0; i < list.length; i++)
+ o = copyObj(list[i], o);
+ return o;
+ }
+ function getFindFunc(findFunc) {
+ return isFunction(findFunc) ? findFunc : function(obj, index) { if (findFunc === obj) return index; };
+ }
+ function getFindIndex(list, index, defaultIndex) {
+ return index == _null ? defaultIndex : index < 0 ? Math.max(list.length+index, 0) : Math.min(list.length, index);
+ }
+ function find(list, findFunc, startIndex, endIndex) {
+ var f = getFindFunc(findFunc);
+ var e = getFindIndex(list, endIndex, list.length);
+ var r;
+ for (var i = getFindIndex(list, startIndex, 0); i < e; i++)
+ if ((r = f.call(list, list[i], i)) != _null)
+ return r;
+ }
+ function findLast(list, findFunc, startIndex, endIndex) {
+ var f = getFindFunc(findFunc);
+ var e = getFindIndex(list, endIndex, -1);
+ var r;
+ for (var i = getFindIndex(list, startIndex, list.length-1); i > e; i--)
+ if ((r = f.call(list, list[i], i)) != _null)
+ return r;
+ }
+ function sub(list, startIndex, endIndex) {
+ var r = [];
+ if (list) {
+ var e = getFindIndex(list, endIndex, list.length);
+ for (var i = getFindIndex(list, startIndex, 0); i < e; i++)
+ r.push(list[i]);
+ }
+ return r;
+ }
+ function array(list) {
+ return map(list, nonOp);
+ }
+ function unite(list) {
+ return function() {
+ return new M(callList(list, arguments));
+ };
+ }
+ function uniq(list) {
+ var found = {};
+ return filter(list, function(item) {
+ if (found[item])
+ return false;
+ else
+ return found[item] = 1;
+ });
+ }
+ function intersection(list, otherList) {
+ var keys = toObject(otherList, 1);
+ return filter(list, function(item) {
+ var r = keys[item];
+ keys[item] = 0;
+ return r;
+ });
+ }
+ function contains(list, value) { // TODO: can Array.indexOf be used in >IE8?
+ for (var i = 0; i < list.length; i++)
+ if (list[i] == value)
+ return true;
+ return false;
+ }
+ // equals if a and b have the same elements and all are equal. Supports getters.
+ function equals(x, y) {
+ var a = isFunction(x) ? x() : x;
+ var b = isFunction(y) ? y() : y;
+ var aKeys;
+ if (a == b)
+ return true;
+ else if (a == _null || b == _null)
+ return false;
+ else if (isValue(a) || isValue(b))
+ return isDate(a) && isDate(b) && +a==+b;
+ else if (isList(a)) {
+ return (a.length == b.length) &&
+ !find(a, function(val, index) {
+ if (!equals(val, b[index]))
+ return true;
+ });
+ }
+ else {
+ return !isList(b) &&
+ ((aKeys = keys(a)).length == keyCount(b)) &&
+ !find(aKeys, function(key) {
+ if (!equals(a[key],b[key]))
+ return true;
+ });
+ }
+ }
+
+ function call(f, fThisOrArgs, args) {
+ if (isFunction(f))
+ return f.apply(args && fThisOrArgs, map(args || fThisOrArgs, nonOp));
+ }
+ function callList(list, fThisOrArgs, args) {
+ return map(list, function(f) { return call(f, fThisOrArgs, args);});
+ }
+ function bind(f, fThis, beforeArgs, afterArgs) {
+ return function() {
+ return call(f, fThis, collect([beforeArgs, arguments, afterArgs], nonOp));
+ };
+ }
+ function partial(f, beforeArgs, afterArgs) {
+ return bind(f, this, beforeArgs, afterArgs);
+ }
+ function pad(digits, number) {
+ var signed = number < 0 ? '-' : '';
+ var preDecimal = (signed?-number:number).toFixed(0);
+ while (preDecimal.length < digits)
+ preDecimal = '0' + preDecimal;
+ return signed + preDecimal;
+ }
+
+ function processNumCharTemplate(tpl, input, fwd) {
+ var inHash;
+ var inputPos = 0;
+ var rInput = fwd ? input : reverse(input);
+ var s = (fwd ? tpl : reverse(tpl)).replace(/./g, function(tplChar) {
+ if (tplChar == '0') {
+ inHash = false;
+ return rInput.charAt(inputPos++) || '0';
+ }
+ else if (tplChar == '#') {
+ inHash = true;
+ return rInput.charAt(inputPos++) || '';
+ }
+ else
+ return inHash && !rInput.charAt(inputPos) ? '' : tplChar;
+ });
+ return fwd ? s : (input.substr(0, input.length - inputPos) + reverse(s));
+ }
+
+ function getTimezone(match, idx, refDate) { // internal helper, see below
+ if (idx == _null || !match)
+ return 0;
+ return parseFloat(match[idx]+match[idx+1])*60 + parseFloat(match[idx]+match[idx+2]) + refDate.getTimezoneOffset();
+ }
+
+ // formats number with format string (e.g. "#.000", "#,#", "00000", "000.00", "000.000.000,00", "000,000,000.##")
+ // choice syntax: :|:|...
+ // e.g. 0:no item|1:one item|>=2:# items
+ // ="null" used to compare with nulls.
+ // choice also works with strings or bools, e.g. ERR:error|WAR:warning|FAT:fatal|ok
+ function formatValue(fmt, value) {
+ var format = replace(fmt, /^\?/);
+ if (isDate(value)) {
+ var timezone, match;
+
+ if (match = /^\[(([+-])(\d\d)(\d\d))\]\s*(.*)/.exec(format)) {
+ timezone = match[1];
+ value = dateAdd(value, 'minutes', getTimezone(match, 2, value));
+ format = match[5];
+ }
+
+ return replace(format, /(\w)(\1*)(?:\[([^\]]+)\])?/g, function(s, placeholderChar, placeholderDigits, params) {
+ var val = FORMAT_DATE_MAP[placeholderChar];
+ if (val) {
+ var d = value['get' + val[0]]();
+ var optionArray = (params && params.split(','));
+
+ if (isList(val[1]))
+ d = (optionArray || val[1])[d];
+ else
+ d = val[1](d, optionArray, timezone);
+ if (d != _null && !isString(d))
+ d = pad(placeholderDigits.length+1, d);
+ return d;
+ }
+ else
+ return s;
+ });
+
+ }
+ else
+ return find(format.split(/\s*\|\s*/), function(fmtPart) {
+ var match, numFmtOrResult;
+ if (match = /^([<>]?)(=?)([^:]*?)\s*:\s*(.*)$/.exec(fmtPart)) {
+ var cmpVal1 = value, cmpVal2 = +(match[3]);
+ if (isNaN(cmpVal2) || !isNumber(cmpVal1)) {
+ cmpVal1 = (cmpVal1==_null) ? "null" : toString(cmpVal1); // not ""+value, because undefined is treated as null here
+ cmpVal2 = match[3];
+ }
+ if (match[1]) {
+ if ((!match[2] && cmpVal1 == cmpVal2 ) ||
+ (match[1] == '<' && cmpVal1 > cmpVal2) ||
+ (match[1] == '>' && cmpVal1 < cmpVal2))
+ return _null;
+ }
+ else if (cmpVal1 != cmpVal2)
+ return _null;
+ numFmtOrResult = match[4];
+ }
+ else
+ numFmtOrResult = fmtPart;
+
+ if (isNumber(value))
+ return numFmtOrResult.replace(/[0#](.*[0#])?/, function(numFmt) {
+ var decimalFmt = /^([^.]+)(\.)([^.]+)$/.exec(numFmt) || /^([^,]+)(,)([^,]+)$/.exec(numFmt);
+ var signed = value < 0 ? '-' : '';
+ var numData = /(\d+)(\.(\d+))?/.exec((signed?-value:value).toFixed(decimalFmt ? decimalFmt[3].length:0));
+ var preDecimalFmt = decimalFmt ? decimalFmt[1] : numFmt;
+ var postDecimal = decimalFmt ? processNumCharTemplate(decimalFmt[3], replace(numData[3], /0+$/), true) : '';
+
+ return (signed ? '-' : '') +
+ (preDecimalFmt == '#' ? numData[1] : processNumCharTemplate(preDecimalFmt, numData[1])) +
+ (postDecimal.length ? decimalFmt[2] : '') +
+ postDecimal;
+ });
+ else
+ return numFmtOrResult;
+ });
+ }
+ // returns date; null if optional and not set; undefined if parsing failed
+ function parseDate(fmt, date) {
+ var indexMap = {}; // contains reGroupPosition -> typeLetter or [typeLetter, value array]
+ var reIndex = 1;
+ var timezoneOffsetMatch;
+ var timezoneIndex;
+ var match;
+
+ var format = replace(fmt, /^\?/);
+ if (format!=fmt && !trim(date))
+ return _null;
+
+ if (match = /^\[([+-])(\d\d)(\d\d)\]\s*(.*)/.exec(format)) {
+ timezoneOffsetMatch = match;
+ format = match[4];
+ }
+
+ var parser = new RegExp(format.replace(/(.)(\1*)(?:\[([^\]]*)\])?/g, function(wholeMatch, placeholderChar, placeholderDigits, param) {
+ if (/[dmhkyhs]/i.test(placeholderChar)) {
+ indexMap[reIndex++] = placeholderChar;
+ var plen = placeholderDigits.length+1;
+ return "(\\d"+(plen<2?"+":("{1,"+plen+"}"))+")";
+ }
+ else if (placeholderChar == 'z') {
+ timezoneIndex = reIndex;
+ reIndex += 3;
+ return "([+-])(\\d\\d)(\\d\\d)";
+ }
+ else if (/[Nna]/.test(placeholderChar)) {
+ indexMap[reIndex++] = [placeholderChar, param && param.split(',')];
+ return "([a-zA-Z\\u0080-\\u1fff]+)";
+ }
+ else if (/w/i.test(placeholderChar))
+ return "[a-zA-Z\\u0080-\\u1fff]+";
+ else if (/\s/.test(placeholderChar))
+ return "\\s+";
+ else
+ return escapeRegExp(wholeMatch);
+ }));
+
+ if (!(match = parser.exec(date)))
+ return undef;
+
+ var ctorArgs = [0, 0, 0, 0, 0, 0, 0];
+ for (var i = 1; i < reIndex; i++) {
+ var matchVal = match[i];
+ var indexEntry = indexMap[i];
+ if (isList(indexEntry)) { // for a, n or N
+ var placeholderChar = indexEntry[0];
+ var mapEntry = PARSE_DATE_MAP[placeholderChar];
+ var ctorIndex = mapEntry[0];
+ var valList = indexEntry[1] || mapEntry[1];
+ var listValue = find(valList, function(v, index) { if (startsWith(matchVal.toLowerCase(), v.toLowerCase())) return index; });
+ if (listValue == _null)
+ return undef;
+ if (placeholderChar == 'a')
+ ctorArgs[ctorIndex] += listValue * 12;
+ else
+ ctorArgs[ctorIndex] = listValue;
+ }
+ else if (indexEntry) { // for numeric values (yHmMs)
+ var value = parseFloat(matchVal);
+ var mapEntry = PARSE_DATE_MAP[indexEntry];
+ if (isList(mapEntry))
+ ctorArgs[mapEntry[0]] += value - mapEntry[1];
+ else
+ ctorArgs[mapEntry] += value;
+ }
+ }
+ var d = new Date(ctorArgs[0], ctorArgs[1], ctorArgs[2], ctorArgs[3], ctorArgs[4], ctorArgs[5], ctorArgs[6]);
+ return dateAdd(d, 'minutes', -getTimezone(timezoneOffsetMatch, 1, d) - getTimezone(match, timezoneIndex, d));
+ }
+ // format ?##00,00##
+ // returns number; null if optional and not set; undefined if parsing failed
+ function parseNumber(fmt, value) {
+ var format = replace(fmt, /^\?/);
+ if (format!=fmt && !trim(value))
+ return _null;
+ var decSep = (/(^|[^0#.,])(,|[0#.]*,[0#]+|[0#]+\.[0#]+\.[0#.,]*)($|[^0#.,])/.test(format)) ? ',' : '.';
+ var r = parseFloat(replace(replace(replace(value, decSep == ',' ? /\./g : /,/g), decSep, '.'), /^[^\d-]*(-?\d)/, '$1'));
+ return isNaN(r) ? undef : r;
+ }
+ function now() {
+ return new Date();
+ }
+ function dateClone(date) {
+ return new Date(+date);
+ }
+ function capWord(w) {
+ return w.charAt(0).toUpperCase() + w.substr(1);
+ }
+ function dateAddInline(d, cProp, value) {
+ d['set'+cProp](d['get'+cProp]() + value);
+ return d;
+ }
+ function dateAdd(date, property, value) {
+ if (value == _null)
+ return dateAdd(now(), date, property);
+ return dateAddInline(dateClone(date), capWord(property), value);
+ }
+ function dateMidnight(date) {
+ var od = date || now();
+ return new Date(od.getFullYear(), od.getMonth(), od.getDate());
+ }
+ function dateDiff(property, date1, date2) {
+ var d1t = +date1;
+ var d2t = +date2;
+ var dt = d2t - d1t;
+ if (dt < 0)
+ return -dateDiff(property, date2, date1);
+
+ var propValues = {'milliseconds': 1, 'seconds': 1000, 'minutes': 60000, 'hours': 3600000};
+ var ft = propValues[property];
+ if (ft)
+ return dt / ft;
+
+ var cProp = capWord(property);
+ var calApproxValues = {'fullYear': 8.64e7*365, 'month': 8.64e7*365/12, 'date': 8.64e7}; // minimum values, a little bit below avg values
+ var minimumResult = Math.floor((dt / calApproxValues[property])-2); // -2 to remove the imperfections caused by the values above
+
+ var d = dateAddInline(new Date(d1t), cProp, minimumResult);
+ for (var i = minimumResult; i < minimumResult*1.2+4; i++) { // try out 20% more than needed, just to be sure
+ if (+dateAddInline(d, cProp, 1) > d2t)
+ return i;
+ }
+ // should never ever be reached
+ }
+
+ function ucode(a) {
+ return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+ }
+
+ function escapeJavaScriptString(s) {
+ return replace(s, /[\x00-\x1f'"\u2028\u2029]/g, ucode);
+ }
+
+ // reimplemented split for IE8
+ function split(str, regexp) {
+
+ return str.split(regexp);
+ }
+
+ function template(template, escapeFunction) {
+ if (templateCache[template])
+ return templateCache[template];
+ else {
+ var funcBody = 'with(_.isObject(obj)?obj:{}){'+
+ map(split(template, /{{|}}}?/g), function(chunk, index) {
+ var match, c1 = trim(chunk), c2 = replace(c1, /^{/), escapeSnippet = (c1==c2) ? 'esc(' : '';
+ if (index%2) { // odd means JS code
+ if (match = /^each\b(\s+([\w_]+(\s*,\s*[\w_]+)?)\s*:)?(.*)/.exec(c2))
+ return 'each('+(trim(match[4])?match[4]:'this')+', function('+match[2]+'){';
+ else if (match = /^if\b(.*)/.exec(c2))
+ return 'if('+match[1]+'){';
+ else if (match = /^else\b\s*(if\b(.*))?/.exec(c2))
+ return '}else ' + (match[1] ? 'if('+match[2] +')' : '')+'{';
+ else if (match = /^\/(if)?/.exec(c2))
+ return match[1] ? '}\n' : '});\n';
+ else if (match = /^(var\s.*)/.exec(c2))
+ return match[1]+';';
+ else if (match = /^#(.*)/.exec(c2))
+ return match[1];
+ else if (match = /(.*)::\s*(.*)/.exec(c2))
+ return 'print('+escapeSnippet+'_.formatValue("'+escapeJavaScriptString(match[2])+'",'+(trim(match[1])?match[1]:'this')+(escapeSnippet&&')')+'));\n';
+ else
+ return 'print('+escapeSnippet+(trim(c2)?c2:'this')+(escapeSnippet&&')')+');\n';
+ }
+ else if (chunk){
+ return 'print("'+escapeJavaScriptString(chunk)+'");\n';
+ }
+ }).join('')+'}';
+ var f = (new Function('obj', 'each', 'esc', 'print', '_', funcBody));
+ var t = function(obj, thisContext) {
+ var result = [];
+ f.call(thisContext || obj, obj, function(obj, func) {
+ if (isList(obj))
+ each(obj, function(value, index) { func.call(value, value, index); });
+ else
+ eachObj(obj, function(key, value) { func.call(value, key, value); });
+ }, escapeFunction || nonOp, function() {call(result['push'], result, arguments);}, _);
+ return result.join('');
+ };
+ if (templates.push(t) > MAX_CACHED_TEMPLATES)
+ delete templateCache[templates.shift()];
+ return templateCache[template] = t;
+ }
+ }
+
+ function escapeHtml(s) {
+ return replace(s, /[<>'"&]/g, function(s) {
+ return ''+s.charCodeAt(0)+';';
+ });
+ }
+
+ function formatHtml(tpl, obj) {
+ return template(tpl, escapeHtml)(obj);
+ }
+
+ function listBindArray(func) {
+ return function(arg1, arg2) {
+ return new M(func(this, arg1, arg2));
+ };
+ }
+ function listBind(func) {
+ return function(arg1, arg2, arg3) {
+ return func(this, arg1, arg2, arg3);
+ };
+ }
+ function funcArrayBind(func) {
+ return function(arg1, arg2, arg3) {
+ return new M(func(arg1, arg2, arg3));
+ };
+ }
+
+ ///#/snippet commonFunctions
+ ///#snippet webFunctions
+
+ // note: only the web version has the f.item check
+ function isFunction(f) {
+ return typeof f == 'function' && !f['item']; // item check as work-around for webkit bug 14547
+ }
+
+ function isList(v) {
+ return v && v.length != _null && !isString(v) && !isNode(v) && !isFunction(v) && v !== _window;
+ }
+
+ // used by IE impl of on() only
+ function push(obj, prop, value) {
+ (obj[prop] = (obj[prop] || [])).push(value);
+ }
+ // used by IE impl of on()/off() only
+ function removeFromArray(array, value) {
+ for (var i = 0; array && i < array.length; i++)
+ if (array[i] === value)
+ array['splice'](i--, 1);
+ }
+
+ function extractNumber(v) {
+ return parseFloat(replace(v, /^[^\d-]+/));
+ }
+
+ // retrieves the node id of the element, create one if needed.
+ function getNodeId(el) {
+ return (el[MINIFIED_MAGIC_NODEID] = (el[MINIFIED_MAGIC_NODEID] || ++idSequence));
+ }
+
+ // collect variant that filters out duplicate nodes from the given list, returns a new array
+ function collectUniqNodes(list, func) {
+ var result = [];
+ var nodeIds = {};
+ var currentNodeId;
+
+ flexiEach(list, function(value) {
+ flexiEach(func(value), function(node) {
+ if (!nodeIds[currentNodeId = getNodeId(node)]) {
+ result.push(node);
+ nodeIds[currentNodeId] = true;
+ }
+ });
+ });
+ return result;
+ }
+
+ // finds out the 'natural' height of the first element, the one if $$slide=1
+ function getNaturalHeight(elementList, factor) {
+ var q = {'$position': 'absolute', '$visibility': 'hidden', '$display': 'block', '$height': _null};
+ var oldStyles = elementList['get'](q);
+ var h = elementList['set'](q)['get']('clientHeight');
+ elementList['set'](oldStyles);
+ return h*factor + 'px';
+ }
+
+
+
+
+ // @condblock !ie8compatibility
+ function on(subSelector, eventSpec, handler, args, bubbleSelector) {
+ if (isFunction(eventSpec))
+ return this['on'](_null, subSelector, eventSpec, handler, args);
+ else if (isString(args))
+ return this['on'](subSelector, eventSpec, handler, _null, args);
+ else
+ return this['each'](function(baseElement, index) {
+ flexiEach(subSelector ? dollarRaw(subSelector, baseElement) : baseElement, function(registeredOn) {
+ flexiEach(toString(eventSpec).split(/\s/), function(namePrefixed) {
+ var name = replace(namePrefixed, /[?|]/g);
+ var prefix = replace(namePrefixed, /[^?|]/g);
+ var capture = (name == 'blur' || name == 'focus') && !!bubbleSelector; // bubble selectors for 'blur' and 'focus' registered as capuring!
+ var triggerId = idSequence++;
+
+ // returns true if processing should be continued
+ function triggerHandler(eventName, event, target) {
+ var match = !bubbleSelector;
+ var el = bubbleSelector ? target : registeredOn;
+ if (bubbleSelector) {
+ var selectorFilter = getFilterFunc(bubbleSelector, registeredOn);
+ while (el && el != registeredOn && !(match = selectorFilter(el)))
+ el = el['parentNode'];
+ }
+ return (!match) || (name != eventName) || ((handler.apply($(el), args || [event, index]) && prefix=='?') || prefix == '|');
+ };
+
+ function eventHandler(event) {
+ if (!triggerHandler(name, event, event['target'])) {
+ event['preventDefault']();
+ event['stopPropagation']();
+ }
+ };
+
+ registeredOn.addEventListener(name, eventHandler, capture);
+
+ if (!registeredOn['M'])
+ registeredOn['M'] = {};
+ registeredOn['M'][triggerId] = triggerHandler; // to be called by trigger()
+
+ handler['M'] = collector(flexiEach, [handler['M'], function () { // this function will be called by off()
+ registeredOn.removeEventListener(name, eventHandler, capture);
+ delete registeredOn['M'][triggerId];
+ }], nonOp);
+
+ });
+ });
+ });
+ }
+ // @condend !ie8compatibility
+
+
+ // @condblock !ie8compatibility
+ function off(handler) {
+ callList(handler['M']);
+ handler['M'] = _null;
+ }
+ // @condend !ie8compatibility
+
+ // for remove & window.unload, IE only
+ function detachHandlerList(dummy, handlerList) {
+ flexiEach(handlerList, function(h) {
+ h.element.detachEvent('on'+h.eventType, h.handlerFunc);
+ });
+ }
+
+ function ready(handler) {
+ if (DOMREADY_HANDLER)
+ DOMREADY_HANDLER.push(handler);
+ else
+ setTimeout(handler, 0);
+ }
+
+ function $$(selector, context, childrenOnly) {
+ return dollarRaw(selector, context, childrenOnly)[0];
+ }
+
+ function EE(elementName, attributes, children) {
+ var e = $(document.createElement(elementName));
+ // @condblock UTIL
+ // this attributes != null check is only required with Util's isObject() implementation. Web's isObject() is simpler.
+ return (isList(attributes) || (attributes != _null && !isObject(attributes)) ) ? e['add'](attributes) : e['set'](attributes)['add'](children);
+ // @condend UTIL
+ // @cond !UTIL return (isList(attributes) || (!isObject(attributes)) ) ? e['add'](attributes) : e['set'](attributes)['add'](children);
+ }
+
+ function clone(listOrNode) {
+ return collector(flexiEach, listOrNode, function(e) {
+ var c;
+ if (isList(e))
+ return clone(e);
+ else if (isNode(e)) {
+ c = e['cloneNode'](true);
+ c['removeAttribute'] && c['removeAttribute']('id');
+ return c;
+ }
+ else
+ return e;
+ });
+ }
+
+ /*$
+ * @stop
+ */
+
+ function $(selector, context, childOnly) {
+ // @condblock ready
+ return isFunction(selector) ? ready(selector) : new M(dollarRaw(selector, context, childOnly));
+ // @condend
+ // @cond !ready return new M(dollarRaw(selector, context));
+ }
+
+ // implementation of $ that does not produce a Minified list, but just an array
+
+
+
+
+
+
+
+
+
+
+ // @condblock !ie7compatibility
+ function dollarRaw(selector, context, childOnly) {
+ function flatten(a) { // flatten list, keep non-lists, remove nulls
+ return isList(a) ? collector(flexiEach, a, flatten) : a;
+ }
+ function filterElements(list) { // converts into array, makes sure context is respected
+ return filter(collector(flexiEach, list, flatten), function(node) {
+ var a = node;
+ while (a = a['parentNode'])
+ if (a == context[0] || childOnly)
+ return a == context[0];
+ // fall through to return undef
+ });
+ }
+
+ if (context) {
+ if ((context = dollarRaw(context)).length != 1)
+ return collectUniqNodes(context, function(ci) { return dollarRaw(selector, ci, childOnly);});
+ else if (isString(selector)) {
+ if (isNode(context[0]) != 1)
+ return [];
+ else
+ return childOnly ? filterElements(context[0].querySelectorAll(selector)) : context[0].querySelectorAll(selector);
+ }
+ else
+ return filterElements(selector);
+
+ }
+ else if (isString(selector))
+ return document.querySelectorAll(selector);
+ else
+ return collector(flexiEach, selector, flatten);
+ };
+ // @condend !ie7compatibility
+
+ // If context is set, live updates will be possible.
+ // Please note that the context is not evaluated for the '*' and 'tagname.classname' patterns, because context is used only
+ // by on(), and in on() only nodes in the right context will be checked
+ function getFilterFunc(selector, context) {
+ function wordRegExpTester(name, prop) {
+ var re = RegExp('(^|\\s+)' + name + '(?=$|\\s)', 'i');
+ return function(obj) {return name ? re.test(obj[prop]) : true;};
+ }
+
+ var nodeSet = {};
+ var dotPos = nodeSet;
+ if (isFunction(selector))
+ return selector;
+ else if (isNumber(selector))
+ return function(v, index) { return index == selector; };
+ else if (!selector || selector == '*' ||
+ (isString(selector) && (dotPos = /^([\w-]*)\.?([\w-]*)$/.exec(selector)))) {
+ var nodeNameFilter = wordRegExpTester(dotPos[1], 'tagName');
+ var classNameFilter = wordRegExpTester(dotPos[2], 'className');
+ return function(v) {
+ return isNode(v) == 1 && nodeNameFilter(v) && classNameFilter(v);
+ };
+ }
+ else if (context)
+ return function(v) {
+ return $(selector, context)['find'](v)!=_null; // live search instead of node set, for on()
+ };
+ else {
+ $(selector)['each'](function(node) {
+ nodeSet[getNodeId(node)] = true;
+ });
+ return function(v) {
+ return nodeSet[getNodeId(v)];
+ };
+ }
+ }
+
+ function getInverseFilterFunc(selector) {
+ var f = getFilterFunc(selector);
+ return function(v) {return f(v) ? _null : true;};
+ }
+ ///#/snippet webFunctions
+
+ ///#snippet extrasFunctions
+ function flexiEach(list, cb) {
+ if (isList(list))
+ each(list, cb);
+ else if (list != _null)
+ cb(list, 0);
+ return list;
+ }
+
+ function Promise() {
+ this['state'] = null;
+ this['values'] = [];
+ this['parent'] = null;
+ }
+
+ /*$
+ * @id promise
+ * @name _.promise()
+ * @syntax _.promise()
+ * @syntax _.promise(otherPromises...)
+ * @module WEB+UTIL
+ *
+ * Creates a new ##promiseClass#Promise##, optionally assimilating other promises. If no other promise is given,
+ * a fresh new promise is returned.
+ *
+ * The returned promise provides the methods ##fulfill() and ##reject() that can be called directly to change the promise's state,
+ * as well as the more powerful ##fire().
+ *
+ * If one promise is given as parameter, the new promise assimilates the given promise as-is, and just forwards
+ * fulfillment and rejection with the original values.
+ *
+ * If more than one promise are given, it will assimilate all of them with slightly different rules:
+ *
the new promise is fulfilled if all assimilated promises have been fulfilled. The fulfillment values
+ * of all assimilated promises are given to the handler as arguments. Note that the fulfillment values themselves are always
+ * arrays, as a promise can have several fulfillment values in Minified's implementation.
+ *
when one of the promises is rejected, the new promise is rejected immediately. The rejection handler gets the
+ * promises rejection value (first argument if it got several) as first argument, an array of the result values
+ * of all promises as a second (that means one array of arguments for each promise), and the index of the failed
+ * promise as third.
+ *
+ *
+ * @example A simple promise that is fulfilled after 1 second, using Minified's invocation syntax:
+ *
var p = _.promise();
+ * setTimeout(function() {
+ * p.fire(true);
+ * }, 1000);
+ *
+ *
+ * @example Request three files in parallel. When all three have been downloaded, concatenate them into a single string.
+ *
+ *
+ * @param otherPromises one or more promises to assimilate (varargs). You can also pass lists of promises.
+ * @return the new promise.
+ */
+ function promise() {
+ var deferred = []; // this function calls the functions supplied by then()
+
+ var assimilatedPromises = arguments;
+ var assimilatedNum = assimilatedPromises.length;
+ var numCompleted = 0; // number of completed, assimilated promises
+ var rejectionHandlerNum = 0;
+
+ var obj = new Promise();
+
+ obj['errHandled'] = function() {
+ rejectionHandlerNum++;
+ if (obj['parent'])
+ obj['parent']['errHandled']();
+ };
+
+ /*$
+ * @id fire
+ * @name promise.fire()
+ * @syntax _.fire(newState)
+ * @syntax _.fire(newState, values)
+ * @module WEB+UTIL
+ *
+ * Changes the state of the promise into either fulfilled or rejected. This will also notify all ##then() handlers. If the promise
+ * already has a state, the call will be ignored.
+ *
+ * fire() can be invoked as a function without context ('this'). Every promise has its own instance.
+ *
+ * @example A simple promise that is fulfilled after 1 second, using Minified's invocation syntax:
+ *
var p = _.promise();
+ * setTimeout(function() {
+ * p.fire(true, []);
+ * }, 1000);
+ *
+ *
+ * @example Call fire() without a context:
+ *
var p = _.promise(function(resolve, reject) {
+ * setTimeout(resolve.fire, 1000);
+ * });
+ *
+ *
+ * @param newState true to set the Promise to fulfilled, false to set the state as rejected. If you pass null or
+ * undefined, the promise's state does not change.
+ * @param values optional an array of values to pass to ##then() handlers as arguments. You can also pass a non-list argument, which will then
+ * be passed as only argument.
+ * @return the promise
+ */
+ var fire = obj['fire'] = function(newState, newValues) {
+ if (obj['state'] == null && newState != null) {
+ obj['state'] = !!newState;
+ obj['values'] = isList(newValues) ? newValues : [newValues];
+ setTimeout(function() {
+ each(deferred, function(f) {f();});
+ }, 0);
+ }
+ return obj;
+ };
+
+ // use promise varargs
+ each(assimilatedPromises, function assimilate(promise, index) {
+ try {
+ if (promise['then'])
+ promise['then'](function(v) {
+ var then;
+ if ((isObject(v) || isFunction(v)) && isFunction(then = v['then']))
+ assimilate(v, index);
+ else {
+ obj['values'][index] = array(arguments);
+ if (++numCompleted == assimilatedNum)
+ fire(true, assimilatedNum < 2 ? obj['values'][index] : obj['values']);
+ }
+ },
+ function(e) {
+ obj['values'][index] = array(arguments);
+ fire(false, assimilatedNum < 2 ? obj['values'][index] : [obj['values'][index][0], obj['values'], index]);
+ });
+ else
+ promise(function() {fire(true, array(arguments));}, function() {fire(false, array(arguments)); });
+ }
+ catch (e) {
+ fire(false, [e, obj['values'], index]);
+ }
+ });
+
+ /*$
+ * @id stop
+ * @name promise.stop()
+ * @syntax promise.stop()
+ * @module WEB+UTIL
+ * Stops an ongoing operation, if supported. Currently the only promises supporting this are those returned by ##request(), ##animate(), ##wait() and
+ * ##asyncEach().
+ * stop() invocation will be propagated over promises returned by ##then() and promises assimilated by ##promise(). You only need to invoke stop
+ * with the last promise, and all dependent promises will automatically stop as well.
+ *
+ * stop() can be invoked as a function without context ('this'). Every promise has its own instance.
+ *
+ * @return In some cases, the stop() can return a value. This is currently only done by ##animate() and ##wait(), which will return the actual duration.
+ * ##asyncEach()'s promise will also return any value it got from the promise that it stopped.
+ *
+ * @example Animation chain that can be stopped.
+ *
+ */
+ obj['stop'] = function() {
+ each(assimilatedPromises, function(promise) {
+ if (promise['stop'])
+ promise['stop']();
+ });
+
+ return obj['stop0'] && call(obj['stop0']);
+ };
+
+ /*$
+ * @id then
+ * @name promise.then()
+ * @syntax promise.then()
+ * @syntax promise.then(onSuccess)
+ * @syntax promise.then(onSuccess, onError)
+ *
+ * @module WEB
+ * Registers two callbacks that will be invoked when the ##promise#Promise##'s asynchronous operation finished
+ * successfully (onSuccess) or an error occurred (onError). The callbacks will be called after
+ * then() returned, from the browser's event loop.
+ * You can chain then() invocations, as then() returns another Promise object that you can attach to.
+ *
+ * The full distribution of Minified implements the Promises/A+ specification, allowing interoperability with other Promises frameworks.
+ *
+ * Note: If you use the Web module, you will get a simplified Promises implementation that cuts some corners. The most notable
+ * difference is that when a then() handler throws an exception, this will not be caught and the promise returned by
+ * then will not be automatically rejected.
+ *
+ * @example Simple handler for an HTTP request. Handles only success and ignores errors.
+ *
+ *
+ * @param onSuccess optional a callback function to be called when the operation has been completed successfully. The exact arguments it receives depend on the operation.
+ * If the function returns a ##promise#Promise##, that Promise will be evaluated to determine the state of the promise returned by then(). If it returns any other value, the
+ * returned Promise will also succeed. If the function throws an error, the returned Promise will be in error state.
+ * Pass null or undefined if you do not need the success handler.
+ * @param onError optional a callback function to be called when the operation failed. The exact arguments it receives depend on the operation. If the function returns a ##promise#Promise##, that promise will
+ * be evaluated to determine the state of the Promise returned by then(). If it returns anything else, the returned Promise will
+ * have success status. If the function throws an error, the returned Promise will be in the error state.
+ * You can pass null or undefined if you do not need the error handler.
+ * @return a new ##promise#Promise## object. If you specified a callback for success or error, the new Promises's state will be determined by that callback if it is called.
+ * If no callback has been provided and the original Promise changes to that state, the new Promise will change to that state as well.
+ */
+ var then = obj['then'] = function (onFulfilled, onRejected) {
+ var promise2 = promise();
+ var callCallbacks = function() {
+ try {
+ var f = (obj['state'] ? onFulfilled : onRejected);
+ if (isFunction(f)) {
+ (function resolve(x) {
+ try {
+ var then, cbCalled = 0;
+ if ((isObject(x) || isFunction(x)) && isFunction(then = x['then'])) {
+ if (x === promise2)
+ throw new TypeError();
+ then.call(x, function(x) { if (!cbCalled++) resolve(x); }, function(value) { if (!cbCalled++) promise2['fire'](false, [value]);});
+ promise2['stop0'] = x['stop'];
+ }
+ else
+ promise2['fire'](true, [x]);
+ }
+ catch(e) {
+ if (!(cbCalled++)) {
+ promise2['fire'](false, [e]);
+ if (!rejectionHandlerNum)
+ throw e;
+ }
+ }
+ })(call(f, undef, obj['values']));
+ }
+ else
+ promise2['fire'](obj['state'], obj['values']);
+ }
+ catch (e) {
+ promise2['fire'](false, [e]);
+ if (!rejectionHandlerNum)
+ throw e;
+ }
+ };
+ if (isFunction(onRejected))
+ obj['errHandled']();
+ promise2['stop0'] = obj['stop'];
+ promise2['parent'] = obj;
+ if (obj['state'] != null)
+ setTimeout(callCallbacks, 0);
+ else
+ deferred.push(callCallbacks);
+ return promise2;
+ };
+
+ /*$
+ * @id always
+ * @group REQUEST
+ * @name promise.always()
+ * @syntax promise.always(callback)
+ * @configurable default
+ * @module WEB+UTIL
+ * Registers a callback that will always be called when the ##promise#Promise##'s operation ended, no matter whether the operation succeeded or not.
+ * This is a convenience function that will call ##then() with the same function for both arguments. It shares all of its semantics.
+ *
+ * @example Simple handler for a HTTP request.
+ *
+ *
+ * @param callback a function to be called when the operation has been finished, no matter what its result was. The exact arguments depend on the operation and may
+ * vary depending on whether it succeeded or not. If the function returns a ##promise#Promise##, that Promise will
+ * be evaluated to determine the state of the returned Promise. If provided and it returns regularly, the returned promise will
+ * have success status. If it throws an error, the returned Promise will be in the error state.
+ * @return a new ##promise#Promise## object. Its state is determined by the callback.
+ */
+ obj['always'] = function(func) { return then(func, func); };
+
+ /*$
+ * @id error
+ * @group REQUEST
+ * @name promise.error()
+ * @syntax promise.error(callback)
+ * @configurable default
+ * @module WEB, UTIL
+ * Registers a callback that will be called when the operation failed.
+ * This is a convenience function that will invoke ##then() with only the second argument set. It shares all of its semantics.
+ *
+ * @example Simple handler for a HTTP request.
+ *
+ *
+ * @param callback a function to be called when the operation has failed. The exact arguments depend on the operation. If the function returns a ##promise#Promise##, that Promise will
+ * be evaluated to determine the state of the returned Promise. If it returns regularly, the returned Promise will
+ * have success status. If it throws an error, the returned Promise will be in error state.
+ * @return a new ##promise#Promise## object. Its state is determined by the callback.
+ */
+ obj['error'] = function(func) { return then(0, func); };
+
+ return obj;
+ }
+
+ ///#/snippet extrasFunctions
+ ///#snippet extrasDocs
+ /*$
+ * @id length
+ * @group SELECTORS
+ * @requires dollar
+ * @name list.length
+ * @syntax length
+ * @module WEB, UTIL
+ *
+ * Contains the number of elements in the ##list#Minified list##.
+ *
+ * @example With Web module:
+ *
+ * var list = $('input');
+ * var myValues = {};
+ * for (var i = 0; i < list.length; i++)
+ * myValues[list[i].name] = list[i].value;
+ *
+ *
+ * @example With Util module:
+ *
+ * var list = _(1, 2, 3);
+ * var sum = 0;
+ * for (var i = 0; i < list.length; i++)
+ * sum += list[i];
+ *
+ */
+ /*$
+ * @stop
+ */
+ ///#/snippet extrasDocs
+
+ ///#snippet utilM
+
+ /*
+ * syntax: M(list, assimilateSublists)
+ * M(null, singleElement)
+ *
+ *
+ */
+ /** @constructor */
+ function M(list, assimilateSublists) {
+ var self = this, idx = 0;
+ if (list)
+ for (var i = 0, len = list.length; i < len; i++) {
+ var item = list[i];
+ if (assimilateSublists && isList(item))
+ for (var j = 0, len2 = item.length; j < len2; j++)
+ self[idx++] = item[j];
+ else
+ self[idx++] = item;
+ }
+ else
+ self[idx++] = assimilateSublists;
+
+ self['length'] = idx;
+ self['_'] = true;
+ }
+
+ function _() {
+ return new M(arguments, true);
+ }
+
+ ///#/snippet utilM
+
+ //// LIST FUNCTIONS ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ copyObj({
+ ///#snippet utilListFuncs
+ /*$
+ * @id each
+ * @group LIST
+ * @requires
+ * @configurable default
+ * @name .each()
+ * @altname _.each()
+ * @syntax list.each(callback)
+ * @syntax list.each(callback, ctx)
+ * @syntax _.each(list, callback)
+ * @syntax _.each(list, callback, ctx)
+ * @module UTIL, WEB
+ * Invokes the given function once for each item in the list. The function will be called with the item as first parameter and
+ * the zero-based index as second. Unlike JavaScript's built-in forEach() it will be invoked for each item in the list,
+ * even if it is undefined.
+ *
+ * @example Creates the sum of all list entries.
+ *
+ * var sum = 0;
+ * _(17, 4, 22).each(function(item, index) {
+ * sum += item;
+ * });
+ *
+ *
+ * @example The previous example with a native array:
+ *
+ * var sum = 0;
+ * _.each([17, 4, 22], function(item, index) {
+ * sum += item;
+ * });
+ *
+ *
+ * @example This goes through all h2 elements of the class 'section' on a web page and changes their content:
+ *
+ *
+ * @param list a list to iterate. Can be an array, a ##list#Minified list## or any other array-like structure with
+ * length property.
+ * @param callback The callback function(item, index) to invoke for each list element.
+ *
item
The current list element.
+ *
index
The second the zero-based index of the current element.
+ *
this
The given context if not null. Otherwise the list.
+ * The callback's return value will be ignored.
+ * @param ctx optional a context to pass to the callback as 'this'. Only supported in UTIL module.
+ * @return the list
+ *
+ * @see ##per() works like each(), but wraps the list elements in a list.
+ * @see ##find() can be used instead of each() if you need to abort the loop.
+ * @see ##eachObj() iterates through the properties of an object.
+ */
+ 'each': listBind(each),
+
+ /*$
+ * @id filter
+ * @group LIST
+ * @requires
+ * @configurable default
+ * @name .filter()
+ * @altname _.filter()
+ * @syntax list.filter(filterFunc)
+ * @syntax list.filter(filterFunc, ctx)
+ * @syntax list.filter(value)
+ * @syntax _.filter(list, filterFunc)
+ * @syntax _.filter(list, filterFunc, ctx)
+ * @syntax _.filter(list, value)
+ * @module WEB, UTIL
+ * Creates a new ##list#Minified list## by taking an existing list and omitting certain elements from it. You
+ * can either specify a callback function to approve those items that will be in the new list (all modules), or
+ * you can pass a value to remove from the new list (Util module only).
+ *
+ * If the callback function returns true, the item is shallow-copied in the new list, otherwise it will be removed.
+ * For values, a simple equality operation (==) will be used.
+ *
+ * @example Removing all instances of the number 10 from a list:
+ *
+ * var list = _([4, 10, 22, 7, 2, 19, 10]).filter(10);
+ *
+ *
+ * @example Removing all numbers over 10 from a list:
+ *
+ *
+ * @param list a list to filter. A list to use as input. Can be an array, a ##list#Minified list## or any other array-like structure with
+ * length property.
+ * @param filterFunc The filter callback function(item, index) that decides which elements to include:
+ *
item
The current list element.
+ *
index
The second the zero-based index of the current element.
+ *
this
The given context if not null. Otherwise the list.
+ *
(callback return value)
true to include the item in the new list, false to omit it.
+ * @param ctx optional a context to pass to the callback as 'this'. Only supported in UTIL module.
+ * @param value a value to remove from the list. It will be determined which elements to remove using ==. Must not
+ * be a function. Requires Util module.
+ * @return the new, filtered ##list#list##
+ *
+ * @see ##only() offers selector-based filtering.
+ */
+ 'filter': listBindArray(filter),
+
+ /*$
+ * @id map
+ * @group LIST
+ * @requires
+ * @configurable default
+ * @name .map()
+ * @altname _.map()
+ * @syntax list.map(mapFunc)
+ * @syntax list.map(mapFunc, ctx)
+ * @syntax _.map(list, mapFunc)
+ * @syntax _.map(list, mapFunc, ctx)
+ * @module UTIL
+ * Creates a new ##list#Minified list## from the current list using the given callback function.
+ * The callback is invoked once for each element of the current list. The callback results will be added to the result list.
+ *
+ * map() is a simpler version of ##collect(). Unlike collect(), it always creates lists of the same size as the input list, but
+ * it is easier to use if the resulting list should contain nulls or nested list.
+ *
+ * @example Goes through a list of numbers and creates a new list with each value increased by 1:
+ *
+ *
+ * @param list a list to transform. Can be an array, a ##list#Minified list## or any other array-like structure with
+ * length property.
+ * @param mapFunc The callback function(item, index) to invoke for each item:
+ *
item
The current list element.
index
The second the zero-based index of the current element.
+ *
this
The given context if not null. Otherwise the list.
+ *
(callback return value)
This value will replace the original value in the new list.
+ * @param ctx optional a context to pass to the callback as 'this'.
+ * @return the new ##list#list##
+ *
+ * @see ##collect() is a more powerful version of map().
+ */
+ 'map': listBindArray(map),
+
+ /*$
+ * @id find
+ * @group LIST
+ * @requires
+ * @configurable default
+ * @name .find()
+ * @altname _.find()
+ * @syntax list.find(findFunc)
+ * @syntax list.find(element)
+ * @syntax list.find(findFunc, startIndex)
+ * @syntax list.find(element, startIndex)
+ * @syntax _.find(list, findFunc)
+ * @syntax _.find(list, element)
+ * @syntax _.find(list, findFunc, startIndex)
+ * @syntax _.find(list, element, startIndex)
+ * @module WEB, UTIL
+ * Finds a specific value in the list. There are two ways of calling find():
+ *
+ *
With a value as argument. Then find() will search for the first occurrence of an identical value in the list,
+ * using the '===' operator for comparisons, and return the index. If it is not found,
+ * find() returns undefined.
+ *
With a callback function. find() will then call the given function for each list element until the function
+ * returns a value that is not null or undefined. This value will be returned.
+ *
+ *
+ * find() can also be used as an alternative to ##each() if you need to abort the loop.
+ *
+ * @example Finds the first negative number in the list:
+ *
+ * var i = _(1, 2, -4, 5, 2, -1).find(function(value, index) { if (value < 0) return index; }); // returns 2
+ *
+
+ * @example Finds the index of the first 5 in the array:
+ *
+ * var i = _.find([3, 6, 7, 6, 5, 4, 5], 5); // returns 4 (index of first 5)
+ *
+ *
+ * @example Determines the position of the element with the id '#wanted' among all li elements:
+ *
+ * var elementIndex = $('li').find($$('#wanted'));
+ *
+ *
+ * @example Goes through the elements to find the first div that has the class 'myClass', and returns this element:
+ *
+ * var myClassElement = $('div').find(function(e) { if ($(e).is('.myClass')) return e; });
+ *
+ *
+ * @param list A list to use as input. Can be an array, a ##list#Minified list## or any other array-like structure with
+ * length property.
+ * @param findFunc The callback function(item, index) that will be invoked for every list item until it returns a non-null value:
+ *
item
The current list element.
index
The second the zero-based index of the current element.
+ *
this
This list.
+ *
(callback return value)
If the callback returns something other than null or
+ * undefined, find() will return it directly. Otherwise it will continue.
+ * @param element the element to search for
+ * @param startIndex optional the 0-based index of the first element to search.
+ * @return if called with an element, either the element's index in the list or undefined if not found. If called with a callback function,
+ * it returns either the value returned by the callback or undefined.
+ *
+ * @see ##findLast() is the equivalent to find() for the list's end.
+ */
+ 'find': listBind(find),
+
+ /*$
+ * @stop
+ */
+ dummySort:0
+ ,
+ ///#/snippet utilListFuncs
+ ///#snippet webListFuncs
+
+ /*$
+ * @id add
+ * @group ELEMENT
+ * @requires dollar each
+ * @configurable default
+ * @name .add()
+ * @syntax list.add(text)
+ * @syntax list.add(node)
+ * @syntax list.add(list)
+ * @syntax list.add(factoryFunction)
+ * @module WEB
+ * Adds the given node(s) as children to the list's HTML elements. If a string has been given, it will be added as text node.
+ * DOM nodes will be added directly. If you pass a list, all its elements will be added using the rules above.
+ *
+ * When you pass a DOM node and the target list has more than one element, the original node will be added to the first list element,
+ * and ##clone#clones## to all following list elements.
+ *
+ * ##EE(), ##HTML() and ##clone() are compatible with add() and can help you create new HTML nodes.
+ *
+ * @example Using the following HTML:
+ *
+ * <div id="comments">Here is some text.<br/></div>
+ *
+ * The next line appends a text node to the div:
+ *
+ *
+ * @param text a string or number to add as text node
+ * @param node a DOM node to add to the list. If the list has more than one element, the given node will be added to the first element.
+ * For all additional elements, the node will be cloned using ##clone().
+ * @param list a list containing text and/or nodes. May also contain nested lists with nodes or text..
+ * @param factoryFunction a function(listItem, listIndex) that will be invoked for each list element to create the nodes:
+ *
listItem
The list element that will receive the new children.
+ *
listIndex
The index of the list element that will receive the new children.
+ *
(callback return value)
The node(s) to be added to the list element.
+ * Can be either a string for a text node, an HTML element or a list containing strings and/or DOM nodes.
+ * If a function is returned, it will be invoked recursively with the same arguments.
+ * @return the current list
+ *
+ * @see ##fill() works like add(), but deletes all children before adding the new nodes.
+ * @see ##addFront() adds nodes as first child, not as last.
+ * @see ##addAfter() adds nodes not as children but as siblings.
+ * @see ##addBefore() also adds nodes not as children but as siblings.
+ * @see ##replace() replaces existing nodes.
+ */
+ 'add': function (children, addFunction) {
+ return this['each'](function(e, index) {
+ var lastAdded;
+ function appendChildren(c) {
+ if (isList(c))
+ flexiEach(c, appendChildren);
+ else if (isFunction(c))
+ appendChildren(c(e, index));
+ else if (c != _null) { // must check null, as 0 is a valid parameter
+ var n = isNode(c) ? c : document.createTextNode(c);
+ if (lastAdded)
+ lastAdded['parentNode']['insertBefore'](n, lastAdded['nextSibling']);
+ else if (addFunction)
+ addFunction(n, e, e['parentNode']);
+ else
+ e.appendChild(n);
+ lastAdded = n;
+ }
+ }
+ appendChildren(index &&!isFunction(children) ? clone(children) : children);
+ });
+ },
+
+ /*$
+ * @id on
+ * @group EVENTS
+ * @requires dollar each
+ * @configurable default
+ * @name .on()
+ * @syntax list.on(names, eventHandler)
+ * @syntax list.on(selector, names, eventHandler)
+ * @syntax list.on(names, customFunc, args)
+ * @syntax list.on(selector, names, customFunc, args)
+ * @syntax list.on(names, eventHandler, bubbleSelector)
+ * @syntax list.on(names, customFunc, args, bubbleSelector)
+ * @module WEB
+ * Registers the function as event handler for all items in the list.
+ *
+ * By default, Minified cancels event propagation and disables element's default behavior for all elements that have an event handler.
+ * You can override this, either by prefixing the event name with a '|', or by prefixing them with '?' and returning a true
+ * in the handler. Both will reinstate the original JavaScript behavior.
+ *
+ * Handlers are called with the original event object as first argument, the index of the source element in the
+ * list as second argument and 'this' set to the source element of the event (e.g. the button that has been clicked).
+ *
+ * Instead of the event objects, you can also pass an array of arguments that will be passed instead of event object and index.
+ *
+ * Optionally you can specify two a selector strings to qualify only certain events. The first one is a selector
+ * that allows you to select only specific children of the list elements. This is mostly useful for adding events to DOM trees
+ * generated using ##HTML() or ##EE().
+ *
+ * The second type of selector is the bubble selector that allows you to receive only events that bubbled up from
+ * elements matching the selector. The selector is executed in the context of the element you registered on to identify whether the
+ * original target of the event qualifies. If not, the handler is not called.
+ *
+ * Minified always registers event handlers with event bubbling enabled. Event capture is not supported.
+ *
+ * Event handlers can be unregistered using #off#$.off().
+ *
+ * @example Adds a handler to all divs which paints the div background color to red when clicked.
+ *
+ * $('div').on('click', function() {
+ * this.style.backgroundColor = 'red'; // 'this' contains the element that caused the event
+ * });
+ *
+ *
+ * @example Registers a handler to call a method setStatus('running') using an inline function:
+ *
+ * Please note that bubble selectors will even listen to events for
+ * table rows that have been added after you registered for the events.
+ *
+ * @param selector optional a selector string for ##dollar#$()## to register the event only on those children of the list elements that
+ * match the selector.
+ * Supports all valid parameters for $() except functions.
+ * @param names the space-separated names of the events to register for, e.g. 'click'. Case-sensitive. The 'on' prefix in front of
+ * the name must not used. You can register the handler for more than one event by specifying several
+ * space-separated event names. If the name is prefixed
+ * with '|' (pipe), the event will be passed through and the event's default actions will be executed by the browser.
+ * If the name is prefixed with '?', the event will only be passed through if the handler returns true.
+ * @param eventHandler the callback function(event, index) to invoke when the event has been triggered:
+ *
+ *
event
The original DOM event object.
+ *
index
The index of the target object in the ##list#Minified list## .
+ *
this
A ##list#Minified list## containing the target element as only item (same as event.target).
+ *
(callback return value)
The return value will only be used if the event name prefix was '?'.
+ * Then, a return value false will stop all further processing of the event and disable event bubbling.
+ * true will keep the event alive.
+ *
+ * @param customFunc a function to be called instead of a regular event handler with the arguments given in args.
+ * 'this' will be a ##list#Minified list## containing the target element as only item (same element as event.target).
+ * @param args optional an array of arguments to pass to the custom callback function instead of the event objects. If omitted, the function is
+ * called as event handler with the event object as argument.
+ * @param bubbleSelector optional a selector string for ##dollar#$()## to receive only events that bubbled up from an
+ * element that matches this selector.
+ * Supports all valid parameters for $() except functions. Analog to ##is(),
+ * the selector is optimized for the simple patterns '.classname', 'tagname' and 'tagname.classname'.
+ * @return the list
+ * @see ##off() allows you to unregister an event handler.
+ * @see ##onClick() as a shortcut for 'click' events.
+ * @see ##onOver() to simplify mouseover/mouseout events.
+ * @see ##onFocus() as convenient way to register for focus events.
+ * @see ##onChange() to get notified when an input's content changes.
+ */
+ 'on': on,
+
+ /*$
+ * @id trigger
+ * @group EVENTS
+ * @requires on each
+ * @configurable default
+ * @name .trigger()
+ * @syntax list.trigger(name)
+ * @syntax list.trigger(name, eventObject)
+ * @module WEB
+ *
+ * Triggers event handlers registered with ##on().
+ * Any event that has been previously registered using ##on() can be invoked with trigger(). Please note that
+ * it will not simulate the default behavior on the elements, such as a form submit when you click on a submit button. Event bubbling
+ * is supported, thus unless there's an event handler that cancels the event, the event will be triggered on all parent elements.
+ *
+ *
+ * @example Simulates a 'click' event on the button.
+ *
+ * $('#myButton').trigger('click');
+ *
+ *
+ * @param name a single event name to trigger
+ * @param eventObj optional an object to pass to the event handler, provided the handler does not have custom arguments.
+ * Anything you pass here will be directly given to event handlers as event object, so you need to know what
+ * they expect.
+ * @return the list
+ * @see ##on() registers events that can be triggered.
+ */
+ 'trigger': function (eventName, eventObj) {
+ return this['each'](function(element, index) {
+ var bubbleOn = true, el = element;
+ while(el && bubbleOn) {
+ eachObj(el['M'], function(id, f) {
+ bubbleOn = bubbleOn && f(eventName, eventObj, element);
+ });
+ el = el['parentNode'];
+ }
+ });
+ }
+
+ /*$
+ * @stop
+ */
+ // @cond !trigger dummyTrigger:0
+ ,
+ ///#/snippet webListFuncs
+ ///#snippet extrasListFuncs
+
+ /*$
+ * @stop
+ */
+ dummyHt:0
+ ///#/snippet extrasListFuncs
+ }, M.prototype);
+
+ //// DOLLAR FUNCTIONS ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ copyObj({
+ ///#snippet webDollarFuncs
+ /*$
+ * @id request
+ * @group REQUEST
+ * @requires
+ * @configurable default
+ * @name $.request()
+ * @syntax $.request(method, url)
+ * @syntax $.request(method, url, data)
+ * @syntax $.request(method, url, data, settings)
+ * @module WEB
+ * Initiates a HTTP request to the given URL, using XMLHttpRequest. It returns a ##promiseClass#Promise## object that allows you to obtain the result.
+ *
+ * @example Invokes a REST web service and parses the resulting document using JSON:
+ *
+ * $.request('get', 'http://service.example.com/weather', {zipcode: 90210})
+ * .then(function(txt) {
+ * var json = $.parseJSON(txt);
+ * $('#weatherResult').fill('Today's forecast is is: ' + json.today.forecast);
+ * })
+ * .error(function(status, statusText, responseText) {
+ * $('#weatherResult').fill('The weather service was not available.');
+ * });
+ *
+ *
+ * @example Sending a JSON object to a REST web service:
+ *
+ *
+ *
+ * @param method the HTTP method, e.g. 'get', 'post' or 'head' (rule of thumb: use 'post' for requests that change data
+ * on the server, and 'get' to request data). Not case sensitive.
+ * @param url the server URL to request. May be a relative URL (relative to the document) or an absolute URL. Note that unless you do something
+ * fancy on the server (keyword to google: Access-Control-Allow-Origin), you can only call URLs on the server your script originates from.
+ * @param data optional data to send in the request, either as POST body or as URL parameters. It can be either a plain object as map of
+ * parameters (for all HTTP methods), a string (for all HTTP methods), a DOM document ('post' only) or a FormData object ('post' only).
+ * If the method is 'post', it will be sent as body, otherwise parameters are appended to the URL. In order to send several parameters with the
+ * same name, use an array of values in the map. Use null as value for a parameter without value.
+ * @param settings optional a map of additional parameters. Supports the following properties (all optional):
+ *
headers
a map of HTTP headers to add to the request. Note that you should use the proper capitalization for the
+ * header 'Content-Type', if you set it, because otherwise it may be overwritten.
+ *
xhr
a map of properties to set in the XMLHttpRequest object before the request is sent, for example {withCredentials: true}.
+ *
user
username for HTTP authentication, together with the pass parameter
+ *
pass
password for HTTP authentication, together with the user parameter
+ *
+ * @return a ##promiseClass#Promise## containing the request's status. If the request has successfully completed with a HTTP status 2xx,
+ * the promise's completion handler will be called as function(text, xhr):
+ *
text
The response sent by the server as text.
+ *
xhr
The XMLHttpRequest used for the request. This allows you to retrieve the response in different
+ * formats (e.g. responseXml for an XML document), to retrieve headers and more.
+ * The failure handler will be called as function(statusCode, statusText, text):
+ *
statusCode
The HTTP status (never 200; 0 if no HTTP request took place).
+ *
text
The response's body text, if there was any, or the exception as string if the browser threw one.
+ *
xhr
The XMLHttpRequest used for the request. This allows you to retrieve the response in different
+ * formats (e.g. responseXml for an XML document), to retrieve headers and more..
+ * The returned promise supports ##stop(). Calling stop() will invoke the XHR's abort() method.
+ * The underlying XmlHttpRequest can also be obtained from the promise's xhr property.
+ *
+ * @see ##values() serializes an HTML form in a format ready to be sent by $.request.
+ * @see ##$.parseJSON() can be used to parse JSON responses.
+ * @see ##$.toJSON() can create JSON messages.
+ * @see ##_.format() can be useful for creating REST-like URLs, if you use JavaScript's built-in escape() function.
+ */
+ 'request': function (method, url, data, settings0) {
+ var settings = settings0 || {};
+ var xhr, callbackCalled = 0, prom = promise(), dataIsMap = data && (data['constructor'] == settings['constructor']);
+ try {
+ prom['xhr'] = xhr = new XMLHttpRequest();
+
+ prom['stop0'] = function() { xhr['abort'](); };
+ // @condend
+
+ if (dataIsMap) { // if data is parameter map...
+ data = collector(eachObj, data, function processParam(paramName, paramValue) {
+ return collector(flexiEach, paramValue, function(v) {
+ return encodeURIComponent(paramName) + ((v != _null) ? '=' + encodeURIComponent(v) : '');
+ });
+ }).join('&');
+ }
+
+ if (data != _null && !/post/i.test(method)) {
+ url += '?' + data;
+ data = _null;
+ }
+
+ xhr['open'](method, url, true, settings['user'], settings['pass']);
+ if (dataIsMap && /post/i.test(method))
+ xhr['setRequestHeader']('Content-Type', 'application/x-www-form-urlencoded');
+
+ eachObj(settings['headers'], function(hdrName, hdrValue) {
+ xhr['setRequestHeader'](hdrName, hdrValue);
+ });
+ eachObj(settings['xhr'], function(name, value) {
+ xhr[name] = value;
+ });
+
+ xhr['onreadystatechange'] = function() {
+ if (xhr['readyState'] == 4 && !callbackCalled++) {
+ if (xhr['status'] >= 200 && xhr['status'] < 300)
+ prom['fire'](true, [xhr['responseText'], xhr]);
+ else
+ prom['fire'](false, [xhr['status'], xhr['responseText'], xhr]);
+ }
+ };
+
+ xhr['send'](data);
+ }
+ catch (e) {
+ if (!callbackCalled)
+ prom['fire'](false, [0, _null, toString(e)]);
+ }
+
+ return prom;
+ },
+
+ /*
+ * JSON Module. Uses browser built-ins or json.org implementation if available. Otherwise its own implementation,
+ * originally based on public domain implementation http://www.JSON.org/json2.js / http://www.JSON.org/js.html.
+ * Extremely simplified code, made variables local, removed all side-effects (especially new properties for String, Date and Number).
+ */
+
+ /*$
+ * @id ready
+ * @group EVENTS
+ * @requires ready_vars ready_init
+ * @configurable default
+ * @name $.ready()
+ * @syntax $.ready(handler)
+ * @module WEB
+ * Registers a handler to be called as soon as the HTML has been fully loaded in the browser. Does not necessarily wait for images and other elements,
+ * only the main HTML document needs to be complete. On older browsers it is the same as window.onload.
+ *
+ * If you call ready() after the page is completed, the handler is scheduled for invocation in the event loop as soon as possible.
+ *
+ * A shortcut for ready() is to call ##dollar#$()## with the handler function. It does the same with fewer characters.
+ *
+ * @example Registers a handler that sets some text in an element:
+ *
+ *
+ * @param handler the function() to be called when the HTML is ready.
+ * @see ##dollar#$()## calls ready() when invoked with a function, offering a more convenient syntax.
+ */
+ 'ready': ready,
+
+ /*$
+ * @stop
+ */
+ dummyOff:null
+ ,
+ ///#/snippet webDollarFuncs
+ ///#snippet extrasDollarFuncs
+
+ /*$
+ * @id wait
+ * @group EVENTS
+ * @configurable default
+ * @requires promise
+ * @name $.wait()
+ * @syntax $.wait()
+ * @syntax $.wait(durationMs)
+ * @syntax $.wait(durationMs, args)
+ * @module WEB+UTIL
+ *
+ * Creates a new ##promise#Promise## that will be fulfilled as soon as the specified number of milliseconds have passed. This is mainly useful for animation,
+ * because it allows you to chain delays into your animation chain.
+ *
+ * The operation can be interrupted by calling the promise's ##stop() function.
+ *
+ * @example Chained animation using Promise callbacks. The element is first moved to the position 200/0, then to 200/200, waits for 50ms
+ * and finally moves to 100/100.
+ *
+ *
+ *
+ * @param durationMs optional the number of milliseconds to wait. If omitted, the promise will be fulfilled as soon as the browser can run it
+ * from the event loop.
+ * @param args optional an array or list of arguments to pass to the promise handler
+ * @return a ##promise#Promise## object that will be fulfilled when the time is over, or fail when the promise's ##stop() has been called.
+ * The promise argument of a fulfilled promise is the args parameter as given to wait(). The returned promise supports ##stop()
+ * to interrupt the promise.
+ */
+ 'wait': function(durationMs, args) {
+ var p = promise();
+ var id = setTimeout(function() {
+ p['fire'](true, args);
+ }, durationMs);
+ p['stop0'] = function() { p['fire'](false); clearTimeout(id); };
+ return p;
+ }
+
+ /*$
+ * @stop
+ */
+ // @cond !wait dummyWait:0
+
+ ///#/snippet extrasDollarFuncs
+ }, $);
+
+ //// UNDERSCORE FUNCTIONS ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ copyObj({
+ ///#snippet utilUnderscoreFuncs
+ // @condblock filter
+ 'filter': funcArrayBind(filter),
+ // @condend
+ // @condblock map
+ 'map': funcArrayBind(map),
+ // @condend
+ // @condblock each
+ 'each': each,
+ // @condend
+ // @condblock each
+ 'toObject': toObject,
+ // @condend
+ // @condblock find
+ 'find': find,
+ // @condend
+
+ /*$
+ * @id extend
+ * @group OBJECT
+ * @requires
+ * @configurable default
+ * @name _.extend()
+ * @syntax _.extend(target, src...)
+ * @module UTIL
+ * Copies every property of the source objects into the first object. The source objects are specified using variable arguments.
+ * There can be more than one.
+ * The properties are copied as shallow-copies.
+ *
+ * Please note: Unlike jQuery, extend does not directly add a function to extend Minified, although
+ * you can use it to for this. To add a function to ##list#Minified lists##, add a property to
+ * ##M#MINI.M##. If you want to extend $ or _, just assign the new function(s) as property.
+ *
+ * @example Copying properties:
+ *
var target = {a:3, c: 3};
+ * _.extend(target, {a: 1, b: 2}); // target is now {a: 1, b: 2, c: 3}
+ *
+ * @example Using several source values:
+ *
var extend = _.extend({a: 1, b: 2}, {a:3, c: 3}, {d: 5}); // target is now {a: 1, b: 2, c: 3, d: 5}
+ *
+ * @param target the object to copy to
+ * @param src the object(s) to copy from. Variable argument, there can be any number of sources. Nulls and undefined
+ * parameters will be ignored.
+ * @return the target
+ *
+ * @see ##_.copyObj() is very similar to extend(), but with a slightly different and more straightforward syntax.
+ * @see ##_.merge() copies a list of objects into a new object.
+ */
+ 'extend': function(target) {
+ return merge(sub(arguments, 1), target);
+ },
+
+ /*$
+ * @id template
+ * @group FORMAT
+ * @requires date_constants
+ * @configurable default
+ * @name _.template()
+ * @syntax _.template(template)
+ * @syntax _.template(template, escapeFunction)
+ * @module UTIL
+ * Parses a Handlebars-like template to create a reusable template function.
+ *
+ * The syntax of the template uses a syntax that superficially looks like
+ * Handlebars. Unlike Handlebars, it is based on raw JavaScript expressions and thus gives you
+ * complete freedom, but also offers you shortcuts for formatting, iteration and conditionals.
+ *
+ * Every template can receive exactly one object as input. If you need more than one value as input, put all required values
+ * into an object.
+ *
+ * Use double curly braces to embed a JavaScript expression and insert its result:
+ *
{{a}} plus {{b}} is {{a+b}}
+ *
+ * To use such a template, create it with template() and then execute the resulting function:
+ *
var myTemplate = _.template('{{a}} plus {{b}} is {{a+b}}');
+ * var result = myTemplate({a: 5, b: 7});
+ * If you pass an object as input, its properties will be mapped using JavaScript's with
+ * statement and are available as variables throughout the template.
+ *
+ * If you have only a simple value to render, you can pass it directly and access it through the pre-defined
+ * variable obj:
+ *
var myTemplate = _.template('The result is {{obj}}.');
+ * var result = myTemplate(17);
+ * Alternatively, you could also access the input as this, but be aware that JavaScript wraps simples types
+ * such as Number and Boolean. this is the default, so you can omit it to get the same result:
+ *
var myTemplate = _.template('The result is {{ }}.');
+ * var result = myTemplate(17);
+ *
+ * Minified templates can use ##_.formatValue() formats directly. Just separate them from the expression by
+ * a double-colon:
+ *
The price is {{obj::#.00}}.
+ *
+ * Conditions can be expressed using if and else:
+ *
Hello {{if visits==0}}New{{else if visits<10}}Returning{{else}}Regular{{/if}} Customer.
+ * You can use any JavaScript expression as condition.
+ *
+ * Use each to iterate through a list:
+ *
var myTemplate = _.template(
+ * '{{each names}}{{this.firstName}} {{this.lastName}}{{/each}}');
+ * var result = myTemplate({names: [{firstName: 'Joe', lastName: 'Jones'},
+ * {firstName: 'Marc', lastName: 'Meyer'}]});
+ * each will iterate through the members of the given object. It
+ * calls its body for each item and put a reference to the item into this.
+ * Optionally, you can specify up to two variables to store the value in and
+ * the zero-based index of the current item:
+ *
+ *
+ * If you do not pass an expression to each, it will take the list from this:
+ *
var myTemplate = _.template('{{each value:}}{{value}};{{/each}}');
+ * var result = myTemplate([1, 2, 3]);
+ *
+ * Beside lists, you can also iterate through the properties of an object. The property name will be stored
+ * in the first given parameter and the value in this and the second parameter:
+ *
var myTemplate = _.template('{{each key, value: nicknames}}{{key}}: {{value}}{{/each}}');
+ * var result = myTemplate({nicknames: {Matt: 'Matthew', John: 'Jonathan'} });
+ *
+ * Shorter version of the previous example that uses this for the value:
+ *
var myTemplate = _.template('{{each key: nicknames}}{{key}}: {{this}}{{/each}}');
+ *
+ * If you do not need the key, you can omit the variable specification:
+ *
var myTemplate = _.template('{{each nicknames}}{{this}}{{/each}}');
+ *
+ * You can define your own variables, using the regular JavaScript syntax, with 'var':
+ *
var myTemplate = _.template('{{var s=very.long.name, sum=a+b;}}{{s.desc}}, {{sum}}');
+ *
+ * In some situations, it may be inevitable to embed raw JavaScript in the template.
+ * To embed JavaScript code, prefix the code with a '#':
+ *
var myTemplate = _.template(
+ * '{{each}}{{#var sum = 0; for (var i = 0; i < 3; i++) sum += this.numbers[i]; }}{{sum}}{{/each}}');
+ * var result = myTemplate([['Foreword', 'Intro'], ['Something', 'Something else']]);
+ *
+ *
+ * By default, all output will be escaped. You can prevent this by using triple-curly-braces:
+ *
Here's the original: {{{rawText}}}
.
+ *
+ * The template's JavaScript code is executed in a sandbox without access to global variables. Minified defines the
+ * following variables for you:
+ *
+ *
Name
Desciption
+ *
this
The template object outside of each. Inside eachs, the current value.
+ *
obj
The parameter given to the template function.
+ *
_
A reference to Minified Util.
+ *
esc
The escape function given when the template has been defined. If no function has been given,
+ * a default function that returns the input unmodified.
+ *
print
A function(text,...) that appends one or more strings to the template result.
+ *
each
A function(listOrObject, eachCallback) that can iterate over lists or object properties.
+ * The eachCallback is a function(key, value) for objects or function(value, index)
+ * for arrays that will be invoked for each item.
+ *
+ *
+ * Every template you create is already cached, so it not an expensive operation to call ##_.template() a second
+ * time with the same template. However, because of caching, you should be careful when creating templates
+ * dynamically, as this will fill the cache up quickly.
+ *
+ * @param template The template as a string using the syntax described below.
+ * @param escapeFunction optional The callback function(inputString) that will be used
+ * to escape all output:
+ *
inputString
The string to escape.
+ *
(callback return value)
The escaped string.
+ * If no escapeFunction has been given, the output will not be escaped.
+ * ##_.escapeHtml() can be used as an escape function for HTML, and ##_.escapeRegExp() for regular expressions.
+ * JavaScript's built-in escape() function can escape URL components.
+ * @return the value returned by the last invocation of func
+ *
+ * @see ##_.format() shares template()'s syntax but returns the result directly.
+ * @see ##_.formatHtml() is a variant of format() with HTML escaping.
+ * @see ##_.escapeHtml() can be used by template() to escape HTML.
+ * @see ##_.escapeRegExp() can be used by template() to escape regular expressions.
+ * @see ##HTML() creates a HTML element tree from a template.
+ */
+ 'template': template,
+
+ /*$
+ * @stop
+ */
+
+ dummyFormatHtml:0
+ ,
+
+ ///#/snippet utilUnderscoreFuncs
+ ///#snippet extrasUnderscoreFuncs
+ // @condblock promise
+ 'promise': promise
+ // @condend promise
+
+ /*$
+ * @stop
+ */
+ // @cond !promise dummyPromise:0
+
+ ///#/snippet extrasUnderscoreFuncs
+ }, _);
+
+ ////INITIALIZATION ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ ///#snippet webInit
+ /*$
+ * @id ready_init
+ * @dependency
+ */
+ document.addEventListener("DOMContentLoaded", function() {
+ callList(DOMREADY_HANDLER);
+ DOMREADY_HANDLER = _null;
+ }, false);
+ /*$
+ @stop
+ */
+
+
+ ///#/snippet webInit
+
+ return {
+ ///#snippet extrasExports
+
+ /*$
+ * @stop
+ */
+
+ ///#/snippet extrasExports
+ ///#snippet utilExports
+ /*$
+ * @id underscore
+ * @group LIST
+ * @name _()
+ * @syntax _(item...)
+ * @configurable default
+ * @module UTIL
+ * Creates a new Minified list. Supports variable arguments so you can add items directly to the list. For arguments that are lists
+ * (as defined by ##_.isList()), the list content will be added to the new list. Unlike #dollar#$()#, this is not done recursively
+ * and thus you can create a list of lists by wrapping arguments in a list. Another difference between _() and $()
+ * is that $() will automatically remove null values while _() will keep them.
+ *
+ * @example Creating an empty list:
+ *
_()
+ *
+ * @example Creating a list with three items:
+ *
_(1, 2, 3)
+ *
+ * @example Creating the same list, but by passing an array. One array level will be flattened:
+ *
_([1, 2, 3])
+ *
+ * @example Creating a list containing the arrays [1, 2] and [3, 4].
+ *
_([[1, 2], [3, 4]])
+ *
+ * @example Merging two lists:
+ *
var a = _("a", "b", "c");
+ * var b = _("x", "y", "z");
+ * var merged = _(a, b); // contains _("a", "b", "c", "x", "y", "z")
+ *
+ *
+ * @example Adding two elements to a list:
+ *
var a = _(1, 2, 3);
+ * var a4 = _(a, 4); // contains _(1, 2, 3, 4)
+ *
+ *
+ * @example Mixing different list types and single elements:
+ *
_(1, [], [2, 3], _(), _(4, 5)); // same content as _(1, 2, 3, 4, 5)
+ *
+ * @param item an item to add to the new list. If it is a list (as defined by ##_.isList()), its content will be to the new
+ * ##Minified list#list## (but NOT recursively).
+ */
+ '_': _,
+ /*$
+ * @stop
+ */
+ ///#/snippet utilExports
+ ///#snippet webExports
+
+ /*$
+ * @id dollar
+ * @group SELECTORS
+ * @requires
+ * @dependency yes
+ * @name $()
+ * @syntax $()
+ * @syntax $(selector)
+ * @syntax $(selector, context)
+ * @syntax $(selector, context, childOnly)
+ * @syntax $(list)
+ * @syntax $(list, context)
+ * @syntax $(list, context, childOnly)
+ * @syntax $(object)
+ * @syntax $(object, context)
+ * @syntax $(object, context, childOnly)
+ * @syntax $(domreadyFunction)
+ * @module WEB
+ * Creates a new ##list#Minified list##, or register a DOMReady-handler.
+ * The most common usage is with a CSS-like selector. $() will then create a list containing all elements of the current HTML
+ * document that fulfill the filter conditions. Alternatively you can also specify a list of objects or a single object.
+ * Nested lists will automatically be flattened, and nulls will automatically be removed from the resulting list.
+ * If you call $() without any arguments, it will return an empty list.
+ *
+ * Additionally, you can specify a second argument to provide a context. Contexts only make sense if you selected
+ * HTML nodes with the first parameter. Then the context limits the resulting list to include only those nodes
+ * that are descendants of the context nodes. The context can be either a selector, a list or a single HTML node, and will be
+ * processed like the first argument. A third arguments allows you to limit the list to
+ * only those elements that are direct children of the context nodes (so a child of a child would be filtered out).
+ *
+ * The lists created by $() are the same type as the ##list#Minified lists## created by Util's #underscore#_() constructor and other
+ * Util methods. All Util methods work on lists created by $(). If you want to add your own methods to those lists,
+ * use ##M#MINI.M##.
+ *
+ * As a special shortcut, if you pass a function to $(), it will be registered using #ready#$.ready() to be executed
+ * when the DOM model is complete.
+ *
+ * @example A simple selector to find an element by id.
+ *
+ * var l0 = $('#myElementId');
+ *
+ *
+ * @example You can pass an object reference to create a list containing only this element:
+ *
+ * var l1 = $(document.getElementById('myElementId'));
+ *
+ *
+ * @example Lists and arrays will be copied:
+ *
+ * var l2 = $([elementA, elementB, elementC]);
+ *
+ *
+ * @example Lists will be automatically flattened and nulls removed. So this list l3 has the same content as l2:
+ *
+ *
+ * @example This is a simple selector to find all elements with the given class.
+ *
+ * var l4 = $('.myClass');
+ *
+ *
+ * @example A selector to find all elements of the given type.
+ *
+ * var l5 = $('input'); // finds all input elements
+ *
+ *
+ * @example A selector to find all elements with the given type and class.
+ *
+ * var l6 = $('input.myRadio'); // finds all input elements with class 'myRadio'
+ *
+ *
+ * @example A selector to find all elements that are descendants of the given element.
+ *
+ * var l7 = $('#myForm input'); // finds all input elements contained in the element myForm
+ *
+ *
+ * @example A selector to find all elements that have either a CSS class 'a' or class 'b':
+ *
+ * var l8 = $('.a, .b'); // finds all elements that have class a or class b
+ *
+ *
+ * @example A selector that finds all elements that are descendants of the element myDivision, are inside an element with the
+ * class .myForm and are input elements:
+ *
+ * var l9 = $('#myDivision .myForm input');
+ *
+ *
+ * @example Contexts can make it easier to specify ancestors:
+ *
+ *
+ * @example Using one of the list functions, ##set(), on the list, and setting the element's text color. '$' at the beginning of the property name sets a CSS value.
+ *
+ * $('#myElementId').set('$color', 'red');
+ *
+ *
+ * @example Most list methods return the list you invoked them on, allowing you to chain them:
+ *
+ *
+ * @example Using $() as a #ready#$.ready() shortcut:
+ *
+ * $(function() {
+ * // in here you can safely work with the HTML document
+ * });
+ *
+ *
+ * @param selector a simple, CSS-like selector for HTML elements. It supports '#id' (lookup by id), '.class' (lookup by class),
+ * 'element' (lookup by elements) and 'element.class' (combined class and element). Use commas to combine several selectors.
+ * You can also join two or more selectors by space to find elements which are descendants of the previous selectors.
+ * For example, use 'div' to find all div elements, '.header' to find all elements containing a class name called 'header', and
+ * 'a.popup' for all a elements with the class 'popup'. To find all elements with 'header' or 'footer' class names,
+ * write '.header, .footer'. To find all divs elements below the element with the id 'main', use '#main div'.
+ * The selector "*" will return all elements.
+ * @param list a list to copy. It can be an array, another Minified list, a DOM nodelist or anything else that has a length property and
+ * allows read access by index. A shallow copy of the list will be returned. Nulls will be automatically removed from the copy. Nested lists
+ * will be flattened, so the result only contains nodes.
+ * @param object an object to create a single-element list containing only the object. If the argument is null, an empty list will be returned.
+ * @param domreadyFunction a function to be registered using #ready#$.ready().
+ * @param context optional an optional selector, node or list of nodes which specifies one or more common ancestor nodes for the selection. The context can be specified as
+ * a selector, a list or using a single object, just like the first argument.
+ * The returned list will contain only descendants of the context nodes. All others will be filtered out.
+ * @param childOnly optional if set, only direct children of the context nodes are included in the list. Children of children will be filtered out. If omitted or not
+ * true, all descendants of the context will be included.
+ * @return the array-like ##list#Minified list## object containing the content specified by the selector.
+ * Please note that if the first argument was a list, the existing order will be kept. If the first argument was a simple selector, the nodes are in document order.
+ * If you combined several selectors using commas, only the individual results of the selectors will keep the document order,
+ * but will then be joined to form a single list. This list will
+ * not be in document order anymore, unless you use a build without legacy IE support.
+ * Duplicate nodes will be removed from selectors, but not from lists.
+ *
+ * @see #underscore#_() is Util's alternative constructor for ##list#Minified lists##
+ * @see ##dollardollar#$$()## works like $(), but returns the resulting list's first element.
+ */
+ '$': $,
+
+ /*$
+ * @id M
+ * @name M
+ * @syntax MINI.M
+ * @module WEB, UTIL
+ *
+ * Exposes the internal class used by all ##list#Minified lists##. This is mainly intended to allow you adding your
+ * own functions.
+ *
+ * @example Adding a function printLength() to M:
+ *
+ */
+ 'M': M,
+
+ /*$
+ * @id getter
+ * @requires get
+ * @name MINI.getter
+ * @syntax MINI.getter
+ * @module WEB
+ *
+ * Exposes a map of prefix handlers used by ##get(). You can add support for a new prefix in get()
+ * by adding a function to this map. The prefix can be any string consisting solely of non-alphanumeric characters
+ * that's not already used by Minified.
+ *
+ * You must not replace getters by a new map, but must always modify the existing map.
+ *
+ * The function's signature is function(list, name) where
+ *
list
Is the Minified list to get the value from. By convention you should always use only the first element. The list is
+ * non-empty and the first elememt can't be null or undefined (get() automatically returns undefined in
+ * all other case).
+ *
name
The name of the property. That's the part AFTER the prefix.
+ *
(callback return value)
The value to return to the user.
+ *
+ * @example Adding a shortcut '||' for accessing border style properties:
+ *
+ * MINI.getter['||'] = function(list, name) {
+ * return list.get('$border' + name.replace(/^[a-z]/, function(a) { return a.toUpperCase()});
+ * };
+ *
+ * var borderColor = $('#box').get('||color'); // same as '$borderColor'
+ * var borderLeftRadius = $('#box').get('||leftRadius'); // same as '$borderLeftRadius'
+ *
+ *
+ * @example Adding XLink attribute support to get(). This is useful if you work with SVG. The prefix is '>'.
+ *
+ */
+ 'getter': getter,
+
+ /*$
+ * @id setter
+ * @requires set
+ * @name MINI.setter
+ * @syntax MINI.setter
+ * @module WEB
+ *
+ * Exposes a map of prefix handlers used by ##set(). You can add support for a new prefix in set()
+ * by adding a function to this map. The prefix can be any string consisting solely of non-alphanumeric characters
+ * that's not already used by Minified.
+ *
+ * You must not replace setters by a new map, but must always modify the existing map.
+ *
+ * The function's signature is function(list, name, value) where
+ *
list
Is the Minified list to use.
+ *
name
The name of the property. That's the part AFTER the prefix.
+ *
value
Either the value to set, or a callback function to create the value that you must call for each
+ * value (see ##set() ).
+ *
+ *
+ * If you provide complete ##get() and ##set() support for a prefix, you are also able to use it in other Minified
+ * function such as ##animate() and ##toggle().
+ *
+ * @example Adding a shortcut '||' for accessing border style properties. As it's just calling ##set() for an existing
+ * property, it is not required to extra code for the callback.
+ *
+ */
+ 'setter': setter
+ /*$
+ * @stop
+ */
+ ///#/snippet webExports
+ };
+
+ ///#snippet commonAmdEnd
+});
+///#/snippet commonAmdEnd
+///#snippet webDocs
+
+/*$
+ * @id list
+ * @name Minified Lists
+ * @module WEB, UTIL
+ *
+ * Minified lists are Array-like objects provided by Minified. Like a regular JavaScript array,
+ * they provide a length property and you can access their content using the index operator (a[5]).
+ * However, they do not provide the same methods as JavaScript's native array and are designed to be immutable, so
+ * there is no direct way to add something to a Minified list. Instead Minified provides a number of functions and methods
+ * that take a list and create a modified copy which, for example, may contain additional elements.
+ *
+ * Minified lists are typically created either using the Web module's #dollar#$() function or with the Util module's
+ * #underscore#_() function, but many functions in the Util module also return a Minified list.
+ *
+ * The Util module provides a function ##_.array() that converts a Minified list to a regular JavaScript array.
+ */
+
+/*$
+ * @id promiseClass
+ * @name Promise
+ * @module WEB, UTIL
+ *
+ * Promises are objects that represent the future result of an asynchronous operation. When you start such an operation, using #request#$.request(),
+ * ##animate(), or ##wait(), you will get a Promise object that allows you to get the result as soon as the operation is finished.
+ *
+ * Minified's full distribution ships with a Promises/A+-compliant implementation of Promises that should
+ * be able to interoperate with most other Promises implementations. Minified's Web module in stand-alone distribution comes with a limited implementation.
+ * See below for details.
+ *
+ * What may be somewhat surprising about this Promises specification is that the only standard-compliant way to access the result is to
+ * register callbacks. They will be invoked as soon as the operation is finished.
+ * If the operation already ended when you register the callbacks, the callback will then just be called from the event loop as soon
+ * as possible (but never while the ##then() you register them with is still running).
+ * This design forces you to handle the operation result asynchronously and disencourages 'bad' techniques such as polling.
+ *
+ * The central method of a Promise, and indeed the only required function in Promises/A+, is ##then(). It allows you to register
+ * two callback methods, one for success (called 'fulfillment' in Promises/A+ terminology) and one for failures (called 'rejection' in Promises/A+).
+ *
+ * This example shows you how to use then():
+ *
+ *
+ * What makes Promises so special is that ##then() itself returns a new Promise, which is based on the Promise then() was called on, but can be
+ * modified by the outcome of callbacks. Both arguments to then() are optional, and you can also write the code like this:
+ *
+ *
+ * Because the first ##then() returns a new Promise based on the original Promise, the second then() will handle errors of the request just like
+ * the first one did. There is only one subtle difference in the second example: the error handler will not only be called if the request failed,
+ * but also when the request succeded but the success handler threw an exception. That's one of the two differences between the original Promise and
+ * the Promise returned by then(). Any exception thrown in a callback causes the new Promise to be in error state.
+ *
+ * Before I show you the second difference between the original Promise and the new Promise, let me make the example a bit more readable
+ * by using ##error(), which is not part of Promises/A+, but a simple extension by Minified. It just registers the failure callback without
+ * forcing you to specify null as first argument:
+ *
+ *
+ * A very powerful capability of Promises is that you can easily chain them. If a ##then() callback returns a value, the new Promise returned
+ * by then() will be marked as success (fulfilled) and this value is the result of the operation. If a callback returns a Promise,
+ * the new Promise will assume the state of the returned Promise. You can use the latter to create chains of asynchronous operations,
+ * but you still need only a single error handler for all of them and you do not need to nest functions to achieve this:
+ *
+ *
+ * Only the full Minified distribution allows you to create promises yourself, using the ##promise() function. The Promises/A+
+ * specification does not specify how to fulfill a promise, but in Minified's implementation every Promise object has a function fire()
+ * that needs to be called when the promise result is ready. It requires two arguments.
+ * The first is a boolean, true for a successful operation and false for a failure. The second is an array or list containing the
+ * arguments to call the corresponding ##then() handler with.
+ *
+ * The following example is a function, similar to ##wait(), that returns a Promise which succeeds after the given amount
+ * of milliseconds has passed.
+ * It then fulfills the promise with the number of milliseconds as argument.
+ *
+ *
+ * function timeout(durationMs) {
+ * var p = _.promise();
+ * setTimeout(function() { p.fire(true, [durationMs]); }, durationMs);
+ * return p;
+ * }
+ *
+ * If you use only the Web module, instead of the full implementation, the promises implementation is not fully Promises/A+ compliant.
+ * One major difference is that it does not allow you create promises yourself. The only way to get a promise in the Web module
+ * is from functions like ##animate() and ##request(). The other difference is that the interoperability with other promises frameworks
+ * is limited, even though it should be good enough most of the time.
+ *
+ * There are two things you may run into when you use Web's simplified implementation with a complete implementation:
+ *
The simplified implementation does not support recursive thenables. So when you register callbacks with ##then(),
+ * you can return a promise or a thenable, but only if that promise is not also returning a promise.
+ *
Many corner cases required by the Promises/A+ specification are not handled. When interoperating using
+ * reasonable implementations, you may never run into this, but Promises/A+ has detailed rules for things like ##then()
+ * methods implemented as dynamic getter and returning a new value on each invocation or throwing exceptions. If you need
+ * a water-proof implementation, you need to use the complete implementation in Minified's full package.