diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..da7852d --- /dev/null +++ b/.eslintignore @@ -0,0 +1,2 @@ +vendor/ +src/scripts/vendor/ diff --git a/dev/config.js b/dev/config.js index 4f3bbf6..f9d1e42 100644 --- a/dev/config.js +++ b/dev/config.js @@ -32,7 +32,7 @@ module.exports = [ "value": "Hello", "label": "Greeting", "attributes": { - "placeholder": "Enter a \"greeting\"", + "placeholder": "Enter a \"greeting\" >", "limit": 10, "required": "required" } diff --git a/dev/custom-fn.js b/dev/custom-fn.js index 3658764..0cedd6f 100644 --- a/dev/custom-fn.js +++ b/dev/custom-fn.js @@ -1,5 +1,14 @@ 'use strict'; module.exports = function() { + var Api = this; + + var testHandler = function() { + console.debug('KEEGAN: EVENT', this); + Api.getItemByAppKey('background').off(testHandler); + }; + + Api.getItemByAppKey('background').on('change', testHandler); + console.debug('custom fn worked'); }; diff --git a/dev/dev.html b/dev/dev.html index 6e10636..b5f6387 100644 --- a/dev/dev.html +++ b/dev/dev.html @@ -1,14 +1,17 @@ - Pebble Clay Development Page - + + + -
+
+ + diff --git a/gulpfile.js b/gulpfile.js index 21861b7..0021387 100755 --- a/gulpfile.js +++ b/gulpfile.js @@ -16,8 +16,9 @@ gulp.task('clean-js', function() { }); gulp.task('js', ['clean-js'], function() { - return browserify('src/scripts/config-page.js', { debug: false }) - .transform(stringify(['.html', '.mustache'])) + return browserify('src/scripts/config-page.js', { debug: true }) + .transform(stringify(['.html', '.tpl'])) + .transform('deamdify') .bundle() .pipe(source('config-page.js')) .pipe(gulp.dest('./tmp/')); @@ -47,7 +48,7 @@ gulp.task('inlineHtml', ['js', 'sass'], function() { gulp.task('clay', ['inlineHtml'], function() { return browserify('index.js', { debug: false }) - .transform(stringify(['.html', '.mustache'])) + .transform(stringify(['.html', '.tpl'])) .require(require.resolve('./index'), {expose: 'pebble-clay'}) .bundle() .pipe(source('clay.js')) @@ -58,8 +59,8 @@ gulp.task('default', ['clay']); gulp.task('dev', ['js', 'sass'], function() { - gulp.watch('./src/styles/', ['sass']); - gulp.watch('./src/scripts/', ['js']); + gulp.watch('src/styles/**/*.scss', ['sass']); + gulp.watch(['src/scripts/**/*.js', 'src/templates/**/*.tpl'], ['js']); return browserify('dev/dev.js', { debug: true }) .bundle() diff --git a/package.json b/package.json index 58b84d0..efe3afd 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ }, "homepage": "https://github.com/keegan-lillo/pebble-clay#readme", "devDependencies": { + "deamdify": "^0.1.1", "eslint": "^1.5.1", "eslint-config-pebble": "^1.2.0", "eslint-plugin-standard": "^1.3.1", @@ -33,18 +34,12 @@ }, "dependencies": { "browserify": "^11.2.0", - "browserify-mustache": "0.0.4", "del": "^2.0.2", "gulp": "^3.9.0", - "gulp-html-to-js": "0.0.1", "gulp-inline": "0.0.15", "gulp-minify-html": "^1.0.4", "gulp-minify-inline": "^0.1.1", - "gulp-template": "^3.0.0", - "hogan.js": "^3.0.2", - "mustache": "^2.1.3", "stringify": "^3.1.0", - "vinyl-source-stream": "^1.1.0", - "zepto-browserify": "^1.0.0" + "vinyl-source-stream": "^1.1.0" } } diff --git a/pebble-clay-example/clay.json b/pebble-clay-example/clay.json deleted file mode 100644 index 684cafe..0000000 --- a/pebble-clay-example/clay.json +++ /dev/null @@ -1,39 +0,0 @@ -[ - { - "type": "static_title", - "text": "My Cool Example App" - }, - { - "type": "static_heading1", - "text": "By Keegan Lillo - Lillo.me" - }, - [ - { - "type": "input_text", - "app_key": "greeting", - "default": "Hello", - "label": "Greeting", - "attributes": { - "placeholder": "Enter a greeting", - "limit": 10, - "required": true - } - }, - { - "type": "input_select", - "app_key": "flavor", - "default": "grape", - "options": [ - { "label": "Berry", "value": "berry" }, - { "label": "Grape", "value": "grape" }, - { "label": "Banana", "value": "banana" } - ] - }, - { - "type": "input_color", - "app_key": "background", - "default": "0xFF0000", - "label": "Background Color" - } - ] -] diff --git a/src/config-page.html b/src/config-page.html index 22ad39c..3349c98 100755 --- a/src/config-page.html +++ b/src/config-page.html @@ -13,7 +13,7 @@ -
+
diff --git a/src/scripts/config-page.js b/src/scripts/config-page.js index 3d0ed6e..9ef80d8 100755 --- a/src/scripts/config-page.js +++ b/src/scripts/config-page.js @@ -14,17 +14,18 @@ * @property {Array} items */ -var mustache = require('mustache'); -var itemTypes = require('./lib/item-types'); -var $ = require('zepto-browserify').$; +var itemTypes = require('./lib/items'); +var $ = require('./vendor/minified/minified').$; +var _ = require('./vendor/minified/minified')._; +var HTML = require('./vendor/minified/minified').HTML; -var config = $.extend(true, [], window.clayConfig || []); -var settings = $.extend(true, {}, window.claySettings || {}); +var config = _.extend([], window.clayConfig || []); +var settings = _.extend({}, window.claySettings || {}); var returnTo = window.returnTo || 'pebblejs://close#'; var customFn = window.customFn; function submit(event) { - $.each(api.itemsByAppKey, function(appKey, item) { + _.each(api.itemsByAppKey, function(appKey, item) { settings[appKey] = item.get(); }); // Set the return URL depending on the runtime environment @@ -47,8 +48,6 @@ function getSetting(key, defaultValue) { // settings[key] = value; // } -var index = 0; - /** * @param {Clay~Item|Array} item * @param {$} $parent @@ -63,29 +62,29 @@ function processConfigItem(item, $parent) { } else if (item.type === 'section') { processConfigItem( item.items, - $('
').appendTo($parent) + $parent.add(HTML('
')) ); } else if (item.type === 'block') { processConfigItem( item.items, - $('
').appendTo($parent) + $parent.add(HTML('
')) ); } 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. + *
+   * var files = _('fileA.txt', 'fileA.txt', 'fileC.txt');
+   * var content;
+   * _.promise(files.map(function(file) {
+   *      return $.request('get', '/txts/' + file);
+   * })).then(function(fileRslt1, fileRslt2, fileRslt3) {
+   *      content = _(fileRslt1, fileRslt2, fileRslt3).map( function(result) { return result[0]; }).join('');
+   * }).error(function(status, response, xhr, url) {
+   *    alert('failed to load file '+url);
+   * });
+   * 
+ * + * @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. + *
+     * var div = $('#myMovingDiv').set({$left: '0px', $top: '0px'});
+     * var prom = div.animate({$left: '200px', $top: '0px'}, 600, 0)
+     *    .then(function() {
+     *           return _.promise(div.animate({$left: '200px', $top: '200px'}, 800, 0),
+     *                            div.animate({$backgroundColor: '#f00'}, 200));
+     *    }).then(function() {
+     *           return div.animate({$left: '100px', $top: '100px'}, 400);
+     *    });
+     *
+     * $('#stopButton').on('click', prom.stop);
+     * 
+ */ + 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. + *
+     * $.request('get', '/weather.html')
+     *     .then(function(txt) {
+     *        alert('Got response!');
+     *     });
+     * 
+ * + * @example Including an error handler. + *
+     * $.request('get', '/weather.html')
+     *     .then(function(txt) {
+     *        alert('Got response!');
+     *     }, function(err) {
+     *        alert('Error!');
+     *     }));
+     * 
+ * + * @example Chained handler. + *
+     * $.request('get', '/weather.do')
+     *     .then(function(txt) {
+     *        showWeather(txt);
+     *     }
+     *     .then(function() {
+     *        return $.request('get', '/traffic.do');
+     *     }
+     *     .then(function(txt) {
+     *        showTraffic(txt);
+     *     }
+     *     .then(function() {
+     *        alert('All result displayed');
+     *     }, function() {
+     *        alert('An error occurred');
+     *     });
+     * 
+ * + * @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. + *
+     * $.request('get', '/weather.html')
+     *     .always(function() {
+     *        alert('Got response or error!');
+     *     });
+     * 
+ * + * @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. + *
+     * $.request('get', '/weather.html')
+     *     .error(function() {
+     *        alert('Got error!');
+     *     });
+     * 
+ * + * @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: + *
+     * $('h2.section').each(function(item, index) {
+     *     item.innerHTML = 'Section ' + index + ': ' + item.innerHTML;
+     * });
+     * 
+ * + * @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: + *
+     * var list = _([4, 22, 7, 2, 19]).filter(function(item, index) {
+     *     return item <= 10;
+     * });
+     * 
+ * + * @example The previous example with a native array is input. Note that the result is always a ##list#Minified list##: + *
+     * var list = _.filter([4, 22, 7, 2, 19], function(item, index) {
+     *     return item <= 10;
+     * });
+     * 
+ * + * @example Creates a list of all unchecked checkboxes on a web page: + *
+     * var list = $('input').filter(function(item, index) {
+     *     return item.getAttribute('type') == 'checkbox' && item.checked;
+     * });
+     * 
+ * + * @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: + *
+     * var inced = _(3, 7, 11, 5, 19, 3).map(function(number, index) {
+     *     return number + 1;
+     * });
+     * 
+ * + * @example The previous example with a native array is input. Note that the result is always a ##list#Minified list##: + *
+     * var inced = _.map([3, 7, 11, 5, 19, 3], function(number, index) {
+     *     return number + 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(): + *
    + *
  1. 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.
  2. + *
  3. 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.
  4. + *
+ * + * 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 SchemaExampleSets what?Description
nameinnerHTMLPropertyA name without prefix of '$' or '@' gets a property of the object.
@name@hrefAttributeGets the HTML attribute using getAttribute().
%name%phoneData-AttributeGets a data attribute using getAttribute(). Data attributes are + * attributes whose names start with 'data-'. '%myattr' and '@data-myattr' are equivalent.
$name$fontSizeCSS PropertyGets 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 ClassesA simple $ returns the CSS classes of the element and is identical with "className".
$$$$StyleReads 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$$showShow/HideReturns 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$$fadeFade EffectThe 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$$slideSlide 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$$scrollYScroll CoordinatesThe 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: + *
+     * var coords = $('#myElement').get({$top: 0, $left: 0}, true);
+     * coords.$top = coords.$top + 10 + 'px';
+     * coords.$left = coords.$left + 20 + 'px';
+     * $('#myElement').set(coords);
+     * 
+ * 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 SchemaExampleSets what?Description
nameinnerHTMLPropertyA name without prefix of '$' or '@' sets a property of the object.
@name@hrefAttributeSets 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%phoneData-AttributeSets a data attribute using setAttribute(). Data attributes are + * attributes whose names start with 'data-'. '%myattr' and '@data-myattr' are equivalent.
$name$fontSizeCSS PropertySets a style using the element's style object. + * The syntax for the CSS styles is camel-case (e.g. "$backgroundColor", not "$background-color").
$$CSS ClassesA 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.
$$$$StyleSets the element's style attribute in a browser-independent way.
$$show$$showShow/HideIf 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$$fadeFade EffectThe 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$$slideSlide EffectThe 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$$scrollYScroll CoordinatesThe 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.
+ * + * @example Unchecking checkboxes: + *
+     * $('input.checkbox').set('checked', false);
+     * 
+ * + * @example Changing the innerHTML property of an element: + *
+     * $('#toc').set('innerHTML', 'Content');
+     * 
+ * + * @example Changing attributes: + *
+     * $('a.someLinks').set('@href', 'http://www.example.com/');
+     * 
+ * + * @example Removing attributes: + *
+     * $('a.someLinks').set('@title', null);
+     * 
+ * + * @example Changing styles: + *
+     * $('.bigText').set('$fontSize', 'x-large');
+     * 
+ * + * @example Adding and removing CSS classes: + *
+     * $('.myElem').set('$', '+myClass -otherClass');
+     * 
+ * + * @example Toggling a CSS class: + *
+     * $('.myElem').set('$', 'on');
+     * 
+ * + * @example Shortcut for CSS manipulation: + *
+     * $('.myElem').set('+myClass -otherClass on');
+     * 
+ * + * @example Making an element transparent: + *
+     * $('.seeThrough').set('$$fade', 0.5);
+     * 
+ * + * @example Making an element visible. Note that $$fade will set the element's display style to 'block' and visibility style to 'visible'. + *
+     * $('.myElem').set('$$fade', 1);
+     * 
+ * + * @example Using a map to change several properties: + *
+     * $('input.checkbox').set({checked: false,
+     *                          '@title': 'Check this'});
+     * 
+ * + * @example Changing CSS with a map: + *
+     * $('.importantText').set({$fontSize: 'x-large',
+     *                          $color: 'black',
+     *                          $backgroundColor: 'red',
+     *                          $: '+selected -default'});
+     * 
+ * + * @example You can specify a function as value to modify a value instead of just setting it: + *
+     * $('h2').set('innerHTML', function(oldValue, index) {
+     *     return 'Chapter ' + index + ': ' + oldValue.toUpperCase();
+     * });
+     * 
+ * + * @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: + *
+     * $('#comments').add('Some additional text.');
+     * 
+ * This results in: + *
+     * <div id="comments">Here is some text.<br/>Some additional text.</div>
+     * 
+ * + * @example Using the following HTML: + *
+     * <ul id="myList">
+     *   <li>First list entry</li>
+     *   <li>Second list entry</li>
+     * </ul>
+     * 
+ * The following Javascript adds an element to the list: + *
+     * $('#myList').add(EE('li', 'My extra point');
+     * 
+ * This results in + *
+     * <ul id="myList">
+     *   <li>First list entry</li>
+     *   <li>Second list entry</li>
+     *   <li>My extra point</li>
+     * </ul>
+     * 
+ * + * @example Use a list to add several elements at once: + *
+     * $('#comments').add([
+     *      EE('br'),
+     *     'Some text',
+     *     EE('span', {'className': 'highlight'}, 'Some highlighted text')
+     * ]);
+     * 
+ * + * @example If you need to customize the content, you can write a factory function: + *
+     * $('.chapter').add(function(parent, index) { return EE('h2', 'Chapter number ' + index); });
+     * 
+ * + * @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: + *
+     * $('#myButton').on('click', function() {
+     *    setStatus('running');
+     * });
+     * 
+ * The previous example can bere written like this, using on()'s args parameter: + *
+     * $('#myButton').on('click', setStatus, ['running']);
+     * 
+ * + * @example Adds two handlers on an input field. The event names are prefixed with '|' and thus keep their original behavior: + *
+     * $('#myInput').on('|keypress |keydown', function() {
+     *    // do something
+     * });
+     * 
+ * + * @example Adds a click handler that will abort the operation by returning false, unless the user confirms it: + *
+     * $('#myLink').on('?click', function() {
+     *    return window.confirm('Really leave?');
+     * });
+     * 
+ * + * @example Adds a button and registers a click handler for it using a sub-selector. + *
+     * $('#myForm').add(HTML("<li><button>click me</button></li>").on('button', 'click', myClickHandler));
+     * 
+ * + * @example Adds listeners for all clicks on a table's rows using the bubble selector 'tr'. + *
+     * $('#table').on('change', 'tr', function(event, index, selectedIndex) {
+     *    alert("Click on table row number: " + selectedIndex);
+     * }, 'tr');
+     * 
+ * 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: + *
+     * var price = 14.9;
+     * $('#price').ht('<b>${{::0.00}}</b>', price);
+     * 
+ * Results in: + *
+     * <div id="price"><b>$14.90</b></div>
+     * 
+ * + * @example Render a list of names: + *
+     * var names = [ {first: 'James', last: 'Sullivan'},
+     *               {first: 'Michael', last: 'Wazowski'} ];
+     * $('#list').ht('<h2>{{listName}}</h2>'+
+     *               '<ul>{{each n: names}}<li>{{n.first}} {{n.last}}</li>{{/each}}</ul>',
+     *               {listName: 'Guys', names: names});
+     * 
+ * The code creates this: + *
+     * <h2>Guys</h2>
+     * <ul><li>James Sullivan<li><li>Michael Wazowski</li></ul>
+     * 
+ * + * @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: + *
+     * var myRequest = {         // create a request object that can be serialized via JSON
+     *      request: 'register',
+     *      entries: [
+     *        {name: 'Joe',
+     *      	    job: 'Plumber'
+     *        }
+     *      ]};
+     *
+     * function failureHandler() {
+     *   $('#registrationResult').fill('Registration failed');
+     * }
+     *
+     * $.request('post', 'http://service.example.com/directory', $.toJSON(myRequest))
+     *     .then(function(txt) {
+     *        if (txt == 'OK')
+     *             $('#registrationResult').fill('Registration succeeded');
+     *        else
+     *              failureHandler();
+     *        })
+     *     .error(failureHandler);
+     * 
+ * + * @example Using HTTP authentication and a custom XMLHttpRequest property. + *
var handler = $.request('get', 'http://service.example.com/userinfo', null, {xhr: {withCredentials: true}, user: 'me', pass: 'secret'});
+ * + * + * @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: + *
+     * $.ready(function() {
+     *   $('#someElement').fill('ready() called');
+     * });
+     * 
+ * + * @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. + *
+     * var div = $('#myMovingDiv').set({$left: '0px', $top: '0px'});
+     * div.animate({$left: '200px', $top: '0px'}, 600, 0)
+     *    .then(function() {
+     *           div.animate({$left: '200px', $top: '200px'}, 800, 0);
+     *    }).then(function() {
+     *    	     return _.wait(50);
+     *    }).then(function() {
+     *           div.animate({$left: '100px', $top: '100px'}, 400);
+     *    });
+     * });
+     * 
+ * + * + * @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: + *
var myTemplate = _.template(
+     * 	   '{{each value, index: names}}{{index}}. {{value.firstName}} {{value.lastName}}{{/each}}');
+     * 
+ * + * 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: + * + * + * + * + * + * + * + *
NameDesciption
thisThe template object outside of each. Inside eachs, the current value.
objThe parameter given to the template function.
_A reference to Minified Util.
escThe escape function given when the template has been defined. If no function has been given, + * a default function that returns the input unmodified.
printA function(text,...) that appends one or more strings to the template result.
eachA 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: + *
+     * <div id="price">-</div>
+     * 
+ * Then the price can be set like this: + *
+     * var price = 14.9;
+     * $('#price').fill(HTML('<b>${{::0.99}}</b>', price));
+     * 
+ * Results in: + *
+     * <div id="price"><b>$14.90</b></div>
+     * 
+ * + * @example Adding elements to an existing list: + *
+     * var names = [ {first: 'James', last: 'Sullivan'},
+     *               {first: 'Michael', last: 'Wazowski'} ];
+     * $('#list').add(HTML('{{each}}<li>{{this.first}} {{this.last}}</li>{{/each}}', names);
+     * 
+ * The code adds this to #list: + *
+     * <li>James Sullivan<li><li>Michael Wazowski</li>
+     * 
+ * + * @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: + *
+     * var l3 = $([elementA, [elementB, null, elementC], null]);
+     * 
+ * + * @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: + *
+     * var l10 = $('.myRadio', '#formA, #formB, #formC');
+     * 
+ * The result is identical to: + *
+     * var l10 = $('#formA .myRadio, #formB .myRadio, #formC .myRadio');
+     * 
+ * + * @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: + *
+     * $('#myForm .myRadio').addClass('uncheckedRadio')
+     *                      .set('checked', true)
+     *                      .on('click', function() {
+     *                             $(this).set({@: 'uncheckedRadio');
+     *                      });
+     * 
+ * + * @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;
+     * 
+ * + * @example Select the checkbox 'myCheckbox': + *
+     * $$('#myCheckbox').checked = true;
+     * 
+ * + * @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: + *
+     * MINI.M.prototype.printLength = function() { console.log(this.length); };
+     * 
+ */ + '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 '>'. + *
+     * MINI.getter['>'] = function(list, name) {
+     * 	return list[0].getAttributeNS('http://www.w3.org/1999/xlink', name);
+     * };
+     *
+     * var xlinkHref = $('#svgLink').get('>href');
+     * 
+ */ + '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. + *
+     * MINI.setter['||'] = function(list, name, value) {
+     * 	list.set('$border' + name.replace(/^[a-z]/, function(a) { return a.toUpperCase()}, value);
+     * };
+     *
+     * $('#box').set('||color', 'red');   // same as set('$borderColor', 'red')
+     * $('#box').set('||leftRadius', 4);  // same as set('$borderLeftRadius', 4)
+     * 
+ * + * @example Adding XLink attribute support to set(). This is useful if you work with SVG. The prefix is '>'. + *
+     * MINI.setter['>'] = function(list, name, value) {
+     * 	list.each(function(obj, index) {
+     * 		var v;
+     * 		if (_.isFunction(value))
+     * 			v = value(obj.getAttributeNS('http://www.w3.org/1999/xlink', name), index, obj);
+     * 		else
+     * 			v = value;
+     *
+     *		if (v == null)
+     *			obj.removeAttributeNS('http://www.w3.org/1999/xlink', name);
+     *		else
+     *			obj.setAttributeNS('http://www.w3.org/1999/xlink', name, v);
+     *	});
+     * };
+     *
+     * $('#svgLink').set('>href', 'http://minifiedjs.com/');
+     * 
+ */ + '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(): + *
+ * $.request('get', 'http://example.com/weather?zip=90210')
+ *  .then(function success(result) {
+ *      alert('The weather is ' + result);
+ *  }, function error(exception) {
+ *  	alert('Something went wrong');
+ *  });
+ * 
+ * + * 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: + *
+ * $.request('get', 'http://example.com/weather?zip=90210')
+ *  .then(function success(result) {
+ *      alert('The weather is ' + result);
+ *  })
+ *  .then(null, function error(exception) {
+ *  	alert('Something went wrong');
+ *  });
+ * 
+ * + * 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: + *
+ * $.request('get', 'http://example.com/weather?zip=90210')
+ *  .then(function success(result) {
+ *      alert('The weather is ' + result);
+ *  })
+ *  .error(function error(exception) {  // error(callback) is equivalent to then(null, callback)
+ *  	alert('Something went wrong');
+ *  });
+ * 
+ * + * 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: + *
+ * $.request('get', 'http://example.com/zipcode?location=Beverly+Hills,+CA')
+ *  .then(function(resultZip) {
+ *      return $.request('get', 'http://example.com/weather', {zip: resultZip});
+ *  })
+ *  .then(function(resultWeather) {
+ *      alert('The weather in Beverly Hills is ' + resultWeather);
+ *  })
+ *  .error(function(exception) {
+ *  	alert('Something went wrong');
+ *  });
+ * 
+ * + * 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;
+ * }
+ * 
+ * Call it like this: + *
+ * timeout(1000).then(function(ms) { window.alert(ms+ ' milliseconds have passed.'); });
+ * 
+ * + *

Limited Promises Implementation in Web module

+ * 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: + *
  1. 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.
  2. + *
  3. 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. + *
+   * var files = _('fileA.txt', 'fileA.txt', 'fileC.txt');
+   * var content;
+   * _.promise(files.map(function(file) {
+   *      return $.request('get', '/txts/' + file);
+   * })).then(function(fileRslt1, fileRslt2, fileRslt3) {
+   *      content = _(fileRslt1, fileRslt2, fileRslt3).map( function(result) { return result[0]; }).join('');
+   * }).error(function(status, response, xhr, url) {
+   *    alert('failed to load file '+url);
+   * });
+   * 
+ * + * @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. + *
+     * var div = $('#myMovingDiv').set({$left: '0px', $top: '0px'});
+     * var prom = div.animate({$left: '200px', $top: '0px'}, 600, 0)
+     *    .then(function() {
+     *           return _.promise(div.animate({$left: '200px', $top: '200px'}, 800, 0),
+     *                            div.animate({$backgroundColor: '#f00'}, 200));
+     *    }).then(function() {
+     *           return div.animate({$left: '100px', $top: '100px'}, 400);
+     *    });
+     *
+     * $('#stopButton').on('click', prom.stop);
+     * 
+ */ + 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. + *
+     * $.request('get', '/weather.html')
+     *     .then(function(txt) {
+     *        alert('Got response!');
+     *     });
+     * 
+ * + * @example Including an error handler. + *
+     * $.request('get', '/weather.html')
+     *     .then(function(txt) {
+     *        alert('Got response!');
+     *     }, function(err) {
+     *        alert('Error!');
+     *     }));
+     * 
+ * + * @example Chained handler. + *
+     * $.request('get', '/weather.do')
+     *     .then(function(txt) {
+     *        showWeather(txt);
+     *     }
+     *     .then(function() {
+     *        return $.request('get', '/traffic.do');
+     *     }
+     *     .then(function(txt) {
+     *        showTraffic(txt);
+     *     }
+     *     .then(function() {
+     *        alert('All result displayed');
+     *     }, function() {
+     *        alert('An error occurred');
+     *     });
+     * 
+ * + * @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. + *
+     * $.request('get', '/weather.html')
+     *     .always(function() {
+     *        alert('Got response or error!');
+     *     });
+     * 
+ * + * @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. + *
+     * $.request('get', '/weather.html')
+     *     .error(function() {
+     *        alert('Got error!');
+     *     });
+     * 
+ * + * @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: + *
+     * $('h2.section').each(function(item, index) {
+     *     item.innerHTML = 'Section ' + index + ': ' + item.innerHTML;
+     * });
+     * 
+ * + * @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: + *
+     * var list = _([4, 22, 7, 2, 19]).filter(function(item, index) {
+     *     return item <= 10;
+     * });
+     * 
+ * + * @example The previous example with a native array is input. Note that the result is always a ##list#Minified list##: + *
+     * var list = _.filter([4, 22, 7, 2, 19], function(item, index) {
+     *     return item <= 10;
+     * });
+     * 
+ * + * @example Creates a list of all unchecked checkboxes on a web page: + *
+     * var list = $('input').filter(function(item, index) {
+     *     return item.getAttribute('type') == 'checkbox' && item.checked;
+     * });
+     * 
+ * + * @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: + *
+     * var inced = _(3, 7, 11, 5, 19, 3).map(function(number, index) {
+     *     return number + 1;
+     * });
+     * 
+ * + * @example The previous example with a native array is input. Note that the result is always a ##list#Minified list##: + *
+     * var inced = _.map([3, 7, 11, 5, 19, 3], function(number, index) {
+     *     return number + 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(): + *
    + *
  1. 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.
  2. + *
  3. 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.
  4. + *
+ * + * 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: + *
+     * $('#comments').add('Some additional text.');
+     * 
+ * This results in: + *
+     * <div id="comments">Here is some text.<br/>Some additional text.</div>
+     * 
+ * + * @example Using the following HTML: + *
+     * <ul id="myList">
+     *   <li>First list entry</li>
+     *   <li>Second list entry</li>
+     * </ul>
+     * 
+ * The following Javascript adds an element to the list: + *
+     * $('#myList').add(EE('li', 'My extra point');
+     * 
+ * This results in + *
+     * <ul id="myList">
+     *   <li>First list entry</li>
+     *   <li>Second list entry</li>
+     *   <li>My extra point</li>
+     * </ul>
+     * 
+ * + * @example Use a list to add several elements at once: + *
+     * $('#comments').add([
+     *      EE('br'),
+     *     'Some text',
+     *     EE('span', {'className': 'highlight'}, 'Some highlighted text')
+     * ]);
+     * 
+ * + * @example If you need to customize the content, you can write a factory function: + *
+     * $('.chapter').add(function(parent, index) { return EE('h2', 'Chapter number ' + index); });
+     * 
+ * + * @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: + *
+     * $('#myButton').on('click', function() {
+     *    setStatus('running');
+     * });
+     * 
+ * The previous example can bere written like this, using on()'s args parameter: + *
+     * $('#myButton').on('click', setStatus, ['running']);
+     * 
+ * + * @example Adds two handlers on an input field. The event names are prefixed with '|' and thus keep their original behavior: + *
+     * $('#myInput').on('|keypress |keydown', function() {
+     *    // do something
+     * });
+     * 
+ * + * @example Adds a click handler that will abort the operation by returning false, unless the user confirms it: + *
+     * $('#myLink').on('?click', function() {
+     *    return window.confirm('Really leave?');
+     * });
+     * 
+ * + * @example Adds a button and registers a click handler for it using a sub-selector. + *
+     * $('#myForm').add(HTML("<li><button>click me</button></li>").on('button', 'click', myClickHandler));
+     * 
+ * + * @example Adds listeners for all clicks on a table's rows using the bubble selector 'tr'. + *
+     * $('#table').on('change', 'tr', function(event, index, selectedIndex) {
+     *    alert("Click on table row number: " + selectedIndex);
+     * }, 'tr');
+     * 
+ * 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: + *
+     * var myRequest = {         // create a request object that can be serialized via JSON
+     *      request: 'register',
+     *      entries: [
+     *        {name: 'Joe',
+     *      	    job: 'Plumber'
+     *        }
+     *      ]};
+     *
+     * function failureHandler() {
+     *   $('#registrationResult').fill('Registration failed');
+     * }
+     *
+     * $.request('post', 'http://service.example.com/directory', $.toJSON(myRequest))
+     *     .then(function(txt) {
+     *        if (txt == 'OK')
+     *             $('#registrationResult').fill('Registration succeeded');
+     *        else
+     *              failureHandler();
+     *        })
+     *     .error(failureHandler);
+     * 
+ * + * @example Using HTTP authentication and a custom XMLHttpRequest property. + *
var handler = $.request('get', 'http://service.example.com/userinfo', null, {xhr: {withCredentials: true}, user: 'me', pass: 'secret'});
+ * + * + * @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: + *
+     * $.ready(function() {
+     *   $('#someElement').fill('ready() called');
+     * });
+     * 
+ * + * @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. + *
+     * var div = $('#myMovingDiv').set({$left: '0px', $top: '0px'});
+     * div.animate({$left: '200px', $top: '0px'}, 600, 0)
+     *    .then(function() {
+     *           div.animate({$left: '200px', $top: '200px'}, 800, 0);
+     *    }).then(function() {
+     *    	     return _.wait(50);
+     *    }).then(function() {
+     *           div.animate({$left: '100px', $top: '100px'}, 400);
+     *    });
+     * });
+     * 
+ * + * + * @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: + *
var myTemplate = _.template(
+     * 	   '{{each value, index: names}}{{index}}. {{value.firstName}} {{value.lastName}}{{/each}}');
+     * 
+ * + * 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: + * + * + * + * + * + * + * + *
NameDesciption
thisThe template object outside of each. Inside eachs, the current value.
objThe parameter given to the template function.
_A reference to Minified Util.
escThe escape function given when the template has been defined. If no function has been given, + * a default function that returns the input unmodified.
printA function(text,...) that appends one or more strings to the template result.
eachA 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: + *
+     * var l3 = $([elementA, [elementB, null, elementC], null]);
+     * 
+ * + * @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: + *
+     * var l10 = $('.myRadio', '#formA, #formB, #formC');
+     * 
+ * The result is identical to: + *
+     * var l10 = $('#formA .myRadio, #formB .myRadio, #formC .myRadio');
+     * 
+ * + * @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: + *
+     * $('#myForm .myRadio').addClass('uncheckedRadio')
+     *                      .set('checked', true)
+     *                      .on('click', function() {
+     *                             $(this).set({@: 'uncheckedRadio');
+     *                      });
+     * 
+ * + * @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: + *
+     * MINI.M.prototype.printLength = function() { console.log(this.length); };
+     * 
+ */ + '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 '>'. + *
+     * MINI.getter['>'] = function(list, name) {
+     * 	return list[0].getAttributeNS('http://www.w3.org/1999/xlink', name);
+     * };
+     *
+     * var xlinkHref = $('#svgLink').get('>href');
+     * 
+ */ + '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. + *
+     * MINI.setter['||'] = function(list, name, value) {
+     * 	list.set('$border' + name.replace(/^[a-z]/, function(a) { return a.toUpperCase()}, value);
+     * };
+     *
+     * $('#box').set('||color', 'red');   // same as set('$borderColor', 'red')
+     * $('#box').set('||leftRadius', 4);  // same as set('$borderLeftRadius', 4)
+     * 
+ * + * @example Adding XLink attribute support to set(). This is useful if you work with SVG. The prefix is '>'. + *
+     * MINI.setter['>'] = function(list, name, value) {
+     * 	list.each(function(obj, index) {
+     * 		var v;
+     * 		if (_.isFunction(value))
+     * 			v = value(obj.getAttributeNS('http://www.w3.org/1999/xlink', name), index, obj);
+     * 		else
+     * 			v = value;
+     *
+     *		if (v == null)
+     *			obj.removeAttributeNS('http://www.w3.org/1999/xlink', name);
+     *		else
+     *			obj.setAttributeNS('http://www.w3.org/1999/xlink', name, v);
+     *	});
+     * };
+     *
+     * $('#svgLink').set('>href', 'http://minifiedjs.com/');
+     * 
+ */ + '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(): + *
+ * $.request('get', 'http://example.com/weather?zip=90210')
+ *  .then(function success(result) {
+ *      alert('The weather is ' + result);
+ *  }, function error(exception) {
+ *  	alert('Something went wrong');
+ *  });
+ * 
+ * + * 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: + *
+ * $.request('get', 'http://example.com/weather?zip=90210')
+ *  .then(function success(result) {
+ *      alert('The weather is ' + result);
+ *  })
+ *  .then(null, function error(exception) {
+ *  	alert('Something went wrong');
+ *  });
+ * 
+ * + * 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: + *
+ * $.request('get', 'http://example.com/weather?zip=90210')
+ *  .then(function success(result) {
+ *      alert('The weather is ' + result);
+ *  })
+ *  .error(function error(exception) {  // error(callback) is equivalent to then(null, callback)
+ *  	alert('Something went wrong');
+ *  });
+ * 
+ * + * 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: + *
+ * $.request('get', 'http://example.com/zipcode?location=Beverly+Hills,+CA')
+ *  .then(function(resultZip) {
+ *      return $.request('get', 'http://example.com/weather', {zip: resultZip});
+ *  })
+ *  .then(function(resultWeather) {
+ *      alert('The weather in Beverly Hills is ' + resultWeather);
+ *  })
+ *  .error(function(exception) {
+ *  	alert('Something went wrong');
+ *  });
+ * 
+ * + * 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;
+ * }
+ * 
+ * Call it like this: + *
+ * timeout(1000).then(function(ms) { window.alert(ms+ ' milliseconds have passed.'); });
+ * 
+ * + *

Limited Promises Implementation in Web module

+ * 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: + *
  1. 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.
  2. + *
  3. 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/styles/base.scss b/src/styles/base.scss new file mode 100644 index 0000000..1170072 --- /dev/null +++ b/src/styles/base.scss @@ -0,0 +1,303 @@ +@import "vendor/bourbon/bourbon"; +@import "partials/vars"; +@import "partials/mixins"; + +html, body { + @include font-pfdin(regular); + font-size: $em-base; + height:100%; + color: $color-white; + + &.platform-ios { + font-size: 13px; + } + +} + +body { + background-color: $color-grey-darker; +} + +strong { + @include font-pfdin(medium); + color: $color-orange; +} + +button, +.button, +input[type="submit"] { + @include font-pfdin(medium); + text-transform: uppercase; + background-color: $color-grey-medium; + border-radius: rem($border-radius); + font-size: rem(18px); + line-height: rem($button-line-height); + border: none; + display: block; + + color: $color-white; + margin: 0 auto rem(12px); + width: 100%; + text-align: center; + @include user-select(none); + -webkit-tap-highlight-color: rgba(0,0,0,0); + + padding: rem($button-padding); + .platform-ios & { + padding: rem($button-padding-ios); + } + + &:disabled { + background-color: $color-grey-dark; + color: $color-grey-lighter; + } + + &:not(:disabled):active { + background-color: $color-grey-lighter; + } + + &.orange { + background-color: $color-orange; + + &:disabled { + background-color: darken($color-orange, 20%); + color: $color-grey-lighter; + } + + &:not(:disabled):active { + background-color: $color-red; + } + } + + &.light-grey { + background-color: $color-grey-light; + } + +} + +a.button { + text-decoration: none; + color: $color-white; +} + +a { + color: $color-grey-lighter; + + &:hover { + color: inherit; + } +} + +.inputs { + display: block; + width:100%; + border-collapse: collapse; + margin-bottom: rem(20px); + + @include font-pfdin(light); + + label { + display: table-row; + + .label, + .input { + display: table-cell; + padding: rem(13px) rem(10px) rem(13px) rem(5px); + font-size: rem(21px); + border-bottom: $border-standard; + position:relative; + + &.no-border { + border-bottom: none; + } + } + + .input { + width: 100%; + + input { + display:inline-block; + width: 100%; + background: transparent; + border: none; + padding: 0; + vertical-align: baseline; + font-size: rem(21px); + @include font-pfdin(light); + color: $color-white; + + @include placeholder { + color: $color-grey-lighter; + } + + &:focus { + @include placeholder { + color: $color-grey-dark; + } + border: none; + box-shadow: none ; + } + } + } + + &:last-child .label, + &:last-child .input { + border-bottom: none; + } + + &:first-child .label, + &:first-child .input { + padding-top: 0; + } + + &:only-child .label, + &:only-child .input { + border-bottom: $border-standard; + border-top: $border-standard; + padding-top: rem(13px); + } + + &.invalid { + .input:after { + content: "!"; + display: inline-block; + color: $color-white; + background: $color-orange; + border-radius: 0.6em; + width: 1.2em; + text-align: center; + height: 1.2em; + font-size: 1em; + vertical-align: middle; + line-height: 1.3em; + margin-left: -0.7em; + position:absolute; + @include font-pfdin(regular); + } + + .input input { + color: $color-orange; + padding-right: 1em; + + @include placeholder { + color: $color-orange; + } + + } + } + + } +} + +.view-inner-container { +// @include display(flex); +// @include flex-direction(column); +// @include flex-wrap(nowrap); +// @include align-content(stretch); + height:100%; + width:100%; + display: table; + table-layout: fixed; +} + +.form-container { +// @include flex-grow(1); +// @include display(flex); +// @include flex-direction(column); +// @include flex-wrap(nowrap); +// @include justify-content(center); + height: 100%; + display: table-row; + + form { + display: table-cell; + vertical-align: middle; + padding: rem($global-margin/2) rem($form-margin); + } + + .buttons { + padding: 0 rem($global-margin - $form-margin); + } + +} + + +footer { + + text-align: center; + font-size: rem(14px); + line-height: 1; + + .privacy-policy-link { + display: block; + } +} + +.caption { + + text-align: center; + margin: 0 auto rem(25px); + color: $color-grey-lightest; +} + + +// +// +//html, body { +// height: 100%; +// margin: 0; +//} +// +//.hide { +// display:none; +//} +//.item-styled-color .value { +// box-sizing: border-box; +// height: 26px; +//} +// +//.item-container { +// margin-top: 0; +// padding-top: 15px; +//} +// +//input[type="button"]:disabled { +// background-color: #ccc; +//} +// +//#cancel-btn { +// background-color: #444; +//} +// +//#cancel-btn, +//#save-btn { +// width: 45%; +//} +// +//#main-form { +// min-height: 100%; +// /* equal to footer height */ +// margin-bottom: -1.5rem; +//} +// +//#main-form:after { +// content: ""; +// display: block; +// padding-top: 2rem; +//} +// +//footer, #main-form:after { +// height: 1.5rem; +//} +// +//footer { +// display: block; +// text-align: center; +// width: 100%; +//} +// +//.item { +// display: table; +// width:100%; +// box-sizing: border-box; +//} diff --git a/src/styles/config-page.scss b/src/styles/config-page.scss index ed0a217..9f5e8dc 100755 --- a/src/styles/config-page.scss +++ b/src/styles/config-page.scss @@ -1,59 +1,6 @@ +@import "partials/vars"; +@import "partials/mixins"; +@import "partials/reset"; +@import "base"; -html, body { - height: 100%; - margin: 0; -} - -.hide { - display:none; -} -.item-styled-color .value { - box-sizing: border-box; - height: 26px; -} - -.item-container { - margin-top: 0; - padding-top: 15px; -} - -input[type="button"]:disabled { - background-color: #ccc; -} - -#cancel-btn { - background-color: #444; -} - -#cancel-btn, -#save-btn { - width: 45%; -} - -#main-form { - min-height: 100%; - /* equal to footer height */ - margin-bottom: -1.5rem; -} - -#main-form:after { - content: ""; - display: block; - padding-top: 2rem; -} - -footer, #main-form:after { - height: 1.5rem; -} - -footer { - display: block; - text-align: center; - width: 100%; -} - -.item { - display: table; - width:100%; - box-sizing: border-box; -} +@import "items/color"; diff --git a/src/styles/items/color.scss b/src/styles/items/color.scss new file mode 100644 index 0000000..06a7e7b --- /dev/null +++ b/src/styles/items/color.scss @@ -0,0 +1,58 @@ +@import "../partials/vars"; + +.item-color { + + .picker { + } + + .value { + width: rem(10px); + border-color: $color-grey-medium; + border-width: 1px; + border-style: solid; + } + + .color-box-wrap { + display:none; + box-sizing: border-box; + position: relative; + height: 0; + width: 100%; + padding: 0 0 100% 0; // overridden with inline style + margin: 0.6em 0 0em; + + &.show { + display: block; + } + + .color-box-container { + position: absolute; + height: 99.97%; + width: 100%; + left: 0; + top: 0; + + .color-box { + float:left; + cursor: pointer; + + &.rounded-tl { + border-top-left-radius: rem($border-radius); + } + + &.rounded-tr { + border-top-right-radius: rem($border-radius); + } + + &.rounded-bl { + border-bottom-left-radius: rem($border-radius); + } + + &.rounded-br { + border-bottom-right-radius: rem($border-radius); + } + } + + } + } +} diff --git a/src/styles/partials/_mixins.scss b/src/styles/partials/_mixins.scss new file mode 100644 index 0000000..178a0b7 --- /dev/null +++ b/src/styles/partials/_mixins.scss @@ -0,0 +1,22 @@ +@mixin font-pfdin($weight: normal) { + + font-weight: normal; + + @if $weight == light { + font-family: 'PFDinDisplayPro-Light', PFDinDisplayProLightWebfont, sans-serif; + } + + @if $weight == normal or $weight == regular { + font-family: 'PFDinDisplayPro-Regular', PFDinDisplayProRegularWebfont, sans-serif; + } + + @if $weight == medium { + font-family: 'PFDinDisplayPro-Medium', PFDinDisplayProRegularWebfont, sans-serif; + + .platform-android & { + font-family: PFDinDisplayProLightWebfont, sans-serif; + font-weight: bold; + letter-spacing: 0.03em; + } + } +} diff --git a/src/styles/partials/_reset.scss b/src/styles/partials/_reset.scss new file mode 100644 index 0000000..8cf8891 --- /dev/null +++ b/src/styles/partials/_reset.scss @@ -0,0 +1,60 @@ +/* http://meyerweb.com/eric/tools/css/reset/ + v2.0b1 | 201101 + NOTE: WORK IN PROGRESS + USE WITH CAUTION AND TEST WITH ABANDON */ + +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, figcaption, figure, +footer, header, hgroup, menu, nav, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + outline: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} + +input, textarea, button { + outline: none; +} + +/* HTML5 display-role reset for older browsers */ +article, aside, details, figcaption, figure, +footer, header, hgroup, menu, nav, section { + display: block; +} + +body { + line-height: 1; +} + +ol, ul { + list-style: none; +} + +blockquote:before, blockquote:after, +q:before, q:after { + content: ''; + content: none; +} + +/* apply a natural box layout model to all elements, but allowing components to change */ +html { + -webkit-box-sizing: border-box; + box-sizing: border-box; +} + +*, *:before, *:after { + box-sizing: inherit; + -webkit-box-sizing: inherit; +} diff --git a/src/styles/partials/_vars.scss b/src/styles/partials/_vars.scss new file mode 100644 index 0000000..1a058b8 --- /dev/null +++ b/src/styles/partials/_vars.scss @@ -0,0 +1,37 @@ +@import "../vendor/bourbon/bourbon"; + +// Bourbon settings +$em-base: 16px; + +// ---- Colors ------ +$color-orange: #ff4700; +$color-red: #ff0000; +$color-white: #ffffff; +$color-borders: #5c5c5c; + +// background color +$color-grey-darker: #333333; + +// secondary background +$color-grey-dark: #484848; + +//buttons +$color-grey-medium: #666666; +$color-grey-light: #767676; +$border-radius: 4px; + +// used for fonts +$color-grey-lighter: #848484; +$color-grey-lightest: #adadad; + + +// custom vars +$global-margin: 55px; +$form-margin: 25px; +$button-padding: 18px; +$button-padding-ios: 16px; +$button-line-height: 13px; +$footer-height-large: 90px; + +$border-standard: 1px solid $color-borders; + diff --git a/src/styles/vendor/bourbon/_bourbon-deprecated-upcoming.scss b/src/styles/vendor/bourbon/_bourbon-deprecated-upcoming.scss new file mode 100644 index 0000000..e6d1b8c --- /dev/null +++ b/src/styles/vendor/bourbon/_bourbon-deprecated-upcoming.scss @@ -0,0 +1,411 @@ +// The following features have been deprecated and will be removed in the next MAJOR version release + +@mixin inline-block { + display: inline-block; + + @warn "The inline-block mixin is deprecated and will be removed in the next major version release"; +} + +@mixin button ($style: simple, $base-color: #4294f0, $text-size: inherit, $padding: 7px 18px) { + + @if type-of($style) == string and type-of($base-color) == color { + @include buttonstyle($style, $base-color, $text-size, $padding); + } + + @if type-of($style) == string and type-of($base-color) == number { + $padding: $text-size; + $text-size: $base-color; + $base-color: #4294f0; + + @if $padding == inherit { + $padding: 7px 18px; + } + + @include buttonstyle($style, $base-color, $text-size, $padding); + } + + @if type-of($style) == color and type-of($base-color) == color { + $base-color: $style; + $style: simple; + @include buttonstyle($style, $base-color, $text-size, $padding); + } + + @if type-of($style) == color and type-of($base-color) == number { + $padding: $text-size; + $text-size: $base-color; + $base-color: $style; + $style: simple; + + @if $padding == inherit { + $padding: 7px 18px; + } + + @include buttonstyle($style, $base-color, $text-size, $padding); + } + + @if type-of($style) == number { + $padding: $base-color; + $text-size: $style; + $base-color: #4294f0; + $style: simple; + + @if $padding == #4294f0 { + $padding: 7px 18px; + } + + @include buttonstyle($style, $base-color, $text-size, $padding); + } + + &:disabled { + cursor: not-allowed; + opacity: 0.5; + } + + @warn "The button mixin is deprecated and will be removed in the next major version release"; +} + +// Selector Style Button +@mixin buttonstyle($type, $b-color, $t-size, $pad) { + // Grayscale button + @if $type == simple and $b-color == grayscale($b-color) { + @include simple($b-color, true, $t-size, $pad); + } + + @if $type == shiny and $b-color == grayscale($b-color) { + @include shiny($b-color, true, $t-size, $pad); + } + + @if $type == pill and $b-color == grayscale($b-color) { + @include pill($b-color, true, $t-size, $pad); + } + + @if $type == flat and $b-color == grayscale($b-color) { + @include flat($b-color, true, $t-size, $pad); + } + + // Colored button + @if $type == simple { + @include simple($b-color, false, $t-size, $pad); + } + + @else if $type == shiny { + @include shiny($b-color, false, $t-size, $pad); + } + + @else if $type == pill { + @include pill($b-color, false, $t-size, $pad); + } + + @else if $type == flat { + @include flat($b-color, false, $t-size, $pad); + } +} + +// Simple Button +@mixin simple($base-color, $grayscale: false, $textsize: inherit, $padding: 7px 18px) { + $color: hsl(0, 0, 100%); + $border: adjust-color($base-color, $saturation: 9%, $lightness: -14%); + $inset-shadow: adjust-color($base-color, $saturation: -8%, $lightness: 15%); + $stop-gradient: adjust-color($base-color, $saturation: 9%, $lightness: -11%); + $text-shadow: adjust-color($base-color, $saturation: 15%, $lightness: -18%); + + @if is-light($base-color) { + $color: hsl(0, 0, 20%); + $text-shadow: adjust-color($base-color, $saturation: 10%, $lightness: 4%); + } + + @if $grayscale == true { + $border: grayscale($border); + $inset-shadow: grayscale($inset-shadow); + $stop-gradient: grayscale($stop-gradient); + $text-shadow: grayscale($text-shadow); + } + + border: 1px solid $border; + border-radius: 3px; + box-shadow: inset 0 1px 0 0 $inset-shadow; + color: $color; + display: inline-block; + font-size: $textsize; + font-weight: bold; + @include linear-gradient ($base-color, $stop-gradient); + padding: $padding; + text-decoration: none; + text-shadow: 0 1px 0 $text-shadow; + background-clip: padding-box; + + &:hover:not(:disabled) { + $base-color-hover: adjust-color($base-color, $saturation: -4%, $lightness: -5%); + $inset-shadow-hover: adjust-color($base-color, $saturation: -7%, $lightness: 5%); + $stop-gradient-hover: adjust-color($base-color, $saturation: 8%, $lightness: -14%); + + @if $grayscale == true { + $base-color-hover: grayscale($base-color-hover); + $inset-shadow-hover: grayscale($inset-shadow-hover); + $stop-gradient-hover: grayscale($stop-gradient-hover); + } + + @include linear-gradient ($base-color-hover, $stop-gradient-hover); + + box-shadow: inset 0 1px 0 0 $inset-shadow-hover; + cursor: pointer; + } + + &:active:not(:disabled), + &:focus:not(:disabled) { + $border-active: adjust-color($base-color, $saturation: 9%, $lightness: -14%); + $inset-shadow-active: adjust-color($base-color, $saturation: 7%, $lightness: -17%); + + @if $grayscale == true { + $border-active: grayscale($border-active); + $inset-shadow-active: grayscale($inset-shadow-active); + } + + border: 1px solid $border-active; + box-shadow: inset 0 0 8px 4px $inset-shadow-active, inset 0 0 8px 4px $inset-shadow-active; + } +} + +// Shiny Button +@mixin shiny($base-color, $grayscale: false, $textsize: inherit, $padding: 7px 18px) { + $color: hsl(0, 0, 100%); + $border: adjust-color($base-color, $red: -117, $green: -111, $blue: -81); + $border-bottom: adjust-color($base-color, $red: -126, $green: -127, $blue: -122); + $fourth-stop: adjust-color($base-color, $red: -79, $green: -70, $blue: -46); + $inset-shadow: adjust-color($base-color, $red: 37, $green: 29, $blue: 12); + $second-stop: adjust-color($base-color, $red: -56, $green: -50, $blue: -33); + $text-shadow: adjust-color($base-color, $red: -140, $green: -141, $blue: -114); + $third-stop: adjust-color($base-color, $red: -86, $green: -75, $blue: -48); + + @if is-light($base-color) { + $color: hsl(0, 0, 20%); + $text-shadow: adjust-color($base-color, $saturation: 10%, $lightness: 4%); + } + + @if $grayscale == true { + $border: grayscale($border); + $border-bottom: grayscale($border-bottom); + $fourth-stop: grayscale($fourth-stop); + $inset-shadow: grayscale($inset-shadow); + $second-stop: grayscale($second-stop); + $text-shadow: grayscale($text-shadow); + $third-stop: grayscale($third-stop); + } + + @include linear-gradient(top, $base-color 0%, $second-stop 50%, $third-stop 50%, $fourth-stop 100%); + + border: 1px solid $border; + border-bottom: 1px solid $border-bottom; + border-radius: 5px; + box-shadow: inset 0 1px 0 0 $inset-shadow; + color: $color; + display: inline-block; + font-size: $textsize; + font-weight: bold; + padding: $padding; + text-align: center; + text-decoration: none; + text-shadow: 0 -1px 1px $text-shadow; + + &:hover:not(:disabled) { + $first-stop-hover: adjust-color($base-color, $red: -13, $green: -15, $blue: -18); + $second-stop-hover: adjust-color($base-color, $red: -66, $green: -62, $blue: -51); + $third-stop-hover: adjust-color($base-color, $red: -93, $green: -85, $blue: -66); + $fourth-stop-hover: adjust-color($base-color, $red: -86, $green: -80, $blue: -63); + + @if $grayscale == true { + $first-stop-hover: grayscale($first-stop-hover); + $second-stop-hover: grayscale($second-stop-hover); + $third-stop-hover: grayscale($third-stop-hover); + $fourth-stop-hover: grayscale($fourth-stop-hover); + } + + @include linear-gradient(top, $first-stop-hover 0%, + $second-stop-hover 50%, + $third-stop-hover 50%, + $fourth-stop-hover 100%); + cursor: pointer; + } + + &:active:not(:disabled), + &:focus:not(:disabled) { + $inset-shadow-active: adjust-color($base-color, $red: -111, $green: -116, $blue: -122); + + @if $grayscale == true { + $inset-shadow-active: grayscale($inset-shadow-active); + } + + box-shadow: inset 0 0 20px 0 $inset-shadow-active; + } +} + +// Pill Button +@mixin pill($base-color, $grayscale: false, $textsize: inherit, $padding: 7px 18px) { + $color: hsl(0, 0, 100%); + $border-bottom: adjust-color($base-color, $hue: 8, $saturation: -11%, $lightness: -26%); + $border-sides: adjust-color($base-color, $hue: 4, $saturation: -21%, $lightness: -21%); + $border-top: adjust-color($base-color, $hue: -1, $saturation: -30%, $lightness: -15%); + $inset-shadow: adjust-color($base-color, $hue: -1, $saturation: -1%, $lightness: 7%); + $stop-gradient: adjust-color($base-color, $hue: 8, $saturation: 14%, $lightness: -10%); + $text-shadow: adjust-color($base-color, $hue: 5, $saturation: -19%, $lightness: -15%); + + @if is-light($base-color) { + $color: hsl(0, 0, 20%); + $text-shadow: adjust-color($base-color, $saturation: 10%, $lightness: 4%); + } + + @if $grayscale == true { + $border-bottom: grayscale($border-bottom); + $border-sides: grayscale($border-sides); + $border-top: grayscale($border-top); + $inset-shadow: grayscale($inset-shadow); + $stop-gradient: grayscale($stop-gradient); + $text-shadow: grayscale($text-shadow); + } + + border: 1px solid $border-top; + border-color: $border-top $border-sides $border-bottom; + border-radius: 16px; + box-shadow: inset 0 1px 0 0 $inset-shadow; + color: $color; + display: inline-block; + font-size: $textsize; + font-weight: normal; + line-height: 1; + @include linear-gradient ($base-color, $stop-gradient); + padding: $padding; + text-align: center; + text-decoration: none; + text-shadow: 0 -1px 1px $text-shadow; + background-clip: padding-box; + + &:hover:not(:disabled) { + $base-color-hover: adjust-color($base-color, $lightness: -4.5%); + $border-bottom: adjust-color($base-color, $hue: 8, $saturation: 13.5%, $lightness: -32%); + $border-sides: adjust-color($base-color, $hue: 4, $saturation: -2%, $lightness: -27%); + $border-top: adjust-color($base-color, $hue: -1, $saturation: -17%, $lightness: -21%); + $inset-shadow-hover: adjust-color($base-color, $saturation: -1%, $lightness: 3%); + $stop-gradient-hover: adjust-color($base-color, $hue: 8, $saturation: -4%, $lightness: -15.5%); + $text-shadow-hover: adjust-color($base-color, $hue: 5, $saturation: -5%, $lightness: -22%); + + @if $grayscale == true { + $base-color-hover: grayscale($base-color-hover); + $border-bottom: grayscale($border-bottom); + $border-sides: grayscale($border-sides); + $border-top: grayscale($border-top); + $inset-shadow-hover: grayscale($inset-shadow-hover); + $stop-gradient-hover: grayscale($stop-gradient-hover); + $text-shadow-hover: grayscale($text-shadow-hover); + } + + @include linear-gradient ($base-color-hover, $stop-gradient-hover); + + background-clip: padding-box; + border: 1px solid $border-top; + border-color: $border-top $border-sides $border-bottom; + box-shadow: inset 0 1px 0 0 $inset-shadow-hover; + cursor: pointer; + text-shadow: 0 -1px 1px $text-shadow-hover; + } + + &:active:not(:disabled), + &:focus:not(:disabled) { + $active-color: adjust-color($base-color, $hue: 4, $saturation: -12%, $lightness: -10%); + $border-active: adjust-color($base-color, $hue: 6, $saturation: -2.5%, $lightness: -30%); + $border-bottom-active: adjust-color($base-color, $hue: 11, $saturation: 6%, $lightness: -31%); + $inset-shadow-active: adjust-color($base-color, $hue: 9, $saturation: 2%, $lightness: -21.5%); + $text-shadow-active: adjust-color($base-color, $hue: 5, $saturation: -12%, $lightness: -21.5%); + + @if $grayscale == true { + $active-color: grayscale($active-color); + $border-active: grayscale($border-active); + $border-bottom-active: grayscale($border-bottom-active); + $inset-shadow-active: grayscale($inset-shadow-active); + $text-shadow-active: grayscale($text-shadow-active); + } + + background: $active-color; + border: 1px solid $border-active; + border-bottom: 1px solid $border-bottom-active; + box-shadow: inset 0 0 6px 3px $inset-shadow-active; + text-shadow: 0 -1px 1px $text-shadow-active; + } +} + +// Flat Button +@mixin flat($base-color, $grayscale: false, $textsize: inherit, $padding: 7px 18px) { + $color: hsl(0, 0, 100%); + + @if is-light($base-color) { + $color: hsl(0, 0, 20%); + } + + background-color: $base-color; + border-radius: 3px; + border: 0; + color: $color; + display: inline-block; + font-size: $textsize; + font-weight: bold; + padding: $padding; + text-decoration: none; + background-clip: padding-box; + + &:hover:not(:disabled){ + $base-color-hover: adjust-color($base-color, $saturation: 4%, $lightness: 5%); + + @if $grayscale == true { + $base-color-hover: grayscale($base-color-hover); + } + + background-color: $base-color-hover; + cursor: pointer; + } + + &:active:not(:disabled), + &:focus:not(:disabled) { + $base-color-active: adjust-color($base-color, $saturation: -4%, $lightness: -5%); + + @if $grayscale == true { + $base-color-active: grayscale($base-color-active); + } + + background-color: $base-color-active; + cursor: pointer; + } +} + +// Flexible grid +@function flex-grid($columns, $container-columns: $fg-max-columns) { + $width: $columns * $fg-column + ($columns - 1) * $fg-gutter; + $container-width: $container-columns * $fg-column + ($container-columns - 1) * $fg-gutter; + @return percentage($width / $container-width); + + @warn "The flex-grid function is deprecated and will be removed in the next major version release"; +} + +// Flexible gutter +@function flex-gutter($container-columns: $fg-max-columns, $gutter: $fg-gutter) { + $container-width: $container-columns * $fg-column + ($container-columns - 1) * $fg-gutter; + @return percentage($gutter / $container-width); + + @warn "The flex-gutter function is deprecated and will be removed in the next major version release"; +} + +@function grid-width($n) { + @return $n * $gw-column + ($n - 1) * $gw-gutter; + + @warn "The grid-width function is deprecated and will be removed in the next major version release"; +} + +@function golden-ratio($value, $increment) { + @return modular-scale($increment, $value, $ratio: $golden); + + @warn "The golden-ratio function is deprecated and will be removed in the next major version release. Please use the modular-scale function, instead."; +} + +@mixin box-sizing($box) { + @include prefixer(box-sizing, $box, webkit moz spec); + + @warn "The box-sizing mixin is deprecated and will be removed in the next major version release. This property can now be used un-prefixed."; +} diff --git a/src/styles/vendor/bourbon/_bourbon.scss b/src/styles/vendor/bourbon/_bourbon.scss new file mode 100644 index 0000000..509fcc1 --- /dev/null +++ b/src/styles/vendor/bourbon/_bourbon.scss @@ -0,0 +1,87 @@ +// Bourbon 4.2.6 +// http://bourbon.io +// Copyright 2011-2015 thoughtbot, inc. +// MIT License + +@import "settings/prefixer"; +@import "settings/px-to-em"; +@import "settings/asset-pipeline"; + +@import "functions/assign-inputs"; +@import "functions/contains"; +@import "functions/contains-falsy"; +@import "functions/is-length"; +@import "functions/is-light"; +@import "functions/is-number"; +@import "functions/is-size"; +@import "functions/px-to-em"; +@import "functions/px-to-rem"; +@import "functions/shade"; +@import "functions/strip-units"; +@import "functions/tint"; +@import "functions/transition-property-name"; +@import "functions/unpack"; +@import "functions/modular-scale"; + +@import "helpers/convert-units"; +@import "helpers/directional-values"; +@import "helpers/font-source-declaration"; +@import "helpers/gradient-positions-parser"; +@import "helpers/linear-angle-parser"; +@import "helpers/linear-gradient-parser"; +@import "helpers/linear-positions-parser"; +@import "helpers/linear-side-corner-parser"; +@import "helpers/radial-arg-parser"; +@import "helpers/radial-positions-parser"; +@import "helpers/radial-gradient-parser"; +@import "helpers/render-gradients"; +@import "helpers/shape-size-stripper"; +@import "helpers/str-to-num"; + +@import "css3/animation"; +@import "css3/appearance"; +@import "css3/backface-visibility"; +@import "css3/background"; +@import "css3/background-image"; +@import "css3/border-image"; +@import "css3/calc"; +@import "css3/columns"; +@import "css3/filter"; +@import "css3/flex-box"; +@import "css3/font-face"; +@import "css3/font-feature-settings"; +@import "css3/hidpi-media-query"; +@import "css3/hyphens"; +@import "css3/image-rendering"; +@import "css3/keyframes"; +@import "css3/linear-gradient"; +@import "css3/perspective"; +@import "css3/placeholder"; +@import "css3/radial-gradient"; +@import "css3/selection"; +@import "css3/text-decoration"; +@import "css3/transform"; +@import "css3/transition"; +@import "css3/user-select"; + +@import "addons/border-color"; +@import "addons/border-radius"; +@import "addons/border-style"; +@import "addons/border-width"; +@import "addons/buttons"; +@import "addons/clearfix"; +@import "addons/ellipsis"; +@import "addons/font-stacks"; +@import "addons/hide-text"; +@import "addons/margin"; +@import "addons/padding"; +@import "addons/position"; +@import "addons/prefixer"; +@import "addons/retina-image"; +@import "addons/size"; +@import "addons/text-inputs"; +@import "addons/timing-functions"; +@import "addons/triangle"; +@import "addons/word-wrap"; + +@import "bourbon-deprecated-upcoming"; diff --git a/src/styles/vendor/bourbon/addons/_border-color.scss b/src/styles/vendor/bourbon/addons/_border-color.scss new file mode 100644 index 0000000..6f6ab36 --- /dev/null +++ b/src/styles/vendor/bourbon/addons/_border-color.scss @@ -0,0 +1,26 @@ +@charset "UTF-8"; + +/// Provides a quick method for targeting `border-color` on specific sides of a box. Use a `null` value to “skip” a side. +/// +/// @param {Arglist} $vals +/// List of arguments +/// +/// @example scss - Usage +/// .element { +/// @include border-color(#a60b55 #76cd9c null #e8ae1a); +/// } +/// +/// @example css - CSS Output +/// .element { +/// border-left-color: #e8ae1a; +/// border-right-color: #76cd9c; +/// border-top-color: #a60b55; +/// } +/// +/// @require {mixin} directional-property +/// +/// @output `border-color` + +@mixin border-color($vals...) { + @include directional-property(border, color, $vals...); +} diff --git a/src/styles/vendor/bourbon/addons/_border-radius.scss b/src/styles/vendor/bourbon/addons/_border-radius.scss new file mode 100644 index 0000000..1f65863 --- /dev/null +++ b/src/styles/vendor/bourbon/addons/_border-radius.scss @@ -0,0 +1,48 @@ +@charset "UTF-8"; + +/// Provides a quick method for targeting `border-radius` on both corners on the side of a box. +/// +/// @param {Number} $radii +/// List of arguments +/// +/// @example scss - Usage +/// .element-one { +/// @include border-top-radius(5px); +/// } +/// +/// .element-two { +/// @include border-left-radius(3px); +/// } +/// +/// @example css - CSS Output +/// .element-one { +/// border-top-left-radius: 5px; +/// border-top-right-radius: 5px; +/// } +/// +/// .element-two { +/// border-bottom-left-radius: 3px; +/// border-top-left-radius: 3px; +/// } +/// +/// @output `border-radius` + +@mixin border-top-radius($radii) { + border-top-left-radius: $radii; + border-top-right-radius: $radii; +} + +@mixin border-right-radius($radii) { + border-bottom-right-radius: $radii; + border-top-right-radius: $radii; +} + +@mixin border-bottom-radius($radii) { + border-bottom-left-radius: $radii; + border-bottom-right-radius: $radii; +} + +@mixin border-left-radius($radii) { + border-bottom-left-radius: $radii; + border-top-left-radius: $radii; +} diff --git a/src/styles/vendor/bourbon/addons/_border-style.scss b/src/styles/vendor/bourbon/addons/_border-style.scss new file mode 100644 index 0000000..d86ee79 --- /dev/null +++ b/src/styles/vendor/bourbon/addons/_border-style.scss @@ -0,0 +1,25 @@ +@charset "UTF-8"; + +/// Provides a quick method for targeting `border-style` on specific sides of a box. Use a `null` value to “skip” a side. +/// +/// @param {Arglist} $vals +/// List of arguments +/// +/// @example scss - Usage +/// .element { +/// @include border-style(dashed null solid); +/// } +/// +/// @example css - CSS Output +/// .element { +/// border-bottom-style: solid; +/// border-top-style: dashed; +/// } +/// +/// @require {mixin} directional-property +/// +/// @output `border-style` + +@mixin border-style($vals...) { + @include directional-property(border, style, $vals...); +} diff --git a/src/styles/vendor/bourbon/addons/_border-width.scss b/src/styles/vendor/bourbon/addons/_border-width.scss new file mode 100644 index 0000000..0ea2d4b --- /dev/null +++ b/src/styles/vendor/bourbon/addons/_border-width.scss @@ -0,0 +1,25 @@ +@charset "UTF-8"; + +/// Provides a quick method for targeting `border-width` on specific sides of a box. Use a `null` value to “skip” a side. +/// +/// @param {Arglist} $vals +/// List of arguments +/// +/// @example scss - Usage +/// .element { +/// @include border-width(1em null 20px); +/// } +/// +/// @example css - CSS Output +/// .element { +/// border-bottom-width: 20px; +/// border-top-width: 1em; +/// } +/// +/// @require {mixin} directional-property +/// +/// @output `border-width` + +@mixin border-width($vals...) { + @include directional-property(border, width, $vals...); +} diff --git a/src/styles/vendor/bourbon/addons/_buttons.scss b/src/styles/vendor/bourbon/addons/_buttons.scss new file mode 100644 index 0000000..debeabc --- /dev/null +++ b/src/styles/vendor/bourbon/addons/_buttons.scss @@ -0,0 +1,64 @@ +@charset "UTF-8"; + +/// Generates variables for all buttons. Please note that you must use interpolation on the variable: `#{$all-buttons}`. +/// +/// @example scss - Usage +/// #{$all-buttons} { +/// background-color: #f00; +/// } +/// +/// #{$all-buttons-focus}, +/// #{$all-buttons-hover} { +/// background-color: #0f0; +/// } +/// +/// #{$all-buttons-active} { +/// background-color: #00f; +/// } +/// +/// @example css - CSS Output +/// button, +/// input[type="button"], +/// input[type="reset"], +/// input[type="submit"] { +/// background-color: #f00; +/// } +/// +/// button:focus, +/// input[type="button"]:focus, +/// input[type="reset"]:focus, +/// input[type="submit"]:focus, +/// button:hover, +/// input[type="button"]:hover, +/// input[type="reset"]:hover, +/// input[type="submit"]:hover { +/// background-color: #0f0; +/// } +/// +/// button:active, +/// input[type="button"]:active, +/// input[type="reset"]:active, +/// input[type="submit"]:active { +/// background-color: #00f; +/// } +/// +/// @require assign-inputs +/// +/// @type List +/// +/// @todo Remove double assigned variables (Lines 59–62) in v5.0.0 + +$buttons-list: 'button', + 'input[type="button"]', + 'input[type="reset"]', + 'input[type="submit"]'; + +$all-buttons: assign-inputs($buttons-list); +$all-buttons-active: assign-inputs($buttons-list, active); +$all-buttons-focus: assign-inputs($buttons-list, focus); +$all-buttons-hover: assign-inputs($buttons-list, hover); + +$all-button-inputs: $all-buttons; +$all-button-inputs-active: $all-buttons-active; +$all-button-inputs-focus: $all-buttons-focus; +$all-button-inputs-hover: $all-buttons-hover; diff --git a/src/styles/vendor/bourbon/addons/_clearfix.scss b/src/styles/vendor/bourbon/addons/_clearfix.scss new file mode 100644 index 0000000..11313d6 --- /dev/null +++ b/src/styles/vendor/bourbon/addons/_clearfix.scss @@ -0,0 +1,25 @@ +@charset "UTF-8"; + +/// Provides an easy way to include a clearfix for containing floats. +/// +/// @link http://cssmojo.com/latest_new_clearfix_so_far/ +/// +/// @example scss - Usage +/// .element { +/// @include clearfix; +/// } +/// +/// @example css - CSS Output +/// .element::after { +/// clear: both; +/// content: ""; +/// display: table; +/// } + +@mixin clearfix { + &::after { + clear: both; + content: ""; + display: table; + } +} diff --git a/src/styles/vendor/bourbon/addons/_ellipsis.scss b/src/styles/vendor/bourbon/addons/_ellipsis.scss new file mode 100644 index 0000000..a367f65 --- /dev/null +++ b/src/styles/vendor/bourbon/addons/_ellipsis.scss @@ -0,0 +1,30 @@ +@charset "UTF-8"; + +/// Truncates text and adds an ellipsis to represent overflow. +/// +/// @param {Number} $width [100%] +/// Max-width for the string to respect before being truncated +/// +/// @example scss - Usage +/// .element { +/// @include ellipsis; +/// } +/// +/// @example css - CSS Output +/// .element { +/// display: inline-block; +/// max-width: 100%; +/// overflow: hidden; +/// text-overflow: ellipsis; +/// white-space: nowrap; +/// word-wrap: normal; +/// } + +@mixin ellipsis($width: 100%) { + display: inline-block; + max-width: $width; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + word-wrap: normal; +} diff --git a/src/styles/vendor/bourbon/addons/_font-stacks.scss b/src/styles/vendor/bourbon/addons/_font-stacks.scss new file mode 100644 index 0000000..57128f4 --- /dev/null +++ b/src/styles/vendor/bourbon/addons/_font-stacks.scss @@ -0,0 +1,31 @@ +@charset "UTF-8"; + +/// Georgia font stack. +/// +/// @type List + +$georgia: "Georgia", "Cambria", "Times New Roman", "Times", serif; + +/// Helvetica font stack. +/// +/// @type List + +$helvetica: "Helvetica Neue", "Helvetica", "Roboto", "Arial", sans-serif; + +/// Lucida Grande font stack. +/// +/// @type List + +$lucida-grande: "Lucida Grande", "Tahoma", "Verdana", "Arial", sans-serif; + +/// Monospace font stack. +/// +/// @type List + +$monospace: "Bitstream Vera Sans Mono", "Consolas", "Courier", monospace; + +/// Verdana font stack. +/// +/// @type List + +$verdana: "Verdana", "Geneva", sans-serif; diff --git a/src/styles/vendor/bourbon/addons/_hide-text.scss b/src/styles/vendor/bourbon/addons/_hide-text.scss new file mode 100644 index 0000000..4caf20e --- /dev/null +++ b/src/styles/vendor/bourbon/addons/_hide-text.scss @@ -0,0 +1,27 @@ +/// Hides the text in an element, commonly used to show an image. Some elements will need block-level styles applied. +/// +/// @link http://zeldman.com/2012/03/01/replacing-the-9999px-hack-new-image-replacement +/// +/// @example scss - Usage +/// .element { +/// @include hide-text; +/// } +/// +/// @example css - CSS Output +/// .element { +/// overflow: hidden; +/// text-indent: 101%; +/// white-space: nowrap; +/// } +/// +/// @todo Remove height argument in v5.0.0 + +@mixin hide-text($height: null) { + overflow: hidden; + text-indent: 101%; + white-space: nowrap; + + @if $height { + @warn "The `hide-text` mixin has changed and no longer requires a height. The height argument will no longer be accepted in v5.0.0"; + } +} diff --git a/src/styles/vendor/bourbon/addons/_margin.scss b/src/styles/vendor/bourbon/addons/_margin.scss new file mode 100644 index 0000000..674f4e5 --- /dev/null +++ b/src/styles/vendor/bourbon/addons/_margin.scss @@ -0,0 +1,26 @@ +@charset "UTF-8"; + +/// Provides a quick method for targeting `margin` on specific sides of a box. Use a `null` value to “skip” a side. +/// +/// @param {Arglist} $vals +/// List of arguments +/// +/// @example scss - Usage +/// .element { +/// @include margin(null 10px 3em 20vh); +/// } +/// +/// @example css - CSS Output +/// .element { +/// margin-bottom: 3em; +/// margin-left: 20vh; +/// margin-right: 10px; +/// } +/// +/// @require {mixin} directional-property +/// +/// @output `margin` + +@mixin margin($vals...) { + @include directional-property(margin, false, $vals...); +} diff --git a/src/styles/vendor/bourbon/addons/_padding.scss b/src/styles/vendor/bourbon/addons/_padding.scss new file mode 100644 index 0000000..40a5f00 --- /dev/null +++ b/src/styles/vendor/bourbon/addons/_padding.scss @@ -0,0 +1,26 @@ +@charset "UTF-8"; + +/// Provides a quick method for targeting `padding` on specific sides of a box. Use a `null` value to “skip” a side. +/// +/// @param {Arglist} $vals +/// List of arguments +/// +/// @example scss - Usage +/// .element { +/// @include padding(12vh null 10px 5%); +/// } +/// +/// @example css - CSS Output +/// .element { +/// padding-bottom: 10px; +/// padding-left: 5%; +/// padding-top: 12vh; +/// } +/// +/// @require {mixin} directional-property +/// +/// @output `padding` + +@mixin padding($vals...) { + @include directional-property(padding, false, $vals...); +} diff --git a/src/styles/vendor/bourbon/addons/_position.scss b/src/styles/vendor/bourbon/addons/_position.scss new file mode 100644 index 0000000..e460f3f --- /dev/null +++ b/src/styles/vendor/bourbon/addons/_position.scss @@ -0,0 +1,48 @@ +@charset "UTF-8"; + +/// Provides a quick method for setting an element’s position. Use a `null` value to “skip” a side. +/// +/// @param {Position} $position [relative] +/// A CSS position value +/// +/// @param {Arglist} $coordinates [null null null null] +/// List of values that correspond to the 4-value syntax for the edges of a box +/// +/// @example scss - Usage +/// .element { +/// @include position(absolute, 0 null null 10em); +/// } +/// +/// @example css - CSS Output +/// .element { +/// left: 10em; +/// position: absolute; +/// top: 0; +/// } +/// +/// @require {function} is-length +/// @require {function} unpack + +@mixin position($position: relative, $coordinates: null null null null) { + @if type-of($position) == list { + $coordinates: $position; + $position: relative; + } + + $coordinates: unpack($coordinates); + + $offsets: ( + top: nth($coordinates, 1), + right: nth($coordinates, 2), + bottom: nth($coordinates, 3), + left: nth($coordinates, 4) + ); + + position: $position; + + @each $offset, $value in $offsets { + @if is-length($value) { + #{$offset}: $value; + } + } +} diff --git a/src/styles/vendor/bourbon/addons/_prefixer.scss b/src/styles/vendor/bourbon/addons/_prefixer.scss new file mode 100644 index 0000000..2b6f731 --- /dev/null +++ b/src/styles/vendor/bourbon/addons/_prefixer.scss @@ -0,0 +1,66 @@ +@charset "UTF-8"; + +/// A mixin for generating vendor prefixes on non-standardized properties. +/// +/// @param {String} $property +/// Property to prefix +/// +/// @param {*} $value +/// Value to use +/// +/// @param {List} $prefixes +/// Prefixes to define +/// +/// @example scss - Usage +/// .element { +/// @include prefixer(border-radius, 10px, webkit ms spec); +/// } +/// +/// @example css - CSS Output +/// .element { +/// -webkit-border-radius: 10px; +/// -moz-border-radius: 10px; +/// border-radius: 10px; +/// } +/// +/// @require {variable} $prefix-for-webkit +/// @require {variable} $prefix-for-mozilla +/// @require {variable} $prefix-for-microsoft +/// @require {variable} $prefix-for-opera +/// @require {variable} $prefix-for-spec + +@mixin prefixer($property, $value, $prefixes) { + @each $prefix in $prefixes { + @if $prefix == webkit { + @if $prefix-for-webkit { + -webkit-#{$property}: $value; + } + } @else if $prefix == moz { + @if $prefix-for-mozilla { + -moz-#{$property}: $value; + } + } @else if $prefix == ms { + @if $prefix-for-microsoft { + -ms-#{$property}: $value; + } + } @else if $prefix == o { + @if $prefix-for-opera { + -o-#{$property}: $value; + } + } @else if $prefix == spec { + @if $prefix-for-spec { + #{$property}: $value; + } + } @else { + @warn "Unrecognized prefix: #{$prefix}"; + } + } +} + +@mixin disable-prefix-for-all() { + $prefix-for-webkit: false !global; + $prefix-for-mozilla: false !global; + $prefix-for-microsoft: false !global; + $prefix-for-opera: false !global; + $prefix-for-spec: false !global; +} diff --git a/src/styles/vendor/bourbon/addons/_retina-image.scss b/src/styles/vendor/bourbon/addons/_retina-image.scss new file mode 100644 index 0000000..7febbd7 --- /dev/null +++ b/src/styles/vendor/bourbon/addons/_retina-image.scss @@ -0,0 +1,25 @@ +@mixin retina-image($filename, $background-size, $extension: png, $retina-filename: null, $retina-suffix: _2x, $asset-pipeline: $asset-pipeline) { + @if $asset-pipeline { + background-image: image-url("#{$filename}.#{$extension}"); + } @else { + background-image: url("#{$filename}.#{$extension}"); + } + + @include hidpi { + @if $asset-pipeline { + @if $retina-filename { + background-image: image-url("#{$retina-filename}.#{$extension}"); + } @else { + background-image: image-url("#{$filename}#{$retina-suffix}.#{$extension}"); + } + } @else { + @if $retina-filename { + background-image: url("#{$retina-filename}.#{$extension}"); + } @else { + background-image: url("#{$filename}#{$retina-suffix}.#{$extension}"); + } + } + + background-size: $background-size; + } +} diff --git a/src/styles/vendor/bourbon/addons/_size.scss b/src/styles/vendor/bourbon/addons/_size.scss new file mode 100644 index 0000000..a2992a3 --- /dev/null +++ b/src/styles/vendor/bourbon/addons/_size.scss @@ -0,0 +1,51 @@ +@charset "UTF-8"; + +/// Sets the `width` and `height` of the element. +/// +/// @param {List} $size +/// A list of at most 2 size values. +/// +/// If there is only a single value in `$size` it is used for both width and height. All units are supported. +/// +/// @example scss - Usage +/// .first-element { +/// @include size(2em); +/// } +/// +/// .second-element { +/// @include size(auto 10em); +/// } +/// +/// @example css - CSS Output +/// .first-element { +/// width: 2em; +/// height: 2em; +/// } +/// +/// .second-element { +/// width: auto; +/// height: 10em; +/// } +/// +/// @todo Refactor in 5.0.0 to use a comma-separated argument + +@mixin size($value) { + $width: nth($value, 1); + $height: $width; + + @if length($value) > 1 { + $height: nth($value, 2); + } + + @if is-size($height) { + height: $height; + } @else { + @warn "`#{$height}` is not a valid length for the `$height` parameter in the `size` mixin."; + } + + @if is-size($width) { + width: $width; + } @else { + @warn "`#{$width}` is not a valid length for the `$width` parameter in the `size` mixin."; + } +} diff --git a/src/styles/vendor/bourbon/addons/_text-inputs.scss b/src/styles/vendor/bourbon/addons/_text-inputs.scss new file mode 100644 index 0000000..1eb7a54 --- /dev/null +++ b/src/styles/vendor/bourbon/addons/_text-inputs.scss @@ -0,0 +1,113 @@ +@charset "UTF-8"; + +/// Generates variables for all text-based inputs. Please note that you must use interpolation on the variable: `#{$all-text-inputs}`. +/// +/// @example scss - Usage +/// #{$all-text-inputs} { +/// border: 1px solid #f00; +/// } +/// +/// #{$all-text-inputs-focus}, +/// #{$all-text-inputs-hover} { +/// border: 1px solid #0f0; +/// } +/// +/// #{$all-text-inputs-active} { +/// border: 1px solid #00f; +/// } +/// +/// @example css - CSS Output +/// input[type="color"], +/// input[type="date"], +/// input[type="datetime"], +/// input[type="datetime-local"], +/// input[type="email"], +/// input[type="month"], +/// input[type="number"], +/// input[type="password"], +/// input[type="search"], +/// input[type="tel"], +/// input[type="text"], +/// input[type="time"], +/// input[type="url"], +/// input[type="week"], +/// textarea { +/// border: 1px solid #f00; +/// } +/// +/// input[type="color"]:focus, +/// input[type="date"]:focus, +/// input[type="datetime"]:focus, +/// input[type="datetime-local"]:focus, +/// input[type="email"]:focus, +/// input[type="month"]:focus, +/// input[type="number"]:focus, +/// input[type="password"]:focus, +/// input[type="search"]:focus, +/// input[type="tel"]:focus, +/// input[type="text"]:focus, +/// input[type="time"]:focus, +/// input[type="url"]:focus, +/// input[type="week"]:focus, +/// textarea:focus, +/// input[type="color"]:hover, +/// input[type="date"]:hover, +/// input[type="datetime"]:hover, +/// input[type="datetime-local"]:hover, +/// input[type="email"]:hover, +/// input[type="month"]:hover, +/// input[type="number"]:hover, +/// input[type="password"]:hover, +/// input[type="search"]:hover, +/// input[type="tel"]:hover, +/// input[type="text"]:hover, +/// input[type="time"]:hover, +/// input[type="url"]:hover, +/// input[type="week"]:hover, +/// textarea:hover { +/// border: 1px solid #0f0; +/// } +/// +/// input[type="color"]:active, +/// input[type="date"]:active, +/// input[type="datetime"]:active, +/// input[type="datetime-local"]:active, +/// input[type="email"]:active, +/// input[type="month"]:active, +/// input[type="number"]:active, +/// input[type="password"]:active, +/// input[type="search"]:active, +/// input[type="tel"]:active, +/// input[type="text"]:active, +/// input[type="time"]:active, +/// input[type="url"]:active, +/// input[type="week"]:active, +/// textarea:active { +/// border: 1px solid #00f; +/// } +/// +/// @require assign-inputs +/// +/// @type List + +$text-inputs-list: 'input[type="color"]', + 'input[type="date"]', + 'input[type="datetime"]', + 'input[type="datetime-local"]', + 'input[type="email"]', + 'input[type="month"]', + 'input[type="number"]', + 'input[type="password"]', + 'input[type="search"]', + 'input[type="tel"]', + 'input[type="text"]', + 'input[type="time"]', + 'input[type="url"]', + 'input[type="week"]', + 'input:not([type])', + 'textarea'; + +$all-text-inputs: assign-inputs($text-inputs-list); +$all-text-inputs-active: assign-inputs($text-inputs-list, active); +$all-text-inputs-focus: assign-inputs($text-inputs-list, focus); +$all-text-inputs-hover: assign-inputs($text-inputs-list, hover); diff --git a/src/styles/vendor/bourbon/addons/_timing-functions.scss b/src/styles/vendor/bourbon/addons/_timing-functions.scss new file mode 100644 index 0000000..20e5f1d --- /dev/null +++ b/src/styles/vendor/bourbon/addons/_timing-functions.scss @@ -0,0 +1,34 @@ +@charset "UTF-8"; + +/// CSS cubic-bezier timing functions. Timing functions courtesy of jquery.easie (github.com/jaukia/easie) +/// +/// Timing functions are the same as demoed here: http://jqueryui.com/resources/demos/effect/easing.html +/// +/// @type cubic-bezier + +$ease-in-quad: cubic-bezier(0.550, 0.085, 0.680, 0.530); +$ease-in-cubic: cubic-bezier(0.550, 0.055, 0.675, 0.190); +$ease-in-quart: cubic-bezier(0.895, 0.030, 0.685, 0.220); +$ease-in-quint: cubic-bezier(0.755, 0.050, 0.855, 0.060); +$ease-in-sine: cubic-bezier(0.470, 0.000, 0.745, 0.715); +$ease-in-expo: cubic-bezier(0.950, 0.050, 0.795, 0.035); +$ease-in-circ: cubic-bezier(0.600, 0.040, 0.980, 0.335); +$ease-in-back: cubic-bezier(0.600, -0.280, 0.735, 0.045); + +$ease-out-quad: cubic-bezier(0.250, 0.460, 0.450, 0.940); +$ease-out-cubic: cubic-bezier(0.215, 0.610, 0.355, 1.000); +$ease-out-quart: cubic-bezier(0.165, 0.840, 0.440, 1.000); +$ease-out-quint: cubic-bezier(0.230, 1.000, 0.320, 1.000); +$ease-out-sine: cubic-bezier(0.390, 0.575, 0.565, 1.000); +$ease-out-expo: cubic-bezier(0.190, 1.000, 0.220, 1.000); +$ease-out-circ: cubic-bezier(0.075, 0.820, 0.165, 1.000); +$ease-out-back: cubic-bezier(0.175, 0.885, 0.320, 1.275); + +$ease-in-out-quad: cubic-bezier(0.455, 0.030, 0.515, 0.955); +$ease-in-out-cubic: cubic-bezier(0.645, 0.045, 0.355, 1.000); +$ease-in-out-quart: cubic-bezier(0.770, 0.000, 0.175, 1.000); +$ease-in-out-quint: cubic-bezier(0.860, 0.000, 0.070, 1.000); +$ease-in-out-sine: cubic-bezier(0.445, 0.050, 0.550, 0.950); +$ease-in-out-expo: cubic-bezier(1.000, 0.000, 0.000, 1.000); +$ease-in-out-circ: cubic-bezier(0.785, 0.135, 0.150, 0.860); +$ease-in-out-back: cubic-bezier(0.680, -0.550, 0.265, 1.550); diff --git a/src/styles/vendor/bourbon/addons/_triangle.scss b/src/styles/vendor/bourbon/addons/_triangle.scss new file mode 100644 index 0000000..8a1ed9c --- /dev/null +++ b/src/styles/vendor/bourbon/addons/_triangle.scss @@ -0,0 +1,63 @@ +@mixin triangle($size, $color, $direction) { + $width: nth($size, 1); + $height: nth($size, length($size)); + $foreground-color: nth($color, 1); + $background-color: if(length($color) == 2, nth($color, 2), transparent); + height: 0; + width: 0; + + @if ($direction == up) or ($direction == down) or ($direction == right) or ($direction == left) { + $width: $width / 2; + $height: if(length($size) > 1, $height, $height/2); + + @if $direction == up { + border-bottom: $height solid $foreground-color; + border-left: $width solid $background-color; + border-right: $width solid $background-color; + } @else if $direction == right { + border-bottom: $width solid $background-color; + border-left: $height solid $foreground-color; + border-top: $width solid $background-color; + } @else if $direction == down { + border-left: $width solid $background-color; + border-right: $width solid $background-color; + border-top: $height solid $foreground-color; + } @else if $direction == left { + border-bottom: $width solid $background-color; + border-right: $height solid $foreground-color; + border-top: $width solid $background-color; + } + } @else if ($direction == up-right) or ($direction == up-left) { + border-top: $height solid $foreground-color; + + @if $direction == up-right { + border-left: $width solid $background-color; + } @else if $direction == up-left { + border-right: $width solid $background-color; + } + } @else if ($direction == down-right) or ($direction == down-left) { + border-bottom: $height solid $foreground-color; + + @if $direction == down-right { + border-left: $width solid $background-color; + } @else if $direction == down-left { + border-right: $width solid $background-color; + } + } @else if ($direction == inset-up) { + border-color: $background-color $background-color $foreground-color; + border-style: solid; + border-width: $height $width; + } @else if ($direction == inset-down) { + border-color: $foreground-color $background-color $background-color; + border-style: solid; + border-width: $height $width; + } @else if ($direction == inset-right) { + border-color: $background-color $background-color $background-color $foreground-color; + border-style: solid; + border-width: $width $height; + } @else if ($direction == inset-left) { + border-color: $background-color $foreground-color $background-color $background-color; + border-style: solid; + border-width: $width $height; + } +} diff --git a/src/styles/vendor/bourbon/addons/_word-wrap.scss b/src/styles/vendor/bourbon/addons/_word-wrap.scss new file mode 100644 index 0000000..64856a9 --- /dev/null +++ b/src/styles/vendor/bourbon/addons/_word-wrap.scss @@ -0,0 +1,29 @@ +@charset "UTF-8"; + +/// Provides an easy way to change the `word-wrap` property. +/// +/// @param {String} $wrap [break-word] +/// Value for the `word-break` property. +/// +/// @example scss - Usage +/// .wrapper { +/// @include word-wrap(break-word); +/// } +/// +/// @example css - CSS Output +/// .wrapper { +/// overflow-wrap: break-word; +/// word-break: break-all; +/// word-wrap: break-word; +/// } + +@mixin word-wrap($wrap: break-word) { + overflow-wrap: $wrap; + word-wrap: $wrap; + + @if $wrap == break-word { + word-break: break-all; + } @else { + word-break: $wrap; + } +} diff --git a/src/styles/vendor/bourbon/css3/_animation.scss b/src/styles/vendor/bourbon/css3/_animation.scss new file mode 100644 index 0000000..aac675f --- /dev/null +++ b/src/styles/vendor/bourbon/css3/_animation.scss @@ -0,0 +1,43 @@ +// http://www.w3.org/TR/css3-animations/#the-animation-name-property- +// Each of these mixins support comma separated lists of values, which allows different transitions for individual properties to be described in a single style rule. Each value in the list corresponds to the value at that same position in the other properties. + +@mixin animation($animations...) { + @include prefixer(animation, $animations, webkit moz spec); +} + +@mixin animation-name($names...) { + @include prefixer(animation-name, $names, webkit moz spec); +} + +@mixin animation-duration($times...) { + @include prefixer(animation-duration, $times, webkit moz spec); +} + +@mixin animation-timing-function($motions...) { + // ease | linear | ease-in | ease-out | ease-in-out + @include prefixer(animation-timing-function, $motions, webkit moz spec); +} + +@mixin animation-iteration-count($values...) { + // infinite | + @include prefixer(animation-iteration-count, $values, webkit moz spec); +} + +@mixin animation-direction($directions...) { + // normal | alternate + @include prefixer(animation-direction, $directions, webkit moz spec); +} + +@mixin animation-play-state($states...) { + // running | paused + @include prefixer(animation-play-state, $states, webkit moz spec); +} + +@mixin animation-delay($times...) { + @include prefixer(animation-delay, $times, webkit moz spec); +} + +@mixin animation-fill-mode($modes...) { + // none | forwards | backwards | both + @include prefixer(animation-fill-mode, $modes, webkit moz spec); +} diff --git a/src/styles/vendor/bourbon/css3/_appearance.scss b/src/styles/vendor/bourbon/css3/_appearance.scss new file mode 100644 index 0000000..abddc02 --- /dev/null +++ b/src/styles/vendor/bourbon/css3/_appearance.scss @@ -0,0 +1,3 @@ +@mixin appearance($value) { + @include prefixer(appearance, $value, webkit moz ms o spec); +} diff --git a/src/styles/vendor/bourbon/css3/_backface-visibility.scss b/src/styles/vendor/bourbon/css3/_backface-visibility.scss new file mode 100644 index 0000000..fc68e2d --- /dev/null +++ b/src/styles/vendor/bourbon/css3/_backface-visibility.scss @@ -0,0 +1,3 @@ +@mixin backface-visibility($visibility) { + @include prefixer(backface-visibility, $visibility, webkit spec); +} diff --git a/src/styles/vendor/bourbon/css3/_background-image.scss b/src/styles/vendor/bourbon/css3/_background-image.scss new file mode 100644 index 0000000..6ed19ab --- /dev/null +++ b/src/styles/vendor/bourbon/css3/_background-image.scss @@ -0,0 +1,42 @@ +//************************************************************************// +// Background-image property for adding multiple background images with +// gradients, or for stringing multiple gradients together. +//************************************************************************// + +@mixin background-image($images...) { + $webkit-images: (); + $spec-images: (); + + @each $image in $images { + $webkit-image: (); + $spec-image: (); + + @if (type-of($image) == string) { + $url-str: str-slice($image, 1, 3); + $gradient-type: str-slice($image, 1, 6); + + @if $url-str == "url" { + $webkit-image: $image; + $spec-image: $image; + } + + @else if $gradient-type == "linear" { + $gradients: _linear-gradient-parser($image); + $webkit-image: map-get($gradients, webkit-image); + $spec-image: map-get($gradients, spec-image); + } + + @else if $gradient-type == "radial" { + $gradients: _radial-gradient-parser($image); + $webkit-image: map-get($gradients, webkit-image); + $spec-image: map-get($gradients, spec-image); + } + } + + $webkit-images: append($webkit-images, $webkit-image, comma); + $spec-images: append($spec-images, $spec-image, comma); + } + + background-image: $webkit-images; + background-image: $spec-images; +} diff --git a/src/styles/vendor/bourbon/css3/_background.scss b/src/styles/vendor/bourbon/css3/_background.scss new file mode 100644 index 0000000..019db0e --- /dev/null +++ b/src/styles/vendor/bourbon/css3/_background.scss @@ -0,0 +1,55 @@ +//************************************************************************// +// Background property for adding multiple backgrounds using shorthand +// notation. +//************************************************************************// + +@mixin background($backgrounds...) { + $webkit-backgrounds: (); + $spec-backgrounds: (); + + @each $background in $backgrounds { + $webkit-background: (); + $spec-background: (); + $background-type: type-of($background); + + @if $background-type == string or $background-type == list { + $background-str: if($background-type == list, nth($background, 1), $background); + + $url-str: str-slice($background-str, 1, 3); + $gradient-type: str-slice($background-str, 1, 6); + + @if $url-str == "url" { + $webkit-background: $background; + $spec-background: $background; + } + + @else if $gradient-type == "linear" { + $gradients: _linear-gradient-parser("#{$background}"); + $webkit-background: map-get($gradients, webkit-image); + $spec-background: map-get($gradients, spec-image); + } + + @else if $gradient-type == "radial" { + $gradients: _radial-gradient-parser("#{$background}"); + $webkit-background: map-get($gradients, webkit-image); + $spec-background: map-get($gradients, spec-image); + } + + @else { + $webkit-background: $background; + $spec-background: $background; + } + } + + @else { + $webkit-background: $background; + $spec-background: $background; + } + + $webkit-backgrounds: append($webkit-backgrounds, $webkit-background, comma); + $spec-backgrounds: append($spec-backgrounds, $spec-background, comma); + } + + background: $webkit-backgrounds; + background: $spec-backgrounds; +} diff --git a/src/styles/vendor/bourbon/css3/_border-image.scss b/src/styles/vendor/bourbon/css3/_border-image.scss new file mode 100644 index 0000000..cf568ce --- /dev/null +++ b/src/styles/vendor/bourbon/css3/_border-image.scss @@ -0,0 +1,59 @@ +@mixin border-image($borders...) { + $webkit-borders: (); + $spec-borders: (); + + @each $border in $borders { + $webkit-border: (); + $spec-border: (); + $border-type: type-of($border); + + @if $border-type == string or list { + $border-str: if($border-type == list, nth($border, 1), $border); + + $url-str: str-slice($border-str, 1, 3); + $gradient-type: str-slice($border-str, 1, 6); + + @if $url-str == "url" { + $webkit-border: $border; + $spec-border: $border; + } + + @else if $gradient-type == "linear" { + $gradients: _linear-gradient-parser("#{$border}"); + $webkit-border: map-get($gradients, webkit-image); + $spec-border: map-get($gradients, spec-image); + } + + @else if $gradient-type == "radial" { + $gradients: _radial-gradient-parser("#{$border}"); + $webkit-border: map-get($gradients, webkit-image); + $spec-border: map-get($gradients, spec-image); + } + + @else { + $webkit-border: $border; + $spec-border: $border; + } + } + + @else { + $webkit-border: $border; + $spec-border: $border; + } + + $webkit-borders: append($webkit-borders, $webkit-border, comma); + $spec-borders: append($spec-borders, $spec-border, comma); + } + + -webkit-border-image: $webkit-borders; + border-image: $spec-borders; + border-style: solid; +} + +//Examples: +// @include border-image(url("image.png")); +// @include border-image(url("image.png") 20 stretch); +// @include border-image(linear-gradient(45deg, orange, yellow)); +// @include border-image(linear-gradient(45deg, orange, yellow) stretch); +// @include border-image(linear-gradient(45deg, orange, yellow) 20 30 40 50 stretch round); +// @include border-image(radial-gradient(top, cover, orange, yellow, orange)); diff --git a/src/styles/vendor/bourbon/css3/_calc.scss b/src/styles/vendor/bourbon/css3/_calc.scss new file mode 100644 index 0000000..0bfc738 --- /dev/null +++ b/src/styles/vendor/bourbon/css3/_calc.scss @@ -0,0 +1,4 @@ +@mixin calc($property, $value) { + #{$property}: -webkit-calc(#{$value}); + #{$property}: calc(#{$value}); +} diff --git a/src/styles/vendor/bourbon/css3/_columns.scss b/src/styles/vendor/bourbon/css3/_columns.scss new file mode 100644 index 0000000..9611767 --- /dev/null +++ b/src/styles/vendor/bourbon/css3/_columns.scss @@ -0,0 +1,47 @@ +@mixin columns($arg: auto) { + // || + @include prefixer(columns, $arg, webkit moz spec); +} + +@mixin column-count($int: auto) { + // auto || integer + @include prefixer(column-count, $int, webkit moz spec); +} + +@mixin column-gap($length: normal) { + // normal || length + @include prefixer(column-gap, $length, webkit moz spec); +} + +@mixin column-fill($arg: auto) { + // auto || length + @include prefixer(column-fill, $arg, webkit moz spec); +} + +@mixin column-rule($arg) { + // || || + @include prefixer(column-rule, $arg, webkit moz spec); +} + +@mixin column-rule-color($color) { + @include prefixer(column-rule-color, $color, webkit moz spec); +} + +@mixin column-rule-style($style: none) { + // none | hidden | dashed | dotted | double | groove | inset | inset | outset | ridge | solid + @include prefixer(column-rule-style, $style, webkit moz spec); +} + +@mixin column-rule-width ($width: none) { + @include prefixer(column-rule-width, $width, webkit moz spec); +} + +@mixin column-span($arg: none) { + // none || all + @include prefixer(column-span, $arg, webkit moz spec); +} + +@mixin column-width($length: auto) { + // auto || length + @include prefixer(column-width, $length, webkit moz spec); +} diff --git a/src/styles/vendor/bourbon/css3/_filter.scss b/src/styles/vendor/bourbon/css3/_filter.scss new file mode 100644 index 0000000..b8f8ffb --- /dev/null +++ b/src/styles/vendor/bourbon/css3/_filter.scss @@ -0,0 +1,4 @@ +@mixin filter($function: none) { + // [ + @include prefixer(perspective, $depth, webkit moz spec); +} + +@mixin perspective-origin($value: 50% 50%) { + @include prefixer(perspective-origin, $value, webkit moz spec); +} diff --git a/src/styles/vendor/bourbon/css3/_placeholder.scss b/src/styles/vendor/bourbon/css3/_placeholder.scss new file mode 100644 index 0000000..5682fd0 --- /dev/null +++ b/src/styles/vendor/bourbon/css3/_placeholder.scss @@ -0,0 +1,8 @@ +@mixin placeholder { + $placeholders: ":-webkit-input" ":-moz" "-moz" "-ms-input"; + @each $placeholder in $placeholders { + &:#{$placeholder}-placeholder { + @content; + } + } +} diff --git a/src/styles/vendor/bourbon/css3/_radial-gradient.scss b/src/styles/vendor/bourbon/css3/_radial-gradient.scss new file mode 100644 index 0000000..8da076e --- /dev/null +++ b/src/styles/vendor/bourbon/css3/_radial-gradient.scss @@ -0,0 +1,39 @@ +// Requires Sass 3.1+ +@mixin radial-gradient($g1, $g2, + $g3: null, $g4: null, + $g5: null, $g6: null, + $g7: null, $g8: null, + $g9: null, $g10: null, + $pos: null, + $shape-size: null, + $fallback: null) { + + $data: _radial-arg-parser($g1, $g2, $pos, $shape-size); + $g1: nth($data, 1); + $g2: nth($data, 2); + $pos: nth($data, 3); + $shape-size: nth($data, 4); + + $full: $g1, $g2, $g3, $g4, $g5, $g6, $g7, $g8, $g9, $g10; + + // Strip deprecated cover/contain for spec + $shape-size-spec: _shape-size-stripper($shape-size); + + // Set $g1 as the default fallback color + $first-color: nth($full, 1); + $fallback-color: nth($first-color, 1); + + @if (type-of($fallback) == color) or ($fallback == "transparent") { + $fallback-color: $fallback; + } + + // Add Commas and spaces + $shape-size: if($shape-size, "#{$shape-size}, ", null); + $pos: if($pos, "#{$pos}, ", null); + $pos-spec: if($pos, "at #{$pos}", null); + $shape-size-spec: if(($shape-size-spec != " ") and ($pos == null), "#{$shape-size-spec}, ", "#{$shape-size-spec} "); + + background-color: $fallback-color; + background-image: -webkit-radial-gradient(unquote(#{$pos}#{$shape-size}#{$full})); + background-image: unquote("radial-gradient(#{$shape-size-spec}#{$pos-spec}#{$full})"); +} diff --git a/src/styles/vendor/bourbon/css3/_selection.scss b/src/styles/vendor/bourbon/css3/_selection.scss new file mode 100644 index 0000000..23303ab --- /dev/null +++ b/src/styles/vendor/bourbon/css3/_selection.scss @@ -0,0 +1,42 @@ +@charset "UTF-8"; + +/// Outputs the spec and prefixed versions of the `::selection` pseudo-element. +/// +/// @param {Bool} $current-selector [false] +/// If set to `true`, it takes the current element into consideration. +/// +/// @example scss - Usage +/// .element { +/// @include selection(true) { +/// background-color: #ffbb52; +/// } +/// } +/// +/// @example css - CSS Output +/// .element::-moz-selection { +/// background-color: #ffbb52; +/// } +/// +/// .element::selection { +/// background-color: #ffbb52; +/// } + +@mixin selection($current-selector: false) { + @if $current-selector { + &::-moz-selection { + @content; + } + + &::selection { + @content; + } + } @else { + ::-moz-selection { + @content; + } + + ::selection { + @content; + } + } +} diff --git a/src/styles/vendor/bourbon/css3/_text-decoration.scss b/src/styles/vendor/bourbon/css3/_text-decoration.scss new file mode 100644 index 0000000..9222746 --- /dev/null +++ b/src/styles/vendor/bourbon/css3/_text-decoration.scss @@ -0,0 +1,19 @@ +@mixin text-decoration($value) { +// || || + @include prefixer(text-decoration, $value, moz); +} + +@mixin text-decoration-line($line: none) { +// none || underline || overline || line-through + @include prefixer(text-decoration-line, $line, moz); +} + +@mixin text-decoration-style($style: solid) { +// solid || double || dotted || dashed || wavy + @include prefixer(text-decoration-style, $style, moz webkit); +} + +@mixin text-decoration-color($color: currentColor) { +// currentColor || + @include prefixer(text-decoration-color, $color, moz); +} diff --git a/src/styles/vendor/bourbon/css3/_transform.scss b/src/styles/vendor/bourbon/css3/_transform.scss new file mode 100644 index 0000000..8ee6509 --- /dev/null +++ b/src/styles/vendor/bourbon/css3/_transform.scss @@ -0,0 +1,15 @@ +@mixin transform($property: none) { + // none | + @include prefixer(transform, $property, webkit moz ms o spec); +} + +@mixin transform-origin($axes: 50%) { + // x-axis - left | center | right | length | % + // y-axis - top | center | bottom | length | % + // z-axis - length + @include prefixer(transform-origin, $axes, webkit moz ms o spec); +} + +@mixin transform-style($style: flat) { + @include prefixer(transform-style, $style, webkit moz ms o spec); +} diff --git a/src/styles/vendor/bourbon/css3/_transition.scss b/src/styles/vendor/bourbon/css3/_transition.scss new file mode 100644 index 0000000..3c785ed --- /dev/null +++ b/src/styles/vendor/bourbon/css3/_transition.scss @@ -0,0 +1,71 @@ +// Shorthand mixin. Supports multiple parentheses-deliminated values for each variable. +// Example: @include transition (all 2s ease-in-out); +// @include transition (opacity 1s ease-in 2s, width 2s ease-out); +// @include transition-property (transform, opacity); + +@mixin transition($properties...) { + // Fix for vendor-prefix transform property + $needs-prefixes: false; + $webkit: (); + $moz: (); + $spec: (); + + // Create lists for vendor-prefixed transform + @each $list in $properties { + @if nth($list, 1) == "transform" { + $needs-prefixes: true; + $list1: -webkit-transform; + $list2: -moz-transform; + $list3: (); + + @each $var in $list { + $list3: join($list3, $var); + + @if $var != "transform" { + $list1: join($list1, $var); + $list2: join($list2, $var); + } + } + + $webkit: append($webkit, $list1); + $moz: append($moz, $list2); + $spec: append($spec, $list3); + } @else { + $webkit: append($webkit, $list, comma); + $moz: append($moz, $list, comma); + $spec: append($spec, $list, comma); + } + } + + @if $needs-prefixes { + -webkit-transition: $webkit; + -moz-transition: $moz; + transition: $spec; + } @else { + @if length($properties) >= 1 { + @include prefixer(transition, $properties, webkit moz spec); + } @else { + $properties: all 0.15s ease-out 0s; + @include prefixer(transition, $properties, webkit moz spec); + } + } +} + +@mixin transition-property($properties...) { + -webkit-transition-property: transition-property-names($properties, "webkit"); + -moz-transition-property: transition-property-names($properties, "moz"); + transition-property: transition-property-names($properties, false); +} + +@mixin transition-duration($times...) { + @include prefixer(transition-duration, $times, webkit moz spec); +} + +@mixin transition-timing-function($motions...) { + // ease | linear | ease-in | ease-out | ease-in-out | cubic-bezier() + @include prefixer(transition-timing-function, $motions, webkit moz spec); +} + +@mixin transition-delay($times...) { + @include prefixer(transition-delay, $times, webkit moz spec); +} diff --git a/src/styles/vendor/bourbon/css3/_user-select.scss b/src/styles/vendor/bourbon/css3/_user-select.scss new file mode 100644 index 0000000..d4e5551 --- /dev/null +++ b/src/styles/vendor/bourbon/css3/_user-select.scss @@ -0,0 +1,3 @@ +@mixin user-select($value: none) { + @include prefixer(user-select, $value, webkit moz ms spec); +} diff --git a/src/styles/vendor/bourbon/functions/_assign-inputs.scss b/src/styles/vendor/bourbon/functions/_assign-inputs.scss new file mode 100644 index 0000000..f8aba96 --- /dev/null +++ b/src/styles/vendor/bourbon/functions/_assign-inputs.scss @@ -0,0 +1,11 @@ +@function assign-inputs($inputs, $pseudo: null) { + $list: (); + + @each $input in $inputs { + $input: unquote($input); + $input: if($pseudo, $input + ":" + $pseudo, $input); + $list: append($list, $input, comma); + } + + @return $list; +} diff --git a/src/styles/vendor/bourbon/functions/_contains-falsy.scss b/src/styles/vendor/bourbon/functions/_contains-falsy.scss new file mode 100644 index 0000000..c096fdb --- /dev/null +++ b/src/styles/vendor/bourbon/functions/_contains-falsy.scss @@ -0,0 +1,20 @@ +@charset "UTF-8"; + +/// Checks if a list does not contains a value. +/// +/// @access private +/// +/// @param {List} $list +/// The list to check against. +/// +/// @return {Bool} + +@function contains-falsy($list) { + @each $item in $list { + @if not $item { + @return true; + } + } + + @return false; +} diff --git a/src/styles/vendor/bourbon/functions/_contains.scss b/src/styles/vendor/bourbon/functions/_contains.scss new file mode 100644 index 0000000..3dec27d --- /dev/null +++ b/src/styles/vendor/bourbon/functions/_contains.scss @@ -0,0 +1,26 @@ +@charset "UTF-8"; + +/// Checks if a list contains a value(s). +/// +/// @access private +/// +/// @param {List} $list +/// The list to check against. +/// +/// @param {List} $values +/// A single value or list of values to check for. +/// +/// @example scss - Usage +/// contains($list, $value) +/// +/// @return {Bool} + +@function contains($list, $values...) { + @each $value in $values { + @if type-of(index($list, $value)) != "number" { + @return false; + } + } + + @return true; +} diff --git a/src/styles/vendor/bourbon/functions/_is-length.scss b/src/styles/vendor/bourbon/functions/_is-length.scss new file mode 100644 index 0000000..5826e78 --- /dev/null +++ b/src/styles/vendor/bourbon/functions/_is-length.scss @@ -0,0 +1,11 @@ +@charset "UTF-8"; + +/// Checks for a valid CSS length. +/// +/// @param {String} $value + +@function is-length($value) { + @return type-of($value) != "null" and (str-slice($value + "", 1, 4) == "calc" + or index(auto inherit initial 0, $value) + or (type-of($value) == "number" and not(unitless($value)))); +} diff --git a/src/styles/vendor/bourbon/functions/_is-light.scss b/src/styles/vendor/bourbon/functions/_is-light.scss new file mode 100644 index 0000000..92d90ac --- /dev/null +++ b/src/styles/vendor/bourbon/functions/_is-light.scss @@ -0,0 +1,21 @@ +@charset "UTF-8"; + +/// Programatically determines whether a color is light or dark. +/// +/// @link http://robots.thoughtbot.com/closer-look-color-lightness +/// +/// @param {Color (Hex)} $color +/// +/// @example scss - Usage +/// is-light($color) +/// +/// @return {Bool} + +@function is-light($hex-color) { + $-local-red: red(rgba($hex-color, 1)); + $-local-green: green(rgba($hex-color, 1)); + $-local-blue: blue(rgba($hex-color, 1)); + $-local-lightness: ($-local-red * 0.2126 + $-local-green * 0.7152 + $-local-blue * 0.0722) / 255; + + @return $-local-lightness > 0.6; +} diff --git a/src/styles/vendor/bourbon/functions/_is-number.scss b/src/styles/vendor/bourbon/functions/_is-number.scss new file mode 100644 index 0000000..a64e0bf --- /dev/null +++ b/src/styles/vendor/bourbon/functions/_is-number.scss @@ -0,0 +1,11 @@ +@charset "UTF-8"; + +/// Checks for a valid number. +/// +/// @param {Number} $value +/// +/// @require {function} contains + +@function is-number($value) { + @return contains("0" "1" "2" "3" "4" "5" "6" "7" "8" "9" 0 1 2 3 4 5 6 7 8 9, $value); +} diff --git a/src/styles/vendor/bourbon/functions/_is-size.scss b/src/styles/vendor/bourbon/functions/_is-size.scss new file mode 100644 index 0000000..661789a --- /dev/null +++ b/src/styles/vendor/bourbon/functions/_is-size.scss @@ -0,0 +1,13 @@ +@charset "UTF-8"; + +/// Checks for a valid CSS size. +/// +/// @param {String} $value +/// +/// @require {function} contains +/// @require {function} is-length + +@function is-size($value) { + @return is-length($value) + or contains("fill" "fit-content" "min-content" "max-content", $value); +} diff --git a/src/styles/vendor/bourbon/functions/_modular-scale.scss b/src/styles/vendor/bourbon/functions/_modular-scale.scss new file mode 100644 index 0000000..20fa388 --- /dev/null +++ b/src/styles/vendor/bourbon/functions/_modular-scale.scss @@ -0,0 +1,69 @@ +// Scaling Variables +$golden: 1.618; +$minor-second: 1.067; +$major-second: 1.125; +$minor-third: 1.2; +$major-third: 1.25; +$perfect-fourth: 1.333; +$augmented-fourth: 1.414; +$perfect-fifth: 1.5; +$minor-sixth: 1.6; +$major-sixth: 1.667; +$minor-seventh: 1.778; +$major-seventh: 1.875; +$octave: 2; +$major-tenth: 2.5; +$major-eleventh: 2.667; +$major-twelfth: 3; +$double-octave: 4; + +$modular-scale-ratio: $perfect-fourth !default; +$modular-scale-base: em($em-base) !default; + +@function modular-scale($increment, $value: $modular-scale-base, $ratio: $modular-scale-ratio) { + $v1: nth($value, 1); + $v2: nth($value, length($value)); + $value: $v1; + + // scale $v2 to just above $v1 + @while $v2 > $v1 { + $v2: ($v2 / $ratio); // will be off-by-1 + } + @while $v2 < $v1 { + $v2: ($v2 * $ratio); // will fix off-by-1 + } + + // check AFTER scaling $v2 to prevent double-counting corner-case + $double-stranded: $v2 > $v1; + + @if $increment > 0 { + @for $i from 1 through $increment { + @if $double-stranded and ($v1 * $ratio) > $v2 { + $value: $v2; + $v2: ($v2 * $ratio); + } @else { + $v1: ($v1 * $ratio); + $value: $v1; + } + } + } + + @if $increment < 0 { + // adjust $v2 to just below $v1 + @if $double-stranded { + $v2: ($v2 / $ratio); + } + + @for $i from $increment through -1 { + @if $double-stranded and ($v1 / $ratio) < $v2 { + $value: $v2; + $v2: ($v2 / $ratio); + } @else { + $v1: ($v1 / $ratio); + $value: $v1; + } + } + } + + @return $value; +} diff --git a/src/styles/vendor/bourbon/functions/_px-to-em.scss b/src/styles/vendor/bourbon/functions/_px-to-em.scss new file mode 100644 index 0000000..ae81a44 --- /dev/null +++ b/src/styles/vendor/bourbon/functions/_px-to-em.scss @@ -0,0 +1,13 @@ +// Convert pixels to ems +// eg. for a relational value of 12px write em(12) when the parent is 16px +// if the parent is another value say 24px write em(12, 24) + +@function em($pxval, $base: $em-base) { + @if not unitless($pxval) { + $pxval: strip-units($pxval); + } + @if not unitless($base) { + $base: strip-units($base); + } + @return ($pxval / $base) * 1em; +} diff --git a/src/styles/vendor/bourbon/functions/_px-to-rem.scss b/src/styles/vendor/bourbon/functions/_px-to-rem.scss new file mode 100644 index 0000000..0ac941e --- /dev/null +++ b/src/styles/vendor/bourbon/functions/_px-to-rem.scss @@ -0,0 +1,15 @@ +// Convert pixels to rems +// eg. for a relational value of 12px write rem(12) +// Assumes $em-base is the font-size of + +@function rem($pxval) { + @if not unitless($pxval) { + $pxval: strip-units($pxval); + } + + $base: $em-base; + @if not unitless($base) { + $base: strip-units($base); + } + @return ($pxval / $base) * 1rem; +} diff --git a/src/styles/vendor/bourbon/functions/_shade.scss b/src/styles/vendor/bourbon/functions/_shade.scss new file mode 100644 index 0000000..8aaf2c6 --- /dev/null +++ b/src/styles/vendor/bourbon/functions/_shade.scss @@ -0,0 +1,24 @@ +@charset "UTF-8"; + +/// Mixes a color with black. +/// +/// @param {Color} $color +/// +/// @param {Number (Percentage)} $percent +/// The amount of black to be mixed in. +/// +/// @example scss - Usage +/// .element { +/// background-color: shade(#ffbb52, 60%); +/// } +/// +/// @example css - CSS Output +/// .element { +/// background-color: #664a20; +/// } +/// +/// @return {Color} + +@function shade($color, $percent) { + @return mix(#000, $color, $percent); +} diff --git a/src/styles/vendor/bourbon/functions/_strip-units.scss b/src/styles/vendor/bourbon/functions/_strip-units.scss new file mode 100644 index 0000000..6c5f3e8 --- /dev/null +++ b/src/styles/vendor/bourbon/functions/_strip-units.scss @@ -0,0 +1,17 @@ +@charset "UTF-8"; + +/// Strips the unit from a number. +/// +/// @param {Number (With Unit)} $value +/// +/// @example scss - Usage +/// $dimension: strip-units(10em); +/// +/// @example css - CSS Output +/// $dimension: 10; +/// +/// @return {Number (Unitless)} + +@function strip-units($value) { + @return ($value / ($value * 0 + 1)); +} diff --git a/src/styles/vendor/bourbon/functions/_tint.scss b/src/styles/vendor/bourbon/functions/_tint.scss new file mode 100644 index 0000000..2e33814 --- /dev/null +++ b/src/styles/vendor/bourbon/functions/_tint.scss @@ -0,0 +1,24 @@ +@charset "UTF-8"; + +/// Mixes a color with white. +/// +/// @param {Color} $color +/// +/// @param {Number (Percentage)} $percent +/// The amount of white to be mixed in. +/// +/// @example scss - Usage +/// .element { +/// background-color: tint(#6ecaa6, 40%); +/// } +/// +/// @example css - CSS Output +/// .element { +/// background-color: #a8dfc9; +/// } +/// +/// @return {Color} + +@function tint($color, $percent) { + @return mix(#fff, $color, $percent); +} diff --git a/src/styles/vendor/bourbon/functions/_transition-property-name.scss b/src/styles/vendor/bourbon/functions/_transition-property-name.scss new file mode 100644 index 0000000..18348b9 --- /dev/null +++ b/src/styles/vendor/bourbon/functions/_transition-property-name.scss @@ -0,0 +1,22 @@ +// Return vendor-prefixed property names if appropriate +// Example: transition-property-names((transform, color, background), moz) -> -moz-transform, color, background +//************************************************************************// +@function transition-property-names($props, $vendor: false) { + $new-props: (); + + @each $prop in $props { + $new-props: append($new-props, transition-property-name($prop, $vendor), comma); + } + + @return $new-props; +} + +@function transition-property-name($prop, $vendor: false) { + // put other properties that need to be prefixed here aswell + @if $vendor and $prop == transform { + @return unquote('-'+$vendor+'-'+$prop); + } + @else { + @return $prop; + } +} diff --git a/src/styles/vendor/bourbon/functions/_unpack.scss b/src/styles/vendor/bourbon/functions/_unpack.scss new file mode 100644 index 0000000..4367935 --- /dev/null +++ b/src/styles/vendor/bourbon/functions/_unpack.scss @@ -0,0 +1,27 @@ +@charset "UTF-8"; + +/// Converts shorthand to the 4-value syntax. +/// +/// @param {List} $shorthand +/// +/// @example scss - Usage +/// .element { +/// margin: unpack(1em 2em); +/// } +/// +/// @example css - CSS Output +/// .element { +/// margin: 1em 2em 1em 2em; +/// } + +@function unpack($shorthand) { + @if length($shorthand) == 1 { + @return nth($shorthand, 1) nth($shorthand, 1) nth($shorthand, 1) nth($shorthand, 1); + } @else if length($shorthand) == 2 { + @return nth($shorthand, 1) nth($shorthand, 2) nth($shorthand, 1) nth($shorthand, 2); + } @else if length($shorthand) == 3 { + @return nth($shorthand, 1) nth($shorthand, 2) nth($shorthand, 3) nth($shorthand, 2); + } @else { + @return $shorthand; + } +} diff --git a/src/styles/vendor/bourbon/helpers/_convert-units.scss b/src/styles/vendor/bourbon/helpers/_convert-units.scss new file mode 100644 index 0000000..e0a65a0 --- /dev/null +++ b/src/styles/vendor/bourbon/helpers/_convert-units.scss @@ -0,0 +1,21 @@ +//************************************************************************// +// Helper function for str-to-num fn. +// Source: http://sassmeister.com/gist/9647408 +//************************************************************************// +@function _convert-units($number, $unit) { + $strings: "px", "cm", "mm", "%", "ch", "pica", "in", "em", "rem", "pt", "pc", "ex", "vw", "vh", "vmin", "vmax", "deg", "rad", "grad", "turn"; + $units: 1px, 1cm, 1mm, 1%, 1ch, 1pica, 1in, 1em, 1rem, 1pt, 1pc, 1ex, 1vw, 1vh, 1vmin, 1vmax, 1deg, 1rad, 1grad, 1turn; + $index: index($strings, $unit); + + @if not $index { + @warn "Unknown unit `#{$unit}`."; + @return false; + } + + @if type-of($number) != "number" { + @warn "`#{$number} is not a number`"; + @return false; + } + + @return $number * nth($units, $index); +} diff --git a/src/styles/vendor/bourbon/helpers/_directional-values.scss b/src/styles/vendor/bourbon/helpers/_directional-values.scss new file mode 100644 index 0000000..6ee538d --- /dev/null +++ b/src/styles/vendor/bourbon/helpers/_directional-values.scss @@ -0,0 +1,96 @@ +@charset "UTF-8"; + +/// Directional-property mixins are shorthands for writing properties like the following +/// +/// @ignore You can also use `false` instead of `null`. +/// +/// @param {List} $vals +/// List of directional values +/// +/// @example scss - Usage +/// .element { +/// @include border-style(dotted null); +/// @include margin(null 0 10px); +/// } +/// +/// @example css - CSS Output +/// .element { +/// border-bottom-style: dotted; +/// border-top-style: dotted; +/// margin-bottom: 10px; +/// margin-left: 0; +/// margin-right: 0; +/// } +/// +/// @require {function} contains-falsy +/// +/// @return {List} + +@function collapse-directionals($vals) { + $output: null; + + $a: nth($vals, 1); + $b: if(length($vals) < 2, $a, nth($vals, 2)); + $c: if(length($vals) < 3, $a, nth($vals, 3)); + $d: if(length($vals) < 2, $a, nth($vals, if(length($vals) < 4, 2, 4))); + + @if $a == 0 { $a: 0; } + @if $b == 0 { $b: 0; } + @if $c == 0 { $c: 0; } + @if $d == 0 { $d: 0; } + + @if $a == $b and $a == $c and $a == $d { $output: $a; } + @else if $a == $c and $b == $d { $output: $a $b; } + @else if $b == $d { $output: $a $b $c; } + @else { $output: $a $b $c $d; } + + @return $output; +} + +/// Output directional properties, for instance `margin`. +/// +/// @access private +/// +/// @param {String} $pre +/// Prefix to use +/// @param {String} $suf +/// Suffix to use +/// @param {List} $vals +/// List of values +/// +/// @require {function} collapse-directionals +/// @require {function} contains-falsy + +@mixin directional-property($pre, $suf, $vals) { + // Property Names + $top: $pre + "-top" + if($suf, "-#{$suf}", ""); + $bottom: $pre + "-bottom" + if($suf, "-#{$suf}", ""); + $left: $pre + "-left" + if($suf, "-#{$suf}", ""); + $right: $pre + "-right" + if($suf, "-#{$suf}", ""); + $all: $pre + if($suf, "-#{$suf}", ""); + + $vals: collapse-directionals($vals); + + @if contains-falsy($vals) { + @if nth($vals, 1) { #{$top}: nth($vals, 1); } + + @if length($vals) == 1 { + @if nth($vals, 1) { #{$right}: nth($vals, 1); } + } @else { + @if nth($vals, 2) { #{$right}: nth($vals, 2); } + } + + @if length($vals) == 2 { + @if nth($vals, 1) { #{$bottom}: nth($vals, 1); } + @if nth($vals, 2) { #{$left}: nth($vals, 2); } + } @else if length($vals) == 3 { + @if nth($vals, 3) { #{$bottom}: nth($vals, 3); } + @if nth($vals, 2) { #{$left}: nth($vals, 2); } + } @else if length($vals) == 4 { + @if nth($vals, 3) { #{$bottom}: nth($vals, 3); } + @if nth($vals, 4) { #{$left}: nth($vals, 4); } + } + } @else { + #{$all}: $vals; + } +} diff --git a/src/styles/vendor/bourbon/helpers/_font-source-declaration.scss b/src/styles/vendor/bourbon/helpers/_font-source-declaration.scss new file mode 100644 index 0000000..7f17586 --- /dev/null +++ b/src/styles/vendor/bourbon/helpers/_font-source-declaration.scss @@ -0,0 +1,43 @@ +// Used for creating the source string for fonts using @font-face +// Reference: http://goo.gl/Ru1bKP + +@function font-url-prefixer($asset-pipeline) { + @if $asset-pipeline == true { + @return font-url; + } @else { + @return url; + } +} + +@function font-source-declaration( + $font-family, + $file-path, + $asset-pipeline, + $file-formats, + $font-url) { + + $src: (); + + $formats-map: ( + eot: "#{$file-path}.eot?#iefix" format("embedded-opentype"), + woff2: "#{$file-path}.woff2" format("woff2"), + woff: "#{$file-path}.woff" format("woff"), + ttf: "#{$file-path}.ttf" format("truetype"), + svg: "#{$file-path}.svg##{$font-family}" format("svg") + ); + + @each $key, $values in $formats-map { + @if contains($file-formats, $key) { + $file-path: nth($values, 1); + $font-format: nth($values, 2); + + @if $asset-pipeline == true { + $src: append($src, font-url($file-path) $font-format, comma); + } @else { + $src: append($src, url($file-path) $font-format, comma); + } + } + } + + @return $src; +} diff --git a/src/styles/vendor/bourbon/helpers/_gradient-positions-parser.scss b/src/styles/vendor/bourbon/helpers/_gradient-positions-parser.scss new file mode 100644 index 0000000..07d30b6 --- /dev/null +++ b/src/styles/vendor/bourbon/helpers/_gradient-positions-parser.scss @@ -0,0 +1,13 @@ +@function _gradient-positions-parser($gradient-type, $gradient-positions) { + @if $gradient-positions + and ($gradient-type == linear) + and (type-of($gradient-positions) != color) { + $gradient-positions: _linear-positions-parser($gradient-positions); + } + @else if $gradient-positions + and ($gradient-type == radial) + and (type-of($gradient-positions) != color) { + $gradient-positions: _radial-positions-parser($gradient-positions); + } + @return $gradient-positions; +} diff --git a/src/styles/vendor/bourbon/helpers/_linear-angle-parser.scss b/src/styles/vendor/bourbon/helpers/_linear-angle-parser.scss new file mode 100644 index 0000000..e0401ed --- /dev/null +++ b/src/styles/vendor/bourbon/helpers/_linear-angle-parser.scss @@ -0,0 +1,25 @@ +// Private function for linear-gradient-parser +@function _linear-angle-parser($image, $first-val, $prefix, $suffix) { + $offset: null; + $unit-short: str-slice($first-val, str-length($first-val) - 2, str-length($first-val)); + $unit-long: str-slice($first-val, str-length($first-val) - 3, str-length($first-val)); + + @if ($unit-long == "grad") or + ($unit-long == "turn") { + $offset: if($unit-long == "grad", -100grad * 3, -0.75turn); + } + + @else if ($unit-short == "deg") or + ($unit-short == "rad") { + $offset: if($unit-short == "deg", -90 * 3, 1.6rad); + } + + @if $offset { + $num: _str-to-num($first-val); + + @return ( + webkit-image: -webkit- + $prefix + ($offset - $num) + $suffix, + spec-image: $image + ); + } +} diff --git a/src/styles/vendor/bourbon/helpers/_linear-gradient-parser.scss b/src/styles/vendor/bourbon/helpers/_linear-gradient-parser.scss new file mode 100644 index 0000000..48a8f77 --- /dev/null +++ b/src/styles/vendor/bourbon/helpers/_linear-gradient-parser.scss @@ -0,0 +1,41 @@ +@function _linear-gradient-parser($image) { + $image: unquote($image); + $gradients: (); + $start: str-index($image, "("); + $end: str-index($image, ","); + $first-val: str-slice($image, $start + 1, $end - 1); + + $prefix: str-slice($image, 1, $start); + $suffix: str-slice($image, $end, str-length($image)); + + $has-multiple-vals: str-index($first-val, " "); + $has-single-position: unquote(_position-flipper($first-val) + ""); + $has-angle: is-number(str-slice($first-val, 1, 1)); + + @if $has-multiple-vals { + $gradients: _linear-side-corner-parser($image, $first-val, $prefix, $suffix, $has-multiple-vals); + } + + @else if $has-single-position != "" { + $pos: unquote($has-single-position + ""); + + $gradients: ( + webkit-image: -webkit- + $image, + spec-image: $prefix + "to " + $pos + $suffix + ); + } + + @else if $has-angle { + // Rotate degree for webkit + $gradients: _linear-angle-parser($image, $first-val, $prefix, $suffix); + } + + @else { + $gradients: ( + webkit-image: -webkit- + $image, + spec-image: $image + ); + } + + @return $gradients; +} diff --git a/src/styles/vendor/bourbon/helpers/_linear-positions-parser.scss b/src/styles/vendor/bourbon/helpers/_linear-positions-parser.scss new file mode 100644 index 0000000..96d6a6d --- /dev/null +++ b/src/styles/vendor/bourbon/helpers/_linear-positions-parser.scss @@ -0,0 +1,61 @@ +@function _linear-positions-parser($pos) { + $type: type-of(nth($pos, 1)); + $spec: null; + $degree: null; + $side: null; + $corner: null; + $length: length($pos); + // Parse Side and corner positions + @if ($length > 1) { + @if nth($pos, 1) == "to" { // Newer syntax + $side: nth($pos, 2); + + @if $length == 2 { // eg. to top + // Swap for backwards compatibility + $degree: _position-flipper(nth($pos, 2)); + } + @else if $length == 3 { // eg. to top left + $corner: nth($pos, 3); + } + } + @else if $length == 2 { // Older syntax ("top left") + $side: _position-flipper(nth($pos, 1)); + $corner: _position-flipper(nth($pos, 2)); + } + + @if ("#{$side} #{$corner}" == "left top") or ("#{$side} #{$corner}" == "top left") { + $degree: _position-flipper(#{$side}) _position-flipper(#{$corner}); + } + @else if ("#{$side} #{$corner}" == "right top") or ("#{$side} #{$corner}" == "top right") { + $degree: _position-flipper(#{$side}) _position-flipper(#{$corner}); + } + @else if ("#{$side} #{$corner}" == "right bottom") or ("#{$side} #{$corner}" == "bottom right") { + $degree: _position-flipper(#{$side}) _position-flipper(#{$corner}); + } + @else if ("#{$side} #{$corner}" == "left bottom") or ("#{$side} #{$corner}" == "bottom left") { + $degree: _position-flipper(#{$side}) _position-flipper(#{$corner}); + } + $spec: to $side $corner; + } + @else if $length == 1 { + // Swap for backwards compatibility + @if $type == string { + $degree: $pos; + $spec: to _position-flipper($pos); + } + @else { + $degree: -270 - $pos; //rotate the gradient opposite from spec + $spec: $pos; + } + } + $degree: unquote($degree + ","); + $spec: unquote($spec + ","); + @return $degree $spec; +} + +@function _position-flipper($pos) { + @return if($pos == left, right, null) + if($pos == right, left, null) + if($pos == top, bottom, null) + if($pos == bottom, top, null); +} diff --git a/src/styles/vendor/bourbon/helpers/_linear-side-corner-parser.scss b/src/styles/vendor/bourbon/helpers/_linear-side-corner-parser.scss new file mode 100644 index 0000000..7a69125 --- /dev/null +++ b/src/styles/vendor/bourbon/helpers/_linear-side-corner-parser.scss @@ -0,0 +1,31 @@ +// Private function for linear-gradient-parser +@function _linear-side-corner-parser($image, $first-val, $prefix, $suffix, $has-multiple-vals) { + $val-1: str-slice($first-val, 1, $has-multiple-vals - 1); + $val-2: str-slice($first-val, $has-multiple-vals + 1, str-length($first-val)); + $val-3: null; + $has-val-3: str-index($val-2, " "); + + @if $has-val-3 { + $val-3: str-slice($val-2, $has-val-3 + 1, str-length($val-2)); + $val-2: str-slice($val-2, 1, $has-val-3 - 1); + } + + $pos: _position-flipper($val-1) _position-flipper($val-2) _position-flipper($val-3); + $pos: unquote($pos + ""); + + // Use old spec for webkit + @if $val-1 == "to" { + @return ( + webkit-image: -webkit- + $prefix + $pos + $suffix, + spec-image: $image + ); + } + + // Bring the code up to spec + @else { + @return ( + webkit-image: -webkit- + $image, + spec-image: $prefix + "to " + $pos + $suffix + ); + } +} diff --git a/src/styles/vendor/bourbon/helpers/_radial-arg-parser.scss b/src/styles/vendor/bourbon/helpers/_radial-arg-parser.scss new file mode 100644 index 0000000..56c6030 --- /dev/null +++ b/src/styles/vendor/bourbon/helpers/_radial-arg-parser.scss @@ -0,0 +1,69 @@ +@function _radial-arg-parser($g1, $g2, $pos, $shape-size) { + @each $value in $g1, $g2 { + $first-val: nth($value, 1); + $pos-type: type-of($first-val); + $spec-at-index: null; + + // Determine if spec was passed to mixin + @if type-of($value) == list { + $spec-at-index: if(index($value, at), index($value, at), false); + } + @if $spec-at-index { + @if $spec-at-index > 1 { + @for $i from 1 through ($spec-at-index - 1) { + $shape-size: $shape-size nth($value, $i); + } + @for $i from ($spec-at-index + 1) through length($value) { + $pos: $pos nth($value, $i); + } + } + @else if $spec-at-index == 1 { + @for $i from ($spec-at-index + 1) through length($value) { + $pos: $pos nth($value, $i); + } + } + $g1: null; + } + + // If not spec calculate correct values + @else { + @if ($pos-type != color) or ($first-val != "transparent") { + @if ($pos-type == number) + or ($first-val == "center") + or ($first-val == "top") + or ($first-val == "right") + or ($first-val == "bottom") + or ($first-val == "left") { + + $pos: $value; + + @if $pos == $g1 { + $g1: null; + } + } + + @else if + ($first-val == "ellipse") + or ($first-val == "circle") + or ($first-val == "closest-side") + or ($first-val == "closest-corner") + or ($first-val == "farthest-side") + or ($first-val == "farthest-corner") + or ($first-val == "contain") + or ($first-val == "cover") { + + $shape-size: $value; + + @if $value == $g1 { + $g1: null; + } + + @else if $value == $g2 { + $g2: null; + } + } + } + } + } + @return $g1, $g2, $pos, $shape-size; +} diff --git a/src/styles/vendor/bourbon/helpers/_radial-gradient-parser.scss b/src/styles/vendor/bourbon/helpers/_radial-gradient-parser.scss new file mode 100644 index 0000000..5444d80 --- /dev/null +++ b/src/styles/vendor/bourbon/helpers/_radial-gradient-parser.scss @@ -0,0 +1,50 @@ +@function _radial-gradient-parser($image) { + $image: unquote($image); + $gradients: (); + $start: str-index($image, "("); + $end: str-index($image, ","); + $first-val: str-slice($image, $start + 1, $end - 1); + + $prefix: str-slice($image, 1, $start); + $suffix: str-slice($image, $end, str-length($image)); + + $is-spec-syntax: str-index($first-val, "at"); + + @if $is-spec-syntax and $is-spec-syntax > 1 { + $keyword: str-slice($first-val, 1, $is-spec-syntax - 2); + $pos: str-slice($first-val, $is-spec-syntax + 3, str-length($first-val)); + $pos: append($pos, $keyword, comma); + + $gradients: ( + webkit-image: -webkit- + $prefix + $pos + $suffix, + spec-image: $image + ); + } + + @else if $is-spec-syntax == 1 { + $pos: str-slice($first-val, $is-spec-syntax + 3, str-length($first-val)); + + $gradients: ( + webkit-image: -webkit- + $prefix + $pos + $suffix, + spec-image: $image + ); + } + + @else if str-index($image, "cover") or str-index($image, "contain") { + @warn "Radial-gradient needs to be updated to conform to latest spec."; + + $gradients: ( + webkit-image: null, + spec-image: $image + ); + } + + @else { + $gradients: ( + webkit-image: -webkit- + $image, + spec-image: $image + ); + } + + @return $gradients; +} diff --git a/src/styles/vendor/bourbon/helpers/_radial-positions-parser.scss b/src/styles/vendor/bourbon/helpers/_radial-positions-parser.scss new file mode 100644 index 0000000..3c552ad --- /dev/null +++ b/src/styles/vendor/bourbon/helpers/_radial-positions-parser.scss @@ -0,0 +1,18 @@ +@function _radial-positions-parser($gradient-pos) { + $shape-size: nth($gradient-pos, 1); + $pos: nth($gradient-pos, 2); + $shape-size-spec: _shape-size-stripper($shape-size); + + $pre-spec: unquote(if($pos, "#{$pos}, ", null)) + unquote(if($shape-size, "#{$shape-size},", null)); + $pos-spec: if($pos, "at #{$pos}", null); + + $spec: "#{$shape-size-spec} #{$pos-spec}"; + + // Add comma + @if ($spec != " ") { + $spec: "#{$spec},"; + } + + @return $pre-spec $spec; +} diff --git a/src/styles/vendor/bourbon/helpers/_render-gradients.scss b/src/styles/vendor/bourbon/helpers/_render-gradients.scss new file mode 100644 index 0000000..5765676 --- /dev/null +++ b/src/styles/vendor/bourbon/helpers/_render-gradients.scss @@ -0,0 +1,26 @@ +// User for linear and radial gradients within background-image or border-image properties + +@function _render-gradients($gradient-positions, $gradients, $gradient-type, $vendor: false) { + $pre-spec: null; + $spec: null; + $vendor-gradients: null; + @if $gradient-type == linear { + @if $gradient-positions { + $pre-spec: nth($gradient-positions, 1); + $spec: nth($gradient-positions, 2); + } + } + @else if $gradient-type == radial { + $pre-spec: nth($gradient-positions, 1); + $spec: nth($gradient-positions, 2); + } + + @if $vendor { + $vendor-gradients: -#{$vendor}-#{$gradient-type}-gradient(#{$pre-spec} $gradients); + } + @else if $vendor == false { + $vendor-gradients: "#{$gradient-type}-gradient(#{$spec} #{$gradients})"; + $vendor-gradients: unquote($vendor-gradients); + } + @return $vendor-gradients; +} diff --git a/src/styles/vendor/bourbon/helpers/_shape-size-stripper.scss b/src/styles/vendor/bourbon/helpers/_shape-size-stripper.scss new file mode 100644 index 0000000..ee5eda4 --- /dev/null +++ b/src/styles/vendor/bourbon/helpers/_shape-size-stripper.scss @@ -0,0 +1,10 @@ +@function _shape-size-stripper($shape-size) { + $shape-size-spec: null; + @each $value in $shape-size { + @if ($value == "cover") or ($value == "contain") { + $value: null; + } + $shape-size-spec: "#{$shape-size-spec} #{$value}"; + } + @return $shape-size-spec; +} diff --git a/src/styles/vendor/bourbon/helpers/_str-to-num.scss b/src/styles/vendor/bourbon/helpers/_str-to-num.scss new file mode 100644 index 0000000..3ef1d87 --- /dev/null +++ b/src/styles/vendor/bourbon/helpers/_str-to-num.scss @@ -0,0 +1,50 @@ +//************************************************************************// +// Helper function for linear/radial-gradient-parsers. +// Source: http://sassmeister.com/gist/9647408 +//************************************************************************// +@function _str-to-num($string) { + // Matrices + $strings: "0" "1" "2" "3" "4" "5" "6" "7" "8" "9"; + $numbers: 0 1 2 3 4 5 6 7 8 9; + + // Result + $result: 0; + $divider: 0; + $minus: false; + + // Looping through all characters + @for $i from 1 through str-length($string) { + $character: str-slice($string, $i, $i); + $index: index($strings, $character); + + @if $character == "-" { + $minus: true; + } + + @else if $character == "." { + $divider: 1; + } + + @else { + @if not $index { + $result: if($minus, $result * -1, $result); + @return _convert-units($result, str-slice($string, $i)); + } + + $number: nth($numbers, $index); + + @if $divider == 0 { + $result: $result * 10; + } + + @else { + // Move the decimal dot to the left + $divider: $divider * 10; + $number: $number / $divider; + } + + $result: $result + $number; + } + } + @return if($minus, $result * -1, $result); +} diff --git a/src/styles/vendor/bourbon/settings/_asset-pipeline.scss b/src/styles/vendor/bourbon/settings/_asset-pipeline.scss new file mode 100644 index 0000000..4c6afc5 --- /dev/null +++ b/src/styles/vendor/bourbon/settings/_asset-pipeline.scss @@ -0,0 +1,7 @@ +@charset "UTF-8"; + +/// A global setting to enable or disable the `$asset-pipeline` variable for all functions that accept it. +/// +/// @type Bool + +$asset-pipeline: false !default; diff --git a/src/styles/vendor/bourbon/settings/_prefixer.scss b/src/styles/vendor/bourbon/settings/_prefixer.scss new file mode 100644 index 0000000..8c39051 --- /dev/null +++ b/src/styles/vendor/bourbon/settings/_prefixer.scss @@ -0,0 +1,9 @@ +@charset "UTF-8"; + +/// Global variables to enable or disable vendor prefixes + +$prefix-for-webkit: true !default; +$prefix-for-mozilla: true !default; +$prefix-for-microsoft: true !default; +$prefix-for-opera: true !default; +$prefix-for-spec: true !default; diff --git a/src/styles/vendor/bourbon/settings/_px-to-em.scss b/src/styles/vendor/bourbon/settings/_px-to-em.scss new file mode 100644 index 0000000..f2f9a3e --- /dev/null +++ b/src/styles/vendor/bourbon/settings/_px-to-em.scss @@ -0,0 +1 @@ +$em-base: 16px !default; diff --git a/src/templates/components/color.mustache b/src/templates/components/color.mustache deleted file mode 100755 index f6e5564..0000000 --- a/src/templates/components/color.mustache +++ /dev/null @@ -1,4 +0,0 @@ - diff --git a/src/templates/components/footer.mustache b/src/templates/components/footer.mustache deleted file mode 100755 index 6c4787d..0000000 --- a/src/templates/components/footer.mustache +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/templates/components/heading.mustache b/src/templates/components/heading.mustache deleted file mode 100755 index 3872b02..0000000 --- a/src/templates/components/heading.mustache +++ /dev/null @@ -1,3 +0,0 @@ -
-

-
diff --git a/src/templates/components/input.mustache b/src/templates/components/input.mustache deleted file mode 100755 index 574a949..0000000 --- a/src/templates/components/input.mustache +++ /dev/null @@ -1,10 +0,0 @@ - diff --git a/src/templates/components/radiogroup.mustache b/src/templates/components/radiogroup.mustache deleted file mode 100644 index 1bb44e2..0000000 --- a/src/templates/components/radiogroup.mustache +++ /dev/null @@ -1,15 +0,0 @@ -
- {{label}} -
- {{#options}} - - {{/options}} -
-
diff --git a/src/templates/components/select.mustache b/src/templates/components/select.mustache deleted file mode 100755 index 2e6e3ff..0000000 --- a/src/templates/components/select.mustache +++ /dev/null @@ -1,8 +0,0 @@ - diff --git a/src/templates/components/subheading.mustache b/src/templates/components/subheading.mustache deleted file mode 100755 index ec02c20..0000000 --- a/src/templates/components/subheading.mustache +++ /dev/null @@ -1,3 +0,0 @@ -
-

-
diff --git a/src/templates/components/submit.mustache b/src/templates/components/submit.mustache deleted file mode 100755 index 27eb81d..0000000 --- a/src/templates/components/submit.mustache +++ /dev/null @@ -1,3 +0,0 @@ -
- -
diff --git a/src/templates/components/toggle.mustache b/src/templates/components/toggle.mustache deleted file mode 100755 index ba7f734..0000000 --- a/src/templates/components/toggle.mustache +++ /dev/null @@ -1,4 +0,0 @@ - diff --git a/src/templates/items/color.tpl b/src/templates/items/color.tpl new file mode 100755 index 0000000..5b67f22 --- /dev/null +++ b/src/templates/items/color.tpl @@ -0,0 +1,13 @@ + diff --git a/src/templates/items/footer.tpl b/src/templates/items/footer.tpl new file mode 100755 index 0000000..3724a0d --- /dev/null +++ b/src/templates/items/footer.tpl @@ -0,0 +1,6 @@ + diff --git a/src/templates/items/heading.tpl b/src/templates/items/heading.tpl new file mode 100755 index 0000000..1df869e --- /dev/null +++ b/src/templates/items/heading.tpl @@ -0,0 +1,3 @@ +
+

+
diff --git a/src/templates/items/input.tpl b/src/templates/items/input.tpl new file mode 100755 index 0000000..62215df --- /dev/null +++ b/src/templates/items/input.tpl @@ -0,0 +1,10 @@ + diff --git a/src/templates/items/radiogroup.tpl b/src/templates/items/radiogroup.tpl new file mode 100644 index 0000000..9e3f4a5 --- /dev/null +++ b/src/templates/items/radiogroup.tpl @@ -0,0 +1,16 @@ +
+ {{{label}}} +
+ {{each options}} + + {{/each}} +
+
diff --git a/src/templates/items/select.tpl b/src/templates/items/select.tpl new file mode 100755 index 0000000..593cc99 --- /dev/null +++ b/src/templates/items/select.tpl @@ -0,0 +1,8 @@ + diff --git a/src/templates/items/subheading.tpl b/src/templates/items/subheading.tpl new file mode 100755 index 0000000..07e4778 --- /dev/null +++ b/src/templates/items/subheading.tpl @@ -0,0 +1,3 @@ +
+

+
diff --git a/src/templates/items/submit.tpl b/src/templates/items/submit.tpl new file mode 100755 index 0000000..a6e9d2b --- /dev/null +++ b/src/templates/items/submit.tpl @@ -0,0 +1,9 @@ +
+ +
diff --git a/src/templates/items/toggle.tpl b/src/templates/items/toggle.tpl new file mode 100755 index 0000000..8183cde --- /dev/null +++ b/src/templates/items/toggle.tpl @@ -0,0 +1,10 @@ +