| 1 | 1 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,3602 @@ |
| 1 |
+// minified.js config start -- use this comment to re-create a configuration in the Builder |
|
| 2 |
+// - Only sections add, always, amdsupport, copyobj, dollardollar, |
|
| 3 |
+// - each, eachobj, equals, error, extend, find, format, formathtml, get, ht, |
|
| 4 |
+// - html, isobject, off, on, ready, request, select, set, template, trigger, |
|
| 5 |
+// - underscore, wait. |
|
| 6 |
+ |
|
| 7 |
+ |
|
| 8 |
+// WARNING! This file is autogenerated from minified-master.js and others. |
|
| 9 |
+ |
|
| 10 |
+/* |
|
| 11 |
+ * Minified.js - Lightweight Client-Side JavaScript Library (full package) |
|
| 12 |
+ * Version: Version 2014 beta 5 b2 |
|
| 13 |
+ * |
|
| 14 |
+ * Public Domain. Use, modify and distribute it any way you like. No attribution required. |
|
| 15 |
+ * To the extent possible under law, Tim Jansen has waived all copyright and related or neighboring rights to Minified. |
|
| 16 |
+ * Please see http://creativecommons.org/publicdomain/zero/1.0/. |
|
| 17 |
+ * NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. |
|
| 18 |
+ * |
|
| 19 |
+ * Contains code based on https://github.com/douglascrockford/JSON-js (also Public Domain). |
|
| 20 |
+ * |
|
| 21 |
+ * https://github.com/timjansen/minified.js |
|
| 22 |
+ */ |
|
| 23 |
+// ==ClosureCompiler== |
|
| 24 |
+// @output_file_name minified.js |
|
| 25 |
+// @compilation_level ADVANCED_OPTIMIZATIONS |
|
| 26 |
+// ==/ClosureCompiler== |
|
| 27 |
+ |
|
| 28 |
+/*$ |
|
| 29 |
+ * @id ALL |
|
| 30 |
+ * @doc no |
|
| 31 |
+ * @required |
|
| 32 |
+ * This id allows identifying whether both Web and Util are available. |
|
| 33 |
+ */ |
|
| 34 |
+ |
|
| 35 |
+///#snippet commonAmdStart |
|
| 36 |
+ |
|
| 37 |
+/*$ |
|
| 38 |
+ * @id require |
|
| 39 |
+ * @name require() |
|
| 40 |
+ * @syntax require(name) |
|
| 41 |
+ * @group OPTIONS |
|
| 42 |
+ * @module WEB, UTIL |
|
| 43 |
+ * Returns a reference to a module. If you do not use an AMD loader to load Minified, just call <var>require()</var> with the |
|
| 44 |
+ * argument 'minified' to get a reference to Minified. You can also access all modules defined using ##define(). |
|
| 45 |
+ * |
|
| 46 |
+ * If you do use an AMD loader, Minified will not define this function and you can use the AMD loader to obtain the |
|
| 47 |
+ * reference to Minified. |
|
| 48 |
+ * Minified's version of <var>require</var> is very simple and will only support Minified and other libraries designed |
|
| 49 |
+ * for Minfied, but <strong>no real AMD libraries</strong>. If you need to work with libraries requiring AMD, you need a real AMD loader. |
|
| 50 |
+ * |
|
| 51 |
+ * @param name the name of the module to request. Minified is available as 'minified'. |
|
| 52 |
+ * @return the reference to the module. Use the name 'minified' to get Minified. You can also access any modules defined using |
|
| 53 |
+ * ##define(). If the name is unknown, it returns <var>undefined</var>. |
|
| 54 |
+ * |
|
| 55 |
+ * @see ##define() allows you to define modules that can be obtained using <var>require()</var>. |
|
| 56 |
+ */ |
|
| 57 |
+ |
|
| 58 |
+/*$ |
|
| 59 |
+ * @id define |
|
| 60 |
+ * @name define() |
|
| 61 |
+ * @syntax define(name, factoryFunction) |
|
| 62 |
+ * @group OPTIONS |
|
| 63 |
+ * @module WEB, UTIL |
|
| 64 |
+ * 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, |
|
| 65 |
+ * <var>define()</var> will not be set and you can use the AMD loader's (more powerful) variant. |
|
| 66 |
+ * |
|
| 67 |
+ * Minified's versions of <var>require()</var> and <var>define()</var> are very simple and can not resolve things like circular references. |
|
| 68 |
+ * 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 |
|
| 69 |
+ * for Minified, you need a real AMD loader. |
|
| 70 |
+ * |
|
| 71 |
+ * @example Creates a simple module and uses it: |
|
| 72 |
+ * <pre> |
|
| 73 |
+ * define('makeGreen', function(require) {
|
|
| 74 |
+ * var MINI = require('minified'), $ = MINI.$; // obtain own ref to Minified
|
|
| 75 |
+ * return function(list) {
|
|
| 76 |
+ * $(list).set({$color: '#0f0', $backgroundColor: '#050'});
|
|
| 77 |
+ * }); |
|
| 78 |
+ * }); |
|
| 79 |
+ * |
|
| 80 |
+ * var makeGreen = require('makeGreen');
|
|
| 81 |
+ * makeGreen('.notGreenEnough');
|
|
| 82 |
+ * </pre> |
|
| 83 |
+ * |
|
| 84 |
+ * @param name the name of the module to request. In Minified's implementation, only 'minified' is supported. |
|
| 85 |
+ * @param factoryFunction is a <code>function(require)</code> will be called the first time the name is defined to obtain the module |
|
| 86 |
+ * reference. It received a reference to ##require() (which is required for AMD backward-compatibility) and |
|
| 87 |
+ * must return the value that is returned by ##require(). The function will only be called once, its result will |
|
| 88 |
+ * be cached. |
|
| 89 |
+ * <dl><dt>require</dt><dd>A reference to ##require(). While you could use <var>require()</var> from the global |
|
| 90 |
+ * context, this would prevent backward compatibility with AMD.</dd> |
|
| 91 |
+ * <dt class="returnValue">(callback return value)</dt><dd>The reference to be returned by ##require().</dd></dl> |
|
| 92 |
+ * |
|
| 93 |
+ * @see ##require() can be used to obtain references defined with ##define(). |
|
| 94 |
+ */ |
|
| 95 |
+ |
|
| 96 |
+/*$ |
|
| 97 |
+ * @id amdsupport |
|
| 98 |
+ * @name AMD stubs |
|
| 99 |
+ * @configurable default |
|
| 100 |
+ * @group OPTIONS |
|
| 101 |
+ * @doc no |
|
| 102 |
+ * @module WEB, UTIL |
|
| 103 |
+ * If enabled, Minified will create stubs so you can use it without an AMD framework. |
|
| 104 |
+ * It requires AMD's <code>define()</code> function. |
|
| 105 |
+ */ |
|
| 106 |
+if (/^u/.test(typeof define)) { // no AMD support available ? define a minimal version
|
|
| 107 |
+ (function(def){
|
|
| 108 |
+ var require = this['require'] = function(name) { return def[name]; };
|
|
| 109 |
+ this['define'] = function(name, f) { def[name] = def[name] || f(require); };
|
|
| 110 |
+ })({});
|
|
| 111 |
+} |
|
| 112 |
+/*$ |
|
| 113 |
+ * @stop |
|
| 114 |
+ */ |
|
| 115 |
+ |
|
| 116 |
+define('minified', function() {
|
|
| 117 |
+ |
|
| 118 |
+ ///#/snippet commonAmdStart |
|
| 119 |
+ ///#snippet webVars |
|
| 120 |
+ /*$ |
|
| 121 |
+ * @id WEB |
|
| 122 |
+ * @doc no |
|
| 123 |
+ * @required |
|
| 124 |
+ * This id allows identifying whether the Web module is available. |
|
| 125 |
+ */ |
|
| 126 |
+ |
|
| 127 |
+ /** |
|
| 128 |
+ * @const |
|
| 129 |
+ */ |
|
| 130 |
+ var _window = window; |
|
| 131 |
+ |
|
| 132 |
+ /** |
|
| 133 |
+ * @const |
|
| 134 |
+ * @type {!string}
|
|
| 135 |
+ */ |
|
| 136 |
+ var MINIFIED_MAGIC_NODEID = 'Nia'; |
|
| 137 |
+ |
|
| 138 |
+ /** |
|
| 139 |
+ * @const |
|
| 140 |
+ * @type {!string}
|
|
| 141 |
+ */ |
|
| 142 |
+ var MINIFIED_MAGIC_PREV = 'NiaP'; |
|
| 143 |
+ |
|
| 144 |
+ var setter = {}, getter = {};
|
|
| 145 |
+ |
|
| 146 |
+ var idSequence = 1; // used as node id to identify nodes, and as general id for other maps |
|
| 147 |
+ |
|
| 148 |
+ |
|
| 149 |
+ /*$ |
|
| 150 |
+ * @id ready_vars |
|
| 151 |
+ * @dependency |
|
| 152 |
+ */ |
|
| 153 |
+ /** @type {!Array.<function()>} */
|
|
| 154 |
+ var DOMREADY_HANDLER = /^[ic]/.test(document['readyState']) ? _null : []; // check for 'interactive' and 'complete' |
|
| 155 |
+ /*$ |
|
| 156 |
+ * @stop |
|
| 157 |
+ */ |
|
| 158 |
+ |
|
| 159 |
+ ///#/snippet webVars |
|
| 160 |
+ ///#snippet utilVars |
|
| 161 |
+ /*$ |
|
| 162 |
+ * @id UTIL |
|
| 163 |
+ * @doc no |
|
| 164 |
+ * @required |
|
| 165 |
+ * This id allows identifying whether the Util module is available. |
|
| 166 |
+ */ |
|
| 167 |
+ |
|
| 168 |
+ var _null = null; |
|
| 169 |
+ |
|
| 170 |
+ /** @const */ |
|
| 171 |
+ var undef; |
|
| 172 |
+ |
|
| 173 |
+ /*$ |
|
| 174 |
+ * @id date_constants |
|
| 175 |
+ * @dependency |
|
| 176 |
+ */ |
|
| 177 |
+ function val3(v) {return v.substr(0,3);}
|
|
| 178 |
+ var MONTH_LONG_NAMES = split('January,February,March,April,May,June,July,August,September,October,November,December', /,/g);
|
|
| 179 |
+ var MONTH_SHORT_NAMES = map(MONTH_LONG_NAMES, val3); // ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; |
|
| 180 |
+ var WEEK_LONG_NAMES = split('Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday', /,/g);
|
|
| 181 |
+ var WEEK_SHORT_NAMES = map(WEEK_LONG_NAMES, val3); |
|
| 182 |
+ var MERIDIAN_NAMES = split('am,pm', /,/g);
|
|
| 183 |
+ 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);
|
|
| 184 |
+ |
|
| 185 |
+ var FORMAT_DATE_MAP = {
|
|
| 186 |
+ 'y': ['FullYear', nonOp], |
|
| 187 |
+ 'Y': ['FullYear', function(d) { return d % 100; }],
|
|
| 188 |
+ 'M': ['Month', plusOne], |
|
| 189 |
+ 'n': ['Month', MONTH_SHORT_NAMES], |
|
| 190 |
+ 'N': ['Month', MONTH_LONG_NAMES], |
|
| 191 |
+ 'd': ['Date', nonOp], |
|
| 192 |
+ 'm': ['Minutes', nonOp], |
|
| 193 |
+ 'H': ['Hours', nonOp], |
|
| 194 |
+ 'h': ['Hours', function(d) { return (d % 12) || 12; }],
|
|
| 195 |
+ 'k': ['Hours', plusOne], |
|
| 196 |
+ 'K': ['Hours', function(d) { return d % 12; }],
|
|
| 197 |
+ 's': ['Seconds', nonOp], |
|
| 198 |
+ 'S': ['Milliseconds', nonOp], |
|
| 199 |
+ 'a': ['Hours', MERIDIAN_NAMES_FULL], |
|
| 200 |
+ 'w': ['Day', WEEK_SHORT_NAMES], |
|
| 201 |
+ 'W': ['Day', WEEK_LONG_NAMES], |
|
| 202 |
+ 'z': ['TimezoneOffset', function(d, dummy, timezone) {
|
|
| 203 |
+ if (timezone) |
|
| 204 |
+ return timezone; |
|
| 205 |
+ |
|
| 206 |
+ var sign = d > 0 ? '-' : '+'; |
|
| 207 |
+ var off = d < 0 ? -d : d; |
|
| 208 |
+ return sign + pad(2, Math.floor(off/60)) + pad(2, off%60); |
|
| 209 |
+ }] |
|
| 210 |
+ }; |
|
| 211 |
+ |
|
| 212 |
+ var PARSE_DATE_MAP = {
|
|
| 213 |
+ 'y': 0, // placeholder -> ctorIndex |
|
| 214 |
+ 'Y': [0, -2000], |
|
| 215 |
+ 'M': [1,1], // placeholder -> [ctorIndex, offset|value array] |
|
| 216 |
+ 'n': [1, MONTH_SHORT_NAMES], |
|
| 217 |
+ 'N': [1, MONTH_LONG_NAMES], |
|
| 218 |
+ 'd': 2, |
|
| 219 |
+ 'm': 4, |
|
| 220 |
+ 'H': 3, |
|
| 221 |
+ 'h': 3, |
|
| 222 |
+ 'K': [3,1], |
|
| 223 |
+ 'k': [3,1], |
|
| 224 |
+ 's': 5, |
|
| 225 |
+ 'S': 6, |
|
| 226 |
+ 'a': [3, MERIDIAN_NAMES] |
|
| 227 |
+ }; |
|
| 228 |
+ |
|
| 229 |
+ /*$ |
|
| 230 |
+ * @stop |
|
| 231 |
+ */ |
|
| 232 |
+ |
|
| 233 |
+ /** @const */ |
|
| 234 |
+ var MAX_CACHED_TEMPLATES = 99; |
|
| 235 |
+ var templateCache={}; // template -> function
|
|
| 236 |
+ var templates = []; // list of MAX_CACHED_TEMPLATES templates |
|
| 237 |
+ |
|
| 238 |
+ ///#/snippet utilVars |
|
| 239 |
+ ///#snippet commonFunctions |
|
| 240 |
+ |
|
| 241 |
+ /** @param s {?} */
|
|
| 242 |
+ function toString(s) {
|
|
| 243 |
+ return s!=_null ? ''+s : ''; |
|
| 244 |
+ } |
|
| 245 |
+ /** |
|
| 246 |
+ * @param s {?}
|
|
| 247 |
+ * @param o {string}
|
|
| 248 |
+ */ |
|
| 249 |
+ function isType(s,o) {
|
|
| 250 |
+ return typeof s == o; |
|
| 251 |
+ } |
|
| 252 |
+ /** @param s {?} */
|
|
| 253 |
+ function isString(s) {
|
|
| 254 |
+ return isType(s, 'string'); |
|
| 255 |
+ } |
|
| 256 |
+ function isObject(f) {
|
|
| 257 |
+ return !!f && isType(f, 'object'); |
|
| 258 |
+ } |
|
| 259 |
+ function isNode(n) {
|
|
| 260 |
+ return n && n['nodeType']; |
|
| 261 |
+ } |
|
| 262 |
+ function isNumber(n) {
|
|
| 263 |
+ return isType(n, 'number'); |
|
| 264 |
+ } |
|
| 265 |
+ function isDate(n) {
|
|
| 266 |
+ return isObject(n) && !!n['getDay']; |
|
| 267 |
+ } |
|
| 268 |
+ function isBool(n) {
|
|
| 269 |
+ return n === true || n === false; |
|
| 270 |
+ } |
|
| 271 |
+ function isValue(n) {
|
|
| 272 |
+ var type = typeof n; |
|
| 273 |
+ return type == 'object' ? !!(n && n['getDay']) : (type == 'string' || type == 'number' || isBool(n)); |
|
| 274 |
+ } |
|
| 275 |
+ function nonOp(v) {
|
|
| 276 |
+ return v; |
|
| 277 |
+ } |
|
| 278 |
+ function plusOne(d) {
|
|
| 279 |
+ return d+1; |
|
| 280 |
+ } |
|
| 281 |
+ function replace(s, regexp, sub) {
|
|
| 282 |
+ return toString(s).replace(regexp, sub != _null ? sub : ''); |
|
| 283 |
+ } |
|
| 284 |
+ function escapeRegExp(s) {
|
|
| 285 |
+ return replace(s, /[\\\[\]\/{}()*+?.$|^-]/g, "\\$&");
|
|
| 286 |
+ } |
|
| 287 |
+ function trim(s) {
|
|
| 288 |
+ return replace(s, /^\s+|\s+$/g); |
|
| 289 |
+ } |
|
| 290 |
+ function eachObj(obj, cb, ctx) {
|
|
| 291 |
+ for (var n in obj) |
|
| 292 |
+ if (obj.hasOwnProperty(n)) |
|
| 293 |
+ cb.call(ctx || obj, n, obj[n]); |
|
| 294 |
+ return obj; |
|
| 295 |
+ } |
|
| 296 |
+ function each(list, cb, ctx) {
|
|
| 297 |
+ if (list) |
|
| 298 |
+ for (var i = 0; i < list.length; i++) |
|
| 299 |
+ cb.call(ctx || list, list[i], i); |
|
| 300 |
+ return list; |
|
| 301 |
+ } |
|
| 302 |
+ function filter(list, filterFuncOrObject, ctx) {
|
|
| 303 |
+ var r = []; |
|
| 304 |
+ var f = isFunction(filterFuncOrObject) ? filterFuncOrObject : function(value) { return filterFuncOrObject != value; };
|
|
| 305 |
+ each(list, function(value, index) {
|
|
| 306 |
+ if (f.call(ctx || list, value, index)) |
|
| 307 |
+ r.push(value); |
|
| 308 |
+ }); |
|
| 309 |
+ return r; |
|
| 310 |
+ } |
|
| 311 |
+ function collector(iterator, obj, collectFunc, ctx) {
|
|
| 312 |
+ var result = []; |
|
| 313 |
+ iterator(obj, function (a, b) {
|
|
| 314 |
+ if (isList(a = collectFunc.call(ctx || obj, a, b))) // extreme variable reusing: a is now the callback result |
|
| 315 |
+ each(a, function(rr) { result.push(rr); });
|
|
| 316 |
+ else if (a != _null) |
|
| 317 |
+ result.push(a); |
|
| 318 |
+ }); |
|
| 319 |
+ return result; |
|
| 320 |
+ } |
|
| 321 |
+ function collectObj(obj, collectFunc, ctx) {
|
|
| 322 |
+ return collector(eachObj, obj, collectFunc, ctx); |
|
| 323 |
+ } |
|
| 324 |
+ function collect(list, collectFunc, ctx) {
|
|
| 325 |
+ return collector(each, list, collectFunc, ctx); |
|
| 326 |
+ } |
|
| 327 |
+ function keyCount(obj) {
|
|
| 328 |
+ var c = 0; |
|
| 329 |
+ eachObj(obj, function(key) { c++; });
|
|
| 330 |
+ return c; |
|
| 331 |
+ } |
|
| 332 |
+ function keys(obj) { // use Object.keys? in IE>=9
|
|
| 333 |
+ var list = []; |
|
| 334 |
+ eachObj(obj, function(key) { list.push(key); });
|
|
| 335 |
+ return list; |
|
| 336 |
+ } |
|
| 337 |
+ function map(list, mapFunc, ctx) {
|
|
| 338 |
+ var result = []; |
|
| 339 |
+ each(list, function(item, index) {
|
|
| 340 |
+ result.push(mapFunc.call(ctx || list, item, index)); |
|
| 341 |
+ }); |
|
| 342 |
+ return result; |
|
| 343 |
+ } |
|
| 344 |
+ function startsWith(base, start) {
|
|
| 345 |
+ if (isList(base)) {
|
|
| 346 |
+ var s2 = _(start); // convert start as we don't know whether it is a list yet |
|
| 347 |
+ return equals(sub(base, 0, s2.length), s2); |
|
| 348 |
+ } |
|
| 349 |
+ else |
|
| 350 |
+ return start != _null && base.substr(0, start.length) == start; |
|
| 351 |
+ } |
|
| 352 |
+ function endsWith(base, end) {
|
|
| 353 |
+ if (isList(base)) {
|
|
| 354 |
+ var e2 = _(end); |
|
| 355 |
+ return equals(sub(base, -e2.length), e2) || !e2.length; |
|
| 356 |
+ } |
|
| 357 |
+ else |
|
| 358 |
+ return end != _null && base.substr(base.length - end.length) == end; |
|
| 359 |
+ } |
|
| 360 |
+ function reverse(list) {
|
|
| 361 |
+ var len = list.length; |
|
| 362 |
+ if (isList(list)) |
|
| 363 |
+ return new M(map(list, function() { return list[--len]; }));
|
|
| 364 |
+ else |
|
| 365 |
+ return replace(list, /[\s\S]/g, function() { return list.charAt(--len); });
|
|
| 366 |
+ } |
|
| 367 |
+ function toObject(list, value) {
|
|
| 368 |
+ var obj = {};
|
|
| 369 |
+ each(list, function(item, index) {
|
|
| 370 |
+ obj[item] = value; |
|
| 371 |
+ }); |
|
| 372 |
+ return obj; |
|
| 373 |
+ } |
|
| 374 |
+ function copyObj(from, to) {
|
|
| 375 |
+ var dest = to || {};
|
|
| 376 |
+ for (var name in from) |
|
| 377 |
+ dest[name] = from[name]; |
|
| 378 |
+ return dest; |
|
| 379 |
+ } |
|
| 380 |
+ function merge(list, target) {
|
|
| 381 |
+ var o = target; |
|
| 382 |
+ for (var i = 0; i < list.length; i++) |
|
| 383 |
+ o = copyObj(list[i], o); |
|
| 384 |
+ return o; |
|
| 385 |
+ } |
|
| 386 |
+ function getFindFunc(findFunc) {
|
|
| 387 |
+ return isFunction(findFunc) ? findFunc : function(obj, index) { if (findFunc === obj) return index; };
|
|
| 388 |
+ } |
|
| 389 |
+ function getFindIndex(list, index, defaultIndex) {
|
|
| 390 |
+ return index == _null ? defaultIndex : index < 0 ? Math.max(list.length+index, 0) : Math.min(list.length, index); |
|
| 391 |
+ } |
|
| 392 |
+ function find(list, findFunc, startIndex, endIndex) {
|
|
| 393 |
+ var f = getFindFunc(findFunc); |
|
| 394 |
+ var e = getFindIndex(list, endIndex, list.length); |
|
| 395 |
+ var r; |
|
| 396 |
+ for (var i = getFindIndex(list, startIndex, 0); i < e; i++) |
|
| 397 |
+ if ((r = f.call(list, list[i], i)) != _null) |
|
| 398 |
+ return r; |
|
| 399 |
+ } |
|
| 400 |
+ function findLast(list, findFunc, startIndex, endIndex) {
|
|
| 401 |
+ var f = getFindFunc(findFunc); |
|
| 402 |
+ var e = getFindIndex(list, endIndex, -1); |
|
| 403 |
+ var r; |
|
| 404 |
+ for (var i = getFindIndex(list, startIndex, list.length-1); i > e; i--) |
|
| 405 |
+ if ((r = f.call(list, list[i], i)) != _null) |
|
| 406 |
+ return r; |
|
| 407 |
+ } |
|
| 408 |
+ function sub(list, startIndex, endIndex) {
|
|
| 409 |
+ var r = []; |
|
| 410 |
+ if (list) {
|
|
| 411 |
+ var e = getFindIndex(list, endIndex, list.length); |
|
| 412 |
+ for (var i = getFindIndex(list, startIndex, 0); i < e; i++) |
|
| 413 |
+ r.push(list[i]); |
|
| 414 |
+ } |
|
| 415 |
+ return r; |
|
| 416 |
+ } |
|
| 417 |
+ function array(list) {
|
|
| 418 |
+ return map(list, nonOp); |
|
| 419 |
+ } |
|
| 420 |
+ function unite(list) {
|
|
| 421 |
+ return function() {
|
|
| 422 |
+ return new M(callList(list, arguments)); |
|
| 423 |
+ }; |
|
| 424 |
+ } |
|
| 425 |
+ function uniq(list) {
|
|
| 426 |
+ var found = {};
|
|
| 427 |
+ return filter(list, function(item) {
|
|
| 428 |
+ if (found[item]) |
|
| 429 |
+ return false; |
|
| 430 |
+ else |
|
| 431 |
+ return found[item] = 1; |
|
| 432 |
+ }); |
|
| 433 |
+ } |
|
| 434 |
+ function intersection(list, otherList) {
|
|
| 435 |
+ var keys = toObject(otherList, 1); |
|
| 436 |
+ return filter(list, function(item) {
|
|
| 437 |
+ var r = keys[item]; |
|
| 438 |
+ keys[item] = 0; |
|
| 439 |
+ return r; |
|
| 440 |
+ }); |
|
| 441 |
+ } |
|
| 442 |
+ function contains(list, value) { // TODO: can Array.indexOf be used in >IE8?
|
|
| 443 |
+ for (var i = 0; i < list.length; i++) |
|
| 444 |
+ if (list[i] == value) |
|
| 445 |
+ return true; |
|
| 446 |
+ return false; |
|
| 447 |
+ } |
|
| 448 |
+ // equals if a and b have the same elements and all are equal. Supports getters. |
|
| 449 |
+ function equals(x, y) {
|
|
| 450 |
+ var a = isFunction(x) ? x() : x; |
|
| 451 |
+ var b = isFunction(y) ? y() : y; |
|
| 452 |
+ var aKeys; |
|
| 453 |
+ if (a == b) |
|
| 454 |
+ return true; |
|
| 455 |
+ else if (a == _null || b == _null) |
|
| 456 |
+ return false; |
|
| 457 |
+ else if (isValue(a) || isValue(b)) |
|
| 458 |
+ return isDate(a) && isDate(b) && +a==+b; |
|
| 459 |
+ else if (isList(a)) {
|
|
| 460 |
+ return (a.length == b.length) && |
|
| 461 |
+ !find(a, function(val, index) {
|
|
| 462 |
+ if (!equals(val, b[index])) |
|
| 463 |
+ return true; |
|
| 464 |
+ }); |
|
| 465 |
+ } |
|
| 466 |
+ else {
|
|
| 467 |
+ return !isList(b) && |
|
| 468 |
+ ((aKeys = keys(a)).length == keyCount(b)) && |
|
| 469 |
+ !find(aKeys, function(key) {
|
|
| 470 |
+ if (!equals(a[key],b[key])) |
|
| 471 |
+ return true; |
|
| 472 |
+ }); |
|
| 473 |
+ } |
|
| 474 |
+ } |
|
| 475 |
+ |
|
| 476 |
+ function call(f, fThisOrArgs, args) {
|
|
| 477 |
+ if (isFunction(f)) |
|
| 478 |
+ return f.apply(args && fThisOrArgs, map(args || fThisOrArgs, nonOp)); |
|
| 479 |
+ } |
|
| 480 |
+ function callList(list, fThisOrArgs, args) {
|
|
| 481 |
+ return map(list, function(f) { return call(f, fThisOrArgs, args);});
|
|
| 482 |
+ } |
|
| 483 |
+ function bind(f, fThis, beforeArgs, afterArgs) {
|
|
| 484 |
+ return function() {
|
|
| 485 |
+ return call(f, fThis, collect([beforeArgs, arguments, afterArgs], nonOp)); |
|
| 486 |
+ }; |
|
| 487 |
+ } |
|
| 488 |
+ function partial(f, beforeArgs, afterArgs) {
|
|
| 489 |
+ return bind(f, this, beforeArgs, afterArgs); |
|
| 490 |
+ } |
|
| 491 |
+ function pad(digits, number) {
|
|
| 492 |
+ var signed = number < 0 ? '-' : ''; |
|
| 493 |
+ var preDecimal = (signed?-number:number).toFixed(0); |
|
| 494 |
+ while (preDecimal.length < digits) |
|
| 495 |
+ preDecimal = '0' + preDecimal; |
|
| 496 |
+ return signed + preDecimal; |
|
| 497 |
+ } |
|
| 498 |
+ |
|
| 499 |
+ function processNumCharTemplate(tpl, input, fwd) {
|
|
| 500 |
+ var inHash; |
|
| 501 |
+ var inputPos = 0; |
|
| 502 |
+ var rInput = fwd ? input : reverse(input); |
|
| 503 |
+ var s = (fwd ? tpl : reverse(tpl)).replace(/./g, function(tplChar) {
|
|
| 504 |
+ if (tplChar == '0') {
|
|
| 505 |
+ inHash = false; |
|
| 506 |
+ return rInput.charAt(inputPos++) || '0'; |
|
| 507 |
+ } |
|
| 508 |
+ else if (tplChar == '#') {
|
|
| 509 |
+ inHash = true; |
|
| 510 |
+ return rInput.charAt(inputPos++) || ''; |
|
| 511 |
+ } |
|
| 512 |
+ else |
|
| 513 |
+ return inHash && !rInput.charAt(inputPos) ? '' : tplChar; |
|
| 514 |
+ }); |
|
| 515 |
+ return fwd ? s : (input.substr(0, input.length - inputPos) + reverse(s)); |
|
| 516 |
+ } |
|
| 517 |
+ |
|
| 518 |
+ function getTimezone(match, idx, refDate) { // internal helper, see below
|
|
| 519 |
+ if (idx == _null || !match) |
|
| 520 |
+ return 0; |
|
| 521 |
+ return parseFloat(match[idx]+match[idx+1])*60 + parseFloat(match[idx]+match[idx+2]) + refDate.getTimezoneOffset(); |
|
| 522 |
+ } |
|
| 523 |
+ |
|
| 524 |
+ // formats number with format string (e.g. "#.000", "#,#", "00000", "000.00", "000.000.000,00", "000,000,000.##") |
|
| 525 |
+ // choice syntax: <cmp><value>:<format>|<cmp><value>:<format>|... |
|
| 526 |
+ // e.g. 0:no item|1:one item|>=2:# items |
|
| 527 |
+ // <value>="null" used to compare with nulls. |
|
| 528 |
+ // choice also works with strings or bools, e.g. ERR:error|WAR:warning|FAT:fatal|ok |
|
| 529 |
+ function formatValue(fmt, value) {
|
|
| 530 |
+ var format = replace(fmt, /^\?/); |
|
| 531 |
+ if (isDate(value)) {
|
|
| 532 |
+ var timezone, match; |
|
| 533 |
+ |
|
| 534 |
+ if (match = /^\[(([+-])(\d\d)(\d\d))\]\s*(.*)/.exec(format)) {
|
|
| 535 |
+ timezone = match[1]; |
|
| 536 |
+ value = dateAdd(value, 'minutes', getTimezone(match, 2, value)); |
|
| 537 |
+ format = match[5]; |
|
| 538 |
+ } |
|
| 539 |
+ |
|
| 540 |
+ return replace(format, /(\w)(\1*)(?:\[([^\]]+)\])?/g, function(s, placeholderChar, placeholderDigits, params) {
|
|
| 541 |
+ var val = FORMAT_DATE_MAP[placeholderChar]; |
|
| 542 |
+ if (val) {
|
|
| 543 |
+ var d = value['get' + val[0]](); |
|
| 544 |
+ var optionArray = (params && params.split(','));
|
|
| 545 |
+ |
|
| 546 |
+ if (isList(val[1])) |
|
| 547 |
+ d = (optionArray || val[1])[d]; |
|
| 548 |
+ else |
|
| 549 |
+ d = val[1](d, optionArray, timezone); |
|
| 550 |
+ if (d != _null && !isString(d)) |
|
| 551 |
+ d = pad(placeholderDigits.length+1, d); |
|
| 552 |
+ return d; |
|
| 553 |
+ } |
|
| 554 |
+ else |
|
| 555 |
+ return s; |
|
| 556 |
+ }); |
|
| 557 |
+ |
|
| 558 |
+ } |
|
| 559 |
+ else |
|
| 560 |
+ return find(format.split(/\s*\|\s*/), function(fmtPart) {
|
|
| 561 |
+ var match, numFmtOrResult; |
|
| 562 |
+ if (match = /^([<>]?)(=?)([^:]*?)\s*:\s*(.*)$/.exec(fmtPart)) {
|
|
| 563 |
+ var cmpVal1 = value, cmpVal2 = +(match[3]); |
|
| 564 |
+ if (isNaN(cmpVal2) || !isNumber(cmpVal1)) {
|
|
| 565 |
+ cmpVal1 = (cmpVal1==_null) ? "null" : toString(cmpVal1); // not ""+value, because undefined is treated as null here |
|
| 566 |
+ cmpVal2 = match[3]; |
|
| 567 |
+ } |
|
| 568 |
+ if (match[1]) {
|
|
| 569 |
+ if ((!match[2] && cmpVal1 == cmpVal2 ) || |
|
| 570 |
+ (match[1] == '<' && cmpVal1 > cmpVal2) || |
|
| 571 |
+ (match[1] == '>' && cmpVal1 < cmpVal2)) |
|
| 572 |
+ return _null; |
|
| 573 |
+ } |
|
| 574 |
+ else if (cmpVal1 != cmpVal2) |
|
| 575 |
+ return _null; |
|
| 576 |
+ numFmtOrResult = match[4]; |
|
| 577 |
+ } |
|
| 578 |
+ else |
|
| 579 |
+ numFmtOrResult = fmtPart; |
|
| 580 |
+ |
|
| 581 |
+ if (isNumber(value)) |
|
| 582 |
+ return numFmtOrResult.replace(/[0#](.*[0#])?/, function(numFmt) {
|
|
| 583 |
+ var decimalFmt = /^([^.]+)(\.)([^.]+)$/.exec(numFmt) || /^([^,]+)(,)([^,]+)$/.exec(numFmt); |
|
| 584 |
+ var signed = value < 0 ? '-' : ''; |
|
| 585 |
+ var numData = /(\d+)(\.(\d+))?/.exec((signed?-value:value).toFixed(decimalFmt ? decimalFmt[3].length:0)); |
|
| 586 |
+ var preDecimalFmt = decimalFmt ? decimalFmt[1] : numFmt; |
|
| 587 |
+ var postDecimal = decimalFmt ? processNumCharTemplate(decimalFmt[3], replace(numData[3], /0+$/), true) : ''; |
|
| 588 |
+ |
|
| 589 |
+ return (signed ? '-' : '') + |
|
| 590 |
+ (preDecimalFmt == '#' ? numData[1] : processNumCharTemplate(preDecimalFmt, numData[1])) + |
|
| 591 |
+ (postDecimal.length ? decimalFmt[2] : '') + |
|
| 592 |
+ postDecimal; |
|
| 593 |
+ }); |
|
| 594 |
+ else |
|
| 595 |
+ return numFmtOrResult; |
|
| 596 |
+ }); |
|
| 597 |
+ } |
|
| 598 |
+ // returns date; null if optional and not set; undefined if parsing failed |
|
| 599 |
+ function parseDate(fmt, date) {
|
|
| 600 |
+ var indexMap = {}; // contains reGroupPosition -> typeLetter or [typeLetter, value array]
|
|
| 601 |
+ var reIndex = 1; |
|
| 602 |
+ var timezoneOffsetMatch; |
|
| 603 |
+ var timezoneIndex; |
|
| 604 |
+ var match; |
|
| 605 |
+ |
|
| 606 |
+ var format = replace(fmt, /^\?/); |
|
| 607 |
+ if (format!=fmt && !trim(date)) |
|
| 608 |
+ return _null; |
|
| 609 |
+ |
|
| 610 |
+ if (match = /^\[([+-])(\d\d)(\d\d)\]\s*(.*)/.exec(format)) {
|
|
| 611 |
+ timezoneOffsetMatch = match; |
|
| 612 |
+ format = match[4]; |
|
| 613 |
+ } |
|
| 614 |
+ |
|
| 615 |
+ var parser = new RegExp(format.replace(/(.)(\1*)(?:\[([^\]]*)\])?/g, function(wholeMatch, placeholderChar, placeholderDigits, param) {
|
|
| 616 |
+ if (/[dmhkyhs]/i.test(placeholderChar)) {
|
|
| 617 |
+ indexMap[reIndex++] = placeholderChar; |
|
| 618 |
+ var plen = placeholderDigits.length+1; |
|
| 619 |
+ return "(\\d"+(plen<2?"+":("{1,"+plen+"}"))+")";
|
|
| 620 |
+ } |
|
| 621 |
+ else if (placeholderChar == 'z') {
|
|
| 622 |
+ timezoneIndex = reIndex; |
|
| 623 |
+ reIndex += 3; |
|
| 624 |
+ return "([+-])(\\d\\d)(\\d\\d)"; |
|
| 625 |
+ } |
|
| 626 |
+ else if (/[Nna]/.test(placeholderChar)) {
|
|
| 627 |
+ indexMap[reIndex++] = [placeholderChar, param && param.split(',')];
|
|
| 628 |
+ return "([a-zA-Z\\u0080-\\u1fff]+)"; |
|
| 629 |
+ } |
|
| 630 |
+ else if (/w/i.test(placeholderChar)) |
|
| 631 |
+ return "[a-zA-Z\\u0080-\\u1fff]+"; |
|
| 632 |
+ else if (/\s/.test(placeholderChar)) |
|
| 633 |
+ return "\\s+"; |
|
| 634 |
+ else |
|
| 635 |
+ return escapeRegExp(wholeMatch); |
|
| 636 |
+ })); |
|
| 637 |
+ |
|
| 638 |
+ if (!(match = parser.exec(date))) |
|
| 639 |
+ return undef; |
|
| 640 |
+ |
|
| 641 |
+ var ctorArgs = [0, 0, 0, 0, 0, 0, 0]; |
|
| 642 |
+ for (var i = 1; i < reIndex; i++) {
|
|
| 643 |
+ var matchVal = match[i]; |
|
| 644 |
+ var indexEntry = indexMap[i]; |
|
| 645 |
+ if (isList(indexEntry)) { // for a, n or N
|
|
| 646 |
+ var placeholderChar = indexEntry[0]; |
|
| 647 |
+ var mapEntry = PARSE_DATE_MAP[placeholderChar]; |
|
| 648 |
+ var ctorIndex = mapEntry[0]; |
|
| 649 |
+ var valList = indexEntry[1] || mapEntry[1]; |
|
| 650 |
+ var listValue = find(valList, function(v, index) { if (startsWith(matchVal.toLowerCase(), v.toLowerCase())) return index; });
|
|
| 651 |
+ if (listValue == _null) |
|
| 652 |
+ return undef; |
|
| 653 |
+ if (placeholderChar == 'a') |
|
| 654 |
+ ctorArgs[ctorIndex] += listValue * 12; |
|
| 655 |
+ else |
|
| 656 |
+ ctorArgs[ctorIndex] = listValue; |
|
| 657 |
+ } |
|
| 658 |
+ else if (indexEntry) { // for numeric values (yHmMs)
|
|
| 659 |
+ var value = parseFloat(matchVal); |
|
| 660 |
+ var mapEntry = PARSE_DATE_MAP[indexEntry]; |
|
| 661 |
+ if (isList(mapEntry)) |
|
| 662 |
+ ctorArgs[mapEntry[0]] += value - mapEntry[1]; |
|
| 663 |
+ else |
|
| 664 |
+ ctorArgs[mapEntry] += value; |
|
| 665 |
+ } |
|
| 666 |
+ } |
|
| 667 |
+ var d = new Date(ctorArgs[0], ctorArgs[1], ctorArgs[2], ctorArgs[3], ctorArgs[4], ctorArgs[5], ctorArgs[6]); |
|
| 668 |
+ return dateAdd(d, 'minutes', -getTimezone(timezoneOffsetMatch, 1, d) - getTimezone(match, timezoneIndex, d)); |
|
| 669 |
+ } |
|
| 670 |
+ // format ?##00,00## |
|
| 671 |
+ // returns number; null if optional and not set; undefined if parsing failed |
|
| 672 |
+ function parseNumber(fmt, value) {
|
|
| 673 |
+ var format = replace(fmt, /^\?/); |
|
| 674 |
+ if (format!=fmt && !trim(value)) |
|
| 675 |
+ return _null; |
|
| 676 |
+ var decSep = (/(^|[^0#.,])(,|[0#.]*,[0#]+|[0#]+\.[0#]+\.[0#.,]*)($|[^0#.,])/.test(format)) ? ',' : '.'; |
|
| 677 |
+ var r = parseFloat(replace(replace(replace(value, decSep == ',' ? /\./g : /,/g), decSep, '.'), /^[^\d-]*(-?\d)/, '$1')); |
|
| 678 |
+ return isNaN(r) ? undef : r; |
|
| 679 |
+ } |
|
| 680 |
+ function now() {
|
|
| 681 |
+ return new Date(); |
|
| 682 |
+ } |
|
| 683 |
+ function dateClone(date) {
|
|
| 684 |
+ return new Date(+date); |
|
| 685 |
+ } |
|
| 686 |
+ function capWord(w) {
|
|
| 687 |
+ return w.charAt(0).toUpperCase() + w.substr(1); |
|
| 688 |
+ } |
|
| 689 |
+ function dateAddInline(d, cProp, value) {
|
|
| 690 |
+ d['set'+cProp](d['get'+cProp]() + value); |
|
| 691 |
+ return d; |
|
| 692 |
+ } |
|
| 693 |
+ function dateAdd(date, property, value) {
|
|
| 694 |
+ if (value == _null) |
|
| 695 |
+ return dateAdd(now(), date, property); |
|
| 696 |
+ return dateAddInline(dateClone(date), capWord(property), value); |
|
| 697 |
+ } |
|
| 698 |
+ function dateMidnight(date) {
|
|
| 699 |
+ var od = date || now(); |
|
| 700 |
+ return new Date(od.getFullYear(), od.getMonth(), od.getDate()); |
|
| 701 |
+ } |
|
| 702 |
+ function dateDiff(property, date1, date2) {
|
|
| 703 |
+ var d1t = +date1; |
|
| 704 |
+ var d2t = +date2; |
|
| 705 |
+ var dt = d2t - d1t; |
|
| 706 |
+ if (dt < 0) |
|
| 707 |
+ return -dateDiff(property, date2, date1); |
|
| 708 |
+ |
|
| 709 |
+ var propValues = {'milliseconds': 1, 'seconds': 1000, 'minutes': 60000, 'hours': 3600000};
|
|
| 710 |
+ var ft = propValues[property]; |
|
| 711 |
+ if (ft) |
|
| 712 |
+ return dt / ft; |
|
| 713 |
+ |
|
| 714 |
+ var cProp = capWord(property); |
|
| 715 |
+ var calApproxValues = {'fullYear': 8.64e7*365, 'month': 8.64e7*365/12, 'date': 8.64e7}; // minimum values, a little bit below avg values
|
|
| 716 |
+ var minimumResult = Math.floor((dt / calApproxValues[property])-2); // -2 to remove the imperfections caused by the values above |
|
| 717 |
+ |
|
| 718 |
+ var d = dateAddInline(new Date(d1t), cProp, minimumResult); |
|
| 719 |
+ for (var i = minimumResult; i < minimumResult*1.2+4; i++) { // try out 20% more than needed, just to be sure
|
|
| 720 |
+ if (+dateAddInline(d, cProp, 1) > d2t) |
|
| 721 |
+ return i; |
|
| 722 |
+ } |
|
| 723 |
+ // should never ever be reached |
|
| 724 |
+ } |
|
| 725 |
+ |
|
| 726 |
+ function ucode(a) {
|
|
| 727 |
+ return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
|
|
| 728 |
+ } |
|
| 729 |
+ |
|
| 730 |
+ function escapeJavaScriptString(s) {
|
|
| 731 |
+ return replace(s, /[\x00-\x1f'"\u2028\u2029]/g, ucode); |
|
| 732 |
+ } |
|
| 733 |
+ |
|
| 734 |
+ // reimplemented split for IE8 |
|
| 735 |
+ function split(str, regexp) {
|
|
| 736 |
+ |
|
| 737 |
+ return str.split(regexp); |
|
| 738 |
+ } |
|
| 739 |
+ |
|
| 740 |
+ function template(template, escapeFunction) {
|
|
| 741 |
+ if (templateCache[template]) |
|
| 742 |
+ return templateCache[template]; |
|
| 743 |
+ else {
|
|
| 744 |
+ var funcBody = 'with(_.isObject(obj)?obj:{}){'+
|
|
| 745 |
+ map(split(template, /{{|}}}?/g), function(chunk, index) {
|
|
| 746 |
+ var match, c1 = trim(chunk), c2 = replace(c1, /^{/), escapeSnippet = (c1==c2) ? 'esc(' : '';
|
|
| 747 |
+ if (index%2) { // odd means JS code
|
|
| 748 |
+ if (match = /^each\b(\s+([\w_]+(\s*,\s*[\w_]+)?)\s*:)?(.*)/.exec(c2)) |
|
| 749 |
+ return 'each('+(trim(match[4])?match[4]:'this')+', function('+match[2]+'){';
|
|
| 750 |
+ else if (match = /^if\b(.*)/.exec(c2)) |
|
| 751 |
+ return 'if('+match[1]+'){';
|
|
| 752 |
+ else if (match = /^else\b\s*(if\b(.*))?/.exec(c2)) |
|
| 753 |
+ return '}else ' + (match[1] ? 'if('+match[2] +')' : '')+'{';
|
|
| 754 |
+ else if (match = /^\/(if)?/.exec(c2)) |
|
| 755 |
+ return match[1] ? '}\n' : '});\n'; |
|
| 756 |
+ else if (match = /^(var\s.*)/.exec(c2)) |
|
| 757 |
+ return match[1]+';'; |
|
| 758 |
+ else if (match = /^#(.*)/.exec(c2)) |
|
| 759 |
+ return match[1]; |
|
| 760 |
+ else if (match = /(.*)::\s*(.*)/.exec(c2)) |
|
| 761 |
+ return 'print('+escapeSnippet+'_.formatValue("'+escapeJavaScriptString(match[2])+'",'+(trim(match[1])?match[1]:'this')+(escapeSnippet&&')')+'));\n';
|
|
| 762 |
+ else |
|
| 763 |
+ return 'print('+escapeSnippet+(trim(c2)?c2:'this')+(escapeSnippet&&')')+');\n';
|
|
| 764 |
+ } |
|
| 765 |
+ else if (chunk){
|
|
| 766 |
+ return 'print("'+escapeJavaScriptString(chunk)+'");\n';
|
|
| 767 |
+ } |
|
| 768 |
+ }).join('')+'}';
|
|
| 769 |
+ var f = (new Function('obj', 'each', 'esc', 'print', '_', funcBody));
|
|
| 770 |
+ var t = function(obj, thisContext) {
|
|
| 771 |
+ var result = []; |
|
| 772 |
+ f.call(thisContext || obj, obj, function(obj, func) {
|
|
| 773 |
+ if (isList(obj)) |
|
| 774 |
+ each(obj, function(value, index) { func.call(value, value, index); });
|
|
| 775 |
+ else |
|
| 776 |
+ eachObj(obj, function(key, value) { func.call(value, key, value); });
|
|
| 777 |
+ }, escapeFunction || nonOp, function() {call(result['push'], result, arguments);}, _);
|
|
| 778 |
+ return result.join('');
|
|
| 779 |
+ }; |
|
| 780 |
+ if (templates.push(t) > MAX_CACHED_TEMPLATES) |
|
| 781 |
+ delete templateCache[templates.shift()]; |
|
| 782 |
+ return templateCache[template] = t; |
|
| 783 |
+ } |
|
| 784 |
+ } |
|
| 785 |
+ |
|
| 786 |
+ function escapeHtml(s) {
|
|
| 787 |
+ return replace(s, /[<>'"&]/g, function(s) {
|
|
| 788 |
+ return '&#'+s.charCodeAt(0)+';'; |
|
| 789 |
+ }); |
|
| 790 |
+ } |
|
| 791 |
+ |
|
| 792 |
+ function formatHtml(tpl, obj) {
|
|
| 793 |
+ return template(tpl, escapeHtml)(obj); |
|
| 794 |
+ } |
|
| 795 |
+ |
|
| 796 |
+ function listBindArray(func) {
|
|
| 797 |
+ return function(arg1, arg2) {
|
|
| 798 |
+ return new M(func(this, arg1, arg2)); |
|
| 799 |
+ }; |
|
| 800 |
+ } |
|
| 801 |
+ function listBind(func) {
|
|
| 802 |
+ return function(arg1, arg2, arg3) {
|
|
| 803 |
+ return func(this, arg1, arg2, arg3); |
|
| 804 |
+ }; |
|
| 805 |
+ } |
|
| 806 |
+ function funcArrayBind(func) {
|
|
| 807 |
+ return function(arg1, arg2, arg3) {
|
|
| 808 |
+ return new M(func(arg1, arg2, arg3)); |
|
| 809 |
+ }; |
|
| 810 |
+ } |
|
| 811 |
+ |
|
| 812 |
+ ///#/snippet commonFunctions |
|
| 813 |
+ ///#snippet webFunctions |
|
| 814 |
+ |
|
| 815 |
+ // note: only the web version has the f.item check |
|
| 816 |
+ function isFunction(f) {
|
|
| 817 |
+ return typeof f == 'function' && !f['item']; // item check as work-around for webkit bug 14547 |
|
| 818 |
+ } |
|
| 819 |
+ |
|
| 820 |
+ function isList(v) {
|
|
| 821 |
+ return v && v.length != _null && !isString(v) && !isNode(v) && !isFunction(v) && v !== _window; |
|
| 822 |
+ } |
|
| 823 |
+ |
|
| 824 |
+ // used by IE impl of on() only |
|
| 825 |
+ function push(obj, prop, value) {
|
|
| 826 |
+ (obj[prop] = (obj[prop] || [])).push(value); |
|
| 827 |
+ } |
|
| 828 |
+ // used by IE impl of on()/off() only |
|
| 829 |
+ function removeFromArray(array, value) {
|
|
| 830 |
+ for (var i = 0; array && i < array.length; i++) |
|
| 831 |
+ if (array[i] === value) |
|
| 832 |
+ array['splice'](i--, 1); |
|
| 833 |
+ } |
|
| 834 |
+ |
|
| 835 |
+ function extractNumber(v) {
|
|
| 836 |
+ return parseFloat(replace(v, /^[^\d-]+/)); |
|
| 837 |
+ } |
|
| 838 |
+ |
|
| 839 |
+ // retrieves the node id of the element, create one if needed. |
|
| 840 |
+ function getNodeId(el) {
|
|
| 841 |
+ return (el[MINIFIED_MAGIC_NODEID] = (el[MINIFIED_MAGIC_NODEID] || ++idSequence)); |
|
| 842 |
+ } |
|
| 843 |
+ |
|
| 844 |
+ // collect variant that filters out duplicate nodes from the given list, returns a new array |
|
| 845 |
+ function collectUniqNodes(list, func) {
|
|
| 846 |
+ var result = []; |
|
| 847 |
+ var nodeIds = {};
|
|
| 848 |
+ var currentNodeId; |
|
| 849 |
+ |
|
| 850 |
+ flexiEach(list, function(value) {
|
|
| 851 |
+ flexiEach(func(value), function(node) {
|
|
| 852 |
+ if (!nodeIds[currentNodeId = getNodeId(node)]) {
|
|
| 853 |
+ result.push(node); |
|
| 854 |
+ nodeIds[currentNodeId] = true; |
|
| 855 |
+ } |
|
| 856 |
+ }); |
|
| 857 |
+ }); |
|
| 858 |
+ return result; |
|
| 859 |
+ } |
|
| 860 |
+ |
|
| 861 |
+ // finds out the 'natural' height of the first element, the one if $$slide=1 |
|
| 862 |
+ function getNaturalHeight(elementList, factor) {
|
|
| 863 |
+ var q = {'$position': 'absolute', '$visibility': 'hidden', '$display': 'block', '$height': _null};
|
|
| 864 |
+ var oldStyles = elementList['get'](q); |
|
| 865 |
+ var h = elementList['set'](q)['get']('clientHeight');
|
|
| 866 |
+ elementList['set'](oldStyles); |
|
| 867 |
+ return h*factor + 'px'; |
|
| 868 |
+ } |
|
| 869 |
+ |
|
| 870 |
+ |
|
| 871 |
+ |
|
| 872 |
+ |
|
| 873 |
+ // @condblock !ie8compatibility |
|
| 874 |
+ function on(subSelector, eventSpec, handler, args, bubbleSelector) {
|
|
| 875 |
+ if (isFunction(eventSpec)) |
|
| 876 |
+ return this['on'](_null, subSelector, eventSpec, handler, args); |
|
| 877 |
+ else if (isString(args)) |
|
| 878 |
+ return this['on'](subSelector, eventSpec, handler, _null, args); |
|
| 879 |
+ else |
|
| 880 |
+ return this['each'](function(baseElement, index) {
|
|
| 881 |
+ flexiEach(subSelector ? dollarRaw(subSelector, baseElement) : baseElement, function(registeredOn) {
|
|
| 882 |
+ flexiEach(toString(eventSpec).split(/\s/), function(namePrefixed) {
|
|
| 883 |
+ var name = replace(namePrefixed, /[?|]/g); |
|
| 884 |
+ var prefix = replace(namePrefixed, /[^?|]/g); |
|
| 885 |
+ var capture = (name == 'blur' || name == 'focus') && !!bubbleSelector; // bubble selectors for 'blur' and 'focus' registered as capuring! |
|
| 886 |
+ var triggerId = idSequence++; |
|
| 887 |
+ |
|
| 888 |
+ // returns true if processing should be continued |
|
| 889 |
+ function triggerHandler(eventName, event, target) {
|
|
| 890 |
+ var match = !bubbleSelector; |
|
| 891 |
+ var el = bubbleSelector ? target : registeredOn; |
|
| 892 |
+ if (bubbleSelector) {
|
|
| 893 |
+ var selectorFilter = getFilterFunc(bubbleSelector, registeredOn); |
|
| 894 |
+ while (el && el != registeredOn && !(match = selectorFilter(el))) |
|
| 895 |
+ el = el['parentNode']; |
|
| 896 |
+ } |
|
| 897 |
+ return (!match) || (name != eventName) || ((handler.apply($(el), args || [event, index]) && prefix=='?') || prefix == '|'); |
|
| 898 |
+ }; |
|
| 899 |
+ |
|
| 900 |
+ function eventHandler(event) {
|
|
| 901 |
+ if (!triggerHandler(name, event, event['target'])) {
|
|
| 902 |
+ event['preventDefault'](); |
|
| 903 |
+ event['stopPropagation'](); |
|
| 904 |
+ } |
|
| 905 |
+ }; |
|
| 906 |
+ |
|
| 907 |
+ registeredOn.addEventListener(name, eventHandler, capture); |
|
| 908 |
+ |
|
| 909 |
+ if (!registeredOn['M']) |
|
| 910 |
+ registeredOn['M'] = {};
|
|
| 911 |
+ registeredOn['M'][triggerId] = triggerHandler; // to be called by trigger() |
|
| 912 |
+ |
|
| 913 |
+ handler['M'] = collector(flexiEach, [handler['M'], function () { // this function will be called by off()
|
|
| 914 |
+ registeredOn.removeEventListener(name, eventHandler, capture); |
|
| 915 |
+ delete registeredOn['M'][triggerId]; |
|
| 916 |
+ }], nonOp); |
|
| 917 |
+ |
|
| 918 |
+ }); |
|
| 919 |
+ }); |
|
| 920 |
+ }); |
|
| 921 |
+ } |
|
| 922 |
+ // @condend !ie8compatibility |
|
| 923 |
+ |
|
| 924 |
+ |
|
| 925 |
+ // @condblock !ie8compatibility |
|
| 926 |
+ function off(handler) {
|
|
| 927 |
+ callList(handler['M']); |
|
| 928 |
+ handler['M'] = _null; |
|
| 929 |
+ } |
|
| 930 |
+ // @condend !ie8compatibility |
|
| 931 |
+ |
|
| 932 |
+ // for remove & window.unload, IE only |
|
| 933 |
+ function detachHandlerList(dummy, handlerList) {
|
|
| 934 |
+ flexiEach(handlerList, function(h) {
|
|
| 935 |
+ h.element.detachEvent('on'+h.eventType, h.handlerFunc);
|
|
| 936 |
+ }); |
|
| 937 |
+ } |
|
| 938 |
+ |
|
| 939 |
+ function ready(handler) {
|
|
| 940 |
+ if (DOMREADY_HANDLER) |
|
| 941 |
+ DOMREADY_HANDLER.push(handler); |
|
| 942 |
+ else |
|
| 943 |
+ setTimeout(handler, 0); |
|
| 944 |
+ } |
|
| 945 |
+ |
|
| 946 |
+ function $$(selector, context, childrenOnly) {
|
|
| 947 |
+ return dollarRaw(selector, context, childrenOnly)[0]; |
|
| 948 |
+ } |
|
| 949 |
+ |
|
| 950 |
+ function EE(elementName, attributes, children) {
|
|
| 951 |
+ var e = $(document.createElement(elementName)); |
|
| 952 |
+ // @condblock UTIL |
|
| 953 |
+ // this attributes != null check is only required with Util's isObject() implementation. Web's isObject() is simpler. |
|
| 954 |
+ return (isList(attributes) || (attributes != _null && !isObject(attributes)) ) ? e['add'](attributes) : e['set'](attributes)['add'](children); |
|
| 955 |
+ // @condend UTIL |
|
| 956 |
+ // @cond !UTIL return (isList(attributes) || (!isObject(attributes)) ) ? e['add'](attributes) : e['set'](attributes)['add'](children); |
|
| 957 |
+ } |
|
| 958 |
+ |
|
| 959 |
+ function clone(listOrNode) {
|
|
| 960 |
+ return collector(flexiEach, listOrNode, function(e) {
|
|
| 961 |
+ var c; |
|
| 962 |
+ if (isList(e)) |
|
| 963 |
+ return clone(e); |
|
| 964 |
+ else if (isNode(e)) {
|
|
| 965 |
+ c = e['cloneNode'](true); |
|
| 966 |
+ c['removeAttribute'] && c['removeAttribute']('id');
|
|
| 967 |
+ return c; |
|
| 968 |
+ } |
|
| 969 |
+ else |
|
| 970 |
+ return e; |
|
| 971 |
+ }); |
|
| 972 |
+ } |
|
| 973 |
+ |
|
| 974 |
+ /*$ |
|
| 975 |
+ * @stop |
|
| 976 |
+ */ |
|
| 977 |
+ |
|
| 978 |
+ function $(selector, context, childOnly) {
|
|
| 979 |
+ // @condblock ready |
|
| 980 |
+ return isFunction(selector) ? ready(selector) : new M(dollarRaw(selector, context, childOnly)); |
|
| 981 |
+ // @condend |
|
| 982 |
+ // @cond !ready return new M(dollarRaw(selector, context)); |
|
| 983 |
+ } |
|
| 984 |
+ |
|
| 985 |
+ // implementation of $ that does not produce a Minified list, but just an array |
|
| 986 |
+ |
|
| 987 |
+ |
|
| 988 |
+ |
|
| 989 |
+ |
|
| 990 |
+ |
|
| 991 |
+ |
|
| 992 |
+ |
|
| 993 |
+ |
|
| 994 |
+ |
|
| 995 |
+ |
|
| 996 |
+ // @condblock !ie7compatibility |
|
| 997 |
+ function dollarRaw(selector, context, childOnly) {
|
|
| 998 |
+ function flatten(a) { // flatten list, keep non-lists, remove nulls
|
|
| 999 |
+ return isList(a) ? collector(flexiEach, a, flatten) : a; |
|
| 1000 |
+ } |
|
| 1001 |
+ function filterElements(list) { // converts into array, makes sure context is respected
|
|
| 1002 |
+ return filter(collector(flexiEach, list, flatten), function(node) {
|
|
| 1003 |
+ var a = node; |
|
| 1004 |
+ while (a = a['parentNode']) |
|
| 1005 |
+ if (a == context[0] || childOnly) |
|
| 1006 |
+ return a == context[0]; |
|
| 1007 |
+ // fall through to return undef |
|
| 1008 |
+ }); |
|
| 1009 |
+ } |
|
| 1010 |
+ |
|
| 1011 |
+ if (context) {
|
|
| 1012 |
+ if ((context = dollarRaw(context)).length != 1) |
|
| 1013 |
+ return collectUniqNodes(context, function(ci) { return dollarRaw(selector, ci, childOnly);});
|
|
| 1014 |
+ else if (isString(selector)) {
|
|
| 1015 |
+ if (isNode(context[0]) != 1) |
|
| 1016 |
+ return []; |
|
| 1017 |
+ else |
|
| 1018 |
+ return childOnly ? filterElements(context[0].querySelectorAll(selector)) : context[0].querySelectorAll(selector); |
|
| 1019 |
+ } |
|
| 1020 |
+ else |
|
| 1021 |
+ return filterElements(selector); |
|
| 1022 |
+ |
|
| 1023 |
+ } |
|
| 1024 |
+ else if (isString(selector)) |
|
| 1025 |
+ return document.querySelectorAll(selector); |
|
| 1026 |
+ else |
|
| 1027 |
+ return collector(flexiEach, selector, flatten); |
|
| 1028 |
+ }; |
|
| 1029 |
+ // @condend !ie7compatibility |
|
| 1030 |
+ |
|
| 1031 |
+ // If context is set, live updates will be possible. |
|
| 1032 |
+ // Please note that the context is not evaluated for the '*' and 'tagname.classname' patterns, because context is used only |
|
| 1033 |
+ // by on(), and in on() only nodes in the right context will be checked |
|
| 1034 |
+ function getFilterFunc(selector, context) {
|
|
| 1035 |
+ function wordRegExpTester(name, prop) {
|
|
| 1036 |
+ var re = RegExp('(^|\\s+)' + name + '(?=$|\\s)', 'i');
|
|
| 1037 |
+ return function(obj) {return name ? re.test(obj[prop]) : true;};
|
|
| 1038 |
+ } |
|
| 1039 |
+ |
|
| 1040 |
+ var nodeSet = {};
|
|
| 1041 |
+ var dotPos = nodeSet; |
|
| 1042 |
+ if (isFunction(selector)) |
|
| 1043 |
+ return selector; |
|
| 1044 |
+ else if (isNumber(selector)) |
|
| 1045 |
+ return function(v, index) { return index == selector; };
|
|
| 1046 |
+ else if (!selector || selector == '*' || |
|
| 1047 |
+ (isString(selector) && (dotPos = /^([\w-]*)\.?([\w-]*)$/.exec(selector)))) {
|
|
| 1048 |
+ var nodeNameFilter = wordRegExpTester(dotPos[1], 'tagName'); |
|
| 1049 |
+ var classNameFilter = wordRegExpTester(dotPos[2], 'className'); |
|
| 1050 |
+ return function(v) {
|
|
| 1051 |
+ return isNode(v) == 1 && nodeNameFilter(v) && classNameFilter(v); |
|
| 1052 |
+ }; |
|
| 1053 |
+ } |
|
| 1054 |
+ else if (context) |
|
| 1055 |
+ return function(v) {
|
|
| 1056 |
+ return $(selector, context)['find'](v)!=_null; // live search instead of node set, for on() |
|
| 1057 |
+ }; |
|
| 1058 |
+ else {
|
|
| 1059 |
+ $(selector)['each'](function(node) {
|
|
| 1060 |
+ nodeSet[getNodeId(node)] = true; |
|
| 1061 |
+ }); |
|
| 1062 |
+ return function(v) {
|
|
| 1063 |
+ return nodeSet[getNodeId(v)]; |
|
| 1064 |
+ }; |
|
| 1065 |
+ } |
|
| 1066 |
+ } |
|
| 1067 |
+ |
|
| 1068 |
+ function getInverseFilterFunc(selector) {
|
|
| 1069 |
+ var f = getFilterFunc(selector); |
|
| 1070 |
+ return function(v) {return f(v) ? _null : true;};
|
|
| 1071 |
+ } |
|
| 1072 |
+ ///#/snippet webFunctions |
|
| 1073 |
+ |
|
| 1074 |
+ ///#snippet extrasFunctions |
|
| 1075 |
+ function flexiEach(list, cb) {
|
|
| 1076 |
+ if (isList(list)) |
|
| 1077 |
+ each(list, cb); |
|
| 1078 |
+ else if (list != _null) |
|
| 1079 |
+ cb(list, 0); |
|
| 1080 |
+ return list; |
|
| 1081 |
+ } |
|
| 1082 |
+ |
|
| 1083 |
+ function Promise() {
|
|
| 1084 |
+ this['state'] = null; |
|
| 1085 |
+ this['values'] = []; |
|
| 1086 |
+ this['parent'] = null; |
|
| 1087 |
+ } |
|
| 1088 |
+ |
|
| 1089 |
+ /*$ |
|
| 1090 |
+ * @id promise |
|
| 1091 |
+ * @name _.promise() |
|
| 1092 |
+ * @syntax _.promise() |
|
| 1093 |
+ * @syntax _.promise(otherPromises...) |
|
| 1094 |
+ * @module WEB+UTIL |
|
| 1095 |
+ * |
|
| 1096 |
+ * Creates a new ##promiseClass#Promise##, optionally assimilating other promises. If no other promise is given, |
|
| 1097 |
+ * a fresh new promise is returned. |
|
| 1098 |
+ * |
|
| 1099 |
+ * The returned promise provides the methods ##fulfill() and ##reject() that can be called directly to change the promise's state, |
|
| 1100 |
+ * as well as the more powerful ##fire(). |
|
| 1101 |
+ * |
|
| 1102 |
+ * If one promise is given as parameter, the new promise assimilates the given promise as-is, and just forwards |
|
| 1103 |
+ * fulfillment and rejection with the original values. |
|
| 1104 |
+ * |
|
| 1105 |
+ * If more than one promise are given, it will assimilate all of them with slightly different rules: |
|
| 1106 |
+ * <ul><li>the new promise is fulfilled if all assimilated promises have been fulfilled. The fulfillment values |
|
| 1107 |
+ * of all assimilated promises are given to the handler as arguments. Note that the fulfillment values themselves are always |
|
| 1108 |
+ * arrays, as a promise can have several fulfillment values in Minified's implementation.</li> |
|
| 1109 |
+ * <li>when one of the promises is rejected, the new promise is rejected immediately. The rejection handler gets the |
|
| 1110 |
+ * promises rejection value (first argument if it got several) as first argument, an array of the result values |
|
| 1111 |
+ * of all promises as a second (that means one array of arguments for each promise), and the index of the failed |
|
| 1112 |
+ * promise as third. |
|
| 1113 |
+ * </li></ul> |
|
| 1114 |
+ * |
|
| 1115 |
+ * @example A simple promise that is fulfilled after 1 second, using Minified's invocation syntax: |
|
| 1116 |
+ * <pre>var p = _.promise(); |
|
| 1117 |
+ * setTimeout(function() {
|
|
| 1118 |
+ * p.fire(true); |
|
| 1119 |
+ * }, 1000); |
|
| 1120 |
+ * </pre> |
|
| 1121 |
+ * |
|
| 1122 |
+ * @example Request three files in parallel. When all three have been downloaded, concatenate them into a single string. |
|
| 1123 |
+ * <pre> |
|
| 1124 |
+ * var files = _('fileA.txt', 'fileA.txt', 'fileC.txt');
|
|
| 1125 |
+ * var content; |
|
| 1126 |
+ * _.promise(files.map(function(file) {
|
|
| 1127 |
+ * return $.request('get', '/txts/' + file);
|
|
| 1128 |
+ * })).then(function(fileRslt1, fileRslt2, fileRslt3) {
|
|
| 1129 |
+ * content = _(fileRslt1, fileRslt2, fileRslt3).map( function(result) { return result[0]; }).join('');
|
|
| 1130 |
+ * }).error(function(status, response, xhr, url) {
|
|
| 1131 |
+ * alert('failed to load file '+url);
|
|
| 1132 |
+ * }); |
|
| 1133 |
+ * </pre> |
|
| 1134 |
+ * |
|
| 1135 |
+ * @param otherPromises one or more promises to assimilate (varargs). You can also pass lists of promises. |
|
| 1136 |
+ * @return the new promise. |
|
| 1137 |
+ */ |
|
| 1138 |
+ function promise() {
|
|
| 1139 |
+ var deferred = []; // this function calls the functions supplied by then() |
|
| 1140 |
+ |
|
| 1141 |
+ var assimilatedPromises = arguments; |
|
| 1142 |
+ var assimilatedNum = assimilatedPromises.length; |
|
| 1143 |
+ var numCompleted = 0; // number of completed, assimilated promises |
|
| 1144 |
+ var rejectionHandlerNum = 0; |
|
| 1145 |
+ |
|
| 1146 |
+ var obj = new Promise(); |
|
| 1147 |
+ |
|
| 1148 |
+ obj['errHandled'] = function() {
|
|
| 1149 |
+ rejectionHandlerNum++; |
|
| 1150 |
+ if (obj['parent']) |
|
| 1151 |
+ obj['parent']['errHandled'](); |
|
| 1152 |
+ }; |
|
| 1153 |
+ |
|
| 1154 |
+ /*$ |
|
| 1155 |
+ * @id fire |
|
| 1156 |
+ * @name promise.fire() |
|
| 1157 |
+ * @syntax _.fire(newState) |
|
| 1158 |
+ * @syntax _.fire(newState, values) |
|
| 1159 |
+ * @module WEB+UTIL |
|
| 1160 |
+ * |
|
| 1161 |
+ * Changes the state of the promise into either fulfilled or rejected. This will also notify all ##then() handlers. If the promise |
|
| 1162 |
+ * already has a state, the call will be ignored. |
|
| 1163 |
+ * |
|
| 1164 |
+ * <var>fire()</var> can be invoked as a function without context ('this'). Every promise has its own instance.
|
|
| 1165 |
+ * |
|
| 1166 |
+ * @example A simple promise that is fulfilled after 1 second, using Minified's invocation syntax: |
|
| 1167 |
+ * <pre>var p = _.promise(); |
|
| 1168 |
+ * setTimeout(function() {
|
|
| 1169 |
+ * p.fire(true, []); |
|
| 1170 |
+ * }, 1000); |
|
| 1171 |
+ * </pre> |
|
| 1172 |
+ * |
|
| 1173 |
+ * @example Call <var>fire()</var> without a context: |
|
| 1174 |
+ * <pre>var p = _.promise(function(resolve, reject) {
|
|
| 1175 |
+ * setTimeout(resolve.fire, 1000); |
|
| 1176 |
+ * }); |
|
| 1177 |
+ * </pre> |
|
| 1178 |
+ * |
|
| 1179 |
+ * @param newState <var>true</var> to set the Promise to fulfilled, <var>false</var> to set the state as rejected. If you pass <var>null</var> or |
|
| 1180 |
+ * <var>undefined</var>, the promise's state does not change. |
|
| 1181 |
+ * @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 |
|
| 1182 |
+ * be passed as only argument. |
|
| 1183 |
+ * @return the promise |
|
| 1184 |
+ */ |
|
| 1185 |
+ var fire = obj['fire'] = function(newState, newValues) {
|
|
| 1186 |
+ if (obj['state'] == null && newState != null) {
|
|
| 1187 |
+ obj['state'] = !!newState; |
|
| 1188 |
+ obj['values'] = isList(newValues) ? newValues : [newValues]; |
|
| 1189 |
+ setTimeout(function() {
|
|
| 1190 |
+ each(deferred, function(f) {f();});
|
|
| 1191 |
+ }, 0); |
|
| 1192 |
+ } |
|
| 1193 |
+ return obj; |
|
| 1194 |
+ }; |
|
| 1195 |
+ |
|
| 1196 |
+ // use promise varargs |
|
| 1197 |
+ each(assimilatedPromises, function assimilate(promise, index) {
|
|
| 1198 |
+ try {
|
|
| 1199 |
+ if (promise['then']) |
|
| 1200 |
+ promise['then'](function(v) {
|
|
| 1201 |
+ var then; |
|
| 1202 |
+ if ((isObject(v) || isFunction(v)) && isFunction(then = v['then'])) |
|
| 1203 |
+ assimilate(v, index); |
|
| 1204 |
+ else {
|
|
| 1205 |
+ obj['values'][index] = array(arguments); |
|
| 1206 |
+ if (++numCompleted == assimilatedNum) |
|
| 1207 |
+ fire(true, assimilatedNum < 2 ? obj['values'][index] : obj['values']); |
|
| 1208 |
+ } |
|
| 1209 |
+ }, |
|
| 1210 |
+ function(e) {
|
|
| 1211 |
+ obj['values'][index] = array(arguments); |
|
| 1212 |
+ fire(false, assimilatedNum < 2 ? obj['values'][index] : [obj['values'][index][0], obj['values'], index]); |
|
| 1213 |
+ }); |
|
| 1214 |
+ else |
|
| 1215 |
+ promise(function() {fire(true, array(arguments));}, function() {fire(false, array(arguments)); });
|
|
| 1216 |
+ } |
|
| 1217 |
+ catch (e) {
|
|
| 1218 |
+ fire(false, [e, obj['values'], index]); |
|
| 1219 |
+ } |
|
| 1220 |
+ }); |
|
| 1221 |
+ |
|
| 1222 |
+ /*$ |
|
| 1223 |
+ * @id stop |
|
| 1224 |
+ * @name promise.stop() |
|
| 1225 |
+ * @syntax promise.stop() |
|
| 1226 |
+ * @module WEB+UTIL |
|
| 1227 |
+ * Stops an ongoing operation, if supported. Currently the only promises supporting this are those returned by ##request(), ##animate(), ##wait() and |
|
| 1228 |
+ * ##asyncEach(). |
|
| 1229 |
+ * stop() invocation will be propagated over promises returned by ##then() and promises assimilated by ##promise(). You only need to invoke stop |
|
| 1230 |
+ * with the last promise, and all dependent promises will automatically stop as well. |
|
| 1231 |
+ * |
|
| 1232 |
+ * <var>stop()</var> can be invoked as a function without context ('this'). Every promise has its own instance.
|
|
| 1233 |
+ * |
|
| 1234 |
+ * @return In some cases, the <var>stop()</var> can return a value. This is currently only done by ##animate() and ##wait(), which will return the actual duration. |
|
| 1235 |
+ * ##asyncEach()'s promise will also return any value it got from the promise that it stopped. |
|
| 1236 |
+ * |
|
| 1237 |
+ * @example Animation chain that can be stopped. |
|
| 1238 |
+ * <pre> |
|
| 1239 |
+ * var div = $('#myMovingDiv').set({$left: '0px', $top: '0px'});
|
|
| 1240 |
+ * var prom = div.animate({$left: '200px', $top: '0px'}, 600, 0)
|
|
| 1241 |
+ * .then(function() {
|
|
| 1242 |
+ * return _.promise(div.animate({$left: '200px', $top: '200px'}, 800, 0),
|
|
| 1243 |
+ * div.animate({$backgroundColor: '#f00'}, 200));
|
|
| 1244 |
+ * }).then(function() {
|
|
| 1245 |
+ * return div.animate({$left: '100px', $top: '100px'}, 400);
|
|
| 1246 |
+ * }); |
|
| 1247 |
+ * |
|
| 1248 |
+ * $('#stopButton').on('click', prom.stop);
|
|
| 1249 |
+ * </pre> |
|
| 1250 |
+ */ |
|
| 1251 |
+ obj['stop'] = function() {
|
|
| 1252 |
+ each(assimilatedPromises, function(promise) {
|
|
| 1253 |
+ if (promise['stop']) |
|
| 1254 |
+ promise['stop'](); |
|
| 1255 |
+ }); |
|
| 1256 |
+ |
|
| 1257 |
+ return obj['stop0'] && call(obj['stop0']); |
|
| 1258 |
+ }; |
|
| 1259 |
+ |
|
| 1260 |
+ /*$ |
|
| 1261 |
+ * @id then |
|
| 1262 |
+ * @name promise.then() |
|
| 1263 |
+ * @syntax promise.then() |
|
| 1264 |
+ * @syntax promise.then(onSuccess) |
|
| 1265 |
+ * @syntax promise.then(onSuccess, onError) |
|
| 1266 |
+ * |
|
| 1267 |
+ * @module WEB |
|
| 1268 |
+ * Registers two callbacks that will be invoked when the ##promise#Promise##'s asynchronous operation finished |
|
| 1269 |
+ * successfully (<var>onSuccess</var>) or an error occurred (<var>onError</var>). The callbacks will be called after |
|
| 1270 |
+ * <var>then()</var> returned, from the browser's event loop. |
|
| 1271 |
+ * You can chain <var>then()</var> invocations, as <var>then()</var> returns another Promise object that you can attach to. |
|
| 1272 |
+ * |
|
| 1273 |
+ * The full distribution of Minified implements the Promises/A+ specification, allowing interoperability with other Promises frameworks. |
|
| 1274 |
+ * |
|
| 1275 |
+ * <strong>Note:</strong> If you use the Web module, you will get a simplified Promises implementation that cuts some corners. The most notable |
|
| 1276 |
+ * difference is that when a <code>then()</code> handler throws an exception, this will not be caught and the promise returned by |
|
| 1277 |
+ * <code>then</code> will not be automatically rejected. |
|
| 1278 |
+ * |
|
| 1279 |
+ * @example Simple handler for an HTTP request. Handles only success and ignores errors. |
|
| 1280 |
+ * <pre> |
|
| 1281 |
+ * $.request('get', '/weather.html')
|
|
| 1282 |
+ * .then(function(txt) {
|
|
| 1283 |
+ * alert('Got response!');
|
|
| 1284 |
+ * }); |
|
| 1285 |
+ * </pre> |
|
| 1286 |
+ * |
|
| 1287 |
+ * @example Including an error handler. |
|
| 1288 |
+ * <pre> |
|
| 1289 |
+ * $.request('get', '/weather.html')
|
|
| 1290 |
+ * .then(function(txt) {
|
|
| 1291 |
+ * alert('Got response!');
|
|
| 1292 |
+ * }, function(err) {
|
|
| 1293 |
+ * alert('Error!');
|
|
| 1294 |
+ * })); |
|
| 1295 |
+ * </pre> |
|
| 1296 |
+ * |
|
| 1297 |
+ * @example Chained handler. |
|
| 1298 |
+ * <pre> |
|
| 1299 |
+ * $.request('get', '/weather.do')
|
|
| 1300 |
+ * .then(function(txt) {
|
|
| 1301 |
+ * showWeather(txt); |
|
| 1302 |
+ * } |
|
| 1303 |
+ * .then(function() {
|
|
| 1304 |
+ * return $.request('get', '/traffic.do');
|
|
| 1305 |
+ * } |
|
| 1306 |
+ * .then(function(txt) {
|
|
| 1307 |
+ * showTraffic(txt); |
|
| 1308 |
+ * } |
|
| 1309 |
+ * .then(function() {
|
|
| 1310 |
+ * alert('All result displayed');
|
|
| 1311 |
+ * }, function() {
|
|
| 1312 |
+ * alert('An error occurred');
|
|
| 1313 |
+ * }); |
|
| 1314 |
+ * </pre> |
|
| 1315 |
+ * |
|
| 1316 |
+ * @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. |
|
| 1317 |
+ * If the function returns a ##promise#Promise##, that Promise will be evaluated to determine the state of the promise returned by <var>then()</var>. If it returns any other value, the |
|
| 1318 |
+ * returned Promise will also succeed. If the function throws an error, the returned Promise will be in error state. |
|
| 1319 |
+ * Pass <var>null</var> or <var>undefined</var> if you do not need the success handler. |
|
| 1320 |
+ * @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 |
|
| 1321 |
+ * be evaluated to determine the state of the Promise returned by <var>then()</var>. If it returns anything else, the returned Promise will |
|
| 1322 |
+ * have success status. If the function throws an error, the returned Promise will be in the error state. |
|
| 1323 |
+ * You can pass <var>null</var> or <var>undefined</var> if you do not need the error handler. |
|
| 1324 |
+ * @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. |
|
| 1325 |
+ * If no callback has been provided and the original Promise changes to that state, the new Promise will change to that state as well. |
|
| 1326 |
+ */ |
|
| 1327 |
+ var then = obj['then'] = function (onFulfilled, onRejected) {
|
|
| 1328 |
+ var promise2 = promise(); |
|
| 1329 |
+ var callCallbacks = function() {
|
|
| 1330 |
+ try {
|
|
| 1331 |
+ var f = (obj['state'] ? onFulfilled : onRejected); |
|
| 1332 |
+ if (isFunction(f)) {
|
|
| 1333 |
+ (function resolve(x) {
|
|
| 1334 |
+ try {
|
|
| 1335 |
+ var then, cbCalled = 0; |
|
| 1336 |
+ if ((isObject(x) || isFunction(x)) && isFunction(then = x['then'])) {
|
|
| 1337 |
+ if (x === promise2) |
|
| 1338 |
+ throw new TypeError(); |
|
| 1339 |
+ then.call(x, function(x) { if (!cbCalled++) resolve(x); }, function(value) { if (!cbCalled++) promise2['fire'](false, [value]);});
|
|
| 1340 |
+ promise2['stop0'] = x['stop']; |
|
| 1341 |
+ } |
|
| 1342 |
+ else |
|
| 1343 |
+ promise2['fire'](true, [x]); |
|
| 1344 |
+ } |
|
| 1345 |
+ catch(e) {
|
|
| 1346 |
+ if (!(cbCalled++)) {
|
|
| 1347 |
+ promise2['fire'](false, [e]); |
|
| 1348 |
+ if (!rejectionHandlerNum) |
|
| 1349 |
+ throw e; |
|
| 1350 |
+ } |
|
| 1351 |
+ } |
|
| 1352 |
+ })(call(f, undef, obj['values'])); |
|
| 1353 |
+ } |
|
| 1354 |
+ else |
|
| 1355 |
+ promise2['fire'](obj['state'], obj['values']); |
|
| 1356 |
+ } |
|
| 1357 |
+ catch (e) {
|
|
| 1358 |
+ promise2['fire'](false, [e]); |
|
| 1359 |
+ if (!rejectionHandlerNum) |
|
| 1360 |
+ throw e; |
|
| 1361 |
+ } |
|
| 1362 |
+ }; |
|
| 1363 |
+ if (isFunction(onRejected)) |
|
| 1364 |
+ obj['errHandled'](); |
|
| 1365 |
+ promise2['stop0'] = obj['stop']; |
|
| 1366 |
+ promise2['parent'] = obj; |
|
| 1367 |
+ if (obj['state'] != null) |
|
| 1368 |
+ setTimeout(callCallbacks, 0); |
|
| 1369 |
+ else |
|
| 1370 |
+ deferred.push(callCallbacks); |
|
| 1371 |
+ return promise2; |
|
| 1372 |
+ }; |
|
| 1373 |
+ |
|
| 1374 |
+ /*$ |
|
| 1375 |
+ * @id always |
|
| 1376 |
+ * @group REQUEST |
|
| 1377 |
+ * @name promise.always() |
|
| 1378 |
+ * @syntax promise.always(callback) |
|
| 1379 |
+ * @configurable default |
|
| 1380 |
+ * @module WEB+UTIL |
|
| 1381 |
+ * Registers a callback that will always be called when the ##promise#Promise##'s operation ended, no matter whether the operation succeeded or not. |
|
| 1382 |
+ * This is a convenience function that will call ##then() with the same function for both arguments. It shares all of its semantics. |
|
| 1383 |
+ * |
|
| 1384 |
+ * @example Simple handler for a HTTP request. |
|
| 1385 |
+ * <pre> |
|
| 1386 |
+ * $.request('get', '/weather.html')
|
|
| 1387 |
+ * .always(function() {
|
|
| 1388 |
+ * alert('Got response or error!');
|
|
| 1389 |
+ * }); |
|
| 1390 |
+ * </pre> |
|
| 1391 |
+ * |
|
| 1392 |
+ * @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 |
|
| 1393 |
+ * vary depending on whether it succeeded or not. If the function returns a ##promise#Promise##, that Promise will |
|
| 1394 |
+ * be evaluated to determine the state of the returned Promise. If provided and it returns regularly, the returned promise will |
|
| 1395 |
+ * have success status. If it throws an error, the returned Promise will be in the error state. |
|
| 1396 |
+ * @return a new ##promise#Promise## object. Its state is determined by the callback. |
|
| 1397 |
+ */ |
|
| 1398 |
+ obj['always'] = function(func) { return then(func, func); };
|
|
| 1399 |
+ |
|
| 1400 |
+ /*$ |
|
| 1401 |
+ * @id error |
|
| 1402 |
+ * @group REQUEST |
|
| 1403 |
+ * @name promise.error() |
|
| 1404 |
+ * @syntax promise.error(callback) |
|
| 1405 |
+ * @configurable default |
|
| 1406 |
+ * @module WEB, UTIL |
|
| 1407 |
+ * Registers a callback that will be called when the operation failed. |
|
| 1408 |
+ * This is a convenience function that will invoke ##then() with only the second argument set. It shares all of its semantics. |
|
| 1409 |
+ * |
|
| 1410 |
+ * @example Simple handler for a HTTP request. |
|
| 1411 |
+ * <pre> |
|
| 1412 |
+ * $.request('get', '/weather.html')
|
|
| 1413 |
+ * .error(function() {
|
|
| 1414 |
+ * alert('Got error!');
|
|
| 1415 |
+ * }); |
|
| 1416 |
+ * </pre> |
|
| 1417 |
+ * |
|
| 1418 |
+ * @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 |
|
| 1419 |
+ * be evaluated to determine the state of the returned Promise. If it returns regularly, the returned Promise will |
|
| 1420 |
+ * have success status. If it throws an error, the returned Promise will be in error state. |
|
| 1421 |
+ * @return a new ##promise#Promise## object. Its state is determined by the callback. |
|
| 1422 |
+ */ |
|
| 1423 |
+ obj['error'] = function(func) { return then(0, func); };
|
|
| 1424 |
+ |
|
| 1425 |
+ return obj; |
|
| 1426 |
+ } |
|
| 1427 |
+ |
|
| 1428 |
+ ///#/snippet extrasFunctions |
|
| 1429 |
+ ///#snippet extrasDocs |
|
| 1430 |
+ /*$ |
|
| 1431 |
+ * @id length |
|
| 1432 |
+ * @group SELECTORS |
|
| 1433 |
+ * @requires dollar |
|
| 1434 |
+ * @name list.length |
|
| 1435 |
+ * @syntax length |
|
| 1436 |
+ * @module WEB, UTIL |
|
| 1437 |
+ * |
|
| 1438 |
+ * Contains the number of elements in the ##list#Minified list##. |
|
| 1439 |
+ * |
|
| 1440 |
+ * @example With Web module: |
|
| 1441 |
+ * <pre> |
|
| 1442 |
+ * var list = $('input');
|
|
| 1443 |
+ * var myValues = {};
|
|
| 1444 |
+ * for (var i = 0; i < list.length; i++) |
|
| 1445 |
+ * myValues[list[i].name] = list[i].value; |
|
| 1446 |
+ * </pre> |
|
| 1447 |
+ * |
|
| 1448 |
+ * @example With Util module: |
|
| 1449 |
+ * <pre> |
|
| 1450 |
+ * var list = _(1, 2, 3); |
|
| 1451 |
+ * var sum = 0; |
|
| 1452 |
+ * for (var i = 0; i < list.length; i++) |
|
| 1453 |
+ * sum += list[i]; |
|
| 1454 |
+ * </pre> |
|
| 1455 |
+ */ |
|
| 1456 |
+ /*$ |
|
| 1457 |
+ * @stop |
|
| 1458 |
+ */ |
|
| 1459 |
+ ///#/snippet extrasDocs |
|
| 1460 |
+ |
|
| 1461 |
+ ///#snippet utilM |
|
| 1462 |
+ |
|
| 1463 |
+ /* |
|
| 1464 |
+ * syntax: M(list, assimilateSublists) |
|
| 1465 |
+ * M(null, singleElement) |
|
| 1466 |
+ * |
|
| 1467 |
+ * |
|
| 1468 |
+ */ |
|
| 1469 |
+ /** @constructor */ |
|
| 1470 |
+ function M(list, assimilateSublists) {
|
|
| 1471 |
+ var self = this, idx = 0; |
|
| 1472 |
+ if (list) |
|
| 1473 |
+ for (var i = 0, len = list.length; i < len; i++) {
|
|
| 1474 |
+ var item = list[i]; |
|
| 1475 |
+ if (assimilateSublists && isList(item)) |
|
| 1476 |
+ for (var j = 0, len2 = item.length; j < len2; j++) |
|
| 1477 |
+ self[idx++] = item[j]; |
|
| 1478 |
+ else |
|
| 1479 |
+ self[idx++] = item; |
|
| 1480 |
+ } |
|
| 1481 |
+ else |
|
| 1482 |
+ self[idx++] = assimilateSublists; |
|
| 1483 |
+ |
|
| 1484 |
+ self['length'] = idx; |
|
| 1485 |
+ self['_'] = true; |
|
| 1486 |
+ } |
|
| 1487 |
+ |
|
| 1488 |
+ function _() {
|
|
| 1489 |
+ return new M(arguments, true); |
|
| 1490 |
+ } |
|
| 1491 |
+ |
|
| 1492 |
+ ///#/snippet utilM |
|
| 1493 |
+ |
|
| 1494 |
+ //// LIST FUNCTIONS //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
| 1495 |
+ |
|
| 1496 |
+ copyObj({
|
|
| 1497 |
+ ///#snippet utilListFuncs |
|
| 1498 |
+ /*$ |
|
| 1499 |
+ * @id each |
|
| 1500 |
+ * @group LIST |
|
| 1501 |
+ * @requires |
|
| 1502 |
+ * @configurable default |
|
| 1503 |
+ * @name .each() |
|
| 1504 |
+ * @altname _.each() |
|
| 1505 |
+ * @syntax list.each(callback) |
|
| 1506 |
+ * @syntax list.each(callback, ctx) |
|
| 1507 |
+ * @syntax _.each(list, callback) |
|
| 1508 |
+ * @syntax _.each(list, callback, ctx) |
|
| 1509 |
+ * @module UTIL, WEB |
|
| 1510 |
+ * Invokes the given function once for each item in the list. The function will be called with the item as first parameter and |
|
| 1511 |
+ * the zero-based index as second. Unlike JavaScript's built-in <var>forEach()</var> it will be invoked for each item in the list, |
|
| 1512 |
+ * even if it is <var>undefined</var>. |
|
| 1513 |
+ * |
|
| 1514 |
+ * @example Creates the sum of all list entries. |
|
| 1515 |
+ * <pre> |
|
| 1516 |
+ * var sum = 0; |
|
| 1517 |
+ * _(17, 4, 22).each(function(item, index) {
|
|
| 1518 |
+ * sum += item; |
|
| 1519 |
+ * }); |
|
| 1520 |
+ * </pre> |
|
| 1521 |
+ * |
|
| 1522 |
+ * @example The previous example with a native array: |
|
| 1523 |
+ * <pre> |
|
| 1524 |
+ * var sum = 0; |
|
| 1525 |
+ * _.each([17, 4, 22], function(item, index) {
|
|
| 1526 |
+ * sum += item; |
|
| 1527 |
+ * }); |
|
| 1528 |
+ * </pre> |
|
| 1529 |
+ * |
|
| 1530 |
+ * @example This goes through all h2 elements of the class 'section' on a web page and changes their content: |
|
| 1531 |
+ * <pre> |
|
| 1532 |
+ * $('h2.section').each(function(item, index) {
|
|
| 1533 |
+ * item.innerHTML = 'Section ' + index + ': ' + item.innerHTML; |
|
| 1534 |
+ * }); |
|
| 1535 |
+ * </pre> |
|
| 1536 |
+ * |
|
| 1537 |
+ * @param list a list to iterate. Can be an array, a ##list#Minified list## or any other array-like structure with |
|
| 1538 |
+ * <var>length</var> property. |
|
| 1539 |
+ * @param callback The callback <code>function(item, index)</code> to invoke for each list element. |
|
| 1540 |
+ * <dl><dt>item</dt><dd>The current list element.</dd> |
|
| 1541 |
+ * <dt>index</dt><dd>The second the zero-based index of the current element.</dd> |
|
| 1542 |
+ * <dt class="this">this</dt><dd>The given context if not null. Otherwise the list.</dd> |
|
| 1543 |
+ * The callback's return value will be ignored. |
|
| 1544 |
+ * @param ctx optional a context to pass to the callback as 'this'. Only supported in UTIL module. |
|
| 1545 |
+ * @return the list |
|
| 1546 |
+ * |
|
| 1547 |
+ * @see ##per() works like <var>each()</var>, but wraps the list elements in a list. |
|
| 1548 |
+ * @see ##find() can be used instead of <var>each()</var> if you need to abort the loop. |
|
| 1549 |
+ * @see ##eachObj() iterates through the properties of an object. |
|
| 1550 |
+ */ |
|
| 1551 |
+ 'each': listBind(each), |
|
| 1552 |
+ |
|
| 1553 |
+ /*$ |
|
| 1554 |
+ * @id equals |
|
| 1555 |
+ * @group LIST |
|
| 1556 |
+ * @requires |
|
| 1557 |
+ * @configurable default |
|
| 1558 |
+ * @name .equals() |
|
| 1559 |
+ * @altname _.equals() |
|
| 1560 |
+ * @syntax list.equals(otherObject) |
|
| 1561 |
+ * @syntax _.equals(thisObject, otherObject) |
|
| 1562 |
+ * @module UTIL |
|
| 1563 |
+ * Checks whether two values, lists or objects are equal in a deep comparison. |
|
| 1564 |
+ * |
|
| 1565 |
+ * First <var>equals()</var> checks whether it got a function as parameter. |
|
| 1566 |
+ * If yes, it will be invoked without arguments and <var>equals()</var> calls itself recursively with the function's result. |
|
| 1567 |
+ * |
|
| 1568 |
+ * Once both values are no functions anymore, the values will be evaluated, If the first value is... |
|
| 1569 |
+ * <ul><li>...<var>null</var> or <var>undefined</var>, they are only equal if the other one is also either <var>null</var> or <var>undefined</var>.</li> |
|
| 1570 |
+ * <li>...a value as defined by ##_.isValue(), but not a Date, they are equal if the other value is the same type and is equal according to the '==' operator.</li> |
|
| 1571 |
+ * <li>...a Date, they are equal if the other value is a Date representing the same time.</li> |
|
| 1572 |
+ * <li>...a list or array, they are equal if the other value is also either a list or an array, has the same number of items and all items equal the items of the other |
|
| 1573 |
+ * list at the same position. The equality of list items is determined recursively using the same rules, so you can also nest lists.</li> |
|
| 1574 |
+ * <li>...a function, it will be invoked without arguments and its return value is evaluated using these rules as if the value has been passed. </li> |
|
| 1575 |
+ * <li>...any other object, they are equal if they contain exactly the same keys (as defined by ##_.eachObj()) and all values are equal as determined using these rules |
|
| 1576 |
+ * recursively.</li> |
|
| 1577 |
+ * </ul> |
|
| 1578 |
+ * |
|
| 1579 |
+ * Please note that, according to the rules, a ##list#Minified list## is equal to an array, as long as their content is equal. <var>equals</var> does not |
|
| 1580 |
+ * differentiate between <var>null</var> and <var>undefined</var>. |
|
| 1581 |
+ * |
|
| 1582 |
+ * <var>equals</var> is commutative. If you swap the parameters, the result is the same as long as no functions are involved. |
|
| 1583 |
+ * |
|
| 1584 |
+ * @example Compare a list and an array: |
|
| 1585 |
+ * <pre> |
|
| 1586 |
+ * _.equals([1, 2, 3], _(1, 2, 3)); // returns true |
|
| 1587 |
+ * </pre> |
|
| 1588 |
+ * |
|
| 1589 |
+ * @example Same result, but with a list method: |
|
| 1590 |
+ * <pre> |
|
| 1591 |
+ * _(1, 2, 3).equals([1, 2, 3]); // returns true |
|
| 1592 |
+ * </pre> |
|
| 1593 |
+ * |
|
| 1594 |
+ * @param thisObject The first reference to evaluate. |
|
| 1595 |
+ * @param otherObject The second reference to evaluate. |
|
| 1596 |
+ * @return true if both references are equal. False otherwise. |
|
| 1597 |
+ */ |
|
| 1598 |
+ 'equals': listBind(equals), |
|
| 1599 |
+ |
|
| 1600 |
+ /*$ |
|
| 1601 |
+ * @id find |
|
| 1602 |
+ * @group LIST |
|
| 1603 |
+ * @requires |
|
| 1604 |
+ * @configurable default |
|
| 1605 |
+ * @name .find() |
|
| 1606 |
+ * @altname _.find() |
|
| 1607 |
+ * @syntax list.find(findFunc) |
|
| 1608 |
+ * @syntax list.find(element) |
|
| 1609 |
+ * @syntax list.find(findFunc, startIndex) |
|
| 1610 |
+ * @syntax list.find(element, startIndex) |
|
| 1611 |
+ * @syntax _.find(list, findFunc) |
|
| 1612 |
+ * @syntax _.find(list, element) |
|
| 1613 |
+ * @syntax _.find(list, findFunc, startIndex) |
|
| 1614 |
+ * @syntax _.find(list, element, startIndex) |
|
| 1615 |
+ * @module WEB, UTIL |
|
| 1616 |
+ * Finds a specific value in the list. There are two ways of calling <var>find()</var>: |
|
| 1617 |
+ * <ol> |
|
| 1618 |
+ * <li>With a value as argument. Then <var>find()</var> will search for the first occurrence of an identical value in the list, |
|
| 1619 |
+ * using the '===' operator for comparisons, and return the index. If it is not found, |
|
| 1620 |
+ * <var>find()</var> returns <var>undefined</var>.</li> |
|
| 1621 |
+ * <li>With a callback function. <var>find()</var> will then call the given function for each list element until the function |
|
| 1622 |
+ * returns a value that is not <var>null</var> or <var>undefined</var>. This value will be returned.</li> |
|
| 1623 |
+ * </ol> |
|
| 1624 |
+ * |
|
| 1625 |
+ * <var>find()</var> can also be used as an alternative to ##each() if you need to abort the loop. |
|
| 1626 |
+ * |
|
| 1627 |
+ * @example Finds the first negative number in the list: |
|
| 1628 |
+ * <pre> |
|
| 1629 |
+ * var i = _(1, 2, -4, 5, 2, -1).find(function(value, index) { if (value < 0) return index; }); // returns 2
|
|
| 1630 |
+ * </pre> |
|
| 1631 |
+ |
|
| 1632 |
+ * @example Finds the index of the first 5 in the array: |
|
| 1633 |
+ * <pre> |
|
| 1634 |
+ * var i = _.find([3, 6, 7, 6, 5, 4, 5], 5); // returns 4 (index of first 5) |
|
| 1635 |
+ * </pre> |
|
| 1636 |
+ * |
|
| 1637 |
+ * @example Determines the position of the element with the id '#wanted' among all li elements: |
|
| 1638 |
+ * <pre> |
|
| 1639 |
+ * var elementIndex = $('li').find($$('#wanted'));
|
|
| 1640 |
+ * </pre> |
|
| 1641 |
+ * |
|
| 1642 |
+ * @example Goes through the elements to find the first div that has the class 'myClass', and returns this element: |
|
| 1643 |
+ * <pre> |
|
| 1644 |
+ * var myClassElement = $('div').find(function(e) { if ($(e).is('.myClass')) return e; });
|
|
| 1645 |
+ * </pre> |
|
| 1646 |
+ * |
|
| 1647 |
+ * @param list A list to use as input. Can be an array, a ##list#Minified list## or any other array-like structure with |
|
| 1648 |
+ * <var>length</var> property. |
|
| 1649 |
+ * @param findFunc The callback <code>function(item, index)</code> that will be invoked for every list item until it returns a non-null value: |
|
| 1650 |
+ * <dl><dt>item</dt><dd>The current list element.</dd><dt>index</dt><dd>The second the zero-based index of the current element.</dd> |
|
| 1651 |
+ * <dt class="this">this</dt><dd>This list.</dd> |
|
| 1652 |
+ * <dt class="returnValue">(callback return value)</dt><dd>If the callback returns something other than <var>null</var> or |
|
| 1653 |
+ * <var>undefined</var>, <var>find()</var> will return it directly. Otherwise it will continue. </dd></dl> |
|
| 1654 |
+ * @param element the element to search for |
|
| 1655 |
+ * @param startIndex optional the 0-based index of the first element to search. |
|
| 1656 |
+ * @return if called with an element, either the element's index in the list or <var>undefined</var> if not found. If called with a callback function, |
|
| 1657 |
+ * it returns either the value returned by the callback or <var>undefined</var>. |
|
| 1658 |
+ * |
|
| 1659 |
+ * @see ##findLast() is the equivalent to <var>find()</var> for the list's end. |
|
| 1660 |
+ */ |
|
| 1661 |
+ 'find': listBind(find), |
|
| 1662 |
+ |
|
| 1663 |
+ /*$ |
|
| 1664 |
+ * @stop |
|
| 1665 |
+ */ |
|
| 1666 |
+ dummySort:0 |
|
| 1667 |
+ , |
|
| 1668 |
+ ///#/snippet utilListFuncs |
|
| 1669 |
+ ///#snippet webListFuncs |
|
| 1670 |
+ |
|
| 1671 |
+ /*$ |
|
| 1672 |
+ * @id select |
|
| 1673 |
+ * @group SELECTORS |
|
| 1674 |
+ * @requires dollar |
|
| 1675 |
+ * @configurable default |
|
| 1676 |
+ * @name .select() |
|
| 1677 |
+ * @syntax list.select(selector) |
|
| 1678 |
+ * @syntax list.select(selector, childrenOnly) |
|
| 1679 |
+ * @module WEB |
|
| 1680 |
+ * Executes a selector with the list as context. <code>list.select(selector, childrenOnly)</code> is equivalent |
|
| 1681 |
+ * to <code>$(selector, list, childrenOnly)</code>. |
|
| 1682 |
+ * |
|
| 1683 |
+ * @example Returns a list of all list elements: |
|
| 1684 |
+ * <pre> |
|
| 1685 |
+ * var parents = $('ol.myList').select('li', true);
|
|
| 1686 |
+ * </pre> |
|
| 1687 |
+ * |
|
| 1688 |
+ * @example Returns a list of all child elements: |
|
| 1689 |
+ * <pre> |
|
| 1690 |
+ * var children = $('.myElements').select('*', true);
|
|
| 1691 |
+ * </pre> |
|
| 1692 |
+ * |
|
| 1693 |
+ * @param selector a selector or any other valid first argument for #dollar#$(). |
|
| 1694 |
+ * @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 |
|
| 1695 |
+ * true, all descendants of the context will be included. |
|
| 1696 |
+ * @return the new list containing the selected descendants. |
|
| 1697 |
+ * |
|
| 1698 |
+ * @see ##only() executes a selector on the list elements, instead of their descendants. |
|
| 1699 |
+ */ |
|
| 1700 |
+ 'select': function(selector, childOnly) {
|
|
| 1701 |
+ return $(selector, this, childOnly); |
|
| 1702 |
+ }, |
|
| 1703 |
+ |
|
| 1704 |
+ /*$ |
|
| 1705 |
+ * @id get |
|
| 1706 |
+ * @group SELECTORS |
|
| 1707 |
+ * @requires dollar |
|
| 1708 |
+ * @configurable default |
|
| 1709 |
+ * @name .get() |
|
| 1710 |
+ * @syntax list.get(name) |
|
| 1711 |
+ * @syntax list.get(name, toNumber) |
|
| 1712 |
+ * @syntax list.get(list) |
|
| 1713 |
+ * @syntax list.get(list, toNumber) |
|
| 1714 |
+ * @syntax list.get(map) |
|
| 1715 |
+ * @syntax list.get(map, toNumber) |
|
| 1716 |
+ * @module WEB |
|
| 1717 |
+ * 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 |
|
| 1718 |
+ * 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. |
|
| 1719 |
+ * |
|
| 1720 |
+ * The <var>name</var> parameter defines what kind of data you are reading. The following name schemes are supported: |
|
| 1721 |
+ * <table> |
|
| 1722 |
+ * <tr><th>Name Schema</th><th>Example</th><th>Sets what?</th><th>Description</th></tr> |
|
| 1723 |
+ * <tr><td>name</td><td>innerHTML</td><td>Property</td><td>A name without prefix of '$' or '@' gets a property of the object.</td></tr> |
|
| 1724 |
+ * <tr><td>@name</td><td>@href</td><td>Attribute</td><td>Gets the HTML attribute using getAttribute().</td></tr> |
|
| 1725 |
+ * <tr><td>%name</td><td>%phone</td><td>Data-Attribute</td><td>Gets a data attribute using getAttribute(). Data attributes are |
|
| 1726 |
+ * attributes whose names start with 'data-'. '%myattr' and '@data-myattr' are equivalent.</td></tr> |
|
| 1727 |
+ * <tr><td>$name</td><td>$fontSize</td><td>CSS Property</td><td>Gets a style using the element's <var>style</var> object. |
|
| 1728 |
+ * The syntax for the CSS styles is camel-case (e.g. "$backgroundColor", not "$background-color"). Shorthand properties like "border" or "margin" are |
|
| 1729 |
+ * not supported. You must use the full name, e.g. "$marginTop". Minified will try to determine the effective style |
|
| 1730 |
+ * and thus will return the value set in style sheets if not overwritten using a regular style.</td></tr> |
|
| 1731 |
+ * <tr><td>$</td><td>$</td><td>CSS Classes</td><td>A simple <var>$</var> returns the CSS classes of the element and is identical with "className".</td></tr> |
|
| 1732 |
+ * <tr><td>$$</td><td>$$</td><td>Style</td><td>Reads the element's style attribute in a browser-independent way. On legacy IEs it uses |
|
| 1733 |
+ * <var>style.cssText</var>, and on everything else just the "style" attribute.</td></tr> |
|
| 1734 |
+ * <tr><td>$$show</td><td>$$show</td><td>Show/Hide</td><td>Returns 1 if the element is visible and 0 if it is not visible. An element counts as |
|
| 1735 |
+ * 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.</td></tr> |
|
| 1736 |
+ * <tr><td>$$fade</td><td>$$fade</td><td>Fade Effect</td><td>The name '$$fade' returns the opacity of the element as a value between 0 and 1. |
|
| 1737 |
+ * '$$fade' will also automatically evaluate the element's 'visibility' and 'display' styles to find out whether the element is actually visible.</td></tr> |
|
| 1738 |
+ * <tr><td>$$slide</td><td>$$slide</td><td>Slide Effect</td><td>'$$slide' returns the height of the element in pixels with a 'px' suffix and is equivalent to '$height'. |
|
| 1739 |
+ * Please note that you can pass that 'px' value to '$$slide' in ##set(), which will then set the according '$height'.</td></tr> |
|
| 1740 |
+ * <tr><td>$$scrollX, $$scrollY</td><td>$$scrollY</td><td>Scroll Coordinates</td><td>The names '$$scrollX' and |
|
| 1741 |
+ * '$$scrollY' can be used on <code>$(window)</code> to retrieve the scroll coordinates of the document. |
|
| 1742 |
+ * The coordinates are specified in pixels without a 'px' unit postfix.</td></tr> |
|
| 1743 |
+ * </table> |
|
| 1744 |
+ * |
|
| 1745 |
+ * @example Retrieves the id, title attribute and the background color of the element '#myElement': |
|
| 1746 |
+ * <pre> |
|
| 1747 |
+ * var id = $('#myElement).get('id');
|
|
| 1748 |
+ * var title = $('#myElement).get('@title');
|
|
| 1749 |
+ * var bgColor = $('#myElement).get('$backgroundColor');
|
|
| 1750 |
+ * </pre> |
|
| 1751 |
+ * |
|
| 1752 |
+ * @example Retrieves the id, title attribute and the background color of the element '#myElement' as a map: |
|
| 1753 |
+ * <pre> |
|
| 1754 |
+ * var m = $('#myElement).get(['id', '@title', '$backgroundColor']);
|
|
| 1755 |
+ * var id = m.id; |
|
| 1756 |
+ * var title = m['@title']; |
|
| 1757 |
+ * var bgColor = m.$backgroundColor; |
|
| 1758 |
+ * </pre> |
|
| 1759 |
+ * |
|
| 1760 |
+ * @example Uses ##get() and ##set() to reposition an element: |
|
| 1761 |
+ * <pre> |
|
| 1762 |
+ * var coords = $('#myElement').get({$top: 0, $left: 0}, true);
|
|
| 1763 |
+ * coords.$top = coords.$top + 10 + 'px'; |
|
| 1764 |
+ * coords.$left = coords.$left + 20 + 'px'; |
|
| 1765 |
+ * $('#myElement').set(coords);
|
|
| 1766 |
+ * </pre> |
|
| 1767 |
+ * Please note that the values of $top and $left in the <var>get()</var> invocation do not matter and will be ignored! |
|
| 1768 |
+ * |
|
| 1769 |
+ * @param name the name of a single property or attribute to modify. Unprefixed names set properties, a '$' prefix sets CSS styles and |
|
| 1770 |
+ * '@' sets attributes. Please see the table above for special properties and other options. |
|
| 1771 |
+ * @param list in order to retrieve more than one value, you can specify several names in an array or list. <var>get()</var> will then return an object map |
|
| 1772 |
+ * containing the values. |
|
| 1773 |
+ * @param map if you specify an object that is neither list nor string, <var>get()</var> will use it as a map of property names. Each property name will be requested. |
|
| 1774 |
+ * The values of the properties in the map will be ignored. <var>get()</var> will then return a new object map containing of results. |
|
| 1775 |
+ * @param toNumber if 'true', <var>get()</var> converts all returned values into numbers. If they are strings, |
|
| 1776 |
+ * <var>get()</var> removes any non-numeric characters before the conversion. This is useful when you request |
|
| 1777 |
+ * a CSS property such as '$marginTop' that returns a value with a unit suffix, like "21px". <var>get()</var> will convert it |
|
| 1778 |
+ * into a number and return 21. If the returned value is not parsable as a number, <var>NaN</var> will be returned. |
|
| 1779 |
+ * @return if <var>get()</var> was called with a single name, it returns the corresponding value. |
|
| 1780 |
+ * If a list or map was given, <var>get()</var> returns a new object map with the names as keys and the values as values. |
|
| 1781 |
+ * It returns <var>undefined</var> if the list is empty. |
|
| 1782 |
+ * |
|
| 1783 |
+ * @see ##set() sets values using the same property syntax. |
|
| 1784 |
+ */ |
|
| 1785 |
+ 'get': function(spec, toNumber) {
|
|
| 1786 |
+ var self = this; |
|
| 1787 |
+ var element = self[0]; |
|
| 1788 |
+ |
|
| 1789 |
+ if (element) {
|
|
| 1790 |
+ if (isString(spec)) {
|
|
| 1791 |
+ var match = /^(\W*)(.*)/.exec(replace(spec, /^%/,'@data-')); |
|
| 1792 |
+ var prefix = match[1]; |
|
| 1793 |
+ var s; |
|
| 1794 |
+ |
|
| 1795 |
+ if (getter[prefix]) |
|
| 1796 |
+ s = getter[prefix](this, match[2]); |
|
| 1797 |
+ else if (spec == '$') |
|
| 1798 |
+ s = self['get']('className');
|
|
| 1799 |
+ else if (spec == '$$') {
|
|
| 1800 |
+ s = self['get']('@style');
|
|
| 1801 |
+ } |
|
| 1802 |
+ else if (spec == '$$slide') |
|
| 1803 |
+ s = self['get']('$height');
|
|
| 1804 |
+ else if (spec == '$$fade' || spec == '$$show') {
|
|
| 1805 |
+ if (self['get']('$visibility') == 'hidden' || self['get']('$display') == 'none')
|
|
| 1806 |
+ s = 0; |
|
| 1807 |
+ else if (spec == '$$fade') {
|
|
| 1808 |
+ s = |
|
| 1809 |
+ isNaN(self['get']('$opacity', true)) ? 1 : self['get']('$opacity', true);
|
|
| 1810 |
+ } |
|
| 1811 |
+ else // $$show |
|
| 1812 |
+ s = 1; |
|
| 1813 |
+ } |
|
| 1814 |
+ else if (prefix == '$') {
|
|
| 1815 |
+ s = _window['getComputedStyle'](element, _null)['getPropertyValue'](replace(match[2], /[A-Z]/g, function (match2) { return '-' + match2.toLowerCase(); }));
|
|
| 1816 |
+ } |
|
| 1817 |
+ else if (prefix == '@') |
|
| 1818 |
+ s = element.getAttribute(match[2]); |
|
| 1819 |
+ else |
|
| 1820 |
+ s = element[match[2]]; |
|
| 1821 |
+ return toNumber ? extractNumber(s) : s; |
|
| 1822 |
+ } |
|
| 1823 |
+ else {
|
|
| 1824 |
+ var r = {};
|
|
| 1825 |
+ (isList(spec) ? flexiEach : eachObj)(spec, function(name) {
|
|
| 1826 |
+ r[name] = self['get'](name, toNumber); |
|
| 1827 |
+ }); |
|
| 1828 |
+ return r; |
|
| 1829 |
+ } |
|
| 1830 |
+ } |
|
| 1831 |
+ }, |
|
| 1832 |
+ |
|
| 1833 |
+ /*$ |
|
| 1834 |
+ * @id set |
|
| 1835 |
+ * @group SELECTORS |
|
| 1836 |
+ * @requires dollar get |
|
| 1837 |
+ * @configurable default |
|
| 1838 |
+ * @name .set() |
|
| 1839 |
+ * @syntax list.set(name, value) |
|
| 1840 |
+ * @syntax list.set(properties) |
|
| 1841 |
+ * @syntax list.set(cssClasses) |
|
| 1842 |
+ * @module WEB |
|
| 1843 |
+ * |
|
| 1844 |
+ * Modifies the list's elements by setting their properties, attributes, CSS styles and/or CSS classes. You can either supply a |
|
| 1845 |
+ * 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. |
|
| 1846 |
+ * More complex operations can be accomplished by supplying functions as values. They will then be called for each element that will |
|
| 1847 |
+ * be set. |
|
| 1848 |
+ * |
|
| 1849 |
+ * The <var>name</var> parameter defines what kind of data you are setting. The following name schemes are supported: |
|
| 1850 |
+ * |
|
| 1851 |
+ * <table> |
|
| 1852 |
+ * <tr><th>Name Schema</th><th>Example</th><th>Sets what?</th><th>Description</th></tr> |
|
| 1853 |
+ * <tr><td>name</td><td>innerHTML</td><td>Property</td><td>A name without prefix of '$' or '@' sets a property of the object.</td></tr> |
|
| 1854 |
+ * <tr><td>@name</td><td>@href</td><td>Attribute</td><td>Sets the HTML attribute using setAttribute(). In order to stay compatible with Internet Explorer 7 and earlier, |
|
| 1855 |
+ * you should not set the attributes '@class' and '@style'. Instead use '$' and '$$' as shown below.</td></tr> |
|
| 1856 |
+ * <tr><td>%name</td><td>%phone</td><td>Data-Attribute</td><td>Sets a data attribute using setAttribute(). Data attributes are |
|
| 1857 |
+ * attributes whose names start with 'data-'. '%myattr' and '@data-myattr' are equivalent.</td></tr> |
|
| 1858 |
+ * <tr><td>$name</td><td>$fontSize</td><td>CSS Property</td><td>Sets a style using the element's <var>style</var> object. |
|
| 1859 |
+ * The syntax for the CSS styles is camel-case (e.g. "$backgroundColor", not "$background-color"). </td></tr> |
|
| 1860 |
+ * <tr><td>$</td><td>$</td><td>CSS Classes</td><td>A simple <var>$</var> modifies the element's CSS classes using the object's <var>className</var> property. The value is a |
|
| 1861 |
+ * 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. |
|
| 1862 |
+ * The name '$' can also be omitted if <var>set</var> is called with class names as only argument.</td></tr> |
|
| 1863 |
+ * <tr><td>$$</td><td>$$</td><td>Style</td><td>Sets the element's style attribute in a browser-independent way.</td></tr> |
|
| 1864 |
+ * <tr><td>$$show</td><td>$$show</td><td>Show/Hide</td><td>If <var>true</var> or a number not 0, it will make sure the element is visible by |
|
| 1865 |
+ * making sure '$display' is not 'none' and by setting '$visibility' to 'visible'. Please see ##show() for details. If the value is <var>false</var> or 0, it |
|
| 1866 |
+ * will be hidden by setting '$display' to 'none'.</td></tr> |
|
| 1867 |
+ * <tr><td>$$fade</td><td>$$fade</td><td>Fade Effect</td><td>The name '$$fade' sets the opacity of the element in a browser-independent way. The value must be a number |
|
| 1868 |
+ * between 0 and 1. '$$fade' will also automatically control the element's 'visibility' style. If the value is 0, |
|
| 1869 |
+ * the element's visibility will automatically be set to 'hidden'. If the value is larger, the visibility will be set to |
|
| 1870 |
+ * 'visible'. '$$fade' only works with block elements.</td></tr> |
|
| 1871 |
+ * <tr><td>$$slide</td><td>$$slide</td><td>Slide Effect</td><td>The name '$$slide' allows a vertical slide-out or slide-in effect. The value must be a number |
|
| 1872 |
+ * between 0 and 1 and will be used to set the element's '$height'. '$$slide' will also automatically control the element's 'visibility' |
|
| 1873 |
+ * style. If the value is 0, the element's visibility will automatically be set to 'hidden'. If the value is larger, |
|
| 1874 |
+ * the visibility will be set to 'visible'. '$$slide' only works with block elements and will not set the |
|
| 1875 |
+ * element's margin or padding. If you need a margin or padding, you should wrap the elements in a simple <div>.</td></tr> |
|
| 1876 |
+ * <tr><td>$$scrollX, $$scrollY</td><td>$$scrollY</td><td>Scroll Coordinates</td><td>The names '$$scrollX' and |
|
| 1877 |
+ * '$$scrollY' can be used on <code>$(window)</code> to set the scroll coordinates of the document. |
|
| 1878 |
+ * The coordinates are specified in pixels, but must not use a 'px' unit postfix.</td></tr> |
|
| 1879 |
+ * </table> |
|
| 1880 |
+ * |
|
| 1881 |
+ * @example Unchecking checkboxes: |
|
| 1882 |
+ * <pre> |
|
| 1883 |
+ * $('input.checkbox').set('checked', false);
|
|
| 1884 |
+ * </pre> |
|
| 1885 |
+ * |
|
| 1886 |
+ * @example Changing the <var>innerHTML</var> property of an element: |
|
| 1887 |
+ * <pre> |
|
| 1888 |
+ * $('#toc').set('innerHTML', 'Content');
|
|
| 1889 |
+ * </pre> |
|
| 1890 |
+ * |
|
| 1891 |
+ * @example Changing attributes: |
|
| 1892 |
+ * <pre> |
|
| 1893 |
+ * $('a.someLinks').set('@href', 'http://www.example.com/');
|
|
| 1894 |
+ * </pre> |
|
| 1895 |
+ * |
|
| 1896 |
+ * @example Removing attributes: |
|
| 1897 |
+ * <pre> |
|
| 1898 |
+ * $('a.someLinks').set('@title', null);
|
|
| 1899 |
+ * </pre> |
|
| 1900 |
+ * |
|
| 1901 |
+ * @example Changing styles: |
|
| 1902 |
+ * <pre> |
|
| 1903 |
+ * $('.bigText').set('$fontSize', 'x-large');
|
|
| 1904 |
+ * </pre> |
|
| 1905 |
+ * |
|
| 1906 |
+ * @example Adding and removing CSS classes: |
|
| 1907 |
+ * <pre> |
|
| 1908 |
+ * $('.myElem').set('$', '+myClass -otherClass');
|
|
| 1909 |
+ * </pre> |
|
| 1910 |
+ * |
|
| 1911 |
+ * @example Toggling a CSS class: |
|
| 1912 |
+ * <pre> |
|
| 1913 |
+ * $('.myElem').set('$', 'on');
|
|
| 1914 |
+ * </pre> |
|
| 1915 |
+ * |
|
| 1916 |
+ * @example Shortcut for CSS manipulation: |
|
| 1917 |
+ * <pre> |
|
| 1918 |
+ * $('.myElem').set('+myClass -otherClass on');
|
|
| 1919 |
+ * </pre> |
|
| 1920 |
+ * |
|
| 1921 |
+ * @example Making an element transparent: |
|
| 1922 |
+ * <pre> |
|
| 1923 |
+ * $('.seeThrough').set('$$fade', 0.5);
|
|
| 1924 |
+ * </pre> |
|
| 1925 |
+ * |
|
| 1926 |
+ * @example Making an element visible. Note that $$fade will set the element's display style to 'block' and visibility style to 'visible'. |
|
| 1927 |
+ * <pre> |
|
| 1928 |
+ * $('.myElem').set('$$fade', 1);
|
|
| 1929 |
+ * </pre> |
|
| 1930 |
+ * |
|
| 1931 |
+ * @example Using a map to change several properties: |
|
| 1932 |
+ * <pre> |
|
| 1933 |
+ * $('input.checkbox').set({checked: false,
|
|
| 1934 |
+ * '@title': 'Check this'}); |
|
| 1935 |
+ * </pre> |
|
| 1936 |
+ * |
|
| 1937 |
+ * @example Changing CSS with a map: |
|
| 1938 |
+ * <pre> |
|
| 1939 |
+ * $('.importantText').set({$fontSize: 'x-large',
|
|
| 1940 |
+ * $color: 'black', |
|
| 1941 |
+ * $backgroundColor: 'red', |
|
| 1942 |
+ * $: '+selected -default'}); |
|
| 1943 |
+ * </pre> |
|
| 1944 |
+ * |
|
| 1945 |
+ * @example You can specify a function as value to modify a value instead of just setting it: |
|
| 1946 |
+ * <pre> |
|
| 1947 |
+ * $('h2').set('innerHTML', function(oldValue, index) {
|
|
| 1948 |
+ * return 'Chapter ' + index + ': ' + oldValue.toUpperCase(); |
|
| 1949 |
+ * }); |
|
| 1950 |
+ * </pre> |
|
| 1951 |
+ * |
|
| 1952 |
+ * @param name the name of a single property or attribute to modify. Unprefixed names set properties, a '$' prefix sets CSS styles and |
|
| 1953 |
+ * '@' sets attributes. Please see the table above for special properties and other options. |
|
| 1954 |
+ * @param value the value to set. If value is <var>null</var> and name specified an attribute, the attribute will be removed. |
|
| 1955 |
+ * 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,
|
|
| 1956 |
+ * with a '-' prefix the class will be removed. Without prefix, the class will be toggled. |
|
| 1957 |
+ * If <var>value</var> is a function, the <code>function(oldValue, index, obj)</code> will be invoked for each list element |
|
| 1958 |
+ * to evaluate the new value: |
|
| 1959 |
+ * <dl><dt>oldValue</dt><dd>The old value of the property to be changed, as returned by ##get(). |
|
| 1960 |
+ * For the CSS style names, this is the computed style of the property </dd> |
|
| 1961 |
+ * <dt>index</dt><dd>The list index of the object owning the property</dd> |
|
| 1962 |
+ * <dt>obj</dt><dd>The list element owning the property.</dd> |
|
| 1963 |
+ * <dt class="returnValue">(callback return value)</dt><dd>The value to be set.</dd></dl> |
|
| 1964 |
+ * Functions are not supported by '$'. |
|
| 1965 |
+ * @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. |
|
| 1966 |
+ * @param cssClasses if <var>set()</var> is invoked with a string as single argument, the name "$" (CSS classes) is assumed and the argument is the |
|
| 1967 |
+ * value. See above for CSS syntax. |
|
| 1968 |
+ * Instead of a string, you can also specify a <code>function(oldValue, index, obj)</code> to modify the existing classes. |
|
| 1969 |
+ * @return the list |
|
| 1970 |
+ * |
|
| 1971 |
+ * @see ##get() retrieves values using the same property syntax. |
|
| 1972 |
+ * @see ##animate() animates values using the same property syntax. |
|
| 1973 |
+ * @see ##toggle() can toggle between two sets of values. |
|
| 1974 |
+ * @see ##dial() allows smooth transitions between two sets of values. |
|
| 1975 |
+ */ |
|
| 1976 |
+ 'set': function (name, value) {
|
|
| 1977 |
+ var self = this; |
|
| 1978 |
+ if (value !== undef) {
|
|
| 1979 |
+ var match = /^(\W*)(.*)/.exec(replace(replace(name, /^\$float$/, 'cssFloat'), /^%/,'@data-')); |
|
| 1980 |
+ var prefix = match[1]; |
|
| 1981 |
+ |
|
| 1982 |
+ if (setter[prefix]) |
|
| 1983 |
+ setter[prefix](this, match[2], value); |
|
| 1984 |
+ else if (name == '$$fade') {
|
|
| 1985 |
+ this['set']({'$visibility': value ? 'visible' : 'hidden', '$opacity': value});
|
|
| 1986 |
+ } |
|
| 1987 |
+ else if (name == '$$slide') {
|
|
| 1988 |
+ self['set']({'$visibility': value ? 'visible' : 'hidden', '$overflow': 'hidden',
|
|
| 1989 |
+ '$height': /px/.test(value) ? value : function(oldValue, idx, element) { return getNaturalHeight($(element), value);}
|
|
| 1990 |
+ }); |
|
| 1991 |
+ } |
|
| 1992 |
+ else if (name == '$$show') {
|
|
| 1993 |
+ if (value) |
|
| 1994 |
+ self['set']({'$visibility': value ? 'visible' : 'hidden', '$display': ''}) // that value? part is only for gzip
|
|
| 1995 |
+ ['set']({'$display': function(oldVal) { // set for 2nd time: now we get the stylesheet's $display
|
|
| 1996 |
+ return oldVal == 'none' ? 'block' : oldVal; |
|
| 1997 |
+ }}); |
|
| 1998 |
+ else |
|
| 1999 |
+ self['set']({'$display': 'none'});
|
|
| 2000 |
+ } |
|
| 2001 |
+ else if (name == '$$') {
|
|
| 2002 |
+ self['set']('@style', value);
|
|
| 2003 |
+ } |
|
| 2004 |
+ else |
|
| 2005 |
+ flexiEach(this, function(obj, c) {
|
|
| 2006 |
+ var newValue = isFunction(value) ? value($(obj)['get'](name), c, obj) : value; |
|
| 2007 |
+ if (prefix == '$') {
|
|
| 2008 |
+ if (match[2]) |
|
| 2009 |
+ obj['style'][match[2]] = newValue; |
|
| 2010 |
+ else {
|
|
| 2011 |
+ flexiEach(newValue && newValue.split(/\s+/), function(clzz) {
|
|
| 2012 |
+ var cName = replace(clzz, /^[+-]/); |
|
| 2013 |
+ |
|
| 2014 |
+ if (/^\+/.test(clzz)) |
|
| 2015 |
+ obj['classList'].add(cName); |
|
| 2016 |
+ else if (/^-/.test(clzz)) |
|
| 2017 |
+ obj['classList'].remove(cName); |
|
| 2018 |
+ else |
|
| 2019 |
+ obj['classList'].toggle(cName); |
|
| 2020 |
+ }); |
|
| 2021 |
+ } |
|
| 2022 |
+ } |
|
| 2023 |
+ else if (name == '$$scrollX') |
|
| 2024 |
+ obj['scroll'](newValue, $(obj)['get']('$$scrollY'));
|
|
| 2025 |
+ else if (name == '$$scrollY') |
|
| 2026 |
+ obj['scroll']($(obj)['get']('$$scrollX'), newValue);
|
|
| 2027 |
+ else if (prefix == '@') {
|
|
| 2028 |
+ if (newValue == _null) |
|
| 2029 |
+ obj.removeAttribute(match[2]); |
|
| 2030 |
+ else |
|
| 2031 |
+ obj.setAttribute(match[2], newValue); |
|
| 2032 |
+ } |
|
| 2033 |
+ else |
|
| 2034 |
+ obj[match[2]] = newValue; |
|
| 2035 |
+ }); |
|
| 2036 |
+ } |
|
| 2037 |
+ else if (isString(name) || isFunction(name)) |
|
| 2038 |
+ self['set']('$', name);
|
|
| 2039 |
+ else |
|
| 2040 |
+ eachObj(name, function(n,v) { self['set'](n, v); });
|
|
| 2041 |
+ return self; |
|
| 2042 |
+ }, |
|
| 2043 |
+ |
|
| 2044 |
+ /*$ |
|
| 2045 |
+ * @id add |
|
| 2046 |
+ * @group ELEMENT |
|
| 2047 |
+ * @requires dollar each |
|
| 2048 |
+ * @configurable default |
|
| 2049 |
+ * @name .add() |
|
| 2050 |
+ * @syntax list.add(text) |
|
| 2051 |
+ * @syntax list.add(node) |
|
| 2052 |
+ * @syntax list.add(list) |
|
| 2053 |
+ * @syntax list.add(factoryFunction) |
|
| 2054 |
+ * @module WEB |
|
| 2055 |
+ * 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. |
|
| 2056 |
+ * DOM nodes will be added directly. If you pass a list, all its elements will be added using the rules above. |
|
| 2057 |
+ * |
|
| 2058 |
+ * 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, |
|
| 2059 |
+ * and ##clone#clones## to all following list elements. |
|
| 2060 |
+ * |
|
| 2061 |
+ * ##EE(), ##HTML() and ##clone() are compatible with <var>add()</var> and can help you create new HTML nodes. |
|
| 2062 |
+ * |
|
| 2063 |
+ * @example Using the following HTML: |
|
| 2064 |
+ * <pre> |
|
| 2065 |
+ * <div id="comments">Here is some text.<br/></div> |
|
| 2066 |
+ * </pre> |
|
| 2067 |
+ * The next line appends a text node to the div: |
|
| 2068 |
+ * <pre> |
|
| 2069 |
+ * $('#comments').add('Some additional text.');
|
|
| 2070 |
+ * </pre> |
|
| 2071 |
+ * This results in: |
|
| 2072 |
+ * <pre> |
|
| 2073 |
+ * <div id="comments">Here is some text.<br/>Some additional text.</div> |
|
| 2074 |
+ * </pre> |
|
| 2075 |
+ * |
|
| 2076 |
+ * @example Using the following HTML: |
|
| 2077 |
+ * <pre> |
|
| 2078 |
+ * <ul id="myList"> |
|
| 2079 |
+ * <li>First list entry</li> |
|
| 2080 |
+ * <li>Second list entry</li> |
|
| 2081 |
+ * </ul> |
|
| 2082 |
+ * </pre> |
|
| 2083 |
+ * The following Javascript adds an element to the list: |
|
| 2084 |
+ * <pre> |
|
| 2085 |
+ * $('#myList').add(EE('li', 'My extra point');
|
|
| 2086 |
+ * </pre> |
|
| 2087 |
+ * This results in |
|
| 2088 |
+ * <pre> |
|
| 2089 |
+ * <ul id="myList"> |
|
| 2090 |
+ * <li>First list entry</li> |
|
| 2091 |
+ * <li>Second list entry</li> |
|
| 2092 |
+ * <li>My extra point</li> |
|
| 2093 |
+ * </ul> |
|
| 2094 |
+ * </pre> |
|
| 2095 |
+ * |
|
| 2096 |
+ * @example Use a list to add several elements at once: |
|
| 2097 |
+ * <pre> |
|
| 2098 |
+ * $('#comments').add([
|
|
| 2099 |
+ * EE('br'),
|
|
| 2100 |
+ * 'Some text', |
|
| 2101 |
+ * EE('span', {'className': 'highlight'}, 'Some highlighted text')
|
|
| 2102 |
+ * ]); |
|
| 2103 |
+ * </pre> |
|
| 2104 |
+ * |
|
| 2105 |
+ * @example If you need to customize the content, you can write a factory function: |
|
| 2106 |
+ * <pre> |
|
| 2107 |
+ * $('.chapter').add(function(parent, index) { return EE('h2', 'Chapter number ' + index); });
|
|
| 2108 |
+ * </pre> |
|
| 2109 |
+ * |
|
| 2110 |
+ * @param text a string or number to add as text node |
|
| 2111 |
+ * @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. |
|
| 2112 |
+ * For all additional elements, the node will be cloned using ##clone(). |
|
| 2113 |
+ * @param list a list containing text and/or nodes. May also contain nested lists with nodes or text.. |
|
| 2114 |
+ * @param factoryFunction a <code>function(listItem, listIndex)</code> that will be invoked for each list element to create the nodes: |
|
| 2115 |
+ * <dl><dt>listItem</dt><dd>The list element that will receive the new children.</dd> |
|
| 2116 |
+ * <dt>listIndex</dt><dd>The index of the list element that will receive the new children.</dd> |
|
| 2117 |
+ * <dt class="returnValue">(callback return value)<dt><dd>The node(s) to be added to the list element. |
|
| 2118 |
+ * Can be either a string for a text node, an HTML element or a list containing strings and/or DOM nodes. |
|
| 2119 |
+ * If a function is returned, it will be invoked recursively with the same arguments.</dd></dl> |
|
| 2120 |
+ * @return the current list |
|
| 2121 |
+ * |
|
| 2122 |
+ * @see ##fill() works like <var>add()</var>, but deletes all children before adding the new nodes. |
|
| 2123 |
+ * @see ##addFront() adds nodes as first child, not as last. |
|
| 2124 |
+ * @see ##addAfter() adds nodes not as children but as siblings. |
|
| 2125 |
+ * @see ##addBefore() also adds nodes not as children but as siblings. |
|
| 2126 |
+ * @see ##replace() replaces existing nodes. |
|
| 2127 |
+ */ |
|
| 2128 |
+ 'add': function (children, addFunction) {
|
|
| 2129 |
+ return this['each'](function(e, index) {
|
|
| 2130 |
+ var lastAdded; |
|
| 2131 |
+ function appendChildren(c) {
|
|
| 2132 |
+ if (isList(c)) |
|
| 2133 |
+ flexiEach(c, appendChildren); |
|
| 2134 |
+ else if (isFunction(c)) |
|
| 2135 |
+ appendChildren(c(e, index)); |
|
| 2136 |
+ else if (c != _null) { // must check null, as 0 is a valid parameter
|
|
| 2137 |
+ var n = isNode(c) ? c : document.createTextNode(c); |
|
| 2138 |
+ if (lastAdded) |
|
| 2139 |
+ lastAdded['parentNode']['insertBefore'](n, lastAdded['nextSibling']); |
|
| 2140 |
+ else if (addFunction) |
|
| 2141 |
+ addFunction(n, e, e['parentNode']); |
|
| 2142 |
+ else |
|
| 2143 |
+ e.appendChild(n); |
|
| 2144 |
+ lastAdded = n; |
|
| 2145 |
+ } |
|
| 2146 |
+ } |
|
| 2147 |
+ appendChildren(index &&!isFunction(children) ? clone(children) : children); |
|
| 2148 |
+ }); |
|
| 2149 |
+ }, |
|
| 2150 |
+ |
|
| 2151 |
+ /*$ |
|
| 2152 |
+ * @id on |
|
| 2153 |
+ * @group EVENTS |
|
| 2154 |
+ * @requires dollar each |
|
| 2155 |
+ * @configurable default |
|
| 2156 |
+ * @name .on() |
|
| 2157 |
+ * @syntax list.on(names, eventHandler) |
|
| 2158 |
+ * @syntax list.on(selector, names, eventHandler) |
|
| 2159 |
+ * @syntax list.on(names, customFunc, args) |
|
| 2160 |
+ * @syntax list.on(selector, names, customFunc, args) |
|
| 2161 |
+ * @syntax list.on(names, eventHandler, bubbleSelector) |
|
| 2162 |
+ * @syntax list.on(names, customFunc, args, bubbleSelector) |
|
| 2163 |
+ * @module WEB |
|
| 2164 |
+ * Registers the function as event handler for all items in the list. |
|
| 2165 |
+ * |
|
| 2166 |
+ * By default, Minified cancels event propagation and disables element's default behavior for all elements that have an event handler. |
|
| 2167 |
+ * You can override this, either by prefixing the event name with a '|', or by prefixing them with '?' and returning a <var>true</var> |
|
| 2168 |
+ * in the handler. Both will reinstate the original JavaScript behavior. |
|
| 2169 |
+ * |
|
| 2170 |
+ * Handlers are called with the original event object as first argument, the index of the source element in the |
|
| 2171 |
+ * list as second argument and 'this' set to the source element of the event (e.g. the button that has been clicked). |
|
| 2172 |
+ * |
|
| 2173 |
+ * Instead of the event objects, you can also pass an array of arguments that will be passed instead of event object and index. |
|
| 2174 |
+ * |
|
| 2175 |
+ * Optionally you can specify two a selector strings to qualify only certain events. The first one is a selector |
|
| 2176 |
+ * that allows you to select only specific children of the list elements. This is mostly useful for adding events to DOM trees |
|
| 2177 |
+ * generated using ##HTML() or ##EE(). |
|
| 2178 |
+ * |
|
| 2179 |
+ * The second type of selector is the bubble selector that allows you to receive only events that bubbled up from |
|
| 2180 |
+ * elements matching the selector. The selector is executed in the context of the element you registered on to identify whether the |
|
| 2181 |
+ * original target of the event qualifies. If not, the handler is not called. |
|
| 2182 |
+ * |
|
| 2183 |
+ * Minified always registers event handlers with event bubbling enabled. Event capture is not supported. |
|
| 2184 |
+ * |
|
| 2185 |
+ * Event handlers can be unregistered using #off#$.off(). |
|
| 2186 |
+ * |
|
| 2187 |
+ * @example Adds a handler to all divs which paints the div background color to red when clicked. |
|
| 2188 |
+ * <pre> |
|
| 2189 |
+ * $('div').on('click', function() {
|
|
| 2190 |
+ * this.style.backgroundColor = 'red'; // 'this' contains the element that caused the event |
|
| 2191 |
+ * }); |
|
| 2192 |
+ * </pre> |
|
| 2193 |
+ * |
|
| 2194 |
+ * @example Registers a handler to call a method setStatus('running') using an inline function:
|
|
| 2195 |
+ * <pre> |
|
| 2196 |
+ * $('#myButton').on('click', function() {
|
|
| 2197 |
+ * setStatus('running');
|
|
| 2198 |
+ * }); |
|
| 2199 |
+ * </pre> |
|
| 2200 |
+ * The previous example can bere written like this, using <var>on()</var>'s <var>args</var> parameter: |
|
| 2201 |
+ * <pre> |
|
| 2202 |
+ * $('#myButton').on('click', setStatus, ['running']);
|
|
| 2203 |
+ * </pre> |
|
| 2204 |
+ * |
|
| 2205 |
+ * @example Adds two handlers on an input field. The event names are prefixed with '|' and thus keep their original behavior: |
|
| 2206 |
+ * <pre> |
|
| 2207 |
+ * $('#myInput').on('|keypress |keydown', function() {
|
|
| 2208 |
+ * // do something |
|
| 2209 |
+ * }); |
|
| 2210 |
+ * </pre> |
|
| 2211 |
+ * |
|
| 2212 |
+ * @example Adds a click handler that will abort the operation by returning false, unless the user confirms it: |
|
| 2213 |
+ * <pre> |
|
| 2214 |
+ * $('#myLink').on('?click', function() {
|
|
| 2215 |
+ * return window.confirm('Really leave?');
|
|
| 2216 |
+ * }); |
|
| 2217 |
+ * </pre> |
|
| 2218 |
+ * |
|
| 2219 |
+ * @example Adds a button and registers a click handler for it using a sub-selector. |
|
| 2220 |
+ * <pre> |
|
| 2221 |
+ * $('#myForm').add(HTML("<li><button>click me</button></li>").on('button', 'click', myClickHandler));
|
|
| 2222 |
+ * </pre> |
|
| 2223 |
+ * |
|
| 2224 |
+ * @example Adds listeners for all clicks on a table's rows using the bubble selector 'tr'. |
|
| 2225 |
+ * <pre> |
|
| 2226 |
+ * $('#table').on('change', 'tr', function(event, index, selectedIndex) {
|
|
| 2227 |
+ * alert("Click on table row number: " + selectedIndex);
|
|
| 2228 |
+ * }, 'tr'); |
|
| 2229 |
+ * </pre> |
|
| 2230 |
+ * Please note that bubble selectors will even listen to events for |
|
| 2231 |
+ * table rows that have been added <strong>after you registered for the events</strong>. |
|
| 2232 |
+ * |
|
| 2233 |
+ * @param selector optional a selector string for ##dollar#$()## to register the event only on those children of the list elements that |
|
| 2234 |
+ * match the selector. |
|
| 2235 |
+ * Supports all valid parameters for <var>$()</var> except functions. |
|
| 2236 |
+ * @param names the space-separated names of the events to register for, e.g. 'click'. Case-sensitive. The 'on' prefix in front of |
|
| 2237 |
+ * the name must not used. You can register the handler for more than one event by specifying several |
|
| 2238 |
+ * space-separated event names. If the name is prefixed |
|
| 2239 |
+ * with '|' (pipe), the event will be passed through and the event's default actions will be executed by the browser. |
|
| 2240 |
+ * If the name is prefixed with '?', the event will only be passed through if the handler returns <var>true</var>. |
|
| 2241 |
+ * @param eventHandler the callback <code>function(event, index)</code> to invoke when the event has been triggered: |
|
| 2242 |
+ * <dl> |
|
| 2243 |
+ * <dt>event</dt><dd>The original DOM event object.</dd> |
|
| 2244 |
+ * <dt>index</dt><dd>The index of the target object in the ##list#Minified list## .</dd> |
|
| 2245 |
+ * <dt class="this">this</dt><dd>A ##list#Minified list## containing the target element as only item (same as <var>event.target</var>).</dd> |
|
| 2246 |
+ * <dt class="returnValue">(callback return value)</dt><dd>The return value will only be used if the event name prefix was '?'. |
|
| 2247 |
+ * Then, a return value <var>false</var> will stop all further processing of the event and disable event bubbling. |
|
| 2248 |
+ * <var>true</var> will keep the event alive.</dd> |
|
| 2249 |
+ * </dl> |
|
| 2250 |
+ * @param customFunc a function to be called instead of a regular event handler with the arguments given in <var>args</var>. |
|
| 2251 |
+ * 'this' will be a ##list#Minified list## containing the target element as only item (same element as <var>event.target</var>). |
|
| 2252 |
+ * @param args optional an array of arguments to pass to the custom callback function instead of the event objects. If omitted, the function is |
|
| 2253 |
+ * called as event handler with the event object as argument. |
|
| 2254 |
+ * @param bubbleSelector optional a selector string for ##dollar#$()## to receive only events that bubbled up from an |
|
| 2255 |
+ * element that matches this selector. |
|
| 2256 |
+ * Supports all valid parameters for <var>$()</var> except functions. Analog to ##is(), |
|
| 2257 |
+ * the selector is optimized for the simple patterns '.classname', 'tagname' and 'tagname.classname'. |
|
| 2258 |
+ * @return the list |
|
| 2259 |
+ * @see ##off() allows you to unregister an event handler. |
|
| 2260 |
+ * @see ##onClick() as a shortcut for 'click' events. |
|
| 2261 |
+ * @see ##onOver() to simplify mouseover/mouseout events. |
|
| 2262 |
+ * @see ##onFocus() as convenient way to register for focus events. |
|
| 2263 |
+ * @see ##onChange() to get notified when an input's content changes. |
|
| 2264 |
+ */ |
|
| 2265 |
+ 'on': on, |
|
| 2266 |
+ |
|
| 2267 |
+ /*$ |
|
| 2268 |
+ * @id trigger |
|
| 2269 |
+ * @group EVENTS |
|
| 2270 |
+ * @requires on each |
|
| 2271 |
+ * @configurable default |
|
| 2272 |
+ * @name .trigger() |
|
| 2273 |
+ * @syntax list.trigger(name) |
|
| 2274 |
+ * @syntax list.trigger(name, eventObject) |
|
| 2275 |
+ * @module WEB |
|
| 2276 |
+ * |
|
| 2277 |
+ * Triggers event handlers registered with ##on(). |
|
| 2278 |
+ * Any event that has been previously registered using ##on() can be invoked with <var>trigger()</var>. Please note that |
|
| 2279 |
+ * it will not simulate the default behavior on the elements, such as a form submit when you click on a submit button. Event bubbling |
|
| 2280 |
+ * is supported, thus unless there's an event handler that cancels the event, the event will be triggered on all parent elements. |
|
| 2281 |
+ * |
|
| 2282 |
+ * |
|
| 2283 |
+ * @example Simulates a 'click' event on the button. |
|
| 2284 |
+ * <pre> |
|
| 2285 |
+ * $('#myButton').trigger('click');
|
|
| 2286 |
+ * </pre> |
|
| 2287 |
+ * |
|
| 2288 |
+ * @param name a single event name to trigger |
|
| 2289 |
+ * @param eventObj optional an object to pass to the event handler, provided the handler does not have custom arguments. |
|
| 2290 |
+ * Anything you pass here will be directly given to event handlers as event object, so you need to know what |
|
| 2291 |
+ * they expect. |
|
| 2292 |
+ * @return the list |
|
| 2293 |
+ * @see ##on() registers events that can be triggered. |
|
| 2294 |
+ */ |
|
| 2295 |
+ 'trigger': function (eventName, eventObj) {
|
|
| 2296 |
+ return this['each'](function(element, index) {
|
|
| 2297 |
+ var bubbleOn = true, el = element; |
|
| 2298 |
+ while(el && bubbleOn) {
|
|
| 2299 |
+ eachObj(el['M'], function(id, f) {
|
|
| 2300 |
+ bubbleOn = bubbleOn && f(eventName, eventObj, element); |
|
| 2301 |
+ }); |
|
| 2302 |
+ el = el['parentNode']; |
|
| 2303 |
+ } |
|
| 2304 |
+ }); |
|
| 2305 |
+ } |
|
| 2306 |
+ |
|
| 2307 |
+ /*$ |
|
| 2308 |
+ * @stop |
|
| 2309 |
+ */ |
|
| 2310 |
+ // @cond !trigger dummyTrigger:0 |
|
| 2311 |
+ , |
|
| 2312 |
+ ///#/snippet webListFuncs |
|
| 2313 |
+ ///#snippet extrasListFuncs |
|
| 2314 |
+ |
|
| 2315 |
+ /*$ |
|
| 2316 |
+ * @id ht |
|
| 2317 |
+ * @group ELEMENT |
|
| 2318 |
+ * @requires set template |
|
| 2319 |
+ * @configurable default |
|
| 2320 |
+ * @name .ht() |
|
| 2321 |
+ * @syntax list.ht(templateString, object...) |
|
| 2322 |
+ * @syntax list.ht(templateFunction, object...) |
|
| 2323 |
+ * @syntax list.ht(idSelector, object...) |
|
| 2324 |
+ * @module WEB+UTIL |
|
| 2325 |
+ * Replaces the content of the list elements with the HTML generated using the given template. The template uses |
|
| 2326 |
+ * ##template() syntax and HTML-escaped its output using ##escapeHtml(). |
|
| 2327 |
+ * |
|
| 2328 |
+ * @example When you have a HTML snippet like this: |
|
| 2329 |
+ * <pre> |
|
| 2330 |
+ * <div id="price"></div> |
|
| 2331 |
+ * </pre> |
|
| 2332 |
+ * Then you can format the price value like this: |
|
| 2333 |
+ * <pre> |
|
| 2334 |
+ * var price = 14.9; |
|
| 2335 |
+ * $('#price').ht('<b>${{::0.00}}</b>', price);
|
|
| 2336 |
+ * </pre> |
|
| 2337 |
+ * Results in: |
|
| 2338 |
+ * <pre> |
|
| 2339 |
+ * <div id="price"><b>$14.90</b></div> |
|
| 2340 |
+ * </pre> |
|
| 2341 |
+ * |
|
| 2342 |
+ * @example Render a list of names: |
|
| 2343 |
+ * <pre> |
|
| 2344 |
+ * var names = [ {first: 'James', last: 'Sullivan'},
|
|
| 2345 |
+ * {first: 'Michael', last: 'Wazowski'} ];
|
|
| 2346 |
+ * $('#list').ht('<h2>{{listName}}</h2>'+
|
|
| 2347 |
+ * '<ul>{{each n: names}}<li>{{n.first}} {{n.last}}</li>{{/each}}</ul>',
|
|
| 2348 |
+ * {listName: 'Guys', names: names});
|
|
| 2349 |
+ * </pre> |
|
| 2350 |
+ * The code creates this: |
|
| 2351 |
+ * <pre> |
|
| 2352 |
+ * <h2>Guys</h2> |
|
| 2353 |
+ * <ul><li>James Sullivan<li><li>Michael Wazowski</li></ul> |
|
| 2354 |
+ * </pre> |
|
| 2355 |
+ * |
|
| 2356 |
+ * @example You can store templates in <script> tags. First you need to create a <script> tag with a type not |
|
| 2357 |
+ * supported by the browser and put your template in there, like this: |
|
| 2358 |
+ * <pre><script id="myTimeTpl" type="minified-template">The time is {{HH:mm:ss}}.</script></pre>
|
|
| 2359 |
+ * Then you can specify the tag's id directly to access it: |
|
| 2360 |
+ * <pre>$('#timeDisplay').ht('#myTimeTpl', new Date());</pre>
|
|
| 2361 |
+ * |
|
| 2362 |
+ * @param templateString the template using ##template() syntax. Please note, because this is a template, you should |
|
| 2363 |
+ * avoid creating the template itself dynamically, as compiling templates is expensive and |
|
| 2364 |
+ * Minified will cache only a limited number of templates. Exception: If the template string does not use |
|
| 2365 |
+ * any template functionality (no {{}}), it does not need to be compiled and won't be cached.<br/>
|
|
| 2366 |
+ * The template will use ##escapeHtml() as escape function, so all template substitutions will be HTML-escaped, |
|
| 2367 |
+ * unless you use triple curly-braces. |
|
| 2368 |
+ * @param templateFunction instead of a HTML template, <var>ht()</var> can also use a template function, e.g. one |
|
| 2369 |
+ * created by ##template(). It will be invoked with the object as only argument. |
|
| 2370 |
+ * @param idSelector if you pass an ID CSS selector in the form "#myScript", Minified will recognize this and use the content |
|
| 2371 |
+ * of the specified <script> element as template. This allows you to put your template into |
|
| 2372 |
+ * a <script> tag with a non-JavaScript type (see example). Any string that starts with '#' and does not |
|
| 2373 |
+ * contain any spaces is used as selector. |
|
| 2374 |
+ * @param object optional one or more objects to pass to the template. If object is not set, the template is called with <var>undefined</var> |
|
| 2375 |
+ * as object. If exactly one object is given, it is passed directly to the template. If you specify more than one |
|
| 2376 |
+ * object, they are ##merge#merged##. |
|
| 2377 |
+ * @return the current list |
|
| 2378 |
+ * |
|
| 2379 |
+ * @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 <var>ht()</var>. |
|
| 2380 |
+ */ |
|
| 2381 |
+ 'ht': function(htmlTemplate, object) {
|
|
| 2382 |
+ var o = arguments.length > 2 ? merge(sub(arguments, 1)) : object; |
|
| 2383 |
+ return this['set']('innerHTML', isFunction(htmlTemplate) ? htmlTemplate(o) :
|
|
| 2384 |
+ /{{/.test(htmlTemplate) ? formatHtml(htmlTemplate, o) :
|
|
| 2385 |
+ /^#\S+$/.test(htmlTemplate) ? formatHtml($$(htmlTemplate)['text'], o) : htmlTemplate); |
|
| 2386 |
+ } |
|
| 2387 |
+ /*$ |
|
| 2388 |
+ * @stop |
|
| 2389 |
+ */ |
|
| 2390 |
+ // @cond !ht dummyHt:0 |
|
| 2391 |
+ ///#/snippet extrasListFuncs |
|
| 2392 |
+ }, M.prototype); |
|
| 2393 |
+ |
|
| 2394 |
+ //// DOLLAR FUNCTIONS //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
| 2395 |
+ copyObj({
|
|
| 2396 |
+ ///#snippet webDollarFuncs |
|
| 2397 |
+ /*$ |
|
| 2398 |
+ * @id request |
|
| 2399 |
+ * @group REQUEST |
|
| 2400 |
+ * @requires |
|
| 2401 |
+ * @configurable default |
|
| 2402 |
+ * @name $.request() |
|
| 2403 |
+ * @syntax $.request(method, url) |
|
| 2404 |
+ * @syntax $.request(method, url, data) |
|
| 2405 |
+ * @syntax $.request(method, url, data, settings) |
|
| 2406 |
+ * @module WEB |
|
| 2407 |
+ * Initiates a HTTP request to the given URL, using XMLHttpRequest. It returns a ##promiseClass#Promise## object that allows you to obtain the result. |
|
| 2408 |
+ * |
|
| 2409 |
+ * @example Invokes a REST web service and parses the resulting document using JSON: |
|
| 2410 |
+ * <pre> |
|
| 2411 |
+ * $.request('get', 'http://service.example.com/weather', {zipcode: 90210})
|
|
| 2412 |
+ * .then(function(txt) {
|
|
| 2413 |
+ * var json = $.parseJSON(txt); |
|
| 2414 |
+ * $('#weatherResult').fill('Today's forecast is is: ' + json.today.forecast);
|
|
| 2415 |
+ * }) |
|
| 2416 |
+ * .error(function(status, statusText, responseText) {
|
|
| 2417 |
+ * $('#weatherResult').fill('The weather service was not available.');
|
|
| 2418 |
+ * }); |
|
| 2419 |
+ * </pre> |
|
| 2420 |
+ * |
|
| 2421 |
+ * @example Sending a JSON object to a REST web service: |
|
| 2422 |
+ * <pre> |
|
| 2423 |
+ * var myRequest = { // create a request object that can be serialized via JSON
|
|
| 2424 |
+ * request: 'register', |
|
| 2425 |
+ * entries: [ |
|
| 2426 |
+ * {name: 'Joe',
|
|
| 2427 |
+ * job: 'Plumber' |
|
| 2428 |
+ * } |
|
| 2429 |
+ * ]}; |
|
| 2430 |
+ * |
|
| 2431 |
+ * function failureHandler() {
|
|
| 2432 |
+ * $('#registrationResult').fill('Registration failed');
|
|
| 2433 |
+ * } |
|
| 2434 |
+ * |
|
| 2435 |
+ * $.request('post', 'http://service.example.com/directory', $.toJSON(myRequest))
|
|
| 2436 |
+ * .then(function(txt) {
|
|
| 2437 |
+ * if (txt == 'OK') |
|
| 2438 |
+ * $('#registrationResult').fill('Registration succeeded');
|
|
| 2439 |
+ * else |
|
| 2440 |
+ * failureHandler(); |
|
| 2441 |
+ * }) |
|
| 2442 |
+ * .error(failureHandler); |
|
| 2443 |
+ * </pre> |
|
| 2444 |
+ * |
|
| 2445 |
+ * @example Using HTTP authentication and a custom XMLHttpRequest property. |
|
| 2446 |
+ * <pre>var handler = $.request('get', 'http://service.example.com/userinfo', null, {xhr: {withCredentials: true}, user: 'me', pass: 'secret'});</pre>
|
|
| 2447 |
+ * |
|
| 2448 |
+ * |
|
| 2449 |
+ * @param method the HTTP method, e.g. 'get', 'post' or 'head' (rule of thumb: use 'post' for requests that change data |
|
| 2450 |
+ * on the server, and 'get' to request data). Not case sensitive. |
|
| 2451 |
+ * @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 |
|
| 2452 |
+ * fancy on the server (keyword to google: Access-Control-Allow-Origin), you can only call URLs on the server your script originates from. |
|
| 2453 |
+ * @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 |
|
| 2454 |
+ * parameters (for all HTTP methods), a string (for all HTTP methods), a DOM document ('post' only) or a FormData object ('post' only).
|
|
| 2455 |
+ * 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 |
|
| 2456 |
+ * same name, use an array of values in the map. Use null as value for a parameter without value. |
|
| 2457 |
+ * @param settings optional a map of additional parameters. Supports the following properties (all optional): |
|
| 2458 |
+ * <dl><dt>headers</dt><dd>a map of HTTP headers to add to the request. Note that you should use the proper capitalization for the |
|
| 2459 |
+ * header 'Content-Type', if you set it, because otherwise it may be overwritten.</dd> |
|
| 2460 |
+ * <dt>xhr</dt><dd>a map of properties to set in the XMLHttpRequest object before the request is sent, for example <code>{withCredentials: true}</code>.</dd>
|
|
| 2461 |
+ * <dt>user</dt><dd>username for HTTP authentication, together with the <var>pass</var> parameter</dd> |
|
| 2462 |
+ * <dt>pass</dt><dd>password for HTTP authentication, together with the <var>user</var> parameter</dd> |
|
| 2463 |
+ * </dl> |
|
| 2464 |
+ * @return a ##promiseClass#Promise## containing the request's status. If the request has successfully completed with a HTTP status 2xx, |
|
| 2465 |
+ * the promise's completion handler will be called as <code>function(text, xhr)</code>: |
|
| 2466 |
+ * <dl><dt>text</dt><dd>The response sent by the server as text.</dd> |
|
| 2467 |
+ * <dt>xhr</dt><dd>The XMLHttpRequest used for the request. This allows you to retrieve the response in different |
|
| 2468 |
+ * formats (e.g. <var>responseXml</var> for an XML document</var>), to retrieve headers and more.</dd></dl> |
|
| 2469 |
+ * The failure handler will be called as <code>function(statusCode, statusText, text)</code>: |
|
| 2470 |
+ * <dl><dt>statusCode</dt><dd>The HTTP status (never 200; 0 if no HTTP request took place).</dd> |
|
| 2471 |
+ * <dt>text</dt><dd>The response's body text, if there was any, or the exception as string if the browser threw one.</dd> |
|
| 2472 |
+ * <dt>xhr</dt><dd>The XMLHttpRequest used for the request. This allows you to retrieve the response in different |
|
| 2473 |
+ * formats (e.g. <var>responseXml</var> for an XML document</var>), to retrieve headers and more..</dd></dl> |
|
| 2474 |
+ * The returned promise supports ##stop(). Calling <var>stop()</var> will invoke the XHR's <var>abort()</var> method. |
|
| 2475 |
+ * The underlying XmlHttpRequest can also be obtained from the promise's <var>xhr</var> property. |
|
| 2476 |
+ * |
|
| 2477 |
+ * @see ##values() serializes an HTML form in a format ready to be sent by <var>$.request</var>. |
|
| 2478 |
+ * @see ##$.parseJSON() can be used to parse JSON responses. |
|
| 2479 |
+ * @see ##$.toJSON() can create JSON messages. |
|
| 2480 |
+ * @see ##_.format() can be useful for creating REST-like URLs, if you use JavaScript's built-in <var>escape()</var> function. |
|
| 2481 |
+ */ |
|
| 2482 |
+ 'request': function (method, url, data, settings0) {
|
|
| 2483 |
+ var settings = settings0 || {};
|
|
| 2484 |
+ var xhr, callbackCalled = 0, prom = promise(), dataIsMap = data && (data['constructor'] == settings['constructor']); |
|
| 2485 |
+ try {
|
|
| 2486 |
+ prom['xhr'] = xhr = new XMLHttpRequest(); |
|
| 2487 |
+ |
|
| 2488 |
+ prom['stop0'] = function() { xhr['abort'](); };
|
|
| 2489 |
+ // @condend |
|
| 2490 |
+ |
|
| 2491 |
+ if (dataIsMap) { // if data is parameter map...
|
|
| 2492 |
+ data = collector(eachObj, data, function processParam(paramName, paramValue) {
|
|
| 2493 |
+ return collector(flexiEach, paramValue, function(v) {
|
|
| 2494 |
+ return encodeURIComponent(paramName) + ((v != _null) ? '=' + encodeURIComponent(v) : ''); |
|
| 2495 |
+ }); |
|
| 2496 |
+ }).join('&');
|
|
| 2497 |
+ } |
|
| 2498 |
+ |
|
| 2499 |
+ if (data != _null && !/post/i.test(method)) {
|
|
| 2500 |
+ url += '?' + data; |
|
| 2501 |
+ data = _null; |
|
| 2502 |
+ } |
|
| 2503 |
+ |
|
| 2504 |
+ xhr['open'](method, url, true, settings['user'], settings['pass']); |
|
| 2505 |
+ if (dataIsMap && /post/i.test(method)) |
|
| 2506 |
+ xhr['setRequestHeader']('Content-Type', 'application/x-www-form-urlencoded');
|
|
| 2507 |
+ |
|
| 2508 |
+ eachObj(settings['headers'], function(hdrName, hdrValue) {
|
|
| 2509 |
+ xhr['setRequestHeader'](hdrName, hdrValue); |
|
| 2510 |
+ }); |
|
| 2511 |
+ eachObj(settings['xhr'], function(name, value) {
|
|
| 2512 |
+ xhr[name] = value; |
|
| 2513 |
+ }); |
|
| 2514 |
+ |
|
| 2515 |
+ xhr['onreadystatechange'] = function() {
|
|
| 2516 |
+ if (xhr['readyState'] == 4 && !callbackCalled++) {
|
|
| 2517 |
+ if (xhr['status'] >= 200 && xhr['status'] < 300) |
|
| 2518 |
+ prom['fire'](true, [xhr['responseText'], xhr]); |
|
| 2519 |
+ else |
|
| 2520 |
+ prom['fire'](false, [xhr['status'], xhr['responseText'], xhr]); |
|
| 2521 |
+ } |
|
| 2522 |
+ }; |
|
| 2523 |
+ |
|
| 2524 |
+ xhr['send'](data); |
|
| 2525 |
+ } |
|
| 2526 |
+ catch (e) {
|
|
| 2527 |
+ if (!callbackCalled) |
|
| 2528 |
+ prom['fire'](false, [0, _null, toString(e)]); |
|
| 2529 |
+ } |
|
| 2530 |
+ |
|
| 2531 |
+ return prom; |
|
| 2532 |
+ }, |
|
| 2533 |
+ |
|
| 2534 |
+ /* |
|
| 2535 |
+ * JSON Module. Uses browser built-ins or json.org implementation if available. Otherwise its own implementation, |
|
| 2536 |
+ * originally based on public domain implementation http://www.JSON.org/json2.js / http://www.JSON.org/js.html. |
|
| 2537 |
+ * Extremely simplified code, made variables local, removed all side-effects (especially new properties for String, Date and Number). |
|
| 2538 |
+ */ |
|
| 2539 |
+ |
|
| 2540 |
+ /*$ |
|
| 2541 |
+ * @id ready |
|
| 2542 |
+ * @group EVENTS |
|
| 2543 |
+ * @requires ready_vars ready_init |
|
| 2544 |
+ * @configurable default |
|
| 2545 |
+ * @name $.ready() |
|
| 2546 |
+ * @syntax $.ready(handler) |
|
| 2547 |
+ * @module WEB |
|
| 2548 |
+ * 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, |
|
| 2549 |
+ * only the main HTML document needs to be complete. On older browsers it is the same as <var>window.onload</var>. |
|
| 2550 |
+ * |
|
| 2551 |
+ * If you call <var>ready()</var> after the page is completed, the handler is scheduled for invocation in the event loop as soon as possible. |
|
| 2552 |
+ * |
|
| 2553 |
+ * A shortcut for <var>ready()</var> is to call ##dollar#$()## with the handler function. It does the same with fewer characters. |
|
| 2554 |
+ * |
|
| 2555 |
+ * @example Registers a handler that sets some text in an element: |
|
| 2556 |
+ * <pre> |
|
| 2557 |
+ * $.ready(function() {
|
|
| 2558 |
+ * $('#someElement').fill('ready() called');
|
|
| 2559 |
+ * }); |
|
| 2560 |
+ * </pre> |
|
| 2561 |
+ * |
|
| 2562 |
+ * @param handler the <code>function()</code> to be called when the HTML is ready. |
|
| 2563 |
+ * @see ##dollar#$()## calls <var>ready()</var> when invoked with a function, offering a more convenient syntax. |
|
| 2564 |
+ */ |
|
| 2565 |
+ 'ready': ready, |
|
| 2566 |
+ |
|
| 2567 |
+ /*$ |
|
| 2568 |
+ * @id off |
|
| 2569 |
+ * @group EVENTS |
|
| 2570 |
+ * @requires on |
|
| 2571 |
+ * @configurable default |
|
| 2572 |
+ * @name $.off() |
|
| 2573 |
+ * @syntax $.off(handler) |
|
| 2574 |
+ * @module WEB |
|
| 2575 |
+ * Removes the given event handler. The call will be ignored if the given handler has not been registered using ##on(). |
|
| 2576 |
+ * If the handler has been registered for more than one element or event, it will be removed from all instances. |
|
| 2577 |
+ * |
|
| 2578 |
+ * Please note that you can not unregister event handlers registered using ##onOver() or ##onChange(). |
|
| 2579 |
+ * |
|
| 2580 |
+ * @example Adds a handler to an element: |
|
| 2581 |
+ * <pre> |
|
| 2582 |
+ * function myEventHandler() {
|
|
| 2583 |
+ * this.style.backgroundColor = 'red'; // 'this' contains the element that caused the event |
|
| 2584 |
+ * } |
|
| 2585 |
+ * $('#myElement').on('click', myEventHandler); // add event handler
|
|
| 2586 |
+ * |
|
| 2587 |
+ * window.setInterval(function() { // after 5s, remove event handler
|
|
| 2588 |
+ * $.off(myEventHandler); |
|
| 2589 |
+ * }, 5000); |
|
| 2590 |
+ * </pre> |
|
| 2591 |
+ * |
|
| 2592 |
+ * @param handler the handler to unregister, as given to ##on(). It must be a handler that has previously been registered using ##on(). |
|
| 2593 |
+ * If the handler is not registered as event handler, the function does nothing. |
|
| 2594 |
+ * |
|
| 2595 |
+ * @see ##on() registers an event handler. |
|
| 2596 |
+ */ |
|
| 2597 |
+ 'off': off |
|
| 2598 |
+ |
|
| 2599 |
+ /*$ |
|
| 2600 |
+ * @stop |
|
| 2601 |
+ */ |
|
| 2602 |
+ // @cond !off dummyOff:null |
|
| 2603 |
+ , |
|
| 2604 |
+ ///#/snippet webDollarFuncs |
|
| 2605 |
+ ///#snippet extrasDollarFuncs |
|
| 2606 |
+ |
|
| 2607 |
+ /*$ |
|
| 2608 |
+ * @id wait |
|
| 2609 |
+ * @group EVENTS |
|
| 2610 |
+ * @configurable default |
|
| 2611 |
+ * @requires promise |
|
| 2612 |
+ * @name $.wait() |
|
| 2613 |
+ * @syntax $.wait() |
|
| 2614 |
+ * @syntax $.wait(durationMs) |
|
| 2615 |
+ * @syntax $.wait(durationMs, args) |
|
| 2616 |
+ * @module WEB+UTIL |
|
| 2617 |
+ * |
|
| 2618 |
+ * 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, |
|
| 2619 |
+ * because it allows you to chain delays into your animation chain. |
|
| 2620 |
+ * |
|
| 2621 |
+ * The operation can be interrupted by calling the promise's ##stop() function. |
|
| 2622 |
+ * |
|
| 2623 |
+ * @example Chained animation using Promise callbacks. The element is first moved to the position 200/0, then to 200/200, waits for 50ms |
|
| 2624 |
+ * and finally moves to 100/100. |
|
| 2625 |
+ * <pre> |
|
| 2626 |
+ * var div = $('#myMovingDiv').set({$left: '0px', $top: '0px'});
|
|
| 2627 |
+ * div.animate({$left: '200px', $top: '0px'}, 600, 0)
|
|
| 2628 |
+ * .then(function() {
|
|
| 2629 |
+ * div.animate({$left: '200px', $top: '200px'}, 800, 0);
|
|
| 2630 |
+ * }).then(function() {
|
|
| 2631 |
+ * return _.wait(50); |
|
| 2632 |
+ * }).then(function() {
|
|
| 2633 |
+ * div.animate({$left: '100px', $top: '100px'}, 400);
|
|
| 2634 |
+ * }); |
|
| 2635 |
+ * }); |
|
| 2636 |
+ * </pre> |
|
| 2637 |
+ * |
|
| 2638 |
+ * |
|
| 2639 |
+ * @param durationMs optional the number of milliseconds to wait. If omitted, the promise will be fulfilled as soon as the browser can run it |
|
| 2640 |
+ * from the event loop. |
|
| 2641 |
+ * @param args optional an array or list of arguments to pass to the promise handler |
|
| 2642 |
+ * @return a ##promise#Promise## object that will be fulfilled when the time is over, or fail when the promise's ##stop() has been called. |
|
| 2643 |
+ * The promise argument of a fulfilled promise is the <var>args</var> parameter as given to <var>wait()</var>. The returned promise supports ##stop() |
|
| 2644 |
+ * to interrupt the promise. |
|
| 2645 |
+ */ |
|
| 2646 |
+ 'wait': function(durationMs, args) {
|
|
| 2647 |
+ var p = promise(); |
|
| 2648 |
+ var id = setTimeout(function() {
|
|
| 2649 |
+ p['fire'](true, args); |
|
| 2650 |
+ }, durationMs); |
|
| 2651 |
+ p['stop0'] = function() { p['fire'](false); clearTimeout(id); };
|
|
| 2652 |
+ return p; |
|
| 2653 |
+ } |
|
| 2654 |
+ |
|
| 2655 |
+ /*$ |
|
| 2656 |
+ * @stop |
|
| 2657 |
+ */ |
|
| 2658 |
+ // @cond !wait dummyWait:0 |
|
| 2659 |
+ |
|
| 2660 |
+ ///#/snippet extrasDollarFuncs |
|
| 2661 |
+ }, $); |
|
| 2662 |
+ |
|
| 2663 |
+ //// UNDERSCORE FUNCTIONS //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
| 2664 |
+ |
|
| 2665 |
+ copyObj({
|
|
| 2666 |
+ ///#snippet utilUnderscoreFuncs |
|
| 2667 |
+ // @condblock each |
|
| 2668 |
+ 'each': each, |
|
| 2669 |
+ // @condend |
|
| 2670 |
+ // @condblock each |
|
| 2671 |
+ 'toObject': toObject, |
|
| 2672 |
+ // @condend |
|
| 2673 |
+ // @condblock find |
|
| 2674 |
+ 'find': find, |
|
| 2675 |
+ // @condend |
|
| 2676 |
+ // @condblock equals |
|
| 2677 |
+ 'equals': equals, |
|
| 2678 |
+ // @condend |
|
| 2679 |
+ |
|
| 2680 |
+ /*$ |
|
| 2681 |
+ * @id copyobj |
|
| 2682 |
+ * @group OBJECT |
|
| 2683 |
+ * @requires |
|
| 2684 |
+ * @configurable default |
|
| 2685 |
+ * @name _.copyObj() |
|
| 2686 |
+ * @syntax _.copyObj(from) |
|
| 2687 |
+ * @syntax _.copyObj(from, to) |
|
| 2688 |
+ * @module UTIL |
|
| 2689 |
+ * Copies every property of the first object into the second object. The properties are copied as shallow-copies. |
|
| 2690 |
+ * |
|
| 2691 |
+ * @example Copying properties: |
|
| 2692 |
+ * <pre>var target = {a:3, c: 3};
|
|
| 2693 |
+ * _.copyObj({a: 1, b: 2}, target); // target is now {a: 1, b: 2, c: 3}</pre>
|
|
| 2694 |
+ * |
|
| 2695 |
+ * @example Inline property merge: |
|
| 2696 |
+ * <pre>var target = _.copyObj({a: 1, b: 2}, {a:3, c: 3}); // target is now {a: 1, b: 2, c: 3}</pre>
|
|
| 2697 |
+ * |
|
| 2698 |
+ * @example Duplicating an object: |
|
| 2699 |
+ * <pre>var target = _.copyObj({a: 1, b: 2}); // target is now {a: 1, b: 2}</pre>
|
|
| 2700 |
+ * |
|
| 2701 |
+ * @param from the object to copy from |
|
| 2702 |
+ * @param to optional the object to copy to. If not given, a new object will be created. |
|
| 2703 |
+ * @return the object that has been copied to |
|
| 2704 |
+ * |
|
| 2705 |
+ * @see ##_.extend() is very similar to <var>copyObj()</var>, but with a slightly different syntax. |
|
| 2706 |
+ * @see ##_.merge() copies a list of objects into a new object. |
|
| 2707 |
+ */ |
|
| 2708 |
+ 'copyObj': copyObj, |
|
| 2709 |
+ |
|
| 2710 |
+ /*$ |
|
| 2711 |
+ * @id extend |
|
| 2712 |
+ * @group OBJECT |
|
| 2713 |
+ * @requires |
|
| 2714 |
+ * @configurable default |
|
| 2715 |
+ * @name _.extend() |
|
| 2716 |
+ * @syntax _.extend(target, src...) |
|
| 2717 |
+ * @module UTIL |
|
| 2718 |
+ * Copies every property of the source objects into the first object. The source objects are specified using variable arguments. |
|
| 2719 |
+ * There can be more than one. |
|
| 2720 |
+ * The properties are copied as shallow-copies. |
|
| 2721 |
+ * |
|
| 2722 |
+ * <b>Please note:</b> Unlike jQuery, <var>extend</var> does not directly add a function to extend Minified, although |
|
| 2723 |
+ * you can use it to for this. To add a function to ##list#Minified lists##, add a property to |
|
| 2724 |
+ * ##M#MINI.M##. If you want to extend <var>$</var> or <var>_</var>, just assign the new function(s) as property. |
|
| 2725 |
+ * |
|
| 2726 |
+ * @example Copying properties: |
|
| 2727 |
+ * <pre>var target = {a:3, c: 3};
|
|
| 2728 |
+ * _.extend(target, {a: 1, b: 2}); // target is now {a: 1, b: 2, c: 3}</pre>
|
|
| 2729 |
+ * |
|
| 2730 |
+ * @example Using several source values: |
|
| 2731 |
+ * <pre>var extend = _.extend({a: 1, b: 2}, {a:3, c: 3}, {d: 5}); // target is now {a: 1, b: 2, c: 3, d: 5}</pre>
|
|
| 2732 |
+ * |
|
| 2733 |
+ * @param target the object to copy to |
|
| 2734 |
+ * @param src the object(s) to copy from. Variable argument, there can be any number of sources. Nulls and <var>undefined</var> |
|
| 2735 |
+ * parameters will be ignored. |
|
| 2736 |
+ * @return the target |
|
| 2737 |
+ * |
|
| 2738 |
+ * @see ##_.copyObj() is very similar to <var>extend()</var>, but with a slightly different and more straightforward syntax. |
|
| 2739 |
+ * @see ##_.merge() copies a list of objects into a new object. |
|
| 2740 |
+ */ |
|
| 2741 |
+ 'extend': function(target) {
|
|
| 2742 |
+ return merge(sub(arguments, 1), target); |
|
| 2743 |
+ }, |
|
| 2744 |
+ |
|
| 2745 |
+ /*$ |
|
| 2746 |
+ * @id eachobj |
|
| 2747 |
+ * @group OBJECT |
|
| 2748 |
+ * @requires |
|
| 2749 |
+ * @configurable default |
|
| 2750 |
+ * @name _.eachObj() |
|
| 2751 |
+ * @syntax _.eachObj(obj, callback) |
|
| 2752 |
+ * @syntax _.eachObj(obj, callback, ctx) |
|
| 2753 |
+ * @module UTIL |
|
| 2754 |
+ * Invokes the given function once for each property of the given object. The callback is not invoked for inherited properties. |
|
| 2755 |
+ * |
|
| 2756 |
+ * @example Dumps all properties of an object. |
|
| 2757 |
+ * <pre> |
|
| 2758 |
+ * var s = ''; |
|
| 2759 |
+ * _.eachObj({a: 1, b: 5, c: 2}, function(key, value) {
|
|
| 2760 |
+ * s += 'key=' + key + ' value=' + value + '\n'; |
|
| 2761 |
+ * }); |
|
| 2762 |
+ * </pre> |
|
| 2763 |
+ * |
|
| 2764 |
+ * @param obj the object to use |
|
| 2765 |
+ * @param callback The callback <code>function(key, value)</code> to invoke for each property. |
|
| 2766 |
+ * <dl><dt>key</dt><dd>The name of the current property.</dd> |
|
| 2767 |
+ * <dt>value</dt><dd>The value of the current property.</dd> |
|
| 2768 |
+ * <dt class="this">this</dt><dd>The given context. If not set, the object itself.</dd> |
|
| 2769 |
+ * The callback's return value will be ignored. |
|
| 2770 |
+ * @param ctx optional a context to pass to the callback as 'this'. |
|
| 2771 |
+ * @return the object |
|
| 2772 |
+ * |
|
| 2773 |
+ * @see ##_.each() iterates through a list. |
|
| 2774 |
+ */ |
|
| 2775 |
+ 'eachObj': eachObj, |
|
| 2776 |
+ |
|
| 2777 |
+ /*$ |
|
| 2778 |
+ * @id isobject |
|
| 2779 |
+ * @group TYPE |
|
| 2780 |
+ * @requires |
|
| 2781 |
+ * @configurable default |
|
| 2782 |
+ * @name _.isObject() |
|
| 2783 |
+ * @syntax _.isObject(obj) |
|
| 2784 |
+ * @module UTIL |
|
| 2785 |
+ * Checks whether the given reference is an object as defined by <var>typeof</var>. |
|
| 2786 |
+ * |
|
| 2787 |
+ * @param obj the object to test |
|
| 2788 |
+ * @return <var>true</var> if the object is an object, <var>false</var> otherwise. |
|
| 2789 |
+ */ |
|
| 2790 |
+ 'isObject': isObject, |
|
| 2791 |
+ |
|
| 2792 |
+ /*$ |
|
| 2793 |
+ * @id format |
|
| 2794 |
+ * @group FORMAT |
|
| 2795 |
+ * @requires template |
|
| 2796 |
+ * @configurable default |
|
| 2797 |
+ * @name _.format() |
|
| 2798 |
+ * @syntax _.format() |
|
| 2799 |
+ * @syntax _.format(template, object) |
|
| 2800 |
+ * @syntax _.format(template, object, escapeFunction) |
|
| 2801 |
+ * @module UTIL |
|
| 2802 |
+ * Formats an object using a ##template#template##. The template syntax is shared with ##_.template(). The only difference is that |
|
| 2803 |
+ * <var>format()</var> frees you from the extra step of creating the template. In any case, whether you use |
|
| 2804 |
+ * <var>format()</var> or ##_.template(), the template will be cached. Be careful when you create templates dynamically, as |
|
| 2805 |
+ * every template is cached and consumes memory.<br/> |
|
| 2806 |
+ * If you only want to format a single value, use ##_.formatValue(). |
|
| 2807 |
+ * |
|
| 2808 |
+ * @example Format a name: |
|
| 2809 |
+ * <pre>var s = _.formatHtml("{{first}} {{last}}", {first: 'Tim', last: 'Taylor'});</pre>
|
|
| 2810 |
+ * |
|
| 2811 |
+ * @example Format a list of dates: |
|
| 2812 |
+ * <pre>var s = _.format("{{each}}{{this :: yyyy-MM-dd}}{{/each}}", dateList);</pre>
|
|
| 2813 |
+ * |
|
| 2814 |
+ * @param template The ##template#template## as a string. The template, once created, will be cached. |
|
| 2815 |
+ * @param object the object to format |
|
| 2816 |
+ * @param escapeFunction optional The callback <code>function(inputString)</code> that will be used |
|
| 2817 |
+ * to escape all output: |
|
| 2818 |
+ * <dl><dt>inputString</dt><dd>The string to escape.</dd> |
|
| 2819 |
+ * <dt class="returnValue">(callback return value)</dt><dd>The escaped string.</dd></dl> |
|
| 2820 |
+ * If no escapeFunction has been given, the output will not be escaped. |
|
| 2821 |
+ * ##_.escapeHtml() can be used as an escape function for HTML, and ##_.escapeRegExp() for regular expressions. |
|
| 2822 |
+ * JavaScript's built-in <var>escape()</var> function can escape URL components. |
|
| 2823 |
+ * See ##_.htmlFormat() for a version of <var>format()</var> that already includes HTML escaping. |
|
| 2824 |
+ * @return the string created by the template |
|
| 2825 |
+ * |
|
| 2826 |
+ * @see ##_.template() creates a template function, using the same syntax. |
|
| 2827 |
+ * @see ##_.formatHtml() is a variant of <var>format()</var> with HTML-escpaping built it. |
|
| 2828 |
+ * @see ##_.formatValue() formats a single number or date. |
|
| 2829 |
+ * @see ##_.escapeRegExp() can be used by <var>format()</var> to escape regular expressions. |
|
| 2830 |
+ */ |
|
| 2831 |
+ 'format': function(tpl, object, escapeFunction) {
|
|
| 2832 |
+ return template(tpl, escapeFunction)(object); |
|
| 2833 |
+ }, |
|
| 2834 |
+ |
|
| 2835 |
+ /*$ |
|
| 2836 |
+ * @id template |
|
| 2837 |
+ * @group FORMAT |
|
| 2838 |
+ * @requires date_constants |
|
| 2839 |
+ * @configurable default |
|
| 2840 |
+ * @name _.template() |
|
| 2841 |
+ * @syntax _.template(template) |
|
| 2842 |
+ * @syntax _.template(template, escapeFunction) |
|
| 2843 |
+ * @module UTIL |
|
| 2844 |
+ * Parses a Handlebars-like template to create a reusable template function. |
|
| 2845 |
+ * |
|
| 2846 |
+ * The syntax of the template uses a syntax that superficially looks like |
|
| 2847 |
+ * <a href="http://handlebarsjs.com/">Handlebars</a>. Unlike Handlebars, it is based on raw JavaScript expressions and thus gives you |
|
| 2848 |
+ * complete freedom, but also offers you shortcuts for formatting, iteration and conditionals. |
|
| 2849 |
+ * |
|
| 2850 |
+ * Every template can receive exactly one object as input. If you need more than one value as input, put all required values |
|
| 2851 |
+ * into an object. |
|
| 2852 |
+ * |
|
| 2853 |
+ * Use double curly braces to embed a JavaScript expression and insert its result: |
|
| 2854 |
+ * <pre>{{a}} plus {{b}} is {{a+b}}</pre>
|
|
| 2855 |
+ * |
|
| 2856 |
+ * To use such a template, create it with <var>template()</var> and then execute the resulting function: |
|
| 2857 |
+ * <pre>var myTemplate = _.template('{{a}} plus {{b}} is {{a+b}}');
|
|
| 2858 |
+ * var result = myTemplate({a: 5, b: 7});</pre>
|
|
| 2859 |
+ * If you pass an object as input, its properties will be mapped using JavaScript's <code>with</code> |
|
| 2860 |
+ * statement and are available as variables throughout the template. |
|
| 2861 |
+ * |
|
| 2862 |
+ * If you have only a simple value to render, you can pass it directly and access it through the pre-defined |
|
| 2863 |
+ * variable <var>obj</var>: |
|
| 2864 |
+ * <pre>var myTemplate = _.template('The result is {{obj}}.');
|
|
| 2865 |
+ * var result = myTemplate(17);</pre> |
|
| 2866 |
+ * Alternatively, you could also access the input as <var>this</var>, but be aware that JavaScript wraps simples types |
|
| 2867 |
+ * such as Number and Boolean. <var>this</var> is the default, so you can omit it to get the same result: |
|
| 2868 |
+ * <pre>var myTemplate = _.template('The result is {{ }}.');
|
|
| 2869 |
+ * var result = myTemplate(17);</pre> |
|
| 2870 |
+ * |
|
| 2871 |
+ * Minified templates can use ##_.formatValue() formats directly. Just separate them from the expression by |
|
| 2872 |
+ * a double-colon: |
|
| 2873 |
+ * <pre>The price is {{obj::#.00}}.</pre>
|
|
| 2874 |
+ * |
|
| 2875 |
+ * Conditions can be expressed using <code>if</code> and <code>else</code>: |
|
| 2876 |
+ * <pre>Hello {{if visits==0}}New{{else if visits<10}}Returning{{else}}Regular{{/if}} Customer.</pre>
|
|
| 2877 |
+ * You can use any JavaScript expression as condition. |
|
| 2878 |
+ * |
|
| 2879 |
+ * Use <code>each</code> to iterate through a list: |
|
| 2880 |
+ * <pre>var myTemplate = _.template( |
|
| 2881 |
+ * '{{each names}}{{this.firstName}} {{this.lastName}}{{/each}}');
|
|
| 2882 |
+ * var result = myTemplate({names: [{firstName: 'Joe', lastName: 'Jones'},
|
|
| 2883 |
+ * {firstName: 'Marc', lastName: 'Meyer'}]});</pre>
|
|
| 2884 |
+ * <code>each</code> will iterate through the members of the given object. It |
|
| 2885 |
+ * calls its body for each item and put a reference to the item into <var>this</var>. |
|
| 2886 |
+ * Optionally, you can specify up to two variables to store the value in and |
|
| 2887 |
+ * the zero-based index of the current item: |
|
| 2888 |
+ * <pre>var myTemplate = _.template( |
|
| 2889 |
+ * '{{each value, index: names}}{{index}}. {{value.firstName}} {{value.lastName}}{{/each}}');
|
|
| 2890 |
+ * </pre> |
|
| 2891 |
+ * |
|
| 2892 |
+ * If you do not pass an expression to <code>each</code>, it will take the list from <var>this</var>: |
|
| 2893 |
+ * <pre>var myTemplate = _.template('{{each value:}}{{value}};{{/each}}');
|
|
| 2894 |
+ * var result = myTemplate([1, 2, 3]);</pre> |
|
| 2895 |
+ * |
|
| 2896 |
+ * Beside lists, you can also iterate through the properties of an object. The property name will be stored |
|
| 2897 |
+ * in the first given parameter and the value in <var>this</var> and the second parameter: |
|
| 2898 |
+ * <pre>var myTemplate = _.template('{{each key, value: nicknames}}{{key}}: {{value}}{{/each}}');
|
|
| 2899 |
+ * var result = myTemplate({nicknames: {Matt: 'Matthew', John: 'Jonathan'} });</pre>
|
|
| 2900 |
+ * |
|
| 2901 |
+ * Shorter version of the previous example that uses <var>this</var> for the value: |
|
| 2902 |
+ * <pre>var myTemplate = _.template('{{each key: nicknames}}{{key}}: {{this}}{{/each}}');</pre>
|
|
| 2903 |
+ * |
|
| 2904 |
+ * If you do not need the key, you can omit the variable specification: |
|
| 2905 |
+ * <pre>var myTemplate = _.template('{{each nicknames}}{{this}}{{/each}}');</pre>
|
|
| 2906 |
+ * |
|
| 2907 |
+ * You can define your own variables, using the regular JavaScript syntax, with 'var': |
|
| 2908 |
+ * <pre>var myTemplate = _.template('{{var s=very.long.name, sum=a+b;}}{{s.desc}}, {{sum}}');</pre>
|
|
| 2909 |
+ * |
|
| 2910 |
+ * In some situations, it may be inevitable to embed raw JavaScript in the template. |
|
| 2911 |
+ * To embed JavaScript code, prefix the code with a '#': |
|
| 2912 |
+ * <pre>var myTemplate = _.template( |
|
| 2913 |
+ * '{{each}}{{#var sum = 0; for (var i = 0; i < 3; i++) sum += this.numbers[i]; }}{{sum}}{{/each}}');
|
|
| 2914 |
+ * var result = myTemplate([['Foreword', 'Intro'], ['Something', 'Something else']]);</pre> |
|
| 2915 |
+ * |
|
| 2916 |
+ * |
|
| 2917 |
+ * By default, all output will be escaped. You can prevent this by using triple-curly-braces: |
|
| 2918 |
+ * <pre>Here's the original: {{{rawText}}}</pre>.
|
|
| 2919 |
+ * |
|
| 2920 |
+ * The template's JavaScript code is executed in a sandbox without access to global variables. Minified defines the |
|
| 2921 |
+ * following variables for you: |
|
| 2922 |
+ * <table> |
|
| 2923 |
+ * <tr><th>Name</th><th>Desciption</th></tr> |
|
| 2924 |
+ * <tr><td>this</td><td>The template object outside of <code>each</code>. Inside <code>each</code>s, the current value.</td></tr> |
|
| 2925 |
+ * <tr><td>obj</td><td>The parameter given to the template function.</td></tr> |
|
| 2926 |
+ * <tr><td>_</td><td>A reference to Minified Util.</td></tr> |
|
| 2927 |
+ * <tr><td>esc</td><td>The escape function given when the template has been defined. If no function has been given, |
|
| 2928 |
+ * a default function that returns the input unmodified.</td></tr> |
|
| 2929 |
+ * <tr><td>print</td><td>A <code>function(text,...)</code> that appends one or more strings to the template result.</td></tr> |
|
| 2930 |
+ * <tr><td>each</td><td>A <code>function(listOrObject, eachCallback)</code> that can iterate over lists or object properties. |
|
| 2931 |
+ * The <var>eachCallback</var> is a <code>function(key, value)</code> for objects or <code>function(value, index)</code> |
|
| 2932 |
+ * for arrays that will be invoked for each item. |
|
| 2933 |
+ * </table> |
|
| 2934 |
+ * |
|
| 2935 |
+ * Every template you create is already cached, so it not an expensive operation to call ##_.template() a second |
|
| 2936 |
+ * time with the same template. However, because of caching, you should be careful when creating templates |
|
| 2937 |
+ * dynamically, as this will fill the cache up quickly. |
|
| 2938 |
+ * |
|
| 2939 |
+ * @param template The template as a string using the syntax described below. |
|
| 2940 |
+ * @param escapeFunction optional The callback <code>function(inputString)</code> that will be used |
|
| 2941 |
+ * to escape all output: |
|
| 2942 |
+ * <dl><dt>inputString</dt><dd>The string to escape.</dd> |
|
| 2943 |
+ * <dt class="returnValue">(callback return value)</dt><dd>The escaped string.</dd></dl> |
|
| 2944 |
+ * If no escapeFunction has been given, the output will not be escaped. |
|
| 2945 |
+ * ##_.escapeHtml() can be used as an escape function for HTML, and ##_.escapeRegExp() for regular expressions. |
|
| 2946 |
+ * JavaScript's built-in <var>escape()</var> function can escape URL components. |
|
| 2947 |
+ * @return the value returned by the last invocation of <var>func</var> |
|
| 2948 |
+ * |
|
| 2949 |
+ * @see ##_.format() shares <var>template()</var>'s syntax but returns the result directly. |
|
| 2950 |
+ * @see ##_.formatHtml() is a variant of <var>format()</var> with HTML escaping. |
|
| 2951 |
+ * @see ##_.escapeHtml() can be used by <var>template()</var> to escape HTML. |
|
| 2952 |
+ * @see ##_.escapeRegExp() can be used by <var>template()</var> to escape regular expressions. |
|
| 2953 |
+ * @see ##HTML() creates a HTML element tree from a template. |
|
| 2954 |
+ */ |
|
| 2955 |
+ 'template': template, |
|
| 2956 |
+ |
|
| 2957 |
+ /*$ |
|
| 2958 |
+ * @id formathtml |
|
| 2959 |
+ * @group FORMAT |
|
| 2960 |
+ * @requires template |
|
| 2961 |
+ * @configurable default |
|
| 2962 |
+ * @name _.formatHtml() |
|
| 2963 |
+ * @syntax _.formatHtml() |
|
| 2964 |
+ * @syntax _.formatHtml(template, object) |
|
| 2965 |
+ * @module UTIL |
|
| 2966 |
+ * Formats an object using a ##template#template## with HTML escaping for the output. |
|
| 2967 |
+ * The template syntax is shared with ##_.template(). Output in double curly braces is automatically escaped using ##_.escapeHtml(). |
|
| 2968 |
+ * <var>formatHtml()</var> just creates a new template with HTML escaping and invokes it immediately. |
|
| 2969 |
+ * The template will be cached. Be careful when you create templates dynamically, as |
|
| 2970 |
+ * every template is cached and consumes memory.<br/> |
|
| 2971 |
+ * If you only want to format a single value, use ##_.formatValue(). |
|
| 2972 |
+ * |
|
| 2973 |
+ * @example Format a name: |
|
| 2974 |
+ * <pre>var s = _.formatHtml("{{first}} {{last}}", {first: 'Tim', last: 'Taylor'});</pre>
|
|
| 2975 |
+ * |
|
| 2976 |
+ * @example Format a list of dates: |
|
| 2977 |
+ * <pre>var s = _.formatHtml("{{each}}{{::yyyy-MM-dd}}{{/each}}", dateList);</pre>
|
|
| 2978 |
+ * |
|
| 2979 |
+ * @param template The ##template#template## as a string. The template, once created, will be cached. |
|
| 2980 |
+ * @param object the object to format |
|
| 2981 |
+ * @return the string created by the template |
|
| 2982 |
+ * |
|
| 2983 |
+ * @see ##ht() works uses <var>formatHtml</var> to set element's innerHTML. |
|
| 2984 |
+ * @see ##HTML() create HTML nodes using <var>formatHtml</var>. |
|
| 2985 |
+ * @see ##_.template() creates a template function, using the same syntax. |
|
| 2986 |
+ * @see ##_.format() allows you to specify alternative escape mechanisms. |
|
| 2987 |
+ */ |
|
| 2988 |
+ 'formatHtml': formatHtml |
|
| 2989 |
+ /*$ |
|
| 2990 |
+ * @stop |
|
| 2991 |
+ */ |
|
| 2992 |
+ |
|
| 2993 |
+ // @cond !format dummyFormatHtml:0 |
|
| 2994 |
+ , |
|
| 2995 |
+ |
|
| 2996 |
+ ///#/snippet utilUnderscoreFuncs |
|
| 2997 |
+ ///#snippet extrasUnderscoreFuncs |
|
| 2998 |
+ // @condblock promise |
|
| 2999 |
+ 'promise': promise |
|
| 3000 |
+ // @condend promise |
|
| 3001 |
+ |
|
| 3002 |
+ /*$ |
|
| 3003 |
+ * @stop |
|
| 3004 |
+ */ |
|
| 3005 |
+ // @cond !promise dummyPromise:0 |
|
| 3006 |
+ |
|
| 3007 |
+ ///#/snippet extrasUnderscoreFuncs |
|
| 3008 |
+ }, _); |
|
| 3009 |
+ |
|
| 3010 |
+ ////INITIALIZATION //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
| 3011 |
+ ///#snippet webInit |
|
| 3012 |
+ /*$ |
|
| 3013 |
+ * @id ready_init |
|
| 3014 |
+ * @dependency |
|
| 3015 |
+ */ |
|
| 3016 |
+ document.addEventListener("DOMContentLoaded", function() {
|
|
| 3017 |
+ callList(DOMREADY_HANDLER); |
|
| 3018 |
+ DOMREADY_HANDLER = _null; |
|
| 3019 |
+ }, false); |
|
| 3020 |
+ /*$ |
|
| 3021 |
+ @stop |
|
| 3022 |
+ */ |
|
| 3023 |
+ |
|
| 3024 |
+ |
|
| 3025 |
+ ///#/snippet webInit |
|
| 3026 |
+ |
|
| 3027 |
+ return {
|
|
| 3028 |
+ ///#snippet extrasExports |
|
| 3029 |
+ |
|
| 3030 |
+ /*$ |
|
| 3031 |
+ * @id html |
|
| 3032 |
+ * @group ELEMENT |
|
| 3033 |
+ * @requires template ht |
|
| 3034 |
+ * @configurable default |
|
| 3035 |
+ * @name HTML() |
|
| 3036 |
+ * @syntax HTML(templateString, object...) |
|
| 3037 |
+ * @syntax HTML(templateFunction, object...) |
|
| 3038 |
+ * @syntax HTML(idSelector, object...) |
|
| 3039 |
+ * @module WEB |
|
| 3040 |
+ * Creates a ##list#list## of HTML nodes from the given HTML template. The list is compatible with ##add(), ##fill() and related methods. |
|
| 3041 |
+ * The template uses the ##template() syntax with ##escapeHtml() escaping for values. |
|
| 3042 |
+ * |
|
| 3043 |
+ * Please note that the function <var>HTML</var> will not be automatically exported by Minified. You should always import it |
|
| 3044 |
+ * using the recommended import statement: |
|
| 3045 |
+ * <pre> |
|
| 3046 |
+ * var MINI = require('minified'), $ = MINI.$, $$ = MINI.$$, EE = MINI.EE, <strong>HTML = MINI.HTML</strong>;
|
|
| 3047 |
+ * </pre> |
|
| 3048 |
+ * |
|
| 3049 |
+ * @example Creating a HTML element showing a number: |
|
| 3050 |
+ * <pre> |
|
| 3051 |
+ * <div id="price">-</div> |
|
| 3052 |
+ * </pre> |
|
| 3053 |
+ * Then the price can be set like this: |
|
| 3054 |
+ * <pre> |
|
| 3055 |
+ * var price = 14.9; |
|
| 3056 |
+ * $('#price').fill(HTML('<b>${{::0.99}}</b>', price));
|
|
| 3057 |
+ * </pre> |
|
| 3058 |
+ * Results in: |
|
| 3059 |
+ * <pre> |
|
| 3060 |
+ * <div id="price"><b>$14.90</b></div> |
|
| 3061 |
+ * </pre> |
|
| 3062 |
+ * |
|
| 3063 |
+ * @example Adding elements to an existing list: |
|
| 3064 |
+ * <pre> |
|
| 3065 |
+ * var names = [ {first: 'James', last: 'Sullivan'},
|
|
| 3066 |
+ * {first: 'Michael', last: 'Wazowski'} ];
|
|
| 3067 |
+ * $('#list').add(HTML('{{each}}<li>{{this.first}} {{this.last}}</li>{{/each}}', names);
|
|
| 3068 |
+ * </pre> |
|
| 3069 |
+ * The code adds this to #list: |
|
| 3070 |
+ * <pre> |
|
| 3071 |
+ * <li>James Sullivan<li><li>Michael Wazowski</li> |
|
| 3072 |
+ * </pre> |
|
| 3073 |
+ * |
|
| 3074 |
+ * @example You can store templates in <script> tags. First you need to create a <script> tag with a type not |
|
| 3075 |
+ * supported by the browser and put your template in there, like this: |
|
| 3076 |
+ * <pre><script id="myTimeTpl" type="minified-template">The time is {{HH:mm:ss}}.</script></pre>
|
|
| 3077 |
+ * Then you can specify the tag's id directly to access it: |
|
| 3078 |
+ * <pre>$('#timeDisplay').fill(HTML('#myTimeTpl', new Date()));</pre>
|
|
| 3079 |
+ * |
|
| 3080 |
+ * @param templateString the template using ##template() syntax. Please note, because this is a template, you should |
|
| 3081 |
+ * avoid creating the template itself dynamically, as compiling templates is expensive and |
|
| 3082 |
+ * Minified will cache only a limited number of templates. Exception: If the template string does not use |
|
| 3083 |
+ * any template functionality (no {{}}), it does not need to be compiled and won't be cached.
|
|
| 3084 |
+ * The template will use ##escapeHtml() as escape function, so all template substitutions will be HTML-escaped, |
|
| 3085 |
+ * unless you use triple curly-braces. |
|
| 3086 |
+ * @param templateFunction instead of a HTML template <var>HTML()</var> also accepts a template function, e.g. one |
|
| 3087 |
+ * created by ##template(). It will be invoked with the object as only argument. |
|
| 3088 |
+ * @param idSelector if you pass an ID CSS selector in the form "#myScript", Minified will recognize this and use the content |
|
| 3089 |
+ * of the specified <script> element as template. This allows you to put your template into |
|
| 3090 |
+ * a <script> tag with a non-JavaScript type (see example). Any string that starts with '#' and does not |
|
| 3091 |
+ * contain any spaces is used as selector. |
|
| 3092 |
+ * @param object optional one or more objects to pass to the template. If object is not set, the template is called with <var>undefined</var> |
|
| 3093 |
+ * as object. If exactly one object is given, it is passed directly to the template. If you specify more than one |
|
| 3094 |
+ * object, they are ##merge#merged##. |
|
| 3095 |
+ * @return the list containing the new HTML nodes |
|
| 3096 |
+ * |
|
| 3097 |
+ * @see ##ht() is a shortcut for <code>fill(HTML())</code>. |
|
| 3098 |
+ * @see ##EE() is a different way of creating HTML nodes. |
|
| 3099 |
+ */ |
|
| 3100 |
+ 'HTML': function () {
|
|
| 3101 |
+ var div = EE('div');
|
|
| 3102 |
+ return _(call(div['ht'], div, arguments)[0].childNodes); |
|
| 3103 |
+ }, |
|
| 3104 |
+ /*$ |
|
| 3105 |
+ * @stop |
|
| 3106 |
+ */ |
|
| 3107 |
+ |
|
| 3108 |
+ ///#/snippet extrasExports |
|
| 3109 |
+ ///#snippet utilExports |
|
| 3110 |
+ /*$ |
|
| 3111 |
+ * @id underscore |
|
| 3112 |
+ * @group LIST |
|
| 3113 |
+ * @name _() |
|
| 3114 |
+ * @syntax _(item...) |
|
| 3115 |
+ * @configurable default |
|
| 3116 |
+ * @module UTIL |
|
| 3117 |
+ * Creates a new Minified list. Supports variable arguments so you can add items directly to the list. For arguments that are lists |
|
| 3118 |
+ * (as defined by ##_.isList()), the list content will be added to the new list. Unlike #dollar#$()#, this is not done recursively |
|
| 3119 |
+ * and thus you can create a list of lists by wrapping arguments in a list. Another difference between <var>_()</var> and <var>$()</var> |
|
| 3120 |
+ * is that <var>$()</var> will automatically remove <var>null</var> values while <var>_()</var> will keep them. |
|
| 3121 |
+ * |
|
| 3122 |
+ * @example Creating an empty list: |
|
| 3123 |
+ * <pre>_()</pre> |
|
| 3124 |
+ * |
|
| 3125 |
+ * @example Creating a list with three items: |
|
| 3126 |
+ * <pre>_(1, 2, 3)</pre> |
|
| 3127 |
+ * |
|
| 3128 |
+ * @example Creating the same list, but by passing an array. One array level will be flattened: |
|
| 3129 |
+ * <pre>_([1, 2, 3])</pre> |
|
| 3130 |
+ * |
|
| 3131 |
+ * @example Creating a list containing the arrays [1, 2] and [3, 4]. |
|
| 3132 |
+ * <pre>_([[1, 2], [3, 4]])</pre> |
|
| 3133 |
+ * |
|
| 3134 |
+ * @example Merging two lists: |
|
| 3135 |
+ * <pre>var a = _("a", "b", "c");
|
|
| 3136 |
+ * var b = _("x", "y", "z");
|
|
| 3137 |
+ * var merged = _(a, b); // contains _("a", "b", "c", "x", "y", "z")
|
|
| 3138 |
+ * </pre> |
|
| 3139 |
+ * |
|
| 3140 |
+ * @example Adding two elements to a list: |
|
| 3141 |
+ * <pre>var a = _(1, 2, 3); |
|
| 3142 |
+ * var a4 = _(a, 4); // contains _(1, 2, 3, 4) |
|
| 3143 |
+ * </pre> |
|
| 3144 |
+ * |
|
| 3145 |
+ * @example Mixing different list types and single elements: |
|
| 3146 |
+ * <pre>_(1, [], [2, 3], _(), _(4, 5)); // same content as _(1, 2, 3, 4, 5)</pre> |
|
| 3147 |
+ * |
|
| 3148 |
+ * @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 |
|
| 3149 |
+ * ##Minified list#list## (but NOT recursively). |
|
| 3150 |
+ */ |
|
| 3151 |
+ '_': _, |
|
| 3152 |
+ /*$ |
|
| 3153 |
+ * @stop |
|
| 3154 |
+ */ |
|
| 3155 |
+ ///#/snippet utilExports |
|
| 3156 |
+ ///#snippet webExports |
|
| 3157 |
+ |
|
| 3158 |
+ /*$ |
|
| 3159 |
+ * @id dollar |
|
| 3160 |
+ * @group SELECTORS |
|
| 3161 |
+ * @requires |
|
| 3162 |
+ * @dependency yes |
|
| 3163 |
+ * @name $() |
|
| 3164 |
+ * @syntax $() |
|
| 3165 |
+ * @syntax $(selector) |
|
| 3166 |
+ * @syntax $(selector, context) |
|
| 3167 |
+ * @syntax $(selector, context, childOnly) |
|
| 3168 |
+ * @syntax $(list) |
|
| 3169 |
+ * @syntax $(list, context) |
|
| 3170 |
+ * @syntax $(list, context, childOnly) |
|
| 3171 |
+ * @syntax $(object) |
|
| 3172 |
+ * @syntax $(object, context) |
|
| 3173 |
+ * @syntax $(object, context, childOnly) |
|
| 3174 |
+ * @syntax $(domreadyFunction) |
|
| 3175 |
+ * @module WEB |
|
| 3176 |
+ * Creates a new ##list#Minified list##, or register a DOMReady-handler. |
|
| 3177 |
+ * The most common usage is with a CSS-like selector. <var>$()</var> will then create a list containing all elements of the current HTML |
|
| 3178 |
+ * document that fulfill the filter conditions. Alternatively you can also specify a list of objects or a single object. |
|
| 3179 |
+ * Nested lists will automatically be flattened, and nulls will automatically be removed from the resulting list. |
|
| 3180 |
+ * If you call <var>$()</var> without any arguments, it will return an empty list. |
|
| 3181 |
+ * |
|
| 3182 |
+ * Additionally, you can specify a second argument to provide a context. Contexts only make sense if you selected |
|
| 3183 |
+ * HTML nodes with the first parameter. Then the context limits the resulting list to include only those nodes |
|
| 3184 |
+ * that are descendants of the context nodes. The context can be either a selector, a list or a single HTML node, and will be |
|
| 3185 |
+ * processed like the first argument. A third arguments allows you to limit the list to |
|
| 3186 |
+ * only those elements that are direct children of the context nodes (so a child of a child would be filtered out). |
|
| 3187 |
+ * |
|
| 3188 |
+ * The lists created by <var>$()</var> are the same type as the ##list#Minified lists## created by Util's #underscore#_() constructor and other |
|
| 3189 |
+ * Util methods. All Util methods work on lists created by <var>$()</var>. If you want to add your own methods to those lists, |
|
| 3190 |
+ * use ##M#MINI.M##. |
|
| 3191 |
+ * |
|
| 3192 |
+ * As a special shortcut, if you pass a function to <var>$()</var>, it will be registered using #ready#$.ready() to be executed |
|
| 3193 |
+ * when the DOM model is complete. |
|
| 3194 |
+ * |
|
| 3195 |
+ * @example A simple selector to find an element by id. |
|
| 3196 |
+ * <pre> |
|
| 3197 |
+ * var l0 = $('#myElementId');
|
|
| 3198 |
+ * </pre> |
|
| 3199 |
+ * |
|
| 3200 |
+ * @example You can pass an object reference to create a list containing only this element: |
|
| 3201 |
+ * <pre> |
|
| 3202 |
+ * var l1 = $(document.getElementById('myElementId'));
|
|
| 3203 |
+ * </pre> |
|
| 3204 |
+ * |
|
| 3205 |
+ * @example Lists and arrays will be copied: |
|
| 3206 |
+ * <pre> |
|
| 3207 |
+ * var l2 = $([elementA, elementB, elementC]); |
|
| 3208 |
+ * </pre> |
|
| 3209 |
+ * |
|
| 3210 |
+ * @example Lists will be automatically flattened and nulls removed. So this list <var>l3</var> has the same content as <var>l2</var>: |
|
| 3211 |
+ * <pre> |
|
| 3212 |
+ * var l3 = $([elementA, [elementB, null, elementC], null]); |
|
| 3213 |
+ * </pre> |
|
| 3214 |
+ * |
|
| 3215 |
+ * @example This is a simple selector to find all elements with the given class. |
|
| 3216 |
+ * <pre> |
|
| 3217 |
+ * var l4 = $('.myClass');
|
|
| 3218 |
+ * </pre> |
|
| 3219 |
+ * |
|
| 3220 |
+ * @example A selector to find all elements of the given type. |
|
| 3221 |
+ * <pre> |
|
| 3222 |
+ * var l5 = $('input'); // finds all input elements
|
|
| 3223 |
+ * </pre> |
|
| 3224 |
+ * |
|
| 3225 |
+ * @example A selector to find all elements with the given type and class. |
|
| 3226 |
+ * <pre> |
|
| 3227 |
+ * var l6 = $('input.myRadio'); // finds all input elements with class 'myRadio'
|
|
| 3228 |
+ * </pre> |
|
| 3229 |
+ * |
|
| 3230 |
+ * @example A selector to find all elements that are descendants of the given element. |
|
| 3231 |
+ * <pre> |
|
| 3232 |
+ * var l7 = $('#myForm input'); // finds all input elements contained in the element myForm
|
|
| 3233 |
+ * </pre> |
|
| 3234 |
+ * |
|
| 3235 |
+ * @example A selector to find all elements that have either a CSS class 'a' or class 'b': |
|
| 3236 |
+ * <pre> |
|
| 3237 |
+ * var l8 = $('.a, .b'); // finds all elements that have class a or class b
|
|
| 3238 |
+ * </pre> |
|
| 3239 |
+ * |
|
| 3240 |
+ * @example A selector that finds all elements that are descendants of the element myDivision, are inside an element with the |
|
| 3241 |
+ * class .myForm and are input elements: |
|
| 3242 |
+ * <pre> |
|
| 3243 |
+ * var l9 = $('#myDivision .myForm input');
|
|
| 3244 |
+ * </pre> |
|
| 3245 |
+ * |
|
| 3246 |
+ * @example Contexts can make it easier to specify ancestors: |
|
| 3247 |
+ * <pre> |
|
| 3248 |
+ * var l10 = $('.myRadio', '#formA, #formB, #formC');
|
|
| 3249 |
+ * </pre> |
|
| 3250 |
+ * The result is identical to: |
|
| 3251 |
+ * <pre> |
|
| 3252 |
+ * var l10 = $('#formA .myRadio, #formB .myRadio, #formC .myRadio');
|
|
| 3253 |
+ * </pre> |
|
| 3254 |
+ * |
|
| 3255 |
+ * @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. |
|
| 3256 |
+ * <pre> |
|
| 3257 |
+ * $('#myElementId').set('$color', 'red');
|
|
| 3258 |
+ * </pre> |
|
| 3259 |
+ * |
|
| 3260 |
+ * @example Most list methods return the list you invoked them on, allowing you to chain them: |
|
| 3261 |
+ * <pre> |
|
| 3262 |
+ * $('#myForm .myRadio').addClass('uncheckedRadio')
|
|
| 3263 |
+ * .set('checked', true)
|
|
| 3264 |
+ * .on('click', function() {
|
|
| 3265 |
+ * $(this).set({@: 'uncheckedRadio');
|
|
| 3266 |
+ * }); |
|
| 3267 |
+ * </pre> |
|
| 3268 |
+ * |
|
| 3269 |
+ * @example Using $() as a #ready#$.ready() shortcut: |
|
| 3270 |
+ * <pre> |
|
| 3271 |
+ * $(function() {
|
|
| 3272 |
+ * // in here you can safely work with the HTML document |
|
| 3273 |
+ * }); |
|
| 3274 |
+ * </pre> |
|
| 3275 |
+ * |
|
| 3276 |
+ * @param selector a simple, CSS-like selector for HTML elements. It supports '#id' (lookup by id), '.class' (lookup by class), |
|
| 3277 |
+ * 'element' (lookup by elements) and 'element.class' (combined class and element). Use commas to combine several selectors. |
|
| 3278 |
+ * You can also join two or more selectors by space to find elements which are descendants of the previous selectors. |
|
| 3279 |
+ * For example, use 'div' to find all div elements, '.header' to find all elements containing a class name called 'header', and |
|
| 3280 |
+ * 'a.popup' for all a elements with the class 'popup'. To find all elements with 'header' or 'footer' class names, |
|
| 3281 |
+ * write '.header, .footer'. To find all divs elements below the element with the id 'main', use '#main div'. |
|
| 3282 |
+ * The selector "*" will return all elements. |
|
| 3283 |
+ * @param list a list to copy. It can be an array, another Minified list, a DOM nodelist or anything else that has a <var>length</var> property and |
|
| 3284 |
+ * allows read access by index. A shallow copy of the list will be returned. Nulls will be automatically removed from the copy. Nested lists |
|
| 3285 |
+ * will be flattened, so the result only contains nodes. |
|
| 3286 |
+ * @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. |
|
| 3287 |
+ * @param domreadyFunction a function to be registered using #ready#$.ready(). |
|
| 3288 |
+ * @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 |
|
| 3289 |
+ * a selector, a list or using a single object, just like the first argument. |
|
| 3290 |
+ * The returned list will contain only descendants of the context nodes. All others will be filtered out. |
|
| 3291 |
+ * @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 |
|
| 3292 |
+ * true, all descendants of the context will be included. |
|
| 3293 |
+ * @return the array-like ##list#Minified list## object containing the content specified by the selector. |
|
| 3294 |
+ * 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. |
|
| 3295 |
+ * If you combined several selectors using commas, only the individual results of the selectors will keep the document order, |
|
| 3296 |
+ * but will then be joined to form a single list. This list will |
|
| 3297 |
+ * not be in document order anymore, unless you use a build without legacy IE support. |
|
| 3298 |
+ * Duplicate nodes will be removed from selectors, but not from lists. |
|
| 3299 |
+ * |
|
| 3300 |
+ * @see #underscore#_() is Util's alternative constructor for ##list#Minified lists## |
|
| 3301 |
+ * @see ##dollardollar#$$()## works like <var>$()</var>, but returns the resulting list's first element. |
|
| 3302 |
+ */ |
|
| 3303 |
+ '$': $, |
|
| 3304 |
+ |
|
| 3305 |
+ /*$ |
|
| 3306 |
+ * @id dollardollar |
|
| 3307 |
+ * @group SELECTORS |
|
| 3308 |
+ * @requires |
|
| 3309 |
+ * @configurable default |
|
| 3310 |
+ * @name $$() |
|
| 3311 |
+ * @syntax $(selector) |
|
| 3312 |
+ * @syntax $(selector, context) |
|
| 3313 |
+ * @syntax $(selector, context, childOnly) |
|
| 3314 |
+ * @shortcut $$() - It is recommended that you assign MINI.$$ to a variable $$. |
|
| 3315 |
+ * @module WEB |
|
| 3316 |
+ * Returns a DOM object containing the first match of the given selector, or <var>undefined</var> if no match was found. |
|
| 3317 |
+ * <var>$$</var> allows you to easily access an element directly. It is the equivalent to writing <code>$(selector)[0]</code>. |
|
| 3318 |
+ * |
|
| 3319 |
+ * Please note that the function <var>$$</var> will not be automatically exported by Minified. You should always import it |
|
| 3320 |
+ * using the recommended import statement: |
|
| 3321 |
+ * <pre> |
|
| 3322 |
+ * var MINI = require('minified'), $ = MINI.$, $$ = MINI.$$, EE = MINI.EE;
|
|
| 3323 |
+ * </pre> |
|
| 3324 |
+ * |
|
| 3325 |
+ * @example Select the checkbox 'myCheckbox': |
|
| 3326 |
+ * <pre> |
|
| 3327 |
+ * $$('#myCheckbox').checked = true;
|
|
| 3328 |
+ * </pre> |
|
| 3329 |
+ * |
|
| 3330 |
+ * @param selector a simple, CSS-like selector for the element. Uses the same syntax as #dollar#$(). The most common |
|
| 3331 |
+ * parameter for this function is the id selector with the syntax "#id". |
|
| 3332 |
+ * @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 |
|
| 3333 |
+ * a selector, a list or using a single object, just like the first argument. |
|
| 3334 |
+ * The returned list will contain only descendants of the context nodes. All others will be filtered out. |
|
| 3335 |
+ * @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 |
|
| 3336 |
+ * true, all descendants of the context will be included. |
|
| 3337 |
+ * @return a DOM object of the first match, or <var>undefined</var> if the selector did not return at least one match |
|
| 3338 |
+ * |
|
| 3339 |
+ * @see ##dollar#$()## creates a list using the selector, instead of returning only the first result. |
|
| 3340 |
+ */ |
|
| 3341 |
+ '$$': $$, |
|
| 3342 |
+ |
|
| 3343 |
+ /*$ |
|
| 3344 |
+ * @id M |
|
| 3345 |
+ * @name M |
|
| 3346 |
+ * @syntax MINI.M |
|
| 3347 |
+ * @module WEB, UTIL |
|
| 3348 |
+ * |
|
| 3349 |
+ * Exposes the internal class used by all ##list#Minified lists##. This is mainly intended to allow you adding your |
|
| 3350 |
+ * own functions. |
|
| 3351 |
+ * |
|
| 3352 |
+ * @example Adding a function printLength() to <var>M</var>: |
|
| 3353 |
+ * <pre> |
|
| 3354 |
+ * MINI.M.prototype.printLength = function() { console.log(this.length); };
|
|
| 3355 |
+ * </pre> |
|
| 3356 |
+ */ |
|
| 3357 |
+ 'M': M, |
|
| 3358 |
+ |
|
| 3359 |
+ /*$ |
|
| 3360 |
+ * @id getter |
|
| 3361 |
+ * @requires get |
|
| 3362 |
+ * @name MINI.getter |
|
| 3363 |
+ * @syntax MINI.getter |
|
| 3364 |
+ * @module WEB |
|
| 3365 |
+ * |
|
| 3366 |
+ * Exposes a map of prefix handlers used by ##get(). You can add support for a new prefix in <var>get()</var> |
|
| 3367 |
+ * by adding a function to this map. The prefix can be any string consisting solely of non-alphanumeric characters |
|
| 3368 |
+ * that's not already used by Minified. |
|
| 3369 |
+ * |
|
| 3370 |
+ * You must not replace <var>getters</var> by a new map, but must always modify the existing map. |
|
| 3371 |
+ * |
|
| 3372 |
+ * The function's signature is <code>function(list, name)</code> where |
|
| 3373 |
+ * <dl><dt>list</dt><dd>Is the Minified list to get the value from. By convention you should always use only the first element. The list is |
|
| 3374 |
+ * non-empty and the first elememt can't be null or undefined (get() automatically returns <var>undefined</var> in |
|
| 3375 |
+ * all other case).</dd> |
|
| 3376 |
+ * <dt>name</dt><dd>The name of the property. That's the part AFTER the prefix.</dd> |
|
| 3377 |
+ * <dt class="returnValue">(callback return value)</dt><dd>The value to return to the user.</dd></dl> |
|
| 3378 |
+ * |
|
| 3379 |
+ * @example Adding a shortcut '||' for accessing border style properties: |
|
| 3380 |
+ * <pre> |
|
| 3381 |
+ * MINI.getter['||'] = function(list, name) {
|
|
| 3382 |
+ * return list.get('$border' + name.replace(/^[a-z]/, function(a) { return a.toUpperCase()});
|
|
| 3383 |
+ * }; |
|
| 3384 |
+ * |
|
| 3385 |
+ * var borderColor = $('#box').get('||color'); // same as '$borderColor'
|
|
| 3386 |
+ * var borderLeftRadius = $('#box').get('||leftRadius'); // same as '$borderLeftRadius'
|
|
| 3387 |
+ * </pre> |
|
| 3388 |
+ * |
|
| 3389 |
+ * @example Adding XLink attribute support to get(). This is useful if you work with SVG. The prefix is '>'. |
|
| 3390 |
+ * <pre> |
|
| 3391 |
+ * MINI.getter['>'] = function(list, name) {
|
|
| 3392 |
+ * return list[0].getAttributeNS('http://www.w3.org/1999/xlink', name);
|
|
| 3393 |
+ * }; |
|
| 3394 |
+ * |
|
| 3395 |
+ * var xlinkHref = $('#svgLink').get('>href');
|
|
| 3396 |
+ * </pre> |
|
| 3397 |
+ */ |
|
| 3398 |
+ 'getter': getter, |
|
| 3399 |
+ |
|
| 3400 |
+ /*$ |
|
| 3401 |
+ * @id setter |
|
| 3402 |
+ * @requires set |
|
| 3403 |
+ * @name MINI.setter |
|
| 3404 |
+ * @syntax MINI.setter |
|
| 3405 |
+ * @module WEB |
|
| 3406 |
+ * |
|
| 3407 |
+ * Exposes a map of prefix handlers used by ##set(). You can add support for a new prefix in <var>set()</var> |
|
| 3408 |
+ * by adding a function to this map. The prefix can be any string consisting solely of non-alphanumeric characters |
|
| 3409 |
+ * that's not already used by Minified. |
|
| 3410 |
+ * |
|
| 3411 |
+ * You must not replace <var>setters</var> by a new map, but must always modify the existing map. |
|
| 3412 |
+ * |
|
| 3413 |
+ * The function's signature is <code>function(list, name, value)</code> where |
|
| 3414 |
+ * <dl><dt>list</dt><dd>Is the Minified list to use.</dd> |
|
| 3415 |
+ * <dt>name</dt><dd>The name of the property. That's the part AFTER the prefix.</dd> |
|
| 3416 |
+ * <dt>value</dt><dd>Either the value to set, or a callback function to create the value that you must call for each |
|
| 3417 |
+ * value (see ##set() ).</dd> |
|
| 3418 |
+ * </dl> |
|
| 3419 |
+ * |
|
| 3420 |
+ * If you provide complete ##get() and ##set() support for a prefix, you are also able to use it in other Minified |
|
| 3421 |
+ * function such as ##animate() and ##toggle(). |
|
| 3422 |
+ * |
|
| 3423 |
+ * @example Adding a shortcut '||' for accessing border style properties. As it's just calling ##set() for an existing |
|
| 3424 |
+ * property, it is not required to extra code for the callback. |
|
| 3425 |
+ * <pre> |
|
| 3426 |
+ * MINI.setter['||'] = function(list, name, value) {
|
|
| 3427 |
+ * list.set('$border' + name.replace(/^[a-z]/, function(a) { return a.toUpperCase()}, value);
|
|
| 3428 |
+ * }; |
|
| 3429 |
+ * |
|
| 3430 |
+ * $('#box').set('||color', 'red'); // same as set('$borderColor', 'red')
|
|
| 3431 |
+ * $('#box').set('||leftRadius', 4); // same as set('$borderLeftRadius', 4)
|
|
| 3432 |
+ * </pre> |
|
| 3433 |
+ * |
|
| 3434 |
+ * @example Adding XLink attribute support to set(). This is useful if you work with SVG. The prefix is '>'. |
|
| 3435 |
+ * <pre> |
|
| 3436 |
+ * MINI.setter['>'] = function(list, name, value) {
|
|
| 3437 |
+ * list.each(function(obj, index) {
|
|
| 3438 |
+ * var v; |
|
| 3439 |
+ * if (_.isFunction(value)) |
|
| 3440 |
+ * v = value(obj.getAttributeNS('http://www.w3.org/1999/xlink', name), index, obj);
|
|
| 3441 |
+ * else |
|
| 3442 |
+ * v = value; |
|
| 3443 |
+ * |
|
| 3444 |
+ * if (v == null) |
|
| 3445 |
+ * obj.removeAttributeNS('http://www.w3.org/1999/xlink', name);
|
|
| 3446 |
+ * else |
|
| 3447 |
+ * obj.setAttributeNS('http://www.w3.org/1999/xlink', name, v);
|
|
| 3448 |
+ * }); |
|
| 3449 |
+ * }; |
|
| 3450 |
+ * |
|
| 3451 |
+ * $('#svgLink').set('>href', 'http://minifiedjs.com/');
|
|
| 3452 |
+ * </pre> |
|
| 3453 |
+ */ |
|
| 3454 |
+ 'setter': setter |
|
| 3455 |
+ /*$ |
|
| 3456 |
+ * @stop |
|
| 3457 |
+ */ |
|
| 3458 |
+ ///#/snippet webExports |
|
| 3459 |
+ }; |
|
| 3460 |
+ |
|
| 3461 |
+ ///#snippet commonAmdEnd |
|
| 3462 |
+}); |
|
| 3463 |
+///#/snippet commonAmdEnd |
|
| 3464 |
+///#snippet webDocs |
|
| 3465 |
+ |
|
| 3466 |
+/*$ |
|
| 3467 |
+ * @id list |
|
| 3468 |
+ * @name Minified Lists |
|
| 3469 |
+ * @module WEB, UTIL |
|
| 3470 |
+ * |
|
| 3471 |
+ * <i>Minified lists</i> are Array-like objects provided by Minified. Like a regular JavaScript array, |
|
| 3472 |
+ * they provide a <var>length</var> property and you can access their content using the index operator (<code>a[5]</code>). |
|
| 3473 |
+ * However, they do not provide the same methods as JavaScript's native array and are designed to be immutable, so |
|
| 3474 |
+ * there is no direct way to add something to a Minified list. Instead Minified provides a number of functions and methods |
|
| 3475 |
+ * that take a list and create a modified copy which, for example, may contain additional elements. |
|
| 3476 |
+ * |
|
| 3477 |
+ * Minified lists are typically created either using the Web module's #dollar#$()</a></code> function or with the Util module's |
|
| 3478 |
+ * #underscore#_()</a></code> function, but many functions in the Util module also return a Minified list. |
|
| 3479 |
+ * |
|
| 3480 |
+ * The Util module provides a function ##_.array() that converts a Minified list to a regular JavaScript array. |
|
| 3481 |
+ */ |
|
| 3482 |
+ |
|
| 3483 |
+/*$ |
|
| 3484 |
+ * @id promiseClass |
|
| 3485 |
+ * @name Promise |
|
| 3486 |
+ * @module WEB, UTIL |
|
| 3487 |
+ * |
|
| 3488 |
+ * <i>Promises</i> are objects that represent the future result of an asynchronous operation. When you start such an operation, using #request#$.request(), |
|
| 3489 |
+ * ##animate(), or ##wait(), you will get a Promise object that allows you to get the result as soon as the operation is finished. |
|
| 3490 |
+ * |
|
| 3491 |
+ * Minified's full distribution ships with a <a href="http://promises-aplus.github.io/promises-spec/">Promises/A+</a>-compliant implementation of Promises that should |
|
| 3492 |
+ * be able to interoperate with most other Promises implementations. Minified's Web module in stand-alone distribution comes with a limited implementation. |
|
| 3493 |
+ * See below for details. |
|
| 3494 |
+ * |
|
| 3495 |
+ * What may be somewhat surprising about this Promises specification is that the only standard-compliant way to access the result is to |
|
| 3496 |
+ * register callbacks. They will be invoked as soon as the operation is finished. |
|
| 3497 |
+ * If the operation already ended when you register the callbacks, the callback will then just be called from the event loop as soon |
|
| 3498 |
+ * as possible (but never while the ##then() you register them with is still running).<br/> |
|
| 3499 |
+ * This design forces you to handle the operation result asynchronously and disencourages 'bad' techniques such as polling. |
|
| 3500 |
+ * |
|
| 3501 |
+ * The central method of a Promise, and indeed the only required function in Promises/A+, is ##then(). It allows you to register |
|
| 3502 |
+ * two callback methods, one for success (called 'fulfillment' in Promises/A+ terminology) and one for failures (called 'rejection' in Promises/A+). |
|
| 3503 |
+ * |
|
| 3504 |
+ * This example shows you how to use <var>then()</var>: |
|
| 3505 |
+ * <pre> |
|
| 3506 |
+ * $.request('get', 'http://example.com/weather?zip=90210')
|
|
| 3507 |
+ * .then(function success(result) {
|
|
| 3508 |
+ * alert('The weather is ' + result);
|
|
| 3509 |
+ * }, function error(exception) {
|
|
| 3510 |
+ * alert('Something went wrong');
|
|
| 3511 |
+ * }); |
|
| 3512 |
+ * </pre> |
|
| 3513 |
+ * |
|
| 3514 |
+ * What makes Promises so special is that ##then() itself returns a new Promise, which is based on the Promise <var>then()</var> was called on, but can be |
|
| 3515 |
+ * modified by the outcome of callbacks. Both arguments to <var>then()</var> are optional, and you can also write the code like this: |
|
| 3516 |
+ * <pre> |
|
| 3517 |
+ * $.request('get', 'http://example.com/weather?zip=90210')
|
|
| 3518 |
+ * .then(function success(result) {
|
|
| 3519 |
+ * alert('The weather is ' + result);
|
|
| 3520 |
+ * }) |
|
| 3521 |
+ * .then(null, function error(exception) {
|
|
| 3522 |
+ * alert('Something went wrong');
|
|
| 3523 |
+ * }); |
|
| 3524 |
+ * </pre> |
|
| 3525 |
+ * |
|
| 3526 |
+ * Because the first ##then() returns a new Promise based on the original Promise, the second <var>then()</var> will handle errors of the request just like |
|
| 3527 |
+ * 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, |
|
| 3528 |
+ * 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 |
|
| 3529 |
+ * the Promise returned by <var>then()</var>. Any exception thrown in a callback causes the new Promise to be in error state. |
|
| 3530 |
+ * |
|
| 3531 |
+ * Before I show you the second difference between the original Promise and the new Promise, let me make the example a bit more readable |
|
| 3532 |
+ * by using ##error(), which is not part of Promises/A+, but a simple extension by Minified. It just registers the failure callback without |
|
| 3533 |
+ * forcing you to specify <var>null</var> as first argument: |
|
| 3534 |
+ * <pre> |
|
| 3535 |
+ * $.request('get', 'http://example.com/weather?zip=90210')
|
|
| 3536 |
+ * .then(function success(result) {
|
|
| 3537 |
+ * alert('The weather is ' + result);
|
|
| 3538 |
+ * }) |
|
| 3539 |
+ * .error(function error(exception) { // error(callback) is equivalent to then(null, callback)
|
|
| 3540 |
+ * alert('Something went wrong');
|
|
| 3541 |
+ * }); |
|
| 3542 |
+ * </pre> |
|
| 3543 |
+ * |
|
| 3544 |
+ * A very powerful capability of Promises is that you can easily chain them. If a ##then() callback returns a value, the new Promise returned |
|
| 3545 |
+ * by <var>then()</var> will be marked as success (fulfilled) and this value is the result of the operation. If a callback returns a Promise, |
|
| 3546 |
+ * the new Promise will assume the state of the returned Promise. You can use the latter to create chains of asynchronous operations, |
|
| 3547 |
+ * but you still need only a single error handler for all of them and you do not need to nest functions to achieve this: |
|
| 3548 |
+ * <pre> |
|
| 3549 |
+ * $.request('get', 'http://example.com/zipcode?location=Beverly+Hills,+CA')
|
|
| 3550 |
+ * .then(function(resultZip) {
|
|
| 3551 |
+ * return $.request('get', 'http://example.com/weather', {zip: resultZip});
|
|
| 3552 |
+ * }) |
|
| 3553 |
+ * .then(function(resultWeather) {
|
|
| 3554 |
+ * alert('The weather in Beverly Hills is ' + resultWeather);
|
|
| 3555 |
+ * }) |
|
| 3556 |
+ * .error(function(exception) {
|
|
| 3557 |
+ * alert('Something went wrong');
|
|
| 3558 |
+ * }); |
|
| 3559 |
+ * </pre> |
|
| 3560 |
+ * |
|
| 3561 |
+ * Only the full Minified distribution allows you to create promises yourself, using the ##promise() function. The Promises/A+ |
|
| 3562 |
+ * specification does not specify how to fulfill a promise, but in Minified's implementation every Promise object has a function <code>fire()</code> |
|
| 3563 |
+ * that needs to be called when the promise result is ready. It requires two arguments. |
|
| 3564 |
+ * The first is a boolean, <var>true</var> for a successful operation and <var>false</var> for a failure. The second is an array or list containing the |
|
| 3565 |
+ * arguments to call the corresponding ##then() handler with. |
|
| 3566 |
+ * |
|
| 3567 |
+ * The following example is a function, similar to ##wait(), that returns a Promise which succeeds after the given amount |
|
| 3568 |
+ * of milliseconds has passed. |
|
| 3569 |
+ * It then fulfills the promise with the number of milliseconds as argument. |
|
| 3570 |
+ * |
|
| 3571 |
+ * <pre> |
|
| 3572 |
+ * function timeout(durationMs) {
|
|
| 3573 |
+ * var p = _.promise(); |
|
| 3574 |
+ * setTimeout(function() { p.fire(true, [durationMs]); }, durationMs);
|
|
| 3575 |
+ * return p; |
|
| 3576 |
+ * } |
|
| 3577 |
+ * </pre> |
|
| 3578 |
+ * Call it like this: |
|
| 3579 |
+ * <pre> |
|
| 3580 |
+ * timeout(1000).then(function(ms) { window.alert(ms+ ' milliseconds have passed.'); });
|
|
| 3581 |
+ * </pre> |
|
| 3582 |
+ * |
|
| 3583 |
+ * <h3>Limited Promises Implementation in Web module</h3> |
|
| 3584 |
+ * If you use only the Web module, instead of the full implementation, the promises implementation is not fully Promises/A+ compliant. |
|
| 3585 |
+ * One major difference is that it does not allow you create promises yourself. The only way to get a promise in the Web module |
|
| 3586 |
+ * is from functions like ##animate() and ##request(). The other difference is that the interoperability with other promises frameworks |
|
| 3587 |
+ * is limited, even though it should be good enough most of the time. |
|
| 3588 |
+ * |
|
| 3589 |
+ * There are two things you may run into when you use Web's simplified implementation with a complete implementation: |
|
| 3590 |
+ * <ol><li>The simplified implementation does not support recursive thenables. So when you register callbacks with ##then(), |
|
| 3591 |
+ * you can return a promise or a thenable, but only if that promise is not also returning a promise.</li> |
|
| 3592 |
+ * <li>Many corner cases required by the Promises/A+ specification are not handled. When interoperating using |
|
| 3593 |
+ * reasonable implementations, you may never run into this, but Promises/A+ has detailed rules for things like ##then() |
|
| 3594 |
+ * methods implemented as dynamic getter and returning a new value on each invocation or throwing exceptions. If you need |
|
| 3595 |
+ * a water-proof implementation, you need to use the complete implementation in Minified's full package.</li></ol> |
|
| 3596 |
+ */ |
|
| 3597 |
+/*$ |
|
| 3598 |
+ * @stop |
|
| 3599 |
+ */ |
|
| 3600 |
+ |
|
| 3601 |
+///#/snippet webDocs |
|
| 3602 |
+ |