|
1 /* |
|
2 YUI 3.10.3 (build 2fb5187) |
|
3 Copyright 2013 Yahoo! Inc. All rights reserved. |
|
4 Licensed under the BSD License. |
|
5 http://yuilibrary.com/license/ |
|
6 */ |
|
7 |
|
8 /** |
|
9 The YUI module contains the components required for building the YUI seed file. |
|
10 This includes the script loading mechanism, a simple queue, and the core |
|
11 utilities for the library. |
|
12 |
|
13 @module yui |
|
14 @main yui |
|
15 @submodule yui-base |
|
16 **/ |
|
17 |
|
18 /*jshint eqeqeq: false*/ |
|
19 if (typeof YUI != 'undefined') { |
|
20 YUI._YUI = YUI; |
|
21 } |
|
22 |
|
23 /** |
|
24 The YUI global namespace object. This is the constructor for all YUI instances. |
|
25 |
|
26 This is a self-instantiable factory function, meaning you don't need to precede |
|
27 it with the `new` operator. You can invoke it directly like this: |
|
28 |
|
29 YUI().use('*', function (Y) { |
|
30 // Y is a new YUI instance. |
|
31 }); |
|
32 |
|
33 But it also works like this: |
|
34 |
|
35 var Y = YUI(); |
|
36 |
|
37 The `YUI` constructor accepts an optional config object, like this: |
|
38 |
|
39 YUI({ |
|
40 debug: true, |
|
41 combine: false |
|
42 }).use('node', function (Y) { |
|
43 // Y.Node is ready to use. |
|
44 }); |
|
45 |
|
46 See the API docs for the <a href="config.html">Config</a> class for the complete |
|
47 list of supported configuration properties accepted by the YUI constuctor. |
|
48 |
|
49 If a global `YUI` object is already defined, the existing YUI object will not be |
|
50 overwritten, to ensure that defined namespaces are preserved. |
|
51 |
|
52 Each YUI instance has full custom event support, but only if the event system is |
|
53 available. |
|
54 |
|
55 @class YUI |
|
56 @uses EventTarget |
|
57 @constructor |
|
58 @global |
|
59 @param {Object} [config]* Zero or more optional configuration objects. Config |
|
60 values are stored in the `Y.config` property. See the |
|
61 <a href="config.html">Config</a> docs for the list of supported properties. |
|
62 **/ |
|
63 |
|
64 /*global YUI*/ |
|
65 /*global YUI_config*/ |
|
66 var YUI = function() { |
|
67 var i = 0, |
|
68 Y = this, |
|
69 args = arguments, |
|
70 l = args.length, |
|
71 instanceOf = function(o, type) { |
|
72 return (o && o.hasOwnProperty && (o instanceof type)); |
|
73 }, |
|
74 gconf = (typeof YUI_config !== 'undefined') && YUI_config; |
|
75 |
|
76 if (!(instanceOf(Y, YUI))) { |
|
77 Y = new YUI(); |
|
78 } else { |
|
79 // set up the core environment |
|
80 Y._init(); |
|
81 |
|
82 /** |
|
83 Master configuration that might span multiple contexts in a non- |
|
84 browser environment. It is applied first to all instances in all |
|
85 contexts. |
|
86 |
|
87 @example |
|
88 |
|
89 YUI.GlobalConfig = { |
|
90 filter: 'debug' |
|
91 }; |
|
92 |
|
93 YUI().use('node', function (Y) { |
|
94 // debug files used here |
|
95 }); |
|
96 |
|
97 YUI({ |
|
98 filter: 'min' |
|
99 }).use('node', function (Y) { |
|
100 // min files used here |
|
101 }); |
|
102 |
|
103 @property {Object} GlobalConfig |
|
104 @global |
|
105 @static |
|
106 **/ |
|
107 if (YUI.GlobalConfig) { |
|
108 Y.applyConfig(YUI.GlobalConfig); |
|
109 } |
|
110 |
|
111 /** |
|
112 Page-level config applied to all YUI instances created on the |
|
113 current page. This is applied after `YUI.GlobalConfig` and before |
|
114 any instance-level configuration. |
|
115 |
|
116 @example |
|
117 |
|
118 // Single global var to include before YUI seed file |
|
119 YUI_config = { |
|
120 filter: 'debug' |
|
121 }; |
|
122 |
|
123 YUI().use('node', function (Y) { |
|
124 // debug files used here |
|
125 }); |
|
126 |
|
127 YUI({ |
|
128 filter: 'min' |
|
129 }).use('node', function (Y) { |
|
130 // min files used here |
|
131 }); |
|
132 |
|
133 @property {Object} YUI_config |
|
134 @global |
|
135 **/ |
|
136 if (gconf) { |
|
137 Y.applyConfig(gconf); |
|
138 } |
|
139 |
|
140 // bind the specified additional modules for this instance |
|
141 if (!l) { |
|
142 Y._setup(); |
|
143 } |
|
144 } |
|
145 |
|
146 if (l) { |
|
147 // Each instance can accept one or more configuration objects. |
|
148 // These are applied after YUI.GlobalConfig and YUI_Config, |
|
149 // overriding values set in those config files if there is a |
|
150 // matching property. |
|
151 for (; i < l; i++) { |
|
152 Y.applyConfig(args[i]); |
|
153 } |
|
154 |
|
155 Y._setup(); |
|
156 } |
|
157 |
|
158 Y.instanceOf = instanceOf; |
|
159 |
|
160 return Y; |
|
161 }; |
|
162 |
|
163 (function() { |
|
164 |
|
165 var proto, prop, |
|
166 VERSION = '@VERSION@', |
|
167 PERIOD = '.', |
|
168 BASE = 'http://yui.yahooapis.com/', |
|
169 /* |
|
170 These CSS class names can't be generated by |
|
171 getClassName since it is not available at the |
|
172 time they are being used. |
|
173 */ |
|
174 DOC_LABEL = 'yui3-js-enabled', |
|
175 CSS_STAMP_EL = 'yui3-css-stamp', |
|
176 NOOP = function() {}, |
|
177 SLICE = Array.prototype.slice, |
|
178 APPLY_TO_AUTH = { 'io.xdrReady': 1, // the functions applyTo |
|
179 'io.xdrResponse': 1, // can call. this should |
|
180 'SWF.eventHandler': 1 }, // be done at build time |
|
181 hasWin = (typeof window != 'undefined'), |
|
182 win = (hasWin) ? window : null, |
|
183 doc = (hasWin) ? win.document : null, |
|
184 docEl = doc && doc.documentElement, |
|
185 docClass = docEl && docEl.className, |
|
186 instances = {}, |
|
187 time = new Date().getTime(), |
|
188 add = function(el, type, fn, capture) { |
|
189 if (el && el.addEventListener) { |
|
190 el.addEventListener(type, fn, capture); |
|
191 } else if (el && el.attachEvent) { |
|
192 el.attachEvent('on' + type, fn); |
|
193 } |
|
194 }, |
|
195 remove = function(el, type, fn, capture) { |
|
196 if (el && el.removeEventListener) { |
|
197 // this can throw an uncaught exception in FF |
|
198 try { |
|
199 el.removeEventListener(type, fn, capture); |
|
200 } catch (ex) {} |
|
201 } else if (el && el.detachEvent) { |
|
202 el.detachEvent('on' + type, fn); |
|
203 } |
|
204 }, |
|
205 handleLoad = function() { |
|
206 YUI.Env.windowLoaded = true; |
|
207 YUI.Env.DOMReady = true; |
|
208 if (hasWin) { |
|
209 remove(window, 'load', handleLoad); |
|
210 } |
|
211 }, |
|
212 getLoader = function(Y, o) { |
|
213 var loader = Y.Env._loader, |
|
214 lCore = [ 'loader-base' ], |
|
215 G_ENV = YUI.Env, |
|
216 mods = G_ENV.mods; |
|
217 |
|
218 if (loader) { |
|
219 //loader._config(Y.config); |
|
220 loader.ignoreRegistered = false; |
|
221 loader.onEnd = null; |
|
222 loader.data = null; |
|
223 loader.required = []; |
|
224 loader.loadType = null; |
|
225 } else { |
|
226 loader = new Y.Loader(Y.config); |
|
227 Y.Env._loader = loader; |
|
228 } |
|
229 if (mods && mods.loader) { |
|
230 lCore = [].concat(lCore, YUI.Env.loaderExtras); |
|
231 } |
|
232 YUI.Env.core = Y.Array.dedupe([].concat(YUI.Env.core, lCore)); |
|
233 |
|
234 return loader; |
|
235 }, |
|
236 |
|
237 clobber = function(r, s) { |
|
238 for (var i in s) { |
|
239 if (s.hasOwnProperty(i)) { |
|
240 r[i] = s[i]; |
|
241 } |
|
242 } |
|
243 }, |
|
244 |
|
245 ALREADY_DONE = { success: true }; |
|
246 |
|
247 // Stamp the documentElement (HTML) with a class of "yui-loaded" to |
|
248 // enable styles that need to key off of JS being enabled. |
|
249 if (docEl && docClass.indexOf(DOC_LABEL) == -1) { |
|
250 if (docClass) { |
|
251 docClass += ' '; |
|
252 } |
|
253 docClass += DOC_LABEL; |
|
254 docEl.className = docClass; |
|
255 } |
|
256 |
|
257 if (VERSION.indexOf('@') > -1) { |
|
258 VERSION = '3.5.0'; // dev time hack for cdn test |
|
259 } |
|
260 |
|
261 proto = { |
|
262 /** |
|
263 Applies a new configuration object to the config of this YUI instance. This |
|
264 will merge new group/module definitions, and will also update the loader |
|
265 cache if necessary. Updating `Y.config` directly will not update the cache. |
|
266 |
|
267 @method applyConfig |
|
268 @param {Object} o the configuration object. |
|
269 @since 3.2.0 |
|
270 **/ |
|
271 applyConfig: function(o) { |
|
272 |
|
273 o = o || NOOP; |
|
274 |
|
275 var attr, |
|
276 name, |
|
277 // detail, |
|
278 config = this.config, |
|
279 mods = config.modules, |
|
280 groups = config.groups, |
|
281 aliases = config.aliases, |
|
282 loader = this.Env._loader; |
|
283 |
|
284 for (name in o) { |
|
285 if (o.hasOwnProperty(name)) { |
|
286 attr = o[name]; |
|
287 if (mods && name == 'modules') { |
|
288 clobber(mods, attr); |
|
289 } else if (aliases && name == 'aliases') { |
|
290 clobber(aliases, attr); |
|
291 } else if (groups && name == 'groups') { |
|
292 clobber(groups, attr); |
|
293 } else if (name == 'win') { |
|
294 config[name] = (attr && attr.contentWindow) || attr; |
|
295 config.doc = config[name] ? config[name].document : null; |
|
296 } else if (name == '_yuid') { |
|
297 // preserve the guid |
|
298 } else { |
|
299 config[name] = attr; |
|
300 } |
|
301 } |
|
302 } |
|
303 |
|
304 if (loader) { |
|
305 loader._config(o); |
|
306 } |
|
307 |
|
308 }, |
|
309 |
|
310 /** |
|
311 Old way to apply a config to this instance (calls `applyConfig` under the |
|
312 hood). |
|
313 |
|
314 @private |
|
315 @method _config |
|
316 @param {Object} o The config to apply |
|
317 **/ |
|
318 _config: function(o) { |
|
319 this.applyConfig(o); |
|
320 }, |
|
321 |
|
322 /** |
|
323 Initializes this YUI instance. |
|
324 |
|
325 @private |
|
326 @method _init |
|
327 **/ |
|
328 _init: function() { |
|
329 var filter, el, |
|
330 Y = this, |
|
331 G_ENV = YUI.Env, |
|
332 Env = Y.Env, |
|
333 prop; |
|
334 |
|
335 /** |
|
336 The version number of this YUI instance. |
|
337 |
|
338 This value is typically updated by a script when a YUI release is built, |
|
339 so it may not reflect the correct version number when YUI is run from |
|
340 the development source tree. |
|
341 |
|
342 @property {String} version |
|
343 **/ |
|
344 Y.version = VERSION; |
|
345 |
|
346 if (!Env) { |
|
347 Y.Env = { |
|
348 core: ['get', 'features', 'intl-base', 'yui-log', 'yui-later'], |
|
349 loaderExtras: ['loader-rollup', 'loader-yui3'], |
|
350 mods: {}, // flat module map |
|
351 versions: {}, // version module map |
|
352 base: BASE, |
|
353 cdn: BASE + VERSION + '/build/', |
|
354 // bootstrapped: false, |
|
355 _idx: 0, |
|
356 _used: {}, |
|
357 _attached: {}, |
|
358 _missed: [], |
|
359 _yidx: 0, |
|
360 _uidx: 0, |
|
361 _guidp: 'y', |
|
362 _loaded: {}, |
|
363 // serviced: {}, |
|
364 // Regex in English: |
|
365 // I'll start at the \b(simpleyui). |
|
366 // 1. Look in the test string for "simpleyui" or "yui" or |
|
367 // "yui-base" or "yui-davglass" or "yui-foobar" that comes after a word break. That is, it |
|
368 // can't match "foyui" or "i_heart_simpleyui". This can be anywhere in the string. |
|
369 // 2. After #1 must come a forward slash followed by the string matched in #1, so |
|
370 // "yui-base/yui-base" or "simpleyui/simpleyui" or "yui-pants/yui-pants". |
|
371 // 3. The second occurence of the #1 token can optionally be followed by "-debug" or "-min", |
|
372 // so "yui/yui-min", "yui/yui-debug", "yui-base/yui-base-debug". NOT "yui/yui-tshirt". |
|
373 // 4. This is followed by ".js", so "yui/yui.js", "simpleyui/simpleyui-min.js" |
|
374 // 0. Going back to the beginning, now. If all that stuff in 1-4 comes after a "?" in the string, |
|
375 // then capture the junk between the LAST "&" and the string in 1-4. So |
|
376 // "blah?foo/yui/yui.js" will capture "foo/" and "blah?some/thing.js&3.3.0/build/yui-davglass/yui-davglass.js" |
|
377 // will capture "3.3.0/build/" |
|
378 // |
|
379 // Regex Exploded: |
|
380 // (?:\? Find a ? |
|
381 // (?:[^&]*&) followed by 0..n characters followed by an & |
|
382 // * in fact, find as many sets of characters followed by a & as you can |
|
383 // ([^&]*) capture the stuff after the last & in \1 |
|
384 // )? but it's ok if all this ?junk&more_junk stuff isn't even there |
|
385 // \b(simpleyui| after a word break find either the string "simpleyui" or |
|
386 // yui(?:-\w+)? the string "yui" optionally followed by a -, then more characters |
|
387 // ) and store the simpleyui or yui-* string in \2 |
|
388 // \/\2 then comes a / followed by the simpleyui or yui-* string in \2 |
|
389 // (?:-(min|debug))? optionally followed by "-min" or "-debug" |
|
390 // .js and ending in ".js" |
|
391 _BASE_RE: /(?:\?(?:[^&]*&)*([^&]*))?\b(simpleyui|yui(?:-\w+)?)\/\2(?:-(min|debug))?\.js/, |
|
392 parseBasePath: function(src, pattern) { |
|
393 var match = src.match(pattern), |
|
394 path, filter; |
|
395 |
|
396 if (match) { |
|
397 path = RegExp.leftContext || src.slice(0, src.indexOf(match[0])); |
|
398 |
|
399 // this is to set up the path to the loader. The file |
|
400 // filter for loader should match the yui include. |
|
401 filter = match[3]; |
|
402 |
|
403 // extract correct path for mixed combo urls |
|
404 // http://yuilibrary.com/projects/yui3/ticket/2528423 |
|
405 if (match[1]) { |
|
406 path += '?' + match[1]; |
|
407 } |
|
408 path = { |
|
409 filter: filter, |
|
410 path: path |
|
411 }; |
|
412 } |
|
413 return path; |
|
414 }, |
|
415 getBase: G_ENV && G_ENV.getBase || |
|
416 function(pattern) { |
|
417 var nodes = (doc && doc.getElementsByTagName('script')) || [], |
|
418 path = Env.cdn, parsed, |
|
419 i, len, src; |
|
420 |
|
421 for (i = 0, len = nodes.length; i < len; ++i) { |
|
422 src = nodes[i].src; |
|
423 if (src) { |
|
424 parsed = Y.Env.parseBasePath(src, pattern); |
|
425 if (parsed) { |
|
426 filter = parsed.filter; |
|
427 path = parsed.path; |
|
428 break; |
|
429 } |
|
430 } |
|
431 } |
|
432 |
|
433 // use CDN default |
|
434 return path; |
|
435 } |
|
436 |
|
437 }; |
|
438 |
|
439 Env = Y.Env; |
|
440 |
|
441 Env._loaded[VERSION] = {}; |
|
442 |
|
443 if (G_ENV && Y !== YUI) { |
|
444 Env._yidx = ++G_ENV._yidx; |
|
445 Env._guidp = ('yui_' + VERSION + '_' + |
|
446 Env._yidx + '_' + time).replace(/[^a-z0-9_]+/g, '_'); |
|
447 } else if (YUI._YUI) { |
|
448 |
|
449 G_ENV = YUI._YUI.Env; |
|
450 Env._yidx += G_ENV._yidx; |
|
451 Env._uidx += G_ENV._uidx; |
|
452 |
|
453 for (prop in G_ENV) { |
|
454 if (!(prop in Env)) { |
|
455 Env[prop] = G_ENV[prop]; |
|
456 } |
|
457 } |
|
458 |
|
459 delete YUI._YUI; |
|
460 } |
|
461 |
|
462 Y.id = Y.stamp(Y); |
|
463 instances[Y.id] = Y; |
|
464 |
|
465 } |
|
466 |
|
467 Y.constructor = YUI; |
|
468 |
|
469 // configuration defaults |
|
470 Y.config = Y.config || { |
|
471 bootstrap: true, |
|
472 cacheUse: true, |
|
473 debug: true, |
|
474 doc: doc, |
|
475 fetchCSS: true, |
|
476 throwFail: true, |
|
477 useBrowserConsole: true, |
|
478 useNativeES5: true, |
|
479 win: win, |
|
480 global: Function('return this')() |
|
481 }; |
|
482 |
|
483 //Register the CSS stamp element |
|
484 if (doc && !doc.getElementById(CSS_STAMP_EL)) { |
|
485 el = doc.createElement('div'); |
|
486 el.innerHTML = '<div id="' + CSS_STAMP_EL + '" style="position: absolute !important; visibility: hidden !important"></div>'; |
|
487 YUI.Env.cssStampEl = el.firstChild; |
|
488 if (doc.body) { |
|
489 doc.body.appendChild(YUI.Env.cssStampEl); |
|
490 } else { |
|
491 docEl.insertBefore(YUI.Env.cssStampEl, docEl.firstChild); |
|
492 } |
|
493 } else if (doc && doc.getElementById(CSS_STAMP_EL) && !YUI.Env.cssStampEl) { |
|
494 YUI.Env.cssStampEl = doc.getElementById(CSS_STAMP_EL); |
|
495 } |
|
496 |
|
497 Y.config.lang = Y.config.lang || 'en-US'; |
|
498 |
|
499 Y.config.base = YUI.config.base || Y.Env.getBase(Y.Env._BASE_RE); |
|
500 |
|
501 if (!filter || (!('mindebug').indexOf(filter))) { |
|
502 filter = 'min'; |
|
503 } |
|
504 filter = (filter) ? '-' + filter : filter; |
|
505 Y.config.loaderPath = YUI.config.loaderPath || 'loader/loader' + filter + '.js'; |
|
506 |
|
507 }, |
|
508 |
|
509 /** |
|
510 Finishes the instance setup. Attaches whatever YUI modules were defined |
|
511 at the time that this instance was created. |
|
512 |
|
513 @method _setup |
|
514 @private |
|
515 **/ |
|
516 _setup: function() { |
|
517 var i, Y = this, |
|
518 core = [], |
|
519 mods = YUI.Env.mods, |
|
520 extras = Y.config.core || [].concat(YUI.Env.core); //Clone it.. |
|
521 |
|
522 for (i = 0; i < extras.length; i++) { |
|
523 if (mods[extras[i]]) { |
|
524 core.push(extras[i]); |
|
525 } |
|
526 } |
|
527 |
|
528 Y._attach(['yui-base']); |
|
529 Y._attach(core); |
|
530 |
|
531 if (Y.Loader) { |
|
532 getLoader(Y); |
|
533 } |
|
534 |
|
535 }, |
|
536 |
|
537 /** |
|
538 Executes the named method on the specified YUI instance if that method is |
|
539 whitelisted. |
|
540 |
|
541 @method applyTo |
|
542 @param {String} id YUI instance id. |
|
543 @param {String} method Name of the method to execute. For example: |
|
544 'Object.keys'. |
|
545 @param {Array} args Arguments to apply to the method. |
|
546 @return {Mixed} Return value from the applied method, or `null` if the |
|
547 specified instance was not found or the method was not whitelisted. |
|
548 **/ |
|
549 applyTo: function(id, method, args) { |
|
550 if (!(method in APPLY_TO_AUTH)) { |
|
551 this.log(method + ': applyTo not allowed', 'warn', 'yui'); |
|
552 return null; |
|
553 } |
|
554 |
|
555 var instance = instances[id], nest, m, i; |
|
556 if (instance) { |
|
557 nest = method.split('.'); |
|
558 m = instance; |
|
559 for (i = 0; i < nest.length; i = i + 1) { |
|
560 m = m[nest[i]]; |
|
561 if (!m) { |
|
562 this.log('applyTo not found: ' + method, 'warn', 'yui'); |
|
563 } |
|
564 } |
|
565 return m && m.apply(instance, args); |
|
566 } |
|
567 |
|
568 return null; |
|
569 }, |
|
570 |
|
571 /** |
|
572 Registers a YUI module and makes it available for use in a `YUI().use()` call or |
|
573 as a dependency for other modules. |
|
574 |
|
575 The easiest way to create a first-class YUI module is to use |
|
576 <a href="http://yui.github.com/shifter/">Shifter</a>, the YUI component build |
|
577 tool. |
|
578 |
|
579 Shifter will automatically wrap your module code in a `YUI.add()` call along |
|
580 with any configuration info required for the module. |
|
581 |
|
582 @example |
|
583 |
|
584 YUI.add('davglass', function (Y) { |
|
585 Y.davglass = function () { |
|
586 }; |
|
587 }, '3.4.0', { |
|
588 requires: ['harley-davidson', 'mt-dew'] |
|
589 }); |
|
590 |
|
591 @method add |
|
592 @param {String} name Module name. |
|
593 @param {Function} fn Function containing module code. This function will be |
|
594 executed whenever the module is attached to a specific YUI instance. |
|
595 |
|
596 @param {YUI} fn.Y The YUI instance to which this module is attached. |
|
597 @param {String} fn.name Name of the module |
|
598 |
|
599 @param {String} version Module version number. This is currently used only for |
|
600 informational purposes, and is not used internally by YUI. |
|
601 |
|
602 @param {Object} [config] Module config. |
|
603 @param {Array} [config.requires] Array of other module names that must be |
|
604 attached before this module can be attached. |
|
605 @param {Array} [config.optional] Array of optional module names that should |
|
606 be attached before this module is attached if they've already been |
|
607 loaded. If the `loadOptional` YUI option is `true`, optional modules |
|
608 that have not yet been loaded will be loaded just as if they were hard |
|
609 requirements. |
|
610 @param {Array} [config.use] Array of module names that are included within |
|
611 or otherwise provided by this module, and which should be attached |
|
612 automatically when this module is attached. This makes it possible to |
|
613 create "virtual rollup" modules that simply attach a collection of other |
|
614 modules or submodules. |
|
615 |
|
616 @return {YUI} This YUI instance. |
|
617 **/ |
|
618 add: function(name, fn, version, details) { |
|
619 details = details || {}; |
|
620 var env = YUI.Env, |
|
621 mod = { |
|
622 name: name, |
|
623 fn: fn, |
|
624 version: version, |
|
625 details: details |
|
626 }, |
|
627 //Instance hash so we don't apply it to the same instance twice |
|
628 applied = {}, |
|
629 loader, inst, |
|
630 i, versions = env.versions; |
|
631 |
|
632 env.mods[name] = mod; |
|
633 versions[version] = versions[version] || {}; |
|
634 versions[version][name] = mod; |
|
635 |
|
636 for (i in instances) { |
|
637 if (instances.hasOwnProperty(i)) { |
|
638 inst = instances[i]; |
|
639 if (!applied[inst.id]) { |
|
640 applied[inst.id] = true; |
|
641 loader = inst.Env._loader; |
|
642 if (loader) { |
|
643 if (!loader.moduleInfo[name] || loader.moduleInfo[name].temp) { |
|
644 loader.addModule(details, name); |
|
645 } |
|
646 } |
|
647 } |
|
648 } |
|
649 } |
|
650 |
|
651 return this; |
|
652 }, |
|
653 |
|
654 /** |
|
655 Executes the callback function associated with each required module, |
|
656 attaching the module to this YUI instance. |
|
657 |
|
658 @method _attach |
|
659 @param {Array} r The array of modules to attach |
|
660 @param {Boolean} [moot=false] If `true`, don't throw a warning if the module |
|
661 is not attached. |
|
662 @private |
|
663 **/ |
|
664 _attach: function(r, moot) { |
|
665 var i, name, mod, details, req, use, after, |
|
666 mods = YUI.Env.mods, |
|
667 aliases = YUI.Env.aliases, |
|
668 Y = this, j, |
|
669 cache = YUI.Env._renderedMods, |
|
670 loader = Y.Env._loader, |
|
671 done = Y.Env._attached, |
|
672 len = r.length, loader, def, go, |
|
673 c = []; |
|
674 |
|
675 //Check for conditional modules (in a second+ instance) and add their requirements |
|
676 //TODO I hate this entire method, it needs to be fixed ASAP (3.5.0) ^davglass |
|
677 for (i = 0; i < len; i++) { |
|
678 name = r[i]; |
|
679 mod = mods[name]; |
|
680 c.push(name); |
|
681 if (loader && loader.conditions[name]) { |
|
682 for (j in loader.conditions[name]) { |
|
683 if (loader.conditions[name].hasOwnProperty(j)) { |
|
684 def = loader.conditions[name][j]; |
|
685 go = def && ((def.ua && Y.UA[def.ua]) || (def.test && def.test(Y))); |
|
686 if (go) { |
|
687 c.push(def.name); |
|
688 } |
|
689 } |
|
690 } |
|
691 } |
|
692 } |
|
693 r = c; |
|
694 len = r.length; |
|
695 |
|
696 for (i = 0; i < len; i++) { |
|
697 if (!done[r[i]]) { |
|
698 name = r[i]; |
|
699 mod = mods[name]; |
|
700 |
|
701 if (aliases && aliases[name] && !mod) { |
|
702 Y._attach(aliases[name]); |
|
703 continue; |
|
704 } |
|
705 if (!mod) { |
|
706 if (loader && loader.moduleInfo[name]) { |
|
707 mod = loader.moduleInfo[name]; |
|
708 moot = true; |
|
709 } |
|
710 |
|
711 |
|
712 //if (!loader || !loader.moduleInfo[name]) { |
|
713 //if ((!loader || !loader.moduleInfo[name]) && !moot) { |
|
714 if (!moot && name) { |
|
715 if ((name.indexOf('skin-') === -1) && (name.indexOf('css') === -1)) { |
|
716 Y.Env._missed.push(name); |
|
717 Y.Env._missed = Y.Array.dedupe(Y.Env._missed); |
|
718 Y.message('NOT loaded: ' + name, 'warn', 'yui'); |
|
719 } |
|
720 } |
|
721 } else { |
|
722 done[name] = true; |
|
723 //Don't like this, but in case a mod was asked for once, then we fetch it |
|
724 //We need to remove it from the missed list ^davglass |
|
725 for (j = 0; j < Y.Env._missed.length; j++) { |
|
726 if (Y.Env._missed[j] === name) { |
|
727 Y.message('Found: ' + name + ' (was reported as missing earlier)', 'warn', 'yui'); |
|
728 Y.Env._missed.splice(j, 1); |
|
729 } |
|
730 } |
|
731 /* |
|
732 If it's a temp module, we need to redo it's requirements if it's already loaded |
|
733 since it may have been loaded by another instance and it's dependencies might |
|
734 have been redefined inside the fetched file. |
|
735 */ |
|
736 if (loader && cache && cache[name] && cache[name].temp) { |
|
737 loader.getRequires(cache[name]); |
|
738 req = []; |
|
739 for (j in loader.moduleInfo[name].expanded_map) { |
|
740 if (loader.moduleInfo[name].expanded_map.hasOwnProperty(j)) { |
|
741 req.push(j); |
|
742 } |
|
743 } |
|
744 Y._attach(req); |
|
745 } |
|
746 |
|
747 details = mod.details; |
|
748 req = details.requires; |
|
749 use = details.use; |
|
750 after = details.after; |
|
751 //Force Intl load if there is a language (Loader logic) @todo fix this shit |
|
752 if (details.lang) { |
|
753 req = req || []; |
|
754 req.unshift('intl'); |
|
755 } |
|
756 |
|
757 if (req) { |
|
758 for (j = 0; j < req.length; j++) { |
|
759 if (!done[req[j]]) { |
|
760 if (!Y._attach(req)) { |
|
761 return false; |
|
762 } |
|
763 break; |
|
764 } |
|
765 } |
|
766 } |
|
767 |
|
768 if (after) { |
|
769 for (j = 0; j < after.length; j++) { |
|
770 if (!done[after[j]]) { |
|
771 if (!Y._attach(after, true)) { |
|
772 return false; |
|
773 } |
|
774 break; |
|
775 } |
|
776 } |
|
777 } |
|
778 |
|
779 if (mod.fn) { |
|
780 if (Y.config.throwFail) { |
|
781 mod.fn(Y, name); |
|
782 } else { |
|
783 try { |
|
784 mod.fn(Y, name); |
|
785 } catch (e) { |
|
786 Y.error('Attach error: ' + name, e, name); |
|
787 return false; |
|
788 } |
|
789 } |
|
790 } |
|
791 |
|
792 if (use) { |
|
793 for (j = 0; j < use.length; j++) { |
|
794 if (!done[use[j]]) { |
|
795 if (!Y._attach(use)) { |
|
796 return false; |
|
797 } |
|
798 break; |
|
799 } |
|
800 } |
|
801 } |
|
802 |
|
803 |
|
804 |
|
805 } |
|
806 } |
|
807 } |
|
808 |
|
809 return true; |
|
810 }, |
|
811 |
|
812 /** |
|
813 Delays the `use` callback until another event has taken place such as |
|
814 `window.onload`, `domready`, `contentready`, or `available`. |
|
815 |
|
816 @private |
|
817 @method _delayCallback |
|
818 @param {Function} cb The original `use` callback. |
|
819 @param {String|Object} until Either an event name ('load', 'domready', etc.) |
|
820 or an object containing event/args keys for contentready/available. |
|
821 @return {Function} |
|
822 **/ |
|
823 _delayCallback: function(cb, until) { |
|
824 |
|
825 var Y = this, |
|
826 mod = ['event-base']; |
|
827 |
|
828 until = (Y.Lang.isObject(until) ? until : { event: until }); |
|
829 |
|
830 if (until.event === 'load') { |
|
831 mod.push('event-synthetic'); |
|
832 } |
|
833 |
|
834 return function() { |
|
835 var args = arguments; |
|
836 Y._use(mod, function() { |
|
837 Y.on(until.event, function() { |
|
838 args[1].delayUntil = until.event; |
|
839 cb.apply(Y, args); |
|
840 }, until.args); |
|
841 }); |
|
842 }; |
|
843 }, |
|
844 |
|
845 /** |
|
846 Attaches one or more modules to this YUI instance. When this is executed, |
|
847 the requirements of the desired modules are analyzed, and one of several |
|
848 things can happen: |
|
849 |
|
850 |
|
851 * All required modules have already been loaded, and just need to be |
|
852 attached to this YUI instance. In this case, the `use()` callback will |
|
853 be executed synchronously after the modules are attached. |
|
854 |
|
855 * One or more modules have not yet been loaded, or the Get utility is not |
|
856 available, or the `bootstrap` config option is `false`. In this case, |
|
857 a warning is issued indicating that modules are missing, but all |
|
858 available modules will still be attached and the `use()` callback will |
|
859 be executed synchronously. |
|
860 |
|
861 * One or more modules are missing and the Loader is not available but the |
|
862 Get utility is, and `bootstrap` is not `false`. In this case, the Get |
|
863 utility will be used to load the Loader, and we will then proceed to |
|
864 the following state: |
|
865 |
|
866 * One or more modules are missing and the Loader is available. In this |
|
867 case, the Loader will be used to resolve the dependency tree for the |
|
868 missing modules and load them and their dependencies. When the Loader is |
|
869 finished loading modules, the `use()` callback will be executed |
|
870 asynchronously. |
|
871 |
|
872 @example |
|
873 |
|
874 // Loads and attaches dd and its dependencies. |
|
875 YUI().use('dd', function (Y) { |
|
876 // ... |
|
877 }); |
|
878 |
|
879 // Loads and attaches dd and node as well as all of their dependencies. |
|
880 YUI().use(['dd', 'node'], function (Y) { |
|
881 // ... |
|
882 }); |
|
883 |
|
884 // Attaches all modules that have already been loaded. |
|
885 YUI().use('*', function (Y) { |
|
886 // ... |
|
887 }); |
|
888 |
|
889 // Attaches a gallery module. |
|
890 YUI().use('gallery-yql', function (Y) { |
|
891 // ... |
|
892 }); |
|
893 |
|
894 // Attaches a YUI 2in3 module. |
|
895 YUI().use('yui2-datatable', function (Y) { |
|
896 // ... |
|
897 }); |
|
898 |
|
899 @method use |
|
900 @param {String|Array} modules* One or more module names to attach. |
|
901 @param {Function} [callback] Callback function to be executed once all |
|
902 specified modules and their dependencies have been attached. |
|
903 @param {YUI} callback.Y The YUI instance created for this sandbox. |
|
904 @param {Object} callback.status Object containing `success`, `msg` and |
|
905 `data` properties. |
|
906 @chainable |
|
907 **/ |
|
908 use: function() { |
|
909 var args = SLICE.call(arguments, 0), |
|
910 callback = args[args.length - 1], |
|
911 Y = this, |
|
912 i = 0, |
|
913 name, |
|
914 Env = Y.Env, |
|
915 provisioned = true; |
|
916 |
|
917 // The last argument supplied to use can be a load complete callback |
|
918 if (Y.Lang.isFunction(callback)) { |
|
919 args.pop(); |
|
920 if (Y.config.delayUntil) { |
|
921 callback = Y._delayCallback(callback, Y.config.delayUntil); |
|
922 } |
|
923 } else { |
|
924 callback = null; |
|
925 } |
|
926 if (Y.Lang.isArray(args[0])) { |
|
927 args = args[0]; |
|
928 } |
|
929 |
|
930 if (Y.config.cacheUse) { |
|
931 while ((name = args[i++])) { |
|
932 if (!Env._attached[name]) { |
|
933 provisioned = false; |
|
934 break; |
|
935 } |
|
936 } |
|
937 |
|
938 if (provisioned) { |
|
939 if (args.length) { |
|
940 } |
|
941 Y._notify(callback, ALREADY_DONE, args); |
|
942 return Y; |
|
943 } |
|
944 } |
|
945 |
|
946 if (Y._loading) { |
|
947 Y._useQueue = Y._useQueue || new Y.Queue(); |
|
948 Y._useQueue.add([args, callback]); |
|
949 } else { |
|
950 Y._use(args, function(Y, response) { |
|
951 Y._notify(callback, response, args); |
|
952 }); |
|
953 } |
|
954 |
|
955 return Y; |
|
956 }, |
|
957 |
|
958 /** |
|
959 Handles Loader notifications about attachment/load errors. |
|
960 |
|
961 @method _notify |
|
962 @param {Function} callback Callback to pass to `Y.config.loadErrorFn`. |
|
963 @param {Object} response Response returned from Loader. |
|
964 @param {Array} args Arguments passed from Loader. |
|
965 @private |
|
966 **/ |
|
967 _notify: function(callback, response, args) { |
|
968 if (!response.success && this.config.loadErrorFn) { |
|
969 this.config.loadErrorFn.call(this, this, callback, response, args); |
|
970 } else if (callback) { |
|
971 if (this.Env._missed && this.Env._missed.length) { |
|
972 response.msg = 'Missing modules: ' + this.Env._missed.join(); |
|
973 response.success = false; |
|
974 } |
|
975 if (this.config.throwFail) { |
|
976 callback(this, response); |
|
977 } else { |
|
978 try { |
|
979 callback(this, response); |
|
980 } catch (e) { |
|
981 this.error('use callback error', e, args); |
|
982 } |
|
983 } |
|
984 } |
|
985 }, |
|
986 |
|
987 /** |
|
988 Called from the `use` method queue to ensure that only one set of loading |
|
989 logic is performed at a time. |
|
990 |
|
991 @method _use |
|
992 @param {String} args* One or more modules to attach. |
|
993 @param {Function} [callback] Function to call once all required modules have |
|
994 been attached. |
|
995 @private |
|
996 **/ |
|
997 _use: function(args, callback) { |
|
998 |
|
999 if (!this.Array) { |
|
1000 this._attach(['yui-base']); |
|
1001 } |
|
1002 |
|
1003 var len, loader, handleBoot, |
|
1004 Y = this, |
|
1005 G_ENV = YUI.Env, |
|
1006 mods = G_ENV.mods, |
|
1007 Env = Y.Env, |
|
1008 used = Env._used, |
|
1009 aliases = G_ENV.aliases, |
|
1010 queue = G_ENV._loaderQueue, |
|
1011 firstArg = args[0], |
|
1012 YArray = Y.Array, |
|
1013 config = Y.config, |
|
1014 boot = config.bootstrap, |
|
1015 missing = [], |
|
1016 i, |
|
1017 r = [], |
|
1018 ret = true, |
|
1019 fetchCSS = config.fetchCSS, |
|
1020 process = function(names, skip) { |
|
1021 |
|
1022 var i = 0, a = [], name, len, m, req, use; |
|
1023 |
|
1024 if (!names.length) { |
|
1025 return; |
|
1026 } |
|
1027 |
|
1028 if (aliases) { |
|
1029 len = names.length; |
|
1030 for (i = 0; i < len; i++) { |
|
1031 if (aliases[names[i]] && !mods[names[i]]) { |
|
1032 a = [].concat(a, aliases[names[i]]); |
|
1033 } else { |
|
1034 a.push(names[i]); |
|
1035 } |
|
1036 } |
|
1037 names = a; |
|
1038 } |
|
1039 |
|
1040 len = names.length; |
|
1041 |
|
1042 for (i = 0; i < len; i++) { |
|
1043 name = names[i]; |
|
1044 if (!skip) { |
|
1045 r.push(name); |
|
1046 } |
|
1047 |
|
1048 // only attach a module once |
|
1049 if (used[name]) { |
|
1050 continue; |
|
1051 } |
|
1052 |
|
1053 m = mods[name]; |
|
1054 req = null; |
|
1055 use = null; |
|
1056 |
|
1057 if (m) { |
|
1058 used[name] = true; |
|
1059 req = m.details.requires; |
|
1060 use = m.details.use; |
|
1061 } else { |
|
1062 // CSS files don't register themselves, see if it has |
|
1063 // been loaded |
|
1064 if (!G_ENV._loaded[VERSION][name]) { |
|
1065 missing.push(name); |
|
1066 } else { |
|
1067 used[name] = true; // probably css |
|
1068 } |
|
1069 } |
|
1070 |
|
1071 // make sure requirements are attached |
|
1072 if (req && req.length) { |
|
1073 process(req); |
|
1074 } |
|
1075 |
|
1076 // make sure we grab the submodule dependencies too |
|
1077 if (use && use.length) { |
|
1078 process(use, 1); |
|
1079 } |
|
1080 } |
|
1081 |
|
1082 }, |
|
1083 |
|
1084 handleLoader = function(fromLoader) { |
|
1085 var response = fromLoader || { |
|
1086 success: true, |
|
1087 msg: 'not dynamic' |
|
1088 }, |
|
1089 redo, origMissing, |
|
1090 ret = true, |
|
1091 data = response.data; |
|
1092 |
|
1093 Y._loading = false; |
|
1094 |
|
1095 if (data) { |
|
1096 origMissing = missing; |
|
1097 missing = []; |
|
1098 r = []; |
|
1099 process(data); |
|
1100 redo = missing.length; |
|
1101 if (redo) { |
|
1102 if ([].concat(missing).sort().join() == |
|
1103 origMissing.sort().join()) { |
|
1104 redo = false; |
|
1105 } |
|
1106 } |
|
1107 } |
|
1108 |
|
1109 if (redo && data) { |
|
1110 Y._loading = true; |
|
1111 Y._use(missing, function() { |
|
1112 if (Y._attach(data)) { |
|
1113 Y._notify(callback, response, data); |
|
1114 } |
|
1115 }); |
|
1116 } else { |
|
1117 if (data) { |
|
1118 ret = Y._attach(data); |
|
1119 } |
|
1120 if (ret) { |
|
1121 Y._notify(callback, response, args); |
|
1122 } |
|
1123 } |
|
1124 |
|
1125 if (Y._useQueue && Y._useQueue.size() && !Y._loading) { |
|
1126 Y._use.apply(Y, Y._useQueue.next()); |
|
1127 } |
|
1128 |
|
1129 }; |
|
1130 |
|
1131 |
|
1132 // YUI().use('*'); // bind everything available |
|
1133 if (firstArg === '*') { |
|
1134 args = []; |
|
1135 for (i in mods) { |
|
1136 if (mods.hasOwnProperty(i)) { |
|
1137 args.push(i); |
|
1138 } |
|
1139 } |
|
1140 ret = Y._attach(args); |
|
1141 if (ret) { |
|
1142 handleLoader(); |
|
1143 } |
|
1144 return Y; |
|
1145 } |
|
1146 |
|
1147 if ((mods.loader || mods['loader-base']) && !Y.Loader) { |
|
1148 Y._attach(['loader' + ((!mods.loader) ? '-base' : '')]); |
|
1149 } |
|
1150 |
|
1151 |
|
1152 // use loader to expand dependencies and sort the |
|
1153 // requirements if it is available. |
|
1154 if (boot && Y.Loader && args.length) { |
|
1155 loader = getLoader(Y); |
|
1156 loader.require(args); |
|
1157 loader.ignoreRegistered = true; |
|
1158 loader._boot = true; |
|
1159 loader.calculate(null, (fetchCSS) ? null : 'js'); |
|
1160 args = loader.sorted; |
|
1161 loader._boot = false; |
|
1162 } |
|
1163 |
|
1164 process(args); |
|
1165 |
|
1166 len = missing.length; |
|
1167 |
|
1168 |
|
1169 if (len) { |
|
1170 missing = YArray.dedupe(missing); |
|
1171 len = missing.length; |
|
1172 } |
|
1173 |
|
1174 |
|
1175 // dynamic load |
|
1176 if (boot && len && Y.Loader) { |
|
1177 Y._loading = true; |
|
1178 loader = getLoader(Y); |
|
1179 loader.onEnd = handleLoader; |
|
1180 loader.context = Y; |
|
1181 loader.data = args; |
|
1182 loader.ignoreRegistered = false; |
|
1183 loader.require(missing); |
|
1184 loader.insert(null, (fetchCSS) ? null : 'js'); |
|
1185 |
|
1186 } else if (boot && len && Y.Get && !Env.bootstrapped) { |
|
1187 |
|
1188 Y._loading = true; |
|
1189 |
|
1190 handleBoot = function() { |
|
1191 Y._loading = false; |
|
1192 queue.running = false; |
|
1193 Env.bootstrapped = true; |
|
1194 G_ENV._bootstrapping = false; |
|
1195 if (Y._attach(['loader'])) { |
|
1196 Y._use(args, callback); |
|
1197 } |
|
1198 }; |
|
1199 |
|
1200 if (G_ENV._bootstrapping) { |
|
1201 queue.add(handleBoot); |
|
1202 } else { |
|
1203 G_ENV._bootstrapping = true; |
|
1204 Y.Get.script(config.base + config.loaderPath, { |
|
1205 onEnd: handleBoot |
|
1206 }); |
|
1207 } |
|
1208 |
|
1209 } else { |
|
1210 ret = Y._attach(args); |
|
1211 if (ret) { |
|
1212 handleLoader(); |
|
1213 } |
|
1214 } |
|
1215 |
|
1216 return Y; |
|
1217 }, |
|
1218 |
|
1219 |
|
1220 /** |
|
1221 Utility method for safely creating namespaces if they don't already exist. |
|
1222 May be called statically on the YUI global object or as a method on a YUI |
|
1223 instance. |
|
1224 |
|
1225 When called statically, a namespace will be created on the YUI global |
|
1226 object: |
|
1227 |
|
1228 // Create `YUI.your.namespace.here` as nested objects, preserving any |
|
1229 // objects that already exist instead of overwriting them. |
|
1230 YUI.namespace('your.namespace.here'); |
|
1231 |
|
1232 When called as a method on a YUI instance, a namespace will be created on |
|
1233 that instance: |
|
1234 |
|
1235 // Creates `Y.property.package`. |
|
1236 Y.namespace('property.package'); |
|
1237 |
|
1238 Dots in the input string cause `namespace` to create nested objects for each |
|
1239 token. If any part of the requested namespace already exists, the current |
|
1240 object will be left in place and will not be overwritten. This allows |
|
1241 multiple calls to `namespace` to preserve existing namespaced properties. |
|
1242 |
|
1243 If the first token in the namespace string is "YAHOO", that token is |
|
1244 discarded. This is legacy behavior for backwards compatibility with YUI 2. |
|
1245 |
|
1246 Be careful with namespace tokens. Reserved words may work in some browsers |
|
1247 and not others. For instance, the following will fail in some browsers |
|
1248 because the supported version of JavaScript reserves the word "long": |
|
1249 |
|
1250 Y.namespace('really.long.nested.namespace'); |
|
1251 |
|
1252 Note: If you pass multiple arguments to create multiple namespaces, only the |
|
1253 last one created is returned from this function. |
|
1254 |
|
1255 @method namespace |
|
1256 @param {String} namespace* One or more namespaces to create. |
|
1257 @return {Object} Reference to the last namespace object created. |
|
1258 **/ |
|
1259 namespace: function() { |
|
1260 var a = arguments, o, i = 0, j, d, arg; |
|
1261 |
|
1262 for (; i < a.length; i++) { |
|
1263 o = this; //Reset base object per argument or it will get reused from the last |
|
1264 arg = a[i]; |
|
1265 if (arg.indexOf(PERIOD) > -1) { //Skip this if no "." is present |
|
1266 d = arg.split(PERIOD); |
|
1267 for (j = (d[0] == 'YAHOO') ? 1 : 0; j < d.length; j++) { |
|
1268 o[d[j]] = o[d[j]] || {}; |
|
1269 o = o[d[j]]; |
|
1270 } |
|
1271 } else { |
|
1272 o[arg] = o[arg] || {}; |
|
1273 o = o[arg]; //Reset base object to the new object so it's returned |
|
1274 } |
|
1275 } |
|
1276 return o; |
|
1277 }, |
|
1278 |
|
1279 // this is replaced if the log module is included |
|
1280 log: NOOP, |
|
1281 message: NOOP, |
|
1282 // this is replaced if the dump module is included |
|
1283 dump: function (o) { return ''+o; }, |
|
1284 |
|
1285 /** |
|
1286 Reports an error. |
|
1287 |
|
1288 The reporting mechanism is controlled by the `throwFail` configuration |
|
1289 attribute. If `throwFail` is falsy, the message is logged. If `throwFail` is |
|
1290 truthy, a JS exception is thrown. |
|
1291 |
|
1292 If an `errorFn` is specified in the config it must return `true` to indicate |
|
1293 that the exception was handled and keep it from being thrown. |
|
1294 |
|
1295 @method error |
|
1296 @param {String} msg Error message. |
|
1297 @param {Error|String} [e] JavaScript error object or an error string. |
|
1298 @param {String} [src] Source of the error (such as the name of the module in |
|
1299 which the error occurred). |
|
1300 @chainable |
|
1301 **/ |
|
1302 error: function(msg, e, src) { |
|
1303 //TODO Add check for window.onerror here |
|
1304 |
|
1305 var Y = this, ret; |
|
1306 |
|
1307 if (Y.config.errorFn) { |
|
1308 ret = Y.config.errorFn.apply(Y, arguments); |
|
1309 } |
|
1310 |
|
1311 if (!ret) { |
|
1312 throw (e || new Error(msg)); |
|
1313 } else { |
|
1314 Y.message(msg, 'error', ''+src); // don't scrub this one |
|
1315 } |
|
1316 |
|
1317 return Y; |
|
1318 }, |
|
1319 |
|
1320 /** |
|
1321 Generates an id string that is unique among all YUI instances in this |
|
1322 execution context. |
|
1323 |
|
1324 @method guid |
|
1325 @param {String} [pre] Prefix. |
|
1326 @return {String} Unique id. |
|
1327 **/ |
|
1328 guid: function(pre) { |
|
1329 var id = this.Env._guidp + '_' + (++this.Env._uidx); |
|
1330 return (pre) ? (pre + id) : id; |
|
1331 }, |
|
1332 |
|
1333 /** |
|
1334 Returns a unique id associated with the given object and (if *readOnly* is |
|
1335 falsy) stamps the object with that id so it can be identified in the future. |
|
1336 |
|
1337 Stamping an object involves adding a `_yuid` property to it that contains |
|
1338 the object's id. One exception to this is that in Internet Explorer, DOM |
|
1339 nodes have a `uniqueID` property that contains a browser-generated unique |
|
1340 id, which will be used instead of a YUI-generated id when available. |
|
1341 |
|
1342 @method stamp |
|
1343 @param {Object} o Object to stamp. |
|
1344 @param {Boolean} readOnly If truthy and the given object has not already |
|
1345 been stamped, the object will not be modified and `null` will be |
|
1346 returned. |
|
1347 @return {String} Object's unique id, or `null` if *readOnly* was truthy and |
|
1348 the given object was not already stamped. |
|
1349 **/ |
|
1350 stamp: function(o, readOnly) { |
|
1351 var uid; |
|
1352 if (!o) { |
|
1353 return o; |
|
1354 } |
|
1355 |
|
1356 // IE generates its own unique ID for dom nodes |
|
1357 // The uniqueID property of a document node returns a new ID |
|
1358 if (o.uniqueID && o.nodeType && o.nodeType !== 9) { |
|
1359 uid = o.uniqueID; |
|
1360 } else { |
|
1361 uid = (typeof o === 'string') ? o : o._yuid; |
|
1362 } |
|
1363 |
|
1364 if (!uid) { |
|
1365 uid = this.guid(); |
|
1366 if (!readOnly) { |
|
1367 try { |
|
1368 o._yuid = uid; |
|
1369 } catch (e) { |
|
1370 uid = null; |
|
1371 } |
|
1372 } |
|
1373 } |
|
1374 return uid; |
|
1375 }, |
|
1376 |
|
1377 /** |
|
1378 Destroys this YUI instance. |
|
1379 |
|
1380 @method destroy |
|
1381 @since 3.3.0 |
|
1382 **/ |
|
1383 destroy: function() { |
|
1384 var Y = this; |
|
1385 if (Y.Event) { |
|
1386 Y.Event._unload(); |
|
1387 } |
|
1388 delete instances[Y.id]; |
|
1389 delete Y.Env; |
|
1390 delete Y.config; |
|
1391 } |
|
1392 |
|
1393 /** |
|
1394 Safe `instanceof` wrapper that works around a memory leak in IE when the |
|
1395 object being tested is `window` or `document`. |
|
1396 |
|
1397 Unless you are testing objects that may be `window` or `document`, you |
|
1398 should use the native `instanceof` operator instead of this method. |
|
1399 |
|
1400 @method instanceOf |
|
1401 @param {Object} o Object to check. |
|
1402 @param {Object} type Class to check against. |
|
1403 @since 3.3.0 |
|
1404 **/ |
|
1405 }; |
|
1406 |
|
1407 YUI.prototype = proto; |
|
1408 |
|
1409 // inheritance utilities are not available yet |
|
1410 for (prop in proto) { |
|
1411 if (proto.hasOwnProperty(prop)) { |
|
1412 YUI[prop] = proto[prop]; |
|
1413 } |
|
1414 } |
|
1415 |
|
1416 /** |
|
1417 Applies a configuration to all YUI instances in this execution context. |
|
1418 |
|
1419 The main use case for this method is in "mashups" where several third-party |
|
1420 scripts need to write to a global YUI config, but cannot share a single |
|
1421 centrally-managed config object. This way they can all call |
|
1422 `YUI.applyConfig({})` instead of overwriting the single global config. |
|
1423 |
|
1424 @example |
|
1425 |
|
1426 YUI.applyConfig({ |
|
1427 modules: { |
|
1428 davglass: { |
|
1429 fullpath: './davglass.js' |
|
1430 } |
|
1431 } |
|
1432 }); |
|
1433 |
|
1434 YUI.applyConfig({ |
|
1435 modules: { |
|
1436 foo: { |
|
1437 fullpath: './foo.js' |
|
1438 } |
|
1439 } |
|
1440 }); |
|
1441 |
|
1442 YUI().use('davglass', function (Y) { |
|
1443 // Module davglass will be available here. |
|
1444 }); |
|
1445 |
|
1446 @method applyConfig |
|
1447 @param {Object} o Configuration object to apply. |
|
1448 @static |
|
1449 @since 3.5.0 |
|
1450 **/ |
|
1451 YUI.applyConfig = function(o) { |
|
1452 if (!o) { |
|
1453 return; |
|
1454 } |
|
1455 //If there is a GlobalConfig, apply it first to set the defaults |
|
1456 if (YUI.GlobalConfig) { |
|
1457 this.prototype.applyConfig.call(this, YUI.GlobalConfig); |
|
1458 } |
|
1459 //Apply this config to it |
|
1460 this.prototype.applyConfig.call(this, o); |
|
1461 //Reset GlobalConfig to the combined config |
|
1462 YUI.GlobalConfig = this.config; |
|
1463 }; |
|
1464 |
|
1465 // set up the environment |
|
1466 YUI._init(); |
|
1467 |
|
1468 if (hasWin) { |
|
1469 // add a window load event at load time so we can capture |
|
1470 // the case where it fires before dynamic loading is |
|
1471 // complete. |
|
1472 add(window, 'load', handleLoad); |
|
1473 } else { |
|
1474 handleLoad(); |
|
1475 } |
|
1476 |
|
1477 YUI.Env.add = add; |
|
1478 YUI.Env.remove = remove; |
|
1479 |
|
1480 /*global exports*/ |
|
1481 // Support the CommonJS method for exporting our single global |
|
1482 if (typeof exports == 'object') { |
|
1483 exports.YUI = YUI; |
|
1484 /** |
|
1485 * Set a method to be called when `Get.script` is called in Node.js |
|
1486 * `Get` will open the file, then pass it's content and it's path |
|
1487 * to this method before attaching it. Commonly used for code coverage |
|
1488 * instrumentation. <strong>Calling this multiple times will only |
|
1489 * attach the last hook method</strong>. This method is only |
|
1490 * available in Node.js. |
|
1491 * @method setLoadHook |
|
1492 * @static |
|
1493 * @param {Function} fn The function to set |
|
1494 * @param {String} fn.data The content of the file |
|
1495 * @param {String} fn.path The file path of the file |
|
1496 */ |
|
1497 YUI.setLoadHook = function(fn) { |
|
1498 YUI._getLoadHook = fn; |
|
1499 }; |
|
1500 /** |
|
1501 * Load hook for `Y.Get.script` in Node.js, see `YUI.setLoadHook` |
|
1502 * @method _getLoadHook |
|
1503 * @private |
|
1504 * @param {String} data The content of the file |
|
1505 * @param {String} path The file path of the file |
|
1506 */ |
|
1507 YUI._getLoadHook = null; |
|
1508 } |
|
1509 |
|
1510 }()); |
|
1511 |
|
1512 |
|
1513 /** |
|
1514 Config object that contains all of the configuration options for |
|
1515 this `YUI` instance. |
|
1516 |
|
1517 This object is supplied by the implementer when instantiating YUI. Some |
|
1518 properties have default values if they are not supplied by the implementer. |
|
1519 |
|
1520 This object should not be updated directly because some values are cached. Use |
|
1521 `applyConfig()` to update the config object on a YUI instance that has already |
|
1522 been configured. |
|
1523 |
|
1524 @class config |
|
1525 @static |
|
1526 **/ |
|
1527 |
|
1528 /** |
|
1529 If `true` (the default), YUI will "bootstrap" the YUI Loader and module metadata |
|
1530 if they're needed to load additional dependencies and aren't already available. |
|
1531 |
|
1532 Setting this to `false` will prevent YUI from automatically loading the Loader |
|
1533 and module metadata, so you will need to manually ensure that they're available |
|
1534 or handle dependency resolution yourself. |
|
1535 |
|
1536 @property {Boolean} bootstrap |
|
1537 @default true |
|
1538 **/ |
|
1539 |
|
1540 /** |
|
1541 |
|
1542 @property {Object} aliases |
|
1543 **/ |
|
1544 |
|
1545 /** |
|
1546 A hash of module group definitions. |
|
1547 |
|
1548 For each group you can specify a list of modules and the base path and |
|
1549 combo spec to use when dynamically loading the modules. |
|
1550 |
|
1551 @example |
|
1552 |
|
1553 groups: { |
|
1554 yui2: { |
|
1555 // specify whether or not this group has a combo service |
|
1556 combine: true, |
|
1557 |
|
1558 // The comboSeperator to use with this group's combo handler |
|
1559 comboSep: ';', |
|
1560 |
|
1561 // The maxURLLength for this server |
|
1562 maxURLLength: 500, |
|
1563 |
|
1564 // the base path for non-combo paths |
|
1565 base: 'http://yui.yahooapis.com/2.8.0r4/build/', |
|
1566 |
|
1567 // the path to the combo service |
|
1568 comboBase: 'http://yui.yahooapis.com/combo?', |
|
1569 |
|
1570 // a fragment to prepend to the path attribute when |
|
1571 // when building combo urls |
|
1572 root: '2.8.0r4/build/', |
|
1573 |
|
1574 // the module definitions |
|
1575 modules: { |
|
1576 yui2_yde: { |
|
1577 path: "yahoo-dom-event/yahoo-dom-event.js" |
|
1578 }, |
|
1579 yui2_anim: { |
|
1580 path: "animation/animation.js", |
|
1581 requires: ['yui2_yde'] |
|
1582 } |
|
1583 } |
|
1584 } |
|
1585 } |
|
1586 |
|
1587 @property {Object} groups |
|
1588 **/ |
|
1589 |
|
1590 /** |
|
1591 Path to the Loader JS file, relative to the `base` path. |
|
1592 |
|
1593 This is used to dynamically bootstrap the Loader when it's needed and isn't yet |
|
1594 available. |
|
1595 |
|
1596 @property {String} loaderPath |
|
1597 @default "loader/loader-min.js" |
|
1598 **/ |
|
1599 |
|
1600 /** |
|
1601 If `true`, YUI will attempt to load CSS dependencies and skins. Set this to |
|
1602 `false` to prevent YUI from loading any CSS, or set it to the string `"force"` |
|
1603 to force CSS dependencies to be loaded even if their associated JS modules are |
|
1604 already loaded. |
|
1605 |
|
1606 @property {Boolean|String} fetchCSS |
|
1607 @default true |
|
1608 **/ |
|
1609 |
|
1610 /** |
|
1611 Default gallery version used to build gallery module urls. |
|
1612 |
|
1613 @property {String} gallery |
|
1614 @since 3.1.0 |
|
1615 **/ |
|
1616 |
|
1617 /** |
|
1618 Default YUI 2 version used to build YUI 2 module urls. |
|
1619 |
|
1620 This is used for intrinsic YUI 2 support via the 2in3 project. Also see the |
|
1621 `2in3` config for pulling different revisions of the wrapped YUI 2 modules. |
|
1622 |
|
1623 @property {String} yui2 |
|
1624 @default "2.9.0" |
|
1625 @since 3.1.0 |
|
1626 **/ |
|
1627 |
|
1628 /** |
|
1629 Revision number of YUI 2in3 modules that should be used when loading YUI 2in3. |
|
1630 |
|
1631 @property {String} 2in3 |
|
1632 @default "4" |
|
1633 @since 3.1.0 |
|
1634 **/ |
|
1635 |
|
1636 /** |
|
1637 Alternate console log function that should be used in environments without a |
|
1638 supported native console. This function is executed with the YUI instance as its |
|
1639 `this` object. |
|
1640 |
|
1641 @property {Function} logFn |
|
1642 @since 3.1.0 |
|
1643 **/ |
|
1644 |
|
1645 /** |
|
1646 The minimum log level to log messages for. Log levels are defined |
|
1647 incrementally. Messages greater than or equal to the level specified will |
|
1648 be shown. All others will be discarded. The order of log levels in |
|
1649 increasing priority is: |
|
1650 |
|
1651 debug |
|
1652 info |
|
1653 warn |
|
1654 error |
|
1655 |
|
1656 @property {String} logLevel |
|
1657 @default 'debug' |
|
1658 @since 3.10.0 |
|
1659 **/ |
|
1660 |
|
1661 /** |
|
1662 Callback to execute when `Y.error()` is called. It receives the error message |
|
1663 and a JavaScript error object if one was provided. |
|
1664 |
|
1665 This function is executed with the YUI instance as its `this` object. |
|
1666 |
|
1667 Returning `true` from this function will prevent an exception from being thrown. |
|
1668 |
|
1669 @property {Function} errorFn |
|
1670 @param {String} errorFn.msg Error message |
|
1671 @param {Object} [errorFn.err] Error object (if one was provided). |
|
1672 @since 3.2.0 |
|
1673 **/ |
|
1674 |
|
1675 /** |
|
1676 A callback to execute when Loader fails to load one or more resources. |
|
1677 |
|
1678 This could be because of a script load failure. It could also be because a |
|
1679 module fails to register itself when the `requireRegistration` config is `true`. |
|
1680 |
|
1681 If this function is defined, the `use()` callback will only be called when the |
|
1682 loader succeeds. Otherwise, `use()` will always executes unless there was a |
|
1683 JavaScript error when attaching a module. |
|
1684 |
|
1685 @property {Function} loadErrorFn |
|
1686 @since 3.3.0 |
|
1687 **/ |
|
1688 |
|
1689 /** |
|
1690 If `true`, Loader will expect all loaded scripts to be first-class YUI modules |
|
1691 that register themselves with the YUI global, and will trigger a failure if a |
|
1692 loaded script does not register a YUI module. |
|
1693 |
|
1694 @property {Boolean} requireRegistration |
|
1695 @default false |
|
1696 @since 3.3.0 |
|
1697 **/ |
|
1698 |
|
1699 /** |
|
1700 Cache serviced use() requests. |
|
1701 |
|
1702 @property {Boolean} cacheUse |
|
1703 @default true |
|
1704 @since 3.3.0 |
|
1705 @deprecated No longer used. |
|
1706 **/ |
|
1707 |
|
1708 /** |
|
1709 Whether or not YUI should use native ES5 functionality when available for |
|
1710 features like `Y.Array.each()`, `Y.Object()`, etc. |
|
1711 |
|
1712 When `false`, YUI will always use its own fallback implementations instead of |
|
1713 relying on ES5 functionality, even when ES5 functionality is available. |
|
1714 |
|
1715 @property {Boolean} useNativeES5 |
|
1716 @default true |
|
1717 @since 3.5.0 |
|
1718 **/ |
|
1719 |
|
1720 /** |
|
1721 * Leverage native JSON stringify if the browser has a native |
|
1722 * implementation. In general, this is a good idea. See the Known Issues |
|
1723 * section in the JSON user guide for caveats. The default value is true |
|
1724 * for browsers with native JSON support. |
|
1725 * |
|
1726 * @property useNativeJSONStringify |
|
1727 * @type Boolean |
|
1728 * @default true |
|
1729 * @since 3.8.0 |
|
1730 */ |
|
1731 |
|
1732 /** |
|
1733 * Leverage native JSON parse if the browser has a native implementation. |
|
1734 * In general, this is a good idea. See the Known Issues section in the |
|
1735 * JSON user guide for caveats. The default value is true for browsers with |
|
1736 * native JSON support. |
|
1737 * |
|
1738 * @property useNativeJSONParse |
|
1739 * @type Boolean |
|
1740 * @default true |
|
1741 * @since 3.8.0 |
|
1742 */ |
|
1743 |
|
1744 /** |
|
1745 Delay the `use` callback until a specific event has passed (`load`, `domready`, `contentready` or `available`) |
|
1746 |
|
1747 @property {Object|String} delayUntil |
|
1748 @since 3.6.0 |
|
1749 @example |
|
1750 |
|
1751 You can use `load` or `domready` strings by default: |
|
1752 |
|
1753 YUI({ |
|
1754 delayUntil: 'domready' |
|
1755 }, function (Y) { |
|
1756 // This will not execute until 'domeready' occurs. |
|
1757 }); |
|
1758 |
|
1759 Or you can delay until a node is available (with `available` or `contentready`): |
|
1760 |
|
1761 YUI({ |
|
1762 delayUntil: { |
|
1763 event: 'available', |
|
1764 args : '#foo' |
|
1765 } |
|
1766 }, function (Y) { |
|
1767 // This will not execute until a node matching the selector "#foo" is |
|
1768 // available in the DOM. |
|
1769 }); |
|
1770 |
|
1771 **/ |
|
1772 YUI.add('yui-base', function (Y, NAME) { |
|
1773 |
|
1774 /* |
|
1775 * YUI stub |
|
1776 * @module yui |
|
1777 * @submodule yui-base |
|
1778 */ |
|
1779 /** |
|
1780 * The YUI module contains the components required for building the YUI |
|
1781 * seed file. This includes the script loading mechanism, a simple queue, |
|
1782 * and the core utilities for the library. |
|
1783 * @module yui |
|
1784 * @submodule yui-base |
|
1785 */ |
|
1786 |
|
1787 /** |
|
1788 * Provides core language utilites and extensions used throughout YUI. |
|
1789 * |
|
1790 * @class Lang |
|
1791 * @static |
|
1792 */ |
|
1793 |
|
1794 var L = Y.Lang || (Y.Lang = {}), |
|
1795 |
|
1796 STRING_PROTO = String.prototype, |
|
1797 TOSTRING = Object.prototype.toString, |
|
1798 |
|
1799 TYPES = { |
|
1800 'undefined' : 'undefined', |
|
1801 'number' : 'number', |
|
1802 'boolean' : 'boolean', |
|
1803 'string' : 'string', |
|
1804 '[object Function]': 'function', |
|
1805 '[object RegExp]' : 'regexp', |
|
1806 '[object Array]' : 'array', |
|
1807 '[object Date]' : 'date', |
|
1808 '[object Error]' : 'error' |
|
1809 }, |
|
1810 |
|
1811 SUBREGEX = /\{\s*([^|}]+?)\s*(?:\|([^}]*))?\s*\}/g, |
|
1812 TRIMREGEX = /^\s+|\s+$/g, |
|
1813 NATIVE_FN_REGEX = /\{\s*\[(?:native code|function)\]\s*\}/i; |
|
1814 |
|
1815 // -- Protected Methods -------------------------------------------------------- |
|
1816 |
|
1817 /** |
|
1818 Returns `true` if the given function appears to be implemented in native code, |
|
1819 `false` otherwise. Will always return `false` -- even in ES5-capable browsers -- |
|
1820 if the `useNativeES5` YUI config option is set to `false`. |
|
1821 |
|
1822 This isn't guaranteed to be 100% accurate and won't work for anything other than |
|
1823 functions, but it can be useful for determining whether a function like |
|
1824 `Array.prototype.forEach` is native or a JS shim provided by another library. |
|
1825 |
|
1826 There's a great article by @kangax discussing certain flaws with this technique: |
|
1827 <http://perfectionkills.com/detecting-built-in-host-methods/> |
|
1828 |
|
1829 While his points are valid, it's still possible to benefit from this function |
|
1830 as long as it's used carefully and sparingly, and in such a way that false |
|
1831 negatives have minimal consequences. It's used internally to avoid using |
|
1832 potentially broken non-native ES5 shims that have been added to the page by |
|
1833 other libraries. |
|
1834 |
|
1835 @method _isNative |
|
1836 @param {Function} fn Function to test. |
|
1837 @return {Boolean} `true` if _fn_ appears to be native, `false` otherwise. |
|
1838 @static |
|
1839 @protected |
|
1840 @since 3.5.0 |
|
1841 **/ |
|
1842 L._isNative = function (fn) { |
|
1843 return !!(Y.config.useNativeES5 && fn && NATIVE_FN_REGEX.test(fn)); |
|
1844 }; |
|
1845 |
|
1846 // -- Public Methods ----------------------------------------------------------- |
|
1847 |
|
1848 /** |
|
1849 * Determines whether or not the provided item is an array. |
|
1850 * |
|
1851 * Returns `false` for array-like collections such as the function `arguments` |
|
1852 * collection or `HTMLElement` collections. Use `Y.Array.test()` if you want to |
|
1853 * test for an array-like collection. |
|
1854 * |
|
1855 * @method isArray |
|
1856 * @param o The object to test. |
|
1857 * @return {boolean} true if o is an array. |
|
1858 * @static |
|
1859 */ |
|
1860 L.isArray = L._isNative(Array.isArray) ? Array.isArray : function (o) { |
|
1861 return L.type(o) === 'array'; |
|
1862 }; |
|
1863 |
|
1864 /** |
|
1865 * Determines whether or not the provided item is a boolean. |
|
1866 * @method isBoolean |
|
1867 * @static |
|
1868 * @param o The object to test. |
|
1869 * @return {boolean} true if o is a boolean. |
|
1870 */ |
|
1871 L.isBoolean = function(o) { |
|
1872 return typeof o === 'boolean'; |
|
1873 }; |
|
1874 |
|
1875 /** |
|
1876 * Determines whether or not the supplied item is a date instance. |
|
1877 * @method isDate |
|
1878 * @static |
|
1879 * @param o The object to test. |
|
1880 * @return {boolean} true if o is a date. |
|
1881 */ |
|
1882 L.isDate = function(o) { |
|
1883 return L.type(o) === 'date' && o.toString() !== 'Invalid Date' && !isNaN(o); |
|
1884 }; |
|
1885 |
|
1886 /** |
|
1887 * <p> |
|
1888 * Determines whether or not the provided item is a function. |
|
1889 * Note: Internet Explorer thinks certain functions are objects: |
|
1890 * </p> |
|
1891 * |
|
1892 * <pre> |
|
1893 * var obj = document.createElement("object"); |
|
1894 * Y.Lang.isFunction(obj.getAttribute) // reports false in IE |
|
1895 * |
|
1896 * var input = document.createElement("input"); // append to body |
|
1897 * Y.Lang.isFunction(input.focus) // reports false in IE |
|
1898 * </pre> |
|
1899 * |
|
1900 * <p> |
|
1901 * You will have to implement additional tests if these functions |
|
1902 * matter to you. |
|
1903 * </p> |
|
1904 * |
|
1905 * @method isFunction |
|
1906 * @static |
|
1907 * @param o The object to test. |
|
1908 * @return {boolean} true if o is a function. |
|
1909 */ |
|
1910 L.isFunction = function(o) { |
|
1911 return L.type(o) === 'function'; |
|
1912 }; |
|
1913 |
|
1914 /** |
|
1915 * Determines whether or not the provided item is null. |
|
1916 * @method isNull |
|
1917 * @static |
|
1918 * @param o The object to test. |
|
1919 * @return {boolean} true if o is null. |
|
1920 */ |
|
1921 L.isNull = function(o) { |
|
1922 return o === null; |
|
1923 }; |
|
1924 |
|
1925 /** |
|
1926 * Determines whether or not the provided item is a legal number. |
|
1927 * @method isNumber |
|
1928 * @static |
|
1929 * @param o The object to test. |
|
1930 * @return {boolean} true if o is a number. |
|
1931 */ |
|
1932 L.isNumber = function(o) { |
|
1933 return typeof o === 'number' && isFinite(o); |
|
1934 }; |
|
1935 |
|
1936 /** |
|
1937 * Determines whether or not the provided item is of type object |
|
1938 * or function. Note that arrays are also objects, so |
|
1939 * <code>Y.Lang.isObject([]) === true</code>. |
|
1940 * @method isObject |
|
1941 * @static |
|
1942 * @param o The object to test. |
|
1943 * @param failfn {boolean} fail if the input is a function. |
|
1944 * @return {boolean} true if o is an object. |
|
1945 * @see isPlainObject |
|
1946 */ |
|
1947 L.isObject = function(o, failfn) { |
|
1948 var t = typeof o; |
|
1949 return (o && (t === 'object' || |
|
1950 (!failfn && (t === 'function' || L.isFunction(o))))) || false; |
|
1951 }; |
|
1952 |
|
1953 /** |
|
1954 * Determines whether or not the provided item is a string. |
|
1955 * @method isString |
|
1956 * @static |
|
1957 * @param o The object to test. |
|
1958 * @return {boolean} true if o is a string. |
|
1959 */ |
|
1960 L.isString = function(o) { |
|
1961 return typeof o === 'string'; |
|
1962 }; |
|
1963 |
|
1964 /** |
|
1965 * Determines whether or not the provided item is undefined. |
|
1966 * @method isUndefined |
|
1967 * @static |
|
1968 * @param o The object to test. |
|
1969 * @return {boolean} true if o is undefined. |
|
1970 */ |
|
1971 L.isUndefined = function(o) { |
|
1972 return typeof o === 'undefined'; |
|
1973 }; |
|
1974 |
|
1975 /** |
|
1976 * A convenience method for detecting a legitimate non-null value. |
|
1977 * Returns false for null/undefined/NaN, true for other values, |
|
1978 * including 0/false/'' |
|
1979 * @method isValue |
|
1980 * @static |
|
1981 * @param o The item to test. |
|
1982 * @return {boolean} true if it is not null/undefined/NaN || false. |
|
1983 */ |
|
1984 L.isValue = function(o) { |
|
1985 var t = L.type(o); |
|
1986 |
|
1987 switch (t) { |
|
1988 case 'number': |
|
1989 return isFinite(o); |
|
1990 |
|
1991 case 'null': // fallthru |
|
1992 case 'undefined': |
|
1993 return false; |
|
1994 |
|
1995 default: |
|
1996 return !!t; |
|
1997 } |
|
1998 }; |
|
1999 |
|
2000 /** |
|
2001 * Returns the current time in milliseconds. |
|
2002 * |
|
2003 * @method now |
|
2004 * @return {Number} Current time in milliseconds. |
|
2005 * @static |
|
2006 * @since 3.3.0 |
|
2007 */ |
|
2008 L.now = Date.now || function () { |
|
2009 return new Date().getTime(); |
|
2010 }; |
|
2011 |
|
2012 /** |
|
2013 * Lightweight version of <code>Y.substitute</code>. Uses the same template |
|
2014 * structure as <code>Y.substitute</code>, but doesn't support recursion, |
|
2015 * auto-object coersion, or formats. |
|
2016 * @method sub |
|
2017 * @param {string} s String to be modified. |
|
2018 * @param {object} o Object containing replacement values. |
|
2019 * @return {string} the substitute result. |
|
2020 * @static |
|
2021 * @since 3.2.0 |
|
2022 */ |
|
2023 L.sub = function(s, o) { |
|
2024 return s.replace ? s.replace(SUBREGEX, function (match, key) { |
|
2025 return L.isUndefined(o[key]) ? match : o[key]; |
|
2026 }) : s; |
|
2027 }; |
|
2028 |
|
2029 /** |
|
2030 * Returns a string without any leading or trailing whitespace. If |
|
2031 * the input is not a string, the input will be returned untouched. |
|
2032 * @method trim |
|
2033 * @static |
|
2034 * @param s {string} the string to trim. |
|
2035 * @return {string} the trimmed string. |
|
2036 */ |
|
2037 L.trim = STRING_PROTO.trim ? function(s) { |
|
2038 return s && s.trim ? s.trim() : s; |
|
2039 } : function (s) { |
|
2040 try { |
|
2041 return s.replace(TRIMREGEX, ''); |
|
2042 } catch (e) { |
|
2043 return s; |
|
2044 } |
|
2045 }; |
|
2046 |
|
2047 /** |
|
2048 * Returns a string without any leading whitespace. |
|
2049 * @method trimLeft |
|
2050 * @static |
|
2051 * @param s {string} the string to trim. |
|
2052 * @return {string} the trimmed string. |
|
2053 */ |
|
2054 L.trimLeft = STRING_PROTO.trimLeft ? function (s) { |
|
2055 return s.trimLeft(); |
|
2056 } : function (s) { |
|
2057 return s.replace(/^\s+/, ''); |
|
2058 }; |
|
2059 |
|
2060 /** |
|
2061 * Returns a string without any trailing whitespace. |
|
2062 * @method trimRight |
|
2063 * @static |
|
2064 * @param s {string} the string to trim. |
|
2065 * @return {string} the trimmed string. |
|
2066 */ |
|
2067 L.trimRight = STRING_PROTO.trimRight ? function (s) { |
|
2068 return s.trimRight(); |
|
2069 } : function (s) { |
|
2070 return s.replace(/\s+$/, ''); |
|
2071 }; |
|
2072 |
|
2073 /** |
|
2074 Returns one of the following strings, representing the type of the item passed |
|
2075 in: |
|
2076 |
|
2077 * "array" |
|
2078 * "boolean" |
|
2079 * "date" |
|
2080 * "error" |
|
2081 * "function" |
|
2082 * "null" |
|
2083 * "number" |
|
2084 * "object" |
|
2085 * "regexp" |
|
2086 * "string" |
|
2087 * "undefined" |
|
2088 |
|
2089 Known issues: |
|
2090 |
|
2091 * `typeof HTMLElementCollection` returns function in Safari, but |
|
2092 `Y.Lang.type()` reports "object", which could be a good thing -- |
|
2093 but it actually caused the logic in <code>Y.Lang.isObject</code> to fail. |
|
2094 |
|
2095 @method type |
|
2096 @param o the item to test. |
|
2097 @return {string} the detected type. |
|
2098 @static |
|
2099 **/ |
|
2100 L.type = function(o) { |
|
2101 return TYPES[typeof o] || TYPES[TOSTRING.call(o)] || (o ? 'object' : 'null'); |
|
2102 }; |
|
2103 /** |
|
2104 @module yui |
|
2105 @submodule yui-base |
|
2106 */ |
|
2107 |
|
2108 var Lang = Y.Lang, |
|
2109 Native = Array.prototype, |
|
2110 |
|
2111 hasOwn = Object.prototype.hasOwnProperty; |
|
2112 |
|
2113 /** |
|
2114 Provides utility methods for working with arrays. Additional array helpers can |
|
2115 be found in the `collection` and `array-extras` modules. |
|
2116 |
|
2117 `Y.Array(thing)` returns a native array created from _thing_. Depending on |
|
2118 _thing_'s type, one of the following will happen: |
|
2119 |
|
2120 * Arrays are returned unmodified unless a non-zero _startIndex_ is |
|
2121 specified. |
|
2122 * Array-like collections (see `Array.test()`) are converted to arrays. |
|
2123 * For everything else, a new array is created with _thing_ as the sole |
|
2124 item. |
|
2125 |
|
2126 Note: elements that are also collections, such as `<form>` and `<select>` |
|
2127 elements, are not automatically converted to arrays. To force a conversion, |
|
2128 pass `true` as the value of the _force_ parameter. |
|
2129 |
|
2130 @class Array |
|
2131 @constructor |
|
2132 @param {Any} thing The thing to arrayify. |
|
2133 @param {Number} [startIndex=0] If non-zero and _thing_ is an array or array-like |
|
2134 collection, a subset of items starting at the specified index will be |
|
2135 returned. |
|
2136 @param {Boolean} [force=false] If `true`, _thing_ will be treated as an |
|
2137 array-like collection no matter what. |
|
2138 @return {Array} A native array created from _thing_, according to the rules |
|
2139 described above. |
|
2140 **/ |
|
2141 function YArray(thing, startIndex, force) { |
|
2142 var len, result; |
|
2143 |
|
2144 /*jshint expr: true*/ |
|
2145 startIndex || (startIndex = 0); |
|
2146 |
|
2147 if (force || YArray.test(thing)) { |
|
2148 // IE throws when trying to slice HTMLElement collections. |
|
2149 try { |
|
2150 return Native.slice.call(thing, startIndex); |
|
2151 } catch (ex) { |
|
2152 result = []; |
|
2153 |
|
2154 for (len = thing.length; startIndex < len; ++startIndex) { |
|
2155 result.push(thing[startIndex]); |
|
2156 } |
|
2157 |
|
2158 return result; |
|
2159 } |
|
2160 } |
|
2161 |
|
2162 return [thing]; |
|
2163 } |
|
2164 |
|
2165 Y.Array = YArray; |
|
2166 |
|
2167 /** |
|
2168 Dedupes an array of strings, returning an array that's guaranteed to contain |
|
2169 only one copy of a given string. |
|
2170 |
|
2171 This method differs from `Array.unique()` in that it's optimized for use only |
|
2172 with strings, whereas `unique` may be used with other types (but is slower). |
|
2173 Using `dedupe()` with non-string values may result in unexpected behavior. |
|
2174 |
|
2175 @method dedupe |
|
2176 @param {String[]} array Array of strings to dedupe. |
|
2177 @return {Array} Deduped copy of _array_. |
|
2178 @static |
|
2179 @since 3.4.0 |
|
2180 **/ |
|
2181 YArray.dedupe = function (array) { |
|
2182 var hash = {}, |
|
2183 results = [], |
|
2184 i, item, len; |
|
2185 |
|
2186 for (i = 0, len = array.length; i < len; ++i) { |
|
2187 item = array[i]; |
|
2188 |
|
2189 if (!hasOwn.call(hash, item)) { |
|
2190 hash[item] = 1; |
|
2191 results.push(item); |
|
2192 } |
|
2193 } |
|
2194 |
|
2195 return results; |
|
2196 }; |
|
2197 |
|
2198 /** |
|
2199 Executes the supplied function on each item in the array. This method wraps |
|
2200 the native ES5 `Array.forEach()` method if available. |
|
2201 |
|
2202 @method each |
|
2203 @param {Array} array Array to iterate. |
|
2204 @param {Function} fn Function to execute on each item in the array. The function |
|
2205 will receive the following arguments: |
|
2206 @param {Any} fn.item Current array item. |
|
2207 @param {Number} fn.index Current array index. |
|
2208 @param {Array} fn.array Array being iterated. |
|
2209 @param {Object} [thisObj] `this` object to use when calling _fn_. |
|
2210 @return {YUI} The YUI instance. |
|
2211 @static |
|
2212 **/ |
|
2213 YArray.each = YArray.forEach = Lang._isNative(Native.forEach) ? function (array, fn, thisObj) { |
|
2214 Native.forEach.call(array || [], fn, thisObj || Y); |
|
2215 return Y; |
|
2216 } : function (array, fn, thisObj) { |
|
2217 for (var i = 0, len = (array && array.length) || 0; i < len; ++i) { |
|
2218 if (i in array) { |
|
2219 fn.call(thisObj || Y, array[i], i, array); |
|
2220 } |
|
2221 } |
|
2222 |
|
2223 return Y; |
|
2224 }; |
|
2225 |
|
2226 /** |
|
2227 Alias for `each()`. |
|
2228 |
|
2229 @method forEach |
|
2230 @static |
|
2231 **/ |
|
2232 |
|
2233 /** |
|
2234 Returns an object using the first array as keys and the second as values. If |
|
2235 the second array is not provided, or if it doesn't contain the same number of |
|
2236 values as the first array, then `true` will be used in place of the missing |
|
2237 values. |
|
2238 |
|
2239 @example |
|
2240 |
|
2241 Y.Array.hash(['a', 'b', 'c'], ['foo', 'bar']); |
|
2242 // => {a: 'foo', b: 'bar', c: true} |
|
2243 |
|
2244 @method hash |
|
2245 @param {String[]} keys Array of strings to use as keys. |
|
2246 @param {Array} [values] Array to use as values. |
|
2247 @return {Object} Hash using the first array as keys and the second as values. |
|
2248 @static |
|
2249 **/ |
|
2250 YArray.hash = function (keys, values) { |
|
2251 var hash = {}, |
|
2252 vlen = (values && values.length) || 0, |
|
2253 i, len; |
|
2254 |
|
2255 for (i = 0, len = keys.length; i < len; ++i) { |
|
2256 if (i in keys) { |
|
2257 hash[keys[i]] = vlen > i && i in values ? values[i] : true; |
|
2258 } |
|
2259 } |
|
2260 |
|
2261 return hash; |
|
2262 }; |
|
2263 |
|
2264 /** |
|
2265 Returns the index of the first item in the array that's equal (using a strict |
|
2266 equality check) to the specified _value_, or `-1` if the value isn't found. |
|
2267 |
|
2268 This method wraps the native ES5 `Array.indexOf()` method if available. |
|
2269 |
|
2270 @method indexOf |
|
2271 @param {Array} array Array to search. |
|
2272 @param {Any} value Value to search for. |
|
2273 @param {Number} [from=0] The index at which to begin the search. |
|
2274 @return {Number} Index of the item strictly equal to _value_, or `-1` if not |
|
2275 found. |
|
2276 @static |
|
2277 **/ |
|
2278 YArray.indexOf = Lang._isNative(Native.indexOf) ? function (array, value, from) { |
|
2279 return Native.indexOf.call(array, value, from); |
|
2280 } : function (array, value, from) { |
|
2281 // http://es5.github.com/#x15.4.4.14 |
|
2282 var len = array.length; |
|
2283 |
|
2284 from = +from || 0; |
|
2285 from = (from > 0 || -1) * Math.floor(Math.abs(from)); |
|
2286 |
|
2287 if (from < 0) { |
|
2288 from += len; |
|
2289 |
|
2290 if (from < 0) { |
|
2291 from = 0; |
|
2292 } |
|
2293 } |
|
2294 |
|
2295 for (; from < len; ++from) { |
|
2296 if (from in array && array[from] === value) { |
|
2297 return from; |
|
2298 } |
|
2299 } |
|
2300 |
|
2301 return -1; |
|
2302 }; |
|
2303 |
|
2304 /** |
|
2305 Numeric sort convenience function. |
|
2306 |
|
2307 The native `Array.prototype.sort()` function converts values to strings and |
|
2308 sorts them in lexicographic order, which is unsuitable for sorting numeric |
|
2309 values. Provide `Array.numericSort` as a custom sort function when you want |
|
2310 to sort values in numeric order. |
|
2311 |
|
2312 @example |
|
2313 |
|
2314 [42, 23, 8, 16, 4, 15].sort(Y.Array.numericSort); |
|
2315 // => [4, 8, 15, 16, 23, 42] |
|
2316 |
|
2317 @method numericSort |
|
2318 @param {Number} a First value to compare. |
|
2319 @param {Number} b Second value to compare. |
|
2320 @return {Number} Difference between _a_ and _b_. |
|
2321 @static |
|
2322 **/ |
|
2323 YArray.numericSort = function (a, b) { |
|
2324 return a - b; |
|
2325 }; |
|
2326 |
|
2327 /** |
|
2328 Executes the supplied function on each item in the array. Returning a truthy |
|
2329 value from the function will stop the processing of remaining items. |
|
2330 |
|
2331 @method some |
|
2332 @param {Array} array Array to iterate over. |
|
2333 @param {Function} fn Function to execute on each item. The function will receive |
|
2334 the following arguments: |
|
2335 @param {Any} fn.value Current array item. |
|
2336 @param {Number} fn.index Current array index. |
|
2337 @param {Array} fn.array Array being iterated over. |
|
2338 @param {Object} [thisObj] `this` object to use when calling _fn_. |
|
2339 @return {Boolean} `true` if the function returns a truthy value on any of the |
|
2340 items in the array; `false` otherwise. |
|
2341 @static |
|
2342 **/ |
|
2343 YArray.some = Lang._isNative(Native.some) ? function (array, fn, thisObj) { |
|
2344 return Native.some.call(array, fn, thisObj); |
|
2345 } : function (array, fn, thisObj) { |
|
2346 for (var i = 0, len = array.length; i < len; ++i) { |
|
2347 if (i in array && fn.call(thisObj, array[i], i, array)) { |
|
2348 return true; |
|
2349 } |
|
2350 } |
|
2351 |
|
2352 return false; |
|
2353 }; |
|
2354 |
|
2355 /** |
|
2356 Evaluates _obj_ to determine if it's an array, an array-like collection, or |
|
2357 something else. This is useful when working with the function `arguments` |
|
2358 collection and `HTMLElement` collections. |
|
2359 |
|
2360 Note: This implementation doesn't consider elements that are also |
|
2361 collections, such as `<form>` and `<select>`, to be array-like. |
|
2362 |
|
2363 @method test |
|
2364 @param {Object} obj Object to test. |
|
2365 @return {Number} A number indicating the results of the test: |
|
2366 |
|
2367 * 0: Neither an array nor an array-like collection. |
|
2368 * 1: Real array. |
|
2369 * 2: Array-like collection. |
|
2370 |
|
2371 @static |
|
2372 **/ |
|
2373 YArray.test = function (obj) { |
|
2374 var result = 0; |
|
2375 |
|
2376 if (Lang.isArray(obj)) { |
|
2377 result = 1; |
|
2378 } else if (Lang.isObject(obj)) { |
|
2379 try { |
|
2380 // indexed, but no tagName (element) or scrollTo/document (window. From DOM.isWindow test which we can't use here), |
|
2381 // or functions without apply/call (Safari |
|
2382 // HTMLElementCollection bug). |
|
2383 if ('length' in obj && !obj.tagName && !(obj.scrollTo && obj.document) && !obj.apply) { |
|
2384 result = 2; |
|
2385 } |
|
2386 } catch (ex) {} |
|
2387 } |
|
2388 |
|
2389 return result; |
|
2390 }; |
|
2391 /** |
|
2392 * The YUI module contains the components required for building the YUI |
|
2393 * seed file. This includes the script loading mechanism, a simple queue, |
|
2394 * and the core utilities for the library. |
|
2395 * @module yui |
|
2396 * @submodule yui-base |
|
2397 */ |
|
2398 |
|
2399 /** |
|
2400 * A simple FIFO queue. Items are added to the Queue with add(1..n items) and |
|
2401 * removed using next(). |
|
2402 * |
|
2403 * @class Queue |
|
2404 * @constructor |
|
2405 * @param {MIXED} item* 0..n items to seed the queue. |
|
2406 */ |
|
2407 function Queue() { |
|
2408 this._init(); |
|
2409 this.add.apply(this, arguments); |
|
2410 } |
|
2411 |
|
2412 Queue.prototype = { |
|
2413 /** |
|
2414 * Initialize the queue |
|
2415 * |
|
2416 * @method _init |
|
2417 * @protected |
|
2418 */ |
|
2419 _init: function() { |
|
2420 /** |
|
2421 * The collection of enqueued items |
|
2422 * |
|
2423 * @property _q |
|
2424 * @type Array |
|
2425 * @protected |
|
2426 */ |
|
2427 this._q = []; |
|
2428 }, |
|
2429 |
|
2430 /** |
|
2431 * Get the next item in the queue. FIFO support |
|
2432 * |
|
2433 * @method next |
|
2434 * @return {MIXED} the next item in the queue. |
|
2435 */ |
|
2436 next: function() { |
|
2437 return this._q.shift(); |
|
2438 }, |
|
2439 |
|
2440 /** |
|
2441 * Get the last in the queue. LIFO support. |
|
2442 * |
|
2443 * @method last |
|
2444 * @return {MIXED} the last item in the queue. |
|
2445 */ |
|
2446 last: function() { |
|
2447 return this._q.pop(); |
|
2448 }, |
|
2449 |
|
2450 /** |
|
2451 * Add 0..n items to the end of the queue. |
|
2452 * |
|
2453 * @method add |
|
2454 * @param {MIXED} item* 0..n items. |
|
2455 * @return {object} this queue. |
|
2456 */ |
|
2457 add: function() { |
|
2458 this._q.push.apply(this._q, arguments); |
|
2459 |
|
2460 return this; |
|
2461 }, |
|
2462 |
|
2463 /** |
|
2464 * Returns the current number of queued items. |
|
2465 * |
|
2466 * @method size |
|
2467 * @return {Number} The size. |
|
2468 */ |
|
2469 size: function() { |
|
2470 return this._q.length; |
|
2471 } |
|
2472 }; |
|
2473 |
|
2474 Y.Queue = Queue; |
|
2475 |
|
2476 YUI.Env._loaderQueue = YUI.Env._loaderQueue || new Queue(); |
|
2477 |
|
2478 /** |
|
2479 The YUI module contains the components required for building the YUI seed file. |
|
2480 This includes the script loading mechanism, a simple queue, and the core |
|
2481 utilities for the library. |
|
2482 |
|
2483 @module yui |
|
2484 @submodule yui-base |
|
2485 **/ |
|
2486 |
|
2487 var CACHED_DELIMITER = '__', |
|
2488 |
|
2489 hasOwn = Object.prototype.hasOwnProperty, |
|
2490 isObject = Y.Lang.isObject; |
|
2491 |
|
2492 /** |
|
2493 Returns a wrapper for a function which caches the return value of that function, |
|
2494 keyed off of the combined string representation of the argument values provided |
|
2495 when the wrapper is called. |
|
2496 |
|
2497 Calling this function again with the same arguments will return the cached value |
|
2498 rather than executing the wrapped function. |
|
2499 |
|
2500 Note that since the cache is keyed off of the string representation of arguments |
|
2501 passed to the wrapper function, arguments that aren't strings and don't provide |
|
2502 a meaningful `toString()` method may result in unexpected caching behavior. For |
|
2503 example, the objects `{}` and `{foo: 'bar'}` would both be converted to the |
|
2504 string `[object Object]` when used as a cache key. |
|
2505 |
|
2506 @method cached |
|
2507 @param {Function} source The function to memoize. |
|
2508 @param {Object} [cache={}] Object in which to store cached values. You may seed |
|
2509 this object with pre-existing cached values if desired. |
|
2510 @param {any} [refetch] If supplied, this value is compared with the cached value |
|
2511 using a `==` comparison. If the values are equal, the wrapped function is |
|
2512 executed again even though a cached value exists. |
|
2513 @return {Function} Wrapped function. |
|
2514 @for YUI |
|
2515 **/ |
|
2516 Y.cached = function (source, cache, refetch) { |
|
2517 /*jshint expr: true*/ |
|
2518 cache || (cache = {}); |
|
2519 |
|
2520 return function (arg) { |
|
2521 var key = arguments.length > 1 ? |
|
2522 Array.prototype.join.call(arguments, CACHED_DELIMITER) : |
|
2523 String(arg); |
|
2524 |
|
2525 /*jshint eqeqeq: false*/ |
|
2526 if (!(key in cache) || (refetch && cache[key] == refetch)) { |
|
2527 cache[key] = source.apply(source, arguments); |
|
2528 } |
|
2529 |
|
2530 return cache[key]; |
|
2531 }; |
|
2532 }; |
|
2533 |
|
2534 /** |
|
2535 Returns the `location` object from the window/frame in which this YUI instance |
|
2536 operates, or `undefined` when executing in a non-browser environment |
|
2537 (e.g. Node.js). |
|
2538 |
|
2539 It is _not_ recommended to hold references to the `window.location` object |
|
2540 outside of the scope of a function in which its properties are being accessed or |
|
2541 its methods are being called. This is because of a nasty bug/issue that exists |
|
2542 in both Safari and MobileSafari browsers: |
|
2543 [WebKit Bug 34679](https://bugs.webkit.org/show_bug.cgi?id=34679). |
|
2544 |
|
2545 @method getLocation |
|
2546 @return {location} The `location` object from the window/frame in which this YUI |
|
2547 instance operates. |
|
2548 @since 3.5.0 |
|
2549 **/ |
|
2550 Y.getLocation = function () { |
|
2551 // It is safer to look this up every time because yui-base is attached to a |
|
2552 // YUI instance before a user's config is applied; i.e. `Y.config.win` does |
|
2553 // not point the correct window object when this file is loaded. |
|
2554 var win = Y.config.win; |
|
2555 |
|
2556 // It is not safe to hold a reference to the `location` object outside the |
|
2557 // scope in which it is being used. The WebKit engine used in Safari and |
|
2558 // MobileSafari will "disconnect" the `location` object from the `window` |
|
2559 // when a page is restored from back/forward history cache. |
|
2560 return win && win.location; |
|
2561 }; |
|
2562 |
|
2563 /** |
|
2564 Returns a new object containing all of the properties of all the supplied |
|
2565 objects. The properties from later objects will overwrite those in earlier |
|
2566 objects. |
|
2567 |
|
2568 Passing in a single object will create a shallow copy of it. For a deep copy, |
|
2569 use `clone()`. |
|
2570 |
|
2571 @method merge |
|
2572 @param {Object} objects* One or more objects to merge. |
|
2573 @return {Object} A new merged object. |
|
2574 **/ |
|
2575 Y.merge = function () { |
|
2576 var i = 0, |
|
2577 len = arguments.length, |
|
2578 result = {}, |
|
2579 key, |
|
2580 obj; |
|
2581 |
|
2582 for (; i < len; ++i) { |
|
2583 obj = arguments[i]; |
|
2584 |
|
2585 for (key in obj) { |
|
2586 if (hasOwn.call(obj, key)) { |
|
2587 result[key] = obj[key]; |
|
2588 } |
|
2589 } |
|
2590 } |
|
2591 |
|
2592 return result; |
|
2593 }; |
|
2594 |
|
2595 /** |
|
2596 Mixes _supplier_'s properties into _receiver_. |
|
2597 |
|
2598 Properties on _receiver_ or _receiver_'s prototype will not be overwritten or |
|
2599 shadowed unless the _overwrite_ parameter is `true`, and will not be merged |
|
2600 unless the _merge_ parameter is `true`. |
|
2601 |
|
2602 In the default mode (0), only properties the supplier owns are copied (prototype |
|
2603 properties are not copied). The following copying modes are available: |
|
2604 |
|
2605 * `0`: _Default_. Object to object. |
|
2606 * `1`: Prototype to prototype. |
|
2607 * `2`: Prototype to prototype and object to object. |
|
2608 * `3`: Prototype to object. |
|
2609 * `4`: Object to prototype. |
|
2610 |
|
2611 @method mix |
|
2612 @param {Function|Object} receiver The object or function to receive the mixed |
|
2613 properties. |
|
2614 @param {Function|Object} supplier The object or function supplying the |
|
2615 properties to be mixed. |
|
2616 @param {Boolean} [overwrite=false] If `true`, properties that already exist |
|
2617 on the receiver will be overwritten with properties from the supplier. |
|
2618 @param {String[]} [whitelist] An array of property names to copy. If |
|
2619 specified, only the whitelisted properties will be copied, and all others |
|
2620 will be ignored. |
|
2621 @param {Number} [mode=0] Mix mode to use. See above for available modes. |
|
2622 @param {Boolean} [merge=false] If `true`, objects and arrays that already |
|
2623 exist on the receiver will have the corresponding object/array from the |
|
2624 supplier merged into them, rather than being skipped or overwritten. When |
|
2625 both _overwrite_ and _merge_ are `true`, _merge_ takes precedence. |
|
2626 @return {Function|Object|YUI} The receiver, or the YUI instance if the |
|
2627 specified receiver is falsy. |
|
2628 **/ |
|
2629 Y.mix = function(receiver, supplier, overwrite, whitelist, mode, merge) { |
|
2630 var alwaysOverwrite, exists, from, i, key, len, to; |
|
2631 |
|
2632 // If no supplier is given, we return the receiver. If no receiver is given, |
|
2633 // we return Y. Returning Y doesn't make much sense to me, but it's |
|
2634 // grandfathered in for backcompat reasons. |
|
2635 if (!receiver || !supplier) { |
|
2636 return receiver || Y; |
|
2637 } |
|
2638 |
|
2639 if (mode) { |
|
2640 // In mode 2 (prototype to prototype and object to object), we recurse |
|
2641 // once to do the proto to proto mix. The object to object mix will be |
|
2642 // handled later on. |
|
2643 if (mode === 2) { |
|
2644 Y.mix(receiver.prototype, supplier.prototype, overwrite, |
|
2645 whitelist, 0, merge); |
|
2646 } |
|
2647 |
|
2648 // Depending on which mode is specified, we may be copying from or to |
|
2649 // the prototypes of the supplier and receiver. |
|
2650 from = mode === 1 || mode === 3 ? supplier.prototype : supplier; |
|
2651 to = mode === 1 || mode === 4 ? receiver.prototype : receiver; |
|
2652 |
|
2653 // If either the supplier or receiver doesn't actually have a |
|
2654 // prototype property, then we could end up with an undefined `from` |
|
2655 // or `to`. If that happens, we abort and return the receiver. |
|
2656 if (!from || !to) { |
|
2657 return receiver; |
|
2658 } |
|
2659 } else { |
|
2660 from = supplier; |
|
2661 to = receiver; |
|
2662 } |
|
2663 |
|
2664 // If `overwrite` is truthy and `merge` is falsy, then we can skip a |
|
2665 // property existence check on each iteration and save some time. |
|
2666 alwaysOverwrite = overwrite && !merge; |
|
2667 |
|
2668 if (whitelist) { |
|
2669 for (i = 0, len = whitelist.length; i < len; ++i) { |
|
2670 key = whitelist[i]; |
|
2671 |
|
2672 // We call `Object.prototype.hasOwnProperty` instead of calling |
|
2673 // `hasOwnProperty` on the object itself, since the object's |
|
2674 // `hasOwnProperty` method may have been overridden or removed. |
|
2675 // Also, some native objects don't implement a `hasOwnProperty` |
|
2676 // method. |
|
2677 if (!hasOwn.call(from, key)) { |
|
2678 continue; |
|
2679 } |
|
2680 |
|
2681 // The `key in to` check here is (sadly) intentional for backwards |
|
2682 // compatibility reasons. It prevents undesired shadowing of |
|
2683 // prototype members on `to`. |
|
2684 exists = alwaysOverwrite ? false : key in to; |
|
2685 |
|
2686 if (merge && exists && isObject(to[key], true) |
|
2687 && isObject(from[key], true)) { |
|
2688 // If we're in merge mode, and the key is present on both |
|
2689 // objects, and the value on both objects is either an object or |
|
2690 // an array (but not a function), then we recurse to merge the |
|
2691 // `from` value into the `to` value instead of overwriting it. |
|
2692 // |
|
2693 // Note: It's intentional that the whitelist isn't passed to the |
|
2694 // recursive call here. This is legacy behavior that lots of |
|
2695 // code still depends on. |
|
2696 Y.mix(to[key], from[key], overwrite, null, 0, merge); |
|
2697 } else if (overwrite || !exists) { |
|
2698 // We're not in merge mode, so we'll only copy the `from` value |
|
2699 // to the `to` value if we're in overwrite mode or if the |
|
2700 // current key doesn't exist on the `to` object. |
|
2701 to[key] = from[key]; |
|
2702 } |
|
2703 } |
|
2704 } else { |
|
2705 for (key in from) { |
|
2706 // The code duplication here is for runtime performance reasons. |
|
2707 // Combining whitelist and non-whitelist operations into a single |
|
2708 // loop or breaking the shared logic out into a function both result |
|
2709 // in worse performance, and Y.mix is critical enough that the byte |
|
2710 // tradeoff is worth it. |
|
2711 if (!hasOwn.call(from, key)) { |
|
2712 continue; |
|
2713 } |
|
2714 |
|
2715 // The `key in to` check here is (sadly) intentional for backwards |
|
2716 // compatibility reasons. It prevents undesired shadowing of |
|
2717 // prototype members on `to`. |
|
2718 exists = alwaysOverwrite ? false : key in to; |
|
2719 |
|
2720 if (merge && exists && isObject(to[key], true) |
|
2721 && isObject(from[key], true)) { |
|
2722 Y.mix(to[key], from[key], overwrite, null, 0, merge); |
|
2723 } else if (overwrite || !exists) { |
|
2724 to[key] = from[key]; |
|
2725 } |
|
2726 } |
|
2727 |
|
2728 // If this is an IE browser with the JScript enumeration bug, force |
|
2729 // enumeration of the buggy properties by making a recursive call with |
|
2730 // the buggy properties as the whitelist. |
|
2731 if (Y.Object._hasEnumBug) { |
|
2732 Y.mix(to, from, overwrite, Y.Object._forceEnum, mode, merge); |
|
2733 } |
|
2734 } |
|
2735 |
|
2736 return receiver; |
|
2737 }; |
|
2738 /** |
|
2739 * The YUI module contains the components required for building the YUI |
|
2740 * seed file. This includes the script loading mechanism, a simple queue, |
|
2741 * and the core utilities for the library. |
|
2742 * @module yui |
|
2743 * @submodule yui-base |
|
2744 */ |
|
2745 |
|
2746 /** |
|
2747 * Adds utilities to the YUI instance for working with objects. |
|
2748 * |
|
2749 * @class Object |
|
2750 */ |
|
2751 |
|
2752 var Lang = Y.Lang, |
|
2753 hasOwn = Object.prototype.hasOwnProperty, |
|
2754 |
|
2755 UNDEFINED, // <-- Note the comma. We're still declaring vars. |
|
2756 |
|
2757 /** |
|
2758 * Returns a new object that uses _obj_ as its prototype. This method wraps the |
|
2759 * native ES5 `Object.create()` method if available, but doesn't currently |
|
2760 * pass through `Object.create()`'s second argument (properties) in order to |
|
2761 * ensure compatibility with older browsers. |
|
2762 * |
|
2763 * @method () |
|
2764 * @param {Object} obj Prototype object. |
|
2765 * @return {Object} New object using _obj_ as its prototype. |
|
2766 * @static |
|
2767 */ |
|
2768 O = Y.Object = Lang._isNative(Object.create) ? function (obj) { |
|
2769 // We currently wrap the native Object.create instead of simply aliasing it |
|
2770 // to ensure consistency with our fallback shim, which currently doesn't |
|
2771 // support Object.create()'s second argument (properties). Once we have a |
|
2772 // safe fallback for the properties arg, we can stop wrapping |
|
2773 // Object.create(). |
|
2774 return Object.create(obj); |
|
2775 } : (function () { |
|
2776 // Reusable constructor function for the Object.create() shim. |
|
2777 function F() {} |
|
2778 |
|
2779 // The actual shim. |
|
2780 return function (obj) { |
|
2781 F.prototype = obj; |
|
2782 return new F(); |
|
2783 }; |
|
2784 }()), |
|
2785 |
|
2786 /** |
|
2787 * Property names that IE doesn't enumerate in for..in loops, even when they |
|
2788 * should be enumerable. When `_hasEnumBug` is `true`, it's necessary to |
|
2789 * manually enumerate these properties. |
|
2790 * |
|
2791 * @property _forceEnum |
|
2792 * @type String[] |
|
2793 * @protected |
|
2794 * @static |
|
2795 */ |
|
2796 forceEnum = O._forceEnum = [ |
|
2797 'hasOwnProperty', |
|
2798 'isPrototypeOf', |
|
2799 'propertyIsEnumerable', |
|
2800 'toString', |
|
2801 'toLocaleString', |
|
2802 'valueOf' |
|
2803 ], |
|
2804 |
|
2805 /** |
|
2806 * `true` if this browser has the JScript enumeration bug that prevents |
|
2807 * enumeration of the properties named in the `_forceEnum` array, `false` |
|
2808 * otherwise. |
|
2809 * |
|
2810 * See: |
|
2811 * - <https://developer.mozilla.org/en/ECMAScript_DontEnum_attribute#JScript_DontEnum_Bug> |
|
2812 * - <http://whattheheadsaid.com/2010/10/a-safer-object-keys-compatibility-implementation> |
|
2813 * |
|
2814 * @property _hasEnumBug |
|
2815 * @type Boolean |
|
2816 * @protected |
|
2817 * @static |
|
2818 */ |
|
2819 hasEnumBug = O._hasEnumBug = !{valueOf: 0}.propertyIsEnumerable('valueOf'), |
|
2820 |
|
2821 /** |
|
2822 * `true` if this browser incorrectly considers the `prototype` property of |
|
2823 * functions to be enumerable. Currently known to affect Opera 11.50. |
|
2824 * |
|
2825 * @property _hasProtoEnumBug |
|
2826 * @type Boolean |
|
2827 * @protected |
|
2828 * @static |
|
2829 */ |
|
2830 hasProtoEnumBug = O._hasProtoEnumBug = (function () {}).propertyIsEnumerable('prototype'), |
|
2831 |
|
2832 /** |
|
2833 * Returns `true` if _key_ exists on _obj_, `false` if _key_ doesn't exist or |
|
2834 * exists only on _obj_'s prototype. This is essentially a safer version of |
|
2835 * `obj.hasOwnProperty()`. |
|
2836 * |
|
2837 * @method owns |
|
2838 * @param {Object} obj Object to test. |
|
2839 * @param {String} key Property name to look for. |
|
2840 * @return {Boolean} `true` if _key_ exists on _obj_, `false` otherwise. |
|
2841 * @static |
|
2842 */ |
|
2843 owns = O.owns = function (obj, key) { |
|
2844 return !!obj && hasOwn.call(obj, key); |
|
2845 }; // <-- End of var declarations. |
|
2846 |
|
2847 /** |
|
2848 * Alias for `owns()`. |
|
2849 * |
|
2850 * @method hasKey |
|
2851 * @param {Object} obj Object to test. |
|
2852 * @param {String} key Property name to look for. |
|
2853 * @return {Boolean} `true` if _key_ exists on _obj_, `false` otherwise. |
|
2854 * @static |
|
2855 */ |
|
2856 O.hasKey = owns; |
|
2857 |
|
2858 /** |
|
2859 * Returns an array containing the object's enumerable keys. Does not include |
|
2860 * prototype keys or non-enumerable keys. |
|
2861 * |
|
2862 * Note that keys are returned in enumeration order (that is, in the same order |
|
2863 * that they would be enumerated by a `for-in` loop), which may not be the same |
|
2864 * as the order in which they were defined. |
|
2865 * |
|
2866 * This method is an alias for the native ES5 `Object.keys()` method if |
|
2867 * available. |
|
2868 * |
|
2869 * @example |
|
2870 * |
|
2871 * Y.Object.keys({a: 'foo', b: 'bar', c: 'baz'}); |
|
2872 * // => ['a', 'b', 'c'] |
|
2873 * |
|
2874 * @method keys |
|
2875 * @param {Object} obj An object. |
|
2876 * @return {String[]} Array of keys. |
|
2877 * @static |
|
2878 */ |
|
2879 O.keys = Lang._isNative(Object.keys) ? Object.keys : function (obj) { |
|
2880 if (!Lang.isObject(obj)) { |
|
2881 throw new TypeError('Object.keys called on a non-object'); |
|
2882 } |
|
2883 |
|
2884 var keys = [], |
|
2885 i, key, len; |
|
2886 |
|
2887 if (hasProtoEnumBug && typeof obj === 'function') { |
|
2888 for (key in obj) { |
|
2889 if (owns(obj, key) && key !== 'prototype') { |
|
2890 keys.push(key); |
|
2891 } |
|
2892 } |
|
2893 } else { |
|
2894 for (key in obj) { |
|
2895 if (owns(obj, key)) { |
|
2896 keys.push(key); |
|
2897 } |
|
2898 } |
|
2899 } |
|
2900 |
|
2901 if (hasEnumBug) { |
|
2902 for (i = 0, len = forceEnum.length; i < len; ++i) { |
|
2903 key = forceEnum[i]; |
|
2904 |
|
2905 if (owns(obj, key)) { |
|
2906 keys.push(key); |
|
2907 } |
|
2908 } |
|
2909 } |
|
2910 |
|
2911 return keys; |
|
2912 }; |
|
2913 |
|
2914 /** |
|
2915 * Returns an array containing the values of the object's enumerable keys. |
|
2916 * |
|
2917 * Note that values are returned in enumeration order (that is, in the same |
|
2918 * order that they would be enumerated by a `for-in` loop), which may not be the |
|
2919 * same as the order in which they were defined. |
|
2920 * |
|
2921 * @example |
|
2922 * |
|
2923 * Y.Object.values({a: 'foo', b: 'bar', c: 'baz'}); |
|
2924 * // => ['foo', 'bar', 'baz'] |
|
2925 * |
|
2926 * @method values |
|
2927 * @param {Object} obj An object. |
|
2928 * @return {Array} Array of values. |
|
2929 * @static |
|
2930 */ |
|
2931 O.values = function (obj) { |
|
2932 var keys = O.keys(obj), |
|
2933 i = 0, |
|
2934 len = keys.length, |
|
2935 values = []; |
|
2936 |
|
2937 for (; i < len; ++i) { |
|
2938 values.push(obj[keys[i]]); |
|
2939 } |
|
2940 |
|
2941 return values; |
|
2942 }; |
|
2943 |
|
2944 /** |
|
2945 * Returns the number of enumerable keys owned by an object. |
|
2946 * |
|
2947 * @method size |
|
2948 * @param {Object} obj An object. |
|
2949 * @return {Number} The object's size. |
|
2950 * @static |
|
2951 */ |
|
2952 O.size = function (obj) { |
|
2953 try { |
|
2954 return O.keys(obj).length; |
|
2955 } catch (ex) { |
|
2956 return 0; // Legacy behavior for non-objects. |
|
2957 } |
|
2958 }; |
|
2959 |
|
2960 /** |
|
2961 * Returns `true` if the object owns an enumerable property with the specified |
|
2962 * value. |
|
2963 * |
|
2964 * @method hasValue |
|
2965 * @param {Object} obj An object. |
|
2966 * @param {any} value The value to search for. |
|
2967 * @return {Boolean} `true` if _obj_ contains _value_, `false` otherwise. |
|
2968 * @static |
|
2969 */ |
|
2970 O.hasValue = function (obj, value) { |
|
2971 return Y.Array.indexOf(O.values(obj), value) > -1; |
|
2972 }; |
|
2973 |
|
2974 /** |
|
2975 * Executes a function on each enumerable property in _obj_. The function |
|
2976 * receives the value, the key, and the object itself as parameters (in that |
|
2977 * order). |
|
2978 * |
|
2979 * By default, only properties owned by _obj_ are enumerated. To include |
|
2980 * prototype properties, set the _proto_ parameter to `true`. |
|
2981 * |
|
2982 * @method each |
|
2983 * @param {Object} obj Object to enumerate. |
|
2984 * @param {Function} fn Function to execute on each enumerable property. |
|
2985 * @param {mixed} fn.value Value of the current property. |
|
2986 * @param {String} fn.key Key of the current property. |
|
2987 * @param {Object} fn.obj Object being enumerated. |
|
2988 * @param {Object} [thisObj] `this` object to use when calling _fn_. |
|
2989 * @param {Boolean} [proto=false] Include prototype properties. |
|
2990 * @return {YUI} the YUI instance. |
|
2991 * @chainable |
|
2992 * @static |
|
2993 */ |
|
2994 O.each = function (obj, fn, thisObj, proto) { |
|
2995 var key; |
|
2996 |
|
2997 for (key in obj) { |
|
2998 if (proto || owns(obj, key)) { |
|
2999 fn.call(thisObj || Y, obj[key], key, obj); |
|
3000 } |
|
3001 } |
|
3002 |
|
3003 return Y; |
|
3004 }; |
|
3005 |
|
3006 /** |
|
3007 * Executes a function on each enumerable property in _obj_, but halts if the |
|
3008 * function returns a truthy value. The function receives the value, the key, |
|
3009 * and the object itself as paramters (in that order). |
|
3010 * |
|
3011 * By default, only properties owned by _obj_ are enumerated. To include |
|
3012 * prototype properties, set the _proto_ parameter to `true`. |
|
3013 * |
|
3014 * @method some |
|
3015 * @param {Object} obj Object to enumerate. |
|
3016 * @param {Function} fn Function to execute on each enumerable property. |
|
3017 * @param {mixed} fn.value Value of the current property. |
|
3018 * @param {String} fn.key Key of the current property. |
|
3019 * @param {Object} fn.obj Object being enumerated. |
|
3020 * @param {Object} [thisObj] `this` object to use when calling _fn_. |
|
3021 * @param {Boolean} [proto=false] Include prototype properties. |
|
3022 * @return {Boolean} `true` if any execution of _fn_ returns a truthy value, |
|
3023 * `false` otherwise. |
|
3024 * @static |
|
3025 */ |
|
3026 O.some = function (obj, fn, thisObj, proto) { |
|
3027 var key; |
|
3028 |
|
3029 for (key in obj) { |
|
3030 if (proto || owns(obj, key)) { |
|
3031 if (fn.call(thisObj || Y, obj[key], key, obj)) { |
|
3032 return true; |
|
3033 } |
|
3034 } |
|
3035 } |
|
3036 |
|
3037 return false; |
|
3038 }; |
|
3039 |
|
3040 /** |
|
3041 * Retrieves the sub value at the provided path, |
|
3042 * from the value object provided. |
|
3043 * |
|
3044 * @method getValue |
|
3045 * @static |
|
3046 * @param o The object from which to extract the property value. |
|
3047 * @param path {Array} A path array, specifying the object traversal path |
|
3048 * from which to obtain the sub value. |
|
3049 * @return {Any} The value stored in the path, undefined if not found, |
|
3050 * undefined if the source is not an object. Returns the source object |
|
3051 * if an empty path is provided. |
|
3052 */ |
|
3053 O.getValue = function(o, path) { |
|
3054 if (!Lang.isObject(o)) { |
|
3055 return UNDEFINED; |
|
3056 } |
|
3057 |
|
3058 var i, |
|
3059 p = Y.Array(path), |
|
3060 l = p.length; |
|
3061 |
|
3062 for (i = 0; o !== UNDEFINED && i < l; i++) { |
|
3063 o = o[p[i]]; |
|
3064 } |
|
3065 |
|
3066 return o; |
|
3067 }; |
|
3068 |
|
3069 /** |
|
3070 * Sets the sub-attribute value at the provided path on the |
|
3071 * value object. Returns the modified value object, or |
|
3072 * undefined if the path is invalid. |
|
3073 * |
|
3074 * @method setValue |
|
3075 * @static |
|
3076 * @param o The object on which to set the sub value. |
|
3077 * @param path {Array} A path array, specifying the object traversal path |
|
3078 * at which to set the sub value. |
|
3079 * @param val {Any} The new value for the sub-attribute. |
|
3080 * @return {Object} The modified object, with the new sub value set, or |
|
3081 * undefined, if the path was invalid. |
|
3082 */ |
|
3083 O.setValue = function(o, path, val) { |
|
3084 var i, |
|
3085 p = Y.Array(path), |
|
3086 leafIdx = p.length - 1, |
|
3087 ref = o; |
|
3088 |
|
3089 if (leafIdx >= 0) { |
|
3090 for (i = 0; ref !== UNDEFINED && i < leafIdx; i++) { |
|
3091 ref = ref[p[i]]; |
|
3092 } |
|
3093 |
|
3094 if (ref !== UNDEFINED) { |
|
3095 ref[p[i]] = val; |
|
3096 } else { |
|
3097 return UNDEFINED; |
|
3098 } |
|
3099 } |
|
3100 |
|
3101 return o; |
|
3102 }; |
|
3103 |
|
3104 /** |
|
3105 * Returns `true` if the object has no enumerable properties of its own. |
|
3106 * |
|
3107 * @method isEmpty |
|
3108 * @param {Object} obj An object. |
|
3109 * @return {Boolean} `true` if the object is empty. |
|
3110 * @static |
|
3111 * @since 3.2.0 |
|
3112 */ |
|
3113 O.isEmpty = function (obj) { |
|
3114 return !O.keys(Object(obj)).length; |
|
3115 }; |
|
3116 /** |
|
3117 * The YUI module contains the components required for building the YUI seed |
|
3118 * file. This includes the script loading mechanism, a simple queue, and the |
|
3119 * core utilities for the library. |
|
3120 * @module yui |
|
3121 * @submodule yui-base |
|
3122 */ |
|
3123 |
|
3124 /** |
|
3125 * YUI user agent detection. |
|
3126 * Do not fork for a browser if it can be avoided. Use feature detection when |
|
3127 * you can. Use the user agent as a last resort. For all fields listed |
|
3128 * as @type float, UA stores a version number for the browser engine, |
|
3129 * 0 otherwise. This value may or may not map to the version number of |
|
3130 * the browser using the engine. The value is presented as a float so |
|
3131 * that it can easily be used for boolean evaluation as well as for |
|
3132 * looking for a particular range of versions. Because of this, |
|
3133 * some of the granularity of the version info may be lost. The fields that |
|
3134 * are @type string default to null. The API docs list the values that |
|
3135 * these fields can have. |
|
3136 * @class UA |
|
3137 * @static |
|
3138 */ |
|
3139 |
|
3140 /** |
|
3141 * Static method on `YUI.Env` for parsing a UA string. Called at instantiation |
|
3142 * to populate `Y.UA`. |
|
3143 * |
|
3144 * @static |
|
3145 * @method parseUA |
|
3146 * @param {String} [subUA=navigator.userAgent] UA string to parse |
|
3147 * @return {Object} The Y.UA object |
|
3148 */ |
|
3149 YUI.Env.parseUA = function(subUA) { |
|
3150 |
|
3151 var numberify = function(s) { |
|
3152 var c = 0; |
|
3153 return parseFloat(s.replace(/\./g, function() { |
|
3154 return (c++ === 1) ? '' : '.'; |
|
3155 })); |
|
3156 }, |
|
3157 |
|
3158 win = Y.config.win, |
|
3159 |
|
3160 nav = win && win.navigator, |
|
3161 |
|
3162 o = { |
|
3163 |
|
3164 /** |
|
3165 * Internet Explorer version number or 0. Example: 6 |
|
3166 * @property ie |
|
3167 * @type float |
|
3168 * @static |
|
3169 */ |
|
3170 ie: 0, |
|
3171 |
|
3172 /** |
|
3173 * Opera version number or 0. Example: 9.2 |
|
3174 * @property opera |
|
3175 * @type float |
|
3176 * @static |
|
3177 */ |
|
3178 opera: 0, |
|
3179 |
|
3180 /** |
|
3181 * Gecko engine revision number. Will evaluate to 1 if Gecko |
|
3182 * is detected but the revision could not be found. Other browsers |
|
3183 * will be 0. Example: 1.8 |
|
3184 * <pre> |
|
3185 * Firefox 1.0.0.4: 1.7.8 <-- Reports 1.7 |
|
3186 * Firefox 1.5.0.9: 1.8.0.9 <-- 1.8 |
|
3187 * Firefox 2.0.0.3: 1.8.1.3 <-- 1.81 |
|
3188 * Firefox 3.0 <-- 1.9 |
|
3189 * Firefox 3.5 <-- 1.91 |
|
3190 * </pre> |
|
3191 * @property gecko |
|
3192 * @type float |
|
3193 * @static |
|
3194 */ |
|
3195 gecko: 0, |
|
3196 |
|
3197 /** |
|
3198 * AppleWebKit version. KHTML browsers that are not WebKit browsers |
|
3199 * will evaluate to 1, other browsers 0. Example: 418.9 |
|
3200 * <pre> |
|
3201 * Safari 1.3.2 (312.6): 312.8.1 <-- Reports 312.8 -- currently the |
|
3202 * latest available for Mac OSX 10.3. |
|
3203 * Safari 2.0.2: 416 <-- hasOwnProperty introduced |
|
3204 * Safari 2.0.4: 418 <-- preventDefault fixed |
|
3205 * Safari 2.0.4 (419.3): 418.9.1 <-- One version of Safari may run |
|
3206 * different versions of webkit |
|
3207 * Safari 2.0.4 (419.3): 419 <-- Tiger installations that have been |
|
3208 * updated, but not updated |
|
3209 * to the latest patch. |
|
3210 * Webkit 212 nightly: 522+ <-- Safari 3.0 precursor (with native |
|
3211 * SVG and many major issues fixed). |
|
3212 * Safari 3.0.4 (523.12) 523.12 <-- First Tiger release - automatic |
|
3213 * update from 2.x via the 10.4.11 OS patch. |
|
3214 * Webkit nightly 1/2008:525+ <-- Supports DOMContentLoaded event. |
|
3215 * yahoo.com user agent hack removed. |
|
3216 * </pre> |
|
3217 * http://en.wikipedia.org/wiki/Safari_version_history |
|
3218 * @property webkit |
|
3219 * @type float |
|
3220 * @static |
|
3221 */ |
|
3222 webkit: 0, |
|
3223 |
|
3224 /** |
|
3225 * Safari will be detected as webkit, but this property will also |
|
3226 * be populated with the Safari version number |
|
3227 * @property safari |
|
3228 * @type float |
|
3229 * @static |
|
3230 */ |
|
3231 safari: 0, |
|
3232 |
|
3233 /** |
|
3234 * Chrome will be detected as webkit, but this property will also |
|
3235 * be populated with the Chrome version number |
|
3236 * @property chrome |
|
3237 * @type float |
|
3238 * @static |
|
3239 */ |
|
3240 chrome: 0, |
|
3241 |
|
3242 /** |
|
3243 * The mobile property will be set to a string containing any relevant |
|
3244 * user agent information when a modern mobile browser is detected. |
|
3245 * Currently limited to Safari on the iPhone/iPod Touch, Nokia N-series |
|
3246 * devices with the WebKit-based browser, and Opera Mini. |
|
3247 * @property mobile |
|
3248 * @type string |
|
3249 * @default null |
|
3250 * @static |
|
3251 */ |
|
3252 mobile: null, |
|
3253 |
|
3254 /** |
|
3255 * Adobe AIR version number or 0. Only populated if webkit is detected. |
|
3256 * Example: 1.0 |
|
3257 * @property air |
|
3258 * @type float |
|
3259 */ |
|
3260 air: 0, |
|
3261 /** |
|
3262 * PhantomJS version number or 0. Only populated if webkit is detected. |
|
3263 * Example: 1.0 |
|
3264 * @property phantomjs |
|
3265 * @type float |
|
3266 */ |
|
3267 phantomjs: 0, |
|
3268 /** |
|
3269 * Detects Apple iPad's OS version |
|
3270 * @property ipad |
|
3271 * @type float |
|
3272 * @static |
|
3273 */ |
|
3274 ipad: 0, |
|
3275 /** |
|
3276 * Detects Apple iPhone's OS version |
|
3277 * @property iphone |
|
3278 * @type float |
|
3279 * @static |
|
3280 */ |
|
3281 iphone: 0, |
|
3282 /** |
|
3283 * Detects Apples iPod's OS version |
|
3284 * @property ipod |
|
3285 * @type float |
|
3286 * @static |
|
3287 */ |
|
3288 ipod: 0, |
|
3289 /** |
|
3290 * General truthy check for iPad, iPhone or iPod |
|
3291 * @property ios |
|
3292 * @type Boolean |
|
3293 * @default null |
|
3294 * @static |
|
3295 */ |
|
3296 ios: null, |
|
3297 /** |
|
3298 * Detects Googles Android OS version |
|
3299 * @property android |
|
3300 * @type float |
|
3301 * @static |
|
3302 */ |
|
3303 android: 0, |
|
3304 /** |
|
3305 * Detects Kindle Silk |
|
3306 * @property silk |
|
3307 * @type float |
|
3308 * @static |
|
3309 */ |
|
3310 silk: 0, |
|
3311 /** |
|
3312 * Detects Kindle Silk Acceleration |
|
3313 * @property accel |
|
3314 * @type Boolean |
|
3315 * @static |
|
3316 */ |
|
3317 accel: false, |
|
3318 /** |
|
3319 * Detects Palms WebOS version |
|
3320 * @property webos |
|
3321 * @type float |
|
3322 * @static |
|
3323 */ |
|
3324 webos: 0, |
|
3325 |
|
3326 /** |
|
3327 * Google Caja version number or 0. |
|
3328 * @property caja |
|
3329 * @type float |
|
3330 */ |
|
3331 caja: nav && nav.cajaVersion, |
|
3332 |
|
3333 /** |
|
3334 * Set to true if the page appears to be in SSL |
|
3335 * @property secure |
|
3336 * @type boolean |
|
3337 * @static |
|
3338 */ |
|
3339 secure: false, |
|
3340 |
|
3341 /** |
|
3342 * The operating system. Currently only detecting windows or macintosh |
|
3343 * @property os |
|
3344 * @type string |
|
3345 * @default null |
|
3346 * @static |
|
3347 */ |
|
3348 os: null, |
|
3349 |
|
3350 /** |
|
3351 * The Nodejs Version |
|
3352 * @property nodejs |
|
3353 * @type float |
|
3354 * @default 0 |
|
3355 * @static |
|
3356 */ |
|
3357 nodejs: 0, |
|
3358 /** |
|
3359 * Window8/IE10 Application host environment |
|
3360 * @property winjs |
|
3361 * @type Boolean |
|
3362 * @static |
|
3363 */ |
|
3364 winjs: !!((typeof Windows !== "undefined") && Windows.System), |
|
3365 /** |
|
3366 * Are touch/msPointer events available on this device |
|
3367 * @property touchEnabled |
|
3368 * @type Boolean |
|
3369 * @static |
|
3370 */ |
|
3371 touchEnabled: false |
|
3372 }, |
|
3373 |
|
3374 ua = subUA || nav && nav.userAgent, |
|
3375 |
|
3376 loc = win && win.location, |
|
3377 |
|
3378 href = loc && loc.href, |
|
3379 |
|
3380 m; |
|
3381 |
|
3382 /** |
|
3383 * The User Agent string that was parsed |
|
3384 * @property userAgent |
|
3385 * @type String |
|
3386 * @static |
|
3387 */ |
|
3388 o.userAgent = ua; |
|
3389 |
|
3390 |
|
3391 o.secure = href && (href.toLowerCase().indexOf('https') === 0); |
|
3392 |
|
3393 if (ua) { |
|
3394 |
|
3395 if ((/windows|win32/i).test(ua)) { |
|
3396 o.os = 'windows'; |
|
3397 } else if ((/macintosh|mac_powerpc/i).test(ua)) { |
|
3398 o.os = 'macintosh'; |
|
3399 } else if ((/android/i).test(ua)) { |
|
3400 o.os = 'android'; |
|
3401 } else if ((/symbos/i).test(ua)) { |
|
3402 o.os = 'symbos'; |
|
3403 } else if ((/linux/i).test(ua)) { |
|
3404 o.os = 'linux'; |
|
3405 } else if ((/rhino/i).test(ua)) { |
|
3406 o.os = 'rhino'; |
|
3407 } |
|
3408 |
|
3409 // Modern KHTML browsers should qualify as Safari X-Grade |
|
3410 if ((/KHTML/).test(ua)) { |
|
3411 o.webkit = 1; |
|
3412 } |
|
3413 if ((/IEMobile|XBLWP7/).test(ua)) { |
|
3414 o.mobile = 'windows'; |
|
3415 } |
|
3416 if ((/Fennec/).test(ua)) { |
|
3417 o.mobile = 'gecko'; |
|
3418 } |
|
3419 // Modern WebKit browsers are at least X-Grade |
|
3420 m = ua.match(/AppleWebKit\/([^\s]*)/); |
|
3421 if (m && m[1]) { |
|
3422 o.webkit = numberify(m[1]); |
|
3423 o.safari = o.webkit; |
|
3424 |
|
3425 if (/PhantomJS/.test(ua)) { |
|
3426 m = ua.match(/PhantomJS\/([^\s]*)/); |
|
3427 if (m && m[1]) { |
|
3428 o.phantomjs = numberify(m[1]); |
|
3429 } |
|
3430 } |
|
3431 |
|
3432 // Mobile browser check |
|
3433 if (/ Mobile\//.test(ua) || (/iPad|iPod|iPhone/).test(ua)) { |
|
3434 o.mobile = 'Apple'; // iPhone or iPod Touch |
|
3435 |
|
3436 m = ua.match(/OS ([^\s]*)/); |
|
3437 if (m && m[1]) { |
|
3438 m = numberify(m[1].replace('_', '.')); |
|
3439 } |
|
3440 o.ios = m; |
|
3441 o.os = 'ios'; |
|
3442 o.ipad = o.ipod = o.iphone = 0; |
|
3443 |
|
3444 m = ua.match(/iPad|iPod|iPhone/); |
|
3445 if (m && m[0]) { |
|
3446 o[m[0].toLowerCase()] = o.ios; |
|
3447 } |
|
3448 } else { |
|
3449 m = ua.match(/NokiaN[^\/]*|webOS\/\d\.\d/); |
|
3450 if (m) { |
|
3451 // Nokia N-series, webOS, ex: NokiaN95 |
|
3452 o.mobile = m[0]; |
|
3453 } |
|
3454 if (/webOS/.test(ua)) { |
|
3455 o.mobile = 'WebOS'; |
|
3456 m = ua.match(/webOS\/([^\s]*);/); |
|
3457 if (m && m[1]) { |
|
3458 o.webos = numberify(m[1]); |
|
3459 } |
|
3460 } |
|
3461 if (/ Android/.test(ua)) { |
|
3462 if (/Mobile/.test(ua)) { |
|
3463 o.mobile = 'Android'; |
|
3464 } |
|
3465 m = ua.match(/Android ([^\s]*);/); |
|
3466 if (m && m[1]) { |
|
3467 o.android = numberify(m[1]); |
|
3468 } |
|
3469 |
|
3470 } |
|
3471 if (/Silk/.test(ua)) { |
|
3472 m = ua.match(/Silk\/([^\s]*)\)/); |
|
3473 if (m && m[1]) { |
|
3474 o.silk = numberify(m[1]); |
|
3475 } |
|
3476 if (!o.android) { |
|
3477 o.android = 2.34; //Hack for desktop mode in Kindle |
|
3478 o.os = 'Android'; |
|
3479 } |
|
3480 if (/Accelerated=true/.test(ua)) { |
|
3481 o.accel = true; |
|
3482 } |
|
3483 } |
|
3484 } |
|
3485 |
|
3486 m = ua.match(/(Chrome|CrMo|CriOS)\/([^\s]*)/); |
|
3487 if (m && m[1] && m[2]) { |
|
3488 o.chrome = numberify(m[2]); // Chrome |
|
3489 o.safari = 0; //Reset safari back to 0 |
|
3490 if (m[1] === 'CrMo') { |
|
3491 o.mobile = 'chrome'; |
|
3492 } |
|
3493 } else { |
|
3494 m = ua.match(/AdobeAIR\/([^\s]*)/); |
|
3495 if (m) { |
|
3496 o.air = m[0]; // Adobe AIR 1.0 or better |
|
3497 } |
|
3498 } |
|
3499 } |
|
3500 |
|
3501 if (!o.webkit) { // not webkit |
|
3502 // @todo check Opera/8.01 (J2ME/MIDP; Opera Mini/2.0.4509/1316; fi; U; ssr) |
|
3503 if (/Opera/.test(ua)) { |
|
3504 m = ua.match(/Opera[\s\/]([^\s]*)/); |
|
3505 if (m && m[1]) { |
|
3506 o.opera = numberify(m[1]); |
|
3507 } |
|
3508 m = ua.match(/Version\/([^\s]*)/); |
|
3509 if (m && m[1]) { |
|
3510 o.opera = numberify(m[1]); // opera 10+ |
|
3511 } |
|
3512 |
|
3513 if (/Opera Mobi/.test(ua)) { |
|
3514 o.mobile = 'opera'; |
|
3515 m = ua.replace('Opera Mobi', '').match(/Opera ([^\s]*)/); |
|
3516 if (m && m[1]) { |
|
3517 o.opera = numberify(m[1]); |
|
3518 } |
|
3519 } |
|
3520 m = ua.match(/Opera Mini[^;]*/); |
|
3521 |
|
3522 if (m) { |
|
3523 o.mobile = m[0]; // ex: Opera Mini/2.0.4509/1316 |
|
3524 } |
|
3525 } else { // not opera or webkit |
|
3526 m = ua.match(/MSIE\s([^;]*)/); |
|
3527 if (m && m[1]) { |
|
3528 o.ie = numberify(m[1]); |
|
3529 } else { // not opera, webkit, or ie |
|
3530 m = ua.match(/Gecko\/([^\s]*)/); |
|
3531 if (m) { |
|
3532 o.gecko = 1; // Gecko detected, look for revision |
|
3533 m = ua.match(/rv:([^\s\)]*)/); |
|
3534 if (m && m[1]) { |
|
3535 o.gecko = numberify(m[1]); |
|
3536 if (/Mobile|Tablet/.test(ua)) { |
|
3537 o.mobile = "ffos"; |
|
3538 } |
|
3539 } |
|
3540 } |
|
3541 } |
|
3542 } |
|
3543 } |
|
3544 } |
|
3545 |
|
3546 //Check for known properties to tell if touch events are enabled on this device or if |
|
3547 //the number of MSPointer touchpoints on this device is greater than 0. |
|
3548 if (win && nav && !(o.chrome && o.chrome < 6)) { |
|
3549 o.touchEnabled = (("ontouchstart" in win) || (("msMaxTouchPoints" in nav) && (nav.msMaxTouchPoints > 0))); |
|
3550 } |
|
3551 |
|
3552 //It was a parsed UA, do not assign the global value. |
|
3553 if (!subUA) { |
|
3554 |
|
3555 if (typeof process === 'object') { |
|
3556 |
|
3557 if (process.versions && process.versions.node) { |
|
3558 //NodeJS |
|
3559 o.os = process.platform; |
|
3560 o.nodejs = numberify(process.versions.node); |
|
3561 } |
|
3562 } |
|
3563 |
|
3564 YUI.Env.UA = o; |
|
3565 |
|
3566 } |
|
3567 |
|
3568 return o; |
|
3569 }; |
|
3570 |
|
3571 |
|
3572 Y.UA = YUI.Env.UA || YUI.Env.parseUA(); |
|
3573 |
|
3574 /** |
|
3575 Performs a simple comparison between two version numbers, accounting for |
|
3576 standard versioning logic such as the fact that "535.8" is a lower version than |
|
3577 "535.24", even though a simple numerical comparison would indicate that it's |
|
3578 greater. Also accounts for cases such as "1.1" vs. "1.1.0", which are |
|
3579 considered equivalent. |
|
3580 |
|
3581 Returns -1 if version _a_ is lower than version _b_, 0 if they're equivalent, |
|
3582 1 if _a_ is higher than _b_. |
|
3583 |
|
3584 Versions may be numbers or strings containing numbers and dots. For example, |
|
3585 both `535` and `"535.8.10"` are acceptable. A version string containing |
|
3586 non-numeric characters, like `"535.8.beta"`, may produce unexpected results. |
|
3587 |
|
3588 @method compareVersions |
|
3589 @param {Number|String} a First version number to compare. |
|
3590 @param {Number|String} b Second version number to compare. |
|
3591 @return -1 if _a_ is lower than _b_, 0 if they're equivalent, 1 if _a_ is |
|
3592 higher than _b_. |
|
3593 **/ |
|
3594 Y.UA.compareVersions = function (a, b) { |
|
3595 var aPart, aParts, bPart, bParts, i, len; |
|
3596 |
|
3597 if (a === b) { |
|
3598 return 0; |
|
3599 } |
|
3600 |
|
3601 aParts = (a + '').split('.'); |
|
3602 bParts = (b + '').split('.'); |
|
3603 |
|
3604 for (i = 0, len = Math.max(aParts.length, bParts.length); i < len; ++i) { |
|
3605 aPart = parseInt(aParts[i], 10); |
|
3606 bPart = parseInt(bParts[i], 10); |
|
3607 |
|
3608 /*jshint expr: true*/ |
|
3609 isNaN(aPart) && (aPart = 0); |
|
3610 isNaN(bPart) && (bPart = 0); |
|
3611 |
|
3612 if (aPart < bPart) { |
|
3613 return -1; |
|
3614 } |
|
3615 |
|
3616 if (aPart > bPart) { |
|
3617 return 1; |
|
3618 } |
|
3619 } |
|
3620 |
|
3621 return 0; |
|
3622 }; |
|
3623 YUI.Env.aliases = { |
|
3624 "anim": ["anim-base","anim-color","anim-curve","anim-easing","anim-node-plugin","anim-scroll","anim-xy"], |
|
3625 "anim-shape-transform": ["anim-shape"], |
|
3626 "app": ["app-base","app-content","app-transitions","lazy-model-list","model","model-list","model-sync-rest","router","view","view-node-map"], |
|
3627 "attribute": ["attribute-base","attribute-complex"], |
|
3628 "attribute-events": ["attribute-observable"], |
|
3629 "autocomplete": ["autocomplete-base","autocomplete-sources","autocomplete-list","autocomplete-plugin"], |
|
3630 "axes": ["axis-numeric","axis-category","axis-time","axis-stacked"], |
|
3631 "axes-base": ["axis-numeric-base","axis-category-base","axis-time-base","axis-stacked-base"], |
|
3632 "base": ["base-base","base-pluginhost","base-build"], |
|
3633 "cache": ["cache-base","cache-offline","cache-plugin"], |
|
3634 "charts": ["charts-base"], |
|
3635 "collection": ["array-extras","arraylist","arraylist-add","arraylist-filter","array-invoke"], |
|
3636 "color": ["color-base","color-hsl","color-harmony"], |
|
3637 "controller": ["router"], |
|
3638 "dataschema": ["dataschema-base","dataschema-json","dataschema-xml","dataschema-array","dataschema-text"], |
|
3639 "datasource": ["datasource-local","datasource-io","datasource-get","datasource-function","datasource-cache","datasource-jsonschema","datasource-xmlschema","datasource-arrayschema","datasource-textschema","datasource-polling"], |
|
3640 "datatable": ["datatable-core","datatable-table","datatable-head","datatable-body","datatable-base","datatable-column-widths","datatable-message","datatable-mutable","datatable-sort","datatable-datasource"], |
|
3641 "datatype": ["datatype-date","datatype-number","datatype-xml"], |
|
3642 "datatype-date": ["datatype-date-parse","datatype-date-format","datatype-date-math"], |
|
3643 "datatype-number": ["datatype-number-parse","datatype-number-format"], |
|
3644 "datatype-xml": ["datatype-xml-parse","datatype-xml-format"], |
|
3645 "dd": ["dd-ddm-base","dd-ddm","dd-ddm-drop","dd-drag","dd-proxy","dd-constrain","dd-drop","dd-scroll","dd-delegate"], |
|
3646 "dom": ["dom-base","dom-screen","dom-style","selector-native","selector"], |
|
3647 "editor": ["frame","editor-selection","exec-command","editor-base","editor-para","editor-br","editor-bidi","editor-tab","createlink-base"], |
|
3648 "event": ["event-base","event-delegate","event-synthetic","event-mousewheel","event-mouseenter","event-key","event-focus","event-resize","event-hover","event-outside","event-touch","event-move","event-flick","event-valuechange","event-tap"], |
|
3649 "event-custom": ["event-custom-base","event-custom-complex"], |
|
3650 "event-gestures": ["event-flick","event-move"], |
|
3651 "handlebars": ["handlebars-compiler"], |
|
3652 "highlight": ["highlight-base","highlight-accentfold"], |
|
3653 "history": ["history-base","history-hash","history-hash-ie","history-html5"], |
|
3654 "io": ["io-base","io-xdr","io-form","io-upload-iframe","io-queue"], |
|
3655 "json": ["json-parse","json-stringify"], |
|
3656 "loader": ["loader-base","loader-rollup","loader-yui3"], |
|
3657 "node": ["node-base","node-event-delegate","node-pluginhost","node-screen","node-style"], |
|
3658 "pluginhost": ["pluginhost-base","pluginhost-config"], |
|
3659 "querystring": ["querystring-parse","querystring-stringify"], |
|
3660 "recordset": ["recordset-base","recordset-sort","recordset-filter","recordset-indexer"], |
|
3661 "resize": ["resize-base","resize-proxy","resize-constrain"], |
|
3662 "slider": ["slider-base","slider-value-range","clickable-rail","range-slider"], |
|
3663 "template": ["template-base","template-micro"], |
|
3664 "text": ["text-accentfold","text-wordbreak"], |
|
3665 "widget": ["widget-base","widget-htmlparser","widget-skin","widget-uievents"] |
|
3666 }; |
|
3667 |
|
3668 |
|
3669 }, '@VERSION@', {"use": ["get", "features", "intl-base", "yui-log", "yui-later"]}); |
|
3670 YUI.add('get', function (Y, NAME) { |
|
3671 |
|
3672 /*jslint boss:true, expr:true, laxbreak: true */ |
|
3673 |
|
3674 /** |
|
3675 Provides dynamic loading of remote JavaScript and CSS resources. |
|
3676 |
|
3677 @module get |
|
3678 @class Get |
|
3679 @static |
|
3680 **/ |
|
3681 |
|
3682 var Lang = Y.Lang, |
|
3683 |
|
3684 CUSTOM_ATTRS, // defined lazily in Y.Get.Transaction._createNode() |
|
3685 |
|
3686 Get, Transaction; |
|
3687 |
|
3688 Y.Get = Get = { |
|
3689 // -- Public Properties ---------------------------------------------------- |
|
3690 |
|
3691 /** |
|
3692 Default options for CSS requests. Options specified here will override |
|
3693 global defaults for CSS requests. |
|
3694 |
|
3695 See the `options` property for all available options. |
|
3696 |
|
3697 @property cssOptions |
|
3698 @type Object |
|
3699 @static |
|
3700 @since 3.5.0 |
|
3701 **/ |
|
3702 cssOptions: { |
|
3703 attributes: { |
|
3704 rel: 'stylesheet' |
|
3705 }, |
|
3706 |
|
3707 doc : Y.config.linkDoc || Y.config.doc, |
|
3708 pollInterval: 50 |
|
3709 }, |
|
3710 |
|
3711 /** |
|
3712 Default options for JS requests. Options specified here will override global |
|
3713 defaults for JS requests. |
|
3714 |
|
3715 See the `options` property for all available options. |
|
3716 |
|
3717 @property jsOptions |
|
3718 @type Object |
|
3719 @static |
|
3720 @since 3.5.0 |
|
3721 **/ |
|
3722 jsOptions: { |
|
3723 autopurge: true, |
|
3724 doc : Y.config.scriptDoc || Y.config.doc |
|
3725 }, |
|
3726 |
|
3727 /** |
|
3728 Default options to use for all requests. |
|
3729 |
|
3730 Note that while all available options are documented here for ease of |
|
3731 discovery, some options (like callback functions) only make sense at the |
|
3732 transaction level. |
|
3733 |
|
3734 Callback functions specified via the options object or the `options` |
|
3735 parameter of the `css()`, `js()`, or `load()` methods will receive the |
|
3736 transaction object as a parameter. See `Y.Get.Transaction` for details on |
|
3737 the properties and methods available on transactions. |
|
3738 |
|
3739 @static |
|
3740 @since 3.5.0 |
|
3741 @property {Object} options |
|
3742 |
|
3743 @property {Boolean} [options.async=false] Whether or not to load scripts |
|
3744 asynchronously, meaning they're requested in parallel and execution |
|
3745 order is not guaranteed. Has no effect on CSS, since CSS is always |
|
3746 loaded asynchronously. |
|
3747 |
|
3748 @property {Object} [options.attributes] HTML attribute name/value pairs that |
|
3749 should be added to inserted nodes. By default, the `charset` attribute |
|
3750 will be set to "utf-8" and nodes will be given an auto-generated `id` |
|
3751 attribute, but you can override these with your own values if desired. |
|
3752 |
|
3753 @property {Boolean} [options.autopurge] Whether or not to automatically |
|
3754 purge inserted nodes after the purge threshold is reached. This is |
|
3755 `true` by default for JavaScript, but `false` for CSS since purging a |
|
3756 CSS node will also remove any styling applied by the referenced file. |
|
3757 |
|
3758 @property {Object} [options.context] `this` object to use when calling |
|
3759 callback functions. Defaults to the transaction object. |
|
3760 |
|
3761 @property {Mixed} [options.data] Arbitrary data object to pass to "on*" |
|
3762 callbacks. |
|
3763 |
|
3764 @property {Document} [options.doc] Document into which nodes should be |
|
3765 inserted. By default, the current document is used. |
|
3766 |
|
3767 @property {HTMLElement|String} [options.insertBefore] HTML element or id |
|
3768 string of an element before which all generated nodes should be |
|
3769 inserted. If not specified, Get will automatically determine the best |
|
3770 place to insert nodes for maximum compatibility. |
|
3771 |
|
3772 @property {Function} [options.onEnd] Callback to execute after a transaction |
|
3773 is complete, regardless of whether it succeeded or failed. |
|
3774 |
|
3775 @property {Function} [options.onFailure] Callback to execute after a |
|
3776 transaction fails, times out, or is aborted. |
|
3777 |
|
3778 @property {Function} [options.onProgress] Callback to execute after each |
|
3779 individual request in a transaction either succeeds or fails. |
|
3780 |
|
3781 @property {Function} [options.onSuccess] Callback to execute after a |
|
3782 transaction completes successfully with no errors. Note that in browsers |
|
3783 that don't support the `error` event on CSS `<link>` nodes, a failed CSS |
|
3784 request may still be reported as a success because in these browsers |
|
3785 it can be difficult or impossible to distinguish between success and |
|
3786 failure for CSS resources. |
|
3787 |
|
3788 @property {Function} [options.onTimeout] Callback to execute after a |
|
3789 transaction times out. |
|
3790 |
|
3791 @property {Number} [options.pollInterval=50] Polling interval (in |
|
3792 milliseconds) for detecting CSS load completion in browsers that don't |
|
3793 support the `load` event on `<link>` nodes. This isn't used for |
|
3794 JavaScript. |
|
3795 |
|
3796 @property {Number} [options.purgethreshold=20] Number of nodes to insert |
|
3797 before triggering an automatic purge when `autopurge` is `true`. |
|
3798 |
|
3799 @property {Number} [options.timeout] Number of milliseconds to wait before |
|
3800 aborting a transaction. When a timeout occurs, the `onTimeout` callback |
|
3801 is called, followed by `onFailure` and finally `onEnd`. By default, |
|
3802 there is no timeout. |
|
3803 |
|
3804 @property {String} [options.type] Resource type ("css" or "js"). This option |
|
3805 is set automatically by the `css()` and `js()` functions and will be |
|
3806 ignored there, but may be useful when using the `load()` function. If |
|
3807 not specified, the type will be inferred from the URL, defaulting to |
|
3808 "js" if the URL doesn't contain a recognizable file extension. |
|
3809 **/ |
|
3810 options: { |
|
3811 attributes: { |
|
3812 charset: 'utf-8' |
|
3813 }, |
|
3814 |
|
3815 purgethreshold: 20 |
|
3816 }, |
|
3817 |
|
3818 // -- Protected Properties ------------------------------------------------- |
|
3819 |
|
3820 /** |
|
3821 Regex that matches a CSS URL. Used to guess the file type when it's not |
|
3822 specified. |
|
3823 |
|
3824 @property REGEX_CSS |
|
3825 @type RegExp |
|
3826 @final |
|
3827 @protected |
|
3828 @static |
|
3829 @since 3.5.0 |
|
3830 **/ |
|
3831 REGEX_CSS: /\.css(?:[?;].*)?$/i, |
|
3832 |
|
3833 /** |
|
3834 Regex that matches a JS URL. Used to guess the file type when it's not |
|
3835 specified. |
|
3836 |
|
3837 @property REGEX_JS |
|
3838 @type RegExp |
|
3839 @final |
|
3840 @protected |
|
3841 @static |
|
3842 @since 3.5.0 |
|
3843 **/ |
|
3844 REGEX_JS : /\.js(?:[?;].*)?$/i, |
|
3845 |
|
3846 /** |
|
3847 Contains information about the current environment, such as what script and |
|
3848 link injection features it supports. |
|
3849 |
|
3850 This object is created and populated the first time the `_getEnv()` method |
|
3851 is called. |
|
3852 |
|
3853 @property _env |
|
3854 @type Object |
|
3855 @protected |
|
3856 @static |
|
3857 @since 3.5.0 |
|
3858 **/ |
|
3859 |
|
3860 /** |
|
3861 Mapping of document _yuid strings to <head> or <base> node references so we |
|
3862 don't have to look the node up each time we want to insert a request node. |
|
3863 |
|
3864 @property _insertCache |
|
3865 @type Object |
|
3866 @protected |
|
3867 @static |
|
3868 @since 3.5.0 |
|
3869 **/ |
|
3870 _insertCache: {}, |
|
3871 |
|
3872 /** |
|
3873 Information about the currently pending transaction, if any. |
|
3874 |
|
3875 This is actually an object with two properties: `callback`, containing the |
|
3876 optional callback passed to `css()`, `load()`, or `js()`; and `transaction`, |
|
3877 containing the actual transaction instance. |
|
3878 |
|
3879 @property _pending |
|
3880 @type Object |
|
3881 @protected |
|
3882 @static |
|
3883 @since 3.5.0 |
|
3884 **/ |
|
3885 _pending: null, |
|
3886 |
|
3887 /** |
|
3888 HTML nodes eligible to be purged next time autopurge is triggered. |
|
3889 |
|
3890 @property _purgeNodes |
|
3891 @type HTMLElement[] |
|
3892 @protected |
|
3893 @static |
|
3894 @since 3.5.0 |
|
3895 **/ |
|
3896 _purgeNodes: [], |
|
3897 |
|
3898 /** |
|
3899 Queued transactions and associated callbacks. |
|
3900 |
|
3901 @property _queue |
|
3902 @type Object[] |
|
3903 @protected |
|
3904 @static |
|
3905 @since 3.5.0 |
|
3906 **/ |
|
3907 _queue: [], |
|
3908 |
|
3909 // -- Public Methods ------------------------------------------------------- |
|
3910 |
|
3911 /** |
|
3912 Aborts the specified transaction. |
|
3913 |
|
3914 This will cause the transaction's `onFailure` callback to be called and |
|
3915 will prevent any new script and link nodes from being added to the document, |
|
3916 but any resources that have already been requested will continue loading |
|
3917 (there's no safe way to prevent this, unfortunately). |
|
3918 |
|
3919 *Note:* This method is deprecated as of 3.5.0, and will be removed in a |
|
3920 future version of YUI. Use the transaction-level `abort()` method instead. |
|
3921 |
|
3922 @method abort |
|
3923 @param {Get.Transaction} transaction Transaction to abort. |
|
3924 @deprecated Use the `abort()` method on the transaction instead. |
|
3925 @static |
|
3926 **/ |
|
3927 abort: function (transaction) { |
|
3928 var i, id, item, len, pending; |
|
3929 |
|
3930 |
|
3931 if (!transaction.abort) { |
|
3932 id = transaction; |
|
3933 pending = this._pending; |
|
3934 transaction = null; |
|
3935 |
|
3936 if (pending && pending.transaction.id === id) { |
|
3937 transaction = pending.transaction; |
|
3938 this._pending = null; |
|
3939 } else { |
|
3940 for (i = 0, len = this._queue.length; i < len; ++i) { |
|
3941 item = this._queue[i].transaction; |
|
3942 |
|
3943 if (item.id === id) { |
|
3944 transaction = item; |
|
3945 this._queue.splice(i, 1); |
|
3946 break; |
|
3947 } |
|
3948 } |
|
3949 } |
|
3950 } |
|
3951 |
|
3952 transaction && transaction.abort(); |
|
3953 }, |
|
3954 |
|
3955 /** |
|
3956 Loads one or more CSS files. |
|
3957 |
|
3958 The _urls_ parameter may be provided as a URL string, a request object, |
|
3959 or an array of URL strings and/or request objects. |
|
3960 |
|
3961 A request object is just an object that contains a `url` property and zero |
|
3962 or more options that should apply specifically to that request. |
|
3963 Request-specific options take priority over transaction-level options and |
|
3964 default options. |
|
3965 |
|
3966 URLs may be relative or absolute, and do not have to have the same origin |
|
3967 as the current page. |
|
3968 |
|
3969 The `options` parameter may be omitted completely and a callback passed in |
|
3970 its place, if desired. |
|
3971 |
|
3972 @example |
|
3973 |
|
3974 // Load a single CSS file and log a message on completion. |
|
3975 Y.Get.css('foo.css', function (err) { |
|
3976 if (err) { |
|
3977 } else { |
|
3978 } |
|
3979 }); |
|
3980 |
|
3981 // Load multiple CSS files and log a message when all have finished |
|
3982 // loading. |
|
3983 var urls = ['foo.css', 'http://example.com/bar.css', 'baz/quux.css']; |
|
3984 |
|
3985 Y.Get.css(urls, function (err) { |
|
3986 if (err) { |
|
3987 } else { |
|
3988 } |
|
3989 }); |
|
3990 |
|
3991 // Specify transaction-level options, which will apply to all requests |
|
3992 // within the transaction. |
|
3993 Y.Get.css(urls, { |
|
3994 attributes: {'class': 'my-css'}, |
|
3995 timeout : 5000 |
|
3996 }); |
|
3997 |
|
3998 // Specify per-request options, which override transaction-level and |
|
3999 // default options. |
|
4000 Y.Get.css([ |
|
4001 {url: 'foo.css', attributes: {id: 'foo'}}, |
|
4002 {url: 'bar.css', attributes: {id: 'bar', charset: 'iso-8859-1'}} |
|
4003 ]); |
|
4004 |
|
4005 @method css |
|
4006 @param {String|Object|Array} urls URL string, request object, or array |
|
4007 of URLs and/or request objects to load. |
|
4008 @param {Object} [options] Options for this transaction. See the |
|
4009 `Y.Get.options` property for a complete list of available options. |
|
4010 @param {Function} [callback] Callback function to be called on completion. |
|
4011 This is a general callback and will be called before any more granular |
|
4012 callbacks (`onSuccess`, `onFailure`, etc.) specified in the `options` |
|
4013 object. |
|
4014 |
|
4015 @param {Array|null} callback.err Array of errors that occurred during |
|
4016 the transaction, or `null` on success. |
|
4017 @param {Get.Transaction} callback.transaction Transaction object. |
|
4018 |
|
4019 @return {Get.Transaction} Transaction object. |
|
4020 @static |
|
4021 **/ |
|
4022 css: function (urls, options, callback) { |
|
4023 return this._load('css', urls, options, callback); |
|
4024 }, |
|
4025 |
|
4026 /** |
|
4027 Loads one or more JavaScript resources. |
|
4028 |
|
4029 The _urls_ parameter may be provided as a URL string, a request object, |
|
4030 or an array of URL strings and/or request objects. |
|
4031 |
|
4032 A request object is just an object that contains a `url` property and zero |
|
4033 or more options that should apply specifically to that request. |
|
4034 Request-specific options take priority over transaction-level options and |
|
4035 default options. |
|
4036 |
|
4037 URLs may be relative or absolute, and do not have to have the same origin |
|
4038 as the current page. |
|
4039 |
|
4040 The `options` parameter may be omitted completely and a callback passed in |
|
4041 its place, if desired. |
|
4042 |
|
4043 Scripts will be executed in the order they're specified unless the `async` |
|
4044 option is `true`, in which case they'll be loaded in parallel and executed |
|
4045 in whatever order they finish loading. |
|
4046 |
|
4047 @example |
|
4048 |
|
4049 // Load a single JS file and log a message on completion. |
|
4050 Y.Get.js('foo.js', function (err) { |
|
4051 if (err) { |
|
4052 } else { |
|
4053 } |
|
4054 }); |
|
4055 |
|
4056 // Load multiple JS files, execute them in order, and log a message when |
|
4057 // all have finished loading. |
|
4058 var urls = ['foo.js', 'http://example.com/bar.js', 'baz/quux.js']; |
|
4059 |
|
4060 Y.Get.js(urls, function (err) { |
|
4061 if (err) { |
|
4062 } else { |
|
4063 } |
|
4064 }); |
|
4065 |
|
4066 // Specify transaction-level options, which will apply to all requests |
|
4067 // within the transaction. |
|
4068 Y.Get.js(urls, { |
|
4069 attributes: {'class': 'my-js'}, |
|
4070 timeout : 5000 |
|
4071 }); |
|
4072 |
|
4073 // Specify per-request options, which override transaction-level and |
|
4074 // default options. |
|
4075 Y.Get.js([ |
|
4076 {url: 'foo.js', attributes: {id: 'foo'}}, |
|
4077 {url: 'bar.js', attributes: {id: 'bar', charset: 'iso-8859-1'}} |
|
4078 ]); |
|
4079 |
|
4080 @method js |
|
4081 @param {String|Object|Array} urls URL string, request object, or array |
|
4082 of URLs and/or request objects to load. |
|
4083 @param {Object} [options] Options for this transaction. See the |
|
4084 `Y.Get.options` property for a complete list of available options. |
|
4085 @param {Function} [callback] Callback function to be called on completion. |
|
4086 This is a general callback and will be called before any more granular |
|
4087 callbacks (`onSuccess`, `onFailure`, etc.) specified in the `options` |
|
4088 object. |
|
4089 |
|
4090 @param {Array|null} callback.err Array of errors that occurred during |
|
4091 the transaction, or `null` on success. |
|
4092 @param {Get.Transaction} callback.transaction Transaction object. |
|
4093 |
|
4094 @return {Get.Transaction} Transaction object. |
|
4095 @since 3.5.0 |
|
4096 @static |
|
4097 **/ |
|
4098 js: function (urls, options, callback) { |
|
4099 return this._load('js', urls, options, callback); |
|
4100 }, |
|
4101 |
|
4102 /** |
|
4103 Loads one or more CSS and/or JavaScript resources in the same transaction. |
|
4104 |
|
4105 Use this method when you want to load both CSS and JavaScript in a single |
|
4106 transaction and be notified when all requested URLs have finished loading, |
|
4107 regardless of type. |
|
4108 |
|
4109 Behavior and options are the same as for the `css()` and `js()` methods. If |
|
4110 a resource type isn't specified in per-request options or transaction-level |
|
4111 options, Get will guess the file type based on the URL's extension (`.css` |
|
4112 or `.js`, with or without a following query string). If the file type can't |
|
4113 be guessed from the URL, a warning will be logged and Get will assume the |
|
4114 URL is a JavaScript resource. |
|
4115 |
|
4116 @example |
|
4117 |
|
4118 // Load both CSS and JS files in a single transaction, and log a message |
|
4119 // when all files have finished loading. |
|
4120 Y.Get.load(['foo.css', 'bar.js', 'baz.css'], function (err) { |
|
4121 if (err) { |
|
4122 } else { |
|
4123 } |
|
4124 }); |
|
4125 |
|
4126 @method load |
|
4127 @param {String|Object|Array} urls URL string, request object, or array |
|
4128 of URLs and/or request objects to load. |
|
4129 @param {Object} [options] Options for this transaction. See the |
|
4130 `Y.Get.options` property for a complete list of available options. |
|
4131 @param {Function} [callback] Callback function to be called on completion. |
|
4132 This is a general callback and will be called before any more granular |
|
4133 callbacks (`onSuccess`, `onFailure`, etc.) specified in the `options` |
|
4134 object. |
|
4135 |
|
4136 @param {Array|null} err Array of errors that occurred during the |
|
4137 transaction, or `null` on success. |
|
4138 @param {Get.Transaction} Transaction object. |
|
4139 |
|
4140 @return {Get.Transaction} Transaction object. |
|
4141 @since 3.5.0 |
|
4142 @static |
|
4143 **/ |
|
4144 load: function (urls, options, callback) { |
|
4145 return this._load(null, urls, options, callback); |
|
4146 }, |
|
4147 |
|
4148 // -- Protected Methods ---------------------------------------------------- |
|
4149 |
|
4150 /** |
|
4151 Triggers an automatic purge if the purge threshold has been reached. |
|
4152 |
|
4153 @method _autoPurge |
|
4154 @param {Number} threshold Purge threshold to use, in milliseconds. |
|
4155 @protected |
|
4156 @since 3.5.0 |
|
4157 @static |
|
4158 **/ |
|
4159 _autoPurge: function (threshold) { |
|
4160 if (threshold && this._purgeNodes.length >= threshold) { |
|
4161 this._purge(this._purgeNodes); |
|
4162 } |
|
4163 }, |
|
4164 |
|
4165 /** |
|
4166 Populates the `_env` property with information about the current |
|
4167 environment. |
|
4168 |
|
4169 @method _getEnv |
|
4170 @return {Object} Environment information. |
|
4171 @protected |
|
4172 @since 3.5.0 |
|
4173 @static |
|
4174 **/ |
|
4175 _getEnv: function () { |
|
4176 var doc = Y.config.doc, |
|
4177 ua = Y.UA; |
|
4178 |
|
4179 // Note: some of these checks require browser sniffs since it's not |
|
4180 // feasible to load test files on every pageview just to perform a |
|
4181 // feature test. I'm sorry if this makes you sad. |
|
4182 return (this._env = { |
|
4183 |
|
4184 // True if this is a browser that supports disabling async mode on |
|
4185 // dynamically created script nodes. See |
|
4186 // https://developer.mozilla.org/En/HTML/Element/Script#Attributes |
|
4187 |
|
4188 // IE10 doesn't return true for the MDN feature test, so setting it explicitly, |
|
4189 // because it is async by default, and allows you to disable async by setting it to false |
|
4190 async: (doc && doc.createElement('script').async === true) || (ua.ie >= 10), |
|
4191 |
|
4192 // True if this browser fires an event when a dynamically injected |
|
4193 // link node fails to load. This is currently true for Firefox 9+ |
|
4194 // and WebKit 535.24+ |
|
4195 cssFail: ua.gecko >= 9 || ua.compareVersions(ua.webkit, 535.24) >= 0, |
|
4196 |
|
4197 // True if this browser fires an event when a dynamically injected |
|
4198 // link node finishes loading. This is currently true for IE, Opera, |
|
4199 // Firefox 9+, and WebKit 535.24+. Note that IE versions <9 fire the |
|
4200 // DOM 0 "onload" event, but not "load". All versions of IE fire |
|
4201 // "onload". |
|
4202 // davglass: Seems that Chrome on Android needs this to be false. |
|
4203 cssLoad: ( |
|
4204 (!ua.gecko && !ua.webkit) || ua.gecko >= 9 || |
|
4205 ua.compareVersions(ua.webkit, 535.24) >= 0 |
|
4206 ) && !(ua.chrome && ua.chrome <= 18), |
|
4207 |
|
4208 // True if this browser preserves script execution order while |
|
4209 // loading scripts in parallel as long as the script node's `async` |
|
4210 // attribute is set to false to explicitly disable async execution. |
|
4211 preservesScriptOrder: !!(ua.gecko || ua.opera || (ua.ie && ua.ie >= 10)) |
|
4212 }); |
|
4213 }, |
|
4214 |
|
4215 _getTransaction: function (urls, options) { |
|
4216 var requests = [], |
|
4217 i, len, req, url; |
|
4218 |
|
4219 if (!Lang.isArray(urls)) { |
|
4220 urls = [urls]; |
|
4221 } |
|
4222 |
|
4223 options = Y.merge(this.options, options); |
|
4224 |
|
4225 // Clone the attributes object so we don't end up modifying it by ref. |
|
4226 options.attributes = Y.merge(this.options.attributes, |
|
4227 options.attributes); |
|
4228 |
|
4229 for (i = 0, len = urls.length; i < len; ++i) { |
|
4230 url = urls[i]; |
|
4231 req = {attributes: {}}; |
|
4232 |
|
4233 // If `url` is a string, we create a URL object for it, then mix in |
|
4234 // global options and request-specific options. If it's an object |
|
4235 // with a "url" property, we assume it's a request object containing |
|
4236 // URL-specific options. |
|
4237 if (typeof url === 'string') { |
|
4238 req.url = url; |
|
4239 } else if (url.url) { |
|
4240 // URL-specific options override both global defaults and |
|
4241 // request-specific options. |
|
4242 Y.mix(req, url, false, null, 0, true); |
|
4243 url = url.url; // Make url a string so we can use it later. |
|
4244 } else { |
|
4245 continue; |
|
4246 } |
|
4247 |
|
4248 Y.mix(req, options, false, null, 0, true); |
|
4249 |
|
4250 // If we didn't get an explicit type for this URL either in the |
|
4251 // request options or the URL-specific options, try to determine |
|
4252 // one from the file extension. |
|
4253 if (!req.type) { |
|
4254 if (this.REGEX_CSS.test(url)) { |
|
4255 req.type = 'css'; |
|
4256 } else { |
|
4257 if (!this.REGEX_JS.test(url)) { |
|
4258 } |
|
4259 |
|
4260 req.type = 'js'; |
|
4261 } |
|
4262 } |
|
4263 |
|
4264 // Mix in type-specific default options, but don't overwrite any |
|
4265 // options that have already been set. |
|
4266 Y.mix(req, req.type === 'js' ? this.jsOptions : this.cssOptions, |
|
4267 false, null, 0, true); |
|
4268 |
|
4269 // Give the node an id attribute if it doesn't already have one. |
|
4270 req.attributes.id || (req.attributes.id = Y.guid()); |
|
4271 |
|
4272 // Backcompat for <3.5.0 behavior. |
|
4273 if (req.win) { |
|
4274 req.doc = req.win.document; |
|
4275 } else { |
|
4276 req.win = req.doc.defaultView || req.doc.parentWindow; |
|
4277 } |
|
4278 |
|
4279 if (req.charset) { |
|
4280 req.attributes.charset = req.charset; |
|
4281 } |
|
4282 |
|
4283 requests.push(req); |
|
4284 } |
|
4285 |
|
4286 return new Transaction(requests, options); |
|
4287 }, |
|
4288 |
|
4289 _load: function (type, urls, options, callback) { |
|
4290 var transaction; |
|
4291 |
|
4292 // Allow callback as third param. |
|
4293 if (typeof options === 'function') { |
|
4294 callback = options; |
|
4295 options = {}; |
|
4296 } |
|
4297 |
|
4298 options || (options = {}); |
|
4299 options.type = type; |
|
4300 |
|
4301 options._onFinish = Get._onTransactionFinish; |
|
4302 |
|
4303 if (!this._env) { |
|
4304 this._getEnv(); |
|
4305 } |
|
4306 |
|
4307 transaction = this._getTransaction(urls, options); |
|
4308 |
|
4309 this._queue.push({ |
|
4310 callback : callback, |
|
4311 transaction: transaction |
|
4312 }); |
|
4313 |
|
4314 this._next(); |
|
4315 |
|
4316 return transaction; |
|
4317 }, |
|
4318 |
|
4319 _onTransactionFinish : function() { |
|
4320 Get._pending = null; |
|
4321 Get._next(); |
|
4322 }, |
|
4323 |
|
4324 _next: function () { |
|
4325 var item; |
|
4326 |
|
4327 if (this._pending) { |
|
4328 return; |
|
4329 } |
|
4330 |
|
4331 item = this._queue.shift(); |
|
4332 |
|
4333 if (item) { |
|
4334 this._pending = item; |
|
4335 item.transaction.execute(item.callback); |
|
4336 } |
|
4337 }, |
|
4338 |
|
4339 _purge: function (nodes) { |
|
4340 var purgeNodes = this._purgeNodes, |
|
4341 isTransaction = nodes !== purgeNodes, |
|
4342 index, node; |
|
4343 |
|
4344 while (node = nodes.pop()) { // assignment |
|
4345 // Don't purge nodes that haven't finished loading (or errored out), |
|
4346 // since this can hang the transaction. |
|
4347 if (!node._yuiget_finished) { |
|
4348 continue; |
|
4349 } |
|
4350 |
|
4351 node.parentNode && node.parentNode.removeChild(node); |
|
4352 |
|
4353 // If this is a transaction-level purge and this node also exists in |
|
4354 // the Get-level _purgeNodes array, we need to remove it from |
|
4355 // _purgeNodes to avoid creating a memory leak. The indexOf lookup |
|
4356 // sucks, but until we get WeakMaps, this is the least troublesome |
|
4357 // way to do this (we can't just hold onto node ids because they may |
|
4358 // not be in the same document). |
|
4359 if (isTransaction) { |
|
4360 index = Y.Array.indexOf(purgeNodes, node); |
|
4361 |
|
4362 if (index > -1) { |
|
4363 purgeNodes.splice(index, 1); |
|
4364 } |
|
4365 } |
|
4366 } |
|
4367 } |
|
4368 }; |
|
4369 |
|
4370 /** |
|
4371 Alias for `js()`. |
|
4372 |
|
4373 @method script |
|
4374 @static |
|
4375 **/ |
|
4376 Get.script = Get.js; |
|
4377 |
|
4378 /** |
|
4379 Represents a Get transaction, which may contain requests for one or more JS or |
|
4380 CSS files. |
|
4381 |
|
4382 This class should not be instantiated manually. Instances will be created and |
|
4383 returned as needed by Y.Get's `css()`, `js()`, and `load()` methods. |
|
4384 |
|
4385 @class Get.Transaction |
|
4386 @constructor |
|
4387 @since 3.5.0 |
|
4388 **/ |
|
4389 Get.Transaction = Transaction = function (requests, options) { |
|
4390 var self = this; |
|
4391 |
|
4392 self.id = Transaction._lastId += 1; |
|
4393 self.data = options.data; |
|
4394 self.errors = []; |
|
4395 self.nodes = []; |
|
4396 self.options = options; |
|
4397 self.requests = requests; |
|
4398 |
|
4399 self._callbacks = []; // callbacks to call after execution finishes |
|
4400 self._queue = []; |
|
4401 self._reqsWaiting = 0; |
|
4402 |
|
4403 // Deprecated pre-3.5.0 properties. |
|
4404 self.tId = self.id; // Use `id` instead. |
|
4405 self.win = options.win || Y.config.win; |
|
4406 }; |
|
4407 |
|
4408 /** |
|
4409 Arbitrary data object associated with this transaction. |
|
4410 |
|
4411 This object comes from the options passed to `Get.css()`, `Get.js()`, or |
|
4412 `Get.load()`, and will be `undefined` if no data object was specified. |
|
4413 |
|
4414 @property {Object} data |
|
4415 **/ |
|
4416 |
|
4417 /** |
|
4418 Array of errors that have occurred during this transaction, if any. |
|
4419 |
|
4420 @since 3.5.0 |
|
4421 @property {Object[]} errors |
|
4422 @property {String} errors.error Error message. |
|
4423 @property {Object} errors.request Request object related to the error. |
|
4424 **/ |
|
4425 |
|
4426 /** |
|
4427 Numeric id for this transaction, unique among all transactions within the same |
|
4428 YUI sandbox in the current pageview. |
|
4429 |
|
4430 @property {Number} id |
|
4431 @since 3.5.0 |
|
4432 **/ |
|
4433 |
|
4434 /** |
|
4435 HTMLElement nodes (native ones, not YUI Node instances) that have been inserted |
|
4436 during the current transaction. |
|
4437 |
|
4438 @property {HTMLElement[]} nodes |
|
4439 **/ |
|
4440 |
|
4441 /** |
|
4442 Options associated with this transaction. |
|
4443 |
|
4444 See `Get.options` for the full list of available options. |
|
4445 |
|
4446 @property {Object} options |
|
4447 @since 3.5.0 |
|
4448 **/ |
|
4449 |
|
4450 /** |
|
4451 Request objects contained in this transaction. Each request object represents |
|
4452 one CSS or JS URL that will be (or has been) requested and loaded into the page. |
|
4453 |
|
4454 @property {Object} requests |
|
4455 @since 3.5.0 |
|
4456 **/ |
|
4457 |
|
4458 /** |
|
4459 Id of the most recent transaction. |
|
4460 |
|
4461 @property _lastId |
|
4462 @type Number |
|
4463 @protected |
|
4464 @static |
|
4465 **/ |
|
4466 Transaction._lastId = 0; |
|
4467 |
|
4468 Transaction.prototype = { |
|
4469 // -- Public Properties ---------------------------------------------------- |
|
4470 |
|
4471 /** |
|
4472 Current state of this transaction. One of "new", "executing", or "done". |
|
4473 |
|
4474 @property _state |
|
4475 @type String |
|
4476 @protected |
|
4477 **/ |
|
4478 _state: 'new', // "new", "executing", or "done" |
|
4479 |
|
4480 // -- Public Methods ------------------------------------------------------- |
|
4481 |
|
4482 /** |
|
4483 Aborts this transaction. |
|
4484 |
|
4485 This will cause the transaction's `onFailure` callback to be called and |
|
4486 will prevent any new script and link nodes from being added to the document, |
|
4487 but any resources that have already been requested will continue loading |
|
4488 (there's no safe way to prevent this, unfortunately). |
|
4489 |
|
4490 @method abort |
|
4491 @param {String} [msg="Aborted."] Optional message to use in the `errors` |
|
4492 array describing why the transaction was aborted. |
|
4493 **/ |
|
4494 abort: function (msg) { |
|
4495 this._pending = null; |
|
4496 this._pendingCSS = null; |
|
4497 this._pollTimer = clearTimeout(this._pollTimer); |
|
4498 this._queue = []; |
|
4499 this._reqsWaiting = 0; |
|
4500 |
|
4501 this.errors.push({error: msg || 'Aborted'}); |
|
4502 this._finish(); |
|
4503 }, |
|
4504 |
|
4505 /** |
|
4506 Begins execting the transaction. |
|
4507 |
|
4508 There's usually no reason to call this manually, since Get will call it |
|
4509 automatically when other pending transactions have finished. If you really |
|
4510 want to execute your transaction before Get does, you can, but be aware that |
|
4511 this transaction's scripts may end up executing before the scripts in other |
|
4512 pending transactions. |
|
4513 |
|
4514 If the transaction is already executing, the specified callback (if any) |
|
4515 will be queued and called after execution finishes. If the transaction has |
|
4516 already finished, the callback will be called immediately (the transaction |
|
4517 will not be executed again). |
|
4518 |
|
4519 @method execute |
|
4520 @param {Function} callback Callback function to execute after all requests |
|
4521 in the transaction are complete, or after the transaction is aborted. |
|
4522 **/ |
|
4523 execute: function (callback) { |
|
4524 var self = this, |
|
4525 requests = self.requests, |
|
4526 state = self._state, |
|
4527 i, len, queue, req; |
|
4528 |
|
4529 if (state === 'done') { |
|
4530 callback && callback(self.errors.length ? self.errors : null, self); |
|
4531 return; |
|
4532 } else { |
|
4533 callback && self._callbacks.push(callback); |
|
4534 |
|
4535 if (state === 'executing') { |
|
4536 return; |
|
4537 } |
|
4538 } |
|
4539 |
|
4540 self._state = 'executing'; |
|
4541 self._queue = queue = []; |
|
4542 |
|
4543 if (self.options.timeout) { |
|
4544 self._timeout = setTimeout(function () { |
|
4545 self.abort('Timeout'); |
|
4546 }, self.options.timeout); |
|
4547 } |
|
4548 |
|
4549 self._reqsWaiting = requests.length; |
|
4550 |
|
4551 for (i = 0, len = requests.length; i < len; ++i) { |
|
4552 req = requests[i]; |
|
4553 |
|
4554 if (req.async || req.type === 'css') { |
|
4555 // No need to queue CSS or fully async JS. |
|
4556 self._insert(req); |
|
4557 } else { |
|
4558 queue.push(req); |
|
4559 } |
|
4560 } |
|
4561 |
|
4562 self._next(); |
|
4563 }, |
|
4564 |
|
4565 /** |
|
4566 Manually purges any `<script>` or `<link>` nodes this transaction has |
|
4567 created. |
|
4568 |
|
4569 Be careful when purging a transaction that contains CSS requests, since |
|
4570 removing `<link>` nodes will also remove any styles they applied. |
|
4571 |
|
4572 @method purge |
|
4573 **/ |
|
4574 purge: function () { |
|
4575 Get._purge(this.nodes); |
|
4576 }, |
|
4577 |
|
4578 // -- Protected Methods ---------------------------------------------------- |
|
4579 _createNode: function (name, attrs, doc) { |
|
4580 var node = doc.createElement(name), |
|
4581 attr, testEl; |
|
4582 |
|
4583 if (!CUSTOM_ATTRS) { |
|
4584 // IE6 and IE7 expect property names rather than attribute names for |
|
4585 // certain attributes. Rather than sniffing, we do a quick feature |
|
4586 // test the first time _createNode() runs to determine whether we |
|
4587 // need to provide a workaround. |
|
4588 testEl = doc.createElement('div'); |
|
4589 testEl.setAttribute('class', 'a'); |
|
4590 |
|
4591 CUSTOM_ATTRS = testEl.className === 'a' ? {} : { |
|
4592 'for' : 'htmlFor', |
|
4593 'class': 'className' |
|
4594 }; |
|
4595 } |
|
4596 |
|
4597 for (attr in attrs) { |
|
4598 if (attrs.hasOwnProperty(attr)) { |
|
4599 node.setAttribute(CUSTOM_ATTRS[attr] || attr, attrs[attr]); |
|
4600 } |
|
4601 } |
|
4602 |
|
4603 return node; |
|
4604 }, |
|
4605 |
|
4606 _finish: function () { |
|
4607 var errors = this.errors.length ? this.errors : null, |
|
4608 options = this.options, |
|
4609 thisObj = options.context || this, |
|
4610 data, i, len; |
|
4611 |
|
4612 if (this._state === 'done') { |
|
4613 return; |
|
4614 } |
|
4615 |
|
4616 this._state = 'done'; |
|
4617 |
|
4618 for (i = 0, len = this._callbacks.length; i < len; ++i) { |
|
4619 this._callbacks[i].call(thisObj, errors, this); |
|
4620 } |
|
4621 |
|
4622 data = this._getEventData(); |
|
4623 |
|
4624 if (errors) { |
|
4625 if (options.onTimeout && errors[errors.length - 1].error === 'Timeout') { |
|
4626 options.onTimeout.call(thisObj, data); |
|
4627 } |
|
4628 |
|
4629 if (options.onFailure) { |
|
4630 options.onFailure.call(thisObj, data); |
|
4631 } |
|
4632 } else if (options.onSuccess) { |
|
4633 options.onSuccess.call(thisObj, data); |
|
4634 } |
|
4635 |
|
4636 if (options.onEnd) { |
|
4637 options.onEnd.call(thisObj, data); |
|
4638 } |
|
4639 |
|
4640 if (options._onFinish) { |
|
4641 options._onFinish(); |
|
4642 } |
|
4643 }, |
|
4644 |
|
4645 _getEventData: function (req) { |
|
4646 if (req) { |
|
4647 // This merge is necessary for backcompat. I hate it. |
|
4648 return Y.merge(this, { |
|
4649 abort : this.abort, // have to copy these because the prototype isn't preserved |
|
4650 purge : this.purge, |
|
4651 request: req, |
|
4652 url : req.url, |
|
4653 win : req.win |
|
4654 }); |
|
4655 } else { |
|
4656 return this; |
|
4657 } |
|
4658 }, |
|
4659 |
|
4660 _getInsertBefore: function (req) { |
|
4661 var doc = req.doc, |
|
4662 el = req.insertBefore, |
|
4663 cache, docStamp; |
|
4664 |
|
4665 if (el) { |
|
4666 return typeof el === 'string' ? doc.getElementById(el) : el; |
|
4667 } |
|
4668 |
|
4669 cache = Get._insertCache; |
|
4670 docStamp = Y.stamp(doc); |
|
4671 |
|
4672 if ((el = cache[docStamp])) { // assignment |
|
4673 return el; |
|
4674 } |
|
4675 |
|
4676 // Inserting before a <base> tag apparently works around an IE bug |
|
4677 // (according to a comment from pre-3.5.0 Y.Get), but I'm not sure what |
|
4678 // bug that is, exactly. Better safe than sorry? |
|
4679 if ((el = doc.getElementsByTagName('base')[0])) { // assignment |
|
4680 return (cache[docStamp] = el); |
|
4681 } |
|
4682 |
|
4683 // Look for a <head> element. |
|
4684 el = doc.head || doc.getElementsByTagName('head')[0]; |
|
4685 |
|
4686 if (el) { |
|
4687 // Create a marker node at the end of <head> to use as an insertion |
|
4688 // point. Inserting before this node will ensure that all our CSS |
|
4689 // gets inserted in the correct order, to maintain style precedence. |
|
4690 el.appendChild(doc.createTextNode('')); |
|
4691 return (cache[docStamp] = el.lastChild); |
|
4692 } |
|
4693 |
|
4694 // If all else fails, just insert before the first script node on the |
|
4695 // page, which is virtually guaranteed to exist. |
|
4696 return (cache[docStamp] = doc.getElementsByTagName('script')[0]); |
|
4697 }, |
|
4698 |
|
4699 _insert: function (req) { |
|
4700 var env = Get._env, |
|
4701 insertBefore = this._getInsertBefore(req), |
|
4702 isScript = req.type === 'js', |
|
4703 node = req.node, |
|
4704 self = this, |
|
4705 ua = Y.UA, |
|
4706 cssTimeout, nodeType; |
|
4707 |
|
4708 if (!node) { |
|
4709 if (isScript) { |
|
4710 nodeType = 'script'; |
|
4711 } else if (!env.cssLoad && ua.gecko) { |
|
4712 nodeType = 'style'; |
|
4713 } else { |
|
4714 nodeType = 'link'; |
|
4715 } |
|
4716 |
|
4717 node = req.node = this._createNode(nodeType, req.attributes, |
|
4718 req.doc); |
|
4719 } |
|
4720 |
|
4721 function onError() { |
|
4722 self._progress('Failed to load ' + req.url, req); |
|
4723 } |
|
4724 |
|
4725 function onLoad() { |
|
4726 if (cssTimeout) { |
|
4727 clearTimeout(cssTimeout); |
|
4728 } |
|
4729 |
|
4730 self._progress(null, req); |
|
4731 } |
|
4732 |
|
4733 // Deal with script asynchronicity. |
|
4734 if (isScript) { |
|
4735 node.setAttribute('src', req.url); |
|
4736 |
|
4737 if (req.async) { |
|
4738 // Explicitly indicate that we want the browser to execute this |
|
4739 // script asynchronously. This is necessary for older browsers |
|
4740 // like Firefox <4. |
|
4741 node.async = true; |
|
4742 } else { |
|
4743 if (env.async) { |
|
4744 // This browser treats injected scripts as async by default |
|
4745 // (standard HTML5 behavior) but asynchronous loading isn't |
|
4746 // desired, so tell the browser not to mark this script as |
|
4747 // async. |
|
4748 node.async = false; |
|
4749 } |
|
4750 |
|
4751 // If this browser doesn't preserve script execution order based |
|
4752 // on insertion order, we'll need to avoid inserting other |
|
4753 // scripts until this one finishes loading. |
|
4754 if (!env.preservesScriptOrder) { |
|
4755 this._pending = req; |
|
4756 } |
|
4757 } |
|
4758 } else { |
|
4759 if (!env.cssLoad && ua.gecko) { |
|
4760 // In Firefox <9, we can import the requested URL into a <style> |
|
4761 // node and poll for the existence of node.sheet.cssRules. This |
|
4762 // gives us a reliable way to determine CSS load completion that |
|
4763 // also works for cross-domain stylesheets. |
|
4764 // |
|
4765 // Props to Zach Leatherman for calling my attention to this |
|
4766 // technique. |
|
4767 node.innerHTML = (req.attributes.charset ? |
|
4768 '@charset "' + req.attributes.charset + '";' : '') + |
|
4769 '@import "' + req.url + '";'; |
|
4770 } else { |
|
4771 node.setAttribute('href', req.url); |
|
4772 } |
|
4773 } |
|
4774 |
|
4775 // Inject the node. |
|
4776 if (isScript && ua.ie && (ua.ie < 9 || (document.documentMode && document.documentMode < 9))) { |
|
4777 // Script on IE < 9, and IE 9+ when in IE 8 or older modes, including quirks mode. |
|
4778 node.onreadystatechange = function () { |
|
4779 if (/loaded|complete/.test(node.readyState)) { |
|
4780 node.onreadystatechange = null; |
|
4781 onLoad(); |
|
4782 } |
|
4783 }; |
|
4784 } else if (!isScript && !env.cssLoad) { |
|
4785 // CSS on Firefox <9 or WebKit. |
|
4786 this._poll(req); |
|
4787 } else { |
|
4788 // Script or CSS on everything else. Using DOM 0 events because that |
|
4789 // evens the playing field with older IEs. |
|
4790 |
|
4791 if (ua.ie >= 10) { |
|
4792 |
|
4793 // We currently need to introduce a timeout for IE10, since it |
|
4794 // calls onerror/onload synchronously for 304s - messing up existing |
|
4795 // program flow. |
|
4796 |
|
4797 // Remove this block if the following bug gets fixed by GA |
|
4798 /*jshint maxlen: 1500 */ |
|
4799 // https://connect.microsoft.com/IE/feedback/details/763871/dynamically-loaded-scripts-with-304s-responses-interrupt-the-currently-executing-js-thread-onload |
|
4800 node.onerror = function() { setTimeout(onError, 0); }; |
|
4801 node.onload = function() { setTimeout(onLoad, 0); }; |
|
4802 } else { |
|
4803 node.onerror = onError; |
|
4804 node.onload = onLoad; |
|
4805 } |
|
4806 |
|
4807 // If this browser doesn't fire an event when CSS fails to load, |
|
4808 // fail after a timeout to avoid blocking the transaction queue. |
|
4809 if (!env.cssFail && !isScript) { |
|
4810 cssTimeout = setTimeout(onError, req.timeout || 3000); |
|
4811 } |
|
4812 } |
|
4813 |
|
4814 this.nodes.push(node); |
|
4815 insertBefore.parentNode.insertBefore(node, insertBefore); |
|
4816 }, |
|
4817 |
|
4818 _next: function () { |
|
4819 if (this._pending) { |
|
4820 return; |
|
4821 } |
|
4822 |
|
4823 // If there are requests in the queue, insert the next queued request. |
|
4824 // Otherwise, if we're waiting on already-inserted requests to finish, |
|
4825 // wait longer. If there are no queued requests and we're not waiting |
|
4826 // for anything to load, then we're done! |
|
4827 if (this._queue.length) { |
|
4828 this._insert(this._queue.shift()); |
|
4829 } else if (!this._reqsWaiting) { |
|
4830 this._finish(); |
|
4831 } |
|
4832 }, |
|
4833 |
|
4834 _poll: function (newReq) { |
|
4835 var self = this, |
|
4836 pendingCSS = self._pendingCSS, |
|
4837 isWebKit = Y.UA.webkit, |
|
4838 i, hasRules, j, nodeHref, req, sheets; |
|
4839 |
|
4840 if (newReq) { |
|
4841 pendingCSS || (pendingCSS = self._pendingCSS = []); |
|
4842 pendingCSS.push(newReq); |
|
4843 |
|
4844 if (self._pollTimer) { |
|
4845 // A poll timeout is already pending, so no need to create a |
|
4846 // new one. |
|
4847 return; |
|
4848 } |
|
4849 } |
|
4850 |
|
4851 self._pollTimer = null; |
|
4852 |
|
4853 // Note: in both the WebKit and Gecko hacks below, a CSS URL that 404s |
|
4854 // will still be treated as a success. There's no good workaround for |
|
4855 // this. |
|
4856 |
|
4857 for (i = 0; i < pendingCSS.length; ++i) { |
|
4858 req = pendingCSS[i]; |
|
4859 |
|
4860 if (isWebKit) { |
|
4861 // Look for a stylesheet matching the pending URL. |
|
4862 sheets = req.doc.styleSheets; |
|
4863 j = sheets.length; |
|
4864 nodeHref = req.node.href; |
|
4865 |
|
4866 while (--j >= 0) { |
|
4867 if (sheets[j].href === nodeHref) { |
|
4868 pendingCSS.splice(i, 1); |
|
4869 i -= 1; |
|
4870 self._progress(null, req); |
|
4871 break; |
|
4872 } |
|
4873 } |
|
4874 } else { |
|
4875 // Many thanks to Zach Leatherman for calling my attention to |
|
4876 // the @import-based cross-domain technique used here, and to |
|
4877 // Oleg Slobodskoi for an earlier same-domain implementation. |
|
4878 // |
|
4879 // See Zach's blog for more details: |
|
4880 // http://www.zachleat.com/web/2010/07/29/load-css-dynamically/ |
|
4881 try { |
|
4882 // We don't really need to store this value since we never |
|
4883 // use it again, but if we don't store it, Closure Compiler |
|
4884 // assumes the code is useless and removes it. |
|
4885 hasRules = !!req.node.sheet.cssRules; |
|
4886 |
|
4887 // If we get here, the stylesheet has loaded. |
|
4888 pendingCSS.splice(i, 1); |
|
4889 i -= 1; |
|
4890 self._progress(null, req); |
|
4891 } catch (ex) { |
|
4892 // An exception means the stylesheet is still loading. |
|
4893 } |
|
4894 } |
|
4895 } |
|
4896 |
|
4897 if (pendingCSS.length) { |
|
4898 self._pollTimer = setTimeout(function () { |
|
4899 self._poll.call(self); |
|
4900 }, self.options.pollInterval); |
|
4901 } |
|
4902 }, |
|
4903 |
|
4904 _progress: function (err, req) { |
|
4905 var options = this.options; |
|
4906 |
|
4907 if (err) { |
|
4908 req.error = err; |
|
4909 |
|
4910 this.errors.push({ |
|
4911 error : err, |
|
4912 request: req |
|
4913 }); |
|
4914 |
|
4915 } |
|
4916 |
|
4917 req.node._yuiget_finished = req.finished = true; |
|
4918 |
|
4919 if (options.onProgress) { |
|
4920 options.onProgress.call(options.context || this, |
|
4921 this._getEventData(req)); |
|
4922 } |
|
4923 |
|
4924 if (req.autopurge) { |
|
4925 // Pre-3.5.0 Get always excludes the most recent node from an |
|
4926 // autopurge. I find this odd, but I'm keeping that behavior for |
|
4927 // the sake of backcompat. |
|
4928 Get._autoPurge(this.options.purgethreshold); |
|
4929 Get._purgeNodes.push(req.node); |
|
4930 } |
|
4931 |
|
4932 if (this._pending === req) { |
|
4933 this._pending = null; |
|
4934 } |
|
4935 |
|
4936 this._reqsWaiting -= 1; |
|
4937 |
|
4938 this._next(); |
|
4939 } |
|
4940 }; |
|
4941 |
|
4942 |
|
4943 }, '@VERSION@', {"requires": ["yui-base"]}); |
|
4944 YUI.add('features', function (Y, NAME) { |
|
4945 |
|
4946 var feature_tests = {}; |
|
4947 |
|
4948 /** |
|
4949 Contains the core of YUI's feature test architecture. |
|
4950 @module features |
|
4951 */ |
|
4952 |
|
4953 /** |
|
4954 * Feature detection |
|
4955 * @class Features |
|
4956 * @static |
|
4957 */ |
|
4958 |
|
4959 Y.mix(Y.namespace('Features'), { |
|
4960 |
|
4961 /** |
|
4962 * Object hash of all registered feature tests |
|
4963 * @property tests |
|
4964 * @type Object |
|
4965 */ |
|
4966 tests: feature_tests, |
|
4967 |
|
4968 /** |
|
4969 * Add a test to the system |
|
4970 * |
|
4971 * ``` |
|
4972 * Y.Features.add("load", "1", {}); |
|
4973 * ``` |
|
4974 * |
|
4975 * @method add |
|
4976 * @param {String} cat The category, right now only 'load' is supported |
|
4977 * @param {String} name The number sequence of the test, how it's reported in the URL or config: 1, 2, 3 |
|
4978 * @param {Object} o Object containing test properties |
|
4979 * @param {String} o.name The name of the test |
|
4980 * @param {Function} o.test The test function to execute, the only argument to the function is the `Y` instance |
|
4981 * @param {String} o.trigger The module that triggers this test. |
|
4982 */ |
|
4983 add: function(cat, name, o) { |
|
4984 feature_tests[cat] = feature_tests[cat] || {}; |
|
4985 feature_tests[cat][name] = o; |
|
4986 }, |
|
4987 /** |
|
4988 * Execute all tests of a given category and return the serialized results |
|
4989 * |
|
4990 * ``` |
|
4991 * caps=1:1;2:1;3:0 |
|
4992 * ``` |
|
4993 * @method all |
|
4994 * @param {String} cat The category to execute |
|
4995 * @param {Array} args The arguments to pass to the test function |
|
4996 * @return {String} A semi-colon separated string of tests and their success/failure: 1:1;2:1;3:0 |
|
4997 */ |
|
4998 all: function(cat, args) { |
|
4999 var cat_o = feature_tests[cat], |
|
5000 // results = {}; |
|
5001 result = []; |
|
5002 if (cat_o) { |
|
5003 Y.Object.each(cat_o, function(v, k) { |
|
5004 result.push(k + ':' + (Y.Features.test(cat, k, args) ? 1 : 0)); |
|
5005 }); |
|
5006 } |
|
5007 |
|
5008 return (result.length) ? result.join(';') : ''; |
|
5009 }, |
|
5010 /** |
|
5011 * Run a sepecific test and return a Boolean response. |
|
5012 * |
|
5013 * ``` |
|
5014 * Y.Features.test("load", "1"); |
|
5015 * ``` |
|
5016 * |
|
5017 * @method test |
|
5018 * @param {String} cat The category of the test to run |
|
5019 * @param {String} name The name of the test to run |
|
5020 * @param {Array} args The arguments to pass to the test function |
|
5021 * @return {Boolean} True or false if the test passed/failed. |
|
5022 */ |
|
5023 test: function(cat, name, args) { |
|
5024 args = args || []; |
|
5025 var result, ua, test, |
|
5026 cat_o = feature_tests[cat], |
|
5027 feature = cat_o && cat_o[name]; |
|
5028 |
|
5029 if (!feature) { |
|
5030 } else { |
|
5031 |
|
5032 result = feature.result; |
|
5033 |
|
5034 if (Y.Lang.isUndefined(result)) { |
|
5035 |
|
5036 ua = feature.ua; |
|
5037 if (ua) { |
|
5038 result = (Y.UA[ua]); |
|
5039 } |
|
5040 |
|
5041 test = feature.test; |
|
5042 if (test && ((!ua) || result)) { |
|
5043 result = test.apply(Y, args); |
|
5044 } |
|
5045 |
|
5046 feature.result = result; |
|
5047 } |
|
5048 } |
|
5049 |
|
5050 return result; |
|
5051 } |
|
5052 }); |
|
5053 |
|
5054 // Y.Features.add("load", "1", {}); |
|
5055 // Y.Features.test("load", "1"); |
|
5056 // caps=1:1;2:0;3:1; |
|
5057 |
|
5058 /* This file is auto-generated by (yogi loader --yes --mix --start ../) */ |
|
5059 /*jshint maxlen:900, eqeqeq: false */ |
|
5060 var add = Y.Features.add; |
|
5061 // app-transitions-native |
|
5062 add('load', '0', { |
|
5063 "name": "app-transitions-native", |
|
5064 "test": function (Y) { |
|
5065 var doc = Y.config.doc, |
|
5066 node = doc ? doc.documentElement : null; |
|
5067 |
|
5068 if (node && node.style) { |
|
5069 return ('MozTransition' in node.style || 'WebkitTransition' in node.style || 'transition' in node.style); |
|
5070 } |
|
5071 |
|
5072 return false; |
|
5073 }, |
|
5074 "trigger": "app-transitions" |
|
5075 }); |
|
5076 // autocomplete-list-keys |
|
5077 add('load', '1', { |
|
5078 "name": "autocomplete-list-keys", |
|
5079 "test": function (Y) { |
|
5080 // Only add keyboard support to autocomplete-list if this doesn't appear to |
|
5081 // be an iOS or Android-based mobile device. |
|
5082 // |
|
5083 // There's currently no feasible way to actually detect whether a device has |
|
5084 // a hardware keyboard, so this sniff will have to do. It can easily be |
|
5085 // overridden by manually loading the autocomplete-list-keys module. |
|
5086 // |
|
5087 // Worth noting: even though iOS supports bluetooth keyboards, Mobile Safari |
|
5088 // doesn't fire the keyboard events used by AutoCompleteList, so there's |
|
5089 // no point loading the -keys module even when a bluetooth keyboard may be |
|
5090 // available. |
|
5091 return !(Y.UA.ios || Y.UA.android); |
|
5092 }, |
|
5093 "trigger": "autocomplete-list" |
|
5094 }); |
|
5095 // dd-gestures |
|
5096 add('load', '2', { |
|
5097 "name": "dd-gestures", |
|
5098 "trigger": "dd-drag", |
|
5099 "ua": "touchEnabled" |
|
5100 }); |
|
5101 // dom-style-ie |
|
5102 add('load', '3', { |
|
5103 "name": "dom-style-ie", |
|
5104 "test": function (Y) { |
|
5105 |
|
5106 var testFeature = Y.Features.test, |
|
5107 addFeature = Y.Features.add, |
|
5108 WINDOW = Y.config.win, |
|
5109 DOCUMENT = Y.config.doc, |
|
5110 DOCUMENT_ELEMENT = 'documentElement', |
|
5111 ret = false; |
|
5112 |
|
5113 addFeature('style', 'computedStyle', { |
|
5114 test: function() { |
|
5115 return WINDOW && 'getComputedStyle' in WINDOW; |
|
5116 } |
|
5117 }); |
|
5118 |
|
5119 addFeature('style', 'opacity', { |
|
5120 test: function() { |
|
5121 return DOCUMENT && 'opacity' in DOCUMENT[DOCUMENT_ELEMENT].style; |
|
5122 } |
|
5123 }); |
|
5124 |
|
5125 ret = (!testFeature('style', 'opacity') && |
|
5126 !testFeature('style', 'computedStyle')); |
|
5127 |
|
5128 return ret; |
|
5129 }, |
|
5130 "trigger": "dom-style" |
|
5131 }); |
|
5132 // editor-para-ie |
|
5133 add('load', '4', { |
|
5134 "name": "editor-para-ie", |
|
5135 "trigger": "editor-para", |
|
5136 "ua": "ie", |
|
5137 "when": "instead" |
|
5138 }); |
|
5139 // event-base-ie |
|
5140 add('load', '5', { |
|
5141 "name": "event-base-ie", |
|
5142 "test": function(Y) { |
|
5143 var imp = Y.config.doc && Y.config.doc.implementation; |
|
5144 return (imp && (!imp.hasFeature('Events', '2.0'))); |
|
5145 }, |
|
5146 "trigger": "node-base" |
|
5147 }); |
|
5148 // graphics-canvas |
|
5149 add('load', '6', { |
|
5150 "name": "graphics-canvas", |
|
5151 "test": function(Y) { |
|
5152 var DOCUMENT = Y.config.doc, |
|
5153 useCanvas = Y.config.defaultGraphicEngine && Y.config.defaultGraphicEngine == "canvas", |
|
5154 canvas = DOCUMENT && DOCUMENT.createElement("canvas"), |
|
5155 svg = (DOCUMENT && DOCUMENT.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1")); |
|
5156 return (!svg || useCanvas) && (canvas && canvas.getContext && canvas.getContext("2d")); |
|
5157 }, |
|
5158 "trigger": "graphics" |
|
5159 }); |
|
5160 // graphics-canvas-default |
|
5161 add('load', '7', { |
|
5162 "name": "graphics-canvas-default", |
|
5163 "test": function(Y) { |
|
5164 var DOCUMENT = Y.config.doc, |
|
5165 useCanvas = Y.config.defaultGraphicEngine && Y.config.defaultGraphicEngine == "canvas", |
|
5166 canvas = DOCUMENT && DOCUMENT.createElement("canvas"), |
|
5167 svg = (DOCUMENT && DOCUMENT.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1")); |
|
5168 return (!svg || useCanvas) && (canvas && canvas.getContext && canvas.getContext("2d")); |
|
5169 }, |
|
5170 "trigger": "graphics" |
|
5171 }); |
|
5172 // graphics-svg |
|
5173 add('load', '8', { |
|
5174 "name": "graphics-svg", |
|
5175 "test": function(Y) { |
|
5176 var DOCUMENT = Y.config.doc, |
|
5177 useSVG = !Y.config.defaultGraphicEngine || Y.config.defaultGraphicEngine != "canvas", |
|
5178 canvas = DOCUMENT && DOCUMENT.createElement("canvas"), |
|
5179 svg = (DOCUMENT && DOCUMENT.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1")); |
|
5180 |
|
5181 return svg && (useSVG || !canvas); |
|
5182 }, |
|
5183 "trigger": "graphics" |
|
5184 }); |
|
5185 // graphics-svg-default |
|
5186 add('load', '9', { |
|
5187 "name": "graphics-svg-default", |
|
5188 "test": function(Y) { |
|
5189 var DOCUMENT = Y.config.doc, |
|
5190 useSVG = !Y.config.defaultGraphicEngine || Y.config.defaultGraphicEngine != "canvas", |
|
5191 canvas = DOCUMENT && DOCUMENT.createElement("canvas"), |
|
5192 svg = (DOCUMENT && DOCUMENT.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1")); |
|
5193 |
|
5194 return svg && (useSVG || !canvas); |
|
5195 }, |
|
5196 "trigger": "graphics" |
|
5197 }); |
|
5198 // graphics-vml |
|
5199 add('load', '10', { |
|
5200 "name": "graphics-vml", |
|
5201 "test": function(Y) { |
|
5202 var DOCUMENT = Y.config.doc, |
|
5203 canvas = DOCUMENT && DOCUMENT.createElement("canvas"); |
|
5204 return (DOCUMENT && !DOCUMENT.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1") && (!canvas || !canvas.getContext || !canvas.getContext("2d"))); |
|
5205 }, |
|
5206 "trigger": "graphics" |
|
5207 }); |
|
5208 // graphics-vml-default |
|
5209 add('load', '11', { |
|
5210 "name": "graphics-vml-default", |
|
5211 "test": function(Y) { |
|
5212 var DOCUMENT = Y.config.doc, |
|
5213 canvas = DOCUMENT && DOCUMENT.createElement("canvas"); |
|
5214 return (DOCUMENT && !DOCUMENT.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1") && (!canvas || !canvas.getContext || !canvas.getContext("2d"))); |
|
5215 }, |
|
5216 "trigger": "graphics" |
|
5217 }); |
|
5218 // history-hash-ie |
|
5219 add('load', '12', { |
|
5220 "name": "history-hash-ie", |
|
5221 "test": function (Y) { |
|
5222 var docMode = Y.config.doc && Y.config.doc.documentMode; |
|
5223 |
|
5224 return Y.UA.ie && (!('onhashchange' in Y.config.win) || |
|
5225 !docMode || docMode < 8); |
|
5226 }, |
|
5227 "trigger": "history-hash" |
|
5228 }); |
|
5229 // io-nodejs |
|
5230 add('load', '13', { |
|
5231 "name": "io-nodejs", |
|
5232 "trigger": "io-base", |
|
5233 "ua": "nodejs" |
|
5234 }); |
|
5235 // json-parse-shim |
|
5236 add('load', '14', { |
|
5237 "name": "json-parse-shim", |
|
5238 "test": function (Y) { |
|
5239 var _JSON = Y.config.global.JSON, |
|
5240 Native = Object.prototype.toString.call(_JSON) === '[object JSON]' && _JSON, |
|
5241 nativeSupport = Y.config.useNativeJSONParse !== false && !!Native; |
|
5242 |
|
5243 function workingNative( k, v ) { |
|
5244 return k === "ok" ? true : v; |
|
5245 } |
|
5246 |
|
5247 // Double check basic functionality. This is mainly to catch early broken |
|
5248 // implementations of the JSON API in Firefox 3.1 beta1 and beta2 |
|
5249 if ( nativeSupport ) { |
|
5250 try { |
|
5251 nativeSupport = ( Native.parse( '{"ok":false}', workingNative ) ).ok; |
|
5252 } |
|
5253 catch ( e ) { |
|
5254 nativeSupport = false; |
|
5255 } |
|
5256 } |
|
5257 |
|
5258 return !nativeSupport; |
|
5259 }, |
|
5260 "trigger": "json-parse" |
|
5261 }); |
|
5262 // json-stringify-shim |
|
5263 add('load', '15', { |
|
5264 "name": "json-stringify-shim", |
|
5265 "test": function (Y) { |
|
5266 var _JSON = Y.config.global.JSON, |
|
5267 Native = Object.prototype.toString.call(_JSON) === '[object JSON]' && _JSON, |
|
5268 nativeSupport = Y.config.useNativeJSONStringify !== false && !!Native; |
|
5269 |
|
5270 // Double check basic native functionality. This is primarily to catch broken |
|
5271 // early JSON API implementations in Firefox 3.1 beta1 and beta2. |
|
5272 if ( nativeSupport ) { |
|
5273 try { |
|
5274 nativeSupport = ( '0' === Native.stringify(0) ); |
|
5275 } catch ( e ) { |
|
5276 nativeSupport = false; |
|
5277 } |
|
5278 } |
|
5279 |
|
5280 |
|
5281 return !nativeSupport; |
|
5282 }, |
|
5283 "trigger": "json-stringify" |
|
5284 }); |
|
5285 // scrollview-base-ie |
|
5286 add('load', '16', { |
|
5287 "name": "scrollview-base-ie", |
|
5288 "trigger": "scrollview-base", |
|
5289 "ua": "ie" |
|
5290 }); |
|
5291 // selector-css2 |
|
5292 add('load', '17', { |
|
5293 "name": "selector-css2", |
|
5294 "test": function (Y) { |
|
5295 var DOCUMENT = Y.config.doc, |
|
5296 ret = DOCUMENT && !('querySelectorAll' in DOCUMENT); |
|
5297 |
|
5298 return ret; |
|
5299 }, |
|
5300 "trigger": "selector" |
|
5301 }); |
|
5302 // transition-timer |
|
5303 add('load', '18', { |
|
5304 "name": "transition-timer", |
|
5305 "test": function (Y) { |
|
5306 var DOCUMENT = Y.config.doc, |
|
5307 node = (DOCUMENT) ? DOCUMENT.documentElement: null, |
|
5308 ret = true; |
|
5309 |
|
5310 if (node && node.style) { |
|
5311 ret = !('MozTransition' in node.style || 'WebkitTransition' in node.style || 'transition' in node.style); |
|
5312 } |
|
5313 |
|
5314 return ret; |
|
5315 }, |
|
5316 "trigger": "transition" |
|
5317 }); |
|
5318 // widget-base-ie |
|
5319 add('load', '19', { |
|
5320 "name": "widget-base-ie", |
|
5321 "trigger": "widget-base", |
|
5322 "ua": "ie" |
|
5323 }); |
|
5324 // yql-jsonp |
|
5325 add('load', '20', { |
|
5326 "name": "yql-jsonp", |
|
5327 "test": function (Y) { |
|
5328 /* Only load the JSONP module when not in nodejs or winjs |
|
5329 TODO Make the winjs module a CORS module |
|
5330 */ |
|
5331 return (!Y.UA.nodejs && !Y.UA.winjs); |
|
5332 }, |
|
5333 "trigger": "yql", |
|
5334 "when": "after" |
|
5335 }); |
|
5336 // yql-nodejs |
|
5337 add('load', '21', { |
|
5338 "name": "yql-nodejs", |
|
5339 "trigger": "yql", |
|
5340 "ua": "nodejs", |
|
5341 "when": "after" |
|
5342 }); |
|
5343 // yql-winjs |
|
5344 add('load', '22', { |
|
5345 "name": "yql-winjs", |
|
5346 "trigger": "yql", |
|
5347 "ua": "winjs", |
|
5348 "when": "after" |
|
5349 }); |
|
5350 |
|
5351 }, '@VERSION@', {"requires": ["yui-base"]}); |
|
5352 YUI.add('intl-base', function (Y, NAME) { |
|
5353 |
|
5354 /** |
|
5355 * The Intl utility provides a central location for managing sets of |
|
5356 * localized resources (strings and formatting patterns). |
|
5357 * |
|
5358 * @class Intl |
|
5359 * @uses EventTarget |
|
5360 * @static |
|
5361 */ |
|
5362 |
|
5363 var SPLIT_REGEX = /[, ]/; |
|
5364 |
|
5365 Y.mix(Y.namespace('Intl'), { |
|
5366 |
|
5367 /** |
|
5368 * Returns the language among those available that |
|
5369 * best matches the preferred language list, using the Lookup |
|
5370 * algorithm of BCP 47. |
|
5371 * If none of the available languages meets the user's preferences, |
|
5372 * then "" is returned. |
|
5373 * Extended language ranges are not supported. |
|
5374 * |
|
5375 * @method lookupBestLang |
|
5376 * @param {String[] | String} preferredLanguages The list of preferred |
|
5377 * languages in descending preference order, represented as BCP 47 |
|
5378 * language tags. A string array or a comma-separated list. |
|
5379 * @param {String[]} availableLanguages The list of languages |
|
5380 * that the application supports, represented as BCP 47 language |
|
5381 * tags. |
|
5382 * |
|
5383 * @return {String} The available language that best matches the |
|
5384 * preferred language list, or "". |
|
5385 * @since 3.1.0 |
|
5386 */ |
|
5387 lookupBestLang: function(preferredLanguages, availableLanguages) { |
|
5388 |
|
5389 var i, language, result, index; |
|
5390 |
|
5391 // check whether the list of available languages contains language; |
|
5392 // if so return it |
|
5393 function scan(language) { |
|
5394 var i; |
|
5395 for (i = 0; i < availableLanguages.length; i += 1) { |
|
5396 if (language.toLowerCase() === |
|
5397 availableLanguages[i].toLowerCase()) { |
|
5398 return availableLanguages[i]; |
|
5399 } |
|
5400 } |
|
5401 } |
|
5402 |
|
5403 if (Y.Lang.isString(preferredLanguages)) { |
|
5404 preferredLanguages = preferredLanguages.split(SPLIT_REGEX); |
|
5405 } |
|
5406 |
|
5407 for (i = 0; i < preferredLanguages.length; i += 1) { |
|
5408 language = preferredLanguages[i]; |
|
5409 if (!language || language === '*') { |
|
5410 continue; |
|
5411 } |
|
5412 // check the fallback sequence for one language |
|
5413 while (language.length > 0) { |
|
5414 result = scan(language); |
|
5415 if (result) { |
|
5416 return result; |
|
5417 } else { |
|
5418 index = language.lastIndexOf('-'); |
|
5419 if (index >= 0) { |
|
5420 language = language.substring(0, index); |
|
5421 // one-character subtags get cut along with the |
|
5422 // following subtag |
|
5423 if (index >= 2 && language.charAt(index - 2) === '-') { |
|
5424 language = language.substring(0, index - 2); |
|
5425 } |
|
5426 } else { |
|
5427 // nothing available for this language |
|
5428 break; |
|
5429 } |
|
5430 } |
|
5431 } |
|
5432 } |
|
5433 |
|
5434 return ''; |
|
5435 } |
|
5436 }); |
|
5437 |
|
5438 |
|
5439 }, '@VERSION@', {"requires": ["yui-base"]}); |
|
5440 YUI.add('yui-log', function (Y, NAME) { |
|
5441 |
|
5442 /** |
|
5443 * Provides console log capability and exposes a custom event for |
|
5444 * console implementations. This module is a `core` YUI module, |
|
5445 * <a href="../classes/YUI.html#method_log">it's documentation is located under the YUI class</a>. |
|
5446 * |
|
5447 * @module yui |
|
5448 * @submodule yui-log |
|
5449 */ |
|
5450 |
|
5451 var INSTANCE = Y, |
|
5452 LOGEVENT = 'yui:log', |
|
5453 UNDEFINED = 'undefined', |
|
5454 LEVELS = { debug: 1, |
|
5455 info: 2, |
|
5456 warn: 4, |
|
5457 error: 8 }; |
|
5458 |
|
5459 /** |
|
5460 * If the 'debug' config is true, a 'yui:log' event will be |
|
5461 * dispatched, which the Console widget and anything else |
|
5462 * can consume. If the 'useBrowserConsole' config is true, it will |
|
5463 * write to the browser console if available. YUI-specific log |
|
5464 * messages will only be present in the -debug versions of the |
|
5465 * JS files. The build system is supposed to remove log statements |
|
5466 * from the raw and minified versions of the files. |
|
5467 * |
|
5468 * @method log |
|
5469 * @for YUI |
|
5470 * @param {String} msg The message to log. |
|
5471 * @param {String} cat The log category for the message. Default |
|
5472 * categories are "info", "warn", "error", time". |
|
5473 * Custom categories can be used as well. (opt). |
|
5474 * @param {String} src The source of the the message (opt). |
|
5475 * @param {boolean} silent If true, the log event won't fire. |
|
5476 * @return {YUI} YUI instance. |
|
5477 */ |
|
5478 INSTANCE.log = function(msg, cat, src, silent) { |
|
5479 var bail, excl, incl, m, f, minlevel, |
|
5480 Y = INSTANCE, |
|
5481 c = Y.config, |
|
5482 publisher = (Y.fire) ? Y : YUI.Env.globalEvents; |
|
5483 // suppress log message if the config is off or the event stack |
|
5484 // or the event call stack contains a consumer of the yui:log event |
|
5485 if (c.debug) { |
|
5486 // apply source filters |
|
5487 src = src || ""; |
|
5488 if (typeof src !== "undefined") { |
|
5489 excl = c.logExclude; |
|
5490 incl = c.logInclude; |
|
5491 if (incl && !(src in incl)) { |
|
5492 bail = 1; |
|
5493 } else if (incl && (src in incl)) { |
|
5494 bail = !incl[src]; |
|
5495 } else if (excl && (src in excl)) { |
|
5496 bail = excl[src]; |
|
5497 } |
|
5498 |
|
5499 // Determine the current minlevel as defined in configuration |
|
5500 Y.config.logLevel = Y.config.logLevel || 'debug'; |
|
5501 minlevel = LEVELS[Y.config.logLevel.toLowerCase()]; |
|
5502 |
|
5503 if (cat in LEVELS && LEVELS[cat] < minlevel) { |
|
5504 // Skip this message if the we don't meet the defined minlevel |
|
5505 bail = 1; |
|
5506 } |
|
5507 } |
|
5508 if (!bail) { |
|
5509 if (c.useBrowserConsole) { |
|
5510 m = (src) ? src + ': ' + msg : msg; |
|
5511 if (Y.Lang.isFunction(c.logFn)) { |
|
5512 c.logFn.call(Y, msg, cat, src); |
|
5513 } else if (typeof console !== UNDEFINED && console.log) { |
|
5514 f = (cat && console[cat] && (cat in LEVELS)) ? cat : 'log'; |
|
5515 console[f](m); |
|
5516 } else if (typeof opera !== UNDEFINED) { |
|
5517 opera.postError(m); |
|
5518 } |
|
5519 } |
|
5520 |
|
5521 if (publisher && !silent) { |
|
5522 |
|
5523 if (publisher === Y && (!publisher.getEvent(LOGEVENT))) { |
|
5524 publisher.publish(LOGEVENT, { |
|
5525 broadcast: 2 |
|
5526 }); |
|
5527 } |
|
5528 |
|
5529 publisher.fire(LOGEVENT, { |
|
5530 msg: msg, |
|
5531 cat: cat, |
|
5532 src: src |
|
5533 }); |
|
5534 } |
|
5535 } |
|
5536 } |
|
5537 |
|
5538 return Y; |
|
5539 }; |
|
5540 |
|
5541 /** |
|
5542 * Write a system message. This message will be preserved in the |
|
5543 * minified and raw versions of the YUI files, unlike log statements. |
|
5544 * @method message |
|
5545 * @for YUI |
|
5546 * @param {String} msg The message to log. |
|
5547 * @param {String} cat The log category for the message. Default |
|
5548 * categories are "info", "warn", "error", time". |
|
5549 * Custom categories can be used as well. (opt). |
|
5550 * @param {String} src The source of the the message (opt). |
|
5551 * @param {boolean} silent If true, the log event won't fire. |
|
5552 * @return {YUI} YUI instance. |
|
5553 */ |
|
5554 INSTANCE.message = function() { |
|
5555 return INSTANCE.log.apply(INSTANCE, arguments); |
|
5556 }; |
|
5557 |
|
5558 |
|
5559 }, '@VERSION@', {"requires": ["yui-base"]}); |
|
5560 YUI.add('yui-later', function (Y, NAME) { |
|
5561 |
|
5562 /** |
|
5563 * Provides a setTimeout/setInterval wrapper. This module is a `core` YUI module, |
|
5564 * <a href="../classes/YUI.html#method_later">it's documentation is located under the YUI class</a>. |
|
5565 * |
|
5566 * @module yui |
|
5567 * @submodule yui-later |
|
5568 */ |
|
5569 |
|
5570 var NO_ARGS = []; |
|
5571 |
|
5572 /** |
|
5573 * Executes the supplied function in the context of the supplied |
|
5574 * object 'when' milliseconds later. Executes the function a |
|
5575 * single time unless periodic is set to true. |
|
5576 * @for YUI |
|
5577 * @method later |
|
5578 * @param when {int} the number of milliseconds to wait until the fn |
|
5579 * is executed. |
|
5580 * @param o the context object. |
|
5581 * @param fn {Function|String} the function to execute or the name of |
|
5582 * the method in the 'o' object to execute. |
|
5583 * @param data [Array] data that is provided to the function. This |
|
5584 * accepts either a single item or an array. If an array is provided, |
|
5585 * the function is executed with one parameter for each array item. |
|
5586 * If you need to pass a single array parameter, it needs to be wrapped |
|
5587 * in an array [myarray]. |
|
5588 * |
|
5589 * Note: native methods in IE may not have the call and apply methods. |
|
5590 * In this case, it will work, but you are limited to four arguments. |
|
5591 * |
|
5592 * @param periodic {boolean} if true, executes continuously at supplied |
|
5593 * interval until canceled. |
|
5594 * @return {object} a timer object. Call the cancel() method on this |
|
5595 * object to stop the timer. |
|
5596 */ |
|
5597 Y.later = function(when, o, fn, data, periodic) { |
|
5598 when = when || 0; |
|
5599 data = (!Y.Lang.isUndefined(data)) ? Y.Array(data) : NO_ARGS; |
|
5600 o = o || Y.config.win || Y; |
|
5601 |
|
5602 var cancelled = false, |
|
5603 method = (o && Y.Lang.isString(fn)) ? o[fn] : fn, |
|
5604 wrapper = function() { |
|
5605 // IE 8- may execute a setInterval callback one last time |
|
5606 // after clearInterval was called, so in order to preserve |
|
5607 // the cancel() === no more runny-run, we have to jump through |
|
5608 // an extra hoop. |
|
5609 if (!cancelled) { |
|
5610 if (!method.apply) { |
|
5611 method(data[0], data[1], data[2], data[3]); |
|
5612 } else { |
|
5613 method.apply(o, data || NO_ARGS); |
|
5614 } |
|
5615 } |
|
5616 }, |
|
5617 id = (periodic) ? setInterval(wrapper, when) : setTimeout(wrapper, when); |
|
5618 |
|
5619 return { |
|
5620 id: id, |
|
5621 interval: periodic, |
|
5622 cancel: function() { |
|
5623 cancelled = true; |
|
5624 if (this.interval) { |
|
5625 clearInterval(id); |
|
5626 } else { |
|
5627 clearTimeout(id); |
|
5628 } |
|
5629 } |
|
5630 }; |
|
5631 }; |
|
5632 |
|
5633 Y.Lang.later = Y.later; |
|
5634 |
|
5635 |
|
5636 |
|
5637 }, '@VERSION@', {"requires": ["yui-base"]}); |
|
5638 YUI.add('yui', function (Y, NAME) {}, '@VERSION@', {"use": ["get", "features", "intl-base", "yui-log", "yui-later"]}); |
|
5639 YUI.add('oop', function (Y, NAME) { |
|
5640 |
|
5641 /** |
|
5642 Adds object inheritance and manipulation utilities to the YUI instance. This |
|
5643 module is required by most YUI components. |
|
5644 |
|
5645 @module oop |
|
5646 **/ |
|
5647 |
|
5648 var L = Y.Lang, |
|
5649 A = Y.Array, |
|
5650 OP = Object.prototype, |
|
5651 CLONE_MARKER = '_~yuim~_', |
|
5652 |
|
5653 hasOwn = OP.hasOwnProperty, |
|
5654 toString = OP.toString; |
|
5655 |
|
5656 function dispatch(o, f, c, proto, action) { |
|
5657 if (o && o[action] && o !== Y) { |
|
5658 return o[action].call(o, f, c); |
|
5659 } else { |
|
5660 switch (A.test(o)) { |
|
5661 case 1: |
|
5662 return A[action](o, f, c); |
|
5663 case 2: |
|
5664 return A[action](Y.Array(o, 0, true), f, c); |
|
5665 default: |
|
5666 return Y.Object[action](o, f, c, proto); |
|
5667 } |
|
5668 } |
|
5669 } |
|
5670 |
|
5671 /** |
|
5672 Augments the _receiver_ with prototype properties from the _supplier_. The |
|
5673 receiver may be a constructor function or an object. The supplier must be a |
|
5674 constructor function. |
|
5675 |
|
5676 If the _receiver_ is an object, then the _supplier_ constructor will be called |
|
5677 immediately after _receiver_ is augmented, with _receiver_ as the `this` object. |
|
5678 |
|
5679 If the _receiver_ is a constructor function, then all prototype methods of |
|
5680 _supplier_ that are copied to _receiver_ will be sequestered, and the |
|
5681 _supplier_ constructor will not be called immediately. The first time any |
|
5682 sequestered method is called on the _receiver_'s prototype, all sequestered |
|
5683 methods will be immediately copied to the _receiver_'s prototype, the |
|
5684 _supplier_'s constructor will be executed, and finally the newly unsequestered |
|
5685 method that was called will be executed. |
|
5686 |
|
5687 This sequestering logic sounds like a bunch of complicated voodoo, but it makes |
|
5688 it cheap to perform frequent augmentation by ensuring that suppliers' |
|
5689 constructors are only called if a supplied method is actually used. If none of |
|
5690 the supplied methods is ever used, then there's no need to take the performance |
|
5691 hit of calling the _supplier_'s constructor. |
|
5692 |
|
5693 @method augment |
|
5694 @param {Function|Object} receiver Object or function to be augmented. |
|
5695 @param {Function} supplier Function that supplies the prototype properties with |
|
5696 which to augment the _receiver_. |
|
5697 @param {Boolean} [overwrite=false] If `true`, properties already on the receiver |
|
5698 will be overwritten if found on the supplier's prototype. |
|
5699 @param {String[]} [whitelist] An array of property names. If specified, |
|
5700 only the whitelisted prototype properties will be applied to the receiver, and |
|
5701 all others will be ignored. |
|
5702 @param {Array|any} [args] Argument or array of arguments to pass to the |
|
5703 supplier's constructor when initializing. |
|
5704 @return {Function} Augmented object. |
|
5705 @for YUI |
|
5706 **/ |
|
5707 Y.augment = function (receiver, supplier, overwrite, whitelist, args) { |
|
5708 var rProto = receiver.prototype, |
|
5709 sequester = rProto && supplier, |
|
5710 sProto = supplier.prototype, |
|
5711 to = rProto || receiver, |
|
5712 |
|
5713 copy, |
|
5714 newPrototype, |
|
5715 replacements, |
|
5716 sequestered, |
|
5717 unsequester; |
|
5718 |
|
5719 args = args ? Y.Array(args) : []; |
|
5720 |
|
5721 if (sequester) { |
|
5722 newPrototype = {}; |
|
5723 replacements = {}; |
|
5724 sequestered = {}; |
|
5725 |
|
5726 copy = function (value, key) { |
|
5727 if (overwrite || !(key in rProto)) { |
|
5728 if (toString.call(value) === '[object Function]') { |
|
5729 sequestered[key] = value; |
|
5730 |
|
5731 newPrototype[key] = replacements[key] = function () { |
|
5732 return unsequester(this, value, arguments); |
|
5733 }; |
|
5734 } else { |
|
5735 newPrototype[key] = value; |
|
5736 } |
|
5737 } |
|
5738 }; |
|
5739 |
|
5740 unsequester = function (instance, fn, fnArgs) { |
|
5741 // Unsequester all sequestered functions. |
|
5742 for (var key in sequestered) { |
|
5743 if (hasOwn.call(sequestered, key) |
|
5744 && instance[key] === replacements[key]) { |
|
5745 |
|
5746 instance[key] = sequestered[key]; |
|
5747 } |
|
5748 } |
|
5749 |
|
5750 // Execute the supplier constructor. |
|
5751 supplier.apply(instance, args); |
|
5752 |
|
5753 // Finally, execute the original sequestered function. |
|
5754 return fn.apply(instance, fnArgs); |
|
5755 }; |
|
5756 |
|
5757 if (whitelist) { |
|
5758 Y.Array.each(whitelist, function (name) { |
|
5759 if (name in sProto) { |
|
5760 copy(sProto[name], name); |
|
5761 } |
|
5762 }); |
|
5763 } else { |
|
5764 Y.Object.each(sProto, copy, null, true); |
|
5765 } |
|
5766 } |
|
5767 |
|
5768 Y.mix(to, newPrototype || sProto, overwrite, whitelist); |
|
5769 |
|
5770 if (!sequester) { |
|
5771 supplier.apply(to, args); |
|
5772 } |
|
5773 |
|
5774 return receiver; |
|
5775 }; |
|
5776 |
|
5777 /** |
|
5778 * Copies object properties from the supplier to the receiver. If the target has |
|
5779 * the property, and the property is an object, the target object will be |
|
5780 * augmented with the supplier's value. |
|
5781 * |
|
5782 * @method aggregate |
|
5783 * @param {Object} receiver Object to receive the augmentation. |
|
5784 * @param {Object} supplier Object that supplies the properties with which to |
|
5785 * augment the receiver. |
|
5786 * @param {Boolean} [overwrite=false] If `true`, properties already on the receiver |
|
5787 * will be overwritten if found on the supplier. |
|
5788 * @param {String[]} [whitelist] Whitelist. If supplied, only properties in this |
|
5789 * list will be applied to the receiver. |
|
5790 * @return {Object} Augmented object. |
|
5791 */ |
|
5792 Y.aggregate = function(r, s, ov, wl) { |
|
5793 return Y.mix(r, s, ov, wl, 0, true); |
|
5794 }; |
|
5795 |
|
5796 /** |
|
5797 * Utility to set up the prototype, constructor and superclass properties to |
|
5798 * support an inheritance strategy that can chain constructors and methods. |
|
5799 * Static members will not be inherited. |
|
5800 * |
|
5801 * @method extend |
|
5802 * @param {function} r the object to modify. |
|
5803 * @param {function} s the object to inherit. |
|
5804 * @param {object} px prototype properties to add/override. |
|
5805 * @param {object} sx static properties to add/override. |
|
5806 * @return {object} the extended object. |
|
5807 */ |
|
5808 Y.extend = function(r, s, px, sx) { |
|
5809 if (!s || !r) { |
|
5810 Y.error('extend failed, verify dependencies'); |
|
5811 } |
|
5812 |
|
5813 var sp = s.prototype, rp = Y.Object(sp); |
|
5814 r.prototype = rp; |
|
5815 |
|
5816 rp.constructor = r; |
|
5817 r.superclass = sp; |
|
5818 |
|
5819 // assign constructor property |
|
5820 if (s != Object && sp.constructor == OP.constructor) { |
|
5821 sp.constructor = s; |
|
5822 } |
|
5823 |
|
5824 // add prototype overrides |
|
5825 if (px) { |
|
5826 Y.mix(rp, px, true); |
|
5827 } |
|
5828 |
|
5829 // add object overrides |
|
5830 if (sx) { |
|
5831 Y.mix(r, sx, true); |
|
5832 } |
|
5833 |
|
5834 return r; |
|
5835 }; |
|
5836 |
|
5837 /** |
|
5838 * Executes the supplied function for each item in |
|
5839 * a collection. Supports arrays, objects, and |
|
5840 * NodeLists |
|
5841 * @method each |
|
5842 * @param {object} o the object to iterate. |
|
5843 * @param {function} f the function to execute. This function |
|
5844 * receives the value, key, and object as parameters. |
|
5845 * @param {object} c the execution context for the function. |
|
5846 * @param {boolean} proto if true, prototype properties are |
|
5847 * iterated on objects. |
|
5848 * @return {YUI} the YUI instance. |
|
5849 */ |
|
5850 Y.each = function(o, f, c, proto) { |
|
5851 return dispatch(o, f, c, proto, 'each'); |
|
5852 }; |
|
5853 |
|
5854 /** |
|
5855 * Executes the supplied function for each item in |
|
5856 * a collection. The operation stops if the function |
|
5857 * returns true. Supports arrays, objects, and |
|
5858 * NodeLists. |
|
5859 * @method some |
|
5860 * @param {object} o the object to iterate. |
|
5861 * @param {function} f the function to execute. This function |
|
5862 * receives the value, key, and object as parameters. |
|
5863 * @param {object} c the execution context for the function. |
|
5864 * @param {boolean} proto if true, prototype properties are |
|
5865 * iterated on objects. |
|
5866 * @return {boolean} true if the function ever returns true, |
|
5867 * false otherwise. |
|
5868 */ |
|
5869 Y.some = function(o, f, c, proto) { |
|
5870 return dispatch(o, f, c, proto, 'some'); |
|
5871 }; |
|
5872 |
|
5873 /** |
|
5874 Deep object/array copy. Function clones are actually wrappers around the |
|
5875 original function. Array-like objects are treated as arrays. Primitives are |
|
5876 returned untouched. Optionally, a function can be provided to handle other data |
|
5877 types, filter keys, validate values, etc. |
|
5878 |
|
5879 **Note:** Cloning a non-trivial object is a reasonably heavy operation, due to |
|
5880 the need to recursively iterate down non-primitive properties. Clone should be |
|
5881 used only when a deep clone down to leaf level properties is explicitly |
|
5882 required. This method will also |
|
5883 |
|
5884 In many cases (for example, when trying to isolate objects used as hashes for |
|
5885 configuration properties), a shallow copy, using `Y.merge()` is normally |
|
5886 sufficient. If more than one level of isolation is required, `Y.merge()` can be |
|
5887 used selectively at each level which needs to be isolated from the original |
|
5888 without going all the way to leaf properties. |
|
5889 |
|
5890 @method clone |
|
5891 @param {object} o what to clone. |
|
5892 @param {boolean} safe if true, objects will not have prototype items from the |
|
5893 source. If false, they will. In this case, the original is initially |
|
5894 protected, but the clone is not completely immune from changes to the source |
|
5895 object prototype. Also, cloned prototype items that are deleted from the |
|
5896 clone will result in the value of the source prototype being exposed. If |
|
5897 operating on a non-safe clone, items should be nulled out rather than |
|
5898 deleted. |
|
5899 @param {function} f optional function to apply to each item in a collection; it |
|
5900 will be executed prior to applying the value to the new object. |
|
5901 Return false to prevent the copy. |
|
5902 @param {object} c optional execution context for f. |
|
5903 @param {object} owner Owner object passed when clone is iterating an object. |
|
5904 Used to set up context for cloned functions. |
|
5905 @param {object} cloned hash of previously cloned objects to avoid multiple |
|
5906 clones. |
|
5907 @return {Array|Object} the cloned object. |
|
5908 **/ |
|
5909 Y.clone = function(o, safe, f, c, owner, cloned) { |
|
5910 var o2, marked, stamp; |
|
5911 |
|
5912 // Does not attempt to clone: |
|
5913 // |
|
5914 // * Non-typeof-object values, "primitive" values don't need cloning. |
|
5915 // |
|
5916 // * YUI instances, cloning complex object like YUI instances is not |
|
5917 // advised, this is like cloning the world. |
|
5918 // |
|
5919 // * DOM nodes (#2528250), common host objects like DOM nodes cannot be |
|
5920 // "subclassed" in Firefox and old versions of IE. Trying to use |
|
5921 // `Object.create()` or `Y.extend()` on a DOM node will throw an error in |
|
5922 // these browsers. |
|
5923 // |
|
5924 // Instad, the passed-in `o` will be return as-is when it matches one of the |
|
5925 // above criteria. |
|
5926 if (!L.isObject(o) || |
|
5927 Y.instanceOf(o, YUI) || |
|
5928 (o.addEventListener || o.attachEvent)) { |
|
5929 |
|
5930 return o; |
|
5931 } |
|
5932 |
|
5933 marked = cloned || {}; |
|
5934 |
|
5935 switch (L.type(o)) { |
|
5936 case 'date': |
|
5937 return new Date(o); |
|
5938 case 'regexp': |
|
5939 // if we do this we need to set the flags too |
|
5940 // return new RegExp(o.source); |
|
5941 return o; |
|
5942 case 'function': |
|
5943 // o2 = Y.bind(o, owner); |
|
5944 // break; |
|
5945 return o; |
|
5946 case 'array': |
|
5947 o2 = []; |
|
5948 break; |
|
5949 default: |
|
5950 |
|
5951 // #2528250 only one clone of a given object should be created. |
|
5952 if (o[CLONE_MARKER]) { |
|
5953 return marked[o[CLONE_MARKER]]; |
|
5954 } |
|
5955 |
|
5956 stamp = Y.guid(); |
|
5957 |
|
5958 o2 = (safe) ? {} : Y.Object(o); |
|
5959 |
|
5960 o[CLONE_MARKER] = stamp; |
|
5961 marked[stamp] = o; |
|
5962 } |
|
5963 |
|
5964 Y.each(o, function(v, k) { |
|
5965 if ((k || k === 0) && (!f || (f.call(c || this, v, k, this, o) !== false))) { |
|
5966 if (k !== CLONE_MARKER) { |
|
5967 if (k == 'prototype') { |
|
5968 // skip the prototype |
|
5969 // } else if (o[k] === o) { |
|
5970 // this[k] = this; |
|
5971 } else { |
|
5972 this[k] = |
|
5973 Y.clone(v, safe, f, c, owner || o, marked); |
|
5974 } |
|
5975 } |
|
5976 } |
|
5977 }, o2); |
|
5978 |
|
5979 if (!cloned) { |
|
5980 Y.Object.each(marked, function(v, k) { |
|
5981 if (v[CLONE_MARKER]) { |
|
5982 try { |
|
5983 delete v[CLONE_MARKER]; |
|
5984 } catch (e) { |
|
5985 v[CLONE_MARKER] = null; |
|
5986 } |
|
5987 } |
|
5988 }, this); |
|
5989 marked = null; |
|
5990 } |
|
5991 |
|
5992 return o2; |
|
5993 }; |
|
5994 |
|
5995 /** |
|
5996 * Returns a function that will execute the supplied function in the |
|
5997 * supplied object's context, optionally adding any additional |
|
5998 * supplied parameters to the beginning of the arguments collection the |
|
5999 * supplied to the function. |
|
6000 * |
|
6001 * @method bind |
|
6002 * @param {Function|String} f the function to bind, or a function name |
|
6003 * to execute on the context object. |
|
6004 * @param {object} c the execution context. |
|
6005 * @param {any} args* 0..n arguments to include before the arguments the |
|
6006 * function is executed with. |
|
6007 * @return {function} the wrapped function. |
|
6008 */ |
|
6009 Y.bind = function(f, c) { |
|
6010 var xargs = arguments.length > 2 ? |
|
6011 Y.Array(arguments, 2, true) : null; |
|
6012 return function() { |
|
6013 var fn = L.isString(f) ? c[f] : f, |
|
6014 args = (xargs) ? |
|
6015 xargs.concat(Y.Array(arguments, 0, true)) : arguments; |
|
6016 return fn.apply(c || fn, args); |
|
6017 }; |
|
6018 }; |
|
6019 |
|
6020 /** |
|
6021 * Returns a function that will execute the supplied function in the |
|
6022 * supplied object's context, optionally adding any additional |
|
6023 * supplied parameters to the end of the arguments the function |
|
6024 * is executed with. |
|
6025 * |
|
6026 * @method rbind |
|
6027 * @param {Function|String} f the function to bind, or a function name |
|
6028 * to execute on the context object. |
|
6029 * @param {object} c the execution context. |
|
6030 * @param {any} args* 0..n arguments to append to the end of |
|
6031 * arguments collection supplied to the function. |
|
6032 * @return {function} the wrapped function. |
|
6033 */ |
|
6034 Y.rbind = function(f, c) { |
|
6035 var xargs = arguments.length > 2 ? Y.Array(arguments, 2, true) : null; |
|
6036 return function() { |
|
6037 var fn = L.isString(f) ? c[f] : f, |
|
6038 args = (xargs) ? |
|
6039 Y.Array(arguments, 0, true).concat(xargs) : arguments; |
|
6040 return fn.apply(c || fn, args); |
|
6041 }; |
|
6042 }; |
|
6043 |
|
6044 |
|
6045 }, '@VERSION@', {"requires": ["yui-base"]}); |
|
6046 YUI.add('features', function (Y, NAME) { |
|
6047 |
|
6048 var feature_tests = {}; |
|
6049 |
|
6050 /** |
|
6051 Contains the core of YUI's feature test architecture. |
|
6052 @module features |
|
6053 */ |
|
6054 |
|
6055 /** |
|
6056 * Feature detection |
|
6057 * @class Features |
|
6058 * @static |
|
6059 */ |
|
6060 |
|
6061 Y.mix(Y.namespace('Features'), { |
|
6062 |
|
6063 /** |
|
6064 * Object hash of all registered feature tests |
|
6065 * @property tests |
|
6066 * @type Object |
|
6067 */ |
|
6068 tests: feature_tests, |
|
6069 |
|
6070 /** |
|
6071 * Add a test to the system |
|
6072 * |
|
6073 * ``` |
|
6074 * Y.Features.add("load", "1", {}); |
|
6075 * ``` |
|
6076 * |
|
6077 * @method add |
|
6078 * @param {String} cat The category, right now only 'load' is supported |
|
6079 * @param {String} name The number sequence of the test, how it's reported in the URL or config: 1, 2, 3 |
|
6080 * @param {Object} o Object containing test properties |
|
6081 * @param {String} o.name The name of the test |
|
6082 * @param {Function} o.test The test function to execute, the only argument to the function is the `Y` instance |
|
6083 * @param {String} o.trigger The module that triggers this test. |
|
6084 */ |
|
6085 add: function(cat, name, o) { |
|
6086 feature_tests[cat] = feature_tests[cat] || {}; |
|
6087 feature_tests[cat][name] = o; |
|
6088 }, |
|
6089 /** |
|
6090 * Execute all tests of a given category and return the serialized results |
|
6091 * |
|
6092 * ``` |
|
6093 * caps=1:1;2:1;3:0 |
|
6094 * ``` |
|
6095 * @method all |
|
6096 * @param {String} cat The category to execute |
|
6097 * @param {Array} args The arguments to pass to the test function |
|
6098 * @return {String} A semi-colon separated string of tests and their success/failure: 1:1;2:1;3:0 |
|
6099 */ |
|
6100 all: function(cat, args) { |
|
6101 var cat_o = feature_tests[cat], |
|
6102 // results = {}; |
|
6103 result = []; |
|
6104 if (cat_o) { |
|
6105 Y.Object.each(cat_o, function(v, k) { |
|
6106 result.push(k + ':' + (Y.Features.test(cat, k, args) ? 1 : 0)); |
|
6107 }); |
|
6108 } |
|
6109 |
|
6110 return (result.length) ? result.join(';') : ''; |
|
6111 }, |
|
6112 /** |
|
6113 * Run a sepecific test and return a Boolean response. |
|
6114 * |
|
6115 * ``` |
|
6116 * Y.Features.test("load", "1"); |
|
6117 * ``` |
|
6118 * |
|
6119 * @method test |
|
6120 * @param {String} cat The category of the test to run |
|
6121 * @param {String} name The name of the test to run |
|
6122 * @param {Array} args The arguments to pass to the test function |
|
6123 * @return {Boolean} True or false if the test passed/failed. |
|
6124 */ |
|
6125 test: function(cat, name, args) { |
|
6126 args = args || []; |
|
6127 var result, ua, test, |
|
6128 cat_o = feature_tests[cat], |
|
6129 feature = cat_o && cat_o[name]; |
|
6130 |
|
6131 if (!feature) { |
|
6132 } else { |
|
6133 |
|
6134 result = feature.result; |
|
6135 |
|
6136 if (Y.Lang.isUndefined(result)) { |
|
6137 |
|
6138 ua = feature.ua; |
|
6139 if (ua) { |
|
6140 result = (Y.UA[ua]); |
|
6141 } |
|
6142 |
|
6143 test = feature.test; |
|
6144 if (test && ((!ua) || result)) { |
|
6145 result = test.apply(Y, args); |
|
6146 } |
|
6147 |
|
6148 feature.result = result; |
|
6149 } |
|
6150 } |
|
6151 |
|
6152 return result; |
|
6153 } |
|
6154 }); |
|
6155 |
|
6156 // Y.Features.add("load", "1", {}); |
|
6157 // Y.Features.test("load", "1"); |
|
6158 // caps=1:1;2:0;3:1; |
|
6159 |
|
6160 /* This file is auto-generated by (yogi loader --yes --mix --start ../) */ |
|
6161 /*jshint maxlen:900, eqeqeq: false */ |
|
6162 var add = Y.Features.add; |
|
6163 // app-transitions-native |
|
6164 add('load', '0', { |
|
6165 "name": "app-transitions-native", |
|
6166 "test": function (Y) { |
|
6167 var doc = Y.config.doc, |
|
6168 node = doc ? doc.documentElement : null; |
|
6169 |
|
6170 if (node && node.style) { |
|
6171 return ('MozTransition' in node.style || 'WebkitTransition' in node.style || 'transition' in node.style); |
|
6172 } |
|
6173 |
|
6174 return false; |
|
6175 }, |
|
6176 "trigger": "app-transitions" |
|
6177 }); |
|
6178 // autocomplete-list-keys |
|
6179 add('load', '1', { |
|
6180 "name": "autocomplete-list-keys", |
|
6181 "test": function (Y) { |
|
6182 // Only add keyboard support to autocomplete-list if this doesn't appear to |
|
6183 // be an iOS or Android-based mobile device. |
|
6184 // |
|
6185 // There's currently no feasible way to actually detect whether a device has |
|
6186 // a hardware keyboard, so this sniff will have to do. It can easily be |
|
6187 // overridden by manually loading the autocomplete-list-keys module. |
|
6188 // |
|
6189 // Worth noting: even though iOS supports bluetooth keyboards, Mobile Safari |
|
6190 // doesn't fire the keyboard events used by AutoCompleteList, so there's |
|
6191 // no point loading the -keys module even when a bluetooth keyboard may be |
|
6192 // available. |
|
6193 return !(Y.UA.ios || Y.UA.android); |
|
6194 }, |
|
6195 "trigger": "autocomplete-list" |
|
6196 }); |
|
6197 // dd-gestures |
|
6198 add('load', '2', { |
|
6199 "name": "dd-gestures", |
|
6200 "trigger": "dd-drag", |
|
6201 "ua": "touchEnabled" |
|
6202 }); |
|
6203 // dom-style-ie |
|
6204 add('load', '3', { |
|
6205 "name": "dom-style-ie", |
|
6206 "test": function (Y) { |
|
6207 |
|
6208 var testFeature = Y.Features.test, |
|
6209 addFeature = Y.Features.add, |
|
6210 WINDOW = Y.config.win, |
|
6211 DOCUMENT = Y.config.doc, |
|
6212 DOCUMENT_ELEMENT = 'documentElement', |
|
6213 ret = false; |
|
6214 |
|
6215 addFeature('style', 'computedStyle', { |
|
6216 test: function() { |
|
6217 return WINDOW && 'getComputedStyle' in WINDOW; |
|
6218 } |
|
6219 }); |
|
6220 |
|
6221 addFeature('style', 'opacity', { |
|
6222 test: function() { |
|
6223 return DOCUMENT && 'opacity' in DOCUMENT[DOCUMENT_ELEMENT].style; |
|
6224 } |
|
6225 }); |
|
6226 |
|
6227 ret = (!testFeature('style', 'opacity') && |
|
6228 !testFeature('style', 'computedStyle')); |
|
6229 |
|
6230 return ret; |
|
6231 }, |
|
6232 "trigger": "dom-style" |
|
6233 }); |
|
6234 // editor-para-ie |
|
6235 add('load', '4', { |
|
6236 "name": "editor-para-ie", |
|
6237 "trigger": "editor-para", |
|
6238 "ua": "ie", |
|
6239 "when": "instead" |
|
6240 }); |
|
6241 // event-base-ie |
|
6242 add('load', '5', { |
|
6243 "name": "event-base-ie", |
|
6244 "test": function(Y) { |
|
6245 var imp = Y.config.doc && Y.config.doc.implementation; |
|
6246 return (imp && (!imp.hasFeature('Events', '2.0'))); |
|
6247 }, |
|
6248 "trigger": "node-base" |
|
6249 }); |
|
6250 // graphics-canvas |
|
6251 add('load', '6', { |
|
6252 "name": "graphics-canvas", |
|
6253 "test": function(Y) { |
|
6254 var DOCUMENT = Y.config.doc, |
|
6255 useCanvas = Y.config.defaultGraphicEngine && Y.config.defaultGraphicEngine == "canvas", |
|
6256 canvas = DOCUMENT && DOCUMENT.createElement("canvas"), |
|
6257 svg = (DOCUMENT && DOCUMENT.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1")); |
|
6258 return (!svg || useCanvas) && (canvas && canvas.getContext && canvas.getContext("2d")); |
|
6259 }, |
|
6260 "trigger": "graphics" |
|
6261 }); |
|
6262 // graphics-canvas-default |
|
6263 add('load', '7', { |
|
6264 "name": "graphics-canvas-default", |
|
6265 "test": function(Y) { |
|
6266 var DOCUMENT = Y.config.doc, |
|
6267 useCanvas = Y.config.defaultGraphicEngine && Y.config.defaultGraphicEngine == "canvas", |
|
6268 canvas = DOCUMENT && DOCUMENT.createElement("canvas"), |
|
6269 svg = (DOCUMENT && DOCUMENT.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1")); |
|
6270 return (!svg || useCanvas) && (canvas && canvas.getContext && canvas.getContext("2d")); |
|
6271 }, |
|
6272 "trigger": "graphics" |
|
6273 }); |
|
6274 // graphics-svg |
|
6275 add('load', '8', { |
|
6276 "name": "graphics-svg", |
|
6277 "test": function(Y) { |
|
6278 var DOCUMENT = Y.config.doc, |
|
6279 useSVG = !Y.config.defaultGraphicEngine || Y.config.defaultGraphicEngine != "canvas", |
|
6280 canvas = DOCUMENT && DOCUMENT.createElement("canvas"), |
|
6281 svg = (DOCUMENT && DOCUMENT.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1")); |
|
6282 |
|
6283 return svg && (useSVG || !canvas); |
|
6284 }, |
|
6285 "trigger": "graphics" |
|
6286 }); |
|
6287 // graphics-svg-default |
|
6288 add('load', '9', { |
|
6289 "name": "graphics-svg-default", |
|
6290 "test": function(Y) { |
|
6291 var DOCUMENT = Y.config.doc, |
|
6292 useSVG = !Y.config.defaultGraphicEngine || Y.config.defaultGraphicEngine != "canvas", |
|
6293 canvas = DOCUMENT && DOCUMENT.createElement("canvas"), |
|
6294 svg = (DOCUMENT && DOCUMENT.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1")); |
|
6295 |
|
6296 return svg && (useSVG || !canvas); |
|
6297 }, |
|
6298 "trigger": "graphics" |
|
6299 }); |
|
6300 // graphics-vml |
|
6301 add('load', '10', { |
|
6302 "name": "graphics-vml", |
|
6303 "test": function(Y) { |
|
6304 var DOCUMENT = Y.config.doc, |
|
6305 canvas = DOCUMENT && DOCUMENT.createElement("canvas"); |
|
6306 return (DOCUMENT && !DOCUMENT.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1") && (!canvas || !canvas.getContext || !canvas.getContext("2d"))); |
|
6307 }, |
|
6308 "trigger": "graphics" |
|
6309 }); |
|
6310 // graphics-vml-default |
|
6311 add('load', '11', { |
|
6312 "name": "graphics-vml-default", |
|
6313 "test": function(Y) { |
|
6314 var DOCUMENT = Y.config.doc, |
|
6315 canvas = DOCUMENT && DOCUMENT.createElement("canvas"); |
|
6316 return (DOCUMENT && !DOCUMENT.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1") && (!canvas || !canvas.getContext || !canvas.getContext("2d"))); |
|
6317 }, |
|
6318 "trigger": "graphics" |
|
6319 }); |
|
6320 // history-hash-ie |
|
6321 add('load', '12', { |
|
6322 "name": "history-hash-ie", |
|
6323 "test": function (Y) { |
|
6324 var docMode = Y.config.doc && Y.config.doc.documentMode; |
|
6325 |
|
6326 return Y.UA.ie && (!('onhashchange' in Y.config.win) || |
|
6327 !docMode || docMode < 8); |
|
6328 }, |
|
6329 "trigger": "history-hash" |
|
6330 }); |
|
6331 // io-nodejs |
|
6332 add('load', '13', { |
|
6333 "name": "io-nodejs", |
|
6334 "trigger": "io-base", |
|
6335 "ua": "nodejs" |
|
6336 }); |
|
6337 // json-parse-shim |
|
6338 add('load', '14', { |
|
6339 "name": "json-parse-shim", |
|
6340 "test": function (Y) { |
|
6341 var _JSON = Y.config.global.JSON, |
|
6342 Native = Object.prototype.toString.call(_JSON) === '[object JSON]' && _JSON, |
|
6343 nativeSupport = Y.config.useNativeJSONParse !== false && !!Native; |
|
6344 |
|
6345 function workingNative( k, v ) { |
|
6346 return k === "ok" ? true : v; |
|
6347 } |
|
6348 |
|
6349 // Double check basic functionality. This is mainly to catch early broken |
|
6350 // implementations of the JSON API in Firefox 3.1 beta1 and beta2 |
|
6351 if ( nativeSupport ) { |
|
6352 try { |
|
6353 nativeSupport = ( Native.parse( '{"ok":false}', workingNative ) ).ok; |
|
6354 } |
|
6355 catch ( e ) { |
|
6356 nativeSupport = false; |
|
6357 } |
|
6358 } |
|
6359 |
|
6360 return !nativeSupport; |
|
6361 }, |
|
6362 "trigger": "json-parse" |
|
6363 }); |
|
6364 // json-stringify-shim |
|
6365 add('load', '15', { |
|
6366 "name": "json-stringify-shim", |
|
6367 "test": function (Y) { |
|
6368 var _JSON = Y.config.global.JSON, |
|
6369 Native = Object.prototype.toString.call(_JSON) === '[object JSON]' && _JSON, |
|
6370 nativeSupport = Y.config.useNativeJSONStringify !== false && !!Native; |
|
6371 |
|
6372 // Double check basic native functionality. This is primarily to catch broken |
|
6373 // early JSON API implementations in Firefox 3.1 beta1 and beta2. |
|
6374 if ( nativeSupport ) { |
|
6375 try { |
|
6376 nativeSupport = ( '0' === Native.stringify(0) ); |
|
6377 } catch ( e ) { |
|
6378 nativeSupport = false; |
|
6379 } |
|
6380 } |
|
6381 |
|
6382 |
|
6383 return !nativeSupport; |
|
6384 }, |
|
6385 "trigger": "json-stringify" |
|
6386 }); |
|
6387 // scrollview-base-ie |
|
6388 add('load', '16', { |
|
6389 "name": "scrollview-base-ie", |
|
6390 "trigger": "scrollview-base", |
|
6391 "ua": "ie" |
|
6392 }); |
|
6393 // selector-css2 |
|
6394 add('load', '17', { |
|
6395 "name": "selector-css2", |
|
6396 "test": function (Y) { |
|
6397 var DOCUMENT = Y.config.doc, |
|
6398 ret = DOCUMENT && !('querySelectorAll' in DOCUMENT); |
|
6399 |
|
6400 return ret; |
|
6401 }, |
|
6402 "trigger": "selector" |
|
6403 }); |
|
6404 // transition-timer |
|
6405 add('load', '18', { |
|
6406 "name": "transition-timer", |
|
6407 "test": function (Y) { |
|
6408 var DOCUMENT = Y.config.doc, |
|
6409 node = (DOCUMENT) ? DOCUMENT.documentElement: null, |
|
6410 ret = true; |
|
6411 |
|
6412 if (node && node.style) { |
|
6413 ret = !('MozTransition' in node.style || 'WebkitTransition' in node.style || 'transition' in node.style); |
|
6414 } |
|
6415 |
|
6416 return ret; |
|
6417 }, |
|
6418 "trigger": "transition" |
|
6419 }); |
|
6420 // widget-base-ie |
|
6421 add('load', '19', { |
|
6422 "name": "widget-base-ie", |
|
6423 "trigger": "widget-base", |
|
6424 "ua": "ie" |
|
6425 }); |
|
6426 // yql-jsonp |
|
6427 add('load', '20', { |
|
6428 "name": "yql-jsonp", |
|
6429 "test": function (Y) { |
|
6430 /* Only load the JSONP module when not in nodejs or winjs |
|
6431 TODO Make the winjs module a CORS module |
|
6432 */ |
|
6433 return (!Y.UA.nodejs && !Y.UA.winjs); |
|
6434 }, |
|
6435 "trigger": "yql", |
|
6436 "when": "after" |
|
6437 }); |
|
6438 // yql-nodejs |
|
6439 add('load', '21', { |
|
6440 "name": "yql-nodejs", |
|
6441 "trigger": "yql", |
|
6442 "ua": "nodejs", |
|
6443 "when": "after" |
|
6444 }); |
|
6445 // yql-winjs |
|
6446 add('load', '22', { |
|
6447 "name": "yql-winjs", |
|
6448 "trigger": "yql", |
|
6449 "ua": "winjs", |
|
6450 "when": "after" |
|
6451 }); |
|
6452 |
|
6453 }, '@VERSION@', {"requires": ["yui-base"]}); |
|
6454 YUI.add('dom-core', function (Y, NAME) { |
|
6455 |
|
6456 var NODE_TYPE = 'nodeType', |
|
6457 OWNER_DOCUMENT = 'ownerDocument', |
|
6458 DOCUMENT_ELEMENT = 'documentElement', |
|
6459 DEFAULT_VIEW = 'defaultView', |
|
6460 PARENT_WINDOW = 'parentWindow', |
|
6461 TAG_NAME = 'tagName', |
|
6462 PARENT_NODE = 'parentNode', |
|
6463 PREVIOUS_SIBLING = 'previousSibling', |
|
6464 NEXT_SIBLING = 'nextSibling', |
|
6465 CONTAINS = 'contains', |
|
6466 COMPARE_DOCUMENT_POSITION = 'compareDocumentPosition', |
|
6467 EMPTY_ARRAY = [], |
|
6468 |
|
6469 // IE < 8 throws on node.contains(textNode) |
|
6470 supportsContainsTextNode = (function() { |
|
6471 var node = Y.config.doc.createElement('div'), |
|
6472 textNode = node.appendChild(Y.config.doc.createTextNode('')), |
|
6473 result = false; |
|
6474 |
|
6475 try { |
|
6476 result = node.contains(textNode); |
|
6477 } catch(e) {} |
|
6478 |
|
6479 return result; |
|
6480 })(), |
|
6481 |
|
6482 /** |
|
6483 * The DOM utility provides a cross-browser abtraction layer |
|
6484 * normalizing DOM tasks, and adds extra helper functionality |
|
6485 * for other common tasks. |
|
6486 * @module dom |
|
6487 * @main dom |
|
6488 * @submodule dom-base |
|
6489 * @for DOM |
|
6490 * |
|
6491 */ |
|
6492 |
|
6493 /** |
|
6494 * Provides DOM helper methods. |
|
6495 * @class DOM |
|
6496 * |
|
6497 */ |
|
6498 |
|
6499 Y_DOM = { |
|
6500 /** |
|
6501 * Returns the HTMLElement with the given ID (Wrapper for document.getElementById). |
|
6502 * @method byId |
|
6503 * @param {String} id the id attribute |
|
6504 * @param {Object} doc optional The document to search. Defaults to current document |
|
6505 * @return {HTMLElement | null} The HTMLElement with the id, or null if none found. |
|
6506 */ |
|
6507 byId: function(id, doc) { |
|
6508 // handle dupe IDs and IE name collision |
|
6509 return Y_DOM.allById(id, doc)[0] || null; |
|
6510 }, |
|
6511 |
|
6512 getId: function(node) { |
|
6513 var id; |
|
6514 // HTMLElement returned from FORM when INPUT name === "id" |
|
6515 // IE < 8: HTMLCollection returned when INPUT id === "id" |
|
6516 // via both getAttribute and form.id |
|
6517 if (node.id && !node.id.tagName && !node.id.item) { |
|
6518 id = node.id; |
|
6519 } else if (node.attributes && node.attributes.id) { |
|
6520 id = node.attributes.id.value; |
|
6521 } |
|
6522 |
|
6523 return id; |
|
6524 }, |
|
6525 |
|
6526 setId: function(node, id) { |
|
6527 if (node.setAttribute) { |
|
6528 node.setAttribute('id', id); |
|
6529 } else { |
|
6530 node.id = id; |
|
6531 } |
|
6532 }, |
|
6533 |
|
6534 /* |
|
6535 * Finds the ancestor of the element. |
|
6536 * @method ancestor |
|
6537 * @param {HTMLElement} element The html element. |
|
6538 * @param {Function} fn optional An optional boolean test to apply. |
|
6539 * The optional function is passed the current DOM node being tested as its only argument. |
|
6540 * If no function is given, the parentNode is returned. |
|
6541 * @param {Boolean} testSelf optional Whether or not to include the element in the scan |
|
6542 * @return {HTMLElement | null} The matching DOM node or null if none found. |
|
6543 */ |
|
6544 ancestor: function(element, fn, testSelf, stopFn) { |
|
6545 var ret = null; |
|
6546 if (testSelf) { |
|
6547 ret = (!fn || fn(element)) ? element : null; |
|
6548 |
|
6549 } |
|
6550 return ret || Y_DOM.elementByAxis(element, PARENT_NODE, fn, null, stopFn); |
|
6551 }, |
|
6552 |
|
6553 /* |
|
6554 * Finds the ancestors of the element. |
|
6555 * @method ancestors |
|
6556 * @param {HTMLElement} element The html element. |
|
6557 * @param {Function} fn optional An optional boolean test to apply. |
|
6558 * The optional function is passed the current DOM node being tested as its only argument. |
|
6559 * If no function is given, all ancestors are returned. |
|
6560 * @param {Boolean} testSelf optional Whether or not to include the element in the scan |
|
6561 * @return {Array} An array containing all matching DOM nodes. |
|
6562 */ |
|
6563 ancestors: function(element, fn, testSelf, stopFn) { |
|
6564 var ancestor = element, |
|
6565 ret = []; |
|
6566 |
|
6567 while ((ancestor = Y_DOM.ancestor(ancestor, fn, testSelf, stopFn))) { |
|
6568 testSelf = false; |
|
6569 if (ancestor) { |
|
6570 ret.unshift(ancestor); |
|
6571 |
|
6572 if (stopFn && stopFn(ancestor)) { |
|
6573 return ret; |
|
6574 } |
|
6575 } |
|
6576 } |
|
6577 |
|
6578 return ret; |
|
6579 }, |
|
6580 |
|
6581 /** |
|
6582 * Searches the element by the given axis for the first matching element. |
|
6583 * @method elementByAxis |
|
6584 * @param {HTMLElement} element The html element. |
|
6585 * @param {String} axis The axis to search (parentNode, nextSibling, previousSibling). |
|
6586 * @param {Function} fn optional An optional boolean test to apply. |
|
6587 * @param {Boolean} all optional Whether all node types should be returned, or just element nodes. |
|
6588 * The optional function is passed the current HTMLElement being tested as its only argument. |
|
6589 * If no function is given, the first element is returned. |
|
6590 * @return {HTMLElement | null} The matching element or null if none found. |
|
6591 */ |
|
6592 elementByAxis: function(element, axis, fn, all, stopAt) { |
|
6593 while (element && (element = element[axis])) { // NOTE: assignment |
|
6594 if ( (all || element[TAG_NAME]) && (!fn || fn(element)) ) { |
|
6595 return element; |
|
6596 } |
|
6597 |
|
6598 if (stopAt && stopAt(element)) { |
|
6599 return null; |
|
6600 } |
|
6601 } |
|
6602 return null; |
|
6603 }, |
|
6604 |
|
6605 /** |
|
6606 * Determines whether or not one HTMLElement is or contains another HTMLElement. |
|
6607 * @method contains |
|
6608 * @param {HTMLElement} element The containing html element. |
|
6609 * @param {HTMLElement} needle The html element that may be contained. |
|
6610 * @return {Boolean} Whether or not the element is or contains the needle. |
|
6611 */ |
|
6612 contains: function(element, needle) { |
|
6613 var ret = false; |
|
6614 |
|
6615 if ( !needle || !element || !needle[NODE_TYPE] || !element[NODE_TYPE]) { |
|
6616 ret = false; |
|
6617 } else if (element[CONTAINS] && |
|
6618 // IE < 8 throws on node.contains(textNode) so fall back to brute. |
|
6619 // Falling back for other nodeTypes as well. |
|
6620 (needle[NODE_TYPE] === 1 || supportsContainsTextNode)) { |
|
6621 ret = element[CONTAINS](needle); |
|
6622 } else if (element[COMPARE_DOCUMENT_POSITION]) { |
|
6623 // Match contains behavior (node.contains(node) === true). |
|
6624 // Needed for Firefox < 4. |
|
6625 if (element === needle || !!(element[COMPARE_DOCUMENT_POSITION](needle) & 16)) { |
|
6626 ret = true; |
|
6627 } |
|
6628 } else { |
|
6629 ret = Y_DOM._bruteContains(element, needle); |
|
6630 } |
|
6631 |
|
6632 return ret; |
|
6633 }, |
|
6634 |
|
6635 /** |
|
6636 * Determines whether or not the HTMLElement is part of the document. |
|
6637 * @method inDoc |
|
6638 * @param {HTMLElement} element The containing html element. |
|
6639 * @param {HTMLElement} doc optional The document to check. |
|
6640 * @return {Boolean} Whether or not the element is attached to the document. |
|
6641 */ |
|
6642 inDoc: function(element, doc) { |
|
6643 var ret = false, |
|
6644 rootNode; |
|
6645 |
|
6646 if (element && element.nodeType) { |
|
6647 (doc) || (doc = element[OWNER_DOCUMENT]); |
|
6648 |
|
6649 rootNode = doc[DOCUMENT_ELEMENT]; |
|
6650 |
|
6651 // contains only works with HTML_ELEMENT |
|
6652 if (rootNode && rootNode.contains && element.tagName) { |
|
6653 ret = rootNode.contains(element); |
|
6654 } else { |
|
6655 ret = Y_DOM.contains(rootNode, element); |
|
6656 } |
|
6657 } |
|
6658 |
|
6659 return ret; |
|
6660 |
|
6661 }, |
|
6662 |
|
6663 allById: function(id, root) { |
|
6664 root = root || Y.config.doc; |
|
6665 var nodes = [], |
|
6666 ret = [], |
|
6667 i, |
|
6668 node; |
|
6669 |
|
6670 if (root.querySelectorAll) { |
|
6671 ret = root.querySelectorAll('[id="' + id + '"]'); |
|
6672 } else if (root.all) { |
|
6673 nodes = root.all(id); |
|
6674 |
|
6675 if (nodes) { |
|
6676 // root.all may return HTMLElement or HTMLCollection. |
|
6677 // some elements are also HTMLCollection (FORM, SELECT). |
|
6678 if (nodes.nodeName) { |
|
6679 if (nodes.id === id) { // avoid false positive on name |
|
6680 ret.push(nodes); |
|
6681 nodes = EMPTY_ARRAY; // done, no need to filter |
|
6682 } else { // prep for filtering |
|
6683 nodes = [nodes]; |
|
6684 } |
|
6685 } |
|
6686 |
|
6687 if (nodes.length) { |
|
6688 // filter out matches on node.name |
|
6689 // and element.id as reference to element with id === 'id' |
|
6690 for (i = 0; node = nodes[i++];) { |
|
6691 if (node.id === id || |
|
6692 (node.attributes && node.attributes.id && |
|
6693 node.attributes.id.value === id)) { |
|
6694 ret.push(node); |
|
6695 } |
|
6696 } |
|
6697 } |
|
6698 } |
|
6699 } else { |
|
6700 ret = [Y_DOM._getDoc(root).getElementById(id)]; |
|
6701 } |
|
6702 |
|
6703 return ret; |
|
6704 }, |
|
6705 |
|
6706 |
|
6707 isWindow: function(obj) { |
|
6708 return !!(obj && obj.scrollTo && obj.document); |
|
6709 }, |
|
6710 |
|
6711 _removeChildNodes: function(node) { |
|
6712 while (node.firstChild) { |
|
6713 node.removeChild(node.firstChild); |
|
6714 } |
|
6715 }, |
|
6716 |
|
6717 siblings: function(node, fn) { |
|
6718 var nodes = [], |
|
6719 sibling = node; |
|
6720 |
|
6721 while ((sibling = sibling[PREVIOUS_SIBLING])) { |
|
6722 if (sibling[TAG_NAME] && (!fn || fn(sibling))) { |
|
6723 nodes.unshift(sibling); |
|
6724 } |
|
6725 } |
|
6726 |
|
6727 sibling = node; |
|
6728 while ((sibling = sibling[NEXT_SIBLING])) { |
|
6729 if (sibling[TAG_NAME] && (!fn || fn(sibling))) { |
|
6730 nodes.push(sibling); |
|
6731 } |
|
6732 } |
|
6733 |
|
6734 return nodes; |
|
6735 }, |
|
6736 |
|
6737 /** |
|
6738 * Brute force version of contains. |
|
6739 * Used for browsers without contains support for non-HTMLElement Nodes (textNodes, etc). |
|
6740 * @method _bruteContains |
|
6741 * @private |
|
6742 * @param {HTMLElement} element The containing html element. |
|
6743 * @param {HTMLElement} needle The html element that may be contained. |
|
6744 * @return {Boolean} Whether or not the element is or contains the needle. |
|
6745 */ |
|
6746 _bruteContains: function(element, needle) { |
|
6747 while (needle) { |
|
6748 if (element === needle) { |
|
6749 return true; |
|
6750 } |
|
6751 needle = needle.parentNode; |
|
6752 } |
|
6753 return false; |
|
6754 }, |
|
6755 |
|
6756 // TODO: move to Lang? |
|
6757 /** |
|
6758 * Memoizes dynamic regular expressions to boost runtime performance. |
|
6759 * @method _getRegExp |
|
6760 * @private |
|
6761 * @param {String} str The string to convert to a regular expression. |
|
6762 * @param {String} flags optional An optinal string of flags. |
|
6763 * @return {RegExp} An instance of RegExp |
|
6764 */ |
|
6765 _getRegExp: function(str, flags) { |
|
6766 flags = flags || ''; |
|
6767 Y_DOM._regexCache = Y_DOM._regexCache || {}; |
|
6768 if (!Y_DOM._regexCache[str + flags]) { |
|
6769 Y_DOM._regexCache[str + flags] = new RegExp(str, flags); |
|
6770 } |
|
6771 return Y_DOM._regexCache[str + flags]; |
|
6772 }, |
|
6773 |
|
6774 // TODO: make getDoc/Win true privates? |
|
6775 /** |
|
6776 * returns the appropriate document. |
|
6777 * @method _getDoc |
|
6778 * @private |
|
6779 * @param {HTMLElement} element optional Target element. |
|
6780 * @return {Object} The document for the given element or the default document. |
|
6781 */ |
|
6782 _getDoc: function(element) { |
|
6783 var doc = Y.config.doc; |
|
6784 if (element) { |
|
6785 doc = (element[NODE_TYPE] === 9) ? element : // element === document |
|
6786 element[OWNER_DOCUMENT] || // element === DOM node |
|
6787 element.document || // element === window |
|
6788 Y.config.doc; // default |
|
6789 } |
|
6790 |
|
6791 return doc; |
|
6792 }, |
|
6793 |
|
6794 /** |
|
6795 * returns the appropriate window. |
|
6796 * @method _getWin |
|
6797 * @private |
|
6798 * @param {HTMLElement} element optional Target element. |
|
6799 * @return {Object} The window for the given element or the default window. |
|
6800 */ |
|
6801 _getWin: function(element) { |
|
6802 var doc = Y_DOM._getDoc(element); |
|
6803 return doc[DEFAULT_VIEW] || doc[PARENT_WINDOW] || Y.config.win; |
|
6804 }, |
|
6805 |
|
6806 _batch: function(nodes, fn, arg1, arg2, arg3, etc) { |
|
6807 fn = (typeof fn === 'string') ? Y_DOM[fn] : fn; |
|
6808 var result, |
|
6809 i = 0, |
|
6810 node, |
|
6811 ret; |
|
6812 |
|
6813 if (fn && nodes) { |
|
6814 while ((node = nodes[i++])) { |
|
6815 result = result = fn.call(Y_DOM, node, arg1, arg2, arg3, etc); |
|
6816 if (typeof result !== 'undefined') { |
|
6817 (ret) || (ret = []); |
|
6818 ret.push(result); |
|
6819 } |
|
6820 } |
|
6821 } |
|
6822 |
|
6823 return (typeof ret !== 'undefined') ? ret : nodes; |
|
6824 }, |
|
6825 |
|
6826 generateID: function(el) { |
|
6827 var id = el.id; |
|
6828 |
|
6829 if (!id) { |
|
6830 id = Y.stamp(el); |
|
6831 el.id = id; |
|
6832 } |
|
6833 |
|
6834 return id; |
|
6835 } |
|
6836 }; |
|
6837 |
|
6838 |
|
6839 Y.DOM = Y_DOM; |
|
6840 |
|
6841 |
|
6842 }, '@VERSION@', {"requires": ["oop", "features"]}); |
|
6843 YUI.add('dom-base', function (Y, NAME) { |
|
6844 |
|
6845 /** |
|
6846 * @for DOM |
|
6847 * @module dom |
|
6848 */ |
|
6849 var documentElement = Y.config.doc.documentElement, |
|
6850 Y_DOM = Y.DOM, |
|
6851 TAG_NAME = 'tagName', |
|
6852 OWNER_DOCUMENT = 'ownerDocument', |
|
6853 EMPTY_STRING = '', |
|
6854 addFeature = Y.Features.add, |
|
6855 testFeature = Y.Features.test; |
|
6856 |
|
6857 Y.mix(Y_DOM, { |
|
6858 /** |
|
6859 * Returns the text content of the HTMLElement. |
|
6860 * @method getText |
|
6861 * @param {HTMLElement} element The html element. |
|
6862 * @return {String} The text content of the element (includes text of any descending elements). |
|
6863 */ |
|
6864 getText: (documentElement.textContent !== undefined) ? |
|
6865 function(element) { |
|
6866 var ret = ''; |
|
6867 if (element) { |
|
6868 ret = element.textContent; |
|
6869 } |
|
6870 return ret || ''; |
|
6871 } : function(element) { |
|
6872 var ret = ''; |
|
6873 if (element) { |
|
6874 ret = element.innerText || element.nodeValue; // might be a textNode |
|
6875 } |
|
6876 return ret || ''; |
|
6877 }, |
|
6878 |
|
6879 /** |
|
6880 * Sets the text content of the HTMLElement. |
|
6881 * @method setText |
|
6882 * @param {HTMLElement} element The html element. |
|
6883 * @param {String} content The content to add. |
|
6884 */ |
|
6885 setText: (documentElement.textContent !== undefined) ? |
|
6886 function(element, content) { |
|
6887 if (element) { |
|
6888 element.textContent = content; |
|
6889 } |
|
6890 } : function(element, content) { |
|
6891 if ('innerText' in element) { |
|
6892 element.innerText = content; |
|
6893 } else if ('nodeValue' in element) { |
|
6894 element.nodeValue = content; |
|
6895 } |
|
6896 }, |
|
6897 |
|
6898 CUSTOM_ATTRIBUTES: (!documentElement.hasAttribute) ? { // IE < 8 |
|
6899 'for': 'htmlFor', |
|
6900 'class': 'className' |
|
6901 } : { // w3c |
|
6902 'htmlFor': 'for', |
|
6903 'className': 'class' |
|
6904 }, |
|
6905 |
|
6906 /** |
|
6907 * Provides a normalized attribute interface. |
|
6908 * @method setAttribute |
|
6909 * @param {HTMLElement} el The target element for the attribute. |
|
6910 * @param {String} attr The attribute to set. |
|
6911 * @param {String} val The value of the attribute. |
|
6912 */ |
|
6913 setAttribute: function(el, attr, val, ieAttr) { |
|
6914 if (el && attr && el.setAttribute) { |
|
6915 attr = Y_DOM.CUSTOM_ATTRIBUTES[attr] || attr; |
|
6916 el.setAttribute(attr, val, ieAttr); |
|
6917 } |
|
6918 }, |
|
6919 |
|
6920 |
|
6921 /** |
|
6922 * Provides a normalized attribute interface. |
|
6923 * @method getAttribute |
|
6924 * @param {HTMLElement} el The target element for the attribute. |
|
6925 * @param {String} attr The attribute to get. |
|
6926 * @return {String} The current value of the attribute. |
|
6927 */ |
|
6928 getAttribute: function(el, attr, ieAttr) { |
|
6929 ieAttr = (ieAttr !== undefined) ? ieAttr : 2; |
|
6930 var ret = ''; |
|
6931 if (el && attr && el.getAttribute) { |
|
6932 attr = Y_DOM.CUSTOM_ATTRIBUTES[attr] || attr; |
|
6933 ret = el.getAttribute(attr, ieAttr); |
|
6934 |
|
6935 if (ret === null) { |
|
6936 ret = ''; // per DOM spec |
|
6937 } |
|
6938 } |
|
6939 return ret; |
|
6940 }, |
|
6941 |
|
6942 VALUE_SETTERS: {}, |
|
6943 |
|
6944 VALUE_GETTERS: {}, |
|
6945 |
|
6946 getValue: function(node) { |
|
6947 var ret = '', // TODO: return null? |
|
6948 getter; |
|
6949 |
|
6950 if (node && node[TAG_NAME]) { |
|
6951 getter = Y_DOM.VALUE_GETTERS[node[TAG_NAME].toLowerCase()]; |
|
6952 |
|
6953 if (getter) { |
|
6954 ret = getter(node); |
|
6955 } else { |
|
6956 ret = node.value; |
|
6957 } |
|
6958 } |
|
6959 |
|
6960 // workaround for IE8 JSON stringify bug |
|
6961 // which converts empty string values to null |
|
6962 if (ret === EMPTY_STRING) { |
|
6963 ret = EMPTY_STRING; // for real |
|
6964 } |
|
6965 |
|
6966 return (typeof ret === 'string') ? ret : ''; |
|
6967 }, |
|
6968 |
|
6969 setValue: function(node, val) { |
|
6970 var setter; |
|
6971 |
|
6972 if (node && node[TAG_NAME]) { |
|
6973 setter = Y_DOM.VALUE_SETTERS[node[TAG_NAME].toLowerCase()]; |
|
6974 |
|
6975 if (setter) { |
|
6976 setter(node, val); |
|
6977 } else { |
|
6978 node.value = val; |
|
6979 } |
|
6980 } |
|
6981 }, |
|
6982 |
|
6983 creators: {} |
|
6984 }); |
|
6985 |
|
6986 addFeature('value-set', 'select', { |
|
6987 test: function() { |
|
6988 var node = Y.config.doc.createElement('select'); |
|
6989 node.innerHTML = '<option>1</option><option>2</option>'; |
|
6990 node.value = '2'; |
|
6991 return (node.value && node.value === '2'); |
|
6992 } |
|
6993 }); |
|
6994 |
|
6995 if (!testFeature('value-set', 'select')) { |
|
6996 Y_DOM.VALUE_SETTERS.select = function(node, val) { |
|
6997 for (var i = 0, options = node.getElementsByTagName('option'), option; |
|
6998 option = options[i++];) { |
|
6999 if (Y_DOM.getValue(option) === val) { |
|
7000 option.selected = true; |
|
7001 //Y_DOM.setAttribute(option, 'selected', 'selected'); |
|
7002 break; |
|
7003 } |
|
7004 } |
|
7005 }; |
|
7006 } |
|
7007 |
|
7008 Y.mix(Y_DOM.VALUE_GETTERS, { |
|
7009 button: function(node) { |
|
7010 return (node.attributes && node.attributes.value) ? node.attributes.value.value : ''; |
|
7011 } |
|
7012 }); |
|
7013 |
|
7014 Y.mix(Y_DOM.VALUE_SETTERS, { |
|
7015 // IE: node.value changes the button text, which should be handled via innerHTML |
|
7016 button: function(node, val) { |
|
7017 var attr = node.attributes.value; |
|
7018 if (!attr) { |
|
7019 attr = node[OWNER_DOCUMENT].createAttribute('value'); |
|
7020 node.setAttributeNode(attr); |
|
7021 } |
|
7022 |
|
7023 attr.value = val; |
|
7024 } |
|
7025 }); |
|
7026 |
|
7027 |
|
7028 Y.mix(Y_DOM.VALUE_GETTERS, { |
|
7029 option: function(node) { |
|
7030 var attrs = node.attributes; |
|
7031 return (attrs.value && attrs.value.specified) ? node.value : node.text; |
|
7032 }, |
|
7033 |
|
7034 select: function(node) { |
|
7035 var val = node.value, |
|
7036 options = node.options; |
|
7037 |
|
7038 if (options && options.length) { |
|
7039 // TODO: implement multipe select |
|
7040 if (node.multiple) { |
|
7041 } else if (node.selectedIndex > -1) { |
|
7042 val = Y_DOM.getValue(options[node.selectedIndex]); |
|
7043 } |
|
7044 } |
|
7045 |
|
7046 return val; |
|
7047 } |
|
7048 }); |
|
7049 var addClass, hasClass, removeClass; |
|
7050 |
|
7051 Y.mix(Y.DOM, { |
|
7052 /** |
|
7053 * Determines whether a DOM element has the given className. |
|
7054 * @method hasClass |
|
7055 * @for DOM |
|
7056 * @param {HTMLElement} element The DOM element. |
|
7057 * @param {String} className the class name to search for |
|
7058 * @return {Boolean} Whether or not the element has the given class. |
|
7059 */ |
|
7060 hasClass: function(node, className) { |
|
7061 var re = Y.DOM._getRegExp('(?:^|\\s+)' + className + '(?:\\s+|$)'); |
|
7062 return re.test(node.className); |
|
7063 }, |
|
7064 |
|
7065 /** |
|
7066 * Adds a class name to a given DOM element. |
|
7067 * @method addClass |
|
7068 * @for DOM |
|
7069 * @param {HTMLElement} element The DOM element. |
|
7070 * @param {String} className the class name to add to the class attribute |
|
7071 */ |
|
7072 addClass: function(node, className) { |
|
7073 if (!Y.DOM.hasClass(node, className)) { // skip if already present |
|
7074 node.className = Y.Lang.trim([node.className, className].join(' ')); |
|
7075 } |
|
7076 }, |
|
7077 |
|
7078 /** |
|
7079 * Removes a class name from a given element. |
|
7080 * @method removeClass |
|
7081 * @for DOM |
|
7082 * @param {HTMLElement} element The DOM element. |
|
7083 * @param {String} className the class name to remove from the class attribute |
|
7084 */ |
|
7085 removeClass: function(node, className) { |
|
7086 if (className && hasClass(node, className)) { |
|
7087 node.className = Y.Lang.trim(node.className.replace(Y.DOM._getRegExp('(?:^|\\s+)' + |
|
7088 className + '(?:\\s+|$)'), ' ')); |
|
7089 |
|
7090 if ( hasClass(node, className) ) { // in case of multiple adjacent |
|
7091 removeClass(node, className); |
|
7092 } |
|
7093 } |
|
7094 }, |
|
7095 |
|
7096 /** |
|
7097 * Replace a class with another class for a given element. |
|
7098 * If no oldClassName is present, the newClassName is simply added. |
|
7099 * @method replaceClass |
|
7100 * @for DOM |
|
7101 * @param {HTMLElement} element The DOM element |
|
7102 * @param {String} oldClassName the class name to be replaced |
|
7103 * @param {String} newClassName the class name that will be replacing the old class name |
|
7104 */ |
|
7105 replaceClass: function(node, oldC, newC) { |
|
7106 removeClass(node, oldC); // remove first in case oldC === newC |
|
7107 addClass(node, newC); |
|
7108 }, |
|
7109 |
|
7110 /** |
|
7111 * If the className exists on the node it is removed, if it doesn't exist it is added. |
|
7112 * @method toggleClass |
|
7113 * @for DOM |
|
7114 * @param {HTMLElement} element The DOM element |
|
7115 * @param {String} className the class name to be toggled |
|
7116 * @param {Boolean} addClass optional boolean to indicate whether class |
|
7117 * should be added or removed regardless of current state |
|
7118 */ |
|
7119 toggleClass: function(node, className, force) { |
|
7120 var add = (force !== undefined) ? force : |
|
7121 !(hasClass(node, className)); |
|
7122 |
|
7123 if (add) { |
|
7124 addClass(node, className); |
|
7125 } else { |
|
7126 removeClass(node, className); |
|
7127 } |
|
7128 } |
|
7129 }); |
|
7130 |
|
7131 hasClass = Y.DOM.hasClass; |
|
7132 removeClass = Y.DOM.removeClass; |
|
7133 addClass = Y.DOM.addClass; |
|
7134 |
|
7135 var re_tag = /<([a-z]+)/i, |
|
7136 |
|
7137 Y_DOM = Y.DOM, |
|
7138 |
|
7139 addFeature = Y.Features.add, |
|
7140 testFeature = Y.Features.test, |
|
7141 |
|
7142 creators = {}, |
|
7143 |
|
7144 createFromDIV = function(html, tag) { |
|
7145 var div = Y.config.doc.createElement('div'), |
|
7146 ret = true; |
|
7147 |
|
7148 div.innerHTML = html; |
|
7149 if (!div.firstChild || div.firstChild.tagName !== tag.toUpperCase()) { |
|
7150 ret = false; |
|
7151 } |
|
7152 |
|
7153 return ret; |
|
7154 }, |
|
7155 |
|
7156 re_tbody = /(?:\/(?:thead|tfoot|tbody|caption|col|colgroup)>)+\s*<tbody/, |
|
7157 |
|
7158 TABLE_OPEN = '<table>', |
|
7159 TABLE_CLOSE = '</table>'; |
|
7160 |
|
7161 Y.mix(Y.DOM, { |
|
7162 _fragClones: {}, |
|
7163 |
|
7164 _create: function(html, doc, tag) { |
|
7165 tag = tag || 'div'; |
|
7166 |
|
7167 var frag = Y_DOM._fragClones[tag]; |
|
7168 if (frag) { |
|
7169 frag = frag.cloneNode(false); |
|
7170 } else { |
|
7171 frag = Y_DOM._fragClones[tag] = doc.createElement(tag); |
|
7172 } |
|
7173 frag.innerHTML = html; |
|
7174 return frag; |
|
7175 }, |
|
7176 |
|
7177 _children: function(node, tag) { |
|
7178 var i = 0, |
|
7179 children = node.children, |
|
7180 childNodes, |
|
7181 hasComments, |
|
7182 child; |
|
7183 |
|
7184 if (children && children.tags) { // use tags filter when possible |
|
7185 if (tag) { |
|
7186 children = node.children.tags(tag); |
|
7187 } else { // IE leaks comments into children |
|
7188 hasComments = children.tags('!').length; |
|
7189 } |
|
7190 } |
|
7191 |
|
7192 if (!children || (!children.tags && tag) || hasComments) { |
|
7193 childNodes = children || node.childNodes; |
|
7194 children = []; |
|
7195 while ((child = childNodes[i++])) { |
|
7196 if (child.nodeType === 1) { |
|
7197 if (!tag || tag === child.tagName) { |
|
7198 children.push(child); |
|
7199 } |
|
7200 } |
|
7201 } |
|
7202 } |
|
7203 |
|
7204 return children || []; |
|
7205 }, |
|
7206 |
|
7207 /** |
|
7208 * Creates a new dom node using the provided markup string. |
|
7209 * @method create |
|
7210 * @param {String} html The markup used to create the element |
|
7211 * @param {HTMLDocument} doc An optional document context |
|
7212 * @return {HTMLElement|DocumentFragment} returns a single HTMLElement |
|
7213 * when creating one node, and a documentFragment when creating |
|
7214 * multiple nodes. |
|
7215 */ |
|
7216 create: function(html, doc) { |
|
7217 if (typeof html === 'string') { |
|
7218 html = Y.Lang.trim(html); // match IE which trims whitespace from innerHTML |
|
7219 |
|
7220 } |
|
7221 |
|
7222 doc = doc || Y.config.doc; |
|
7223 var m = re_tag.exec(html), |
|
7224 create = Y_DOM._create, |
|
7225 custom = creators, |
|
7226 ret = null, |
|
7227 creator, |
|
7228 tag, nodes; |
|
7229 |
|
7230 if (html != undefined) { // not undefined or null |
|
7231 if (m && m[1]) { |
|
7232 creator = custom[m[1].toLowerCase()]; |
|
7233 if (typeof creator === 'function') { |
|
7234 create = creator; |
|
7235 } else { |
|
7236 tag = creator; |
|
7237 } |
|
7238 } |
|
7239 |
|
7240 nodes = create(html, doc, tag).childNodes; |
|
7241 |
|
7242 if (nodes.length === 1) { // return single node, breaking parentNode ref from "fragment" |
|
7243 ret = nodes[0].parentNode.removeChild(nodes[0]); |
|
7244 } else if (nodes[0] && nodes[0].className === 'yui3-big-dummy') { // using dummy node to preserve some attributes (e.g. OPTION not selected) |
|
7245 if (nodes.length === 2) { |
|
7246 ret = nodes[0].nextSibling; |
|
7247 } else { |
|
7248 nodes[0].parentNode.removeChild(nodes[0]); |
|
7249 ret = Y_DOM._nl2frag(nodes, doc); |
|
7250 } |
|
7251 } else { // return multiple nodes as a fragment |
|
7252 ret = Y_DOM._nl2frag(nodes, doc); |
|
7253 } |
|
7254 |
|
7255 } |
|
7256 |
|
7257 return ret; |
|
7258 }, |
|
7259 |
|
7260 _nl2frag: function(nodes, doc) { |
|
7261 var ret = null, |
|
7262 i, len; |
|
7263 |
|
7264 if (nodes && (nodes.push || nodes.item) && nodes[0]) { |
|
7265 doc = doc || nodes[0].ownerDocument; |
|
7266 ret = doc.createDocumentFragment(); |
|
7267 |
|
7268 if (nodes.item) { // convert live list to static array |
|
7269 nodes = Y.Array(nodes, 0, true); |
|
7270 } |
|
7271 |
|
7272 for (i = 0, len = nodes.length; i < len; i++) { |
|
7273 ret.appendChild(nodes[i]); |
|
7274 } |
|
7275 } // else inline with log for minification |
|
7276 return ret; |
|
7277 }, |
|
7278 |
|
7279 /** |
|
7280 * Inserts content in a node at the given location |
|
7281 * @method addHTML |
|
7282 * @param {HTMLElement} node The node to insert into |
|
7283 * @param {HTMLElement | Array | HTMLCollection} content The content to be inserted |
|
7284 * @param {HTMLElement} where Where to insert the content |
|
7285 * If no "where" is given, content is appended to the node |
|
7286 * Possible values for "where" |
|
7287 * <dl> |
|
7288 * <dt>HTMLElement</dt> |
|
7289 * <dd>The element to insert before</dd> |
|
7290 * <dt>"replace"</dt> |
|
7291 * <dd>Replaces the existing HTML</dd> |
|
7292 * <dt>"before"</dt> |
|
7293 * <dd>Inserts before the existing HTML</dd> |
|
7294 * <dt>"before"</dt> |
|
7295 * <dd>Inserts content before the node</dd> |
|
7296 * <dt>"after"</dt> |
|
7297 * <dd>Inserts content after the node</dd> |
|
7298 * </dl> |
|
7299 */ |
|
7300 addHTML: function(node, content, where) { |
|
7301 var nodeParent = node.parentNode, |
|
7302 i = 0, |
|
7303 item, |
|
7304 ret = content, |
|
7305 newNode; |
|
7306 |
|
7307 |
|
7308 if (content != undefined) { // not null or undefined (maybe 0) |
|
7309 if (content.nodeType) { // DOM node, just add it |
|
7310 newNode = content; |
|
7311 } else if (typeof content == 'string' || typeof content == 'number') { |
|
7312 ret = newNode = Y_DOM.create(content); |
|
7313 } else if (content[0] && content[0].nodeType) { // array or collection |
|
7314 newNode = Y.config.doc.createDocumentFragment(); |
|
7315 while ((item = content[i++])) { |
|
7316 newNode.appendChild(item); // append to fragment for insertion |
|
7317 } |
|
7318 } |
|
7319 } |
|
7320 |
|
7321 if (where) { |
|
7322 if (newNode && where.parentNode) { // insert regardless of relationship to node |
|
7323 where.parentNode.insertBefore(newNode, where); |
|
7324 } else { |
|
7325 switch (where) { |
|
7326 case 'replace': |
|
7327 while (node.firstChild) { |
|
7328 node.removeChild(node.firstChild); |
|
7329 } |
|
7330 if (newNode) { // allow empty content to clear node |
|
7331 node.appendChild(newNode); |
|
7332 } |
|
7333 break; |
|
7334 case 'before': |
|
7335 if (newNode) { |
|
7336 nodeParent.insertBefore(newNode, node); |
|
7337 } |
|
7338 break; |
|
7339 case 'after': |
|
7340 if (newNode) { |
|
7341 if (node.nextSibling) { // IE errors if refNode is null |
|
7342 nodeParent.insertBefore(newNode, node.nextSibling); |
|
7343 } else { |
|
7344 nodeParent.appendChild(newNode); |
|
7345 } |
|
7346 } |
|
7347 break; |
|
7348 default: |
|
7349 if (newNode) { |
|
7350 node.appendChild(newNode); |
|
7351 } |
|
7352 } |
|
7353 } |
|
7354 } else if (newNode) { |
|
7355 node.appendChild(newNode); |
|
7356 } |
|
7357 |
|
7358 return ret; |
|
7359 }, |
|
7360 |
|
7361 wrap: function(node, html) { |
|
7362 var parent = (html && html.nodeType) ? html : Y.DOM.create(html), |
|
7363 nodes = parent.getElementsByTagName('*'); |
|
7364 |
|
7365 if (nodes.length) { |
|
7366 parent = nodes[nodes.length - 1]; |
|
7367 } |
|
7368 |
|
7369 if (node.parentNode) { |
|
7370 node.parentNode.replaceChild(parent, node); |
|
7371 } |
|
7372 parent.appendChild(node); |
|
7373 }, |
|
7374 |
|
7375 unwrap: function(node) { |
|
7376 var parent = node.parentNode, |
|
7377 lastChild = parent.lastChild, |
|
7378 next = node, |
|
7379 grandparent; |
|
7380 |
|
7381 if (parent) { |
|
7382 grandparent = parent.parentNode; |
|
7383 if (grandparent) { |
|
7384 node = parent.firstChild; |
|
7385 while (node !== lastChild) { |
|
7386 next = node.nextSibling; |
|
7387 grandparent.insertBefore(node, parent); |
|
7388 node = next; |
|
7389 } |
|
7390 grandparent.replaceChild(lastChild, parent); |
|
7391 } else { |
|
7392 parent.removeChild(node); |
|
7393 } |
|
7394 } |
|
7395 } |
|
7396 }); |
|
7397 |
|
7398 addFeature('innerhtml', 'table', { |
|
7399 test: function() { |
|
7400 var node = Y.config.doc.createElement('table'); |
|
7401 try { |
|
7402 node.innerHTML = '<tbody></tbody>'; |
|
7403 } catch(e) { |
|
7404 return false; |
|
7405 } |
|
7406 return (node.firstChild && node.firstChild.nodeName === 'TBODY'); |
|
7407 } |
|
7408 }); |
|
7409 |
|
7410 addFeature('innerhtml-div', 'tr', { |
|
7411 test: function() { |
|
7412 return createFromDIV('<tr></tr>', 'tr'); |
|
7413 } |
|
7414 }); |
|
7415 |
|
7416 addFeature('innerhtml-div', 'script', { |
|
7417 test: function() { |
|
7418 return createFromDIV('<script></script>', 'script'); |
|
7419 } |
|
7420 }); |
|
7421 |
|
7422 if (!testFeature('innerhtml', 'table')) { |
|
7423 // TODO: thead/tfoot with nested tbody |
|
7424 // IE adds TBODY when creating TABLE elements (which may share this impl) |
|
7425 creators.tbody = function(html, doc) { |
|
7426 var frag = Y_DOM.create(TABLE_OPEN + html + TABLE_CLOSE, doc), |
|
7427 tb = Y.DOM._children(frag, 'tbody')[0]; |
|
7428 |
|
7429 if (frag.children.length > 1 && tb && !re_tbody.test(html)) { |
|
7430 tb.parentNode.removeChild(tb); // strip extraneous tbody |
|
7431 } |
|
7432 return frag; |
|
7433 }; |
|
7434 } |
|
7435 |
|
7436 if (!testFeature('innerhtml-div', 'script')) { |
|
7437 creators.script = function(html, doc) { |
|
7438 var frag = doc.createElement('div'); |
|
7439 |
|
7440 frag.innerHTML = '-' + html; |
|
7441 frag.removeChild(frag.firstChild); |
|
7442 return frag; |
|
7443 }; |
|
7444 |
|
7445 creators.link = creators.style = creators.script; |
|
7446 } |
|
7447 |
|
7448 if (!testFeature('innerhtml-div', 'tr')) { |
|
7449 Y.mix(creators, { |
|
7450 option: function(html, doc) { |
|
7451 return Y_DOM.create('<select><option class="yui3-big-dummy" selected></option>' + html + '</select>', doc); |
|
7452 }, |
|
7453 |
|
7454 tr: function(html, doc) { |
|
7455 return Y_DOM.create('<tbody>' + html + '</tbody>', doc); |
|
7456 }, |
|
7457 |
|
7458 td: function(html, doc) { |
|
7459 return Y_DOM.create('<tr>' + html + '</tr>', doc); |
|
7460 }, |
|
7461 |
|
7462 col: function(html, doc) { |
|
7463 return Y_DOM.create('<colgroup>' + html + '</colgroup>', doc); |
|
7464 }, |
|
7465 |
|
7466 tbody: 'table' |
|
7467 }); |
|
7468 |
|
7469 Y.mix(creators, { |
|
7470 legend: 'fieldset', |
|
7471 th: creators.td, |
|
7472 thead: creators.tbody, |
|
7473 tfoot: creators.tbody, |
|
7474 caption: creators.tbody, |
|
7475 colgroup: creators.tbody, |
|
7476 optgroup: creators.option |
|
7477 }); |
|
7478 } |
|
7479 |
|
7480 Y_DOM.creators = creators; |
|
7481 Y.mix(Y.DOM, { |
|
7482 /** |
|
7483 * Sets the width of the element to the given size, regardless |
|
7484 * of box model, border, padding, etc. |
|
7485 * @method setWidth |
|
7486 * @param {HTMLElement} element The DOM element. |
|
7487 * @param {String|Number} size The pixel height to size to |
|
7488 */ |
|
7489 |
|
7490 setWidth: function(node, size) { |
|
7491 Y.DOM._setSize(node, 'width', size); |
|
7492 }, |
|
7493 |
|
7494 /** |
|
7495 * Sets the height of the element to the given size, regardless |
|
7496 * of box model, border, padding, etc. |
|
7497 * @method setHeight |
|
7498 * @param {HTMLElement} element The DOM element. |
|
7499 * @param {String|Number} size The pixel height to size to |
|
7500 */ |
|
7501 |
|
7502 setHeight: function(node, size) { |
|
7503 Y.DOM._setSize(node, 'height', size); |
|
7504 }, |
|
7505 |
|
7506 _setSize: function(node, prop, val) { |
|
7507 val = (val > 0) ? val : 0; |
|
7508 var size = 0; |
|
7509 |
|
7510 node.style[prop] = val + 'px'; |
|
7511 size = (prop === 'height') ? node.offsetHeight : node.offsetWidth; |
|
7512 |
|
7513 if (size > val) { |
|
7514 val = val - (size - val); |
|
7515 |
|
7516 if (val < 0) { |
|
7517 val = 0; |
|
7518 } |
|
7519 |
|
7520 node.style[prop] = val + 'px'; |
|
7521 } |
|
7522 } |
|
7523 }); |
|
7524 |
|
7525 |
|
7526 }, '@VERSION@', {"requires": ["dom-core"]}); |
|
7527 YUI.add('color-base', function (Y, NAME) { |
|
7528 |
|
7529 /** |
|
7530 Color provides static methods for color conversion. |
|
7531 |
|
7532 Y.Color.toRGB('f00'); // rgb(255, 0, 0) |
|
7533 |
|
7534 Y.Color.toHex('rgb(255, 255, 0)'); // #ffff00 |
|
7535 |
|
7536 @module color |
|
7537 @submodule color-base |
|
7538 @class Color |
|
7539 @since 3.8.0 |
|
7540 **/ |
|
7541 |
|
7542 var REGEX_HEX = /^#?([\da-fA-F]{2})([\da-fA-F]{2})([\da-fA-F]{2})(\ufffe)?/, |
|
7543 REGEX_HEX3 = /^#?([\da-fA-F]{1})([\da-fA-F]{1})([\da-fA-F]{1})(\ufffe)?/, |
|
7544 REGEX_RGB = /rgba?\(([\d]{1,3}), ?([\d]{1,3}), ?([\d]{1,3}),? ?([.\d]*)?\)/, |
|
7545 TYPES = { 'HEX': 'hex', 'RGB': 'rgb', 'RGBA': 'rgba' }, |
|
7546 CONVERTS = { 'hex': 'toHex', 'rgb': 'toRGB', 'rgba': 'toRGBA' }; |
|
7547 |
|
7548 |
|
7549 Y.Color = { |
|
7550 /** |
|
7551 @static |
|
7552 @property KEYWORDS |
|
7553 @type Object |
|
7554 @since 3.8.0 |
|
7555 **/ |
|
7556 KEYWORDS: { |
|
7557 'black': '000', 'silver': 'c0c0c0', 'gray': '808080', 'white': 'fff', |
|
7558 'maroon': '800000', 'red': 'f00', 'purple': '800080', 'fuchsia': 'f0f', |
|
7559 'green': '008000', 'lime': '0f0', 'olive': '808000', 'yellow': 'ff0', |
|
7560 'navy': '000080', 'blue': '00f', 'teal': '008080', 'aqua': '0ff' |
|
7561 }, |
|
7562 |
|
7563 /** |
|
7564 NOTE: `(\ufffe)?` is added to the Regular Expression to carve out a |
|
7565 place for the alpha channel that is returned from toArray |
|
7566 without compromising any usage of the Regular Expression |
|
7567 |
|
7568 @static |
|
7569 @property REGEX_HEX |
|
7570 @type RegExp |
|
7571 @default /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})(\ufffe)?/ |
|
7572 @since 3.8.0 |
|
7573 **/ |
|
7574 REGEX_HEX: REGEX_HEX, |
|
7575 |
|
7576 /** |
|
7577 NOTE: `(\ufffe)?` is added to the Regular Expression to carve out a |
|
7578 place for the alpha channel that is returned from toArray |
|
7579 without compromising any usage of the Regular Expression |
|
7580 |
|
7581 @static |
|
7582 @property REGEX_HEX3 |
|
7583 @type RegExp |
|
7584 @default /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})(\ufffe)?/ |
|
7585 @since 3.8.0 |
|
7586 **/ |
|
7587 REGEX_HEX3: REGEX_HEX3, |
|
7588 |
|
7589 /** |
|
7590 @static |
|
7591 @property REGEX_RGB |
|
7592 @type RegExp |
|
7593 @default /rgba?\(([0-9]{1,3}), ?([0-9]{1,3}), ?([0-9]{1,3}),? ?([.0-9]{1,3})?\)/ |
|
7594 @since 3.8.0 |
|
7595 **/ |
|
7596 REGEX_RGB: REGEX_RGB, |
|
7597 |
|
7598 re_RGB: REGEX_RGB, |
|
7599 |
|
7600 re_hex: REGEX_HEX, |
|
7601 |
|
7602 re_hex3: REGEX_HEX3, |
|
7603 |
|
7604 /** |
|
7605 @static |
|
7606 @property STR_HEX |
|
7607 @type String |
|
7608 @default #{*}{*}{*} |
|
7609 @since 3.8.0 |
|
7610 **/ |
|
7611 STR_HEX: '#{*}{*}{*}', |
|
7612 |
|
7613 /** |
|
7614 @static |
|
7615 @property STR_RGB |
|
7616 @type String |
|
7617 @default rgb({*}, {*}, {*}) |
|
7618 @since 3.8.0 |
|
7619 **/ |
|
7620 STR_RGB: 'rgb({*}, {*}, {*})', |
|
7621 |
|
7622 /** |
|
7623 @static |
|
7624 @property STR_RGBA |
|
7625 @type String |
|
7626 @default rgba({*}, {*}, {*}, {*}) |
|
7627 @since 3.8.0 |
|
7628 **/ |
|
7629 STR_RGBA: 'rgba({*}, {*}, {*}, {*})', |
|
7630 |
|
7631 /** |
|
7632 @static |
|
7633 @property TYPES |
|
7634 @type Object |
|
7635 @default {'rgb':'rgb', 'rgba':'rgba'} |
|
7636 @since 3.8.0 |
|
7637 **/ |
|
7638 TYPES: TYPES, |
|
7639 |
|
7640 /** |
|
7641 @static |
|
7642 @property CONVERTS |
|
7643 @type Object |
|
7644 @default {} |
|
7645 @since 3.8.0 |
|
7646 **/ |
|
7647 CONVERTS: CONVERTS, |
|
7648 |
|
7649 /** |
|
7650 Converts the provided string to the provided type. |
|
7651 You can use the `Y.Color.TYPES` to get a valid `to` type. |
|
7652 If the color cannot be converted, the original color will be returned. |
|
7653 |
|
7654 @public |
|
7655 @method convert |
|
7656 @param {String} str |
|
7657 @param {String} to |
|
7658 @return {String} |
|
7659 @since 3.8.0 |
|
7660 **/ |
|
7661 convert: function (str, to) { |
|
7662 var convert = Y.Color.CONVERTS[to.toLowerCase()], |
|
7663 clr = str; |
|
7664 |
|
7665 if (convert && Y.Color[convert]) { |
|
7666 clr = Y.Color[convert](str); |
|
7667 } |
|
7668 |
|
7669 return clr; |
|
7670 }, |
|
7671 |
|
7672 /** |
|
7673 Converts provided color value to a hex value string |
|
7674 |
|
7675 @public |
|
7676 @method toHex |
|
7677 @param {String} str Hex or RGB value string |
|
7678 @return {String} returns array of values or CSS string if options.css is true |
|
7679 @since 3.8.0 |
|
7680 **/ |
|
7681 toHex: function (str) { |
|
7682 var clr = Y.Color._convertTo(str, 'hex'), |
|
7683 isTransparent = clr.toLowerCase() === 'transparent'; |
|
7684 |
|
7685 if (clr.charAt(0) !== '#' && !isTransparent) { |
|
7686 clr = '#' + clr; |
|
7687 } |
|
7688 |
|
7689 return isTransparent ? clr.toLowerCase() : clr.toUpperCase(); |
|
7690 }, |
|
7691 |
|
7692 /** |
|
7693 Converts provided color value to an RGB value string |
|
7694 @public |
|
7695 @method toRGB |
|
7696 @param {String} str Hex or RGB value string |
|
7697 @return {String} |
|
7698 @since 3.8.0 |
|
7699 **/ |
|
7700 toRGB: function (str) { |
|
7701 var clr = Y.Color._convertTo(str, 'rgb'); |
|
7702 return clr.toLowerCase(); |
|
7703 }, |
|
7704 |
|
7705 /** |
|
7706 Converts provided color value to an RGB value string |
|
7707 @public |
|
7708 @method toRGBA |
|
7709 @param {String} str Hex or RGB value string |
|
7710 @return {String} |
|
7711 @since 3.8.0 |
|
7712 **/ |
|
7713 toRGBA: function (str) { |
|
7714 var clr = Y.Color._convertTo(str, 'rgba' ); |
|
7715 return clr.toLowerCase(); |
|
7716 }, |
|
7717 |
|
7718 /** |
|
7719 Converts the provided color string to an array of values where the |
|
7720 last value is the alpha value. Will return an empty array if |
|
7721 the provided string is not able to be parsed. |
|
7722 |
|
7723 NOTE: `(\ufffe)?` is added to `HEX` and `HEX3` Regular Expressions to |
|
7724 carve out a place for the alpha channel that is returned from |
|
7725 toArray without compromising any usage of the Regular Expression |
|
7726 |
|
7727 Y.Color.toArray('fff'); // ['ff', 'ff', 'ff', 1] |
|
7728 Y.Color.toArray('rgb(0, 0, 0)'); // ['0', '0', '0', 1] |
|
7729 Y.Color.toArray('rgba(0, 0, 0, 0)'); // ['0', '0', '0', 1] |
|
7730 |
|
7731 |
|
7732 |
|
7733 @public |
|
7734 @method toArray |
|
7735 @param {String} str |
|
7736 @return {Array} |
|
7737 @since 3.8.0 |
|
7738 **/ |
|
7739 toArray: function(str) { |
|
7740 // parse with regex and return "matches" array |
|
7741 var type = Y.Color.findType(str).toUpperCase(), |
|
7742 regex, |
|
7743 arr, |
|
7744 length, |
|
7745 lastItem; |
|
7746 |
|
7747 if (type === 'HEX' && str.length < 5) { |
|
7748 type = 'HEX3'; |
|
7749 } |
|
7750 |
|
7751 if (type.charAt(type.length - 1) === 'A') { |
|
7752 type = type.slice(0, -1); |
|
7753 } |
|
7754 |
|
7755 regex = Y.Color['REGEX_' + type]; |
|
7756 |
|
7757 if (regex) { |
|
7758 arr = regex.exec(str) || []; |
|
7759 length = arr.length; |
|
7760 |
|
7761 if (length) { |
|
7762 |
|
7763 arr.shift(); |
|
7764 length--; |
|
7765 |
|
7766 if (type === 'HEX3') { |
|
7767 arr[0] += arr[0]; |
|
7768 arr[1] += arr[1]; |
|
7769 arr[2] += arr[2]; |
|
7770 } |
|
7771 |
|
7772 lastItem = arr[length - 1]; |
|
7773 if (!lastItem) { |
|
7774 arr[length - 1] = 1; |
|
7775 } |
|
7776 } |
|
7777 } |
|
7778 |
|
7779 return arr; |
|
7780 |
|
7781 }, |
|
7782 |
|
7783 /** |
|
7784 Converts the array of values to a string based on the provided template. |
|
7785 @public |
|
7786 @method fromArray |
|
7787 @param {Array} arr |
|
7788 @param {String} template |
|
7789 @return {String} |
|
7790 @since 3.8.0 |
|
7791 **/ |
|
7792 fromArray: function(arr, template) { |
|
7793 arr = arr.concat(); |
|
7794 |
|
7795 if (typeof template === 'undefined') { |
|
7796 return arr.join(', '); |
|
7797 } |
|
7798 |
|
7799 var replace = '{*}'; |
|
7800 |
|
7801 template = Y.Color['STR_' + template.toUpperCase()]; |
|
7802 |
|
7803 if (arr.length === 3 && template.match(/\{\*\}/g).length === 4) { |
|
7804 arr.push(1); |
|
7805 } |
|
7806 |
|
7807 while ( template.indexOf(replace) >= 0 && arr.length > 0) { |
|
7808 template = template.replace(replace, arr.shift()); |
|
7809 } |
|
7810 |
|
7811 return template; |
|
7812 }, |
|
7813 |
|
7814 /** |
|
7815 Finds the value type based on the str value provided. |
|
7816 @public |
|
7817 @method findType |
|
7818 @param {String} str |
|
7819 @return {String} |
|
7820 @since 3.8.0 |
|
7821 **/ |
|
7822 findType: function (str) { |
|
7823 if (Y.Color.KEYWORDS[str]) { |
|
7824 return 'keyword'; |
|
7825 } |
|
7826 |
|
7827 var index = str.indexOf('('), |
|
7828 key; |
|
7829 |
|
7830 if (index > 0) { |
|
7831 key = str.substr(0, index); |
|
7832 } |
|
7833 |
|
7834 if (key && Y.Color.TYPES[key.toUpperCase()]) { |
|
7835 return Y.Color.TYPES[key.toUpperCase()]; |
|
7836 } |
|
7837 |
|
7838 return 'hex'; |
|
7839 |
|
7840 }, // return 'keyword', 'hex', 'rgb' |
|
7841 |
|
7842 /** |
|
7843 Retrives the alpha channel from the provided string. If no alpha |
|
7844 channel is present, `1` will be returned. |
|
7845 @protected |
|
7846 @method _getAlpha |
|
7847 @param {String} clr |
|
7848 @return {Number} |
|
7849 @since 3.8.0 |
|
7850 **/ |
|
7851 _getAlpha: function (clr) { |
|
7852 var alpha, |
|
7853 arr = Y.Color.toArray(clr); |
|
7854 |
|
7855 if (arr.length > 3) { |
|
7856 alpha = arr.pop(); |
|
7857 } |
|
7858 |
|
7859 return +alpha || 1; |
|
7860 }, |
|
7861 |
|
7862 /** |
|
7863 Returns the hex value string if found in the KEYWORDS object |
|
7864 @protected |
|
7865 @method _keywordToHex |
|
7866 @param {String} clr |
|
7867 @return {String} |
|
7868 @since 3.8.0 |
|
7869 **/ |
|
7870 _keywordToHex: function (clr) { |
|
7871 var keyword = Y.Color.KEYWORDS[clr]; |
|
7872 |
|
7873 if (keyword) { |
|
7874 return keyword; |
|
7875 } |
|
7876 }, |
|
7877 |
|
7878 /** |
|
7879 Converts the provided color string to the value type provided as `to` |
|
7880 @protected |
|
7881 @method _convertTo |
|
7882 @param {String} clr |
|
7883 @param {String} to |
|
7884 @return {String} |
|
7885 @since 3.8.0 |
|
7886 **/ |
|
7887 _convertTo: function(clr, to) { |
|
7888 |
|
7889 if (clr === 'transparent') { |
|
7890 return clr; |
|
7891 } |
|
7892 |
|
7893 var from = Y.Color.findType(clr), |
|
7894 originalTo = to, |
|
7895 needsAlpha, |
|
7896 alpha, |
|
7897 method, |
|
7898 ucTo; |
|
7899 |
|
7900 if (from === 'keyword') { |
|
7901 clr = Y.Color._keywordToHex(clr); |
|
7902 from = 'hex'; |
|
7903 } |
|
7904 |
|
7905 if (from === 'hex' && clr.length < 5) { |
|
7906 if (clr.charAt(0) === '#') { |
|
7907 clr = clr.substr(1); |
|
7908 } |
|
7909 |
|
7910 clr = '#' + clr.charAt(0) + clr.charAt(0) + |
|
7911 clr.charAt(1) + clr.charAt(1) + |
|
7912 clr.charAt(2) + clr.charAt(2); |
|
7913 } |
|
7914 |
|
7915 if (from === to) { |
|
7916 return clr; |
|
7917 } |
|
7918 |
|
7919 if (from.charAt(from.length - 1) === 'a') { |
|
7920 from = from.slice(0, -1); |
|
7921 } |
|
7922 |
|
7923 needsAlpha = (to.charAt(to.length - 1) === 'a'); |
|
7924 if (needsAlpha) { |
|
7925 to = to.slice(0, -1); |
|
7926 alpha = Y.Color._getAlpha(clr); |
|
7927 } |
|
7928 |
|
7929 ucTo = to.charAt(0).toUpperCase() + to.substr(1).toLowerCase(); |
|
7930 method = Y.Color['_' + from + 'To' + ucTo ]; |
|
7931 |
|
7932 // check to see if need conversion to rgb first |
|
7933 // check to see if there is a direct conversion method |
|
7934 // convertions are: hex <-> rgb <-> hsl |
|
7935 if (!method) { |
|
7936 if (from !== 'rgb' && to !== 'rgb') { |
|
7937 clr = Y.Color['_' + from + 'ToRgb'](clr); |
|
7938 from = 'rgb'; |
|
7939 method = Y.Color['_' + from + 'To' + ucTo ]; |
|
7940 } |
|
7941 } |
|
7942 |
|
7943 if (method) { |
|
7944 clr = ((method)(clr, needsAlpha)); |
|
7945 } |
|
7946 |
|
7947 // process clr from arrays to strings after conversions if alpha is needed |
|
7948 if (needsAlpha) { |
|
7949 if (!Y.Lang.isArray(clr)) { |
|
7950 clr = Y.Color.toArray(clr); |
|
7951 } |
|
7952 clr.push(alpha); |
|
7953 clr = Y.Color.fromArray(clr, originalTo.toUpperCase()); |
|
7954 } |
|
7955 |
|
7956 return clr; |
|
7957 }, |
|
7958 |
|
7959 /** |
|
7960 Processes the hex string into r, g, b values. Will return values as |
|
7961 an array, or as an rgb string. |
|
7962 @protected |
|
7963 @method _hexToRgb |
|
7964 @param {String} str |
|
7965 @param {Boolean} [toArray] |
|
7966 @return {String|Array} |
|
7967 @since 3.8.0 |
|
7968 **/ |
|
7969 _hexToRgb: function (str, toArray) { |
|
7970 var r, g, b; |
|
7971 |
|
7972 /*jshint bitwise:false*/ |
|
7973 if (str.charAt(0) === '#') { |
|
7974 str = str.substr(1); |
|
7975 } |
|
7976 |
|
7977 str = parseInt(str, 16); |
|
7978 |
|
7979 r = str >> 16; |
|
7980 g = str >> 8 & 0xFF; |
|
7981 b = str & 0xFF; |
|
7982 |
|
7983 if (toArray) { |
|
7984 return [r, g, b]; |
|
7985 } |
|
7986 |
|
7987 return 'rgb(' + r + ', ' + g + ', ' + b + ')'; |
|
7988 }, |
|
7989 |
|
7990 /** |
|
7991 Processes the rgb string into r, g, b values. Will return values as |
|
7992 an array, or as a hex string. |
|
7993 @protected |
|
7994 @method _rgbToHex |
|
7995 @param {String} str |
|
7996 @param {Boolean} [toArray] |
|
7997 @return {String|Array} |
|
7998 @since 3.8.0 |
|
7999 **/ |
|
8000 _rgbToHex: function (str) { |
|
8001 /*jshint bitwise:false*/ |
|
8002 var rgb = Y.Color.toArray(str), |
|
8003 hex = rgb[2] | (rgb[1] << 8) | (rgb[0] << 16); |
|
8004 |
|
8005 hex = (+hex).toString(16); |
|
8006 |
|
8007 while (hex.length < 6) { |
|
8008 hex = '0' + hex; |
|
8009 } |
|
8010 |
|
8011 return '#' + hex; |
|
8012 } |
|
8013 |
|
8014 }; |
|
8015 |
|
8016 |
|
8017 |
|
8018 }, '@VERSION@', {"requires": ["yui-base"]}); |
|
8019 YUI.add('dom-style', function (Y, NAME) { |
|
8020 |
|
8021 (function(Y) { |
|
8022 /** |
|
8023 * Add style management functionality to DOM. |
|
8024 * @module dom |
|
8025 * @submodule dom-style |
|
8026 * @for DOM |
|
8027 */ |
|
8028 |
|
8029 var DOCUMENT_ELEMENT = 'documentElement', |
|
8030 DEFAULT_VIEW = 'defaultView', |
|
8031 OWNER_DOCUMENT = 'ownerDocument', |
|
8032 STYLE = 'style', |
|
8033 FLOAT = 'float', |
|
8034 CSS_FLOAT = 'cssFloat', |
|
8035 STYLE_FLOAT = 'styleFloat', |
|
8036 TRANSPARENT = 'transparent', |
|
8037 GET_COMPUTED_STYLE = 'getComputedStyle', |
|
8038 GET_BOUNDING_CLIENT_RECT = 'getBoundingClientRect', |
|
8039 |
|
8040 WINDOW = Y.config.win, |
|
8041 DOCUMENT = Y.config.doc, |
|
8042 UNDEFINED = undefined, |
|
8043 |
|
8044 Y_DOM = Y.DOM, |
|
8045 |
|
8046 TRANSFORM = 'transform', |
|
8047 TRANSFORMORIGIN = 'transformOrigin', |
|
8048 VENDOR_TRANSFORM = [ |
|
8049 'WebkitTransform', |
|
8050 'MozTransform', |
|
8051 'OTransform', |
|
8052 'msTransform' |
|
8053 ], |
|
8054 |
|
8055 re_color = /color$/i, |
|
8056 re_unit = /width|height|top|left|right|bottom|margin|padding/i; |
|
8057 |
|
8058 Y.Array.each(VENDOR_TRANSFORM, function(val) { |
|
8059 if (val in DOCUMENT[DOCUMENT_ELEMENT].style) { |
|
8060 TRANSFORM = val; |
|
8061 TRANSFORMORIGIN = val + "Origin"; |
|
8062 } |
|
8063 }); |
|
8064 |
|
8065 Y.mix(Y_DOM, { |
|
8066 DEFAULT_UNIT: 'px', |
|
8067 |
|
8068 CUSTOM_STYLES: { |
|
8069 }, |
|
8070 |
|
8071 |
|
8072 /** |
|
8073 * Sets a style property for a given element. |
|
8074 * @method setStyle |
|
8075 * @param {HTMLElement} An HTMLElement to apply the style to. |
|
8076 * @param {String} att The style property to set. |
|
8077 * @param {String|Number} val The value. |
|
8078 */ |
|
8079 setStyle: function(node, att, val, style) { |
|
8080 style = style || node.style; |
|
8081 var CUSTOM_STYLES = Y_DOM.CUSTOM_STYLES; |
|
8082 |
|
8083 if (style) { |
|
8084 if (val === null || val === '') { // normalize unsetting |
|
8085 val = ''; |
|
8086 } else if (!isNaN(new Number(val)) && re_unit.test(att)) { // number values may need a unit |
|
8087 val += Y_DOM.DEFAULT_UNIT; |
|
8088 } |
|
8089 |
|
8090 if (att in CUSTOM_STYLES) { |
|
8091 if (CUSTOM_STYLES[att].set) { |
|
8092 CUSTOM_STYLES[att].set(node, val, style); |
|
8093 return; // NOTE: return |
|
8094 } else if (typeof CUSTOM_STYLES[att] === 'string') { |
|
8095 att = CUSTOM_STYLES[att]; |
|
8096 } |
|
8097 } else if (att === '') { // unset inline styles |
|
8098 att = 'cssText'; |
|
8099 val = ''; |
|
8100 } |
|
8101 style[att] = val; |
|
8102 } |
|
8103 }, |
|
8104 |
|
8105 /** |
|
8106 * Returns the current style value for the given property. |
|
8107 * @method getStyle |
|
8108 * @param {HTMLElement} An HTMLElement to get the style from. |
|
8109 * @param {String} att The style property to get. |
|
8110 */ |
|
8111 getStyle: function(node, att, style) { |
|
8112 style = style || node.style; |
|
8113 var CUSTOM_STYLES = Y_DOM.CUSTOM_STYLES, |
|
8114 val = ''; |
|
8115 |
|
8116 if (style) { |
|
8117 if (att in CUSTOM_STYLES) { |
|
8118 if (CUSTOM_STYLES[att].get) { |
|
8119 return CUSTOM_STYLES[att].get(node, att, style); // NOTE: return |
|
8120 } else if (typeof CUSTOM_STYLES[att] === 'string') { |
|
8121 att = CUSTOM_STYLES[att]; |
|
8122 } |
|
8123 } |
|
8124 val = style[att]; |
|
8125 if (val === '') { // TODO: is empty string sufficient? |
|
8126 val = Y_DOM[GET_COMPUTED_STYLE](node, att); |
|
8127 } |
|
8128 } |
|
8129 |
|
8130 return val; |
|
8131 }, |
|
8132 |
|
8133 /** |
|
8134 * Sets multiple style properties. |
|
8135 * @method setStyles |
|
8136 * @param {HTMLElement} node An HTMLElement to apply the styles to. |
|
8137 * @param {Object} hash An object literal of property:value pairs. |
|
8138 */ |
|
8139 setStyles: function(node, hash) { |
|
8140 var style = node.style; |
|
8141 Y.each(hash, function(v, n) { |
|
8142 Y_DOM.setStyle(node, n, v, style); |
|
8143 }, Y_DOM); |
|
8144 }, |
|
8145 |
|
8146 /** |
|
8147 * Returns the computed style for the given node. |
|
8148 * @method getComputedStyle |
|
8149 * @param {HTMLElement} An HTMLElement to get the style from. |
|
8150 * @param {String} att The style property to get. |
|
8151 * @return {String} The computed value of the style property. |
|
8152 */ |
|
8153 getComputedStyle: function(node, att) { |
|
8154 var val = '', |
|
8155 doc = node[OWNER_DOCUMENT], |
|
8156 computed; |
|
8157 |
|
8158 if (node[STYLE] && doc[DEFAULT_VIEW] && doc[DEFAULT_VIEW][GET_COMPUTED_STYLE]) { |
|
8159 computed = doc[DEFAULT_VIEW][GET_COMPUTED_STYLE](node, null); |
|
8160 if (computed) { // FF may be null in some cases (ticket #2530548) |
|
8161 val = computed[att]; |
|
8162 } |
|
8163 } |
|
8164 return val; |
|
8165 } |
|
8166 }); |
|
8167 |
|
8168 // normalize reserved word float alternatives ("cssFloat" or "styleFloat") |
|
8169 if (DOCUMENT[DOCUMENT_ELEMENT][STYLE][CSS_FLOAT] !== UNDEFINED) { |
|
8170 Y_DOM.CUSTOM_STYLES[FLOAT] = CSS_FLOAT; |
|
8171 } else if (DOCUMENT[DOCUMENT_ELEMENT][STYLE][STYLE_FLOAT] !== UNDEFINED) { |
|
8172 Y_DOM.CUSTOM_STYLES[FLOAT] = STYLE_FLOAT; |
|
8173 } |
|
8174 |
|
8175 // fix opera computedStyle default color unit (convert to rgb) |
|
8176 if (Y.UA.opera) { |
|
8177 Y_DOM[GET_COMPUTED_STYLE] = function(node, att) { |
|
8178 var view = node[OWNER_DOCUMENT][DEFAULT_VIEW], |
|
8179 val = view[GET_COMPUTED_STYLE](node, '')[att]; |
|
8180 |
|
8181 if (re_color.test(att)) { |
|
8182 val = Y.Color.toRGB(val); |
|
8183 } |
|
8184 |
|
8185 return val; |
|
8186 }; |
|
8187 |
|
8188 } |
|
8189 |
|
8190 // safari converts transparent to rgba(), others use "transparent" |
|
8191 if (Y.UA.webkit) { |
|
8192 Y_DOM[GET_COMPUTED_STYLE] = function(node, att) { |
|
8193 var view = node[OWNER_DOCUMENT][DEFAULT_VIEW], |
|
8194 val = view[GET_COMPUTED_STYLE](node, '')[att]; |
|
8195 |
|
8196 if (val === 'rgba(0, 0, 0, 0)') { |
|
8197 val = TRANSPARENT; |
|
8198 } |
|
8199 |
|
8200 return val; |
|
8201 }; |
|
8202 |
|
8203 } |
|
8204 |
|
8205 Y.DOM._getAttrOffset = function(node, attr) { |
|
8206 var val = Y.DOM[GET_COMPUTED_STYLE](node, attr), |
|
8207 offsetParent = node.offsetParent, |
|
8208 position, |
|
8209 parentOffset, |
|
8210 offset; |
|
8211 |
|
8212 if (val === 'auto') { |
|
8213 position = Y.DOM.getStyle(node, 'position'); |
|
8214 if (position === 'static' || position === 'relative') { |
|
8215 val = 0; |
|
8216 } else if (offsetParent && offsetParent[GET_BOUNDING_CLIENT_RECT]) { |
|
8217 parentOffset = offsetParent[GET_BOUNDING_CLIENT_RECT]()[attr]; |
|
8218 offset = node[GET_BOUNDING_CLIENT_RECT]()[attr]; |
|
8219 if (attr === 'left' || attr === 'top') { |
|
8220 val = offset - parentOffset; |
|
8221 } else { |
|
8222 val = parentOffset - node[GET_BOUNDING_CLIENT_RECT]()[attr]; |
|
8223 } |
|
8224 } |
|
8225 } |
|
8226 |
|
8227 return val; |
|
8228 }; |
|
8229 |
|
8230 Y.DOM._getOffset = function(node) { |
|
8231 var pos, |
|
8232 xy = null; |
|
8233 |
|
8234 if (node) { |
|
8235 pos = Y_DOM.getStyle(node, 'position'); |
|
8236 xy = [ |
|
8237 parseInt(Y_DOM[GET_COMPUTED_STYLE](node, 'left'), 10), |
|
8238 parseInt(Y_DOM[GET_COMPUTED_STYLE](node, 'top'), 10) |
|
8239 ]; |
|
8240 |
|
8241 if ( isNaN(xy[0]) ) { // in case of 'auto' |
|
8242 xy[0] = parseInt(Y_DOM.getStyle(node, 'left'), 10); // try inline |
|
8243 if ( isNaN(xy[0]) ) { // default to offset value |
|
8244 xy[0] = (pos === 'relative') ? 0 : node.offsetLeft || 0; |
|
8245 } |
|
8246 } |
|
8247 |
|
8248 if ( isNaN(xy[1]) ) { // in case of 'auto' |
|
8249 xy[1] = parseInt(Y_DOM.getStyle(node, 'top'), 10); // try inline |
|
8250 if ( isNaN(xy[1]) ) { // default to offset value |
|
8251 xy[1] = (pos === 'relative') ? 0 : node.offsetTop || 0; |
|
8252 } |
|
8253 } |
|
8254 } |
|
8255 |
|
8256 return xy; |
|
8257 |
|
8258 }; |
|
8259 |
|
8260 Y_DOM.CUSTOM_STYLES.transform = { |
|
8261 set: function(node, val, style) { |
|
8262 style[TRANSFORM] = val; |
|
8263 }, |
|
8264 |
|
8265 get: function(node, style) { |
|
8266 return Y_DOM[GET_COMPUTED_STYLE](node, TRANSFORM); |
|
8267 } |
|
8268 }; |
|
8269 |
|
8270 Y_DOM.CUSTOM_STYLES.transformOrigin = { |
|
8271 set: function(node, val, style) { |
|
8272 style[TRANSFORMORIGIN] = val; |
|
8273 }, |
|
8274 |
|
8275 get: function(node, style) { |
|
8276 return Y_DOM[GET_COMPUTED_STYLE](node, TRANSFORMORIGIN); |
|
8277 } |
|
8278 }; |
|
8279 |
|
8280 |
|
8281 })(Y); |
|
8282 |
|
8283 |
|
8284 }, '@VERSION@', {"requires": ["dom-base", "color-base"]}); |
|
8285 YUI.add('dom-style-ie', function (Y, NAME) { |
|
8286 |
|
8287 (function(Y) { |
|
8288 var HAS_LAYOUT = 'hasLayout', |
|
8289 PX = 'px', |
|
8290 FILTER = 'filter', |
|
8291 FILTERS = 'filters', |
|
8292 OPACITY = 'opacity', |
|
8293 AUTO = 'auto', |
|
8294 |
|
8295 BORDER_WIDTH = 'borderWidth', |
|
8296 BORDER_TOP_WIDTH = 'borderTopWidth', |
|
8297 BORDER_RIGHT_WIDTH = 'borderRightWidth', |
|
8298 BORDER_BOTTOM_WIDTH = 'borderBottomWidth', |
|
8299 BORDER_LEFT_WIDTH = 'borderLeftWidth', |
|
8300 WIDTH = 'width', |
|
8301 HEIGHT = 'height', |
|
8302 TRANSPARENT = 'transparent', |
|
8303 VISIBLE = 'visible', |
|
8304 GET_COMPUTED_STYLE = 'getComputedStyle', |
|
8305 UNDEFINED = undefined, |
|
8306 documentElement = Y.config.doc.documentElement, |
|
8307 |
|
8308 testFeature = Y.Features.test, |
|
8309 addFeature = Y.Features.add, |
|
8310 |
|
8311 // TODO: unit-less lineHeight (e.g. 1.22) |
|
8312 re_unit = /^(\d[.\d]*)+(em|ex|px|gd|rem|vw|vh|vm|ch|mm|cm|in|pt|pc|deg|rad|ms|s|hz|khz|%){1}?/i, |
|
8313 |
|
8314 isIE8 = (Y.UA.ie >= 8), |
|
8315 |
|
8316 _getStyleObj = function(node) { |
|
8317 return node.currentStyle || node.style; |
|
8318 }, |
|
8319 |
|
8320 ComputedStyle = { |
|
8321 CUSTOM_STYLES: {}, |
|
8322 |
|
8323 get: function(el, property) { |
|
8324 var value = '', |
|
8325 current; |
|
8326 |
|
8327 if (el) { |
|
8328 current = _getStyleObj(el)[property]; |
|
8329 |
|
8330 if (property === OPACITY && Y.DOM.CUSTOM_STYLES[OPACITY]) { |
|
8331 value = Y.DOM.CUSTOM_STYLES[OPACITY].get(el); |
|
8332 } else if (!current || (current.indexOf && current.indexOf(PX) > -1)) { // no need to convert |
|
8333 value = current; |
|
8334 } else if (Y.DOM.IE.COMPUTED[property]) { // use compute function |
|
8335 value = Y.DOM.IE.COMPUTED[property](el, property); |
|
8336 } else if (re_unit.test(current)) { // convert to pixel |
|
8337 value = ComputedStyle.getPixel(el, property) + PX; |
|
8338 } else { |
|
8339 value = current; |
|
8340 } |
|
8341 } |
|
8342 |
|
8343 return value; |
|
8344 }, |
|
8345 |
|
8346 sizeOffsets: { |
|
8347 width: ['Left', 'Right'], |
|
8348 height: ['Top', 'Bottom'], |
|
8349 top: ['Top'], |
|
8350 bottom: ['Bottom'] |
|
8351 }, |
|
8352 |
|
8353 getOffset: function(el, prop) { |
|
8354 var current = _getStyleObj(el)[prop], // value of "width", "top", etc. |
|
8355 capped = prop.charAt(0).toUpperCase() + prop.substr(1), // "Width", "Top", etc. |
|
8356 offset = 'offset' + capped, // "offsetWidth", "offsetTop", etc. |
|
8357 pixel = 'pixel' + capped, // "pixelWidth", "pixelTop", etc. |
|
8358 sizeOffsets = ComputedStyle.sizeOffsets[prop], |
|
8359 mode = el.ownerDocument.compatMode, |
|
8360 value = ''; |
|
8361 |
|
8362 // IE pixelWidth incorrect for percent |
|
8363 // manually compute by subtracting padding and border from offset size |
|
8364 // NOTE: clientWidth/Height (size minus border) is 0 when current === AUTO so offsetHeight is used |
|
8365 // reverting to auto from auto causes position stacking issues (old impl) |
|
8366 if (current === AUTO || current.indexOf('%') > -1) { |
|
8367 value = el['offset' + capped]; |
|
8368 |
|
8369 if (mode !== 'BackCompat') { |
|
8370 if (sizeOffsets[0]) { |
|
8371 value -= ComputedStyle.getPixel(el, 'padding' + sizeOffsets[0]); |
|
8372 value -= ComputedStyle.getBorderWidth(el, 'border' + sizeOffsets[0] + 'Width', 1); |
|
8373 } |
|
8374 |
|
8375 if (sizeOffsets[1]) { |
|
8376 value -= ComputedStyle.getPixel(el, 'padding' + sizeOffsets[1]); |
|
8377 value -= ComputedStyle.getBorderWidth(el, 'border' + sizeOffsets[1] + 'Width', 1); |
|
8378 } |
|
8379 } |
|
8380 |
|
8381 } else { // use style.pixelWidth, etc. to convert to pixels |
|
8382 // need to map style.width to currentStyle (no currentStyle.pixelWidth) |
|
8383 if (!el.style[pixel] && !el.style[prop]) { |
|
8384 el.style[prop] = current; |
|
8385 } |
|
8386 value = el.style[pixel]; |
|
8387 |
|
8388 } |
|
8389 return value + PX; |
|
8390 }, |
|
8391 |
|
8392 borderMap: { |
|
8393 thin: (isIE8) ? '1px' : '2px', |
|
8394 medium: (isIE8) ? '3px': '4px', |
|
8395 thick: (isIE8) ? '5px' : '6px' |
|
8396 }, |
|
8397 |
|
8398 getBorderWidth: function(el, property, omitUnit) { |
|
8399 var unit = omitUnit ? '' : PX, |
|
8400 current = el.currentStyle[property]; |
|
8401 |
|
8402 if (current.indexOf(PX) < 0) { // look up keywords if a border exists |
|
8403 if (ComputedStyle.borderMap[current] && |
|
8404 el.currentStyle.borderStyle !== 'none') { |
|
8405 current = ComputedStyle.borderMap[current]; |
|
8406 } else { // otherwise no border (default is "medium") |
|
8407 current = 0; |
|
8408 } |
|
8409 } |
|
8410 return (omitUnit) ? parseFloat(current) : current; |
|
8411 }, |
|
8412 |
|
8413 getPixel: function(node, att) { |
|
8414 // use pixelRight to convert to px |
|
8415 var val = null, |
|
8416 style = _getStyleObj(node), |
|
8417 styleRight = style.right, |
|
8418 current = style[att]; |
|
8419 |
|
8420 node.style.right = current; |
|
8421 val = node.style.pixelRight; |
|
8422 node.style.right = styleRight; // revert |
|
8423 |
|
8424 return val; |
|
8425 }, |
|
8426 |
|
8427 getMargin: function(node, att) { |
|
8428 var val, |
|
8429 style = _getStyleObj(node); |
|
8430 |
|
8431 if (style[att] == AUTO) { |
|
8432 val = 0; |
|
8433 } else { |
|
8434 val = ComputedStyle.getPixel(node, att); |
|
8435 } |
|
8436 return val + PX; |
|
8437 }, |
|
8438 |
|
8439 getVisibility: function(node, att) { |
|
8440 var current; |
|
8441 while ( (current = node.currentStyle) && current[att] == 'inherit') { // NOTE: assignment in test |
|
8442 node = node.parentNode; |
|
8443 } |
|
8444 return (current) ? current[att] : VISIBLE; |
|
8445 }, |
|
8446 |
|
8447 getColor: function(node, att) { |
|
8448 var current = _getStyleObj(node)[att]; |
|
8449 |
|
8450 if (!current || current === TRANSPARENT) { |
|
8451 Y.DOM.elementByAxis(node, 'parentNode', null, function(parent) { |
|
8452 current = _getStyleObj(parent)[att]; |
|
8453 if (current && current !== TRANSPARENT) { |
|
8454 node = parent; |
|
8455 return true; |
|
8456 } |
|
8457 }); |
|
8458 } |
|
8459 |
|
8460 return Y.Color.toRGB(current); |
|
8461 }, |
|
8462 |
|
8463 getBorderColor: function(node, att) { |
|
8464 var current = _getStyleObj(node), |
|
8465 val = current[att] || current.color; |
|
8466 return Y.Color.toRGB(Y.Color.toHex(val)); |
|
8467 } |
|
8468 }, |
|
8469 |
|
8470 //fontSize: getPixelFont, |
|
8471 IEComputed = {}; |
|
8472 |
|
8473 addFeature('style', 'computedStyle', { |
|
8474 test: function() { |
|
8475 return 'getComputedStyle' in Y.config.win; |
|
8476 } |
|
8477 }); |
|
8478 |
|
8479 addFeature('style', 'opacity', { |
|
8480 test: function() { |
|
8481 return 'opacity' in documentElement.style; |
|
8482 } |
|
8483 }); |
|
8484 |
|
8485 addFeature('style', 'filter', { |
|
8486 test: function() { |
|
8487 return 'filters' in documentElement; |
|
8488 } |
|
8489 }); |
|
8490 |
|
8491 // use alpha filter for IE opacity |
|
8492 if (!testFeature('style', 'opacity') && testFeature('style', 'filter')) { |
|
8493 Y.DOM.CUSTOM_STYLES[OPACITY] = { |
|
8494 get: function(node) { |
|
8495 var val = 100; |
|
8496 try { // will error if no DXImageTransform |
|
8497 val = node[FILTERS]['DXImageTransform.Microsoft.Alpha'][OPACITY]; |
|
8498 |
|
8499 } catch(e) { |
|
8500 try { // make sure its in the document |
|
8501 val = node[FILTERS]('alpha')[OPACITY]; |
|
8502 } catch(err) { |
|
8503 } |
|
8504 } |
|
8505 return val / 100; |
|
8506 }, |
|
8507 |
|
8508 set: function(node, val, style) { |
|
8509 var current, |
|
8510 styleObj = _getStyleObj(node), |
|
8511 currentFilter = styleObj[FILTER]; |
|
8512 |
|
8513 style = style || node.style; |
|
8514 if (val === '') { // normalize inline style behavior |
|
8515 current = (OPACITY in styleObj) ? styleObj[OPACITY] : 1; // revert to original opacity |
|
8516 val = current; |
|
8517 } |
|
8518 |
|
8519 if (typeof currentFilter == 'string') { // in case not appended |
|
8520 style[FILTER] = currentFilter.replace(/alpha([^)]*\))/gi, '') + |
|
8521 ((val < 1) ? 'alpha(' + OPACITY + '=' + val * 100 + ')' : ''); |
|
8522 |
|
8523 if (!style[FILTER]) { |
|
8524 style.removeAttribute(FILTER); |
|
8525 } |
|
8526 |
|
8527 if (!styleObj[HAS_LAYOUT]) { |
|
8528 style.zoom = 1; // needs layout |
|
8529 } |
|
8530 } |
|
8531 } |
|
8532 }; |
|
8533 } |
|
8534 |
|
8535 try { |
|
8536 Y.config.doc.createElement('div').style.height = '-1px'; |
|
8537 } catch(e) { // IE throws error on invalid style set; trap common cases |
|
8538 Y.DOM.CUSTOM_STYLES.height = { |
|
8539 set: function(node, val, style) { |
|
8540 var floatVal = parseFloat(val); |
|
8541 if (floatVal >= 0 || val === 'auto' || val === '') { |
|
8542 style.height = val; |
|
8543 } else { |
|
8544 } |
|
8545 } |
|
8546 }; |
|
8547 |
|
8548 Y.DOM.CUSTOM_STYLES.width = { |
|
8549 set: function(node, val, style) { |
|
8550 var floatVal = parseFloat(val); |
|
8551 if (floatVal >= 0 || val === 'auto' || val === '') { |
|
8552 style.width = val; |
|
8553 } else { |
|
8554 } |
|
8555 } |
|
8556 }; |
|
8557 } |
|
8558 |
|
8559 if (!testFeature('style', 'computedStyle')) { |
|
8560 // TODO: top, right, bottom, left |
|
8561 IEComputed[WIDTH] = IEComputed[HEIGHT] = ComputedStyle.getOffset; |
|
8562 |
|
8563 IEComputed.color = IEComputed.backgroundColor = ComputedStyle.getColor; |
|
8564 |
|
8565 IEComputed[BORDER_WIDTH] = IEComputed[BORDER_TOP_WIDTH] = IEComputed[BORDER_RIGHT_WIDTH] = |
|
8566 IEComputed[BORDER_BOTTOM_WIDTH] = IEComputed[BORDER_LEFT_WIDTH] = |
|
8567 ComputedStyle.getBorderWidth; |
|
8568 |
|
8569 IEComputed.marginTop = IEComputed.marginRight = IEComputed.marginBottom = |
|
8570 IEComputed.marginLeft = ComputedStyle.getMargin; |
|
8571 |
|
8572 IEComputed.visibility = ComputedStyle.getVisibility; |
|
8573 IEComputed.borderColor = IEComputed.borderTopColor = |
|
8574 IEComputed.borderRightColor = IEComputed.borderBottomColor = |
|
8575 IEComputed.borderLeftColor = ComputedStyle.getBorderColor; |
|
8576 |
|
8577 Y.DOM[GET_COMPUTED_STYLE] = ComputedStyle.get; |
|
8578 |
|
8579 Y.namespace('DOM.IE'); |
|
8580 Y.DOM.IE.COMPUTED = IEComputed; |
|
8581 Y.DOM.IE.ComputedStyle = ComputedStyle; |
|
8582 } |
|
8583 |
|
8584 })(Y); |
|
8585 |
|
8586 |
|
8587 }, '@VERSION@', {"requires": ["dom-style"]}); |
|
8588 YUI.add('dom-screen', function (Y, NAME) { |
|
8589 |
|
8590 (function(Y) { |
|
8591 |
|
8592 /** |
|
8593 * Adds position and region management functionality to DOM. |
|
8594 * @module dom |
|
8595 * @submodule dom-screen |
|
8596 * @for DOM |
|
8597 */ |
|
8598 |
|
8599 var DOCUMENT_ELEMENT = 'documentElement', |
|
8600 COMPAT_MODE = 'compatMode', |
|
8601 POSITION = 'position', |
|
8602 FIXED = 'fixed', |
|
8603 RELATIVE = 'relative', |
|
8604 LEFT = 'left', |
|
8605 TOP = 'top', |
|
8606 _BACK_COMPAT = 'BackCompat', |
|
8607 MEDIUM = 'medium', |
|
8608 BORDER_LEFT_WIDTH = 'borderLeftWidth', |
|
8609 BORDER_TOP_WIDTH = 'borderTopWidth', |
|
8610 GET_BOUNDING_CLIENT_RECT = 'getBoundingClientRect', |
|
8611 GET_COMPUTED_STYLE = 'getComputedStyle', |
|
8612 |
|
8613 Y_DOM = Y.DOM, |
|
8614 |
|
8615 // TODO: how about thead/tbody/tfoot/tr? |
|
8616 // TODO: does caption matter? |
|
8617 RE_TABLE = /^t(?:able|d|h)$/i, |
|
8618 |
|
8619 SCROLL_NODE; |
|
8620 |
|
8621 if (Y.UA.ie) { |
|
8622 if (Y.config.doc[COMPAT_MODE] !== 'BackCompat') { |
|
8623 SCROLL_NODE = DOCUMENT_ELEMENT; |
|
8624 } else { |
|
8625 SCROLL_NODE = 'body'; |
|
8626 } |
|
8627 } |
|
8628 |
|
8629 Y.mix(Y_DOM, { |
|
8630 /** |
|
8631 * Returns the inner height of the viewport (exludes scrollbar). |
|
8632 * @method winHeight |
|
8633 * @return {Number} The current height of the viewport. |
|
8634 */ |
|
8635 winHeight: function(node) { |
|
8636 var h = Y_DOM._getWinSize(node).height; |
|
8637 return h; |
|
8638 }, |
|
8639 |
|
8640 /** |
|
8641 * Returns the inner width of the viewport (exludes scrollbar). |
|
8642 * @method winWidth |
|
8643 * @return {Number} The current width of the viewport. |
|
8644 */ |
|
8645 winWidth: function(node) { |
|
8646 var w = Y_DOM._getWinSize(node).width; |
|
8647 return w; |
|
8648 }, |
|
8649 |
|
8650 /** |
|
8651 * Document height |
|
8652 * @method docHeight |
|
8653 * @return {Number} The current height of the document. |
|
8654 */ |
|
8655 docHeight: function(node) { |
|
8656 var h = Y_DOM._getDocSize(node).height; |
|
8657 return Math.max(h, Y_DOM._getWinSize(node).height); |
|
8658 }, |
|
8659 |
|
8660 /** |
|
8661 * Document width |
|
8662 * @method docWidth |
|
8663 * @return {Number} The current width of the document. |
|
8664 */ |
|
8665 docWidth: function(node) { |
|
8666 var w = Y_DOM._getDocSize(node).width; |
|
8667 return Math.max(w, Y_DOM._getWinSize(node).width); |
|
8668 }, |
|
8669 |
|
8670 /** |
|
8671 * Amount page has been scroll horizontally |
|
8672 * @method docScrollX |
|
8673 * @return {Number} The current amount the screen is scrolled horizontally. |
|
8674 */ |
|
8675 docScrollX: function(node, doc) { |
|
8676 doc = doc || (node) ? Y_DOM._getDoc(node) : Y.config.doc; // perf optimization |
|
8677 var dv = doc.defaultView, |
|
8678 pageOffset = (dv) ? dv.pageXOffset : 0; |
|
8679 return Math.max(doc[DOCUMENT_ELEMENT].scrollLeft, doc.body.scrollLeft, pageOffset); |
|
8680 }, |
|
8681 |
|
8682 /** |
|
8683 * Amount page has been scroll vertically |
|
8684 * @method docScrollY |
|
8685 * @return {Number} The current amount the screen is scrolled vertically. |
|
8686 */ |
|
8687 docScrollY: function(node, doc) { |
|
8688 doc = doc || (node) ? Y_DOM._getDoc(node) : Y.config.doc; // perf optimization |
|
8689 var dv = doc.defaultView, |
|
8690 pageOffset = (dv) ? dv.pageYOffset : 0; |
|
8691 return Math.max(doc[DOCUMENT_ELEMENT].scrollTop, doc.body.scrollTop, pageOffset); |
|
8692 }, |
|
8693 |
|
8694 /** |
|
8695 * Gets the current position of an element based on page coordinates. |
|
8696 * Element must be part of the DOM tree to have page coordinates |
|
8697 * (display:none or elements not appended return false). |
|
8698 * @method getXY |
|
8699 * @param element The target element |
|
8700 * @return {Array} The XY position of the element |
|
8701 |
|
8702 TODO: test inDocument/display? |
|
8703 */ |
|
8704 getXY: function() { |
|
8705 if (Y.config.doc[DOCUMENT_ELEMENT][GET_BOUNDING_CLIENT_RECT]) { |
|
8706 return function(node) { |
|
8707 var xy = null, |
|
8708 scrollLeft, |
|
8709 scrollTop, |
|
8710 mode, |
|
8711 box, |
|
8712 offX, |
|
8713 offY, |
|
8714 doc, |
|
8715 win, |
|
8716 inDoc, |
|
8717 rootNode; |
|
8718 |
|
8719 if (node && node.tagName) { |
|
8720 doc = node.ownerDocument; |
|
8721 mode = doc[COMPAT_MODE]; |
|
8722 |
|
8723 if (mode !== _BACK_COMPAT) { |
|
8724 rootNode = doc[DOCUMENT_ELEMENT]; |
|
8725 } else { |
|
8726 rootNode = doc.body; |
|
8727 } |
|
8728 |
|
8729 // inline inDoc check for perf |
|
8730 if (rootNode.contains) { |
|
8731 inDoc = rootNode.contains(node); |
|
8732 } else { |
|
8733 inDoc = Y.DOM.contains(rootNode, node); |
|
8734 } |
|
8735 |
|
8736 if (inDoc) { |
|
8737 win = doc.defaultView; |
|
8738 |
|
8739 // inline scroll calc for perf |
|
8740 if (win && 'pageXOffset' in win) { |
|
8741 scrollLeft = win.pageXOffset; |
|
8742 scrollTop = win.pageYOffset; |
|
8743 } else { |
|
8744 scrollLeft = (SCROLL_NODE) ? doc[SCROLL_NODE].scrollLeft : Y_DOM.docScrollX(node, doc); |
|
8745 scrollTop = (SCROLL_NODE) ? doc[SCROLL_NODE].scrollTop : Y_DOM.docScrollY(node, doc); |
|
8746 } |
|
8747 |
|
8748 if (Y.UA.ie) { // IE < 8, quirks, or compatMode |
|
8749 if (!doc.documentMode || doc.documentMode < 8 || mode === _BACK_COMPAT) { |
|
8750 offX = rootNode.clientLeft; |
|
8751 offY = rootNode.clientTop; |
|
8752 } |
|
8753 } |
|
8754 box = node[GET_BOUNDING_CLIENT_RECT](); |
|
8755 xy = [box.left, box.top]; |
|
8756 |
|
8757 if (offX || offY) { |
|
8758 xy[0] -= offX; |
|
8759 xy[1] -= offY; |
|
8760 |
|
8761 } |
|
8762 if ((scrollTop || scrollLeft)) { |
|
8763 if (!Y.UA.ios || (Y.UA.ios >= 4.2)) { |
|
8764 xy[0] += scrollLeft; |
|
8765 xy[1] += scrollTop; |
|
8766 } |
|
8767 |
|
8768 } |
|
8769 } else { |
|
8770 xy = Y_DOM._getOffset(node); |
|
8771 } |
|
8772 } |
|
8773 return xy; |
|
8774 }; |
|
8775 } else { |
|
8776 return function(node) { // manually calculate by crawling up offsetParents |
|
8777 //Calculate the Top and Left border sizes (assumes pixels) |
|
8778 var xy = null, |
|
8779 doc, |
|
8780 parentNode, |
|
8781 bCheck, |
|
8782 scrollTop, |
|
8783 scrollLeft; |
|
8784 |
|
8785 if (node) { |
|
8786 if (Y_DOM.inDoc(node)) { |
|
8787 xy = [node.offsetLeft, node.offsetTop]; |
|
8788 doc = node.ownerDocument; |
|
8789 parentNode = node; |
|
8790 // TODO: refactor with !! or just falsey |
|
8791 bCheck = ((Y.UA.gecko || Y.UA.webkit > 519) ? true : false); |
|
8792 |
|
8793 // TODO: worth refactoring for TOP/LEFT only? |
|
8794 while ((parentNode = parentNode.offsetParent)) { |
|
8795 xy[0] += parentNode.offsetLeft; |
|
8796 xy[1] += parentNode.offsetTop; |
|
8797 if (bCheck) { |
|
8798 xy = Y_DOM._calcBorders(parentNode, xy); |
|
8799 } |
|
8800 } |
|
8801 |
|
8802 // account for any scrolled ancestors |
|
8803 if (Y_DOM.getStyle(node, POSITION) != FIXED) { |
|
8804 parentNode = node; |
|
8805 |
|
8806 while ((parentNode = parentNode.parentNode)) { |
|
8807 scrollTop = parentNode.scrollTop; |
|
8808 scrollLeft = parentNode.scrollLeft; |
|
8809 |
|
8810 //Firefox does something funky with borders when overflow is not visible. |
|
8811 if (Y.UA.gecko && (Y_DOM.getStyle(parentNode, 'overflow') !== 'visible')) { |
|
8812 xy = Y_DOM._calcBorders(parentNode, xy); |
|
8813 } |
|
8814 |
|
8815 |
|
8816 if (scrollTop || scrollLeft) { |
|
8817 xy[0] -= scrollLeft; |
|
8818 xy[1] -= scrollTop; |
|
8819 } |
|
8820 } |
|
8821 xy[0] += Y_DOM.docScrollX(node, doc); |
|
8822 xy[1] += Y_DOM.docScrollY(node, doc); |
|
8823 |
|
8824 } else { |
|
8825 //Fix FIXED position -- add scrollbars |
|
8826 xy[0] += Y_DOM.docScrollX(node, doc); |
|
8827 xy[1] += Y_DOM.docScrollY(node, doc); |
|
8828 } |
|
8829 } else { |
|
8830 xy = Y_DOM._getOffset(node); |
|
8831 } |
|
8832 } |
|
8833 |
|
8834 return xy; |
|
8835 }; |
|
8836 } |
|
8837 }(),// NOTE: Executing for loadtime branching |
|
8838 |
|
8839 /** |
|
8840 Gets the width of vertical scrollbars on overflowed containers in the body |
|
8841 content. |
|
8842 |
|
8843 @method getScrollbarWidth |
|
8844 @return {Number} Pixel width of a scrollbar in the current browser |
|
8845 **/ |
|
8846 getScrollbarWidth: Y.cached(function () { |
|
8847 var doc = Y.config.doc, |
|
8848 testNode = doc.createElement('div'), |
|
8849 body = doc.getElementsByTagName('body')[0], |
|
8850 // 0.1 because cached doesn't support falsy refetch values |
|
8851 width = 0.1; |
|
8852 |
|
8853 if (body) { |
|
8854 testNode.style.cssText = "position:absolute;visibility:hidden;overflow:scroll;width:20px;"; |
|
8855 testNode.appendChild(doc.createElement('p')).style.height = '1px'; |
|
8856 body.insertBefore(testNode, body.firstChild); |
|
8857 width = testNode.offsetWidth - testNode.clientWidth; |
|
8858 |
|
8859 body.removeChild(testNode); |
|
8860 } |
|
8861 |
|
8862 return width; |
|
8863 }, null, 0.1), |
|
8864 |
|
8865 /** |
|
8866 * Gets the current X position of an element based on page coordinates. |
|
8867 * Element must be part of the DOM tree to have page coordinates |
|
8868 * (display:none or elements not appended return false). |
|
8869 * @method getX |
|
8870 * @param element The target element |
|
8871 * @return {Number} The X position of the element |
|
8872 */ |
|
8873 |
|
8874 getX: function(node) { |
|
8875 return Y_DOM.getXY(node)[0]; |
|
8876 }, |
|
8877 |
|
8878 /** |
|
8879 * Gets the current Y position of an element based on page coordinates. |
|
8880 * Element must be part of the DOM tree to have page coordinates |
|
8881 * (display:none or elements not appended return false). |
|
8882 * @method getY |
|
8883 * @param element The target element |
|
8884 * @return {Number} The Y position of the element |
|
8885 */ |
|
8886 |
|
8887 getY: function(node) { |
|
8888 return Y_DOM.getXY(node)[1]; |
|
8889 }, |
|
8890 |
|
8891 /** |
|
8892 * Set the position of an html element in page coordinates. |
|
8893 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false). |
|
8894 * @method setXY |
|
8895 * @param element The target element |
|
8896 * @param {Array} xy Contains X & Y values for new position (coordinates are page-based) |
|
8897 * @param {Boolean} noRetry By default we try and set the position a second time if the first fails |
|
8898 */ |
|
8899 setXY: function(node, xy, noRetry) { |
|
8900 var setStyle = Y_DOM.setStyle, |
|
8901 pos, |
|
8902 delta, |
|
8903 newXY, |
|
8904 currentXY; |
|
8905 |
|
8906 if (node && xy) { |
|
8907 pos = Y_DOM.getStyle(node, POSITION); |
|
8908 |
|
8909 delta = Y_DOM._getOffset(node); |
|
8910 if (pos == 'static') { // default to relative |
|
8911 pos = RELATIVE; |
|
8912 setStyle(node, POSITION, pos); |
|
8913 } |
|
8914 currentXY = Y_DOM.getXY(node); |
|
8915 |
|
8916 if (xy[0] !== null) { |
|
8917 setStyle(node, LEFT, xy[0] - currentXY[0] + delta[0] + 'px'); |
|
8918 } |
|
8919 |
|
8920 if (xy[1] !== null) { |
|
8921 setStyle(node, TOP, xy[1] - currentXY[1] + delta[1] + 'px'); |
|
8922 } |
|
8923 |
|
8924 if (!noRetry) { |
|
8925 newXY = Y_DOM.getXY(node); |
|
8926 if (newXY[0] !== xy[0] || newXY[1] !== xy[1]) { |
|
8927 Y_DOM.setXY(node, xy, true); |
|
8928 } |
|
8929 } |
|
8930 |
|
8931 } else { |
|
8932 } |
|
8933 }, |
|
8934 |
|
8935 /** |
|
8936 * Set the X position of an html element in page coordinates, regardless of how the element is positioned. |
|
8937 * The element(s) must be part of the DOM tree to have page coordinates (display:none or elements not appended return false). |
|
8938 * @method setX |
|
8939 * @param element The target element |
|
8940 * @param {Number} x The X values for new position (coordinates are page-based) |
|
8941 */ |
|
8942 setX: function(node, x) { |
|
8943 return Y_DOM.setXY(node, [x, null]); |
|
8944 }, |
|
8945 |
|
8946 /** |
|
8947 * Set the Y position of an html element in page coordinates, regardless of how the element is positioned. |
|
8948 * The element(s) must be part of the DOM tree to have page coordinates (display:none or elements not appended return false). |
|
8949 * @method setY |
|
8950 * @param element The target element |
|
8951 * @param {Number} y The Y values for new position (coordinates are page-based) |
|
8952 */ |
|
8953 setY: function(node, y) { |
|
8954 return Y_DOM.setXY(node, [null, y]); |
|
8955 }, |
|
8956 |
|
8957 /** |
|
8958 * @method swapXY |
|
8959 * @description Swap the xy position with another node |
|
8960 * @param {Node} node The node to swap with |
|
8961 * @param {Node} otherNode The other node to swap with |
|
8962 * @return {Node} |
|
8963 */ |
|
8964 swapXY: function(node, otherNode) { |
|
8965 var xy = Y_DOM.getXY(node); |
|
8966 Y_DOM.setXY(node, Y_DOM.getXY(otherNode)); |
|
8967 Y_DOM.setXY(otherNode, xy); |
|
8968 }, |
|
8969 |
|
8970 _calcBorders: function(node, xy2) { |
|
8971 var t = parseInt(Y_DOM[GET_COMPUTED_STYLE](node, BORDER_TOP_WIDTH), 10) || 0, |
|
8972 l = parseInt(Y_DOM[GET_COMPUTED_STYLE](node, BORDER_LEFT_WIDTH), 10) || 0; |
|
8973 if (Y.UA.gecko) { |
|
8974 if (RE_TABLE.test(node.tagName)) { |
|
8975 t = 0; |
|
8976 l = 0; |
|
8977 } |
|
8978 } |
|
8979 xy2[0] += l; |
|
8980 xy2[1] += t; |
|
8981 return xy2; |
|
8982 }, |
|
8983 |
|
8984 _getWinSize: function(node, doc) { |
|
8985 doc = doc || (node) ? Y_DOM._getDoc(node) : Y.config.doc; |
|
8986 var win = doc.defaultView || doc.parentWindow, |
|
8987 mode = doc[COMPAT_MODE], |
|
8988 h = win.innerHeight, |
|
8989 w = win.innerWidth, |
|
8990 root = doc[DOCUMENT_ELEMENT]; |
|
8991 |
|
8992 if ( mode && !Y.UA.opera ) { // IE, Gecko |
|
8993 if (mode != 'CSS1Compat') { // Quirks |
|
8994 root = doc.body; |
|
8995 } |
|
8996 h = root.clientHeight; |
|
8997 w = root.clientWidth; |
|
8998 } |
|
8999 return { height: h, width: w }; |
|
9000 }, |
|
9001 |
|
9002 _getDocSize: function(node) { |
|
9003 var doc = (node) ? Y_DOM._getDoc(node) : Y.config.doc, |
|
9004 root = doc[DOCUMENT_ELEMENT]; |
|
9005 |
|
9006 if (doc[COMPAT_MODE] != 'CSS1Compat') { |
|
9007 root = doc.body; |
|
9008 } |
|
9009 |
|
9010 return { height: root.scrollHeight, width: root.scrollWidth }; |
|
9011 } |
|
9012 }); |
|
9013 |
|
9014 })(Y); |
|
9015 (function(Y) { |
|
9016 var TOP = 'top', |
|
9017 RIGHT = 'right', |
|
9018 BOTTOM = 'bottom', |
|
9019 LEFT = 'left', |
|
9020 |
|
9021 getOffsets = function(r1, r2) { |
|
9022 var t = Math.max(r1[TOP], r2[TOP]), |
|
9023 r = Math.min(r1[RIGHT], r2[RIGHT]), |
|
9024 b = Math.min(r1[BOTTOM], r2[BOTTOM]), |
|
9025 l = Math.max(r1[LEFT], r2[LEFT]), |
|
9026 ret = {}; |
|
9027 |
|
9028 ret[TOP] = t; |
|
9029 ret[RIGHT] = r; |
|
9030 ret[BOTTOM] = b; |
|
9031 ret[LEFT] = l; |
|
9032 return ret; |
|
9033 }, |
|
9034 |
|
9035 DOM = Y.DOM; |
|
9036 |
|
9037 Y.mix(DOM, { |
|
9038 /** |
|
9039 * Returns an Object literal containing the following about this element: (top, right, bottom, left) |
|
9040 * @for DOM |
|
9041 * @method region |
|
9042 * @param {HTMLElement} element The DOM element. |
|
9043 * @return {Object} Object literal containing the following about this element: (top, right, bottom, left) |
|
9044 */ |
|
9045 region: function(node) { |
|
9046 var xy = DOM.getXY(node), |
|
9047 ret = false; |
|
9048 |
|
9049 if (node && xy) { |
|
9050 ret = DOM._getRegion( |
|
9051 xy[1], // top |
|
9052 xy[0] + node.offsetWidth, // right |
|
9053 xy[1] + node.offsetHeight, // bottom |
|
9054 xy[0] // left |
|
9055 ); |
|
9056 } |
|
9057 |
|
9058 return ret; |
|
9059 }, |
|
9060 |
|
9061 /** |
|
9062 * Find the intersect information for the passed nodes. |
|
9063 * @method intersect |
|
9064 * @for DOM |
|
9065 * @param {HTMLElement} element The first element |
|
9066 * @param {HTMLElement | Object} element2 The element or region to check the interect with |
|
9067 * @param {Object} altRegion An object literal containing the region for the first element if we already have the data (for performance e.g. DragDrop) |
|
9068 * @return {Object} Object literal containing the following intersection data: (top, right, bottom, left, area, yoff, xoff, inRegion) |
|
9069 */ |
|
9070 intersect: function(node, node2, altRegion) { |
|
9071 var r = altRegion || DOM.region(node), region = {}, |
|
9072 n = node2, |
|
9073 off; |
|
9074 |
|
9075 if (n.tagName) { |
|
9076 region = DOM.region(n); |
|
9077 } else if (Y.Lang.isObject(node2)) { |
|
9078 region = node2; |
|
9079 } else { |
|
9080 return false; |
|
9081 } |
|
9082 |
|
9083 off = getOffsets(region, r); |
|
9084 return { |
|
9085 top: off[TOP], |
|
9086 right: off[RIGHT], |
|
9087 bottom: off[BOTTOM], |
|
9088 left: off[LEFT], |
|
9089 area: ((off[BOTTOM] - off[TOP]) * (off[RIGHT] - off[LEFT])), |
|
9090 yoff: ((off[BOTTOM] - off[TOP])), |
|
9091 xoff: (off[RIGHT] - off[LEFT]), |
|
9092 inRegion: DOM.inRegion(node, node2, false, altRegion) |
|
9093 }; |
|
9094 |
|
9095 }, |
|
9096 /** |
|
9097 * Check if any part of this node is in the passed region |
|
9098 * @method inRegion |
|
9099 * @for DOM |
|
9100 * @param {Object} node The node to get the region from |
|
9101 * @param {Object} node2 The second node to get the region from or an Object literal of the region |
|
9102 * @param {Boolean} all Should all of the node be inside the region |
|
9103 * @param {Object} altRegion An object literal containing the region for this node if we already have the data (for performance e.g. DragDrop) |
|
9104 * @return {Boolean} True if in region, false if not. |
|
9105 */ |
|
9106 inRegion: function(node, node2, all, altRegion) { |
|
9107 var region = {}, |
|
9108 r = altRegion || DOM.region(node), |
|
9109 n = node2, |
|
9110 off; |
|
9111 |
|
9112 if (n.tagName) { |
|
9113 region = DOM.region(n); |
|
9114 } else if (Y.Lang.isObject(node2)) { |
|
9115 region = node2; |
|
9116 } else { |
|
9117 return false; |
|
9118 } |
|
9119 |
|
9120 if (all) { |
|
9121 return ( |
|
9122 r[LEFT] >= region[LEFT] && |
|
9123 r[RIGHT] <= region[RIGHT] && |
|
9124 r[TOP] >= region[TOP] && |
|
9125 r[BOTTOM] <= region[BOTTOM] ); |
|
9126 } else { |
|
9127 off = getOffsets(region, r); |
|
9128 if (off[BOTTOM] >= off[TOP] && off[RIGHT] >= off[LEFT]) { |
|
9129 return true; |
|
9130 } else { |
|
9131 return false; |
|
9132 } |
|
9133 |
|
9134 } |
|
9135 }, |
|
9136 |
|
9137 /** |
|
9138 * Check if any part of this element is in the viewport |
|
9139 * @method inViewportRegion |
|
9140 * @for DOM |
|
9141 * @param {HTMLElement} element The DOM element. |
|
9142 * @param {Boolean} all Should all of the node be inside the region |
|
9143 * @param {Object} altRegion An object literal containing the region for this node if we already have the data (for performance e.g. DragDrop) |
|
9144 * @return {Boolean} True if in region, false if not. |
|
9145 */ |
|
9146 inViewportRegion: function(node, all, altRegion) { |
|
9147 return DOM.inRegion(node, DOM.viewportRegion(node), all, altRegion); |
|
9148 |
|
9149 }, |
|
9150 |
|
9151 _getRegion: function(t, r, b, l) { |
|
9152 var region = {}; |
|
9153 |
|
9154 region[TOP] = region[1] = t; |
|
9155 region[LEFT] = region[0] = l; |
|
9156 region[BOTTOM] = b; |
|
9157 region[RIGHT] = r; |
|
9158 region.width = region[RIGHT] - region[LEFT]; |
|
9159 region.height = region[BOTTOM] - region[TOP]; |
|
9160 |
|
9161 return region; |
|
9162 }, |
|
9163 |
|
9164 /** |
|
9165 * Returns an Object literal containing the following about the visible region of viewport: (top, right, bottom, left) |
|
9166 * @method viewportRegion |
|
9167 * @for DOM |
|
9168 * @return {Object} Object literal containing the following about the visible region of the viewport: (top, right, bottom, left) |
|
9169 */ |
|
9170 viewportRegion: function(node) { |
|
9171 node = node || Y.config.doc.documentElement; |
|
9172 var ret = false, |
|
9173 scrollX, |
|
9174 scrollY; |
|
9175 |
|
9176 if (node) { |
|
9177 scrollX = DOM.docScrollX(node); |
|
9178 scrollY = DOM.docScrollY(node); |
|
9179 |
|
9180 ret = DOM._getRegion(scrollY, // top |
|
9181 DOM.winWidth(node) + scrollX, // right |
|
9182 scrollY + DOM.winHeight(node), // bottom |
|
9183 scrollX); // left |
|
9184 } |
|
9185 |
|
9186 return ret; |
|
9187 } |
|
9188 }); |
|
9189 })(Y); |
|
9190 |
|
9191 |
|
9192 }, '@VERSION@', {"requires": ["dom-base", "dom-style"]}); |
|
9193 YUI.add('selector-native', function (Y, NAME) { |
|
9194 |
|
9195 (function(Y) { |
|
9196 /** |
|
9197 * The selector-native module provides support for native querySelector |
|
9198 * @module dom |
|
9199 * @submodule selector-native |
|
9200 * @for Selector |
|
9201 */ |
|
9202 |
|
9203 /** |
|
9204 * Provides support for using CSS selectors to query the DOM |
|
9205 * @class Selector |
|
9206 * @static |
|
9207 * @for Selector |
|
9208 */ |
|
9209 |
|
9210 Y.namespace('Selector'); // allow native module to standalone |
|
9211 |
|
9212 var COMPARE_DOCUMENT_POSITION = 'compareDocumentPosition', |
|
9213 OWNER_DOCUMENT = 'ownerDocument'; |
|
9214 |
|
9215 var Selector = { |
|
9216 _types: { |
|
9217 esc: { |
|
9218 token: '\uE000', |
|
9219 re: /\\[:\[\]\(\)#\.\'\>+~"]/gi |
|
9220 }, |
|
9221 |
|
9222 attr: { |
|
9223 token: '\uE001', |
|
9224 re: /(\[[^\]]*\])/g |
|
9225 }, |
|
9226 |
|
9227 pseudo: { |
|
9228 token: '\uE002', |
|
9229 re: /(\([^\)]*\))/g |
|
9230 } |
|
9231 }, |
|
9232 |
|
9233 useNative: true, |
|
9234 |
|
9235 _escapeId: function(id) { |
|
9236 if (id) { |
|
9237 id = id.replace(/([:\[\]\(\)#\.'<>+~"])/g,'\\$1'); |
|
9238 } |
|
9239 return id; |
|
9240 }, |
|
9241 |
|
9242 _compare: ('sourceIndex' in Y.config.doc.documentElement) ? |
|
9243 function(nodeA, nodeB) { |
|
9244 var a = nodeA.sourceIndex, |
|
9245 b = nodeB.sourceIndex; |
|
9246 |
|
9247 if (a === b) { |
|
9248 return 0; |
|
9249 } else if (a > b) { |
|
9250 return 1; |
|
9251 } |
|
9252 |
|
9253 return -1; |
|
9254 |
|
9255 } : (Y.config.doc.documentElement[COMPARE_DOCUMENT_POSITION] ? |
|
9256 function(nodeA, nodeB) { |
|
9257 if (nodeA[COMPARE_DOCUMENT_POSITION](nodeB) & 4) { |
|
9258 return -1; |
|
9259 } else { |
|
9260 return 1; |
|
9261 } |
|
9262 } : |
|
9263 function(nodeA, nodeB) { |
|
9264 var rangeA, rangeB, compare; |
|
9265 if (nodeA && nodeB) { |
|
9266 rangeA = nodeA[OWNER_DOCUMENT].createRange(); |
|
9267 rangeA.setStart(nodeA, 0); |
|
9268 rangeB = nodeB[OWNER_DOCUMENT].createRange(); |
|
9269 rangeB.setStart(nodeB, 0); |
|
9270 compare = rangeA.compareBoundaryPoints(1, rangeB); // 1 === Range.START_TO_END |
|
9271 } |
|
9272 |
|
9273 return compare; |
|
9274 |
|
9275 }), |
|
9276 |
|
9277 _sort: function(nodes) { |
|
9278 if (nodes) { |
|
9279 nodes = Y.Array(nodes, 0, true); |
|
9280 if (nodes.sort) { |
|
9281 nodes.sort(Selector._compare); |
|
9282 } |
|
9283 } |
|
9284 |
|
9285 return nodes; |
|
9286 }, |
|
9287 |
|
9288 _deDupe: function(nodes) { |
|
9289 var ret = [], |
|
9290 i, node; |
|
9291 |
|
9292 for (i = 0; (node = nodes[i++]);) { |
|
9293 if (!node._found) { |
|
9294 ret[ret.length] = node; |
|
9295 node._found = true; |
|
9296 } |
|
9297 } |
|
9298 |
|
9299 for (i = 0; (node = ret[i++]);) { |
|
9300 node._found = null; |
|
9301 node.removeAttribute('_found'); |
|
9302 } |
|
9303 |
|
9304 return ret; |
|
9305 }, |
|
9306 |
|
9307 /** |
|
9308 * Retrieves a set of nodes based on a given CSS selector. |
|
9309 * @method query |
|
9310 * |
|
9311 * @param {string} selector The CSS Selector to test the node against. |
|
9312 * @param {HTMLElement} root optional An HTMLElement to start the query from. Defaults to Y.config.doc |
|
9313 * @param {Boolean} firstOnly optional Whether or not to return only the first match. |
|
9314 * @return {Array} An array of nodes that match the given selector. |
|
9315 * @static |
|
9316 */ |
|
9317 query: function(selector, root, firstOnly, skipNative) { |
|
9318 root = root || Y.config.doc; |
|
9319 var ret = [], |
|
9320 useNative = (Y.Selector.useNative && Y.config.doc.querySelector && !skipNative), |
|
9321 queries = [[selector, root]], |
|
9322 query, |
|
9323 result, |
|
9324 i, |
|
9325 fn = (useNative) ? Y.Selector._nativeQuery : Y.Selector._bruteQuery; |
|
9326 |
|
9327 if (selector && fn) { |
|
9328 // split group into seperate queries |
|
9329 if (!skipNative && // already done if skipping |
|
9330 (!useNative || root.tagName)) { // split native when element scoping is needed |
|
9331 queries = Selector._splitQueries(selector, root); |
|
9332 } |
|
9333 |
|
9334 for (i = 0; (query = queries[i++]);) { |
|
9335 result = fn(query[0], query[1], firstOnly); |
|
9336 if (!firstOnly) { // coerce DOM Collection to Array |
|
9337 result = Y.Array(result, 0, true); |
|
9338 } |
|
9339 if (result) { |
|
9340 ret = ret.concat(result); |
|
9341 } |
|
9342 } |
|
9343 |
|
9344 if (queries.length > 1) { // remove dupes and sort by doc order |
|
9345 ret = Selector._sort(Selector._deDupe(ret)); |
|
9346 } |
|
9347 } |
|
9348 |
|
9349 return (firstOnly) ? (ret[0] || null) : ret; |
|
9350 |
|
9351 }, |
|
9352 |
|
9353 _replaceSelector: function(selector) { |
|
9354 var esc = Y.Selector._parse('esc', selector), // pull escaped colon, brackets, etc. |
|
9355 attrs, |
|
9356 pseudos; |
|
9357 |
|
9358 // first replace escaped chars, which could be present in attrs or pseudos |
|
9359 selector = Y.Selector._replace('esc', selector); |
|
9360 |
|
9361 // then replace pseudos before attrs to avoid replacing :not([foo]) |
|
9362 pseudos = Y.Selector._parse('pseudo', selector); |
|
9363 selector = Selector._replace('pseudo', selector); |
|
9364 |
|
9365 attrs = Y.Selector._parse('attr', selector); |
|
9366 selector = Y.Selector._replace('attr', selector); |
|
9367 |
|
9368 return { |
|
9369 esc: esc, |
|
9370 attrs: attrs, |
|
9371 pseudos: pseudos, |
|
9372 selector: selector |
|
9373 }; |
|
9374 }, |
|
9375 |
|
9376 _restoreSelector: function(replaced) { |
|
9377 var selector = replaced.selector; |
|
9378 selector = Y.Selector._restore('attr', selector, replaced.attrs); |
|
9379 selector = Y.Selector._restore('pseudo', selector, replaced.pseudos); |
|
9380 selector = Y.Selector._restore('esc', selector, replaced.esc); |
|
9381 return selector; |
|
9382 }, |
|
9383 |
|
9384 _replaceCommas: function(selector) { |
|
9385 var replaced = Y.Selector._replaceSelector(selector), |
|
9386 selector = replaced.selector; |
|
9387 |
|
9388 if (selector) { |
|
9389 selector = selector.replace(/,/g, '\uE007'); |
|
9390 replaced.selector = selector; |
|
9391 selector = Y.Selector._restoreSelector(replaced); |
|
9392 } |
|
9393 return selector; |
|
9394 }, |
|
9395 |
|
9396 // allows element scoped queries to begin with combinator |
|
9397 // e.g. query('> p', document.body) === query('body > p') |
|
9398 _splitQueries: function(selector, node) { |
|
9399 if (selector.indexOf(',') > -1) { |
|
9400 selector = Y.Selector._replaceCommas(selector); |
|
9401 } |
|
9402 |
|
9403 var groups = selector.split('\uE007'), // split on replaced comma token |
|
9404 queries = [], |
|
9405 prefix = '', |
|
9406 id, |
|
9407 i, |
|
9408 len; |
|
9409 |
|
9410 if (node) { |
|
9411 // enforce for element scoping |
|
9412 if (node.nodeType === 1) { // Elements only |
|
9413 id = Y.Selector._escapeId(Y.DOM.getId(node)); |
|
9414 |
|
9415 if (!id) { |
|
9416 id = Y.guid(); |
|
9417 Y.DOM.setId(node, id); |
|
9418 } |
|
9419 |
|
9420 prefix = '[id="' + id + '"] '; |
|
9421 } |
|
9422 |
|
9423 for (i = 0, len = groups.length; i < len; ++i) { |
|
9424 selector = prefix + groups[i]; |
|
9425 queries.push([selector, node]); |
|
9426 } |
|
9427 } |
|
9428 |
|
9429 return queries; |
|
9430 }, |
|
9431 |
|
9432 _nativeQuery: function(selector, root, one) { |
|
9433 if (Y.UA.webkit && selector.indexOf(':checked') > -1 && |
|
9434 (Y.Selector.pseudos && Y.Selector.pseudos.checked)) { // webkit (chrome, safari) fails to pick up "selected" with "checked" |
|
9435 return Y.Selector.query(selector, root, one, true); // redo with skipNative true to try brute query |
|
9436 } |
|
9437 try { |
|
9438 return root['querySelector' + (one ? '' : 'All')](selector); |
|
9439 } catch(e) { // fallback to brute if available |
|
9440 return Y.Selector.query(selector, root, one, true); // redo with skipNative true |
|
9441 } |
|
9442 }, |
|
9443 |
|
9444 filter: function(nodes, selector) { |
|
9445 var ret = [], |
|
9446 i, node; |
|
9447 |
|
9448 if (nodes && selector) { |
|
9449 for (i = 0; (node = nodes[i++]);) { |
|
9450 if (Y.Selector.test(node, selector)) { |
|
9451 ret[ret.length] = node; |
|
9452 } |
|
9453 } |
|
9454 } else { |
|
9455 } |
|
9456 |
|
9457 return ret; |
|
9458 }, |
|
9459 |
|
9460 test: function(node, selector, root) { |
|
9461 var ret = false, |
|
9462 useFrag = false, |
|
9463 groups, |
|
9464 parent, |
|
9465 item, |
|
9466 items, |
|
9467 frag, |
|
9468 id, |
|
9469 i, j, group; |
|
9470 |
|
9471 if (node && node.tagName) { // only test HTMLElements |
|
9472 |
|
9473 if (typeof selector == 'function') { // test with function |
|
9474 ret = selector.call(node, node); |
|
9475 } else { // test with query |
|
9476 // we need a root if off-doc |
|
9477 groups = selector.split(','); |
|
9478 if (!root && !Y.DOM.inDoc(node)) { |
|
9479 parent = node.parentNode; |
|
9480 if (parent) { |
|
9481 root = parent; |
|
9482 } else { // only use frag when no parent to query |
|
9483 frag = node[OWNER_DOCUMENT].createDocumentFragment(); |
|
9484 frag.appendChild(node); |
|
9485 root = frag; |
|
9486 useFrag = true; |
|
9487 } |
|
9488 } |
|
9489 root = root || node[OWNER_DOCUMENT]; |
|
9490 |
|
9491 id = Y.Selector._escapeId(Y.DOM.getId(node)); |
|
9492 if (!id) { |
|
9493 id = Y.guid(); |
|
9494 Y.DOM.setId(node, id); |
|
9495 } |
|
9496 |
|
9497 for (i = 0; (group = groups[i++]);) { // TODO: off-dom test |
|
9498 group += '[id="' + id + '"]'; |
|
9499 items = Y.Selector.query(group, root); |
|
9500 |
|
9501 for (j = 0; item = items[j++];) { |
|
9502 if (item === node) { |
|
9503 ret = true; |
|
9504 break; |
|
9505 } |
|
9506 } |
|
9507 if (ret) { |
|
9508 break; |
|
9509 } |
|
9510 } |
|
9511 |
|
9512 if (useFrag) { // cleanup |
|
9513 frag.removeChild(node); |
|
9514 } |
|
9515 }; |
|
9516 } |
|
9517 |
|
9518 return ret; |
|
9519 }, |
|
9520 |
|
9521 /** |
|
9522 * A convenience function to emulate Y.Node's aNode.ancestor(selector). |
|
9523 * @param {HTMLElement} element An HTMLElement to start the query from. |
|
9524 * @param {String} selector The CSS selector to test the node against. |
|
9525 * @return {HTMLElement} The ancestor node matching the selector, or null. |
|
9526 * @param {Boolean} testSelf optional Whether or not to include the element in the scan |
|
9527 * @static |
|
9528 * @method ancestor |
|
9529 */ |
|
9530 ancestor: function (element, selector, testSelf) { |
|
9531 return Y.DOM.ancestor(element, function(n) { |
|
9532 return Y.Selector.test(n, selector); |
|
9533 }, testSelf); |
|
9534 }, |
|
9535 |
|
9536 _parse: function(name, selector) { |
|
9537 return selector.match(Y.Selector._types[name].re); |
|
9538 }, |
|
9539 |
|
9540 _replace: function(name, selector) { |
|
9541 var o = Y.Selector._types[name]; |
|
9542 return selector.replace(o.re, o.token); |
|
9543 }, |
|
9544 |
|
9545 _restore: function(name, selector, items) { |
|
9546 if (items) { |
|
9547 var token = Y.Selector._types[name].token, |
|
9548 i, len; |
|
9549 for (i = 0, len = items.length; i < len; ++i) { |
|
9550 selector = selector.replace(token, items[i]); |
|
9551 } |
|
9552 } |
|
9553 return selector; |
|
9554 } |
|
9555 }; |
|
9556 |
|
9557 Y.mix(Y.Selector, Selector, true); |
|
9558 |
|
9559 })(Y); |
|
9560 |
|
9561 |
|
9562 }, '@VERSION@', {"requires": ["dom-base"]}); |
|
9563 YUI.add('selector', function (Y, NAME) { |
|
9564 |
|
9565 |
|
9566 |
|
9567 }, '@VERSION@', {"requires": ["selector-native"]}); |
|
9568 YUI.add('event-custom-base', function (Y, NAME) { |
|
9569 |
|
9570 /** |
|
9571 * Custom event engine, DOM event listener abstraction layer, synthetic DOM |
|
9572 * events. |
|
9573 * @module event-custom |
|
9574 */ |
|
9575 |
|
9576 Y.Env.evt = { |
|
9577 handles: {}, |
|
9578 plugins: {} |
|
9579 }; |
|
9580 |
|
9581 |
|
9582 /** |
|
9583 * Custom event engine, DOM event listener abstraction layer, synthetic DOM |
|
9584 * events. |
|
9585 * @module event-custom |
|
9586 * @submodule event-custom-base |
|
9587 */ |
|
9588 |
|
9589 /** |
|
9590 * Allows for the insertion of methods that are executed before or after |
|
9591 * a specified method |
|
9592 * @class Do |
|
9593 * @static |
|
9594 */ |
|
9595 |
|
9596 var DO_BEFORE = 0, |
|
9597 DO_AFTER = 1, |
|
9598 |
|
9599 DO = { |
|
9600 |
|
9601 /** |
|
9602 * Cache of objects touched by the utility |
|
9603 * @property objs |
|
9604 * @static |
|
9605 * @deprecated Since 3.6.0. The `_yuiaop` property on the AOP'd object |
|
9606 * replaces the role of this property, but is considered to be private, and |
|
9607 * is only mentioned to provide a migration path. |
|
9608 * |
|
9609 * If you have a use case which warrants migration to the _yuiaop property, |
|
9610 * please file a ticket to let us know what it's used for and we can see if |
|
9611 * we need to expose hooks for that functionality more formally. |
|
9612 */ |
|
9613 objs: null, |
|
9614 |
|
9615 /** |
|
9616 * <p>Execute the supplied method before the specified function. Wrapping |
|
9617 * function may optionally return an instance of the following classes to |
|
9618 * further alter runtime behavior:</p> |
|
9619 * <dl> |
|
9620 * <dt></code>Y.Do.Halt(message, returnValue)</code></dt> |
|
9621 * <dd>Immediatly stop execution and return |
|
9622 * <code>returnValue</code>. No other wrapping functions will be |
|
9623 * executed.</dd> |
|
9624 * <dt></code>Y.Do.AlterArgs(message, newArgArray)</code></dt> |
|
9625 * <dd>Replace the arguments that the original function will be |
|
9626 * called with.</dd> |
|
9627 * <dt></code>Y.Do.Prevent(message)</code></dt> |
|
9628 * <dd>Don't execute the wrapped function. Other before phase |
|
9629 * wrappers will be executed.</dd> |
|
9630 * </dl> |
|
9631 * |
|
9632 * @method before |
|
9633 * @param fn {Function} the function to execute |
|
9634 * @param obj the object hosting the method to displace |
|
9635 * @param sFn {string} the name of the method to displace |
|
9636 * @param c The execution context for fn |
|
9637 * @param arg* {mixed} 0..n additional arguments to supply to the subscriber |
|
9638 * when the event fires. |
|
9639 * @return {string} handle for the subscription |
|
9640 * @static |
|
9641 */ |
|
9642 before: function(fn, obj, sFn, c) { |
|
9643 var f = fn, a; |
|
9644 if (c) { |
|
9645 a = [fn, c].concat(Y.Array(arguments, 4, true)); |
|
9646 f = Y.rbind.apply(Y, a); |
|
9647 } |
|
9648 |
|
9649 return this._inject(DO_BEFORE, f, obj, sFn); |
|
9650 }, |
|
9651 |
|
9652 /** |
|
9653 * <p>Execute the supplied method after the specified function. Wrapping |
|
9654 * function may optionally return an instance of the following classes to |
|
9655 * further alter runtime behavior:</p> |
|
9656 * <dl> |
|
9657 * <dt></code>Y.Do.Halt(message, returnValue)</code></dt> |
|
9658 * <dd>Immediatly stop execution and return |
|
9659 * <code>returnValue</code>. No other wrapping functions will be |
|
9660 * executed.</dd> |
|
9661 * <dt></code>Y.Do.AlterReturn(message, returnValue)</code></dt> |
|
9662 * <dd>Return <code>returnValue</code> instead of the wrapped |
|
9663 * method's original return value. This can be further altered by |
|
9664 * other after phase wrappers.</dd> |
|
9665 * </dl> |
|
9666 * |
|
9667 * <p>The static properties <code>Y.Do.originalRetVal</code> and |
|
9668 * <code>Y.Do.currentRetVal</code> will be populated for reference.</p> |
|
9669 * |
|
9670 * @method after |
|
9671 * @param fn {Function} the function to execute |
|
9672 * @param obj the object hosting the method to displace |
|
9673 * @param sFn {string} the name of the method to displace |
|
9674 * @param c The execution context for fn |
|
9675 * @param arg* {mixed} 0..n additional arguments to supply to the subscriber |
|
9676 * @return {string} handle for the subscription |
|
9677 * @static |
|
9678 */ |
|
9679 after: function(fn, obj, sFn, c) { |
|
9680 var f = fn, a; |
|
9681 if (c) { |
|
9682 a = [fn, c].concat(Y.Array(arguments, 4, true)); |
|
9683 f = Y.rbind.apply(Y, a); |
|
9684 } |
|
9685 |
|
9686 return this._inject(DO_AFTER, f, obj, sFn); |
|
9687 }, |
|
9688 |
|
9689 /** |
|
9690 * Execute the supplied method before or after the specified function. |
|
9691 * Used by <code>before</code> and <code>after</code>. |
|
9692 * |
|
9693 * @method _inject |
|
9694 * @param when {string} before or after |
|
9695 * @param fn {Function} the function to execute |
|
9696 * @param obj the object hosting the method to displace |
|
9697 * @param sFn {string} the name of the method to displace |
|
9698 * @param c The execution context for fn |
|
9699 * @return {string} handle for the subscription |
|
9700 * @private |
|
9701 * @static |
|
9702 */ |
|
9703 _inject: function(when, fn, obj, sFn) { |
|
9704 // object id |
|
9705 var id = Y.stamp(obj), o, sid; |
|
9706 |
|
9707 if (!obj._yuiaop) { |
|
9708 // create a map entry for the obj if it doesn't exist, to hold overridden methods |
|
9709 obj._yuiaop = {}; |
|
9710 } |
|
9711 |
|
9712 o = obj._yuiaop; |
|
9713 |
|
9714 if (!o[sFn]) { |
|
9715 // create a map entry for the method if it doesn't exist |
|
9716 o[sFn] = new Y.Do.Method(obj, sFn); |
|
9717 |
|
9718 // re-route the method to our wrapper |
|
9719 obj[sFn] = function() { |
|
9720 return o[sFn].exec.apply(o[sFn], arguments); |
|
9721 }; |
|
9722 } |
|
9723 |
|
9724 // subscriber id |
|
9725 sid = id + Y.stamp(fn) + sFn; |
|
9726 |
|
9727 // register the callback |
|
9728 o[sFn].register(sid, fn, when); |
|
9729 |
|
9730 return new Y.EventHandle(o[sFn], sid); |
|
9731 }, |
|
9732 |
|
9733 /** |
|
9734 * Detach a before or after subscription. |
|
9735 * |
|
9736 * @method detach |
|
9737 * @param handle {string} the subscription handle |
|
9738 * @static |
|
9739 */ |
|
9740 detach: function(handle) { |
|
9741 if (handle.detach) { |
|
9742 handle.detach(); |
|
9743 } |
|
9744 } |
|
9745 }; |
|
9746 |
|
9747 Y.Do = DO; |
|
9748 |
|
9749 ////////////////////////////////////////////////////////////////////////// |
|
9750 |
|
9751 /** |
|
9752 * Contains the return value from the wrapped method, accessible |
|
9753 * by 'after' event listeners. |
|
9754 * |
|
9755 * @property originalRetVal |
|
9756 * @static |
|
9757 * @since 3.2.0 |
|
9758 */ |
|
9759 |
|
9760 /** |
|
9761 * Contains the current state of the return value, consumable by |
|
9762 * 'after' event listeners, and updated if an after subscriber |
|
9763 * changes the return value generated by the wrapped function. |
|
9764 * |
|
9765 * @property currentRetVal |
|
9766 * @static |
|
9767 * @since 3.2.0 |
|
9768 */ |
|
9769 |
|
9770 ////////////////////////////////////////////////////////////////////////// |
|
9771 |
|
9772 /** |
|
9773 * Wrapper for a displaced method with aop enabled |
|
9774 * @class Do.Method |
|
9775 * @constructor |
|
9776 * @param obj The object to operate on |
|
9777 * @param sFn The name of the method to displace |
|
9778 */ |
|
9779 DO.Method = function(obj, sFn) { |
|
9780 this.obj = obj; |
|
9781 this.methodName = sFn; |
|
9782 this.method = obj[sFn]; |
|
9783 this.before = {}; |
|
9784 this.after = {}; |
|
9785 }; |
|
9786 |
|
9787 /** |
|
9788 * Register a aop subscriber |
|
9789 * @method register |
|
9790 * @param sid {string} the subscriber id |
|
9791 * @param fn {Function} the function to execute |
|
9792 * @param when {string} when to execute the function |
|
9793 */ |
|
9794 DO.Method.prototype.register = function (sid, fn, when) { |
|
9795 if (when) { |
|
9796 this.after[sid] = fn; |
|
9797 } else { |
|
9798 this.before[sid] = fn; |
|
9799 } |
|
9800 }; |
|
9801 |
|
9802 /** |
|
9803 * Unregister a aop subscriber |
|
9804 * @method delete |
|
9805 * @param sid {string} the subscriber id |
|
9806 * @param fn {Function} the function to execute |
|
9807 * @param when {string} when to execute the function |
|
9808 */ |
|
9809 DO.Method.prototype._delete = function (sid) { |
|
9810 delete this.before[sid]; |
|
9811 delete this.after[sid]; |
|
9812 }; |
|
9813 |
|
9814 /** |
|
9815 * <p>Execute the wrapped method. All arguments are passed into the wrapping |
|
9816 * functions. If any of the before wrappers return an instance of |
|
9817 * <code>Y.Do.Halt</code> or <code>Y.Do.Prevent</code>, neither the wrapped |
|
9818 * function nor any after phase subscribers will be executed.</p> |
|
9819 * |
|
9820 * <p>The return value will be the return value of the wrapped function or one |
|
9821 * provided by a wrapper function via an instance of <code>Y.Do.Halt</code> or |
|
9822 * <code>Y.Do.AlterReturn</code>. |
|
9823 * |
|
9824 * @method exec |
|
9825 * @param arg* {any} Arguments are passed to the wrapping and wrapped functions |
|
9826 * @return {any} Return value of wrapped function unless overwritten (see above) |
|
9827 */ |
|
9828 DO.Method.prototype.exec = function () { |
|
9829 |
|
9830 var args = Y.Array(arguments, 0, true), |
|
9831 i, ret, newRet, |
|
9832 bf = this.before, |
|
9833 af = this.after, |
|
9834 prevented = false; |
|
9835 |
|
9836 // execute before |
|
9837 for (i in bf) { |
|
9838 if (bf.hasOwnProperty(i)) { |
|
9839 ret = bf[i].apply(this.obj, args); |
|
9840 if (ret) { |
|
9841 switch (ret.constructor) { |
|
9842 case DO.Halt: |
|
9843 return ret.retVal; |
|
9844 case DO.AlterArgs: |
|
9845 args = ret.newArgs; |
|
9846 break; |
|
9847 case DO.Prevent: |
|
9848 prevented = true; |
|
9849 break; |
|
9850 default: |
|
9851 } |
|
9852 } |
|
9853 } |
|
9854 } |
|
9855 |
|
9856 // execute method |
|
9857 if (!prevented) { |
|
9858 ret = this.method.apply(this.obj, args); |
|
9859 } |
|
9860 |
|
9861 DO.originalRetVal = ret; |
|
9862 DO.currentRetVal = ret; |
|
9863 |
|
9864 // execute after methods. |
|
9865 for (i in af) { |
|
9866 if (af.hasOwnProperty(i)) { |
|
9867 newRet = af[i].apply(this.obj, args); |
|
9868 // Stop processing if a Halt object is returned |
|
9869 if (newRet && newRet.constructor === DO.Halt) { |
|
9870 return newRet.retVal; |
|
9871 // Check for a new return value |
|
9872 } else if (newRet && newRet.constructor === DO.AlterReturn) { |
|
9873 ret = newRet.newRetVal; |
|
9874 // Update the static retval state |
|
9875 DO.currentRetVal = ret; |
|
9876 } |
|
9877 } |
|
9878 } |
|
9879 |
|
9880 return ret; |
|
9881 }; |
|
9882 |
|
9883 ////////////////////////////////////////////////////////////////////////// |
|
9884 |
|
9885 /** |
|
9886 * Return an AlterArgs object when you want to change the arguments that |
|
9887 * were passed into the function. Useful for Do.before subscribers. An |
|
9888 * example would be a service that scrubs out illegal characters prior to |
|
9889 * executing the core business logic. |
|
9890 * @class Do.AlterArgs |
|
9891 * @constructor |
|
9892 * @param msg {String} (optional) Explanation of the altered return value |
|
9893 * @param newArgs {Array} Call parameters to be used for the original method |
|
9894 * instead of the arguments originally passed in. |
|
9895 */ |
|
9896 DO.AlterArgs = function(msg, newArgs) { |
|
9897 this.msg = msg; |
|
9898 this.newArgs = newArgs; |
|
9899 }; |
|
9900 |
|
9901 /** |
|
9902 * Return an AlterReturn object when you want to change the result returned |
|
9903 * from the core method to the caller. Useful for Do.after subscribers. |
|
9904 * @class Do.AlterReturn |
|
9905 * @constructor |
|
9906 * @param msg {String} (optional) Explanation of the altered return value |
|
9907 * @param newRetVal {any} Return value passed to code that invoked the wrapped |
|
9908 * function. |
|
9909 */ |
|
9910 DO.AlterReturn = function(msg, newRetVal) { |
|
9911 this.msg = msg; |
|
9912 this.newRetVal = newRetVal; |
|
9913 }; |
|
9914 |
|
9915 /** |
|
9916 * Return a Halt object when you want to terminate the execution |
|
9917 * of all subsequent subscribers as well as the wrapped method |
|
9918 * if it has not exectued yet. Useful for Do.before subscribers. |
|
9919 * @class Do.Halt |
|
9920 * @constructor |
|
9921 * @param msg {String} (optional) Explanation of why the termination was done |
|
9922 * @param retVal {any} Return value passed to code that invoked the wrapped |
|
9923 * function. |
|
9924 */ |
|
9925 DO.Halt = function(msg, retVal) { |
|
9926 this.msg = msg; |
|
9927 this.retVal = retVal; |
|
9928 }; |
|
9929 |
|
9930 /** |
|
9931 * Return a Prevent object when you want to prevent the wrapped function |
|
9932 * from executing, but want the remaining listeners to execute. Useful |
|
9933 * for Do.before subscribers. |
|
9934 * @class Do.Prevent |
|
9935 * @constructor |
|
9936 * @param msg {String} (optional) Explanation of why the termination was done |
|
9937 */ |
|
9938 DO.Prevent = function(msg) { |
|
9939 this.msg = msg; |
|
9940 }; |
|
9941 |
|
9942 /** |
|
9943 * Return an Error object when you want to terminate the execution |
|
9944 * of all subsequent method calls. |
|
9945 * @class Do.Error |
|
9946 * @constructor |
|
9947 * @param msg {String} (optional) Explanation of the altered return value |
|
9948 * @param retVal {any} Return value passed to code that invoked the wrapped |
|
9949 * function. |
|
9950 * @deprecated use Y.Do.Halt or Y.Do.Prevent |
|
9951 */ |
|
9952 DO.Error = DO.Halt; |
|
9953 |
|
9954 |
|
9955 ////////////////////////////////////////////////////////////////////////// |
|
9956 |
|
9957 /** |
|
9958 * Custom event engine, DOM event listener abstraction layer, synthetic DOM |
|
9959 * events. |
|
9960 * @module event-custom |
|
9961 * @submodule event-custom-base |
|
9962 */ |
|
9963 |
|
9964 |
|
9965 // var onsubscribeType = "_event:onsub", |
|
9966 var YArray = Y.Array, |
|
9967 |
|
9968 AFTER = 'after', |
|
9969 CONFIGS = [ |
|
9970 'broadcast', |
|
9971 'monitored', |
|
9972 'bubbles', |
|
9973 'context', |
|
9974 'contextFn', |
|
9975 'currentTarget', |
|
9976 'defaultFn', |
|
9977 'defaultTargetOnly', |
|
9978 'details', |
|
9979 'emitFacade', |
|
9980 'fireOnce', |
|
9981 'async', |
|
9982 'host', |
|
9983 'preventable', |
|
9984 'preventedFn', |
|
9985 'queuable', |
|
9986 'silent', |
|
9987 'stoppedFn', |
|
9988 'target', |
|
9989 'type' |
|
9990 ], |
|
9991 |
|
9992 CONFIGS_HASH = YArray.hash(CONFIGS), |
|
9993 |
|
9994 nativeSlice = Array.prototype.slice, |
|
9995 |
|
9996 YUI3_SIGNATURE = 9, |
|
9997 YUI_LOG = 'yui:log', |
|
9998 |
|
9999 mixConfigs = function(r, s, ov) { |
|
10000 var p; |
|
10001 |
|
10002 for (p in s) { |
|
10003 if (CONFIGS_HASH[p] && (ov || !(p in r))) { |
|
10004 r[p] = s[p]; |
|
10005 } |
|
10006 } |
|
10007 |
|
10008 return r; |
|
10009 }; |
|
10010 |
|
10011 /** |
|
10012 * The CustomEvent class lets you define events for your application |
|
10013 * that can be subscribed to by one or more independent component. |
|
10014 * |
|
10015 * @param {String} type The type of event, which is passed to the callback |
|
10016 * when the event fires. |
|
10017 * @param {object} defaults configuration object. |
|
10018 * @class CustomEvent |
|
10019 * @constructor |
|
10020 */ |
|
10021 |
|
10022 /** |
|
10023 * The type of event, returned to subscribers when the event fires |
|
10024 * @property type |
|
10025 * @type string |
|
10026 */ |
|
10027 |
|
10028 /** |
|
10029 * By default all custom events are logged in the debug build, set silent |
|
10030 * to true to disable debug outpu for this event. |
|
10031 * @property silent |
|
10032 * @type boolean |
|
10033 */ |
|
10034 |
|
10035 Y.CustomEvent = function(type, defaults) { |
|
10036 |
|
10037 this._kds = Y.CustomEvent.keepDeprecatedSubs; |
|
10038 |
|
10039 this.id = Y.guid(); |
|
10040 |
|
10041 this.type = type; |
|
10042 this.silent = this.logSystem = (type === YUI_LOG); |
|
10043 |
|
10044 if (this._kds) { |
|
10045 /** |
|
10046 * The subscribers to this event |
|
10047 * @property subscribers |
|
10048 * @type Subscriber {} |
|
10049 * @deprecated |
|
10050 */ |
|
10051 |
|
10052 /** |
|
10053 * 'After' subscribers |
|
10054 * @property afters |
|
10055 * @type Subscriber {} |
|
10056 * @deprecated |
|
10057 */ |
|
10058 this.subscribers = {}; |
|
10059 this.afters = {}; |
|
10060 } |
|
10061 |
|
10062 if (defaults) { |
|
10063 mixConfigs(this, defaults, true); |
|
10064 } |
|
10065 }; |
|
10066 |
|
10067 /** |
|
10068 * Static flag to enable population of the <a href="#property_subscribers">`subscribers`</a> |
|
10069 * and <a href="#property_subscribers">`afters`</a> properties held on a `CustomEvent` instance. |
|
10070 * |
|
10071 * These properties were changed to private properties (`_subscribers` and `_afters`), and |
|
10072 * converted from objects to arrays for performance reasons. |
|
10073 * |
|
10074 * Setting this property to true will populate the deprecated `subscribers` and `afters` |
|
10075 * properties for people who may be using them (which is expected to be rare). There will |
|
10076 * be a performance hit, compared to the new array based implementation. |
|
10077 * |
|
10078 * If you are using these deprecated properties for a use case which the public API |
|
10079 * does not support, please file an enhancement request, and we can provide an alternate |
|
10080 * public implementation which doesn't have the performance cost required to maintiain the |
|
10081 * properties as objects. |
|
10082 * |
|
10083 * @property keepDeprecatedSubs |
|
10084 * @static |
|
10085 * @for CustomEvent |
|
10086 * @type boolean |
|
10087 * @default false |
|
10088 * @deprecated |
|
10089 */ |
|
10090 Y.CustomEvent.keepDeprecatedSubs = false; |
|
10091 |
|
10092 Y.CustomEvent.mixConfigs = mixConfigs; |
|
10093 |
|
10094 Y.CustomEvent.prototype = { |
|
10095 |
|
10096 constructor: Y.CustomEvent, |
|
10097 |
|
10098 /** |
|
10099 * Monitor when an event is attached or detached. |
|
10100 * |
|
10101 * @property monitored |
|
10102 * @type boolean |
|
10103 */ |
|
10104 |
|
10105 /** |
|
10106 * If 0, this event does not broadcast. If 1, the YUI instance is notified |
|
10107 * every time this event fires. If 2, the YUI instance and the YUI global |
|
10108 * (if event is enabled on the global) are notified every time this event |
|
10109 * fires. |
|
10110 * @property broadcast |
|
10111 * @type int |
|
10112 */ |
|
10113 |
|
10114 /** |
|
10115 * Specifies whether this event should be queued when the host is actively |
|
10116 * processing an event. This will effect exectution order of the callbacks |
|
10117 * for the various events. |
|
10118 * @property queuable |
|
10119 * @type boolean |
|
10120 * @default false |
|
10121 */ |
|
10122 |
|
10123 /** |
|
10124 * This event has fired if true |
|
10125 * |
|
10126 * @property fired |
|
10127 * @type boolean |
|
10128 * @default false; |
|
10129 */ |
|
10130 |
|
10131 /** |
|
10132 * An array containing the arguments the custom event |
|
10133 * was last fired with. |
|
10134 * @property firedWith |
|
10135 * @type Array |
|
10136 */ |
|
10137 |
|
10138 /** |
|
10139 * This event should only fire one time if true, and if |
|
10140 * it has fired, any new subscribers should be notified |
|
10141 * immediately. |
|
10142 * |
|
10143 * @property fireOnce |
|
10144 * @type boolean |
|
10145 * @default false; |
|
10146 */ |
|
10147 |
|
10148 /** |
|
10149 * fireOnce listeners will fire syncronously unless async |
|
10150 * is set to true |
|
10151 * @property async |
|
10152 * @type boolean |
|
10153 * @default false |
|
10154 */ |
|
10155 |
|
10156 /** |
|
10157 * Flag for stopPropagation that is modified during fire() |
|
10158 * 1 means to stop propagation to bubble targets. 2 means |
|
10159 * to also stop additional subscribers on this target. |
|
10160 * @property stopped |
|
10161 * @type int |
|
10162 */ |
|
10163 |
|
10164 /** |
|
10165 * Flag for preventDefault that is modified during fire(). |
|
10166 * if it is not 0, the default behavior for this event |
|
10167 * @property prevented |
|
10168 * @type int |
|
10169 */ |
|
10170 |
|
10171 /** |
|
10172 * Specifies the host for this custom event. This is used |
|
10173 * to enable event bubbling |
|
10174 * @property host |
|
10175 * @type EventTarget |
|
10176 */ |
|
10177 |
|
10178 /** |
|
10179 * The default function to execute after event listeners |
|
10180 * have fire, but only if the default action was not |
|
10181 * prevented. |
|
10182 * @property defaultFn |
|
10183 * @type Function |
|
10184 */ |
|
10185 |
|
10186 /** |
|
10187 * The function to execute if a subscriber calls |
|
10188 * stopPropagation or stopImmediatePropagation |
|
10189 * @property stoppedFn |
|
10190 * @type Function |
|
10191 */ |
|
10192 |
|
10193 /** |
|
10194 * The function to execute if a subscriber calls |
|
10195 * preventDefault |
|
10196 * @property preventedFn |
|
10197 * @type Function |
|
10198 */ |
|
10199 |
|
10200 /** |
|
10201 * The subscribers to this event |
|
10202 * @property _subscribers |
|
10203 * @type Subscriber [] |
|
10204 * @private |
|
10205 */ |
|
10206 |
|
10207 /** |
|
10208 * 'After' subscribers |
|
10209 * @property _afters |
|
10210 * @type Subscriber [] |
|
10211 * @private |
|
10212 */ |
|
10213 |
|
10214 /** |
|
10215 * If set to true, the custom event will deliver an EventFacade object |
|
10216 * that is similar to a DOM event object. |
|
10217 * @property emitFacade |
|
10218 * @type boolean |
|
10219 * @default false |
|
10220 */ |
|
10221 |
|
10222 /** |
|
10223 * Supports multiple options for listener signatures in order to |
|
10224 * port YUI 2 apps. |
|
10225 * @property signature |
|
10226 * @type int |
|
10227 * @default 9 |
|
10228 */ |
|
10229 signature : YUI3_SIGNATURE, |
|
10230 |
|
10231 /** |
|
10232 * The context the the event will fire from by default. Defaults to the YUI |
|
10233 * instance. |
|
10234 * @property context |
|
10235 * @type object |
|
10236 */ |
|
10237 context : Y, |
|
10238 |
|
10239 /** |
|
10240 * Specifies whether or not this event's default function |
|
10241 * can be cancelled by a subscriber by executing preventDefault() |
|
10242 * on the event facade |
|
10243 * @property preventable |
|
10244 * @type boolean |
|
10245 * @default true |
|
10246 */ |
|
10247 preventable : true, |
|
10248 |
|
10249 /** |
|
10250 * Specifies whether or not a subscriber can stop the event propagation |
|
10251 * via stopPropagation(), stopImmediatePropagation(), or halt() |
|
10252 * |
|
10253 * Events can only bubble if emitFacade is true. |
|
10254 * |
|
10255 * @property bubbles |
|
10256 * @type boolean |
|
10257 * @default true |
|
10258 */ |
|
10259 bubbles : true, |
|
10260 |
|
10261 /** |
|
10262 * Returns the number of subscribers for this event as the sum of the on() |
|
10263 * subscribers and after() subscribers. |
|
10264 * |
|
10265 * @method hasSubs |
|
10266 * @return Number |
|
10267 */ |
|
10268 hasSubs: function(when) { |
|
10269 var s = 0, |
|
10270 a = 0, |
|
10271 subs = this._subscribers, |
|
10272 afters = this._afters, |
|
10273 sib = this.sibling; |
|
10274 |
|
10275 if (subs) { |
|
10276 s = subs.length; |
|
10277 } |
|
10278 |
|
10279 if (afters) { |
|
10280 a = afters.length; |
|
10281 } |
|
10282 |
|
10283 if (sib) { |
|
10284 subs = sib._subscribers; |
|
10285 afters = sib._afters; |
|
10286 |
|
10287 if (subs) { |
|
10288 s += subs.length; |
|
10289 } |
|
10290 |
|
10291 if (afters) { |
|
10292 a += afters.length; |
|
10293 } |
|
10294 } |
|
10295 |
|
10296 if (when) { |
|
10297 return (when === 'after') ? a : s; |
|
10298 } |
|
10299 |
|
10300 return (s + a); |
|
10301 }, |
|
10302 |
|
10303 /** |
|
10304 * Monitor the event state for the subscribed event. The first parameter |
|
10305 * is what should be monitored, the rest are the normal parameters when |
|
10306 * subscribing to an event. |
|
10307 * @method monitor |
|
10308 * @param what {string} what to monitor ('detach', 'attach', 'publish'). |
|
10309 * @return {EventHandle} return value from the monitor event subscription. |
|
10310 */ |
|
10311 monitor: function(what) { |
|
10312 this.monitored = true; |
|
10313 var type = this.id + '|' + this.type + '_' + what, |
|
10314 args = nativeSlice.call(arguments, 0); |
|
10315 args[0] = type; |
|
10316 return this.host.on.apply(this.host, args); |
|
10317 }, |
|
10318 |
|
10319 /** |
|
10320 * Get all of the subscribers to this event and any sibling event |
|
10321 * @method getSubs |
|
10322 * @return {Array} first item is the on subscribers, second the after. |
|
10323 */ |
|
10324 getSubs: function() { |
|
10325 |
|
10326 var sibling = this.sibling, |
|
10327 subs = this._subscribers, |
|
10328 afters = this._afters, |
|
10329 siblingSubs, |
|
10330 siblingAfters; |
|
10331 |
|
10332 if (sibling) { |
|
10333 siblingSubs = sibling._subscribers; |
|
10334 siblingAfters = sibling._afters; |
|
10335 } |
|
10336 |
|
10337 if (siblingSubs) { |
|
10338 if (subs) { |
|
10339 subs = subs.concat(siblingSubs); |
|
10340 } else { |
|
10341 subs = siblingSubs.concat(); |
|
10342 } |
|
10343 } else { |
|
10344 if (subs) { |
|
10345 subs = subs.concat(); |
|
10346 } else { |
|
10347 subs = []; |
|
10348 } |
|
10349 } |
|
10350 |
|
10351 if (siblingAfters) { |
|
10352 if (afters) { |
|
10353 afters = afters.concat(siblingAfters); |
|
10354 } else { |
|
10355 afters = siblingAfters.concat(); |
|
10356 } |
|
10357 } else { |
|
10358 if (afters) { |
|
10359 afters = afters.concat(); |
|
10360 } else { |
|
10361 afters = []; |
|
10362 } |
|
10363 } |
|
10364 |
|
10365 return [subs, afters]; |
|
10366 }, |
|
10367 |
|
10368 /** |
|
10369 * Apply configuration properties. Only applies the CONFIG whitelist |
|
10370 * @method applyConfig |
|
10371 * @param o hash of properties to apply. |
|
10372 * @param force {boolean} if true, properties that exist on the event |
|
10373 * will be overwritten. |
|
10374 */ |
|
10375 applyConfig: function(o, force) { |
|
10376 mixConfigs(this, o, force); |
|
10377 }, |
|
10378 |
|
10379 /** |
|
10380 * Create the Subscription for subscribing function, context, and bound |
|
10381 * arguments. If this is a fireOnce event, the subscriber is immediately |
|
10382 * notified. |
|
10383 * |
|
10384 * @method _on |
|
10385 * @param fn {Function} Subscription callback |
|
10386 * @param [context] {Object} Override `this` in the callback |
|
10387 * @param [args] {Array} bound arguments that will be passed to the callback after the arguments generated by fire() |
|
10388 * @param [when] {String} "after" to slot into after subscribers |
|
10389 * @return {EventHandle} |
|
10390 * @protected |
|
10391 */ |
|
10392 _on: function(fn, context, args, when) { |
|
10393 |
|
10394 if (!fn) { this.log('Invalid callback for CE: ' + this.type); } |
|
10395 |
|
10396 var s = new Y.Subscriber(fn, context, args, when); |
|
10397 |
|
10398 if (this.fireOnce && this.fired) { |
|
10399 if (this.async) { |
|
10400 setTimeout(Y.bind(this._notify, this, s, this.firedWith), 0); |
|
10401 } else { |
|
10402 this._notify(s, this.firedWith); |
|
10403 } |
|
10404 } |
|
10405 |
|
10406 if (when === AFTER) { |
|
10407 if (!this._afters) { |
|
10408 this._afters = []; |
|
10409 this._hasAfters = true; |
|
10410 } |
|
10411 this._afters.push(s); |
|
10412 } else { |
|
10413 if (!this._subscribers) { |
|
10414 this._subscribers = []; |
|
10415 this._hasSubs = true; |
|
10416 } |
|
10417 this._subscribers.push(s); |
|
10418 } |
|
10419 |
|
10420 if (this._kds) { |
|
10421 if (when === AFTER) { |
|
10422 this.afters[s.id] = s; |
|
10423 } else { |
|
10424 this.subscribers[s.id] = s; |
|
10425 } |
|
10426 } |
|
10427 |
|
10428 return new Y.EventHandle(this, s); |
|
10429 }, |
|
10430 |
|
10431 /** |
|
10432 * Listen for this event |
|
10433 * @method subscribe |
|
10434 * @param {Function} fn The function to execute. |
|
10435 * @return {EventHandle} Unsubscribe handle. |
|
10436 * @deprecated use on. |
|
10437 */ |
|
10438 subscribe: function(fn, context) { |
|
10439 var a = (arguments.length > 2) ? nativeSlice.call(arguments, 2) : null; |
|
10440 return this._on(fn, context, a, true); |
|
10441 }, |
|
10442 |
|
10443 /** |
|
10444 * Listen for this event |
|
10445 * @method on |
|
10446 * @param {Function} fn The function to execute. |
|
10447 * @param {object} context optional execution context. |
|
10448 * @param {mixed} arg* 0..n additional arguments to supply to the subscriber |
|
10449 * when the event fires. |
|
10450 * @return {EventHandle} An object with a detach method to detch the handler(s). |
|
10451 */ |
|
10452 on: function(fn, context) { |
|
10453 var a = (arguments.length > 2) ? nativeSlice.call(arguments, 2) : null; |
|
10454 |
|
10455 if (this.monitored && this.host) { |
|
10456 this.host._monitor('attach', this, { |
|
10457 args: arguments |
|
10458 }); |
|
10459 } |
|
10460 return this._on(fn, context, a, true); |
|
10461 }, |
|
10462 |
|
10463 /** |
|
10464 * Listen for this event after the normal subscribers have been notified and |
|
10465 * the default behavior has been applied. If a normal subscriber prevents the |
|
10466 * default behavior, it also prevents after listeners from firing. |
|
10467 * @method after |
|
10468 * @param {Function} fn The function to execute. |
|
10469 * @param {object} context optional execution context. |
|
10470 * @param {mixed} arg* 0..n additional arguments to supply to the subscriber |
|
10471 * when the event fires. |
|
10472 * @return {EventHandle} handle Unsubscribe handle. |
|
10473 */ |
|
10474 after: function(fn, context) { |
|
10475 var a = (arguments.length > 2) ? nativeSlice.call(arguments, 2) : null; |
|
10476 return this._on(fn, context, a, AFTER); |
|
10477 }, |
|
10478 |
|
10479 /** |
|
10480 * Detach listeners. |
|
10481 * @method detach |
|
10482 * @param {Function} fn The subscribed function to remove, if not supplied |
|
10483 * all will be removed. |
|
10484 * @param {Object} context The context object passed to subscribe. |
|
10485 * @return {int} returns the number of subscribers unsubscribed. |
|
10486 */ |
|
10487 detach: function(fn, context) { |
|
10488 // unsubscribe handle |
|
10489 if (fn && fn.detach) { |
|
10490 return fn.detach(); |
|
10491 } |
|
10492 |
|
10493 var i, s, |
|
10494 found = 0, |
|
10495 subs = this._subscribers, |
|
10496 afters = this._afters; |
|
10497 |
|
10498 if (subs) { |
|
10499 for (i = subs.length; i >= 0; i--) { |
|
10500 s = subs[i]; |
|
10501 if (s && (!fn || fn === s.fn)) { |
|
10502 this._delete(s, subs, i); |
|
10503 found++; |
|
10504 } |
|
10505 } |
|
10506 } |
|
10507 |
|
10508 if (afters) { |
|
10509 for (i = afters.length; i >= 0; i--) { |
|
10510 s = afters[i]; |
|
10511 if (s && (!fn || fn === s.fn)) { |
|
10512 this._delete(s, afters, i); |
|
10513 found++; |
|
10514 } |
|
10515 } |
|
10516 } |
|
10517 |
|
10518 return found; |
|
10519 }, |
|
10520 |
|
10521 /** |
|
10522 * Detach listeners. |
|
10523 * @method unsubscribe |
|
10524 * @param {Function} fn The subscribed function to remove, if not supplied |
|
10525 * all will be removed. |
|
10526 * @param {Object} context The context object passed to subscribe. |
|
10527 * @return {int|undefined} returns the number of subscribers unsubscribed. |
|
10528 * @deprecated use detach. |
|
10529 */ |
|
10530 unsubscribe: function() { |
|
10531 return this.detach.apply(this, arguments); |
|
10532 }, |
|
10533 |
|
10534 /** |
|
10535 * Notify a single subscriber |
|
10536 * @method _notify |
|
10537 * @param {Subscriber} s the subscriber. |
|
10538 * @param {Array} args the arguments array to apply to the listener. |
|
10539 * @protected |
|
10540 */ |
|
10541 _notify: function(s, args, ef) { |
|
10542 |
|
10543 this.log(this.type + '->' + 'sub: ' + s.id); |
|
10544 |
|
10545 var ret; |
|
10546 |
|
10547 ret = s.notify(args, this); |
|
10548 |
|
10549 if (false === ret || this.stopped > 1) { |
|
10550 this.log(this.type + ' cancelled by subscriber'); |
|
10551 return false; |
|
10552 } |
|
10553 |
|
10554 return true; |
|
10555 }, |
|
10556 |
|
10557 /** |
|
10558 * Logger abstraction to centralize the application of the silent flag |
|
10559 * @method log |
|
10560 * @param {string} msg message to log. |
|
10561 * @param {string} cat log category. |
|
10562 */ |
|
10563 log: function(msg, cat) { |
|
10564 }, |
|
10565 |
|
10566 /** |
|
10567 * Notifies the subscribers. The callback functions will be executed |
|
10568 * from the context specified when the event was created, and with the |
|
10569 * following parameters: |
|
10570 * <ul> |
|
10571 * <li>The type of event</li> |
|
10572 * <li>All of the arguments fire() was executed with as an array</li> |
|
10573 * <li>The custom object (if any) that was passed into the subscribe() |
|
10574 * method</li> |
|
10575 * </ul> |
|
10576 * @method fire |
|
10577 * @param {Object*} arguments an arbitrary set of parameters to pass to |
|
10578 * the handler. |
|
10579 * @return {boolean} false if one of the subscribers returned false, |
|
10580 * true otherwise. |
|
10581 * |
|
10582 */ |
|
10583 fire: function() { |
|
10584 |
|
10585 // push is the fastest way to go from arguments to arrays |
|
10586 // for most browsers currently |
|
10587 // http://jsperf.com/push-vs-concat-vs-slice/2 |
|
10588 |
|
10589 var args = []; |
|
10590 args.push.apply(args, arguments); |
|
10591 |
|
10592 return this._fire(args); |
|
10593 }, |
|
10594 |
|
10595 /** |
|
10596 * Private internal implementation for `fire`, which is can be used directly by |
|
10597 * `EventTarget` and other event module classes which have already converted from |
|
10598 * an `arguments` list to an array, to avoid the repeated overhead. |
|
10599 * |
|
10600 * @method _fire |
|
10601 * @private |
|
10602 * @param {Array} args The array of arguments passed to be passed to handlers. |
|
10603 * @return {boolean} false if one of the subscribers returned false, true otherwise. |
|
10604 */ |
|
10605 _fire: function(args) { |
|
10606 |
|
10607 if (this.fireOnce && this.fired) { |
|
10608 this.log('fireOnce event: ' + this.type + ' already fired'); |
|
10609 return true; |
|
10610 } else { |
|
10611 |
|
10612 // this doesn't happen if the event isn't published |
|
10613 // this.host._monitor('fire', this.type, args); |
|
10614 |
|
10615 this.fired = true; |
|
10616 |
|
10617 if (this.fireOnce) { |
|
10618 this.firedWith = args; |
|
10619 } |
|
10620 |
|
10621 if (this.emitFacade) { |
|
10622 return this.fireComplex(args); |
|
10623 } else { |
|
10624 return this.fireSimple(args); |
|
10625 } |
|
10626 } |
|
10627 }, |
|
10628 |
|
10629 /** |
|
10630 * Set up for notifying subscribers of non-emitFacade events. |
|
10631 * |
|
10632 * @method fireSimple |
|
10633 * @param args {Array} Arguments passed to fire() |
|
10634 * @return Boolean false if a subscriber returned false |
|
10635 * @protected |
|
10636 */ |
|
10637 fireSimple: function(args) { |
|
10638 this.stopped = 0; |
|
10639 this.prevented = 0; |
|
10640 if (this.hasSubs()) { |
|
10641 var subs = this.getSubs(); |
|
10642 this._procSubs(subs[0], args); |
|
10643 this._procSubs(subs[1], args); |
|
10644 } |
|
10645 if (this.broadcast) { |
|
10646 this._broadcast(args); |
|
10647 } |
|
10648 return this.stopped ? false : true; |
|
10649 }, |
|
10650 |
|
10651 // Requires the event-custom-complex module for full funcitonality. |
|
10652 fireComplex: function(args) { |
|
10653 this.log('Missing event-custom-complex needed to emit a facade for: ' + this.type); |
|
10654 args[0] = args[0] || {}; |
|
10655 return this.fireSimple(args); |
|
10656 }, |
|
10657 |
|
10658 /** |
|
10659 * Notifies a list of subscribers. |
|
10660 * |
|
10661 * @method _procSubs |
|
10662 * @param subs {Array} List of subscribers |
|
10663 * @param args {Array} Arguments passed to fire() |
|
10664 * @param ef {} |
|
10665 * @return Boolean false if a subscriber returns false or stops the event |
|
10666 * propagation via e.stopPropagation(), |
|
10667 * e.stopImmediatePropagation(), or e.halt() |
|
10668 * @private |
|
10669 */ |
|
10670 _procSubs: function(subs, args, ef) { |
|
10671 var s, i, l; |
|
10672 |
|
10673 for (i = 0, l = subs.length; i < l; i++) { |
|
10674 s = subs[i]; |
|
10675 if (s && s.fn) { |
|
10676 if (false === this._notify(s, args, ef)) { |
|
10677 this.stopped = 2; |
|
10678 } |
|
10679 if (this.stopped === 2) { |
|
10680 return false; |
|
10681 } |
|
10682 } |
|
10683 } |
|
10684 |
|
10685 return true; |
|
10686 }, |
|
10687 |
|
10688 /** |
|
10689 * Notifies the YUI instance if the event is configured with broadcast = 1, |
|
10690 * and both the YUI instance and Y.Global if configured with broadcast = 2. |
|
10691 * |
|
10692 * @method _broadcast |
|
10693 * @param args {Array} Arguments sent to fire() |
|
10694 * @private |
|
10695 */ |
|
10696 _broadcast: function(args) { |
|
10697 if (!this.stopped && this.broadcast) { |
|
10698 |
|
10699 var a = args.concat(); |
|
10700 a.unshift(this.type); |
|
10701 |
|
10702 if (this.host !== Y) { |
|
10703 Y.fire.apply(Y, a); |
|
10704 } |
|
10705 |
|
10706 if (this.broadcast === 2) { |
|
10707 Y.Global.fire.apply(Y.Global, a); |
|
10708 } |
|
10709 } |
|
10710 }, |
|
10711 |
|
10712 /** |
|
10713 * Removes all listeners |
|
10714 * @method unsubscribeAll |
|
10715 * @return {int} The number of listeners unsubscribed. |
|
10716 * @deprecated use detachAll. |
|
10717 */ |
|
10718 unsubscribeAll: function() { |
|
10719 return this.detachAll.apply(this, arguments); |
|
10720 }, |
|
10721 |
|
10722 /** |
|
10723 * Removes all listeners |
|
10724 * @method detachAll |
|
10725 * @return {int} The number of listeners unsubscribed. |
|
10726 */ |
|
10727 detachAll: function() { |
|
10728 return this.detach(); |
|
10729 }, |
|
10730 |
|
10731 /** |
|
10732 * Deletes the subscriber from the internal store of on() and after() |
|
10733 * subscribers. |
|
10734 * |
|
10735 * @method _delete |
|
10736 * @param s subscriber object. |
|
10737 * @param subs (optional) on or after subscriber array |
|
10738 * @param index (optional) The index found. |
|
10739 * @private |
|
10740 */ |
|
10741 _delete: function(s, subs, i) { |
|
10742 var when = s._when; |
|
10743 |
|
10744 if (!subs) { |
|
10745 subs = (when === AFTER) ? this._afters : this._subscribers; |
|
10746 } |
|
10747 |
|
10748 if (subs) { |
|
10749 i = YArray.indexOf(subs, s, 0); |
|
10750 |
|
10751 if (s && subs[i] === s) { |
|
10752 subs.splice(i, 1); |
|
10753 |
|
10754 if (subs.length === 0) { |
|
10755 if (when === AFTER) { |
|
10756 this._hasAfters = false; |
|
10757 } else { |
|
10758 this._hasSubs = false; |
|
10759 } |
|
10760 } |
|
10761 } |
|
10762 } |
|
10763 |
|
10764 if (this._kds) { |
|
10765 if (when === AFTER) { |
|
10766 delete this.afters[s.id]; |
|
10767 } else { |
|
10768 delete this.subscribers[s.id]; |
|
10769 } |
|
10770 } |
|
10771 |
|
10772 if (this.monitored && this.host) { |
|
10773 this.host._monitor('detach', this, { |
|
10774 ce: this, |
|
10775 sub: s |
|
10776 }); |
|
10777 } |
|
10778 |
|
10779 if (s) { |
|
10780 s.deleted = true; |
|
10781 } |
|
10782 } |
|
10783 }; |
|
10784 /** |
|
10785 * Stores the subscriber information to be used when the event fires. |
|
10786 * @param {Function} fn The wrapped function to execute. |
|
10787 * @param {Object} context The value of the keyword 'this' in the listener. |
|
10788 * @param {Array} args* 0..n additional arguments to supply the listener. |
|
10789 * |
|
10790 * @class Subscriber |
|
10791 * @constructor |
|
10792 */ |
|
10793 Y.Subscriber = function(fn, context, args, when) { |
|
10794 |
|
10795 /** |
|
10796 * The callback that will be execute when the event fires |
|
10797 * This is wrapped by Y.rbind if obj was supplied. |
|
10798 * @property fn |
|
10799 * @type Function |
|
10800 */ |
|
10801 this.fn = fn; |
|
10802 |
|
10803 /** |
|
10804 * Optional 'this' keyword for the listener |
|
10805 * @property context |
|
10806 * @type Object |
|
10807 */ |
|
10808 this.context = context; |
|
10809 |
|
10810 /** |
|
10811 * Unique subscriber id |
|
10812 * @property id |
|
10813 * @type String |
|
10814 */ |
|
10815 this.id = Y.guid(); |
|
10816 |
|
10817 /** |
|
10818 * Additional arguments to propagate to the subscriber |
|
10819 * @property args |
|
10820 * @type Array |
|
10821 */ |
|
10822 this.args = args; |
|
10823 |
|
10824 this._when = when; |
|
10825 |
|
10826 /** |
|
10827 * Custom events for a given fire transaction. |
|
10828 * @property events |
|
10829 * @type {EventTarget} |
|
10830 */ |
|
10831 // this.events = null; |
|
10832 |
|
10833 /** |
|
10834 * This listener only reacts to the event once |
|
10835 * @property once |
|
10836 */ |
|
10837 // this.once = false; |
|
10838 |
|
10839 }; |
|
10840 |
|
10841 Y.Subscriber.prototype = { |
|
10842 constructor: Y.Subscriber, |
|
10843 |
|
10844 _notify: function(c, args, ce) { |
|
10845 if (this.deleted && !this.postponed) { |
|
10846 if (this.postponed) { |
|
10847 delete this.fn; |
|
10848 delete this.context; |
|
10849 } else { |
|
10850 delete this.postponed; |
|
10851 return null; |
|
10852 } |
|
10853 } |
|
10854 var a = this.args, ret; |
|
10855 switch (ce.signature) { |
|
10856 case 0: |
|
10857 ret = this.fn.call(c, ce.type, args, c); |
|
10858 break; |
|
10859 case 1: |
|
10860 ret = this.fn.call(c, args[0] || null, c); |
|
10861 break; |
|
10862 default: |
|
10863 if (a || args) { |
|
10864 args = args || []; |
|
10865 a = (a) ? args.concat(a) : args; |
|
10866 ret = this.fn.apply(c, a); |
|
10867 } else { |
|
10868 ret = this.fn.call(c); |
|
10869 } |
|
10870 } |
|
10871 |
|
10872 if (this.once) { |
|
10873 ce._delete(this); |
|
10874 } |
|
10875 |
|
10876 return ret; |
|
10877 }, |
|
10878 |
|
10879 /** |
|
10880 * Executes the subscriber. |
|
10881 * @method notify |
|
10882 * @param args {Array} Arguments array for the subscriber. |
|
10883 * @param ce {CustomEvent} The custom event that sent the notification. |
|
10884 */ |
|
10885 notify: function(args, ce) { |
|
10886 var c = this.context, |
|
10887 ret = true; |
|
10888 |
|
10889 if (!c) { |
|
10890 c = (ce.contextFn) ? ce.contextFn() : ce.context; |
|
10891 } |
|
10892 |
|
10893 // only catch errors if we will not re-throw them. |
|
10894 if (Y.config && Y.config.throwFail) { |
|
10895 ret = this._notify(c, args, ce); |
|
10896 } else { |
|
10897 try { |
|
10898 ret = this._notify(c, args, ce); |
|
10899 } catch (e) { |
|
10900 Y.error(this + ' failed: ' + e.message, e); |
|
10901 } |
|
10902 } |
|
10903 |
|
10904 return ret; |
|
10905 }, |
|
10906 |
|
10907 /** |
|
10908 * Returns true if the fn and obj match this objects properties. |
|
10909 * Used by the unsubscribe method to match the right subscriber. |
|
10910 * |
|
10911 * @method contains |
|
10912 * @param {Function} fn the function to execute. |
|
10913 * @param {Object} context optional 'this' keyword for the listener. |
|
10914 * @return {boolean} true if the supplied arguments match this |
|
10915 * subscriber's signature. |
|
10916 */ |
|
10917 contains: function(fn, context) { |
|
10918 if (context) { |
|
10919 return ((this.fn === fn) && this.context === context); |
|
10920 } else { |
|
10921 return (this.fn === fn); |
|
10922 } |
|
10923 }, |
|
10924 |
|
10925 valueOf : function() { |
|
10926 return this.id; |
|
10927 } |
|
10928 |
|
10929 }; |
|
10930 /** |
|
10931 * Return value from all subscribe operations |
|
10932 * @class EventHandle |
|
10933 * @constructor |
|
10934 * @param {CustomEvent} evt the custom event. |
|
10935 * @param {Subscriber} sub the subscriber. |
|
10936 */ |
|
10937 Y.EventHandle = function(evt, sub) { |
|
10938 |
|
10939 /** |
|
10940 * The custom event |
|
10941 * |
|
10942 * @property evt |
|
10943 * @type CustomEvent |
|
10944 */ |
|
10945 this.evt = evt; |
|
10946 |
|
10947 /** |
|
10948 * The subscriber object |
|
10949 * |
|
10950 * @property sub |
|
10951 * @type Subscriber |
|
10952 */ |
|
10953 this.sub = sub; |
|
10954 }; |
|
10955 |
|
10956 Y.EventHandle.prototype = { |
|
10957 batch: function(f, c) { |
|
10958 f.call(c || this, this); |
|
10959 if (Y.Lang.isArray(this.evt)) { |
|
10960 Y.Array.each(this.evt, function(h) { |
|
10961 h.batch.call(c || h, f); |
|
10962 }); |
|
10963 } |
|
10964 }, |
|
10965 |
|
10966 /** |
|
10967 * Detaches this subscriber |
|
10968 * @method detach |
|
10969 * @return {int} the number of detached listeners |
|
10970 */ |
|
10971 detach: function() { |
|
10972 var evt = this.evt, detached = 0, i; |
|
10973 if (evt) { |
|
10974 if (Y.Lang.isArray(evt)) { |
|
10975 for (i = 0; i < evt.length; i++) { |
|
10976 detached += evt[i].detach(); |
|
10977 } |
|
10978 } else { |
|
10979 evt._delete(this.sub); |
|
10980 detached = 1; |
|
10981 } |
|
10982 |
|
10983 } |
|
10984 |
|
10985 return detached; |
|
10986 }, |
|
10987 |
|
10988 /** |
|
10989 * Monitor the event state for the subscribed event. The first parameter |
|
10990 * is what should be monitored, the rest are the normal parameters when |
|
10991 * subscribing to an event. |
|
10992 * @method monitor |
|
10993 * @param what {string} what to monitor ('attach', 'detach', 'publish'). |
|
10994 * @return {EventHandle} return value from the monitor event subscription. |
|
10995 */ |
|
10996 monitor: function(what) { |
|
10997 return this.evt.monitor.apply(this.evt, arguments); |
|
10998 } |
|
10999 }; |
|
11000 |
|
11001 /** |
|
11002 * Custom event engine, DOM event listener abstraction layer, synthetic DOM |
|
11003 * events. |
|
11004 * @module event-custom |
|
11005 * @submodule event-custom-base |
|
11006 */ |
|
11007 |
|
11008 /** |
|
11009 * EventTarget provides the implementation for any object to |
|
11010 * publish, subscribe and fire to custom events, and also |
|
11011 * alows other EventTargets to target the object with events |
|
11012 * sourced from the other object. |
|
11013 * EventTarget is designed to be used with Y.augment to wrap |
|
11014 * EventCustom in an interface that allows events to be listened to |
|
11015 * and fired by name. This makes it possible for implementing code to |
|
11016 * subscribe to an event that either has not been created yet, or will |
|
11017 * not be created at all. |
|
11018 * @class EventTarget |
|
11019 * @param opts a configuration object |
|
11020 * @config emitFacade {boolean} if true, all events will emit event |
|
11021 * facade payloads by default (default false) |
|
11022 * @config prefix {String} the prefix to apply to non-prefixed event names |
|
11023 */ |
|
11024 |
|
11025 var L = Y.Lang, |
|
11026 PREFIX_DELIMITER = ':', |
|
11027 CATEGORY_DELIMITER = '|', |
|
11028 AFTER_PREFIX = '~AFTER~', |
|
11029 WILD_TYPE_RE = /(.*?)(:)(.*?)/, |
|
11030 |
|
11031 _wildType = Y.cached(function(type) { |
|
11032 return type.replace(WILD_TYPE_RE, "*$2$3"); |
|
11033 }), |
|
11034 |
|
11035 /** |
|
11036 * If the instance has a prefix attribute and the |
|
11037 * event type is not prefixed, the instance prefix is |
|
11038 * applied to the supplied type. |
|
11039 * @method _getType |
|
11040 * @private |
|
11041 */ |
|
11042 _getType = function(type, pre) { |
|
11043 |
|
11044 if (!pre || type.indexOf(PREFIX_DELIMITER) > -1) { |
|
11045 return type; |
|
11046 } |
|
11047 |
|
11048 return pre + PREFIX_DELIMITER + type; |
|
11049 }, |
|
11050 |
|
11051 /** |
|
11052 * Returns an array with the detach key (if provided), |
|
11053 * and the prefixed event name from _getType |
|
11054 * Y.on('detachcategory| menu:click', fn) |
|
11055 * @method _parseType |
|
11056 * @private |
|
11057 */ |
|
11058 _parseType = Y.cached(function(type, pre) { |
|
11059 |
|
11060 var t = type, detachcategory, after, i; |
|
11061 |
|
11062 if (!L.isString(t)) { |
|
11063 return t; |
|
11064 } |
|
11065 |
|
11066 i = t.indexOf(AFTER_PREFIX); |
|
11067 |
|
11068 if (i > -1) { |
|
11069 after = true; |
|
11070 t = t.substr(AFTER_PREFIX.length); |
|
11071 } |
|
11072 |
|
11073 i = t.indexOf(CATEGORY_DELIMITER); |
|
11074 |
|
11075 if (i > -1) { |
|
11076 detachcategory = t.substr(0, (i)); |
|
11077 t = t.substr(i+1); |
|
11078 if (t === '*') { |
|
11079 t = null; |
|
11080 } |
|
11081 } |
|
11082 |
|
11083 // detach category, full type with instance prefix, is this an after listener, short type |
|
11084 return [detachcategory, (pre) ? _getType(t, pre) : t, after, t]; |
|
11085 }), |
|
11086 |
|
11087 ET = function(opts) { |
|
11088 |
|
11089 var etState = this._yuievt, |
|
11090 etConfig; |
|
11091 |
|
11092 if (!etState) { |
|
11093 etState = this._yuievt = { |
|
11094 events: {}, // PERF: Not much point instantiating lazily. We're bound to have events |
|
11095 targets: null, // PERF: Instantiate lazily, if user actually adds target, |
|
11096 config: { |
|
11097 host: this, |
|
11098 context: this |
|
11099 }, |
|
11100 chain: Y.config.chain |
|
11101 }; |
|
11102 } |
|
11103 |
|
11104 etConfig = etState.config; |
|
11105 |
|
11106 if (opts) { |
|
11107 mixConfigs(etConfig, opts, true); |
|
11108 |
|
11109 if (opts.chain !== undefined) { |
|
11110 etState.chain = opts.chain; |
|
11111 } |
|
11112 |
|
11113 if (opts.prefix) { |
|
11114 etConfig.prefix = opts.prefix; |
|
11115 } |
|
11116 } |
|
11117 }; |
|
11118 |
|
11119 ET.prototype = { |
|
11120 |
|
11121 constructor: ET, |
|
11122 |
|
11123 /** |
|
11124 * Listen to a custom event hosted by this object one time. |
|
11125 * This is the equivalent to <code>on</code> except the |
|
11126 * listener is immediatelly detached when it is executed. |
|
11127 * @method once |
|
11128 * @param {String} type The name of the event |
|
11129 * @param {Function} fn The callback to execute in response to the event |
|
11130 * @param {Object} [context] Override `this` object in callback |
|
11131 * @param {Any} [arg*] 0..n additional arguments to supply to the subscriber |
|
11132 * @return {EventHandle} A subscription handle capable of detaching the |
|
11133 * subscription |
|
11134 */ |
|
11135 once: function() { |
|
11136 var handle = this.on.apply(this, arguments); |
|
11137 handle.batch(function(hand) { |
|
11138 if (hand.sub) { |
|
11139 hand.sub.once = true; |
|
11140 } |
|
11141 }); |
|
11142 return handle; |
|
11143 }, |
|
11144 |
|
11145 /** |
|
11146 * Listen to a custom event hosted by this object one time. |
|
11147 * This is the equivalent to <code>after</code> except the |
|
11148 * listener is immediatelly detached when it is executed. |
|
11149 * @method onceAfter |
|
11150 * @param {String} type The name of the event |
|
11151 * @param {Function} fn The callback to execute in response to the event |
|
11152 * @param {Object} [context] Override `this` object in callback |
|
11153 * @param {Any} [arg*] 0..n additional arguments to supply to the subscriber |
|
11154 * @return {EventHandle} A subscription handle capable of detaching that |
|
11155 * subscription |
|
11156 */ |
|
11157 onceAfter: function() { |
|
11158 var handle = this.after.apply(this, arguments); |
|
11159 handle.batch(function(hand) { |
|
11160 if (hand.sub) { |
|
11161 hand.sub.once = true; |
|
11162 } |
|
11163 }); |
|
11164 return handle; |
|
11165 }, |
|
11166 |
|
11167 /** |
|
11168 * Takes the type parameter passed to 'on' and parses out the |
|
11169 * various pieces that could be included in the type. If the |
|
11170 * event type is passed without a prefix, it will be expanded |
|
11171 * to include the prefix one is supplied or the event target |
|
11172 * is configured with a default prefix. |
|
11173 * @method parseType |
|
11174 * @param {String} type the type |
|
11175 * @param {String} [pre=this._yuievt.config.prefix] the prefix |
|
11176 * @since 3.3.0 |
|
11177 * @return {Array} an array containing: |
|
11178 * * the detach category, if supplied, |
|
11179 * * the prefixed event type, |
|
11180 * * whether or not this is an after listener, |
|
11181 * * the supplied event type |
|
11182 */ |
|
11183 parseType: function(type, pre) { |
|
11184 return _parseType(type, pre || this._yuievt.config.prefix); |
|
11185 }, |
|
11186 |
|
11187 /** |
|
11188 * Subscribe a callback function to a custom event fired by this object or |
|
11189 * from an object that bubbles its events to this object. |
|
11190 * |
|
11191 * Callback functions for events published with `emitFacade = true` will |
|
11192 * receive an `EventFacade` as the first argument (typically named "e"). |
|
11193 * These callbacks can then call `e.preventDefault()` to disable the |
|
11194 * behavior published to that event's `defaultFn`. See the `EventFacade` |
|
11195 * API for all available properties and methods. Subscribers to |
|
11196 * non-`emitFacade` events will receive the arguments passed to `fire()` |
|
11197 * after the event name. |
|
11198 * |
|
11199 * To subscribe to multiple events at once, pass an object as the first |
|
11200 * argument, where the key:value pairs correspond to the eventName:callback, |
|
11201 * or pass an array of event names as the first argument to subscribe to |
|
11202 * all listed events with the same callback. |
|
11203 * |
|
11204 * Returning `false` from a callback is supported as an alternative to |
|
11205 * calling `e.preventDefault(); e.stopPropagation();`. However, it is |
|
11206 * recommended to use the event methods whenever possible. |
|
11207 * |
|
11208 * @method on |
|
11209 * @param {String} type The name of the event |
|
11210 * @param {Function} fn The callback to execute in response to the event |
|
11211 * @param {Object} [context] Override `this` object in callback |
|
11212 * @param {Any} [arg*] 0..n additional arguments to supply to the subscriber |
|
11213 * @return {EventHandle} A subscription handle capable of detaching that |
|
11214 * subscription |
|
11215 */ |
|
11216 on: function(type, fn, context) { |
|
11217 |
|
11218 var yuievt = this._yuievt, |
|
11219 parts = _parseType(type, yuievt.config.prefix), f, c, args, ret, ce, |
|
11220 detachcategory, handle, store = Y.Env.evt.handles, after, adapt, shorttype, |
|
11221 Node = Y.Node, n, domevent, isArr; |
|
11222 |
|
11223 // full name, args, detachcategory, after |
|
11224 this._monitor('attach', parts[1], { |
|
11225 args: arguments, |
|
11226 category: parts[0], |
|
11227 after: parts[2] |
|
11228 }); |
|
11229 |
|
11230 if (L.isObject(type)) { |
|
11231 |
|
11232 if (L.isFunction(type)) { |
|
11233 return Y.Do.before.apply(Y.Do, arguments); |
|
11234 } |
|
11235 |
|
11236 f = fn; |
|
11237 c = context; |
|
11238 args = nativeSlice.call(arguments, 0); |
|
11239 ret = []; |
|
11240 |
|
11241 if (L.isArray(type)) { |
|
11242 isArr = true; |
|
11243 } |
|
11244 |
|
11245 after = type._after; |
|
11246 delete type._after; |
|
11247 |
|
11248 Y.each(type, function(v, k) { |
|
11249 |
|
11250 if (L.isObject(v)) { |
|
11251 f = v.fn || ((L.isFunction(v)) ? v : f); |
|
11252 c = v.context || c; |
|
11253 } |
|
11254 |
|
11255 var nv = (after) ? AFTER_PREFIX : ''; |
|
11256 |
|
11257 args[0] = nv + ((isArr) ? v : k); |
|
11258 args[1] = f; |
|
11259 args[2] = c; |
|
11260 |
|
11261 ret.push(this.on.apply(this, args)); |
|
11262 |
|
11263 }, this); |
|
11264 |
|
11265 return (yuievt.chain) ? this : new Y.EventHandle(ret); |
|
11266 } |
|
11267 |
|
11268 detachcategory = parts[0]; |
|
11269 after = parts[2]; |
|
11270 shorttype = parts[3]; |
|
11271 |
|
11272 // extra redirection so we catch adaptor events too. take a look at this. |
|
11273 if (Node && Y.instanceOf(this, Node) && (shorttype in Node.DOM_EVENTS)) { |
|
11274 args = nativeSlice.call(arguments, 0); |
|
11275 args.splice(2, 0, Node.getDOMNode(this)); |
|
11276 return Y.on.apply(Y, args); |
|
11277 } |
|
11278 |
|
11279 type = parts[1]; |
|
11280 |
|
11281 if (Y.instanceOf(this, YUI)) { |
|
11282 |
|
11283 adapt = Y.Env.evt.plugins[type]; |
|
11284 args = nativeSlice.call(arguments, 0); |
|
11285 args[0] = shorttype; |
|
11286 |
|
11287 if (Node) { |
|
11288 n = args[2]; |
|
11289 |
|
11290 if (Y.instanceOf(n, Y.NodeList)) { |
|
11291 n = Y.NodeList.getDOMNodes(n); |
|
11292 } else if (Y.instanceOf(n, Node)) { |
|
11293 n = Node.getDOMNode(n); |
|
11294 } |
|
11295 |
|
11296 domevent = (shorttype in Node.DOM_EVENTS); |
|
11297 |
|
11298 // Captures both DOM events and event plugins. |
|
11299 if (domevent) { |
|
11300 args[2] = n; |
|
11301 } |
|
11302 } |
|
11303 |
|
11304 // check for the existance of an event adaptor |
|
11305 if (adapt) { |
|
11306 handle = adapt.on.apply(Y, args); |
|
11307 } else if ((!type) || domevent) { |
|
11308 handle = Y.Event._attach(args); |
|
11309 } |
|
11310 |
|
11311 } |
|
11312 |
|
11313 if (!handle) { |
|
11314 ce = yuievt.events[type] || this.publish(type); |
|
11315 handle = ce._on(fn, context, (arguments.length > 3) ? nativeSlice.call(arguments, 3) : null, (after) ? 'after' : true); |
|
11316 |
|
11317 // TODO: More robust regex, accounting for category |
|
11318 if (type.indexOf("*:") !== -1) { |
|
11319 this._hasSiblings = true; |
|
11320 } |
|
11321 } |
|
11322 |
|
11323 if (detachcategory) { |
|
11324 store[detachcategory] = store[detachcategory] || {}; |
|
11325 store[detachcategory][type] = store[detachcategory][type] || []; |
|
11326 store[detachcategory][type].push(handle); |
|
11327 } |
|
11328 |
|
11329 return (yuievt.chain) ? this : handle; |
|
11330 |
|
11331 }, |
|
11332 |
|
11333 /** |
|
11334 * subscribe to an event |
|
11335 * @method subscribe |
|
11336 * @deprecated use on |
|
11337 */ |
|
11338 subscribe: function() { |
|
11339 return this.on.apply(this, arguments); |
|
11340 }, |
|
11341 |
|
11342 /** |
|
11343 * Detach one or more listeners the from the specified event |
|
11344 * @method detach |
|
11345 * @param type {string|Object} Either the handle to the subscriber or the |
|
11346 * type of event. If the type |
|
11347 * is not specified, it will attempt to remove |
|
11348 * the listener from all hosted events. |
|
11349 * @param fn {Function} The subscribed function to unsubscribe, if not |
|
11350 * supplied, all subscribers will be removed. |
|
11351 * @param context {Object} The custom object passed to subscribe. This is |
|
11352 * optional, but if supplied will be used to |
|
11353 * disambiguate multiple listeners that are the same |
|
11354 * (e.g., you subscribe many object using a function |
|
11355 * that lives on the prototype) |
|
11356 * @return {EventTarget} the host |
|
11357 */ |
|
11358 detach: function(type, fn, context) { |
|
11359 |
|
11360 var evts = this._yuievt.events, |
|
11361 i, |
|
11362 Node = Y.Node, |
|
11363 isNode = Node && (Y.instanceOf(this, Node)); |
|
11364 |
|
11365 // detachAll disabled on the Y instance. |
|
11366 if (!type && (this !== Y)) { |
|
11367 for (i in evts) { |
|
11368 if (evts.hasOwnProperty(i)) { |
|
11369 evts[i].detach(fn, context); |
|
11370 } |
|
11371 } |
|
11372 if (isNode) { |
|
11373 Y.Event.purgeElement(Node.getDOMNode(this)); |
|
11374 } |
|
11375 |
|
11376 return this; |
|
11377 } |
|
11378 |
|
11379 var parts = _parseType(type, this._yuievt.config.prefix), |
|
11380 detachcategory = L.isArray(parts) ? parts[0] : null, |
|
11381 shorttype = (parts) ? parts[3] : null, |
|
11382 adapt, store = Y.Env.evt.handles, detachhost, cat, args, |
|
11383 ce, |
|
11384 |
|
11385 keyDetacher = function(lcat, ltype, host) { |
|
11386 var handles = lcat[ltype], ce, i; |
|
11387 if (handles) { |
|
11388 for (i = handles.length - 1; i >= 0; --i) { |
|
11389 ce = handles[i].evt; |
|
11390 if (ce.host === host || ce.el === host) { |
|
11391 handles[i].detach(); |
|
11392 } |
|
11393 } |
|
11394 } |
|
11395 }; |
|
11396 |
|
11397 if (detachcategory) { |
|
11398 |
|
11399 cat = store[detachcategory]; |
|
11400 type = parts[1]; |
|
11401 detachhost = (isNode) ? Y.Node.getDOMNode(this) : this; |
|
11402 |
|
11403 if (cat) { |
|
11404 if (type) { |
|
11405 keyDetacher(cat, type, detachhost); |
|
11406 } else { |
|
11407 for (i in cat) { |
|
11408 if (cat.hasOwnProperty(i)) { |
|
11409 keyDetacher(cat, i, detachhost); |
|
11410 } |
|
11411 } |
|
11412 } |
|
11413 |
|
11414 return this; |
|
11415 } |
|
11416 |
|
11417 // If this is an event handle, use it to detach |
|
11418 } else if (L.isObject(type) && type.detach) { |
|
11419 type.detach(); |
|
11420 return this; |
|
11421 // extra redirection so we catch adaptor events too. take a look at this. |
|
11422 } else if (isNode && ((!shorttype) || (shorttype in Node.DOM_EVENTS))) { |
|
11423 args = nativeSlice.call(arguments, 0); |
|
11424 args[2] = Node.getDOMNode(this); |
|
11425 Y.detach.apply(Y, args); |
|
11426 return this; |
|
11427 } |
|
11428 |
|
11429 adapt = Y.Env.evt.plugins[shorttype]; |
|
11430 |
|
11431 // The YUI instance handles DOM events and adaptors |
|
11432 if (Y.instanceOf(this, YUI)) { |
|
11433 args = nativeSlice.call(arguments, 0); |
|
11434 // use the adaptor specific detach code if |
|
11435 if (adapt && adapt.detach) { |
|
11436 adapt.detach.apply(Y, args); |
|
11437 return this; |
|
11438 // DOM event fork |
|
11439 } else if (!type || (!adapt && Node && (type in Node.DOM_EVENTS))) { |
|
11440 args[0] = type; |
|
11441 Y.Event.detach.apply(Y.Event, args); |
|
11442 return this; |
|
11443 } |
|
11444 } |
|
11445 |
|
11446 // ce = evts[type]; |
|
11447 ce = evts[parts[1]]; |
|
11448 if (ce) { |
|
11449 ce.detach(fn, context); |
|
11450 } |
|
11451 |
|
11452 return this; |
|
11453 }, |
|
11454 |
|
11455 /** |
|
11456 * detach a listener |
|
11457 * @method unsubscribe |
|
11458 * @deprecated use detach |
|
11459 */ |
|
11460 unsubscribe: function() { |
|
11461 return this.detach.apply(this, arguments); |
|
11462 }, |
|
11463 |
|
11464 /** |
|
11465 * Removes all listeners from the specified event. If the event type |
|
11466 * is not specified, all listeners from all hosted custom events will |
|
11467 * be removed. |
|
11468 * @method detachAll |
|
11469 * @param type {String} The type, or name of the event |
|
11470 */ |
|
11471 detachAll: function(type) { |
|
11472 return this.detach(type); |
|
11473 }, |
|
11474 |
|
11475 /** |
|
11476 * Removes all listeners from the specified event. If the event type |
|
11477 * is not specified, all listeners from all hosted custom events will |
|
11478 * be removed. |
|
11479 * @method unsubscribeAll |
|
11480 * @param type {String} The type, or name of the event |
|
11481 * @deprecated use detachAll |
|
11482 */ |
|
11483 unsubscribeAll: function() { |
|
11484 return this.detachAll.apply(this, arguments); |
|
11485 }, |
|
11486 |
|
11487 /** |
|
11488 * Creates a new custom event of the specified type. If a custom event |
|
11489 * by that name already exists, it will not be re-created. In either |
|
11490 * case the custom event is returned. |
|
11491 * |
|
11492 * @method publish |
|
11493 * |
|
11494 * @param type {String} the type, or name of the event |
|
11495 * @param opts {object} optional config params. Valid properties are: |
|
11496 * |
|
11497 * <ul> |
|
11498 * <li> |
|
11499 * 'broadcast': whether or not the YUI instance and YUI global are notified when the event is fired (false) |
|
11500 * </li> |
|
11501 * <li> |
|
11502 * 'bubbles': whether or not this event bubbles (true) |
|
11503 * Events can only bubble if emitFacade is true. |
|
11504 * </li> |
|
11505 * <li> |
|
11506 * 'context': the default execution context for the listeners (this) |
|
11507 * </li> |
|
11508 * <li> |
|
11509 * 'defaultFn': the default function to execute when this event fires if preventDefault was not called |
|
11510 * </li> |
|
11511 * <li> |
|
11512 * 'emitFacade': whether or not this event emits a facade (false) |
|
11513 * </li> |
|
11514 * <li> |
|
11515 * 'prefix': the prefix for this targets events, e.g., 'menu' in 'menu:click' |
|
11516 * </li> |
|
11517 * <li> |
|
11518 * 'fireOnce': if an event is configured to fire once, new subscribers after |
|
11519 * the fire will be notified immediately. |
|
11520 * </li> |
|
11521 * <li> |
|
11522 * 'async': fireOnce event listeners will fire synchronously if the event has already |
|
11523 * fired unless async is true. |
|
11524 * </li> |
|
11525 * <li> |
|
11526 * 'preventable': whether or not preventDefault() has an effect (true) |
|
11527 * </li> |
|
11528 * <li> |
|
11529 * 'preventedFn': a function that is executed when preventDefault is called |
|
11530 * </li> |
|
11531 * <li> |
|
11532 * 'queuable': whether or not this event can be queued during bubbling (false) |
|
11533 * </li> |
|
11534 * <li> |
|
11535 * 'silent': if silent is true, debug messages are not provided for this event. |
|
11536 * </li> |
|
11537 * <li> |
|
11538 * 'stoppedFn': a function that is executed when stopPropagation is called |
|
11539 * </li> |
|
11540 * |
|
11541 * <li> |
|
11542 * 'monitored': specifies whether or not this event should send notifications about |
|
11543 * when the event has been attached, detached, or published. |
|
11544 * </li> |
|
11545 * <li> |
|
11546 * 'type': the event type (valid option if not provided as the first parameter to publish) |
|
11547 * </li> |
|
11548 * </ul> |
|
11549 * |
|
11550 * @return {CustomEvent} the custom event |
|
11551 * |
|
11552 */ |
|
11553 publish: function(type, opts) { |
|
11554 |
|
11555 var ret, |
|
11556 etState = this._yuievt, |
|
11557 etConfig = etState.config, |
|
11558 pre = etConfig.prefix; |
|
11559 |
|
11560 if (typeof type === "string") { |
|
11561 if (pre) { |
|
11562 type = _getType(type, pre); |
|
11563 } |
|
11564 ret = this._publish(type, etConfig, opts); |
|
11565 } else { |
|
11566 ret = {}; |
|
11567 |
|
11568 Y.each(type, function(v, k) { |
|
11569 if (pre) { |
|
11570 k = _getType(k, pre); |
|
11571 } |
|
11572 ret[k] = this._publish(k, etConfig, v || opts); |
|
11573 }, this); |
|
11574 |
|
11575 } |
|
11576 |
|
11577 return ret; |
|
11578 }, |
|
11579 |
|
11580 /** |
|
11581 * Returns the fully qualified type, given a short type string. |
|
11582 * That is, returns "foo:bar" when given "bar" if "foo" is the configured prefix. |
|
11583 * |
|
11584 * NOTE: This method, unlike _getType, does no checking of the value passed in, and |
|
11585 * is designed to be used with the low level _publish() method, for critical path |
|
11586 * implementations which need to fast-track publish for performance reasons. |
|
11587 * |
|
11588 * @method _getFullType |
|
11589 * @private |
|
11590 * @param {String} type The short type to prefix |
|
11591 * @return {String} The prefixed type, if a prefix is set, otherwise the type passed in |
|
11592 */ |
|
11593 _getFullType : function(type) { |
|
11594 |
|
11595 var pre = this._yuievt.config.prefix; |
|
11596 |
|
11597 if (pre) { |
|
11598 return pre + PREFIX_DELIMITER + type; |
|
11599 } else { |
|
11600 return type; |
|
11601 } |
|
11602 }, |
|
11603 |
|
11604 /** |
|
11605 * The low level event publish implementation. It expects all the massaging to have been done |
|
11606 * outside of this method. e.g. the `type` to `fullType` conversion. It's designed to be a fast |
|
11607 * path publish, which can be used by critical code paths to improve performance. |
|
11608 * |
|
11609 * @method _publish |
|
11610 * @private |
|
11611 * @param {String} fullType The prefixed type of the event to publish. |
|
11612 * @param {Object} etOpts The EventTarget specific configuration to mix into the published event. |
|
11613 * @param {Object} ceOpts The publish specific configuration to mix into the published event. |
|
11614 * @return {CustomEvent} The published event. If called without `etOpts` or `ceOpts`, this will |
|
11615 * be the default `CustomEvent` instance, and can be configured independently. |
|
11616 */ |
|
11617 _publish : function(fullType, etOpts, ceOpts) { |
|
11618 |
|
11619 var ce, |
|
11620 etState = this._yuievt, |
|
11621 etConfig = etState.config, |
|
11622 host = etConfig.host, |
|
11623 context = etConfig.context, |
|
11624 events = etState.events; |
|
11625 |
|
11626 ce = events[fullType]; |
|
11627 |
|
11628 // PERF: Hate to pull the check out of monitor, but trying to keep critical path tight. |
|
11629 if ((etConfig.monitored && !ce) || (ce && ce.monitored)) { |
|
11630 this._monitor('publish', fullType, { |
|
11631 args: arguments |
|
11632 }); |
|
11633 } |
|
11634 |
|
11635 if (!ce) { |
|
11636 // Publish event |
|
11637 ce = events[fullType] = new Y.CustomEvent(fullType, etOpts); |
|
11638 |
|
11639 if (!etOpts) { |
|
11640 ce.host = host; |
|
11641 ce.context = context; |
|
11642 } |
|
11643 } |
|
11644 |
|
11645 if (ceOpts) { |
|
11646 mixConfigs(ce, ceOpts, true); |
|
11647 } |
|
11648 |
|
11649 return ce; |
|
11650 }, |
|
11651 |
|
11652 /** |
|
11653 * This is the entry point for the event monitoring system. |
|
11654 * You can monitor 'attach', 'detach', 'fire', and 'publish'. |
|
11655 * When configured, these events generate an event. click -> |
|
11656 * click_attach, click_detach, click_publish -- these can |
|
11657 * be subscribed to like other events to monitor the event |
|
11658 * system. Inividual published events can have monitoring |
|
11659 * turned on or off (publish can't be turned off before it |
|
11660 * it published) by setting the events 'monitor' config. |
|
11661 * |
|
11662 * @method _monitor |
|
11663 * @param what {String} 'attach', 'detach', 'fire', or 'publish' |
|
11664 * @param eventType {String|CustomEvent} The prefixed name of the event being monitored, or the CustomEvent object. |
|
11665 * @param o {Object} Information about the event interaction, such as |
|
11666 * fire() args, subscription category, publish config |
|
11667 * @private |
|
11668 */ |
|
11669 _monitor: function(what, eventType, o) { |
|
11670 var monitorevt, ce, type; |
|
11671 |
|
11672 if (eventType) { |
|
11673 if (typeof eventType === "string") { |
|
11674 type = eventType; |
|
11675 ce = this.getEvent(eventType, true); |
|
11676 } else { |
|
11677 ce = eventType; |
|
11678 type = eventType.type; |
|
11679 } |
|
11680 |
|
11681 if ((this._yuievt.config.monitored && (!ce || ce.monitored)) || (ce && ce.monitored)) { |
|
11682 monitorevt = type + '_' + what; |
|
11683 o.monitored = what; |
|
11684 this.fire.call(this, monitorevt, o); |
|
11685 } |
|
11686 } |
|
11687 }, |
|
11688 |
|
11689 /** |
|
11690 * Fire a custom event by name. The callback functions will be executed |
|
11691 * from the context specified when the event was created, and with the |
|
11692 * following parameters. |
|
11693 * |
|
11694 * If the custom event object hasn't been created, then the event hasn't |
|
11695 * been published and it has no subscribers. For performance sake, we |
|
11696 * immediate exit in this case. This means the event won't bubble, so |
|
11697 * if the intention is that a bubble target be notified, the event must |
|
11698 * be published on this object first. |
|
11699 * |
|
11700 * The first argument is the event type, and any additional arguments are |
|
11701 * passed to the listeners as parameters. If the first of these is an |
|
11702 * object literal, and the event is configured to emit an event facade, |
|
11703 * that object is mixed into the event facade and the facade is provided |
|
11704 * in place of the original object. |
|
11705 * |
|
11706 * @method fire |
|
11707 * @param type {String|Object} The type of the event, or an object that contains |
|
11708 * a 'type' property. |
|
11709 * @param arguments {Object*} an arbitrary set of parameters to pass to |
|
11710 * the handler. If the first of these is an object literal and the event is |
|
11711 * configured to emit an event facade, the event facade will replace that |
|
11712 * parameter after the properties the object literal contains are copied to |
|
11713 * the event facade. |
|
11714 * @return {EventTarget} the event host |
|
11715 */ |
|
11716 fire: function(type) { |
|
11717 |
|
11718 var typeIncluded = (typeof type === "string"), |
|
11719 argCount = arguments.length, |
|
11720 t = type, |
|
11721 yuievt = this._yuievt, |
|
11722 etConfig = yuievt.config, |
|
11723 pre = etConfig.prefix, |
|
11724 ret, |
|
11725 ce, |
|
11726 ce2, |
|
11727 args; |
|
11728 |
|
11729 if (typeIncluded && argCount <= 3) { |
|
11730 |
|
11731 // PERF: Try to avoid slice/iteration for the common signatures |
|
11732 |
|
11733 // Most common |
|
11734 if (argCount === 2) { |
|
11735 args = [arguments[1]]; // fire("foo", {}) |
|
11736 } else if (argCount === 3) { |
|
11737 args = [arguments[1], arguments[2]]; // fire("foo", {}, opts) |
|
11738 } else { |
|
11739 args = []; // fire("foo") |
|
11740 } |
|
11741 |
|
11742 } else { |
|
11743 args = nativeSlice.call(arguments, ((typeIncluded) ? 1 : 0)); |
|
11744 } |
|
11745 |
|
11746 if (!typeIncluded) { |
|
11747 t = (type && type.type); |
|
11748 } |
|
11749 |
|
11750 if (pre) { |
|
11751 t = _getType(t, pre); |
|
11752 } |
|
11753 |
|
11754 ce = yuievt.events[t]; |
|
11755 |
|
11756 if (this._hasSiblings) { |
|
11757 ce2 = this.getSibling(t, ce); |
|
11758 |
|
11759 if (ce2 && !ce) { |
|
11760 ce = this.publish(t); |
|
11761 } |
|
11762 } |
|
11763 |
|
11764 // PERF: trying to avoid function call, since this is a critical path |
|
11765 if ((etConfig.monitored && (!ce || ce.monitored)) || (ce && ce.monitored)) { |
|
11766 this._monitor('fire', (ce || t), { |
|
11767 args: args |
|
11768 }); |
|
11769 } |
|
11770 |
|
11771 // this event has not been published or subscribed to |
|
11772 if (!ce) { |
|
11773 if (yuievt.hasTargets) { |
|
11774 return this.bubble({ type: t }, args, this); |
|
11775 } |
|
11776 |
|
11777 // otherwise there is nothing to be done |
|
11778 ret = true; |
|
11779 } else { |
|
11780 |
|
11781 if (ce2) { |
|
11782 ce.sibling = ce2; |
|
11783 } |
|
11784 |
|
11785 ret = ce._fire(args); |
|
11786 } |
|
11787 |
|
11788 return (yuievt.chain) ? this : ret; |
|
11789 }, |
|
11790 |
|
11791 getSibling: function(type, ce) { |
|
11792 var ce2; |
|
11793 |
|
11794 // delegate to *:type events if there are subscribers |
|
11795 if (type.indexOf(PREFIX_DELIMITER) > -1) { |
|
11796 type = _wildType(type); |
|
11797 ce2 = this.getEvent(type, true); |
|
11798 if (ce2) { |
|
11799 ce2.applyConfig(ce); |
|
11800 ce2.bubbles = false; |
|
11801 ce2.broadcast = 0; |
|
11802 } |
|
11803 } |
|
11804 |
|
11805 return ce2; |
|
11806 }, |
|
11807 |
|
11808 /** |
|
11809 * Returns the custom event of the provided type has been created, a |
|
11810 * falsy value otherwise |
|
11811 * @method getEvent |
|
11812 * @param type {String} the type, or name of the event |
|
11813 * @param prefixed {String} if true, the type is prefixed already |
|
11814 * @return {CustomEvent} the custom event or null |
|
11815 */ |
|
11816 getEvent: function(type, prefixed) { |
|
11817 var pre, e; |
|
11818 |
|
11819 if (!prefixed) { |
|
11820 pre = this._yuievt.config.prefix; |
|
11821 type = (pre) ? _getType(type, pre) : type; |
|
11822 } |
|
11823 e = this._yuievt.events; |
|
11824 return e[type] || null; |
|
11825 }, |
|
11826 |
|
11827 /** |
|
11828 * Subscribe to a custom event hosted by this object. The |
|
11829 * supplied callback will execute after any listeners add |
|
11830 * via the subscribe method, and after the default function, |
|
11831 * if configured for the event, has executed. |
|
11832 * |
|
11833 * @method after |
|
11834 * @param {String} type The name of the event |
|
11835 * @param {Function} fn The callback to execute in response to the event |
|
11836 * @param {Object} [context] Override `this` object in callback |
|
11837 * @param {Any} [arg*] 0..n additional arguments to supply to the subscriber |
|
11838 * @return {EventHandle} A subscription handle capable of detaching the |
|
11839 * subscription |
|
11840 */ |
|
11841 after: function(type, fn) { |
|
11842 |
|
11843 var a = nativeSlice.call(arguments, 0); |
|
11844 |
|
11845 switch (L.type(type)) { |
|
11846 case 'function': |
|
11847 return Y.Do.after.apply(Y.Do, arguments); |
|
11848 case 'array': |
|
11849 // YArray.each(a[0], function(v) { |
|
11850 // v = AFTER_PREFIX + v; |
|
11851 // }); |
|
11852 // break; |
|
11853 case 'object': |
|
11854 a[0]._after = true; |
|
11855 break; |
|
11856 default: |
|
11857 a[0] = AFTER_PREFIX + type; |
|
11858 } |
|
11859 |
|
11860 return this.on.apply(this, a); |
|
11861 |
|
11862 }, |
|
11863 |
|
11864 /** |
|
11865 * Executes the callback before a DOM event, custom event |
|
11866 * or method. If the first argument is a function, it |
|
11867 * is assumed the target is a method. For DOM and custom |
|
11868 * events, this is an alias for Y.on. |
|
11869 * |
|
11870 * For DOM and custom events: |
|
11871 * type, callback, context, 0-n arguments |
|
11872 * |
|
11873 * For methods: |
|
11874 * callback, object (method host), methodName, context, 0-n arguments |
|
11875 * |
|
11876 * @method before |
|
11877 * @return detach handle |
|
11878 */ |
|
11879 before: function() { |
|
11880 return this.on.apply(this, arguments); |
|
11881 } |
|
11882 |
|
11883 }; |
|
11884 |
|
11885 Y.EventTarget = ET; |
|
11886 |
|
11887 // make Y an event target |
|
11888 Y.mix(Y, ET.prototype); |
|
11889 ET.call(Y, { bubbles: false }); |
|
11890 |
|
11891 YUI.Env.globalEvents = YUI.Env.globalEvents || new ET(); |
|
11892 |
|
11893 /** |
|
11894 * Hosts YUI page level events. This is where events bubble to |
|
11895 * when the broadcast config is set to 2. This property is |
|
11896 * only available if the custom event module is loaded. |
|
11897 * @property Global |
|
11898 * @type EventTarget |
|
11899 * @for YUI |
|
11900 */ |
|
11901 Y.Global = YUI.Env.globalEvents; |
|
11902 |
|
11903 // @TODO implement a global namespace function on Y.Global? |
|
11904 |
|
11905 /** |
|
11906 `Y.on()` can do many things: |
|
11907 |
|
11908 <ul> |
|
11909 <li>Subscribe to custom events `publish`ed and `fire`d from Y</li> |
|
11910 <li>Subscribe to custom events `publish`ed with `broadcast` 1 or 2 and |
|
11911 `fire`d from any object in the YUI instance sandbox</li> |
|
11912 <li>Subscribe to DOM events</li> |
|
11913 <li>Subscribe to the execution of a method on any object, effectively |
|
11914 treating that method as an event</li> |
|
11915 </ul> |
|
11916 |
|
11917 For custom event subscriptions, pass the custom event name as the first argument |
|
11918 and callback as the second. The `this` object in the callback will be `Y` unless |
|
11919 an override is passed as the third argument. |
|
11920 |
|
11921 Y.on('io:complete', function () { |
|
11922 Y.MyApp.updateStatus('Transaction complete'); |
|
11923 }); |
|
11924 |
|
11925 To subscribe to DOM events, pass the name of a DOM event as the first argument |
|
11926 and a CSS selector string as the third argument after the callback function. |
|
11927 Alternately, the third argument can be a `Node`, `NodeList`, `HTMLElement`, |
|
11928 array, or simply omitted (the default is the `window` object). |
|
11929 |
|
11930 Y.on('click', function (e) { |
|
11931 e.preventDefault(); |
|
11932 |
|
11933 // proceed with ajax form submission |
|
11934 var url = this.get('action'); |
|
11935 ... |
|
11936 }, '#my-form'); |
|
11937 |
|
11938 The `this` object in DOM event callbacks will be the `Node` targeted by the CSS |
|
11939 selector or other identifier. |
|
11940 |
|
11941 `on()` subscribers for DOM events or custom events `publish`ed with a |
|
11942 `defaultFn` can prevent the default behavior with `e.preventDefault()` from the |
|
11943 event object passed as the first parameter to the subscription callback. |
|
11944 |
|
11945 To subscribe to the execution of an object method, pass arguments corresponding to the call signature for |
|
11946 <a href="../classes/Do.html#methods_before">`Y.Do.before(...)`</a>. |
|
11947 |
|
11948 NOTE: The formal parameter list below is for events, not for function |
|
11949 injection. See `Y.Do.before` for that signature. |
|
11950 |
|
11951 @method on |
|
11952 @param {String} type DOM or custom event name |
|
11953 @param {Function} fn The callback to execute in response to the event |
|
11954 @param {Object} [context] Override `this` object in callback |
|
11955 @param {Any} [arg*] 0..n additional arguments to supply to the subscriber |
|
11956 @return {EventHandle} A subscription handle capable of detaching the |
|
11957 subscription |
|
11958 @see Do.before |
|
11959 @for YUI |
|
11960 **/ |
|
11961 |
|
11962 /** |
|
11963 Listen for an event one time. Equivalent to `on()`, except that |
|
11964 the listener is immediately detached when executed. |
|
11965 |
|
11966 See the <a href="#methods_on">`on()` method</a> for additional subscription |
|
11967 options. |
|
11968 |
|
11969 @see on |
|
11970 @method once |
|
11971 @param {String} type DOM or custom event name |
|
11972 @param {Function} fn The callback to execute in response to the event |
|
11973 @param {Object} [context] Override `this` object in callback |
|
11974 @param {Any} [arg*] 0..n additional arguments to supply to the subscriber |
|
11975 @return {EventHandle} A subscription handle capable of detaching the |
|
11976 subscription |
|
11977 @for YUI |
|
11978 **/ |
|
11979 |
|
11980 /** |
|
11981 Listen for an event one time. Equivalent to `once()`, except, like `after()`, |
|
11982 the subscription callback executes after all `on()` subscribers and the event's |
|
11983 `defaultFn` (if configured) have executed. Like `after()` if any `on()` phase |
|
11984 subscriber calls `e.preventDefault()`, neither the `defaultFn` nor the `after()` |
|
11985 subscribers will execute. |
|
11986 |
|
11987 The listener is immediately detached when executed. |
|
11988 |
|
11989 See the <a href="#methods_on">`on()` method</a> for additional subscription |
|
11990 options. |
|
11991 |
|
11992 @see once |
|
11993 @method onceAfter |
|
11994 @param {String} type The custom event name |
|
11995 @param {Function} fn The callback to execute in response to the event |
|
11996 @param {Object} [context] Override `this` object in callback |
|
11997 @param {Any} [arg*] 0..n additional arguments to supply to the subscriber |
|
11998 @return {EventHandle} A subscription handle capable of detaching the |
|
11999 subscription |
|
12000 @for YUI |
|
12001 **/ |
|
12002 |
|
12003 /** |
|
12004 Like `on()`, this method creates a subscription to a custom event or to the |
|
12005 execution of a method on an object. |
|
12006 |
|
12007 For events, `after()` subscribers are executed after the event's |
|
12008 `defaultFn` unless `e.preventDefault()` was called from an `on()` subscriber. |
|
12009 |
|
12010 See the <a href="#methods_on">`on()` method</a> for additional subscription |
|
12011 options. |
|
12012 |
|
12013 NOTE: The subscription signature shown is for events, not for function |
|
12014 injection. See <a href="../classes/Do.html#methods_after">`Y.Do.after`</a> |
|
12015 for that signature. |
|
12016 |
|
12017 @see on |
|
12018 @see Do.after |
|
12019 @method after |
|
12020 @param {String} type The custom event name |
|
12021 @param {Function} fn The callback to execute in response to the event |
|
12022 @param {Object} [context] Override `this` object in callback |
|
12023 @param {Any} [args*] 0..n additional arguments to supply to the subscriber |
|
12024 @return {EventHandle} A subscription handle capable of detaching the |
|
12025 subscription |
|
12026 @for YUI |
|
12027 **/ |
|
12028 |
|
12029 |
|
12030 }, '@VERSION@', {"requires": ["oop"]}); |
|
12031 YUI.add('event-custom-complex', function (Y, NAME) { |
|
12032 |
|
12033 |
|
12034 /** |
|
12035 * Adds event facades, preventable default behavior, and bubbling. |
|
12036 * events. |
|
12037 * @module event-custom |
|
12038 * @submodule event-custom-complex |
|
12039 */ |
|
12040 |
|
12041 var FACADE, |
|
12042 FACADE_KEYS, |
|
12043 YObject = Y.Object, |
|
12044 key, |
|
12045 EMPTY = {}, |
|
12046 CEProto = Y.CustomEvent.prototype, |
|
12047 ETProto = Y.EventTarget.prototype, |
|
12048 |
|
12049 mixFacadeProps = function(facade, payload) { |
|
12050 var p; |
|
12051 |
|
12052 for (p in payload) { |
|
12053 if (!(FACADE_KEYS.hasOwnProperty(p))) { |
|
12054 facade[p] = payload[p]; |
|
12055 } |
|
12056 } |
|
12057 }; |
|
12058 |
|
12059 /** |
|
12060 * Wraps and protects a custom event for use when emitFacade is set to true. |
|
12061 * Requires the event-custom-complex module |
|
12062 * @class EventFacade |
|
12063 * @param e {Event} the custom event |
|
12064 * @param currentTarget {HTMLElement} the element the listener was attached to |
|
12065 */ |
|
12066 |
|
12067 Y.EventFacade = function(e, currentTarget) { |
|
12068 |
|
12069 if (!e) { |
|
12070 e = EMPTY; |
|
12071 } |
|
12072 |
|
12073 this._event = e; |
|
12074 |
|
12075 /** |
|
12076 * The arguments passed to fire |
|
12077 * @property details |
|
12078 * @type Array |
|
12079 */ |
|
12080 this.details = e.details; |
|
12081 |
|
12082 /** |
|
12083 * The event type, this can be overridden by the fire() payload |
|
12084 * @property type |
|
12085 * @type string |
|
12086 */ |
|
12087 this.type = e.type; |
|
12088 |
|
12089 /** |
|
12090 * The real event type |
|
12091 * @property _type |
|
12092 * @type string |
|
12093 * @private |
|
12094 */ |
|
12095 this._type = e.type; |
|
12096 |
|
12097 ////////////////////////////////////////////////////// |
|
12098 |
|
12099 /** |
|
12100 * Node reference for the targeted eventtarget |
|
12101 * @property target |
|
12102 * @type Node |
|
12103 */ |
|
12104 this.target = e.target; |
|
12105 |
|
12106 /** |
|
12107 * Node reference for the element that the listener was attached to. |
|
12108 * @property currentTarget |
|
12109 * @type Node |
|
12110 */ |
|
12111 this.currentTarget = currentTarget; |
|
12112 |
|
12113 /** |
|
12114 * Node reference to the relatedTarget |
|
12115 * @property relatedTarget |
|
12116 * @type Node |
|
12117 */ |
|
12118 this.relatedTarget = e.relatedTarget; |
|
12119 |
|
12120 }; |
|
12121 |
|
12122 Y.mix(Y.EventFacade.prototype, { |
|
12123 |
|
12124 /** |
|
12125 * Stops the propagation to the next bubble target |
|
12126 * @method stopPropagation |
|
12127 */ |
|
12128 stopPropagation: function() { |
|
12129 this._event.stopPropagation(); |
|
12130 this.stopped = 1; |
|
12131 }, |
|
12132 |
|
12133 /** |
|
12134 * Stops the propagation to the next bubble target and |
|
12135 * prevents any additional listeners from being exectued |
|
12136 * on the current target. |
|
12137 * @method stopImmediatePropagation |
|
12138 */ |
|
12139 stopImmediatePropagation: function() { |
|
12140 this._event.stopImmediatePropagation(); |
|
12141 this.stopped = 2; |
|
12142 }, |
|
12143 |
|
12144 /** |
|
12145 * Prevents the event's default behavior |
|
12146 * @method preventDefault |
|
12147 */ |
|
12148 preventDefault: function() { |
|
12149 this._event.preventDefault(); |
|
12150 this.prevented = 1; |
|
12151 }, |
|
12152 |
|
12153 /** |
|
12154 * Stops the event propagation and prevents the default |
|
12155 * event behavior. |
|
12156 * @method halt |
|
12157 * @param immediate {boolean} if true additional listeners |
|
12158 * on the current target will not be executed |
|
12159 */ |
|
12160 halt: function(immediate) { |
|
12161 this._event.halt(immediate); |
|
12162 this.prevented = 1; |
|
12163 this.stopped = (immediate) ? 2 : 1; |
|
12164 } |
|
12165 |
|
12166 }); |
|
12167 |
|
12168 CEProto.fireComplex = function(args) { |
|
12169 |
|
12170 var es, |
|
12171 ef, |
|
12172 q, |
|
12173 queue, |
|
12174 ce, |
|
12175 ret = true, |
|
12176 events, |
|
12177 subs, |
|
12178 ons, |
|
12179 afters, |
|
12180 afterQueue, |
|
12181 postponed, |
|
12182 prevented, |
|
12183 preventedFn, |
|
12184 defaultFn, |
|
12185 self = this, |
|
12186 host = self.host || self, |
|
12187 next, |
|
12188 oldbubble, |
|
12189 stack, |
|
12190 yuievt = host._yuievt, |
|
12191 hasPotentialSubscribers; |
|
12192 |
|
12193 stack = self.stack; |
|
12194 |
|
12195 if (stack) { |
|
12196 |
|
12197 // queue this event if the current item in the queue bubbles |
|
12198 if (self.queuable && self.type !== stack.next.type) { |
|
12199 self.log('queue ' + self.type); |
|
12200 |
|
12201 if (!stack.queue) { |
|
12202 stack.queue = []; |
|
12203 } |
|
12204 stack.queue.push([self, args]); |
|
12205 |
|
12206 return true; |
|
12207 } |
|
12208 } |
|
12209 |
|
12210 hasPotentialSubscribers = self.hasSubs() || yuievt.hasTargets || self.broadcast; |
|
12211 |
|
12212 self.target = self.target || host; |
|
12213 self.currentTarget = host; |
|
12214 |
|
12215 self.details = args.concat(); |
|
12216 |
|
12217 if (hasPotentialSubscribers) { |
|
12218 |
|
12219 es = stack || { |
|
12220 |
|
12221 id: self.id, // id of the first event in the stack |
|
12222 next: self, |
|
12223 silent: self.silent, |
|
12224 stopped: 0, |
|
12225 prevented: 0, |
|
12226 bubbling: null, |
|
12227 type: self.type, |
|
12228 // defaultFnQueue: new Y.Queue(), |
|
12229 defaultTargetOnly: self.defaultTargetOnly |
|
12230 |
|
12231 }; |
|
12232 |
|
12233 subs = self.getSubs(); |
|
12234 ons = subs[0]; |
|
12235 afters = subs[1]; |
|
12236 |
|
12237 self.stopped = (self.type !== es.type) ? 0 : es.stopped; |
|
12238 self.prevented = (self.type !== es.type) ? 0 : es.prevented; |
|
12239 |
|
12240 if (self.stoppedFn) { |
|
12241 // PERF TODO: Can we replace with callback, like preventedFn. Look into history |
|
12242 events = new Y.EventTarget({ |
|
12243 fireOnce: true, |
|
12244 context: host |
|
12245 }); |
|
12246 self.events = events; |
|
12247 events.on('stopped', self.stoppedFn); |
|
12248 } |
|
12249 |
|
12250 // self.log("Firing " + self + ", " + "args: " + args); |
|
12251 self.log("Firing " + self.type); |
|
12252 |
|
12253 self._facade = null; // kill facade to eliminate stale properties |
|
12254 |
|
12255 ef = self._getFacade(args); |
|
12256 |
|
12257 if (ons) { |
|
12258 self._procSubs(ons, args, ef); |
|
12259 } |
|
12260 |
|
12261 // bubble if this is hosted in an event target and propagation has not been stopped |
|
12262 if (self.bubbles && host.bubble && !self.stopped) { |
|
12263 oldbubble = es.bubbling; |
|
12264 |
|
12265 es.bubbling = self.type; |
|
12266 |
|
12267 if (es.type !== self.type) { |
|
12268 es.stopped = 0; |
|
12269 es.prevented = 0; |
|
12270 } |
|
12271 |
|
12272 ret = host.bubble(self, args, null, es); |
|
12273 |
|
12274 self.stopped = Math.max(self.stopped, es.stopped); |
|
12275 self.prevented = Math.max(self.prevented, es.prevented); |
|
12276 |
|
12277 es.bubbling = oldbubble; |
|
12278 } |
|
12279 |
|
12280 prevented = self.prevented; |
|
12281 |
|
12282 if (prevented) { |
|
12283 preventedFn = self.preventedFn; |
|
12284 if (preventedFn) { |
|
12285 preventedFn.apply(host, args); |
|
12286 } |
|
12287 } else { |
|
12288 defaultFn = self.defaultFn; |
|
12289 |
|
12290 if (defaultFn && ((!self.defaultTargetOnly && !es.defaultTargetOnly) || host === ef.target)) { |
|
12291 defaultFn.apply(host, args); |
|
12292 } |
|
12293 } |
|
12294 |
|
12295 // broadcast listeners are fired as discreet events on the |
|
12296 // YUI instance and potentially the YUI global. |
|
12297 if (self.broadcast) { |
|
12298 self._broadcast(args); |
|
12299 } |
|
12300 |
|
12301 if (afters && !self.prevented && self.stopped < 2) { |
|
12302 |
|
12303 // Queue the after |
|
12304 afterQueue = es.afterQueue; |
|
12305 |
|
12306 if (es.id === self.id || self.type !== yuievt.bubbling) { |
|
12307 |
|
12308 self._procSubs(afters, args, ef); |
|
12309 |
|
12310 if (afterQueue) { |
|
12311 while ((next = afterQueue.last())) { |
|
12312 next(); |
|
12313 } |
|
12314 } |
|
12315 } else { |
|
12316 postponed = afters; |
|
12317 |
|
12318 if (es.execDefaultCnt) { |
|
12319 postponed = Y.merge(postponed); |
|
12320 |
|
12321 Y.each(postponed, function(s) { |
|
12322 s.postponed = true; |
|
12323 }); |
|
12324 } |
|
12325 |
|
12326 if (!afterQueue) { |
|
12327 es.afterQueue = new Y.Queue(); |
|
12328 } |
|
12329 |
|
12330 es.afterQueue.add(function() { |
|
12331 self._procSubs(postponed, args, ef); |
|
12332 }); |
|
12333 } |
|
12334 |
|
12335 } |
|
12336 |
|
12337 self.target = null; |
|
12338 |
|
12339 if (es.id === self.id) { |
|
12340 |
|
12341 queue = es.queue; |
|
12342 |
|
12343 if (queue) { |
|
12344 while (queue.length) { |
|
12345 q = queue.pop(); |
|
12346 ce = q[0]; |
|
12347 // set up stack to allow the next item to be processed |
|
12348 es.next = ce; |
|
12349 ce._fire(q[1]); |
|
12350 } |
|
12351 } |
|
12352 |
|
12353 self.stack = null; |
|
12354 } |
|
12355 |
|
12356 ret = !(self.stopped); |
|
12357 |
|
12358 if (self.type !== yuievt.bubbling) { |
|
12359 es.stopped = 0; |
|
12360 es.prevented = 0; |
|
12361 self.stopped = 0; |
|
12362 self.prevented = 0; |
|
12363 } |
|
12364 |
|
12365 } else { |
|
12366 defaultFn = self.defaultFn; |
|
12367 |
|
12368 if(defaultFn) { |
|
12369 ef = self._getFacade(args); |
|
12370 |
|
12371 if ((!self.defaultTargetOnly) || (host === ef.target)) { |
|
12372 defaultFn.apply(host, args); |
|
12373 } |
|
12374 } |
|
12375 } |
|
12376 |
|
12377 // Kill the cached facade to free up memory. |
|
12378 // Otherwise we have the facade from the last fire, sitting around forever. |
|
12379 self._facade = null; |
|
12380 |
|
12381 return ret; |
|
12382 }; |
|
12383 |
|
12384 CEProto._getFacade = function(fireArgs) { |
|
12385 |
|
12386 var userArgs = this.details, |
|
12387 firstArg = userArgs && userArgs[0], |
|
12388 firstArgIsObj = (firstArg && (typeof firstArg === "object")), |
|
12389 ef = this._facade; |
|
12390 |
|
12391 if (!ef) { |
|
12392 ef = new Y.EventFacade(this, this.currentTarget); |
|
12393 } |
|
12394 |
|
12395 if (firstArgIsObj) { |
|
12396 // protect the event facade properties |
|
12397 mixFacadeProps(ef, firstArg); |
|
12398 |
|
12399 // Allow the event type to be faked http://yuilibrary.com/projects/yui3/ticket/2528376 |
|
12400 if (firstArg.type) { |
|
12401 ef.type = firstArg.type; |
|
12402 } |
|
12403 |
|
12404 if (fireArgs) { |
|
12405 fireArgs[0] = ef; |
|
12406 } |
|
12407 } else { |
|
12408 if (fireArgs) { |
|
12409 fireArgs.unshift(ef); |
|
12410 } |
|
12411 } |
|
12412 |
|
12413 // update the details field with the arguments |
|
12414 ef.details = this.details; |
|
12415 |
|
12416 // use the original target when the event bubbled to this target |
|
12417 ef.target = this.originalTarget || this.target; |
|
12418 |
|
12419 ef.currentTarget = this.currentTarget; |
|
12420 ef.stopped = 0; |
|
12421 ef.prevented = 0; |
|
12422 |
|
12423 this._facade = ef; |
|
12424 |
|
12425 return this._facade; |
|
12426 }; |
|
12427 |
|
12428 /** |
|
12429 * Stop propagation to bubble targets |
|
12430 * @for CustomEvent |
|
12431 * @method stopPropagation |
|
12432 */ |
|
12433 CEProto.stopPropagation = function() { |
|
12434 this.stopped = 1; |
|
12435 if (this.stack) { |
|
12436 this.stack.stopped = 1; |
|
12437 } |
|
12438 if (this.events) { |
|
12439 this.events.fire('stopped', this); |
|
12440 } |
|
12441 }; |
|
12442 |
|
12443 /** |
|
12444 * Stops propagation to bubble targets, and prevents any remaining |
|
12445 * subscribers on the current target from executing. |
|
12446 * @method stopImmediatePropagation |
|
12447 */ |
|
12448 CEProto.stopImmediatePropagation = function() { |
|
12449 this.stopped = 2; |
|
12450 if (this.stack) { |
|
12451 this.stack.stopped = 2; |
|
12452 } |
|
12453 if (this.events) { |
|
12454 this.events.fire('stopped', this); |
|
12455 } |
|
12456 }; |
|
12457 |
|
12458 /** |
|
12459 * Prevents the execution of this event's defaultFn |
|
12460 * @method preventDefault |
|
12461 */ |
|
12462 CEProto.preventDefault = function() { |
|
12463 if (this.preventable) { |
|
12464 this.prevented = 1; |
|
12465 if (this.stack) { |
|
12466 this.stack.prevented = 1; |
|
12467 } |
|
12468 } |
|
12469 }; |
|
12470 |
|
12471 /** |
|
12472 * Stops the event propagation and prevents the default |
|
12473 * event behavior. |
|
12474 * @method halt |
|
12475 * @param immediate {boolean} if true additional listeners |
|
12476 * on the current target will not be executed |
|
12477 */ |
|
12478 CEProto.halt = function(immediate) { |
|
12479 if (immediate) { |
|
12480 this.stopImmediatePropagation(); |
|
12481 } else { |
|
12482 this.stopPropagation(); |
|
12483 } |
|
12484 this.preventDefault(); |
|
12485 }; |
|
12486 |
|
12487 /** |
|
12488 * Registers another EventTarget as a bubble target. Bubble order |
|
12489 * is determined by the order registered. Multiple targets can |
|
12490 * be specified. |
|
12491 * |
|
12492 * Events can only bubble if emitFacade is true. |
|
12493 * |
|
12494 * Included in the event-custom-complex submodule. |
|
12495 * |
|
12496 * @method addTarget |
|
12497 * @param o {EventTarget} the target to add |
|
12498 * @for EventTarget |
|
12499 */ |
|
12500 ETProto.addTarget = function(o) { |
|
12501 var etState = this._yuievt; |
|
12502 |
|
12503 if (!etState.targets) { |
|
12504 etState.targets = {}; |
|
12505 } |
|
12506 |
|
12507 etState.targets[Y.stamp(o)] = o; |
|
12508 etState.hasTargets = true; |
|
12509 }; |
|
12510 |
|
12511 /** |
|
12512 * Returns an array of bubble targets for this object. |
|
12513 * @method getTargets |
|
12514 * @return EventTarget[] |
|
12515 */ |
|
12516 ETProto.getTargets = function() { |
|
12517 var targets = this._yuievt.targets; |
|
12518 return targets ? YObject.values(targets) : []; |
|
12519 }; |
|
12520 |
|
12521 /** |
|
12522 * Removes a bubble target |
|
12523 * @method removeTarget |
|
12524 * @param o {EventTarget} the target to remove |
|
12525 * @for EventTarget |
|
12526 */ |
|
12527 ETProto.removeTarget = function(o) { |
|
12528 var targets = this._yuievt.targets; |
|
12529 |
|
12530 if (targets) { |
|
12531 delete targets[Y.stamp(o, true)]; |
|
12532 |
|
12533 if (YObject.size(targets) === 0) { |
|
12534 this._yuievt.hasTargets = false; |
|
12535 } |
|
12536 } |
|
12537 }; |
|
12538 |
|
12539 /** |
|
12540 * Propagate an event. Requires the event-custom-complex module. |
|
12541 * @method bubble |
|
12542 * @param evt {CustomEvent} the custom event to propagate |
|
12543 * @return {boolean} the aggregated return value from Event.Custom.fire |
|
12544 * @for EventTarget |
|
12545 */ |
|
12546 ETProto.bubble = function(evt, args, target, es) { |
|
12547 |
|
12548 var targs = this._yuievt.targets, |
|
12549 ret = true, |
|
12550 t, |
|
12551 ce, |
|
12552 i, |
|
12553 bc, |
|
12554 ce2, |
|
12555 type = evt && evt.type, |
|
12556 originalTarget = target || (evt && evt.target) || this, |
|
12557 oldbubble; |
|
12558 |
|
12559 if (!evt || ((!evt.stopped) && targs)) { |
|
12560 |
|
12561 for (i in targs) { |
|
12562 if (targs.hasOwnProperty(i)) { |
|
12563 |
|
12564 t = targs[i]; |
|
12565 |
|
12566 ce = t._yuievt.events[type]; |
|
12567 |
|
12568 if (t._hasSiblings) { |
|
12569 ce2 = t.getSibling(type, ce); |
|
12570 } |
|
12571 |
|
12572 if (ce2 && !ce) { |
|
12573 ce = t.publish(type); |
|
12574 } |
|
12575 |
|
12576 oldbubble = t._yuievt.bubbling; |
|
12577 t._yuievt.bubbling = type; |
|
12578 |
|
12579 // if this event was not published on the bubble target, |
|
12580 // continue propagating the event. |
|
12581 if (!ce) { |
|
12582 if (t._yuievt.hasTargets) { |
|
12583 t.bubble(evt, args, originalTarget, es); |
|
12584 } |
|
12585 } else { |
|
12586 |
|
12587 if (ce2) { |
|
12588 ce.sibling = ce2; |
|
12589 } |
|
12590 |
|
12591 // set the original target to that the target payload on the facade is correct. |
|
12592 ce.target = originalTarget; |
|
12593 ce.originalTarget = originalTarget; |
|
12594 ce.currentTarget = t; |
|
12595 bc = ce.broadcast; |
|
12596 ce.broadcast = false; |
|
12597 |
|
12598 // default publish may not have emitFacade true -- that |
|
12599 // shouldn't be what the implementer meant to do |
|
12600 ce.emitFacade = true; |
|
12601 |
|
12602 ce.stack = es; |
|
12603 |
|
12604 // TODO: See what's getting in the way of changing this to use |
|
12605 // the more performant ce._fire(args || evt.details || []). |
|
12606 |
|
12607 // Something in Widget Parent/Child tests is not happy if we |
|
12608 // change it - maybe evt.details related? |
|
12609 ret = ret && ce.fire.apply(ce, args || evt.details || []); |
|
12610 |
|
12611 ce.broadcast = bc; |
|
12612 ce.originalTarget = null; |
|
12613 |
|
12614 // stopPropagation() was called |
|
12615 if (ce.stopped) { |
|
12616 break; |
|
12617 } |
|
12618 } |
|
12619 |
|
12620 t._yuievt.bubbling = oldbubble; |
|
12621 } |
|
12622 } |
|
12623 } |
|
12624 |
|
12625 return ret; |
|
12626 }; |
|
12627 |
|
12628 FACADE = new Y.EventFacade(); |
|
12629 FACADE_KEYS = {}; |
|
12630 |
|
12631 // Flatten whitelist |
|
12632 for (key in FACADE) { |
|
12633 FACADE_KEYS[key] = true; |
|
12634 } |
|
12635 |
|
12636 |
|
12637 }, '@VERSION@', {"requires": ["event-custom-base"]}); |
|
12638 YUI.add('node-core', function (Y, NAME) { |
|
12639 |
|
12640 /** |
|
12641 * The Node Utility provides a DOM-like interface for interacting with DOM nodes. |
|
12642 * @module node |
|
12643 * @main node |
|
12644 * @submodule node-core |
|
12645 */ |
|
12646 |
|
12647 /** |
|
12648 * The Node class provides a wrapper for manipulating DOM Nodes. |
|
12649 * Node properties can be accessed via the set/get methods. |
|
12650 * Use `Y.one()` to retrieve Node instances. |
|
12651 * |
|
12652 * <strong>NOTE:</strong> Node properties are accessed using |
|
12653 * the <code>set</code> and <code>get</code> methods. |
|
12654 * |
|
12655 * @class Node |
|
12656 * @constructor |
|
12657 * @param {DOMNode} node the DOM node to be mapped to the Node instance. |
|
12658 * @uses EventTarget |
|
12659 */ |
|
12660 |
|
12661 // "globals" |
|
12662 var DOT = '.', |
|
12663 NODE_NAME = 'nodeName', |
|
12664 NODE_TYPE = 'nodeType', |
|
12665 OWNER_DOCUMENT = 'ownerDocument', |
|
12666 TAG_NAME = 'tagName', |
|
12667 UID = '_yuid', |
|
12668 EMPTY_OBJ = {}, |
|
12669 |
|
12670 _slice = Array.prototype.slice, |
|
12671 |
|
12672 Y_DOM = Y.DOM, |
|
12673 |
|
12674 Y_Node = function(node) { |
|
12675 if (!this.getDOMNode) { // support optional "new" |
|
12676 return new Y_Node(node); |
|
12677 } |
|
12678 |
|
12679 if (typeof node == 'string') { |
|
12680 node = Y_Node._fromString(node); |
|
12681 if (!node) { |
|
12682 return null; // NOTE: return |
|
12683 } |
|
12684 } |
|
12685 |
|
12686 var uid = (node.nodeType !== 9) ? node.uniqueID : node[UID]; |
|
12687 |
|
12688 if (uid && Y_Node._instances[uid] && Y_Node._instances[uid]._node !== node) { |
|
12689 node[UID] = null; // unset existing uid to prevent collision (via clone or hack) |
|
12690 } |
|
12691 |
|
12692 uid = uid || Y.stamp(node); |
|
12693 if (!uid) { // stamp failed; likely IE non-HTMLElement |
|
12694 uid = Y.guid(); |
|
12695 } |
|
12696 |
|
12697 this[UID] = uid; |
|
12698 |
|
12699 /** |
|
12700 * The underlying DOM node bound to the Y.Node instance |
|
12701 * @property _node |
|
12702 * @type DOMNode |
|
12703 * @private |
|
12704 */ |
|
12705 this._node = node; |
|
12706 |
|
12707 this._stateProxy = node; // when augmented with Attribute |
|
12708 |
|
12709 if (this._initPlugins) { // when augmented with Plugin.Host |
|
12710 this._initPlugins(); |
|
12711 } |
|
12712 }, |
|
12713 |
|
12714 // used with previous/next/ancestor tests |
|
12715 _wrapFn = function(fn) { |
|
12716 var ret = null; |
|
12717 if (fn) { |
|
12718 ret = (typeof fn == 'string') ? |
|
12719 function(n) { |
|
12720 return Y.Selector.test(n, fn); |
|
12721 } : |
|
12722 function(n) { |
|
12723 return fn(Y.one(n)); |
|
12724 }; |
|
12725 } |
|
12726 |
|
12727 return ret; |
|
12728 }; |
|
12729 // end "globals" |
|
12730 |
|
12731 Y_Node.ATTRS = {}; |
|
12732 Y_Node.DOM_EVENTS = {}; |
|
12733 |
|
12734 Y_Node._fromString = function(node) { |
|
12735 if (node) { |
|
12736 if (node.indexOf('doc') === 0) { // doc OR document |
|
12737 node = Y.config.doc; |
|
12738 } else if (node.indexOf('win') === 0) { // win OR window |
|
12739 node = Y.config.win; |
|
12740 } else { |
|
12741 node = Y.Selector.query(node, null, true); |
|
12742 } |
|
12743 } |
|
12744 |
|
12745 return node || null; |
|
12746 }; |
|
12747 |
|
12748 /** |
|
12749 * The name of the component |
|
12750 * @static |
|
12751 * @type String |
|
12752 * @property NAME |
|
12753 */ |
|
12754 Y_Node.NAME = 'node'; |
|
12755 |
|
12756 /* |
|
12757 * The pattern used to identify ARIA attributes |
|
12758 */ |
|
12759 Y_Node.re_aria = /^(?:role$|aria-)/; |
|
12760 |
|
12761 Y_Node.SHOW_TRANSITION = 'fadeIn'; |
|
12762 Y_Node.HIDE_TRANSITION = 'fadeOut'; |
|
12763 |
|
12764 /** |
|
12765 * A list of Node instances that have been created |
|
12766 * @private |
|
12767 * @type Object |
|
12768 * @property _instances |
|
12769 * @static |
|
12770 * |
|
12771 */ |
|
12772 Y_Node._instances = {}; |
|
12773 |
|
12774 /** |
|
12775 * Retrieves the DOM node bound to a Node instance |
|
12776 * @method getDOMNode |
|
12777 * @static |
|
12778 * |
|
12779 * @param {Node | HTMLNode} node The Node instance or an HTMLNode |
|
12780 * @return {HTMLNode} The DOM node bound to the Node instance. If a DOM node is passed |
|
12781 * as the node argument, it is simply returned. |
|
12782 */ |
|
12783 Y_Node.getDOMNode = function(node) { |
|
12784 if (node) { |
|
12785 return (node.nodeType) ? node : node._node || null; |
|
12786 } |
|
12787 return null; |
|
12788 }; |
|
12789 |
|
12790 /** |
|
12791 * Checks Node return values and wraps DOM Nodes as Y.Node instances |
|
12792 * and DOM Collections / Arrays as Y.NodeList instances. |
|
12793 * Other return values just pass thru. If undefined is returned (e.g. no return) |
|
12794 * then the Node instance is returned for chainability. |
|
12795 * @method scrubVal |
|
12796 * @static |
|
12797 * |
|
12798 * @param {any} node The Node instance or an HTMLNode |
|
12799 * @return {Node | NodeList | Any} Depends on what is returned from the DOM node. |
|
12800 */ |
|
12801 Y_Node.scrubVal = function(val, node) { |
|
12802 if (val) { // only truthy values are risky |
|
12803 if (typeof val == 'object' || typeof val == 'function') { // safari nodeList === function |
|
12804 if (NODE_TYPE in val || Y_DOM.isWindow(val)) {// node || window |
|
12805 val = Y.one(val); |
|
12806 } else if ((val.item && !val._nodes) || // dom collection or Node instance |
|
12807 (val[0] && val[0][NODE_TYPE])) { // array of DOM Nodes |
|
12808 val = Y.all(val); |
|
12809 } |
|
12810 } |
|
12811 } else if (typeof val === 'undefined') { |
|
12812 val = node; // for chaining |
|
12813 } else if (val === null) { |
|
12814 val = null; // IE: DOM null not the same as null |
|
12815 } |
|
12816 |
|
12817 return val; |
|
12818 }; |
|
12819 |
|
12820 /** |
|
12821 * Adds methods to the Y.Node prototype, routing through scrubVal. |
|
12822 * @method addMethod |
|
12823 * @static |
|
12824 * |
|
12825 * @param {String} name The name of the method to add |
|
12826 * @param {Function} fn The function that becomes the method |
|
12827 * @param {Object} context An optional context to call the method with |
|
12828 * (defaults to the Node instance) |
|
12829 * @return {any} Depends on what is returned from the DOM node. |
|
12830 */ |
|
12831 Y_Node.addMethod = function(name, fn, context) { |
|
12832 if (name && fn && typeof fn == 'function') { |
|
12833 Y_Node.prototype[name] = function() { |
|
12834 var args = _slice.call(arguments), |
|
12835 node = this, |
|
12836 ret; |
|
12837 |
|
12838 if (args[0] && args[0]._node) { |
|
12839 args[0] = args[0]._node; |
|
12840 } |
|
12841 |
|
12842 if (args[1] && args[1]._node) { |
|
12843 args[1] = args[1]._node; |
|
12844 } |
|
12845 args.unshift(node._node); |
|
12846 |
|
12847 ret = fn.apply(node, args); |
|
12848 |
|
12849 if (ret) { // scrub truthy |
|
12850 ret = Y_Node.scrubVal(ret, node); |
|
12851 } |
|
12852 |
|
12853 (typeof ret != 'undefined') || (ret = node); |
|
12854 return ret; |
|
12855 }; |
|
12856 } else { |
|
12857 } |
|
12858 }; |
|
12859 |
|
12860 /** |
|
12861 * Imports utility methods to be added as Y.Node methods. |
|
12862 * @method importMethod |
|
12863 * @static |
|
12864 * |
|
12865 * @param {Object} host The object that contains the method to import. |
|
12866 * @param {String} name The name of the method to import |
|
12867 * @param {String} altName An optional name to use in place of the host name |
|
12868 * @param {Object} context An optional context to call the method with |
|
12869 */ |
|
12870 Y_Node.importMethod = function(host, name, altName) { |
|
12871 if (typeof name == 'string') { |
|
12872 altName = altName || name; |
|
12873 Y_Node.addMethod(altName, host[name], host); |
|
12874 } else { |
|
12875 Y.Array.each(name, function(n) { |
|
12876 Y_Node.importMethod(host, n); |
|
12877 }); |
|
12878 } |
|
12879 }; |
|
12880 |
|
12881 /** |
|
12882 * Retrieves a NodeList based on the given CSS selector. |
|
12883 * @method all |
|
12884 * |
|
12885 * @param {string} selector The CSS selector to test against. |
|
12886 * @return {NodeList} A NodeList instance for the matching HTMLCollection/Array. |
|
12887 * @for YUI |
|
12888 */ |
|
12889 |
|
12890 /** |
|
12891 * Returns a single Node instance bound to the node or the |
|
12892 * first element matching the given selector. Returns null if no match found. |
|
12893 * <strong>Note:</strong> For chaining purposes you may want to |
|
12894 * use <code>Y.all</code>, which returns a NodeList when no match is found. |
|
12895 * @method one |
|
12896 * @param {String | HTMLElement} node a node or Selector |
|
12897 * @return {Node | null} a Node instance or null if no match found. |
|
12898 * @for YUI |
|
12899 */ |
|
12900 |
|
12901 /** |
|
12902 * Returns a single Node instance bound to the node or the |
|
12903 * first element matching the given selector. Returns null if no match found. |
|
12904 * <strong>Note:</strong> For chaining purposes you may want to |
|
12905 * use <code>Y.all</code>, which returns a NodeList when no match is found. |
|
12906 * @method one |
|
12907 * @static |
|
12908 * @param {String | HTMLElement} node a node or Selector |
|
12909 * @return {Node | null} a Node instance or null if no match found. |
|
12910 * @for Node |
|
12911 */ |
|
12912 Y_Node.one = function(node) { |
|
12913 var instance = null, |
|
12914 cachedNode, |
|
12915 uid; |
|
12916 |
|
12917 if (node) { |
|
12918 if (typeof node == 'string') { |
|
12919 node = Y_Node._fromString(node); |
|
12920 if (!node) { |
|
12921 return null; // NOTE: return |
|
12922 } |
|
12923 } else if (node.getDOMNode) { |
|
12924 return node; // NOTE: return |
|
12925 } |
|
12926 |
|
12927 if (node.nodeType || Y.DOM.isWindow(node)) { // avoid bad input (numbers, boolean, etc) |
|
12928 uid = (node.uniqueID && node.nodeType !== 9) ? node.uniqueID : node._yuid; |
|
12929 instance = Y_Node._instances[uid]; // reuse exising instances |
|
12930 cachedNode = instance ? instance._node : null; |
|
12931 if (!instance || (cachedNode && node !== cachedNode)) { // new Node when nodes don't match |
|
12932 instance = new Y_Node(node); |
|
12933 if (node.nodeType != 11) { // dont cache document fragment |
|
12934 Y_Node._instances[instance[UID]] = instance; // cache node |
|
12935 } |
|
12936 } |
|
12937 } |
|
12938 } |
|
12939 |
|
12940 return instance; |
|
12941 }; |
|
12942 |
|
12943 /** |
|
12944 * The default setter for DOM properties |
|
12945 * Called with instance context (this === the Node instance) |
|
12946 * @method DEFAULT_SETTER |
|
12947 * @static |
|
12948 * @param {String} name The attribute/property being set |
|
12949 * @param {any} val The value to be set |
|
12950 * @return {any} The value |
|
12951 */ |
|
12952 Y_Node.DEFAULT_SETTER = function(name, val) { |
|
12953 var node = this._stateProxy, |
|
12954 strPath; |
|
12955 |
|
12956 if (name.indexOf(DOT) > -1) { |
|
12957 strPath = name; |
|
12958 name = name.split(DOT); |
|
12959 // only allow when defined on node |
|
12960 Y.Object.setValue(node, name, val); |
|
12961 } else if (typeof node[name] != 'undefined') { // pass thru DOM properties |
|
12962 node[name] = val; |
|
12963 } |
|
12964 |
|
12965 return val; |
|
12966 }; |
|
12967 |
|
12968 /** |
|
12969 * The default getter for DOM properties |
|
12970 * Called with instance context (this === the Node instance) |
|
12971 * @method DEFAULT_GETTER |
|
12972 * @static |
|
12973 * @param {String} name The attribute/property to look up |
|
12974 * @return {any} The current value |
|
12975 */ |
|
12976 Y_Node.DEFAULT_GETTER = function(name) { |
|
12977 var node = this._stateProxy, |
|
12978 val; |
|
12979 |
|
12980 if (name.indexOf && name.indexOf(DOT) > -1) { |
|
12981 val = Y.Object.getValue(node, name.split(DOT)); |
|
12982 } else if (typeof node[name] != 'undefined') { // pass thru from DOM |
|
12983 val = node[name]; |
|
12984 } |
|
12985 |
|
12986 return val; |
|
12987 }; |
|
12988 |
|
12989 Y.mix(Y_Node.prototype, { |
|
12990 DATA_PREFIX: 'data-', |
|
12991 |
|
12992 /** |
|
12993 * The method called when outputting Node instances as strings |
|
12994 * @method toString |
|
12995 * @return {String} A string representation of the Node instance |
|
12996 */ |
|
12997 toString: function() { |
|
12998 var str = this[UID] + ': not bound to a node', |
|
12999 node = this._node, |
|
13000 attrs, id, className; |
|
13001 |
|
13002 if (node) { |
|
13003 attrs = node.attributes; |
|
13004 id = (attrs && attrs.id) ? node.getAttribute('id') : null; |
|
13005 className = (attrs && attrs.className) ? node.getAttribute('className') : null; |
|
13006 str = node[NODE_NAME]; |
|
13007 |
|
13008 if (id) { |
|
13009 str += '#' + id; |
|
13010 } |
|
13011 |
|
13012 if (className) { |
|
13013 str += '.' + className.replace(' ', '.'); |
|
13014 } |
|
13015 |
|
13016 // TODO: add yuid? |
|
13017 str += ' ' + this[UID]; |
|
13018 } |
|
13019 return str; |
|
13020 }, |
|
13021 |
|
13022 /** |
|
13023 * Returns an attribute value on the Node instance. |
|
13024 * Unless pre-configured (via `Node.ATTRS`), get hands |
|
13025 * off to the underlying DOM node. Only valid |
|
13026 * attributes/properties for the node will be queried. |
|
13027 * @method get |
|
13028 * @param {String} attr The attribute |
|
13029 * @return {any} The current value of the attribute |
|
13030 */ |
|
13031 get: function(attr) { |
|
13032 var val; |
|
13033 |
|
13034 if (this._getAttr) { // use Attribute imple |
|
13035 val = this._getAttr(attr); |
|
13036 } else { |
|
13037 val = this._get(attr); |
|
13038 } |
|
13039 |
|
13040 if (val) { |
|
13041 val = Y_Node.scrubVal(val, this); |
|
13042 } else if (val === null) { |
|
13043 val = null; // IE: DOM null is not true null (even though they ===) |
|
13044 } |
|
13045 return val; |
|
13046 }, |
|
13047 |
|
13048 /** |
|
13049 * Helper method for get. |
|
13050 * @method _get |
|
13051 * @private |
|
13052 * @param {String} attr The attribute |
|
13053 * @return {any} The current value of the attribute |
|
13054 */ |
|
13055 _get: function(attr) { |
|
13056 var attrConfig = Y_Node.ATTRS[attr], |
|
13057 val; |
|
13058 |
|
13059 if (attrConfig && attrConfig.getter) { |
|
13060 val = attrConfig.getter.call(this); |
|
13061 } else if (Y_Node.re_aria.test(attr)) { |
|
13062 val = this._node.getAttribute(attr, 2); |
|
13063 } else { |
|
13064 val = Y_Node.DEFAULT_GETTER.apply(this, arguments); |
|
13065 } |
|
13066 |
|
13067 return val; |
|
13068 }, |
|
13069 |
|
13070 /** |
|
13071 * Sets an attribute on the Node instance. |
|
13072 * Unless pre-configured (via Node.ATTRS), set hands |
|
13073 * off to the underlying DOM node. Only valid |
|
13074 * attributes/properties for the node will be set. |
|
13075 * To set custom attributes use setAttribute. |
|
13076 * @method set |
|
13077 * @param {String} attr The attribute to be set. |
|
13078 * @param {any} val The value to set the attribute to. |
|
13079 * @chainable |
|
13080 */ |
|
13081 set: function(attr, val) { |
|
13082 var attrConfig = Y_Node.ATTRS[attr]; |
|
13083 |
|
13084 if (this._setAttr) { // use Attribute imple |
|
13085 this._setAttr.apply(this, arguments); |
|
13086 } else { // use setters inline |
|
13087 if (attrConfig && attrConfig.setter) { |
|
13088 attrConfig.setter.call(this, val, attr); |
|
13089 } else if (Y_Node.re_aria.test(attr)) { // special case Aria |
|
13090 this._node.setAttribute(attr, val); |
|
13091 } else { |
|
13092 Y_Node.DEFAULT_SETTER.apply(this, arguments); |
|
13093 } |
|
13094 } |
|
13095 |
|
13096 return this; |
|
13097 }, |
|
13098 |
|
13099 /** |
|
13100 * Sets multiple attributes. |
|
13101 * @method setAttrs |
|
13102 * @param {Object} attrMap an object of name/value pairs to set |
|
13103 * @chainable |
|
13104 */ |
|
13105 setAttrs: function(attrMap) { |
|
13106 if (this._setAttrs) { // use Attribute imple |
|
13107 this._setAttrs(attrMap); |
|
13108 } else { // use setters inline |
|
13109 Y.Object.each(attrMap, function(v, n) { |
|
13110 this.set(n, v); |
|
13111 }, this); |
|
13112 } |
|
13113 |
|
13114 return this; |
|
13115 }, |
|
13116 |
|
13117 /** |
|
13118 * Returns an object containing the values for the requested attributes. |
|
13119 * @method getAttrs |
|
13120 * @param {Array} attrs an array of attributes to get values |
|
13121 * @return {Object} An object with attribute name/value pairs. |
|
13122 */ |
|
13123 getAttrs: function(attrs) { |
|
13124 var ret = {}; |
|
13125 if (this._getAttrs) { // use Attribute imple |
|
13126 this._getAttrs(attrs); |
|
13127 } else { // use setters inline |
|
13128 Y.Array.each(attrs, function(v, n) { |
|
13129 ret[v] = this.get(v); |
|
13130 }, this); |
|
13131 } |
|
13132 |
|
13133 return ret; |
|
13134 }, |
|
13135 |
|
13136 /** |
|
13137 * Compares nodes to determine if they match. |
|
13138 * Node instances can be compared to each other and/or HTMLElements. |
|
13139 * @method compareTo |
|
13140 * @param {HTMLElement | Node} refNode The reference node to compare to the node. |
|
13141 * @return {Boolean} True if the nodes match, false if they do not. |
|
13142 */ |
|
13143 compareTo: function(refNode) { |
|
13144 var node = this._node; |
|
13145 |
|
13146 if (refNode && refNode._node) { |
|
13147 refNode = refNode._node; |
|
13148 } |
|
13149 return node === refNode; |
|
13150 }, |
|
13151 |
|
13152 /** |
|
13153 * Determines whether the node is appended to the document. |
|
13154 * @method inDoc |
|
13155 * @param {Node|HTMLElement} doc optional An optional document to check against. |
|
13156 * Defaults to current document. |
|
13157 * @return {Boolean} Whether or not this node is appended to the document. |
|
13158 */ |
|
13159 inDoc: function(doc) { |
|
13160 var node = this._node; |
|
13161 doc = (doc) ? doc._node || doc : node[OWNER_DOCUMENT]; |
|
13162 if (doc.documentElement) { |
|
13163 return Y_DOM.contains(doc.documentElement, node); |
|
13164 } |
|
13165 }, |
|
13166 |
|
13167 getById: function(id) { |
|
13168 var node = this._node, |
|
13169 ret = Y_DOM.byId(id, node[OWNER_DOCUMENT]); |
|
13170 if (ret && Y_DOM.contains(node, ret)) { |
|
13171 ret = Y.one(ret); |
|
13172 } else { |
|
13173 ret = null; |
|
13174 } |
|
13175 return ret; |
|
13176 }, |
|
13177 |
|
13178 /** |
|
13179 * Returns the nearest ancestor that passes the test applied by supplied boolean method. |
|
13180 * @method ancestor |
|
13181 * @param {String | Function} fn A selector string or boolean method for testing elements. |
|
13182 * If a function is used, it receives the current node being tested as the only argument. |
|
13183 * If fn is not passed as an argument, the parent node will be returned. |
|
13184 * @param {Boolean} testSelf optional Whether or not to include the element in the scan |
|
13185 * @param {String | Function} stopFn optional A selector string or boolean |
|
13186 * method to indicate when the search should stop. The search bails when the function |
|
13187 * returns true or the selector matches. |
|
13188 * If a function is used, it receives the current node being tested as the only argument. |
|
13189 * @return {Node} The matching Node instance or null if not found |
|
13190 */ |
|
13191 ancestor: function(fn, testSelf, stopFn) { |
|
13192 // testSelf is optional, check for stopFn as 2nd arg |
|
13193 if (arguments.length === 2 && |
|
13194 (typeof testSelf == 'string' || typeof testSelf == 'function')) { |
|
13195 stopFn = testSelf; |
|
13196 } |
|
13197 |
|
13198 return Y.one(Y_DOM.ancestor(this._node, _wrapFn(fn), testSelf, _wrapFn(stopFn))); |
|
13199 }, |
|
13200 |
|
13201 /** |
|
13202 * Returns the ancestors that pass the test applied by supplied boolean method. |
|
13203 * @method ancestors |
|
13204 * @param {String | Function} fn A selector string or boolean method for testing elements. |
|
13205 * @param {Boolean} testSelf optional Whether or not to include the element in the scan |
|
13206 * If a function is used, it receives the current node being tested as the only argument. |
|
13207 * @return {NodeList} A NodeList instance containing the matching elements |
|
13208 */ |
|
13209 ancestors: function(fn, testSelf, stopFn) { |
|
13210 if (arguments.length === 2 && |
|
13211 (typeof testSelf == 'string' || typeof testSelf == 'function')) { |
|
13212 stopFn = testSelf; |
|
13213 } |
|
13214 return Y.all(Y_DOM.ancestors(this._node, _wrapFn(fn), testSelf, _wrapFn(stopFn))); |
|
13215 }, |
|
13216 |
|
13217 /** |
|
13218 * Returns the previous matching sibling. |
|
13219 * Returns the nearest element node sibling if no method provided. |
|
13220 * @method previous |
|
13221 * @param {String | Function} fn A selector or boolean method for testing elements. |
|
13222 * If a function is used, it receives the current node being tested as the only argument. |
|
13223 * @return {Node} Node instance or null if not found |
|
13224 */ |
|
13225 previous: function(fn, all) { |
|
13226 return Y.one(Y_DOM.elementByAxis(this._node, 'previousSibling', _wrapFn(fn), all)); |
|
13227 }, |
|
13228 |
|
13229 /** |
|
13230 * Returns the next matching sibling. |
|
13231 * Returns the nearest element node sibling if no method provided. |
|
13232 * @method next |
|
13233 * @param {String | Function} fn A selector or boolean method for testing elements. |
|
13234 * If a function is used, it receives the current node being tested as the only argument. |
|
13235 * @return {Node} Node instance or null if not found |
|
13236 */ |
|
13237 next: function(fn, all) { |
|
13238 return Y.one(Y_DOM.elementByAxis(this._node, 'nextSibling', _wrapFn(fn), all)); |
|
13239 }, |
|
13240 |
|
13241 /** |
|
13242 * Returns all matching siblings. |
|
13243 * Returns all siblings if no method provided. |
|
13244 * @method siblings |
|
13245 * @param {String | Function} fn A selector or boolean method for testing elements. |
|
13246 * If a function is used, it receives the current node being tested as the only argument. |
|
13247 * @return {NodeList} NodeList instance bound to found siblings |
|
13248 */ |
|
13249 siblings: function(fn) { |
|
13250 return Y.all(Y_DOM.siblings(this._node, _wrapFn(fn))); |
|
13251 }, |
|
13252 |
|
13253 /** |
|
13254 * Retrieves a single Node instance, the first element matching the given |
|
13255 * CSS selector. |
|
13256 * Returns null if no match found. |
|
13257 * @method one |
|
13258 * |
|
13259 * @param {string} selector The CSS selector to test against. |
|
13260 * @return {Node | null} A Node instance for the matching HTMLElement or null |
|
13261 * if no match found. |
|
13262 */ |
|
13263 one: function(selector) { |
|
13264 return Y.one(Y.Selector.query(selector, this._node, true)); |
|
13265 }, |
|
13266 |
|
13267 /** |
|
13268 * Retrieves a NodeList based on the given CSS selector. |
|
13269 * @method all |
|
13270 * |
|
13271 * @param {string} selector The CSS selector to test against. |
|
13272 * @return {NodeList} A NodeList instance for the matching HTMLCollection/Array. |
|
13273 */ |
|
13274 all: function(selector) { |
|
13275 var nodelist; |
|
13276 |
|
13277 if (this._node) { |
|
13278 nodelist = Y.all(Y.Selector.query(selector, this._node)); |
|
13279 nodelist._query = selector; |
|
13280 nodelist._queryRoot = this._node; |
|
13281 } |
|
13282 |
|
13283 return nodelist || Y.all([]); |
|
13284 }, |
|
13285 |
|
13286 // TODO: allow fn test |
|
13287 /** |
|
13288 * Test if the supplied node matches the supplied selector. |
|
13289 * @method test |
|
13290 * |
|
13291 * @param {string} selector The CSS selector to test against. |
|
13292 * @return {boolean} Whether or not the node matches the selector. |
|
13293 */ |
|
13294 test: function(selector) { |
|
13295 return Y.Selector.test(this._node, selector); |
|
13296 }, |
|
13297 |
|
13298 /** |
|
13299 * Removes the node from its parent. |
|
13300 * Shortcut for myNode.get('parentNode').removeChild(myNode); |
|
13301 * @method remove |
|
13302 * @param {Boolean} destroy whether or not to call destroy() on the node |
|
13303 * after removal. |
|
13304 * @chainable |
|
13305 * |
|
13306 */ |
|
13307 remove: function(destroy) { |
|
13308 var node = this._node; |
|
13309 |
|
13310 if (node && node.parentNode) { |
|
13311 node.parentNode.removeChild(node); |
|
13312 } |
|
13313 |
|
13314 if (destroy) { |
|
13315 this.destroy(); |
|
13316 } |
|
13317 |
|
13318 return this; |
|
13319 }, |
|
13320 |
|
13321 /** |
|
13322 * Replace the node with the other node. This is a DOM update only |
|
13323 * and does not change the node bound to the Node instance. |
|
13324 * Shortcut for myNode.get('parentNode').replaceChild(newNode, myNode); |
|
13325 * @method replace |
|
13326 * @param {Node | HTMLNode} newNode Node to be inserted |
|
13327 * @chainable |
|
13328 * |
|
13329 */ |
|
13330 replace: function(newNode) { |
|
13331 var node = this._node; |
|
13332 if (typeof newNode == 'string') { |
|
13333 newNode = Y_Node.create(newNode); |
|
13334 } |
|
13335 node.parentNode.replaceChild(Y_Node.getDOMNode(newNode), node); |
|
13336 return this; |
|
13337 }, |
|
13338 |
|
13339 /** |
|
13340 * @method replaceChild |
|
13341 * @for Node |
|
13342 * @param {String | HTMLElement | Node} node Node to be inserted |
|
13343 * @param {HTMLElement | Node} refNode Node to be replaced |
|
13344 * @return {Node} The replaced node |
|
13345 */ |
|
13346 replaceChild: function(node, refNode) { |
|
13347 if (typeof node == 'string') { |
|
13348 node = Y_DOM.create(node); |
|
13349 } |
|
13350 |
|
13351 return Y.one(this._node.replaceChild(Y_Node.getDOMNode(node), Y_Node.getDOMNode(refNode))); |
|
13352 }, |
|
13353 |
|
13354 /** |
|
13355 * Nulls internal node references, removes any plugins and event listeners. |
|
13356 * Note that destroy() will not remove the node from its parent or from the DOM. For that |
|
13357 * functionality, call remove(true). |
|
13358 * @method destroy |
|
13359 * @param {Boolean} recursivePurge (optional) Whether or not to remove listeners from the |
|
13360 * node's subtree (default is false) |
|
13361 * |
|
13362 */ |
|
13363 destroy: function(recursive) { |
|
13364 var UID = Y.config.doc.uniqueID ? 'uniqueID' : '_yuid', |
|
13365 instance; |
|
13366 |
|
13367 this.purge(); // TODO: only remove events add via this Node |
|
13368 |
|
13369 if (this.unplug) { // may not be a PluginHost |
|
13370 this.unplug(); |
|
13371 } |
|
13372 |
|
13373 this.clearData(); |
|
13374 |
|
13375 if (recursive) { |
|
13376 Y.NodeList.each(this.all('*'), function(node) { |
|
13377 instance = Y_Node._instances[node[UID]]; |
|
13378 if (instance) { |
|
13379 instance.destroy(); |
|
13380 } else { // purge in case added by other means |
|
13381 Y.Event.purgeElement(node); |
|
13382 } |
|
13383 }); |
|
13384 } |
|
13385 |
|
13386 this._node = null; |
|
13387 this._stateProxy = null; |
|
13388 |
|
13389 delete Y_Node._instances[this._yuid]; |
|
13390 }, |
|
13391 |
|
13392 /** |
|
13393 * Invokes a method on the Node instance |
|
13394 * @method invoke |
|
13395 * @param {String} method The name of the method to invoke |
|
13396 * @param {Any} a, b, c, etc. Arguments to invoke the method with. |
|
13397 * @return Whatever the underly method returns. |
|
13398 * DOM Nodes and Collections return values |
|
13399 * are converted to Node/NodeList instances. |
|
13400 * |
|
13401 */ |
|
13402 invoke: function(method, a, b, c, d, e) { |
|
13403 var node = this._node, |
|
13404 ret; |
|
13405 |
|
13406 if (a && a._node) { |
|
13407 a = a._node; |
|
13408 } |
|
13409 |
|
13410 if (b && b._node) { |
|
13411 b = b._node; |
|
13412 } |
|
13413 |
|
13414 ret = node[method](a, b, c, d, e); |
|
13415 return Y_Node.scrubVal(ret, this); |
|
13416 }, |
|
13417 |
|
13418 /** |
|
13419 * @method swap |
|
13420 * @description Swap DOM locations with the given node. |
|
13421 * This does not change which DOM node each Node instance refers to. |
|
13422 * @param {Node} otherNode The node to swap with |
|
13423 * @chainable |
|
13424 */ |
|
13425 swap: Y.config.doc.documentElement.swapNode ? |
|
13426 function(otherNode) { |
|
13427 this._node.swapNode(Y_Node.getDOMNode(otherNode)); |
|
13428 } : |
|
13429 function(otherNode) { |
|
13430 otherNode = Y_Node.getDOMNode(otherNode); |
|
13431 var node = this._node, |
|
13432 parent = otherNode.parentNode, |
|
13433 nextSibling = otherNode.nextSibling; |
|
13434 |
|
13435 if (nextSibling === node) { |
|
13436 parent.insertBefore(node, otherNode); |
|
13437 } else if (otherNode === node.nextSibling) { |
|
13438 parent.insertBefore(otherNode, node); |
|
13439 } else { |
|
13440 node.parentNode.replaceChild(otherNode, node); |
|
13441 Y_DOM.addHTML(parent, node, nextSibling); |
|
13442 } |
|
13443 return this; |
|
13444 }, |
|
13445 |
|
13446 |
|
13447 hasMethod: function(method) { |
|
13448 var node = this._node; |
|
13449 return !!(node && method in node && |
|
13450 typeof node[method] != 'unknown' && |
|
13451 (typeof node[method] == 'function' || |
|
13452 String(node[method]).indexOf('function') === 1)); // IE reports as object, prepends space |
|
13453 }, |
|
13454 |
|
13455 isFragment: function() { |
|
13456 return (this.get('nodeType') === 11); |
|
13457 }, |
|
13458 |
|
13459 /** |
|
13460 * Removes and destroys all of the nodes within the node. |
|
13461 * @method empty |
|
13462 * @chainable |
|
13463 */ |
|
13464 empty: function() { |
|
13465 this.get('childNodes').remove().destroy(true); |
|
13466 return this; |
|
13467 }, |
|
13468 |
|
13469 /** |
|
13470 * Returns the DOM node bound to the Node instance |
|
13471 * @method getDOMNode |
|
13472 * @return {DOMNode} |
|
13473 */ |
|
13474 getDOMNode: function() { |
|
13475 return this._node; |
|
13476 } |
|
13477 }, true); |
|
13478 |
|
13479 Y.Node = Y_Node; |
|
13480 Y.one = Y_Node.one; |
|
13481 /** |
|
13482 * The NodeList module provides support for managing collections of Nodes. |
|
13483 * @module node |
|
13484 * @submodule node-core |
|
13485 */ |
|
13486 |
|
13487 /** |
|
13488 * The NodeList class provides a wrapper for manipulating DOM NodeLists. |
|
13489 * NodeList properties can be accessed via the set/get methods. |
|
13490 * Use Y.all() to retrieve NodeList instances. |
|
13491 * |
|
13492 * @class NodeList |
|
13493 * @constructor |
|
13494 * @param nodes {String|element|Node|Array} A selector, DOM element, Node, list of DOM elements, or list of Nodes with which to populate this NodeList. |
|
13495 */ |
|
13496 |
|
13497 var NodeList = function(nodes) { |
|
13498 var tmp = []; |
|
13499 |
|
13500 if (nodes) { |
|
13501 if (typeof nodes === 'string') { // selector query |
|
13502 this._query = nodes; |
|
13503 nodes = Y.Selector.query(nodes); |
|
13504 } else if (nodes.nodeType || Y_DOM.isWindow(nodes)) { // domNode || window |
|
13505 nodes = [nodes]; |
|
13506 } else if (nodes._node) { // Y.Node |
|
13507 nodes = [nodes._node]; |
|
13508 } else if (nodes[0] && nodes[0]._node) { // allow array of Y.Nodes |
|
13509 Y.Array.each(nodes, function(node) { |
|
13510 if (node._node) { |
|
13511 tmp.push(node._node); |
|
13512 } |
|
13513 }); |
|
13514 nodes = tmp; |
|
13515 } else { // array of domNodes or domNodeList (no mixed array of Y.Node/domNodes) |
|
13516 nodes = Y.Array(nodes, 0, true); |
|
13517 } |
|
13518 } |
|
13519 |
|
13520 /** |
|
13521 * The underlying array of DOM nodes bound to the Y.NodeList instance |
|
13522 * @property _nodes |
|
13523 * @private |
|
13524 */ |
|
13525 this._nodes = nodes || []; |
|
13526 }; |
|
13527 |
|
13528 NodeList.NAME = 'NodeList'; |
|
13529 |
|
13530 /** |
|
13531 * Retrieves the DOM nodes bound to a NodeList instance |
|
13532 * @method getDOMNodes |
|
13533 * @static |
|
13534 * |
|
13535 * @param {NodeList} nodelist The NodeList instance |
|
13536 * @return {Array} The array of DOM nodes bound to the NodeList |
|
13537 */ |
|
13538 NodeList.getDOMNodes = function(nodelist) { |
|
13539 return (nodelist && nodelist._nodes) ? nodelist._nodes : nodelist; |
|
13540 }; |
|
13541 |
|
13542 NodeList.each = function(instance, fn, context) { |
|
13543 var nodes = instance._nodes; |
|
13544 if (nodes && nodes.length) { |
|
13545 Y.Array.each(nodes, fn, context || instance); |
|
13546 } else { |
|
13547 } |
|
13548 }; |
|
13549 |
|
13550 NodeList.addMethod = function(name, fn, context) { |
|
13551 if (name && fn) { |
|
13552 NodeList.prototype[name] = function() { |
|
13553 var ret = [], |
|
13554 args = arguments; |
|
13555 |
|
13556 Y.Array.each(this._nodes, function(node) { |
|
13557 var UID = (node.uniqueID && node.nodeType !== 9 ) ? 'uniqueID' : '_yuid', |
|
13558 instance = Y.Node._instances[node[UID]], |
|
13559 ctx, |
|
13560 result; |
|
13561 |
|
13562 if (!instance) { |
|
13563 instance = NodeList._getTempNode(node); |
|
13564 } |
|
13565 ctx = context || instance; |
|
13566 result = fn.apply(ctx, args); |
|
13567 if (result !== undefined && result !== instance) { |
|
13568 ret[ret.length] = result; |
|
13569 } |
|
13570 }); |
|
13571 |
|
13572 // TODO: remove tmp pointer |
|
13573 return ret.length ? ret : this; |
|
13574 }; |
|
13575 } else { |
|
13576 } |
|
13577 }; |
|
13578 |
|
13579 NodeList.importMethod = function(host, name, altName) { |
|
13580 if (typeof name === 'string') { |
|
13581 altName = altName || name; |
|
13582 NodeList.addMethod(name, host[name]); |
|
13583 } else { |
|
13584 Y.Array.each(name, function(n) { |
|
13585 NodeList.importMethod(host, n); |
|
13586 }); |
|
13587 } |
|
13588 }; |
|
13589 |
|
13590 NodeList._getTempNode = function(node) { |
|
13591 var tmp = NodeList._tempNode; |
|
13592 if (!tmp) { |
|
13593 tmp = Y.Node.create('<div></div>'); |
|
13594 NodeList._tempNode = tmp; |
|
13595 } |
|
13596 |
|
13597 tmp._node = node; |
|
13598 tmp._stateProxy = node; |
|
13599 return tmp; |
|
13600 }; |
|
13601 |
|
13602 Y.mix(NodeList.prototype, { |
|
13603 _invoke: function(method, args, getter) { |
|
13604 var ret = (getter) ? [] : this; |
|
13605 |
|
13606 this.each(function(node) { |
|
13607 var val = node[method].apply(node, args); |
|
13608 if (getter) { |
|
13609 ret.push(val); |
|
13610 } |
|
13611 }); |
|
13612 |
|
13613 return ret; |
|
13614 }, |
|
13615 |
|
13616 /** |
|
13617 * Retrieves the Node instance at the given index. |
|
13618 * @method item |
|
13619 * |
|
13620 * @param {Number} index The index of the target Node. |
|
13621 * @return {Node} The Node instance at the given index. |
|
13622 */ |
|
13623 item: function(index) { |
|
13624 return Y.one((this._nodes || [])[index]); |
|
13625 }, |
|
13626 |
|
13627 /** |
|
13628 * Applies the given function to each Node in the NodeList. |
|
13629 * @method each |
|
13630 * @param {Function} fn The function to apply. It receives 3 arguments: |
|
13631 * the current node instance, the node's index, and the NodeList instance |
|
13632 * @param {Object} context optional An optional context to apply the function with |
|
13633 * Default context is the current Node instance |
|
13634 * @chainable |
|
13635 */ |
|
13636 each: function(fn, context) { |
|
13637 var instance = this; |
|
13638 Y.Array.each(this._nodes, function(node, index) { |
|
13639 node = Y.one(node); |
|
13640 return fn.call(context || node, node, index, instance); |
|
13641 }); |
|
13642 return instance; |
|
13643 }, |
|
13644 |
|
13645 batch: function(fn, context) { |
|
13646 var nodelist = this; |
|
13647 |
|
13648 Y.Array.each(this._nodes, function(node, index) { |
|
13649 var instance = Y.Node._instances[node[UID]]; |
|
13650 if (!instance) { |
|
13651 instance = NodeList._getTempNode(node); |
|
13652 } |
|
13653 |
|
13654 return fn.call(context || instance, instance, index, nodelist); |
|
13655 }); |
|
13656 return nodelist; |
|
13657 }, |
|
13658 |
|
13659 /** |
|
13660 * Executes the function once for each node until a true value is returned. |
|
13661 * @method some |
|
13662 * @param {Function} fn The function to apply. It receives 3 arguments: |
|
13663 * the current node instance, the node's index, and the NodeList instance |
|
13664 * @param {Object} context optional An optional context to execute the function from. |
|
13665 * Default context is the current Node instance |
|
13666 * @return {Boolean} Whether or not the function returned true for any node. |
|
13667 */ |
|
13668 some: function(fn, context) { |
|
13669 var instance = this; |
|
13670 return Y.Array.some(this._nodes, function(node, index) { |
|
13671 node = Y.one(node); |
|
13672 context = context || node; |
|
13673 return fn.call(context, node, index, instance); |
|
13674 }); |
|
13675 }, |
|
13676 |
|
13677 /** |
|
13678 * Creates a documenFragment from the nodes bound to the NodeList instance |
|
13679 * @method toFrag |
|
13680 * @return {Node} a Node instance bound to the documentFragment |
|
13681 */ |
|
13682 toFrag: function() { |
|
13683 return Y.one(Y.DOM._nl2frag(this._nodes)); |
|
13684 }, |
|
13685 |
|
13686 /** |
|
13687 * Returns the index of the node in the NodeList instance |
|
13688 * or -1 if the node isn't found. |
|
13689 * @method indexOf |
|
13690 * @param {Node | DOMNode} node the node to search for |
|
13691 * @return {Int} the index of the node value or -1 if not found |
|
13692 */ |
|
13693 indexOf: function(node) { |
|
13694 return Y.Array.indexOf(this._nodes, Y.Node.getDOMNode(node)); |
|
13695 }, |
|
13696 |
|
13697 /** |
|
13698 * Filters the NodeList instance down to only nodes matching the given selector. |
|
13699 * @method filter |
|
13700 * @param {String} selector The selector to filter against |
|
13701 * @return {NodeList} NodeList containing the updated collection |
|
13702 * @see Selector |
|
13703 */ |
|
13704 filter: function(selector) { |
|
13705 return Y.all(Y.Selector.filter(this._nodes, selector)); |
|
13706 }, |
|
13707 |
|
13708 |
|
13709 /** |
|
13710 * Creates a new NodeList containing all nodes at every n indices, where |
|
13711 * remainder n % index equals r. |
|
13712 * (zero-based index). |
|
13713 * @method modulus |
|
13714 * @param {Int} n The offset to use (return every nth node) |
|
13715 * @param {Int} r An optional remainder to use with the modulus operation (defaults to zero) |
|
13716 * @return {NodeList} NodeList containing the updated collection |
|
13717 */ |
|
13718 modulus: function(n, r) { |
|
13719 r = r || 0; |
|
13720 var nodes = []; |
|
13721 NodeList.each(this, function(node, i) { |
|
13722 if (i % n === r) { |
|
13723 nodes.push(node); |
|
13724 } |
|
13725 }); |
|
13726 |
|
13727 return Y.all(nodes); |
|
13728 }, |
|
13729 |
|
13730 /** |
|
13731 * Creates a new NodeList containing all nodes at odd indices |
|
13732 * (zero-based index). |
|
13733 * @method odd |
|
13734 * @return {NodeList} NodeList containing the updated collection |
|
13735 */ |
|
13736 odd: function() { |
|
13737 return this.modulus(2, 1); |
|
13738 }, |
|
13739 |
|
13740 /** |
|
13741 * Creates a new NodeList containing all nodes at even indices |
|
13742 * (zero-based index), including zero. |
|
13743 * @method even |
|
13744 * @return {NodeList} NodeList containing the updated collection |
|
13745 */ |
|
13746 even: function() { |
|
13747 return this.modulus(2); |
|
13748 }, |
|
13749 |
|
13750 destructor: function() { |
|
13751 }, |
|
13752 |
|
13753 /** |
|
13754 * Reruns the initial query, when created using a selector query |
|
13755 * @method refresh |
|
13756 * @chainable |
|
13757 */ |
|
13758 refresh: function() { |
|
13759 var doc, |
|
13760 nodes = this._nodes, |
|
13761 query = this._query, |
|
13762 root = this._queryRoot; |
|
13763 |
|
13764 if (query) { |
|
13765 if (!root) { |
|
13766 if (nodes && nodes[0] && nodes[0].ownerDocument) { |
|
13767 root = nodes[0].ownerDocument; |
|
13768 } |
|
13769 } |
|
13770 |
|
13771 this._nodes = Y.Selector.query(query, root); |
|
13772 } |
|
13773 |
|
13774 return this; |
|
13775 }, |
|
13776 |
|
13777 /** |
|
13778 * Returns the current number of items in the NodeList. |
|
13779 * @method size |
|
13780 * @return {Int} The number of items in the NodeList. |
|
13781 */ |
|
13782 size: function() { |
|
13783 return this._nodes.length; |
|
13784 }, |
|
13785 |
|
13786 /** |
|
13787 * Determines if the instance is bound to any nodes |
|
13788 * @method isEmpty |
|
13789 * @return {Boolean} Whether or not the NodeList is bound to any nodes |
|
13790 */ |
|
13791 isEmpty: function() { |
|
13792 return this._nodes.length < 1; |
|
13793 }, |
|
13794 |
|
13795 toString: function() { |
|
13796 var str = '', |
|
13797 errorMsg = this[UID] + ': not bound to any nodes', |
|
13798 nodes = this._nodes, |
|
13799 node; |
|
13800 |
|
13801 if (nodes && nodes[0]) { |
|
13802 node = nodes[0]; |
|
13803 str += node[NODE_NAME]; |
|
13804 if (node.id) { |
|
13805 str += '#' + node.id; |
|
13806 } |
|
13807 |
|
13808 if (node.className) { |
|
13809 str += '.' + node.className.replace(' ', '.'); |
|
13810 } |
|
13811 |
|
13812 if (nodes.length > 1) { |
|
13813 str += '...[' + nodes.length + ' items]'; |
|
13814 } |
|
13815 } |
|
13816 return str || errorMsg; |
|
13817 }, |
|
13818 |
|
13819 /** |
|
13820 * Returns the DOM node bound to the Node instance |
|
13821 * @method getDOMNodes |
|
13822 * @return {Array} |
|
13823 */ |
|
13824 getDOMNodes: function() { |
|
13825 return this._nodes; |
|
13826 } |
|
13827 }, true); |
|
13828 |
|
13829 NodeList.importMethod(Y.Node.prototype, [ |
|
13830 /** |
|
13831 * Called on each Node instance. Nulls internal node references, |
|
13832 * removes any plugins and event listeners |
|
13833 * @method destroy |
|
13834 * @param {Boolean} recursivePurge (optional) Whether or not to |
|
13835 * remove listeners from the node's subtree (default is false) |
|
13836 * @see Node.destroy |
|
13837 */ |
|
13838 'destroy', |
|
13839 |
|
13840 /** |
|
13841 * Called on each Node instance. Removes and destroys all of the nodes |
|
13842 * within the node |
|
13843 * @method empty |
|
13844 * @chainable |
|
13845 * @see Node.empty |
|
13846 */ |
|
13847 'empty', |
|
13848 |
|
13849 /** |
|
13850 * Called on each Node instance. Removes the node from its parent. |
|
13851 * Shortcut for myNode.get('parentNode').removeChild(myNode); |
|
13852 * @method remove |
|
13853 * @param {Boolean} destroy whether or not to call destroy() on the node |
|
13854 * after removal. |
|
13855 * @chainable |
|
13856 * @see Node.remove |
|
13857 */ |
|
13858 'remove', |
|
13859 |
|
13860 /** |
|
13861 * Called on each Node instance. Sets an attribute on the Node instance. |
|
13862 * Unless pre-configured (via Node.ATTRS), set hands |
|
13863 * off to the underlying DOM node. Only valid |
|
13864 * attributes/properties for the node will be set. |
|
13865 * To set custom attributes use setAttribute. |
|
13866 * @method set |
|
13867 * @param {String} attr The attribute to be set. |
|
13868 * @param {any} val The value to set the attribute to. |
|
13869 * @chainable |
|
13870 * @see Node.set |
|
13871 */ |
|
13872 'set' |
|
13873 ]); |
|
13874 |
|
13875 // one-off implementation to convert array of Nodes to NodeList |
|
13876 // e.g. Y.all('input').get('parentNode'); |
|
13877 |
|
13878 /** Called on each Node instance |
|
13879 * @method get |
|
13880 * @see Node |
|
13881 */ |
|
13882 NodeList.prototype.get = function(attr) { |
|
13883 var ret = [], |
|
13884 nodes = this._nodes, |
|
13885 isNodeList = false, |
|
13886 getTemp = NodeList._getTempNode, |
|
13887 instance, |
|
13888 val; |
|
13889 |
|
13890 if (nodes[0]) { |
|
13891 instance = Y.Node._instances[nodes[0]._yuid] || getTemp(nodes[0]); |
|
13892 val = instance._get(attr); |
|
13893 if (val && val.nodeType) { |
|
13894 isNodeList = true; |
|
13895 } |
|
13896 } |
|
13897 |
|
13898 Y.Array.each(nodes, function(node) { |
|
13899 instance = Y.Node._instances[node._yuid]; |
|
13900 |
|
13901 if (!instance) { |
|
13902 instance = getTemp(node); |
|
13903 } |
|
13904 |
|
13905 val = instance._get(attr); |
|
13906 if (!isNodeList) { // convert array of Nodes to NodeList |
|
13907 val = Y.Node.scrubVal(val, instance); |
|
13908 } |
|
13909 |
|
13910 ret.push(val); |
|
13911 }); |
|
13912 |
|
13913 return (isNodeList) ? Y.all(ret) : ret; |
|
13914 }; |
|
13915 |
|
13916 Y.NodeList = NodeList; |
|
13917 |
|
13918 Y.all = function(nodes) { |
|
13919 return new NodeList(nodes); |
|
13920 }; |
|
13921 |
|
13922 Y.Node.all = Y.all; |
|
13923 /** |
|
13924 * @module node |
|
13925 * @submodule node-core |
|
13926 */ |
|
13927 |
|
13928 var Y_NodeList = Y.NodeList, |
|
13929 ArrayProto = Array.prototype, |
|
13930 ArrayMethods = { |
|
13931 /** Returns a new NodeList combining the given NodeList(s) |
|
13932 * @for NodeList |
|
13933 * @method concat |
|
13934 * @param {NodeList | Array} valueN Arrays/NodeLists and/or values to |
|
13935 * concatenate to the resulting NodeList |
|
13936 * @return {NodeList} A new NodeList comprised of this NodeList joined with the input. |
|
13937 */ |
|
13938 'concat': 1, |
|
13939 /** Removes the last from the NodeList and returns it. |
|
13940 * @for NodeList |
|
13941 * @method pop |
|
13942 * @return {Node | null} The last item in the NodeList, or null if the list is empty. |
|
13943 */ |
|
13944 'pop': 0, |
|
13945 /** Adds the given Node(s) to the end of the NodeList. |
|
13946 * @for NodeList |
|
13947 * @method push |
|
13948 * @param {Node | DOMNode} nodes One or more nodes to add to the end of the NodeList. |
|
13949 */ |
|
13950 'push': 0, |
|
13951 /** Removes the first item from the NodeList and returns it. |
|
13952 * @for NodeList |
|
13953 * @method shift |
|
13954 * @return {Node | null} The first item in the NodeList, or null if the NodeList is empty. |
|
13955 */ |
|
13956 'shift': 0, |
|
13957 /** Returns a new NodeList comprising the Nodes in the given range. |
|
13958 * @for NodeList |
|
13959 * @method slice |
|
13960 * @param {Number} begin Zero-based index at which to begin extraction. |
|
13961 As a negative index, start indicates an offset from the end of the sequence. slice(-2) extracts the second-to-last element and the last element in the sequence. |
|
13962 * @param {Number} end Zero-based index at which to end extraction. slice extracts up to but not including end. |
|
13963 slice(1,4) extracts the second element through the fourth element (elements indexed 1, 2, and 3). |
|
13964 As a negative index, end indicates an offset from the end of the sequence. slice(2,-1) extracts the third element through the second-to-last element in the sequence. |
|
13965 If end is omitted, slice extracts to the end of the sequence. |
|
13966 * @return {NodeList} A new NodeList comprised of this NodeList joined with the input. |
|
13967 */ |
|
13968 'slice': 1, |
|
13969 /** Changes the content of the NodeList, adding new elements while removing old elements. |
|
13970 * @for NodeList |
|
13971 * @method splice |
|
13972 * @param {Number} index Index at which to start changing the array. If negative, will begin that many elements from the end. |
|
13973 * @param {Number} howMany An integer indicating the number of old array elements to remove. If howMany is 0, no elements are removed. In this case, you should specify at least one new element. If no howMany parameter is specified (second syntax above, which is a SpiderMonkey extension), all elements after index are removed. |
|
13974 * {Node | DOMNode| element1, ..., elementN |
|
13975 The elements to add to the array. If you don't specify any elements, splice simply removes elements from the array. |
|
13976 * @return {NodeList} The element(s) removed. |
|
13977 */ |
|
13978 'splice': 1, |
|
13979 /** Adds the given Node(s) to the beginning of the NodeList. |
|
13980 * @for NodeList |
|
13981 * @method unshift |
|
13982 * @param {Node | DOMNode} nodes One or more nodes to add to the NodeList. |
|
13983 */ |
|
13984 'unshift': 0 |
|
13985 }; |
|
13986 |
|
13987 |
|
13988 Y.Object.each(ArrayMethods, function(returnNodeList, name) { |
|
13989 Y_NodeList.prototype[name] = function() { |
|
13990 var args = [], |
|
13991 i = 0, |
|
13992 arg, |
|
13993 ret; |
|
13994 |
|
13995 while (typeof (arg = arguments[i++]) != 'undefined') { // use DOM nodes/nodeLists |
|
13996 args.push(arg._node || arg._nodes || arg); |
|
13997 } |
|
13998 |
|
13999 ret = ArrayProto[name].apply(this._nodes, args); |
|
14000 |
|
14001 if (returnNodeList) { |
|
14002 ret = Y.all(ret); |
|
14003 } else { |
|
14004 ret = Y.Node.scrubVal(ret); |
|
14005 } |
|
14006 |
|
14007 return ret; |
|
14008 }; |
|
14009 }); |
|
14010 /** |
|
14011 * @module node |
|
14012 * @submodule node-core |
|
14013 */ |
|
14014 |
|
14015 Y.Array.each([ |
|
14016 /** |
|
14017 * Passes through to DOM method. |
|
14018 * @for Node |
|
14019 * @method removeChild |
|
14020 * @param {HTMLElement | Node} node Node to be removed |
|
14021 * @return {Node} The removed node |
|
14022 */ |
|
14023 'removeChild', |
|
14024 |
|
14025 /** |
|
14026 * Passes through to DOM method. |
|
14027 * @method hasChildNodes |
|
14028 * @return {Boolean} Whether or not the node has any childNodes |
|
14029 */ |
|
14030 'hasChildNodes', |
|
14031 |
|
14032 /** |
|
14033 * Passes through to DOM method. |
|
14034 * @method cloneNode |
|
14035 * @param {Boolean} deep Whether or not to perform a deep clone, which includes |
|
14036 * subtree and attributes |
|
14037 * @return {Node} The clone |
|
14038 */ |
|
14039 'cloneNode', |
|
14040 |
|
14041 /** |
|
14042 * Passes through to DOM method. |
|
14043 * @method hasAttribute |
|
14044 * @param {String} attribute The attribute to test for |
|
14045 * @return {Boolean} Whether or not the attribute is present |
|
14046 */ |
|
14047 'hasAttribute', |
|
14048 |
|
14049 /** |
|
14050 * Passes through to DOM method. |
|
14051 * @method scrollIntoView |
|
14052 * @chainable |
|
14053 */ |
|
14054 'scrollIntoView', |
|
14055 |
|
14056 /** |
|
14057 * Passes through to DOM method. |
|
14058 * @method getElementsByTagName |
|
14059 * @param {String} tagName The tagName to collect |
|
14060 * @return {NodeList} A NodeList representing the HTMLCollection |
|
14061 */ |
|
14062 'getElementsByTagName', |
|
14063 |
|
14064 /** |
|
14065 * Passes through to DOM method. |
|
14066 * @method focus |
|
14067 * @chainable |
|
14068 */ |
|
14069 'focus', |
|
14070 |
|
14071 /** |
|
14072 * Passes through to DOM method. |
|
14073 * @method blur |
|
14074 * @chainable |
|
14075 */ |
|
14076 'blur', |
|
14077 |
|
14078 /** |
|
14079 * Passes through to DOM method. |
|
14080 * Only valid on FORM elements |
|
14081 * @method submit |
|
14082 * @chainable |
|
14083 */ |
|
14084 'submit', |
|
14085 |
|
14086 /** |
|
14087 * Passes through to DOM method. |
|
14088 * Only valid on FORM elements |
|
14089 * @method reset |
|
14090 * @chainable |
|
14091 */ |
|
14092 'reset', |
|
14093 |
|
14094 /** |
|
14095 * Passes through to DOM method. |
|
14096 * @method select |
|
14097 * @chainable |
|
14098 */ |
|
14099 'select', |
|
14100 |
|
14101 /** |
|
14102 * Passes through to DOM method. |
|
14103 * Only valid on TABLE elements |
|
14104 * @method createCaption |
|
14105 * @chainable |
|
14106 */ |
|
14107 'createCaption' |
|
14108 |
|
14109 ], function(method) { |
|
14110 Y.Node.prototype[method] = function(arg1, arg2, arg3) { |
|
14111 var ret = this.invoke(method, arg1, arg2, arg3); |
|
14112 return ret; |
|
14113 }; |
|
14114 }); |
|
14115 |
|
14116 /** |
|
14117 * Passes through to DOM method. |
|
14118 * @method removeAttribute |
|
14119 * @param {String} attribute The attribute to be removed |
|
14120 * @chainable |
|
14121 */ |
|
14122 // one-off implementation due to IE returning boolean, breaking chaining |
|
14123 Y.Node.prototype.removeAttribute = function(attr) { |
|
14124 var node = this._node; |
|
14125 if (node) { |
|
14126 node.removeAttribute(attr, 0); // comma zero for IE < 8 to force case-insensitive |
|
14127 } |
|
14128 |
|
14129 return this; |
|
14130 }; |
|
14131 |
|
14132 Y.Node.importMethod(Y.DOM, [ |
|
14133 /** |
|
14134 * Determines whether the node is an ancestor of another HTML element in the DOM hierarchy. |
|
14135 * @method contains |
|
14136 * @param {Node | HTMLElement} needle The possible node or descendent |
|
14137 * @return {Boolean} Whether or not this node is the needle its ancestor |
|
14138 */ |
|
14139 'contains', |
|
14140 /** |
|
14141 * Allows setting attributes on DOM nodes, normalizing in some cases. |
|
14142 * This passes through to the DOM node, allowing for custom attributes. |
|
14143 * @method setAttribute |
|
14144 * @for Node |
|
14145 * @chainable |
|
14146 * @param {string} name The attribute name |
|
14147 * @param {string} value The value to set |
|
14148 */ |
|
14149 'setAttribute', |
|
14150 /** |
|
14151 * Allows getting attributes on DOM nodes, normalizing in some cases. |
|
14152 * This passes through to the DOM node, allowing for custom attributes. |
|
14153 * @method getAttribute |
|
14154 * @for Node |
|
14155 * @param {string} name The attribute name |
|
14156 * @return {string} The attribute value |
|
14157 */ |
|
14158 'getAttribute', |
|
14159 |
|
14160 /** |
|
14161 * Wraps the given HTML around the node. |
|
14162 * @method wrap |
|
14163 * @param {String} html The markup to wrap around the node. |
|
14164 * @chainable |
|
14165 * @for Node |
|
14166 */ |
|
14167 'wrap', |
|
14168 |
|
14169 /** |
|
14170 * Removes the node's parent node. |
|
14171 * @method unwrap |
|
14172 * @chainable |
|
14173 */ |
|
14174 'unwrap', |
|
14175 |
|
14176 /** |
|
14177 * Applies a unique ID to the node if none exists |
|
14178 * @method generateID |
|
14179 * @return {String} The existing or generated ID |
|
14180 */ |
|
14181 'generateID' |
|
14182 ]); |
|
14183 |
|
14184 Y.NodeList.importMethod(Y.Node.prototype, [ |
|
14185 /** |
|
14186 * Allows getting attributes on DOM nodes, normalizing in some cases. |
|
14187 * This passes through to the DOM node, allowing for custom attributes. |
|
14188 * @method getAttribute |
|
14189 * @see Node |
|
14190 * @for NodeList |
|
14191 * @param {string} name The attribute name |
|
14192 * @return {string} The attribute value |
|
14193 */ |
|
14194 |
|
14195 'getAttribute', |
|
14196 /** |
|
14197 * Allows setting attributes on DOM nodes, normalizing in some cases. |
|
14198 * This passes through to the DOM node, allowing for custom attributes. |
|
14199 * @method setAttribute |
|
14200 * @see Node |
|
14201 * @for NodeList |
|
14202 * @chainable |
|
14203 * @param {string} name The attribute name |
|
14204 * @param {string} value The value to set |
|
14205 */ |
|
14206 'setAttribute', |
|
14207 |
|
14208 /** |
|
14209 * Allows for removing attributes on DOM nodes. |
|
14210 * This passes through to the DOM node, allowing for custom attributes. |
|
14211 * @method removeAttribute |
|
14212 * @see Node |
|
14213 * @for NodeList |
|
14214 * @param {string} name The attribute to remove |
|
14215 */ |
|
14216 'removeAttribute', |
|
14217 /** |
|
14218 * Removes the parent node from node in the list. |
|
14219 * @method unwrap |
|
14220 * @chainable |
|
14221 */ |
|
14222 'unwrap', |
|
14223 /** |
|
14224 * Wraps the given HTML around each node. |
|
14225 * @method wrap |
|
14226 * @param {String} html The markup to wrap around the node. |
|
14227 * @chainable |
|
14228 */ |
|
14229 'wrap', |
|
14230 |
|
14231 /** |
|
14232 * Applies a unique ID to each node if none exists |
|
14233 * @method generateID |
|
14234 * @return {String} The existing or generated ID |
|
14235 */ |
|
14236 'generateID' |
|
14237 ]); |
|
14238 |
|
14239 |
|
14240 }, '@VERSION@', {"requires": ["dom-core", "selector"]}); |
|
14241 YUI.add('node-base', function (Y, NAME) { |
|
14242 |
|
14243 /** |
|
14244 * @module node |
|
14245 * @submodule node-base |
|
14246 */ |
|
14247 |
|
14248 var methods = [ |
|
14249 /** |
|
14250 * Determines whether each node has the given className. |
|
14251 * @method hasClass |
|
14252 * @for Node |
|
14253 * @param {String} className the class name to search for |
|
14254 * @return {Boolean} Whether or not the element has the specified class |
|
14255 */ |
|
14256 'hasClass', |
|
14257 |
|
14258 /** |
|
14259 * Adds a class name to each node. |
|
14260 * @method addClass |
|
14261 * @param {String} className the class name to add to the node's class attribute |
|
14262 * @chainable |
|
14263 */ |
|
14264 'addClass', |
|
14265 |
|
14266 /** |
|
14267 * Removes a class name from each node. |
|
14268 * @method removeClass |
|
14269 * @param {String} className the class name to remove from the node's class attribute |
|
14270 * @chainable |
|
14271 */ |
|
14272 'removeClass', |
|
14273 |
|
14274 /** |
|
14275 * Replace a class with another class for each node. |
|
14276 * If no oldClassName is present, the newClassName is simply added. |
|
14277 * @method replaceClass |
|
14278 * @param {String} oldClassName the class name to be replaced |
|
14279 * @param {String} newClassName the class name that will be replacing the old class name |
|
14280 * @chainable |
|
14281 */ |
|
14282 'replaceClass', |
|
14283 |
|
14284 /** |
|
14285 * If the className exists on the node it is removed, if it doesn't exist it is added. |
|
14286 * @method toggleClass |
|
14287 * @param {String} className the class name to be toggled |
|
14288 * @param {Boolean} force Option to force adding or removing the class. |
|
14289 * @chainable |
|
14290 */ |
|
14291 'toggleClass' |
|
14292 ]; |
|
14293 |
|
14294 Y.Node.importMethod(Y.DOM, methods); |
|
14295 /** |
|
14296 * Determines whether each node has the given className. |
|
14297 * @method hasClass |
|
14298 * @see Node.hasClass |
|
14299 * @for NodeList |
|
14300 * @param {String} className the class name to search for |
|
14301 * @return {Array} An array of booleans for each node bound to the NodeList. |
|
14302 */ |
|
14303 |
|
14304 /** |
|
14305 * Adds a class name to each node. |
|
14306 * @method addClass |
|
14307 * @see Node.addClass |
|
14308 * @param {String} className the class name to add to the node's class attribute |
|
14309 * @chainable |
|
14310 */ |
|
14311 |
|
14312 /** |
|
14313 * Removes a class name from each node. |
|
14314 * @method removeClass |
|
14315 * @see Node.removeClass |
|
14316 * @param {String} className the class name to remove from the node's class attribute |
|
14317 * @chainable |
|
14318 */ |
|
14319 |
|
14320 /** |
|
14321 * Replace a class with another class for each node. |
|
14322 * If no oldClassName is present, the newClassName is simply added. |
|
14323 * @method replaceClass |
|
14324 * @see Node.replaceClass |
|
14325 * @param {String} oldClassName the class name to be replaced |
|
14326 * @param {String} newClassName the class name that will be replacing the old class name |
|
14327 * @chainable |
|
14328 */ |
|
14329 |
|
14330 /** |
|
14331 * If the className exists on the node it is removed, if it doesn't exist it is added. |
|
14332 * @method toggleClass |
|
14333 * @see Node.toggleClass |
|
14334 * @param {String} className the class name to be toggled |
|
14335 * @chainable |
|
14336 */ |
|
14337 Y.NodeList.importMethod(Y.Node.prototype, methods); |
|
14338 /** |
|
14339 * @module node |
|
14340 * @submodule node-base |
|
14341 */ |
|
14342 |
|
14343 var Y_Node = Y.Node, |
|
14344 Y_DOM = Y.DOM; |
|
14345 |
|
14346 /** |
|
14347 * Returns a new dom node using the provided markup string. |
|
14348 * @method create |
|
14349 * @static |
|
14350 * @param {String} html The markup used to create the element |
|
14351 * Use <a href="../classes/Escape.html#method_html">`Y.Escape.html()`</a> |
|
14352 * to escape html content. |
|
14353 * @param {HTMLDocument} doc An optional document context |
|
14354 * @return {Node} A Node instance bound to a DOM node or fragment |
|
14355 * @for Node |
|
14356 */ |
|
14357 Y_Node.create = function(html, doc) { |
|
14358 if (doc && doc._node) { |
|
14359 doc = doc._node; |
|
14360 } |
|
14361 return Y.one(Y_DOM.create(html, doc)); |
|
14362 }; |
|
14363 |
|
14364 Y.mix(Y_Node.prototype, { |
|
14365 /** |
|
14366 * Creates a new Node using the provided markup string. |
|
14367 * @method create |
|
14368 * @param {String} html The markup used to create the element. |
|
14369 * Use <a href="../classes/Escape.html#method_html">`Y.Escape.html()`</a> |
|
14370 * to escape html content. |
|
14371 * @param {HTMLDocument} doc An optional document context |
|
14372 * @return {Node} A Node instance bound to a DOM node or fragment |
|
14373 */ |
|
14374 create: Y_Node.create, |
|
14375 |
|
14376 /** |
|
14377 * Inserts the content before the reference node. |
|
14378 * @method insert |
|
14379 * @param {String | Node | HTMLElement | NodeList | HTMLCollection} content The content to insert |
|
14380 * Use <a href="../classes/Escape.html#method_html">`Y.Escape.html()`</a> |
|
14381 * to escape html content. |
|
14382 * @param {Int | Node | HTMLElement | String} where The position to insert at. |
|
14383 * Possible "where" arguments |
|
14384 * <dl> |
|
14385 * <dt>Y.Node</dt> |
|
14386 * <dd>The Node to insert before</dd> |
|
14387 * <dt>HTMLElement</dt> |
|
14388 * <dd>The element to insert before</dd> |
|
14389 * <dt>Int</dt> |
|
14390 * <dd>The index of the child element to insert before</dd> |
|
14391 * <dt>"replace"</dt> |
|
14392 * <dd>Replaces the existing HTML</dd> |
|
14393 * <dt>"before"</dt> |
|
14394 * <dd>Inserts before the existing HTML</dd> |
|
14395 * <dt>"before"</dt> |
|
14396 * <dd>Inserts content before the node</dd> |
|
14397 * <dt>"after"</dt> |
|
14398 * <dd>Inserts content after the node</dd> |
|
14399 * </dl> |
|
14400 * @chainable |
|
14401 */ |
|
14402 insert: function(content, where) { |
|
14403 this._insert(content, where); |
|
14404 return this; |
|
14405 }, |
|
14406 |
|
14407 _insert: function(content, where) { |
|
14408 var node = this._node, |
|
14409 ret = null; |
|
14410 |
|
14411 if (typeof where == 'number') { // allow index |
|
14412 where = this._node.childNodes[where]; |
|
14413 } else if (where && where._node) { // Node |
|
14414 where = where._node; |
|
14415 } |
|
14416 |
|
14417 if (content && typeof content != 'string') { // allow Node or NodeList/Array instances |
|
14418 content = content._node || content._nodes || content; |
|
14419 } |
|
14420 ret = Y_DOM.addHTML(node, content, where); |
|
14421 |
|
14422 return ret; |
|
14423 }, |
|
14424 |
|
14425 /** |
|
14426 * Inserts the content as the firstChild of the node. |
|
14427 * @method prepend |
|
14428 * @param {String | Node | HTMLElement} content The content to insert |
|
14429 * Use <a href="../classes/Escape.html#method_html">`Y.Escape.html()`</a> |
|
14430 * to escape html content. |
|
14431 * @chainable |
|
14432 */ |
|
14433 prepend: function(content) { |
|
14434 return this.insert(content, 0); |
|
14435 }, |
|
14436 |
|
14437 /** |
|
14438 * Inserts the content as the lastChild of the node. |
|
14439 * @method append |
|
14440 * @param {String | Node | HTMLElement} content The content to insert |
|
14441 * Use <a href="../classes/Escape.html#method_html">`Y.Escape.html()`</a> |
|
14442 * to escape html content. |
|
14443 * @chainable |
|
14444 */ |
|
14445 append: function(content) { |
|
14446 return this.insert(content, null); |
|
14447 }, |
|
14448 |
|
14449 /** |
|
14450 * @method appendChild |
|
14451 * @param {String | HTMLElement | Node} node Node to be appended |
|
14452 * Use <a href="../classes/Escape.html#method_html">`Y.Escape.html()`</a> |
|
14453 * to escape html content. |
|
14454 * @return {Node} The appended node |
|
14455 */ |
|
14456 appendChild: function(node) { |
|
14457 return Y_Node.scrubVal(this._insert(node)); |
|
14458 }, |
|
14459 |
|
14460 /** |
|
14461 * @method insertBefore |
|
14462 * @param {String | HTMLElement | Node} newNode Node to be appended |
|
14463 * @param {HTMLElement | Node} refNode Node to be inserted before |
|
14464 * Use <a href="../classes/Escape.html#method_html">`Y.Escape.html()`</a> |
|
14465 * to escape html content. |
|
14466 * @return {Node} The inserted node |
|
14467 */ |
|
14468 insertBefore: function(newNode, refNode) { |
|
14469 return Y.Node.scrubVal(this._insert(newNode, refNode)); |
|
14470 }, |
|
14471 |
|
14472 /** |
|
14473 * Appends the node to the given node. |
|
14474 * @method appendTo |
|
14475 * @param {Node | HTMLElement} node The node to append to |
|
14476 * @chainable |
|
14477 */ |
|
14478 appendTo: function(node) { |
|
14479 Y.one(node).append(this); |
|
14480 return this; |
|
14481 }, |
|
14482 |
|
14483 /** |
|
14484 * Replaces the node's current content with the content. |
|
14485 * Note that this passes to innerHTML and is not escaped. |
|
14486 * Use <a href="../classes/Escape.html#method_html">`Y.Escape.html()`</a> |
|
14487 * to escape html content or `set('text')` to add as text. |
|
14488 * @method setContent |
|
14489 * @deprecated Use setHTML |
|
14490 * @param {String | Node | HTMLElement | NodeList | HTMLCollection} content The content to insert |
|
14491 * @chainable |
|
14492 */ |
|
14493 setContent: function(content) { |
|
14494 this._insert(content, 'replace'); |
|
14495 return this; |
|
14496 }, |
|
14497 |
|
14498 /** |
|
14499 * Returns the node's current content (e.g. innerHTML) |
|
14500 * @method getContent |
|
14501 * @deprecated Use getHTML |
|
14502 * @return {String} The current content |
|
14503 */ |
|
14504 getContent: function(content) { |
|
14505 return this.get('innerHTML'); |
|
14506 } |
|
14507 }); |
|
14508 |
|
14509 /** |
|
14510 * Replaces the node's current html content with the content provided. |
|
14511 * Note that this passes to innerHTML and is not escaped. |
|
14512 * Use `Y.Escape.html()` to escape HTML, or `set('text')` to add as text. |
|
14513 * @method setHTML |
|
14514 * @param {String | HTML | Node | HTMLElement | NodeList | HTMLCollection} content The content to insert |
|
14515 * @chainable |
|
14516 */ |
|
14517 Y.Node.prototype.setHTML = Y.Node.prototype.setContent; |
|
14518 |
|
14519 /** |
|
14520 * Returns the node's current html content (e.g. innerHTML) |
|
14521 * @method getHTML |
|
14522 * @return {String} The html content |
|
14523 */ |
|
14524 Y.Node.prototype.getHTML = Y.Node.prototype.getContent; |
|
14525 |
|
14526 Y.NodeList.importMethod(Y.Node.prototype, [ |
|
14527 /** |
|
14528 * Called on each Node instance |
|
14529 * @for NodeList |
|
14530 * @method append |
|
14531 * @see Node.append |
|
14532 */ |
|
14533 'append', |
|
14534 |
|
14535 /** |
|
14536 * Called on each Node instance |
|
14537 * @for NodeList |
|
14538 * @method insert |
|
14539 * @see Node.insert |
|
14540 */ |
|
14541 'insert', |
|
14542 |
|
14543 /** |
|
14544 * Called on each Node instance |
|
14545 * @for NodeList |
|
14546 * @method appendChild |
|
14547 * @see Node.appendChild |
|
14548 */ |
|
14549 'appendChild', |
|
14550 |
|
14551 /** |
|
14552 * Called on each Node instance |
|
14553 * @for NodeList |
|
14554 * @method insertBefore |
|
14555 * @see Node.insertBefore |
|
14556 */ |
|
14557 'insertBefore', |
|
14558 |
|
14559 /** |
|
14560 * Called on each Node instance |
|
14561 * @for NodeList |
|
14562 * @method prepend |
|
14563 * @see Node.prepend |
|
14564 */ |
|
14565 'prepend', |
|
14566 |
|
14567 /** |
|
14568 * Called on each Node instance |
|
14569 * Note that this passes to innerHTML and is not escaped. |
|
14570 * Use `Y.Escape.html()` to escape HTML, or `set('text')` to add as text. |
|
14571 * @for NodeList |
|
14572 * @method setContent |
|
14573 * @deprecated Use setHTML |
|
14574 */ |
|
14575 'setContent', |
|
14576 |
|
14577 /** |
|
14578 * Called on each Node instance |
|
14579 * @for NodeList |
|
14580 * @method getContent |
|
14581 * @deprecated Use getHTML |
|
14582 */ |
|
14583 'getContent', |
|
14584 |
|
14585 /** |
|
14586 * Called on each Node instance |
|
14587 * Note that this passes to innerHTML and is not escaped. |
|
14588 * Use `Y.Escape.html()` to escape HTML, or `set('text')` to add as text. |
|
14589 * @for NodeList |
|
14590 * @method setHTML |
|
14591 * @see Node.setHTML |
|
14592 */ |
|
14593 'setHTML', |
|
14594 |
|
14595 /** |
|
14596 * Called on each Node instance |
|
14597 * @for NodeList |
|
14598 * @method getHTML |
|
14599 * @see Node.getHTML |
|
14600 */ |
|
14601 'getHTML' |
|
14602 ]); |
|
14603 /** |
|
14604 * @module node |
|
14605 * @submodule node-base |
|
14606 */ |
|
14607 |
|
14608 var Y_Node = Y.Node, |
|
14609 Y_DOM = Y.DOM; |
|
14610 |
|
14611 /** |
|
14612 * Static collection of configuration attributes for special handling |
|
14613 * @property ATTRS |
|
14614 * @static |
|
14615 * @type object |
|
14616 */ |
|
14617 Y_Node.ATTRS = { |
|
14618 /** |
|
14619 * Allows for getting and setting the text of an element. |
|
14620 * Formatting is preserved and special characters are treated literally. |
|
14621 * @config text |
|
14622 * @type String |
|
14623 */ |
|
14624 text: { |
|
14625 getter: function() { |
|
14626 return Y_DOM.getText(this._node); |
|
14627 }, |
|
14628 |
|
14629 setter: function(content) { |
|
14630 Y_DOM.setText(this._node, content); |
|
14631 return content; |
|
14632 } |
|
14633 }, |
|
14634 |
|
14635 /** |
|
14636 * Allows for getting and setting the text of an element. |
|
14637 * Formatting is preserved and special characters are treated literally. |
|
14638 * @config for |
|
14639 * @type String |
|
14640 */ |
|
14641 'for': { |
|
14642 getter: function() { |
|
14643 return Y_DOM.getAttribute(this._node, 'for'); |
|
14644 }, |
|
14645 |
|
14646 setter: function(val) { |
|
14647 Y_DOM.setAttribute(this._node, 'for', val); |
|
14648 return val; |
|
14649 } |
|
14650 }, |
|
14651 |
|
14652 'options': { |
|
14653 getter: function() { |
|
14654 return this._node.getElementsByTagName('option'); |
|
14655 } |
|
14656 }, |
|
14657 |
|
14658 /** |
|
14659 * Returns a NodeList instance of all HTMLElement children. |
|
14660 * @readOnly |
|
14661 * @config children |
|
14662 * @type NodeList |
|
14663 */ |
|
14664 'children': { |
|
14665 getter: function() { |
|
14666 var node = this._node, |
|
14667 children = node.children, |
|
14668 childNodes, i, len; |
|
14669 |
|
14670 if (!children) { |
|
14671 childNodes = node.childNodes; |
|
14672 children = []; |
|
14673 |
|
14674 for (i = 0, len = childNodes.length; i < len; ++i) { |
|
14675 if (childNodes[i].tagName) { |
|
14676 children[children.length] = childNodes[i]; |
|
14677 } |
|
14678 } |
|
14679 } |
|
14680 return Y.all(children); |
|
14681 } |
|
14682 }, |
|
14683 |
|
14684 value: { |
|
14685 getter: function() { |
|
14686 return Y_DOM.getValue(this._node); |
|
14687 }, |
|
14688 |
|
14689 setter: function(val) { |
|
14690 Y_DOM.setValue(this._node, val); |
|
14691 return val; |
|
14692 } |
|
14693 } |
|
14694 }; |
|
14695 |
|
14696 Y.Node.importMethod(Y.DOM, [ |
|
14697 /** |
|
14698 * Allows setting attributes on DOM nodes, normalizing in some cases. |
|
14699 * This passes through to the DOM node, allowing for custom attributes. |
|
14700 * @method setAttribute |
|
14701 * @for Node |
|
14702 * @for NodeList |
|
14703 * @chainable |
|
14704 * @param {string} name The attribute name |
|
14705 * @param {string} value The value to set |
|
14706 */ |
|
14707 'setAttribute', |
|
14708 /** |
|
14709 * Allows getting attributes on DOM nodes, normalizing in some cases. |
|
14710 * This passes through to the DOM node, allowing for custom attributes. |
|
14711 * @method getAttribute |
|
14712 * @for Node |
|
14713 * @for NodeList |
|
14714 * @param {string} name The attribute name |
|
14715 * @return {string} The attribute value |
|
14716 */ |
|
14717 'getAttribute' |
|
14718 |
|
14719 ]); |
|
14720 /** |
|
14721 * @module node |
|
14722 * @submodule node-base |
|
14723 */ |
|
14724 |
|
14725 var Y_Node = Y.Node; |
|
14726 var Y_NodeList = Y.NodeList; |
|
14727 /** |
|
14728 * List of events that route to DOM events |
|
14729 * @static |
|
14730 * @property DOM_EVENTS |
|
14731 * @for Node |
|
14732 */ |
|
14733 |
|
14734 Y_Node.DOM_EVENTS = { |
|
14735 abort: 1, |
|
14736 beforeunload: 1, |
|
14737 blur: 1, |
|
14738 change: 1, |
|
14739 click: 1, |
|
14740 close: 1, |
|
14741 command: 1, |
|
14742 contextmenu: 1, |
|
14743 dblclick: 1, |
|
14744 DOMMouseScroll: 1, |
|
14745 drag: 1, |
|
14746 dragstart: 1, |
|
14747 dragenter: 1, |
|
14748 dragover: 1, |
|
14749 dragleave: 1, |
|
14750 dragend: 1, |
|
14751 drop: 1, |
|
14752 error: 1, |
|
14753 focus: 1, |
|
14754 key: 1, |
|
14755 keydown: 1, |
|
14756 keypress: 1, |
|
14757 keyup: 1, |
|
14758 load: 1, |
|
14759 message: 1, |
|
14760 mousedown: 1, |
|
14761 mouseenter: 1, |
|
14762 mouseleave: 1, |
|
14763 mousemove: 1, |
|
14764 mousemultiwheel: 1, |
|
14765 mouseout: 1, |
|
14766 mouseover: 1, |
|
14767 mouseup: 1, |
|
14768 mousewheel: 1, |
|
14769 orientationchange: 1, |
|
14770 reset: 1, |
|
14771 resize: 1, |
|
14772 select: 1, |
|
14773 selectstart: 1, |
|
14774 submit: 1, |
|
14775 scroll: 1, |
|
14776 textInput: 1, |
|
14777 unload: 1 |
|
14778 }; |
|
14779 |
|
14780 // Add custom event adaptors to this list. This will make it so |
|
14781 // that delegate, key, available, contentready, etc all will |
|
14782 // be available through Node.on |
|
14783 Y.mix(Y_Node.DOM_EVENTS, Y.Env.evt.plugins); |
|
14784 |
|
14785 Y.augment(Y_Node, Y.EventTarget); |
|
14786 |
|
14787 Y.mix(Y_Node.prototype, { |
|
14788 /** |
|
14789 * Removes event listeners from the node and (optionally) its subtree |
|
14790 * @method purge |
|
14791 * @param {Boolean} recurse (optional) Whether or not to remove listeners from the |
|
14792 * node's subtree |
|
14793 * @param {String} type (optional) Only remove listeners of the specified type |
|
14794 * @chainable |
|
14795 * |
|
14796 */ |
|
14797 purge: function(recurse, type) { |
|
14798 Y.Event.purgeElement(this._node, recurse, type); |
|
14799 return this; |
|
14800 } |
|
14801 |
|
14802 }); |
|
14803 |
|
14804 Y.mix(Y.NodeList.prototype, { |
|
14805 _prepEvtArgs: function(type, fn, context) { |
|
14806 // map to Y.on/after signature (type, fn, nodes, context, arg1, arg2, etc) |
|
14807 var args = Y.Array(arguments, 0, true); |
|
14808 |
|
14809 if (args.length < 2) { // type only (event hash) just add nodes |
|
14810 args[2] = this._nodes; |
|
14811 } else { |
|
14812 args.splice(2, 0, this._nodes); |
|
14813 } |
|
14814 |
|
14815 args[3] = context || this; // default to NodeList instance as context |
|
14816 |
|
14817 return args; |
|
14818 }, |
|
14819 |
|
14820 /** |
|
14821 Subscribe a callback function for each `Node` in the collection to execute |
|
14822 in response to a DOM event. |
|
14823 |
|
14824 NOTE: Generally, the `on()` method should be avoided on `NodeLists`, in |
|
14825 favor of using event delegation from a parent Node. See the Event user |
|
14826 guide for details. |
|
14827 |
|
14828 Most DOM events are associated with a preventable default behavior, such as |
|
14829 link clicks navigating to a new page. Callbacks are passed a |
|
14830 `DOMEventFacade` object as their first argument (usually called `e`) that |
|
14831 can be used to prevent this default behavior with `e.preventDefault()`. See |
|
14832 the `DOMEventFacade` API for all available properties and methods on the |
|
14833 object. |
|
14834 |
|
14835 By default, the `this` object will be the `NodeList` that the subscription |
|
14836 came from, <em>not the `Node` that received the event</em>. Use |
|
14837 `e.currentTarget` to refer to the `Node`. |
|
14838 |
|
14839 Returning `false` from a callback is supported as an alternative to calling |
|
14840 `e.preventDefault(); e.stopPropagation();`. However, it is recommended to |
|
14841 use the event methods. |
|
14842 |
|
14843 @example |
|
14844 |
|
14845 Y.all(".sku").on("keydown", function (e) { |
|
14846 if (e.keyCode === 13) { |
|
14847 e.preventDefault(); |
|
14848 |
|
14849 // Use e.currentTarget to refer to the individual Node |
|
14850 var item = Y.MyApp.searchInventory( e.currentTarget.get('value') ); |
|
14851 // etc ... |
|
14852 } |
|
14853 }); |
|
14854 |
|
14855 @method on |
|
14856 @param {String} type The name of the event |
|
14857 @param {Function} fn The callback to execute in response to the event |
|
14858 @param {Object} [context] Override `this` object in callback |
|
14859 @param {Any} [arg*] 0..n additional arguments to supply to the subscriber |
|
14860 @return {EventHandle} A subscription handle capable of detaching that |
|
14861 subscription |
|
14862 @for NodeList |
|
14863 **/ |
|
14864 on: function(type, fn, context) { |
|
14865 return Y.on.apply(Y, this._prepEvtArgs.apply(this, arguments)); |
|
14866 }, |
|
14867 |
|
14868 /** |
|
14869 * Applies an one-time event listener to each Node bound to the NodeList. |
|
14870 * @method once |
|
14871 * @param {String} type The event being listened for |
|
14872 * @param {Function} fn The handler to call when the event fires |
|
14873 * @param {Object} context The context to call the handler with. |
|
14874 * Default is the NodeList instance. |
|
14875 * @return {EventHandle} A subscription handle capable of detaching that |
|
14876 * subscription |
|
14877 * @for NodeList |
|
14878 */ |
|
14879 once: function(type, fn, context) { |
|
14880 return Y.once.apply(Y, this._prepEvtArgs.apply(this, arguments)); |
|
14881 }, |
|
14882 |
|
14883 /** |
|
14884 * Applies an event listener to each Node bound to the NodeList. |
|
14885 * The handler is called only after all on() handlers are called |
|
14886 * and the event is not prevented. |
|
14887 * @method after |
|
14888 * @param {String} type The event being listened for |
|
14889 * @param {Function} fn The handler to call when the event fires |
|
14890 * @param {Object} context The context to call the handler with. |
|
14891 * Default is the NodeList instance. |
|
14892 * @return {EventHandle} A subscription handle capable of detaching that |
|
14893 * subscription |
|
14894 * @for NodeList |
|
14895 */ |
|
14896 after: function(type, fn, context) { |
|
14897 return Y.after.apply(Y, this._prepEvtArgs.apply(this, arguments)); |
|
14898 }, |
|
14899 |
|
14900 /** |
|
14901 * Applies an one-time event listener to each Node bound to the NodeList |
|
14902 * that will be called only after all on() handlers are called and the |
|
14903 * event is not prevented. |
|
14904 * |
|
14905 * @method onceAfter |
|
14906 * @param {String} type The event being listened for |
|
14907 * @param {Function} fn The handler to call when the event fires |
|
14908 * @param {Object} context The context to call the handler with. |
|
14909 * Default is the NodeList instance. |
|
14910 * @return {EventHandle} A subscription handle capable of detaching that |
|
14911 * subscription |
|
14912 * @for NodeList |
|
14913 */ |
|
14914 onceAfter: function(type, fn, context) { |
|
14915 return Y.onceAfter.apply(Y, this._prepEvtArgs.apply(this, arguments)); |
|
14916 } |
|
14917 }); |
|
14918 |
|
14919 Y_NodeList.importMethod(Y.Node.prototype, [ |
|
14920 /** |
|
14921 * Called on each Node instance |
|
14922 * @method detach |
|
14923 * @see Node.detach |
|
14924 * @for NodeList |
|
14925 */ |
|
14926 'detach', |
|
14927 |
|
14928 /** Called on each Node instance |
|
14929 * @method detachAll |
|
14930 * @see Node.detachAll |
|
14931 * @for NodeList |
|
14932 */ |
|
14933 'detachAll' |
|
14934 ]); |
|
14935 |
|
14936 /** |
|
14937 Subscribe a callback function to execute in response to a DOM event or custom |
|
14938 event. |
|
14939 |
|
14940 Most DOM events are associated with a preventable default behavior such as |
|
14941 link clicks navigating to a new page. Callbacks are passed a `DOMEventFacade` |
|
14942 object as their first argument (usually called `e`) that can be used to |
|
14943 prevent this default behavior with `e.preventDefault()`. See the |
|
14944 `DOMEventFacade` API for all available properties and methods on the object. |
|
14945 |
|
14946 If the event name passed as the first parameter is not a whitelisted DOM event, |
|
14947 it will be treated as a custom event subscriptions, allowing |
|
14948 `node.fire('customEventName')` later in the code. Refer to the Event user guide |
|
14949 for the full DOM event whitelist. |
|
14950 |
|
14951 By default, the `this` object in the callback will refer to the subscribed |
|
14952 `Node`. |
|
14953 |
|
14954 Returning `false` from a callback is supported as an alternative to calling |
|
14955 `e.preventDefault(); e.stopPropagation();`. However, it is recommended to use |
|
14956 the event methods. |
|
14957 |
|
14958 @example |
|
14959 |
|
14960 Y.one("#my-form").on("submit", function (e) { |
|
14961 e.preventDefault(); |
|
14962 |
|
14963 // proceed with ajax form submission instead... |
|
14964 }); |
|
14965 |
|
14966 @method on |
|
14967 @param {String} type The name of the event |
|
14968 @param {Function} fn The callback to execute in response to the event |
|
14969 @param {Object} [context] Override `this` object in callback |
|
14970 @param {Any} [arg*] 0..n additional arguments to supply to the subscriber |
|
14971 @return {EventHandle} A subscription handle capable of detaching that |
|
14972 subscription |
|
14973 @for Node |
|
14974 **/ |
|
14975 |
|
14976 Y.mix(Y.Node.ATTRS, { |
|
14977 offsetHeight: { |
|
14978 setter: function(h) { |
|
14979 Y.DOM.setHeight(this._node, h); |
|
14980 return h; |
|
14981 }, |
|
14982 |
|
14983 getter: function() { |
|
14984 return this._node.offsetHeight; |
|
14985 } |
|
14986 }, |
|
14987 |
|
14988 offsetWidth: { |
|
14989 setter: function(w) { |
|
14990 Y.DOM.setWidth(this._node, w); |
|
14991 return w; |
|
14992 }, |
|
14993 |
|
14994 getter: function() { |
|
14995 return this._node.offsetWidth; |
|
14996 } |
|
14997 } |
|
14998 }); |
|
14999 |
|
15000 Y.mix(Y.Node.prototype, { |
|
15001 sizeTo: function(w, h) { |
|
15002 var node; |
|
15003 if (arguments.length < 2) { |
|
15004 node = Y.one(w); |
|
15005 w = node.get('offsetWidth'); |
|
15006 h = node.get('offsetHeight'); |
|
15007 } |
|
15008 |
|
15009 this.setAttrs({ |
|
15010 offsetWidth: w, |
|
15011 offsetHeight: h |
|
15012 }); |
|
15013 } |
|
15014 }); |
|
15015 /** |
|
15016 * @module node |
|
15017 * @submodule node-base |
|
15018 */ |
|
15019 |
|
15020 var Y_Node = Y.Node; |
|
15021 |
|
15022 Y.mix(Y_Node.prototype, { |
|
15023 /** |
|
15024 * Makes the node visible. |
|
15025 * If the "transition" module is loaded, show optionally |
|
15026 * animates the showing of the node using either the default |
|
15027 * transition effect ('fadeIn'), or the given named effect. |
|
15028 * @method show |
|
15029 * @for Node |
|
15030 * @param {String} name A named Transition effect to use as the show effect. |
|
15031 * @param {Object} config Options to use with the transition. |
|
15032 * @param {Function} callback An optional function to run after the transition completes. |
|
15033 * @chainable |
|
15034 */ |
|
15035 show: function(callback) { |
|
15036 callback = arguments[arguments.length - 1]; |
|
15037 this.toggleView(true, callback); |
|
15038 return this; |
|
15039 }, |
|
15040 |
|
15041 /** |
|
15042 * The implementation for showing nodes. |
|
15043 * Default is to remove the hidden attribute and reset the CSS style.display property. |
|
15044 * @method _show |
|
15045 * @protected |
|
15046 * @chainable |
|
15047 */ |
|
15048 _show: function() { |
|
15049 this.removeAttribute('hidden'); |
|
15050 |
|
15051 // For back-compat we need to leave this in for browsers that |
|
15052 // do not visually hide a node via the hidden attribute |
|
15053 // and for users that check visibility based on style display. |
|
15054 this.setStyle('display', ''); |
|
15055 |
|
15056 }, |
|
15057 |
|
15058 _isHidden: function() { |
|
15059 return Y.DOM.getAttribute(this._node, 'hidden') === 'true'; |
|
15060 }, |
|
15061 |
|
15062 /** |
|
15063 * Displays or hides the node. |
|
15064 * If the "transition" module is loaded, toggleView optionally |
|
15065 * animates the toggling of the node using given named effect. |
|
15066 * @method toggleView |
|
15067 * @for Node |
|
15068 * @param {String} [name] An optional string value to use as transition effect. |
|
15069 * @param {Boolean} [on] An optional boolean value to force the node to be shown or hidden |
|
15070 * @param {Function} [callback] An optional function to run after the transition completes. |
|
15071 * @chainable |
|
15072 */ |
|
15073 toggleView: function(on, callback) { |
|
15074 this._toggleView.apply(this, arguments); |
|
15075 return this; |
|
15076 }, |
|
15077 |
|
15078 _toggleView: function(on, callback) { |
|
15079 callback = arguments[arguments.length - 1]; |
|
15080 |
|
15081 // base on current state if not forcing |
|
15082 if (typeof on != 'boolean') { |
|
15083 on = (this._isHidden()) ? 1 : 0; |
|
15084 } |
|
15085 |
|
15086 if (on) { |
|
15087 this._show(); |
|
15088 } else { |
|
15089 this._hide(); |
|
15090 } |
|
15091 |
|
15092 if (typeof callback == 'function') { |
|
15093 callback.call(this); |
|
15094 } |
|
15095 |
|
15096 return this; |
|
15097 }, |
|
15098 |
|
15099 /** |
|
15100 * Hides the node. |
|
15101 * If the "transition" module is loaded, hide optionally |
|
15102 * animates the hiding of the node using either the default |
|
15103 * transition effect ('fadeOut'), or the given named effect. |
|
15104 * @method hide |
|
15105 * @param {String} name A named Transition effect to use as the show effect. |
|
15106 * @param {Object} config Options to use with the transition. |
|
15107 * @param {Function} callback An optional function to run after the transition completes. |
|
15108 * @chainable |
|
15109 */ |
|
15110 hide: function(callback) { |
|
15111 callback = arguments[arguments.length - 1]; |
|
15112 this.toggleView(false, callback); |
|
15113 return this; |
|
15114 }, |
|
15115 |
|
15116 /** |
|
15117 * The implementation for hiding nodes. |
|
15118 * Default is to set the hidden attribute to true and set the CSS style.display to 'none'. |
|
15119 * @method _hide |
|
15120 * @protected |
|
15121 * @chainable |
|
15122 */ |
|
15123 _hide: function() { |
|
15124 this.setAttribute('hidden', true); |
|
15125 |
|
15126 // For back-compat we need to leave this in for browsers that |
|
15127 // do not visually hide a node via the hidden attribute |
|
15128 // and for users that check visibility based on style display. |
|
15129 this.setStyle('display', 'none'); |
|
15130 } |
|
15131 }); |
|
15132 |
|
15133 Y.NodeList.importMethod(Y.Node.prototype, [ |
|
15134 /** |
|
15135 * Makes each node visible. |
|
15136 * If the "transition" module is loaded, show optionally |
|
15137 * animates the showing of the node using either the default |
|
15138 * transition effect ('fadeIn'), or the given named effect. |
|
15139 * @method show |
|
15140 * @param {String} name A named Transition effect to use as the show effect. |
|
15141 * @param {Object} config Options to use with the transition. |
|
15142 * @param {Function} callback An optional function to run after the transition completes. |
|
15143 * @for NodeList |
|
15144 * @chainable |
|
15145 */ |
|
15146 'show', |
|
15147 |
|
15148 /** |
|
15149 * Hides each node. |
|
15150 * If the "transition" module is loaded, hide optionally |
|
15151 * animates the hiding of the node using either the default |
|
15152 * transition effect ('fadeOut'), or the given named effect. |
|
15153 * @method hide |
|
15154 * @param {String} name A named Transition effect to use as the show effect. |
|
15155 * @param {Object} config Options to use with the transition. |
|
15156 * @param {Function} callback An optional function to run after the transition completes. |
|
15157 * @chainable |
|
15158 */ |
|
15159 'hide', |
|
15160 |
|
15161 /** |
|
15162 * Displays or hides each node. |
|
15163 * If the "transition" module is loaded, toggleView optionally |
|
15164 * animates the toggling of the nodes using given named effect. |
|
15165 * @method toggleView |
|
15166 * @param {String} [name] An optional string value to use as transition effect. |
|
15167 * @param {Boolean} [on] An optional boolean value to force the nodes to be shown or hidden |
|
15168 * @param {Function} [callback] An optional function to run after the transition completes. |
|
15169 * @chainable |
|
15170 */ |
|
15171 'toggleView' |
|
15172 ]); |
|
15173 |
|
15174 if (!Y.config.doc.documentElement.hasAttribute) { // IE < 8 |
|
15175 Y.Node.prototype.hasAttribute = function(attr) { |
|
15176 if (attr === 'value') { |
|
15177 if (this.get('value') !== "") { // IE < 8 fails to populate specified when set in HTML |
|
15178 return true; |
|
15179 } |
|
15180 } |
|
15181 return !!(this._node.attributes[attr] && |
|
15182 this._node.attributes[attr].specified); |
|
15183 }; |
|
15184 } |
|
15185 |
|
15186 // IE throws an error when calling focus() on an element that's invisible, not |
|
15187 // displayed, or disabled. |
|
15188 Y.Node.prototype.focus = function () { |
|
15189 try { |
|
15190 this._node.focus(); |
|
15191 } catch (e) { |
|
15192 } |
|
15193 |
|
15194 return this; |
|
15195 }; |
|
15196 |
|
15197 // IE throws error when setting input.type = 'hidden', |
|
15198 // input.setAttribute('type', 'hidden') and input.attributes.type.value = 'hidden' |
|
15199 Y.Node.ATTRS.type = { |
|
15200 setter: function(val) { |
|
15201 if (val === 'hidden') { |
|
15202 try { |
|
15203 this._node.type = 'hidden'; |
|
15204 } catch(e) { |
|
15205 this.setStyle('display', 'none'); |
|
15206 this._inputType = 'hidden'; |
|
15207 } |
|
15208 } else { |
|
15209 try { // IE errors when changing the type from "hidden' |
|
15210 this._node.type = val; |
|
15211 } catch (e) { |
|
15212 } |
|
15213 } |
|
15214 return val; |
|
15215 }, |
|
15216 |
|
15217 getter: function() { |
|
15218 return this._inputType || this._node.type; |
|
15219 }, |
|
15220 |
|
15221 _bypassProxy: true // don't update DOM when using with Attribute |
|
15222 }; |
|
15223 |
|
15224 if (Y.config.doc.createElement('form').elements.nodeType) { |
|
15225 // IE: elements collection is also FORM node which trips up scrubVal. |
|
15226 Y.Node.ATTRS.elements = { |
|
15227 getter: function() { |
|
15228 return this.all('input, textarea, button, select'); |
|
15229 } |
|
15230 }; |
|
15231 } |
|
15232 |
|
15233 /** |
|
15234 * Provides methods for managing custom Node data. |
|
15235 * |
|
15236 * @module node |
|
15237 * @main node |
|
15238 * @submodule node-data |
|
15239 */ |
|
15240 |
|
15241 Y.mix(Y.Node.prototype, { |
|
15242 _initData: function() { |
|
15243 if (! ('_data' in this)) { |
|
15244 this._data = {}; |
|
15245 } |
|
15246 }, |
|
15247 |
|
15248 /** |
|
15249 * @method getData |
|
15250 * @for Node |
|
15251 * @description Retrieves arbitrary data stored on a Node instance. |
|
15252 * If no data is associated with the Node, it will attempt to retrieve |
|
15253 * a value from the corresponding HTML data attribute. (e.g. node.getData('foo') |
|
15254 * will check node.getAttribute('data-foo')). |
|
15255 * @param {string} name Optional name of the data field to retrieve. |
|
15256 * If no name is given, all data is returned. |
|
15257 * @return {any | Object} Whatever is stored at the given field, |
|
15258 * or an object hash of all fields. |
|
15259 */ |
|
15260 getData: function(name) { |
|
15261 this._initData(); |
|
15262 var data = this._data, |
|
15263 ret = data; |
|
15264 |
|
15265 if (arguments.length) { // single field |
|
15266 if (name in data) { |
|
15267 ret = data[name]; |
|
15268 } else { // initialize from HTML attribute |
|
15269 ret = this._getDataAttribute(name); |
|
15270 } |
|
15271 } else if (typeof data == 'object' && data !== null) { // all fields |
|
15272 ret = {}; |
|
15273 Y.Object.each(data, function(v, n) { |
|
15274 ret[n] = v; |
|
15275 }); |
|
15276 |
|
15277 ret = this._getDataAttributes(ret); |
|
15278 } |
|
15279 |
|
15280 return ret; |
|
15281 |
|
15282 }, |
|
15283 |
|
15284 _getDataAttributes: function(ret) { |
|
15285 ret = ret || {}; |
|
15286 var i = 0, |
|
15287 attrs = this._node.attributes, |
|
15288 len = attrs.length, |
|
15289 prefix = this.DATA_PREFIX, |
|
15290 prefixLength = prefix.length, |
|
15291 name; |
|
15292 |
|
15293 while (i < len) { |
|
15294 name = attrs[i].name; |
|
15295 if (name.indexOf(prefix) === 0) { |
|
15296 name = name.substr(prefixLength); |
|
15297 if (!(name in ret)) { // only merge if not already stored |
|
15298 ret[name] = this._getDataAttribute(name); |
|
15299 } |
|
15300 } |
|
15301 |
|
15302 i += 1; |
|
15303 } |
|
15304 |
|
15305 return ret; |
|
15306 }, |
|
15307 |
|
15308 _getDataAttribute: function(name) { |
|
15309 name = this.DATA_PREFIX + name; |
|
15310 |
|
15311 var node = this._node, |
|
15312 attrs = node.attributes, |
|
15313 data = attrs && attrs[name] && attrs[name].value; |
|
15314 |
|
15315 return data; |
|
15316 }, |
|
15317 |
|
15318 /** |
|
15319 * @method setData |
|
15320 * @for Node |
|
15321 * @description Stores arbitrary data on a Node instance. |
|
15322 * This is not stored with the DOM node. |
|
15323 * @param {string} name The name of the field to set. If no val |
|
15324 * is given, name is treated as the data and overrides any existing data. |
|
15325 * @param {any} val The value to be assigned to the field. |
|
15326 * @chainable |
|
15327 */ |
|
15328 setData: function(name, val) { |
|
15329 this._initData(); |
|
15330 if (arguments.length > 1) { |
|
15331 this._data[name] = val; |
|
15332 } else { |
|
15333 this._data = name; |
|
15334 } |
|
15335 |
|
15336 return this; |
|
15337 }, |
|
15338 |
|
15339 /** |
|
15340 * @method clearData |
|
15341 * @for Node |
|
15342 * @description Clears internally stored data. |
|
15343 * @param {string} name The name of the field to clear. If no name |
|
15344 * is given, all data is cleared. |
|
15345 * @chainable |
|
15346 */ |
|
15347 clearData: function(name) { |
|
15348 if ('_data' in this) { |
|
15349 if (typeof name != 'undefined') { |
|
15350 delete this._data[name]; |
|
15351 } else { |
|
15352 delete this._data; |
|
15353 } |
|
15354 } |
|
15355 |
|
15356 return this; |
|
15357 } |
|
15358 }); |
|
15359 |
|
15360 Y.mix(Y.NodeList.prototype, { |
|
15361 /** |
|
15362 * @method getData |
|
15363 * @for NodeList |
|
15364 * @description Retrieves arbitrary data stored on each Node instance |
|
15365 * bound to the NodeList. |
|
15366 * @see Node |
|
15367 * @param {string} name Optional name of the data field to retrieve. |
|
15368 * If no name is given, all data is returned. |
|
15369 * @return {Array} An array containing all of the data for each Node instance. |
|
15370 * or an object hash of all fields. |
|
15371 */ |
|
15372 getData: function(name) { |
|
15373 var args = (arguments.length) ? [name] : []; |
|
15374 return this._invoke('getData', args, true); |
|
15375 }, |
|
15376 |
|
15377 /** |
|
15378 * @method setData |
|
15379 * @for NodeList |
|
15380 * @description Stores arbitrary data on each Node instance bound to the |
|
15381 * NodeList. This is not stored with the DOM node. |
|
15382 * @param {string} name The name of the field to set. If no name |
|
15383 * is given, name is treated as the data and overrides any existing data. |
|
15384 * @param {any} val The value to be assigned to the field. |
|
15385 * @chainable |
|
15386 */ |
|
15387 setData: function(name, val) { |
|
15388 var args = (arguments.length > 1) ? [name, val] : [name]; |
|
15389 return this._invoke('setData', args); |
|
15390 }, |
|
15391 |
|
15392 /** |
|
15393 * @method clearData |
|
15394 * @for NodeList |
|
15395 * @description Clears data on all Node instances bound to the NodeList. |
|
15396 * @param {string} name The name of the field to clear. If no name |
|
15397 * is given, all data is cleared. |
|
15398 * @chainable |
|
15399 */ |
|
15400 clearData: function(name) { |
|
15401 var args = (arguments.length) ? [name] : []; |
|
15402 return this._invoke('clearData', [name]); |
|
15403 } |
|
15404 }); |
|
15405 |
|
15406 |
|
15407 }, '@VERSION@', {"requires": ["event-base", "node-core", "dom-base"]}); |
|
15408 (function () { |
|
15409 var GLOBAL_ENV = YUI.Env; |
|
15410 |
|
15411 if (!GLOBAL_ENV._ready) { |
|
15412 GLOBAL_ENV._ready = function() { |
|
15413 GLOBAL_ENV.DOMReady = true; |
|
15414 GLOBAL_ENV.remove(YUI.config.doc, 'DOMContentLoaded', GLOBAL_ENV._ready); |
|
15415 }; |
|
15416 |
|
15417 GLOBAL_ENV.add(YUI.config.doc, 'DOMContentLoaded', GLOBAL_ENV._ready); |
|
15418 } |
|
15419 })(); |
|
15420 YUI.add('event-base', function (Y, NAME) { |
|
15421 |
|
15422 /* |
|
15423 * DOM event listener abstraction layer |
|
15424 * @module event |
|
15425 * @submodule event-base |
|
15426 */ |
|
15427 |
|
15428 /** |
|
15429 * The domready event fires at the moment the browser's DOM is |
|
15430 * usable. In most cases, this is before images are fully |
|
15431 * downloaded, allowing you to provide a more responsive user |
|
15432 * interface. |
|
15433 * |
|
15434 * In YUI 3, domready subscribers will be notified immediately if |
|
15435 * that moment has already passed when the subscription is created. |
|
15436 * |
|
15437 * One exception is if the yui.js file is dynamically injected into |
|
15438 * the page. If this is done, you must tell the YUI instance that |
|
15439 * you did this in order for DOMReady (and window load events) to |
|
15440 * fire normally. That configuration option is 'injected' -- set |
|
15441 * it to true if the yui.js script is not included inline. |
|
15442 * |
|
15443 * This method is part of the 'event-ready' module, which is a |
|
15444 * submodule of 'event'. |
|
15445 * |
|
15446 * @event domready |
|
15447 * @for YUI |
|
15448 */ |
|
15449 Y.publish('domready', { |
|
15450 fireOnce: true, |
|
15451 async: true |
|
15452 }); |
|
15453 |
|
15454 if (YUI.Env.DOMReady) { |
|
15455 Y.fire('domready'); |
|
15456 } else { |
|
15457 Y.Do.before(function() { Y.fire('domready'); }, YUI.Env, '_ready'); |
|
15458 } |
|
15459 |
|
15460 /** |
|
15461 * Custom event engine, DOM event listener abstraction layer, synthetic DOM |
|
15462 * events. |
|
15463 * @module event |
|
15464 * @submodule event-base |
|
15465 */ |
|
15466 |
|
15467 /** |
|
15468 * Wraps a DOM event, properties requiring browser abstraction are |
|
15469 * fixed here. Provids a security layer when required. |
|
15470 * @class DOMEventFacade |
|
15471 * @param ev {Event} the DOM event |
|
15472 * @param currentTarget {HTMLElement} the element the listener was attached to |
|
15473 * @param wrapper {Event.Custom} the custom event wrapper for this DOM event |
|
15474 */ |
|
15475 |
|
15476 var ua = Y.UA, |
|
15477 |
|
15478 EMPTY = {}, |
|
15479 |
|
15480 /** |
|
15481 * webkit key remapping required for Safari < 3.1 |
|
15482 * @property webkitKeymap |
|
15483 * @private |
|
15484 */ |
|
15485 webkitKeymap = { |
|
15486 63232: 38, // up |
|
15487 63233: 40, // down |
|
15488 63234: 37, // left |
|
15489 63235: 39, // right |
|
15490 63276: 33, // page up |
|
15491 63277: 34, // page down |
|
15492 25: 9, // SHIFT-TAB (Safari provides a different key code in |
|
15493 // this case, even though the shiftKey modifier is set) |
|
15494 63272: 46, // delete |
|
15495 63273: 36, // home |
|
15496 63275: 35 // end |
|
15497 }, |
|
15498 |
|
15499 /** |
|
15500 * Returns a wrapped node. Intended to be used on event targets, |
|
15501 * so it will return the node's parent if the target is a text |
|
15502 * node. |
|
15503 * |
|
15504 * If accessing a property of the node throws an error, this is |
|
15505 * probably the anonymous div wrapper Gecko adds inside text |
|
15506 * nodes. This likely will only occur when attempting to access |
|
15507 * the relatedTarget. In this case, we now return null because |
|
15508 * the anonymous div is completely useless and we do not know |
|
15509 * what the related target was because we can't even get to |
|
15510 * the element's parent node. |
|
15511 * |
|
15512 * @method resolve |
|
15513 * @private |
|
15514 */ |
|
15515 resolve = function(n) { |
|
15516 if (!n) { |
|
15517 return n; |
|
15518 } |
|
15519 try { |
|
15520 if (n && 3 == n.nodeType) { |
|
15521 n = n.parentNode; |
|
15522 } |
|
15523 } catch(e) { |
|
15524 return null; |
|
15525 } |
|
15526 |
|
15527 return Y.one(n); |
|
15528 }, |
|
15529 |
|
15530 DOMEventFacade = function(ev, currentTarget, wrapper) { |
|
15531 this._event = ev; |
|
15532 this._currentTarget = currentTarget; |
|
15533 this._wrapper = wrapper || EMPTY; |
|
15534 |
|
15535 // if not lazy init |
|
15536 this.init(); |
|
15537 }; |
|
15538 |
|
15539 Y.extend(DOMEventFacade, Object, { |
|
15540 |
|
15541 init: function() { |
|
15542 |
|
15543 var e = this._event, |
|
15544 overrides = this._wrapper.overrides, |
|
15545 x = e.pageX, |
|
15546 y = e.pageY, |
|
15547 c, |
|
15548 currentTarget = this._currentTarget; |
|
15549 |
|
15550 this.altKey = e.altKey; |
|
15551 this.ctrlKey = e.ctrlKey; |
|
15552 this.metaKey = e.metaKey; |
|
15553 this.shiftKey = e.shiftKey; |
|
15554 this.type = (overrides && overrides.type) || e.type; |
|
15555 this.clientX = e.clientX; |
|
15556 this.clientY = e.clientY; |
|
15557 |
|
15558 this.pageX = x; |
|
15559 this.pageY = y; |
|
15560 |
|
15561 // charCode is unknown in keyup, keydown. keyCode is unknown in keypress. |
|
15562 // FF 3.6 - 8+? pass 0 for keyCode in keypress events. |
|
15563 // Webkit, FF 3.6-8+?, and IE9+? pass 0 for charCode in keydown, keyup. |
|
15564 // Webkit and IE9+? duplicate charCode in keyCode. |
|
15565 // Opera never sets charCode, always keyCode (though with the charCode). |
|
15566 // IE6-8 don't set charCode or which. |
|
15567 // All browsers other than IE6-8 set which=keyCode in keydown, keyup, and |
|
15568 // which=charCode in keypress. |
|
15569 // |
|
15570 // Moral of the story: (e.which || e.keyCode) will always return the |
|
15571 // known code for that key event phase. e.keyCode is often different in |
|
15572 // keypress from keydown and keyup. |
|
15573 c = e.keyCode || e.charCode; |
|
15574 |
|
15575 if (ua.webkit && (c in webkitKeymap)) { |
|
15576 c = webkitKeymap[c]; |
|
15577 } |
|
15578 |
|
15579 this.keyCode = c; |
|
15580 this.charCode = c; |
|
15581 // Fill in e.which for IE - implementers should always use this over |
|
15582 // e.keyCode or e.charCode. |
|
15583 this.which = e.which || e.charCode || c; |
|
15584 // this.button = e.button; |
|
15585 this.button = this.which; |
|
15586 |
|
15587 this.target = resolve(e.target); |
|
15588 this.currentTarget = resolve(currentTarget); |
|
15589 this.relatedTarget = resolve(e.relatedTarget); |
|
15590 |
|
15591 if (e.type == "mousewheel" || e.type == "DOMMouseScroll") { |
|
15592 this.wheelDelta = (e.detail) ? (e.detail * -1) : Math.round(e.wheelDelta / 80) || ((e.wheelDelta < 0) ? -1 : 1); |
|
15593 } |
|
15594 |
|
15595 if (this._touch) { |
|
15596 this._touch(e, currentTarget, this._wrapper); |
|
15597 } |
|
15598 }, |
|
15599 |
|
15600 stopPropagation: function() { |
|
15601 this._event.stopPropagation(); |
|
15602 this._wrapper.stopped = 1; |
|
15603 this.stopped = 1; |
|
15604 }, |
|
15605 |
|
15606 stopImmediatePropagation: function() { |
|
15607 var e = this._event; |
|
15608 if (e.stopImmediatePropagation) { |
|
15609 e.stopImmediatePropagation(); |
|
15610 } else { |
|
15611 this.stopPropagation(); |
|
15612 } |
|
15613 this._wrapper.stopped = 2; |
|
15614 this.stopped = 2; |
|
15615 }, |
|
15616 |
|
15617 preventDefault: function(returnValue) { |
|
15618 var e = this._event; |
|
15619 e.preventDefault(); |
|
15620 e.returnValue = returnValue || false; |
|
15621 this._wrapper.prevented = 1; |
|
15622 this.prevented = 1; |
|
15623 }, |
|
15624 |
|
15625 halt: function(immediate) { |
|
15626 if (immediate) { |
|
15627 this.stopImmediatePropagation(); |
|
15628 } else { |
|
15629 this.stopPropagation(); |
|
15630 } |
|
15631 |
|
15632 this.preventDefault(); |
|
15633 } |
|
15634 |
|
15635 }); |
|
15636 |
|
15637 DOMEventFacade.resolve = resolve; |
|
15638 Y.DOM2EventFacade = DOMEventFacade; |
|
15639 Y.DOMEventFacade = DOMEventFacade; |
|
15640 |
|
15641 /** |
|
15642 * The native event |
|
15643 * @property _event |
|
15644 * @type {Native DOM Event} |
|
15645 * @private |
|
15646 */ |
|
15647 |
|
15648 /** |
|
15649 The name of the event (e.g. "click") |
|
15650 |
|
15651 @property type |
|
15652 @type {String} |
|
15653 **/ |
|
15654 |
|
15655 /** |
|
15656 `true` if the "alt" or "option" key is pressed. |
|
15657 |
|
15658 @property altKey |
|
15659 @type {Boolean} |
|
15660 **/ |
|
15661 |
|
15662 /** |
|
15663 `true` if the shift key is pressed. |
|
15664 |
|
15665 @property shiftKey |
|
15666 @type {Boolean} |
|
15667 **/ |
|
15668 |
|
15669 /** |
|
15670 `true` if the "Windows" key on a Windows keyboard, "command" key on an |
|
15671 Apple keyboard, or "meta" key on other keyboards is pressed. |
|
15672 |
|
15673 @property metaKey |
|
15674 @type {Boolean} |
|
15675 **/ |
|
15676 |
|
15677 /** |
|
15678 `true` if the "Ctrl" or "control" key is pressed. |
|
15679 |
|
15680 @property ctrlKey |
|
15681 @type {Boolean} |
|
15682 **/ |
|
15683 |
|
15684 /** |
|
15685 * The X location of the event on the page (including scroll) |
|
15686 * @property pageX |
|
15687 * @type {Number} |
|
15688 */ |
|
15689 |
|
15690 /** |
|
15691 * The Y location of the event on the page (including scroll) |
|
15692 * @property pageY |
|
15693 * @type {Number} |
|
15694 */ |
|
15695 |
|
15696 /** |
|
15697 * The X location of the event in the viewport |
|
15698 * @property clientX |
|
15699 * @type {Number} |
|
15700 */ |
|
15701 |
|
15702 /** |
|
15703 * The Y location of the event in the viewport |
|
15704 * @property clientY |
|
15705 * @type {Number} |
|
15706 */ |
|
15707 |
|
15708 /** |
|
15709 * The keyCode for key events. Uses charCode if keyCode is not available |
|
15710 * @property keyCode |
|
15711 * @type {Number} |
|
15712 */ |
|
15713 |
|
15714 /** |
|
15715 * The charCode for key events. Same as keyCode |
|
15716 * @property charCode |
|
15717 * @type {Number} |
|
15718 */ |
|
15719 |
|
15720 /** |
|
15721 * The button that was pushed. 1 for left click, 2 for middle click, 3 for |
|
15722 * right click. This is only reliably populated on `mouseup` events. |
|
15723 * @property button |
|
15724 * @type {Number} |
|
15725 */ |
|
15726 |
|
15727 /** |
|
15728 * The button that was pushed. Same as button. |
|
15729 * @property which |
|
15730 * @type {Number} |
|
15731 */ |
|
15732 |
|
15733 /** |
|
15734 * Node reference for the targeted element |
|
15735 * @property target |
|
15736 * @type {Node} |
|
15737 */ |
|
15738 |
|
15739 /** |
|
15740 * Node reference for the element that the listener was attached to. |
|
15741 * @property currentTarget |
|
15742 * @type {Node} |
|
15743 */ |
|
15744 |
|
15745 /** |
|
15746 * Node reference to the relatedTarget |
|
15747 * @property relatedTarget |
|
15748 * @type {Node} |
|
15749 */ |
|
15750 |
|
15751 /** |
|
15752 * Number representing the direction and velocity of the movement of the mousewheel. |
|
15753 * Negative is down, the higher the number, the faster. Applies to the mousewheel event. |
|
15754 * @property wheelDelta |
|
15755 * @type {Number} |
|
15756 */ |
|
15757 |
|
15758 /** |
|
15759 * Stops the propagation to the next bubble target |
|
15760 * @method stopPropagation |
|
15761 */ |
|
15762 |
|
15763 /** |
|
15764 * Stops the propagation to the next bubble target and |
|
15765 * prevents any additional listeners from being exectued |
|
15766 * on the current target. |
|
15767 * @method stopImmediatePropagation |
|
15768 */ |
|
15769 |
|
15770 /** |
|
15771 * Prevents the event's default behavior |
|
15772 * @method preventDefault |
|
15773 * @param returnValue {string} sets the returnValue of the event to this value |
|
15774 * (rather than the default false value). This can be used to add a customized |
|
15775 * confirmation query to the beforeunload event). |
|
15776 */ |
|
15777 |
|
15778 /** |
|
15779 * Stops the event propagation and prevents the default |
|
15780 * event behavior. |
|
15781 * @method halt |
|
15782 * @param immediate {boolean} if true additional listeners |
|
15783 * on the current target will not be executed |
|
15784 */ |
|
15785 (function() { |
|
15786 |
|
15787 /** |
|
15788 * The event utility provides functions to add and remove event listeners, |
|
15789 * event cleansing. It also tries to automatically remove listeners it |
|
15790 * registers during the unload event. |
|
15791 * @module event |
|
15792 * @main event |
|
15793 * @submodule event-base |
|
15794 */ |
|
15795 |
|
15796 /** |
|
15797 * The event utility provides functions to add and remove event listeners, |
|
15798 * event cleansing. It also tries to automatically remove listeners it |
|
15799 * registers during the unload event. |
|
15800 * |
|
15801 * @class Event |
|
15802 * @static |
|
15803 */ |
|
15804 |
|
15805 Y.Env.evt.dom_wrappers = {}; |
|
15806 Y.Env.evt.dom_map = {}; |
|
15807 |
|
15808 var _eventenv = Y.Env.evt, |
|
15809 config = Y.config, |
|
15810 win = config.win, |
|
15811 add = YUI.Env.add, |
|
15812 remove = YUI.Env.remove, |
|
15813 |
|
15814 onLoad = function() { |
|
15815 YUI.Env.windowLoaded = true; |
|
15816 Y.Event._load(); |
|
15817 remove(win, "load", onLoad); |
|
15818 }, |
|
15819 |
|
15820 onUnload = function() { |
|
15821 Y.Event._unload(); |
|
15822 }, |
|
15823 |
|
15824 EVENT_READY = 'domready', |
|
15825 |
|
15826 COMPAT_ARG = '~yui|2|compat~', |
|
15827 |
|
15828 shouldIterate = function(o) { |
|
15829 try { |
|
15830 // TODO: See if there's a more performant way to return true early on this, for the common case |
|
15831 return (o && typeof o !== "string" && Y.Lang.isNumber(o.length) && !o.tagName && !Y.DOM.isWindow(o)); |
|
15832 } catch(ex) { |
|
15833 return false; |
|
15834 } |
|
15835 }, |
|
15836 |
|
15837 // aliases to support DOM event subscription clean up when the last |
|
15838 // subscriber is detached. deleteAndClean overrides the DOM event's wrapper |
|
15839 // CustomEvent _delete method. |
|
15840 _ceProtoDelete = Y.CustomEvent.prototype._delete, |
|
15841 _deleteAndClean = function(s) { |
|
15842 var ret = _ceProtoDelete.apply(this, arguments); |
|
15843 |
|
15844 if (!this.hasSubs()) { |
|
15845 Y.Event._clean(this); |
|
15846 } |
|
15847 |
|
15848 return ret; |
|
15849 }, |
|
15850 |
|
15851 Event = function() { |
|
15852 |
|
15853 /** |
|
15854 * True after the onload event has fired |
|
15855 * @property _loadComplete |
|
15856 * @type boolean |
|
15857 * @static |
|
15858 * @private |
|
15859 */ |
|
15860 var _loadComplete = false, |
|
15861 |
|
15862 /** |
|
15863 * The number of times to poll after window.onload. This number is |
|
15864 * increased if additional late-bound handlers are requested after |
|
15865 * the page load. |
|
15866 * @property _retryCount |
|
15867 * @static |
|
15868 * @private |
|
15869 */ |
|
15870 _retryCount = 0, |
|
15871 |
|
15872 /** |
|
15873 * onAvailable listeners |
|
15874 * @property _avail |
|
15875 * @static |
|
15876 * @private |
|
15877 */ |
|
15878 _avail = [], |
|
15879 |
|
15880 /** |
|
15881 * Custom event wrappers for DOM events. Key is |
|
15882 * 'event:' + Element uid stamp + event type |
|
15883 * @property _wrappers |
|
15884 * @type Y.Event.Custom |
|
15885 * @static |
|
15886 * @private |
|
15887 */ |
|
15888 _wrappers = _eventenv.dom_wrappers, |
|
15889 |
|
15890 _windowLoadKey = null, |
|
15891 |
|
15892 /** |
|
15893 * Custom event wrapper map DOM events. Key is |
|
15894 * Element uid stamp. Each item is a hash of custom event |
|
15895 * wrappers as provided in the _wrappers collection. This |
|
15896 * provides the infrastructure for getListeners. |
|
15897 * @property _el_events |
|
15898 * @static |
|
15899 * @private |
|
15900 */ |
|
15901 _el_events = _eventenv.dom_map; |
|
15902 |
|
15903 return { |
|
15904 |
|
15905 /** |
|
15906 * The number of times we should look for elements that are not |
|
15907 * in the DOM at the time the event is requested after the document |
|
15908 * has been loaded. The default is 1000@amp;40 ms, so it will poll |
|
15909 * for 40 seconds or until all outstanding handlers are bound |
|
15910 * (whichever comes first). |
|
15911 * @property POLL_RETRYS |
|
15912 * @type int |
|
15913 * @static |
|
15914 * @final |
|
15915 */ |
|
15916 POLL_RETRYS: 1000, |
|
15917 |
|
15918 /** |
|
15919 * The poll interval in milliseconds |
|
15920 * @property POLL_INTERVAL |
|
15921 * @type int |
|
15922 * @static |
|
15923 * @final |
|
15924 */ |
|
15925 POLL_INTERVAL: 40, |
|
15926 |
|
15927 /** |
|
15928 * addListener/removeListener can throw errors in unexpected scenarios. |
|
15929 * These errors are suppressed, the method returns false, and this property |
|
15930 * is set |
|
15931 * @property lastError |
|
15932 * @static |
|
15933 * @type Error |
|
15934 */ |
|
15935 lastError: null, |
|
15936 |
|
15937 |
|
15938 /** |
|
15939 * poll handle |
|
15940 * @property _interval |
|
15941 * @static |
|
15942 * @private |
|
15943 */ |
|
15944 _interval: null, |
|
15945 |
|
15946 /** |
|
15947 * document readystate poll handle |
|
15948 * @property _dri |
|
15949 * @static |
|
15950 * @private |
|
15951 */ |
|
15952 _dri: null, |
|
15953 |
|
15954 /** |
|
15955 * True when the document is initially usable |
|
15956 * @property DOMReady |
|
15957 * @type boolean |
|
15958 * @static |
|
15959 */ |
|
15960 DOMReady: false, |
|
15961 |
|
15962 /** |
|
15963 * @method startInterval |
|
15964 * @static |
|
15965 * @private |
|
15966 */ |
|
15967 startInterval: function() { |
|
15968 if (!Event._interval) { |
|
15969 Event._interval = setInterval(Event._poll, Event.POLL_INTERVAL); |
|
15970 } |
|
15971 }, |
|
15972 |
|
15973 /** |
|
15974 * Executes the supplied callback when the item with the supplied |
|
15975 * id is found. This is meant to be used to execute behavior as |
|
15976 * soon as possible as the page loads. If you use this after the |
|
15977 * initial page load it will poll for a fixed time for the element. |
|
15978 * The number of times it will poll and the frequency are |
|
15979 * configurable. By default it will poll for 10 seconds. |
|
15980 * |
|
15981 * <p>The callback is executed with a single parameter: |
|
15982 * the custom object parameter, if provided.</p> |
|
15983 * |
|
15984 * @method onAvailable |
|
15985 * |
|
15986 * @param {string||string[]} id the id of the element, or an array |
|
15987 * of ids to look for. |
|
15988 * @param {function} fn what to execute when the element is found. |
|
15989 * @param {object} p_obj an optional object to be passed back as |
|
15990 * a parameter to fn. |
|
15991 * @param {boolean|object} p_override If set to true, fn will execute |
|
15992 * in the context of p_obj, if set to an object it |
|
15993 * will execute in the context of that object |
|
15994 * @param checkContent {boolean} check child node readiness (onContentReady) |
|
15995 * @static |
|
15996 * @deprecated Use Y.on("available") |
|
15997 */ |
|
15998 // @TODO fix arguments |
|
15999 onAvailable: function(id, fn, p_obj, p_override, checkContent, compat) { |
|
16000 |
|
16001 var a = Y.Array(id), i, availHandle; |
|
16002 |
|
16003 |
|
16004 for (i=0; i<a.length; i=i+1) { |
|
16005 _avail.push({ |
|
16006 id: a[i], |
|
16007 fn: fn, |
|
16008 obj: p_obj, |
|
16009 override: p_override, |
|
16010 checkReady: checkContent, |
|
16011 compat: compat |
|
16012 }); |
|
16013 } |
|
16014 _retryCount = this.POLL_RETRYS; |
|
16015 |
|
16016 // We want the first test to be immediate, but async |
|
16017 setTimeout(Event._poll, 0); |
|
16018 |
|
16019 availHandle = new Y.EventHandle({ |
|
16020 |
|
16021 _delete: function() { |
|
16022 // set by the event system for lazy DOM listeners |
|
16023 if (availHandle.handle) { |
|
16024 availHandle.handle.detach(); |
|
16025 return; |
|
16026 } |
|
16027 |
|
16028 var i, j; |
|
16029 |
|
16030 // otherwise try to remove the onAvailable listener(s) |
|
16031 for (i = 0; i < a.length; i++) { |
|
16032 for (j = 0; j < _avail.length; j++) { |
|
16033 if (a[i] === _avail[j].id) { |
|
16034 _avail.splice(j, 1); |
|
16035 } |
|
16036 } |
|
16037 } |
|
16038 } |
|
16039 |
|
16040 }); |
|
16041 |
|
16042 return availHandle; |
|
16043 }, |
|
16044 |
|
16045 /** |
|
16046 * Works the same way as onAvailable, but additionally checks the |
|
16047 * state of sibling elements to determine if the content of the |
|
16048 * available element is safe to modify. |
|
16049 * |
|
16050 * <p>The callback is executed with a single parameter: |
|
16051 * the custom object parameter, if provided.</p> |
|
16052 * |
|
16053 * @method onContentReady |
|
16054 * |
|
16055 * @param {string} id the id of the element to look for. |
|
16056 * @param {function} fn what to execute when the element is ready. |
|
16057 * @param {object} obj an optional object to be passed back as |
|
16058 * a parameter to fn. |
|
16059 * @param {boolean|object} override If set to true, fn will execute |
|
16060 * in the context of p_obj. If an object, fn will |
|
16061 * exectute in the context of that object |
|
16062 * |
|
16063 * @static |
|
16064 * @deprecated Use Y.on("contentready") |
|
16065 */ |
|
16066 // @TODO fix arguments |
|
16067 onContentReady: function(id, fn, obj, override, compat) { |
|
16068 return Event.onAvailable(id, fn, obj, override, true, compat); |
|
16069 }, |
|
16070 |
|
16071 /** |
|
16072 * Adds an event listener |
|
16073 * |
|
16074 * @method attach |
|
16075 * |
|
16076 * @param {String} type The type of event to append |
|
16077 * @param {Function} fn The method the event invokes |
|
16078 * @param {String|HTMLElement|Array|NodeList} el An id, an element |
|
16079 * reference, or a collection of ids and/or elements to assign the |
|
16080 * listener to. |
|
16081 * @param {Object} context optional context object |
|
16082 * @param {Boolean|object} args 0..n arguments to pass to the callback |
|
16083 * @return {EventHandle} an object to that can be used to detach the listener |
|
16084 * |
|
16085 * @static |
|
16086 */ |
|
16087 |
|
16088 attach: function(type, fn, el, context) { |
|
16089 return Event._attach(Y.Array(arguments, 0, true)); |
|
16090 }, |
|
16091 |
|
16092 _createWrapper: function (el, type, capture, compat, facade) { |
|
16093 |
|
16094 var cewrapper, |
|
16095 ek = Y.stamp(el), |
|
16096 key = 'event:' + ek + type; |
|
16097 |
|
16098 if (false === facade) { |
|
16099 key += 'native'; |
|
16100 } |
|
16101 if (capture) { |
|
16102 key += 'capture'; |
|
16103 } |
|
16104 |
|
16105 |
|
16106 cewrapper = _wrappers[key]; |
|
16107 |
|
16108 |
|
16109 if (!cewrapper) { |
|
16110 // create CE wrapper |
|
16111 cewrapper = Y.publish(key, { |
|
16112 silent: true, |
|
16113 bubbles: false, |
|
16114 emitFacade:false, |
|
16115 contextFn: function() { |
|
16116 if (compat) { |
|
16117 return cewrapper.el; |
|
16118 } else { |
|
16119 cewrapper.nodeRef = cewrapper.nodeRef || Y.one(cewrapper.el); |
|
16120 return cewrapper.nodeRef; |
|
16121 } |
|
16122 } |
|
16123 }); |
|
16124 |
|
16125 cewrapper.overrides = {}; |
|
16126 |
|
16127 // for later removeListener calls |
|
16128 cewrapper.el = el; |
|
16129 cewrapper.key = key; |
|
16130 cewrapper.domkey = ek; |
|
16131 cewrapper.type = type; |
|
16132 cewrapper.fn = function(e) { |
|
16133 cewrapper.fire(Event.getEvent(e, el, (compat || (false === facade)))); |
|
16134 }; |
|
16135 cewrapper.capture = capture; |
|
16136 |
|
16137 if (el == win && type == "load") { |
|
16138 // window load happens once |
|
16139 cewrapper.fireOnce = true; |
|
16140 _windowLoadKey = key; |
|
16141 } |
|
16142 cewrapper._delete = _deleteAndClean; |
|
16143 |
|
16144 _wrappers[key] = cewrapper; |
|
16145 _el_events[ek] = _el_events[ek] || {}; |
|
16146 _el_events[ek][key] = cewrapper; |
|
16147 |
|
16148 add(el, type, cewrapper.fn, capture); |
|
16149 } |
|
16150 |
|
16151 return cewrapper; |
|
16152 |
|
16153 }, |
|
16154 |
|
16155 _attach: function(args, conf) { |
|
16156 |
|
16157 var compat, |
|
16158 handles, oEl, cewrapper, context, |
|
16159 fireNow = false, ret, |
|
16160 type = args[0], |
|
16161 fn = args[1], |
|
16162 el = args[2] || win, |
|
16163 facade = conf && conf.facade, |
|
16164 capture = conf && conf.capture, |
|
16165 overrides = conf && conf.overrides; |
|
16166 |
|
16167 if (args[args.length-1] === COMPAT_ARG) { |
|
16168 compat = true; |
|
16169 } |
|
16170 |
|
16171 if (!fn || !fn.call) { |
|
16172 // throw new TypeError(type + " attach call failed, callback undefined"); |
|
16173 return false; |
|
16174 } |
|
16175 |
|
16176 // The el argument can be an array of elements or element ids. |
|
16177 if (shouldIterate(el)) { |
|
16178 |
|
16179 handles=[]; |
|
16180 |
|
16181 Y.each(el, function(v, k) { |
|
16182 args[2] = v; |
|
16183 handles.push(Event._attach(args.slice(), conf)); |
|
16184 }); |
|
16185 |
|
16186 // return (handles.length === 1) ? handles[0] : handles; |
|
16187 return new Y.EventHandle(handles); |
|
16188 |
|
16189 // If the el argument is a string, we assume it is |
|
16190 // actually the id of the element. If the page is loaded |
|
16191 // we convert el to the actual element, otherwise we |
|
16192 // defer attaching the event until the element is |
|
16193 // ready |
|
16194 } else if (Y.Lang.isString(el)) { |
|
16195 |
|
16196 // oEl = (compat) ? Y.DOM.byId(el) : Y.Selector.query(el); |
|
16197 |
|
16198 if (compat) { |
|
16199 oEl = Y.DOM.byId(el); |
|
16200 } else { |
|
16201 |
|
16202 oEl = Y.Selector.query(el); |
|
16203 |
|
16204 switch (oEl.length) { |
|
16205 case 0: |
|
16206 oEl = null; |
|
16207 break; |
|
16208 case 1: |
|
16209 oEl = oEl[0]; |
|
16210 break; |
|
16211 default: |
|
16212 args[2] = oEl; |
|
16213 return Event._attach(args, conf); |
|
16214 } |
|
16215 } |
|
16216 |
|
16217 if (oEl) { |
|
16218 |
|
16219 el = oEl; |
|
16220 |
|
16221 // Not found = defer adding the event until the element is available |
|
16222 } else { |
|
16223 |
|
16224 ret = Event.onAvailable(el, function() { |
|
16225 |
|
16226 ret.handle = Event._attach(args, conf); |
|
16227 |
|
16228 }, Event, true, false, compat); |
|
16229 |
|
16230 return ret; |
|
16231 |
|
16232 } |
|
16233 } |
|
16234 |
|
16235 // Element should be an html element or node |
|
16236 if (!el) { |
|
16237 return false; |
|
16238 } |
|
16239 |
|
16240 if (Y.Node && Y.instanceOf(el, Y.Node)) { |
|
16241 el = Y.Node.getDOMNode(el); |
|
16242 } |
|
16243 |
|
16244 cewrapper = Event._createWrapper(el, type, capture, compat, facade); |
|
16245 if (overrides) { |
|
16246 Y.mix(cewrapper.overrides, overrides); |
|
16247 } |
|
16248 |
|
16249 if (el == win && type == "load") { |
|
16250 |
|
16251 // if the load is complete, fire immediately. |
|
16252 // all subscribers, including the current one |
|
16253 // will be notified. |
|
16254 if (YUI.Env.windowLoaded) { |
|
16255 fireNow = true; |
|
16256 } |
|
16257 } |
|
16258 |
|
16259 if (compat) { |
|
16260 args.pop(); |
|
16261 } |
|
16262 |
|
16263 context = args[3]; |
|
16264 |
|
16265 // set context to the Node if not specified |
|
16266 // ret = cewrapper.on.apply(cewrapper, trimmedArgs); |
|
16267 ret = cewrapper._on(fn, context, (args.length > 4) ? args.slice(4) : null); |
|
16268 |
|
16269 if (fireNow) { |
|
16270 cewrapper.fire(); |
|
16271 } |
|
16272 |
|
16273 return ret; |
|
16274 |
|
16275 }, |
|
16276 |
|
16277 /** |
|
16278 * Removes an event listener. Supports the signature the event was bound |
|
16279 * with, but the preferred way to remove listeners is using the handle |
|
16280 * that is returned when using Y.on |
|
16281 * |
|
16282 * @method detach |
|
16283 * |
|
16284 * @param {String} type the type of event to remove. |
|
16285 * @param {Function} fn the method the event invokes. If fn is |
|
16286 * undefined, then all event handlers for the type of event are |
|
16287 * removed. |
|
16288 * @param {String|HTMLElement|Array|NodeList|EventHandle} el An |
|
16289 * event handle, an id, an element reference, or a collection |
|
16290 * of ids and/or elements to remove the listener from. |
|
16291 * @return {boolean} true if the unbind was successful, false otherwise. |
|
16292 * @static |
|
16293 */ |
|
16294 detach: function(type, fn, el, obj) { |
|
16295 |
|
16296 var args=Y.Array(arguments, 0, true), compat, l, ok, i, |
|
16297 id, ce; |
|
16298 |
|
16299 if (args[args.length-1] === COMPAT_ARG) { |
|
16300 compat = true; |
|
16301 // args.pop(); |
|
16302 } |
|
16303 |
|
16304 if (type && type.detach) { |
|
16305 return type.detach(); |
|
16306 } |
|
16307 |
|
16308 // The el argument can be a string |
|
16309 if (typeof el == "string") { |
|
16310 |
|
16311 // el = (compat) ? Y.DOM.byId(el) : Y.all(el); |
|
16312 if (compat) { |
|
16313 el = Y.DOM.byId(el); |
|
16314 } else { |
|
16315 el = Y.Selector.query(el); |
|
16316 l = el.length; |
|
16317 if (l < 1) { |
|
16318 el = null; |
|
16319 } else if (l == 1) { |
|
16320 el = el[0]; |
|
16321 } |
|
16322 } |
|
16323 // return Event.detach.apply(Event, args); |
|
16324 } |
|
16325 |
|
16326 if (!el) { |
|
16327 return false; |
|
16328 } |
|
16329 |
|
16330 if (el.detach) { |
|
16331 args.splice(2, 1); |
|
16332 return el.detach.apply(el, args); |
|
16333 // The el argument can be an array of elements or element ids. |
|
16334 } else if (shouldIterate(el)) { |
|
16335 ok = true; |
|
16336 for (i=0, l=el.length; i<l; ++i) { |
|
16337 args[2] = el[i]; |
|
16338 ok = ( Y.Event.detach.apply(Y.Event, args) && ok ); |
|
16339 } |
|
16340 |
|
16341 return ok; |
|
16342 } |
|
16343 |
|
16344 if (!type || !fn || !fn.call) { |
|
16345 return Event.purgeElement(el, false, type); |
|
16346 } |
|
16347 |
|
16348 id = 'event:' + Y.stamp(el) + type; |
|
16349 ce = _wrappers[id]; |
|
16350 |
|
16351 if (ce) { |
|
16352 return ce.detach(fn); |
|
16353 } else { |
|
16354 return false; |
|
16355 } |
|
16356 |
|
16357 }, |
|
16358 |
|
16359 /** |
|
16360 * Finds the event in the window object, the caller's arguments, or |
|
16361 * in the arguments of another method in the callstack. This is |
|
16362 * executed automatically for events registered through the event |
|
16363 * manager, so the implementer should not normally need to execute |
|
16364 * this function at all. |
|
16365 * @method getEvent |
|
16366 * @param {Event} e the event parameter from the handler |
|
16367 * @param {HTMLElement} el the element the listener was attached to |
|
16368 * @return {Event} the event |
|
16369 * @static |
|
16370 */ |
|
16371 getEvent: function(e, el, noFacade) { |
|
16372 var ev = e || win.event; |
|
16373 |
|
16374 return (noFacade) ? ev : |
|
16375 new Y.DOMEventFacade(ev, el, _wrappers['event:' + Y.stamp(el) + e.type]); |
|
16376 }, |
|
16377 |
|
16378 /** |
|
16379 * Generates an unique ID for the element if it does not already |
|
16380 * have one. |
|
16381 * @method generateId |
|
16382 * @param el the element to create the id for |
|
16383 * @return {string} the resulting id of the element |
|
16384 * @static |
|
16385 */ |
|
16386 generateId: function(el) { |
|
16387 return Y.DOM.generateID(el); |
|
16388 }, |
|
16389 |
|
16390 /** |
|
16391 * We want to be able to use getElementsByTagName as a collection |
|
16392 * to attach a group of events to. Unfortunately, different |
|
16393 * browsers return different types of collections. This function |
|
16394 * tests to determine if the object is array-like. It will also |
|
16395 * fail if the object is an array, but is empty. |
|
16396 * @method _isValidCollection |
|
16397 * @param o the object to test |
|
16398 * @return {boolean} true if the object is array-like and populated |
|
16399 * @deprecated was not meant to be used directly |
|
16400 * @static |
|
16401 * @private |
|
16402 */ |
|
16403 _isValidCollection: shouldIterate, |
|
16404 |
|
16405 /** |
|
16406 * hook up any deferred listeners |
|
16407 * @method _load |
|
16408 * @static |
|
16409 * @private |
|
16410 */ |
|
16411 _load: function(e) { |
|
16412 if (!_loadComplete) { |
|
16413 _loadComplete = true; |
|
16414 |
|
16415 // Just in case DOMReady did not go off for some reason |
|
16416 // E._ready(); |
|
16417 if (Y.fire) { |
|
16418 Y.fire(EVENT_READY); |
|
16419 } |
|
16420 |
|
16421 // Available elements may not have been detected before the |
|
16422 // window load event fires. Try to find them now so that the |
|
16423 // the user is more likely to get the onAvailable notifications |
|
16424 // before the window load notification |
|
16425 Event._poll(); |
|
16426 } |
|
16427 }, |
|
16428 |
|
16429 /** |
|
16430 * Polling function that runs before the onload event fires, |
|
16431 * attempting to attach to DOM Nodes as soon as they are |
|
16432 * available |
|
16433 * @method _poll |
|
16434 * @static |
|
16435 * @private |
|
16436 */ |
|
16437 _poll: function() { |
|
16438 if (Event.locked) { |
|
16439 return; |
|
16440 } |
|
16441 |
|
16442 if (Y.UA.ie && !YUI.Env.DOMReady) { |
|
16443 // Hold off if DOMReady has not fired and check current |
|
16444 // readyState to protect against the IE operation aborted |
|
16445 // issue. |
|
16446 Event.startInterval(); |
|
16447 return; |
|
16448 } |
|
16449 |
|
16450 Event.locked = true; |
|
16451 |
|
16452 // keep trying until after the page is loaded. We need to |
|
16453 // check the page load state prior to trying to bind the |
|
16454 // elements so that we can be certain all elements have been |
|
16455 // tested appropriately |
|
16456 var i, len, item, el, notAvail, executeItem, |
|
16457 tryAgain = !_loadComplete; |
|
16458 |
|
16459 if (!tryAgain) { |
|
16460 tryAgain = (_retryCount > 0); |
|
16461 } |
|
16462 |
|
16463 // onAvailable |
|
16464 notAvail = []; |
|
16465 |
|
16466 executeItem = function (el, item) { |
|
16467 var context, ov = item.override; |
|
16468 try { |
|
16469 if (item.compat) { |
|
16470 if (item.override) { |
|
16471 if (ov === true) { |
|
16472 context = item.obj; |
|
16473 } else { |
|
16474 context = ov; |
|
16475 } |
|
16476 } else { |
|
16477 context = el; |
|
16478 } |
|
16479 item.fn.call(context, item.obj); |
|
16480 } else { |
|
16481 context = item.obj || Y.one(el); |
|
16482 item.fn.apply(context, (Y.Lang.isArray(ov)) ? ov : []); |
|
16483 } |
|
16484 } catch (e) { |
|
16485 } |
|
16486 }; |
|
16487 |
|
16488 // onAvailable |
|
16489 for (i=0,len=_avail.length; i<len; ++i) { |
|
16490 item = _avail[i]; |
|
16491 if (item && !item.checkReady) { |
|
16492 |
|
16493 // el = (item.compat) ? Y.DOM.byId(item.id) : Y.one(item.id); |
|
16494 el = (item.compat) ? Y.DOM.byId(item.id) : Y.Selector.query(item.id, null, true); |
|
16495 |
|
16496 if (el) { |
|
16497 executeItem(el, item); |
|
16498 _avail[i] = null; |
|
16499 } else { |
|
16500 notAvail.push(item); |
|
16501 } |
|
16502 } |
|
16503 } |
|
16504 |
|
16505 // onContentReady |
|
16506 for (i=0,len=_avail.length; i<len; ++i) { |
|
16507 item = _avail[i]; |
|
16508 if (item && item.checkReady) { |
|
16509 |
|
16510 // el = (item.compat) ? Y.DOM.byId(item.id) : Y.one(item.id); |
|
16511 el = (item.compat) ? Y.DOM.byId(item.id) : Y.Selector.query(item.id, null, true); |
|
16512 |
|
16513 if (el) { |
|
16514 // The element is available, but not necessarily ready |
|
16515 // @todo should we test parentNode.nextSibling? |
|
16516 if (_loadComplete || (el.get && el.get('nextSibling')) || el.nextSibling) { |
|
16517 executeItem(el, item); |
|
16518 _avail[i] = null; |
|
16519 } |
|
16520 } else { |
|
16521 notAvail.push(item); |
|
16522 } |
|
16523 } |
|
16524 } |
|
16525 |
|
16526 _retryCount = (notAvail.length === 0) ? 0 : _retryCount - 1; |
|
16527 |
|
16528 if (tryAgain) { |
|
16529 // we may need to strip the nulled out items here |
|
16530 Event.startInterval(); |
|
16531 } else { |
|
16532 clearInterval(Event._interval); |
|
16533 Event._interval = null; |
|
16534 } |
|
16535 |
|
16536 Event.locked = false; |
|
16537 |
|
16538 return; |
|
16539 |
|
16540 }, |
|
16541 |
|
16542 /** |
|
16543 * Removes all listeners attached to the given element via addListener. |
|
16544 * Optionally, the node's children can also be purged. |
|
16545 * Optionally, you can specify a specific type of event to remove. |
|
16546 * @method purgeElement |
|
16547 * @param {HTMLElement} el the element to purge |
|
16548 * @param {boolean} recurse recursively purge this element's children |
|
16549 * as well. Use with caution. |
|
16550 * @param {string} type optional type of listener to purge. If |
|
16551 * left out, all listeners will be removed |
|
16552 * @static |
|
16553 */ |
|
16554 purgeElement: function(el, recurse, type) { |
|
16555 // var oEl = (Y.Lang.isString(el)) ? Y.one(el) : el, |
|
16556 var oEl = (Y.Lang.isString(el)) ? Y.Selector.query(el, null, true) : el, |
|
16557 lis = Event.getListeners(oEl, type), i, len, children, child; |
|
16558 |
|
16559 if (recurse && oEl) { |
|
16560 lis = lis || []; |
|
16561 children = Y.Selector.query('*', oEl); |
|
16562 len = children.length; |
|
16563 for (i = 0; i < len; ++i) { |
|
16564 child = Event.getListeners(children[i], type); |
|
16565 if (child) { |
|
16566 lis = lis.concat(child); |
|
16567 } |
|
16568 } |
|
16569 } |
|
16570 |
|
16571 if (lis) { |
|
16572 for (i = 0, len = lis.length; i < len; ++i) { |
|
16573 lis[i].detachAll(); |
|
16574 } |
|
16575 } |
|
16576 |
|
16577 }, |
|
16578 |
|
16579 /** |
|
16580 * Removes all object references and the DOM proxy subscription for |
|
16581 * a given event for a DOM node. |
|
16582 * |
|
16583 * @method _clean |
|
16584 * @param wrapper {CustomEvent} Custom event proxy for the DOM |
|
16585 * subscription |
|
16586 * @private |
|
16587 * @static |
|
16588 * @since 3.4.0 |
|
16589 */ |
|
16590 _clean: function (wrapper) { |
|
16591 var key = wrapper.key, |
|
16592 domkey = wrapper.domkey; |
|
16593 |
|
16594 remove(wrapper.el, wrapper.type, wrapper.fn, wrapper.capture); |
|
16595 delete _wrappers[key]; |
|
16596 delete Y._yuievt.events[key]; |
|
16597 if (_el_events[domkey]) { |
|
16598 delete _el_events[domkey][key]; |
|
16599 if (!Y.Object.size(_el_events[domkey])) { |
|
16600 delete _el_events[domkey]; |
|
16601 } |
|
16602 } |
|
16603 }, |
|
16604 |
|
16605 /** |
|
16606 * Returns all listeners attached to the given element via addListener. |
|
16607 * Optionally, you can specify a specific type of event to return. |
|
16608 * @method getListeners |
|
16609 * @param el {HTMLElement|string} the element or element id to inspect |
|
16610 * @param type {string} optional type of listener to return. If |
|
16611 * left out, all listeners will be returned |
|
16612 * @return {CustomEvent} the custom event wrapper for the DOM event(s) |
|
16613 * @static |
|
16614 */ |
|
16615 getListeners: function(el, type) { |
|
16616 var ek = Y.stamp(el, true), evts = _el_events[ek], |
|
16617 results=[] , key = (type) ? 'event:' + ek + type : null, |
|
16618 adapters = _eventenv.plugins; |
|
16619 |
|
16620 if (!evts) { |
|
16621 return null; |
|
16622 } |
|
16623 |
|
16624 if (key) { |
|
16625 // look for synthetic events |
|
16626 if (adapters[type] && adapters[type].eventDef) { |
|
16627 key += '_synth'; |
|
16628 } |
|
16629 |
|
16630 if (evts[key]) { |
|
16631 results.push(evts[key]); |
|
16632 } |
|
16633 |
|
16634 // get native events as well |
|
16635 key += 'native'; |
|
16636 if (evts[key]) { |
|
16637 results.push(evts[key]); |
|
16638 } |
|
16639 |
|
16640 } else { |
|
16641 Y.each(evts, function(v, k) { |
|
16642 results.push(v); |
|
16643 }); |
|
16644 } |
|
16645 |
|
16646 return (results.length) ? results : null; |
|
16647 }, |
|
16648 |
|
16649 /** |
|
16650 * Removes all listeners registered by pe.event. Called |
|
16651 * automatically during the unload event. |
|
16652 * @method _unload |
|
16653 * @static |
|
16654 * @private |
|
16655 */ |
|
16656 _unload: function(e) { |
|
16657 Y.each(_wrappers, function(v, k) { |
|
16658 if (v.type == 'unload') { |
|
16659 v.fire(e); |
|
16660 } |
|
16661 v.detachAll(); |
|
16662 }); |
|
16663 remove(win, "unload", onUnload); |
|
16664 }, |
|
16665 |
|
16666 /** |
|
16667 * Adds a DOM event directly without the caching, cleanup, context adj, etc |
|
16668 * |
|
16669 * @method nativeAdd |
|
16670 * @param {HTMLElement} el the element to bind the handler to |
|
16671 * @param {string} type the type of event handler |
|
16672 * @param {function} fn the callback to invoke |
|
16673 * @param {boolen} capture capture or bubble phase |
|
16674 * @static |
|
16675 * @private |
|
16676 */ |
|
16677 nativeAdd: add, |
|
16678 |
|
16679 /** |
|
16680 * Basic remove listener |
|
16681 * |
|
16682 * @method nativeRemove |
|
16683 * @param {HTMLElement} el the element to bind the handler to |
|
16684 * @param {string} type the type of event handler |
|
16685 * @param {function} fn the callback to invoke |
|
16686 * @param {boolen} capture capture or bubble phase |
|
16687 * @static |
|
16688 * @private |
|
16689 */ |
|
16690 nativeRemove: remove |
|
16691 }; |
|
16692 |
|
16693 }(); |
|
16694 |
|
16695 Y.Event = Event; |
|
16696 |
|
16697 if (config.injected || YUI.Env.windowLoaded) { |
|
16698 onLoad(); |
|
16699 } else { |
|
16700 add(win, "load", onLoad); |
|
16701 } |
|
16702 |
|
16703 // Process onAvailable/onContentReady items when when the DOM is ready in IE |
|
16704 if (Y.UA.ie) { |
|
16705 Y.on(EVENT_READY, Event._poll); |
|
16706 } |
|
16707 |
|
16708 try { |
|
16709 add(win, "unload", onUnload); |
|
16710 } catch(e) { |
|
16711 /*jshint maxlen:300*/ |
|
16712 } |
|
16713 |
|
16714 Event.Custom = Y.CustomEvent; |
|
16715 Event.Subscriber = Y.Subscriber; |
|
16716 Event.Target = Y.EventTarget; |
|
16717 Event.Handle = Y.EventHandle; |
|
16718 Event.Facade = Y.EventFacade; |
|
16719 |
|
16720 Event._poll(); |
|
16721 |
|
16722 }()); |
|
16723 |
|
16724 /** |
|
16725 * DOM event listener abstraction layer |
|
16726 * @module event |
|
16727 * @submodule event-base |
|
16728 */ |
|
16729 |
|
16730 /** |
|
16731 * Executes the callback as soon as the specified element |
|
16732 * is detected in the DOM. This function expects a selector |
|
16733 * string for the element(s) to detect. If you already have |
|
16734 * an element reference, you don't need this event. |
|
16735 * @event available |
|
16736 * @param type {string} 'available' |
|
16737 * @param fn {function} the callback function to execute. |
|
16738 * @param el {string} an selector for the element(s) to attach |
|
16739 * @param context optional argument that specifies what 'this' refers to. |
|
16740 * @param args* 0..n additional arguments to pass on to the callback function. |
|
16741 * These arguments will be added after the event object. |
|
16742 * @return {EventHandle} the detach handle |
|
16743 * @for YUI |
|
16744 */ |
|
16745 Y.Env.evt.plugins.available = { |
|
16746 on: function(type, fn, id, o) { |
|
16747 var a = arguments.length > 4 ? Y.Array(arguments, 4, true) : null; |
|
16748 return Y.Event.onAvailable.call(Y.Event, id, fn, o, a); |
|
16749 } |
|
16750 }; |
|
16751 |
|
16752 /** |
|
16753 * Executes the callback as soon as the specified element |
|
16754 * is detected in the DOM with a nextSibling property |
|
16755 * (indicating that the element's children are available). |
|
16756 * This function expects a selector |
|
16757 * string for the element(s) to detect. If you already have |
|
16758 * an element reference, you don't need this event. |
|
16759 * @event contentready |
|
16760 * @param type {string} 'contentready' |
|
16761 * @param fn {function} the callback function to execute. |
|
16762 * @param el {string} an selector for the element(s) to attach. |
|
16763 * @param context optional argument that specifies what 'this' refers to. |
|
16764 * @param args* 0..n additional arguments to pass on to the callback function. |
|
16765 * These arguments will be added after the event object. |
|
16766 * @return {EventHandle} the detach handle |
|
16767 * @for YUI |
|
16768 */ |
|
16769 Y.Env.evt.plugins.contentready = { |
|
16770 on: function(type, fn, id, o) { |
|
16771 var a = arguments.length > 4 ? Y.Array(arguments, 4, true) : null; |
|
16772 return Y.Event.onContentReady.call(Y.Event, id, fn, o, a); |
|
16773 } |
|
16774 }; |
|
16775 |
|
16776 |
|
16777 }, '@VERSION@', {"requires": ["event-custom-base"]}); |
|
16778 (function() { |
|
16779 |
|
16780 var stateChangeListener, |
|
16781 GLOBAL_ENV = YUI.Env, |
|
16782 config = YUI.config, |
|
16783 doc = config.doc, |
|
16784 docElement = doc && doc.documentElement, |
|
16785 EVENT_NAME = 'onreadystatechange', |
|
16786 pollInterval = config.pollInterval || 40; |
|
16787 |
|
16788 if (docElement.doScroll && !GLOBAL_ENV._ieready) { |
|
16789 GLOBAL_ENV._ieready = function() { |
|
16790 GLOBAL_ENV._ready(); |
|
16791 }; |
|
16792 |
|
16793 /*! DOMReady: based on work by: Dean Edwards/John Resig/Matthias Miller/Diego Perini */ |
|
16794 // Internet Explorer: use the doScroll() method on the root element. |
|
16795 // This isolates what appears to be a safe moment to manipulate the |
|
16796 // DOM prior to when the document's readyState suggests it is safe to do so. |
|
16797 if (self !== self.top) { |
|
16798 stateChangeListener = function() { |
|
16799 if (doc.readyState == 'complete') { |
|
16800 GLOBAL_ENV.remove(doc, EVENT_NAME, stateChangeListener); |
|
16801 GLOBAL_ENV.ieready(); |
|
16802 } |
|
16803 }; |
|
16804 GLOBAL_ENV.add(doc, EVENT_NAME, stateChangeListener); |
|
16805 } else { |
|
16806 GLOBAL_ENV._dri = setInterval(function() { |
|
16807 try { |
|
16808 docElement.doScroll('left'); |
|
16809 clearInterval(GLOBAL_ENV._dri); |
|
16810 GLOBAL_ENV._dri = null; |
|
16811 GLOBAL_ENV._ieready(); |
|
16812 } catch (domNotReady) { } |
|
16813 }, pollInterval); |
|
16814 } |
|
16815 } |
|
16816 |
|
16817 })(); |
|
16818 YUI.add('event-base-ie', function (Y, NAME) { |
|
16819 |
|
16820 /* |
|
16821 * Custom event engine, DOM event listener abstraction layer, synthetic DOM |
|
16822 * events. |
|
16823 * @module event |
|
16824 * @submodule event-base |
|
16825 */ |
|
16826 |
|
16827 function IEEventFacade() { |
|
16828 // IEEventFacade.superclass.constructor.apply(this, arguments); |
|
16829 Y.DOM2EventFacade.apply(this, arguments); |
|
16830 } |
|
16831 |
|
16832 /* |
|
16833 * (intentially left out of API docs) |
|
16834 * Alternate Facade implementation that is based on Object.defineProperty, which |
|
16835 * is partially supported in IE8. Properties that involve setup work are |
|
16836 * deferred to temporary getters using the static _define method. |
|
16837 */ |
|
16838 function IELazyFacade(e) { |
|
16839 var proxy = Y.config.doc.createEventObject(e), |
|
16840 proto = IELazyFacade.prototype; |
|
16841 |
|
16842 // TODO: necessary? |
|
16843 proxy.hasOwnProperty = function () { return true; }; |
|
16844 |
|
16845 proxy.init = proto.init; |
|
16846 proxy.halt = proto.halt; |
|
16847 proxy.preventDefault = proto.preventDefault; |
|
16848 proxy.stopPropagation = proto.stopPropagation; |
|
16849 proxy.stopImmediatePropagation = proto.stopImmediatePropagation; |
|
16850 |
|
16851 Y.DOM2EventFacade.apply(proxy, arguments); |
|
16852 |
|
16853 return proxy; |
|
16854 } |
|
16855 |
|
16856 |
|
16857 var imp = Y.config.doc && Y.config.doc.implementation, |
|
16858 useLazyFacade = Y.config.lazyEventFacade, |
|
16859 |
|
16860 buttonMap = { |
|
16861 0: 1, // left click |
|
16862 4: 2, // middle click |
|
16863 2: 3 // right click |
|
16864 }, |
|
16865 relatedTargetMap = { |
|
16866 mouseout: 'toElement', |
|
16867 mouseover: 'fromElement' |
|
16868 }, |
|
16869 |
|
16870 resolve = Y.DOM2EventFacade.resolve, |
|
16871 |
|
16872 proto = { |
|
16873 init: function() { |
|
16874 |
|
16875 IEEventFacade.superclass.init.apply(this, arguments); |
|
16876 |
|
16877 var e = this._event, |
|
16878 x, y, d, b, de, t; |
|
16879 |
|
16880 this.target = resolve(e.srcElement); |
|
16881 |
|
16882 if (('clientX' in e) && (!x) && (0 !== x)) { |
|
16883 x = e.clientX; |
|
16884 y = e.clientY; |
|
16885 |
|
16886 d = Y.config.doc; |
|
16887 b = d.body; |
|
16888 de = d.documentElement; |
|
16889 |
|
16890 x += (de.scrollLeft || (b && b.scrollLeft) || 0); |
|
16891 y += (de.scrollTop || (b && b.scrollTop) || 0); |
|
16892 |
|
16893 this.pageX = x; |
|
16894 this.pageY = y; |
|
16895 } |
|
16896 |
|
16897 if (e.type == "mouseout") { |
|
16898 t = e.toElement; |
|
16899 } else if (e.type == "mouseover") { |
|
16900 t = e.fromElement; |
|
16901 } |
|
16902 |
|
16903 // fallback to t.relatedTarget to support simulated events. |
|
16904 // IE doesn't support setting toElement or fromElement on generic |
|
16905 // events, so Y.Event.simulate sets relatedTarget instead. |
|
16906 this.relatedTarget = resolve(t || e.relatedTarget); |
|
16907 |
|
16908 // which should contain the unicode key code if this is a key event. |
|
16909 // For click events, which is normalized for which mouse button was |
|
16910 // clicked. |
|
16911 this.which = // chained assignment |
|
16912 this.button = e.keyCode || buttonMap[e.button] || e.button; |
|
16913 }, |
|
16914 |
|
16915 stopPropagation: function() { |
|
16916 this._event.cancelBubble = true; |
|
16917 this._wrapper.stopped = 1; |
|
16918 this.stopped = 1; |
|
16919 }, |
|
16920 |
|
16921 stopImmediatePropagation: function() { |
|
16922 this.stopPropagation(); |
|
16923 this._wrapper.stopped = 2; |
|
16924 this.stopped = 2; |
|
16925 }, |
|
16926 |
|
16927 preventDefault: function(returnValue) { |
|
16928 this._event.returnValue = returnValue || false; |
|
16929 this._wrapper.prevented = 1; |
|
16930 this.prevented = 1; |
|
16931 } |
|
16932 }; |
|
16933 |
|
16934 Y.extend(IEEventFacade, Y.DOM2EventFacade, proto); |
|
16935 |
|
16936 Y.extend(IELazyFacade, Y.DOM2EventFacade, proto); |
|
16937 IELazyFacade.prototype.init = function () { |
|
16938 var e = this._event, |
|
16939 overrides = this._wrapper.overrides, |
|
16940 define = IELazyFacade._define, |
|
16941 lazyProperties = IELazyFacade._lazyProperties, |
|
16942 prop; |
|
16943 |
|
16944 this.altKey = e.altKey; |
|
16945 this.ctrlKey = e.ctrlKey; |
|
16946 this.metaKey = e.metaKey; |
|
16947 this.shiftKey = e.shiftKey; |
|
16948 this.type = (overrides && overrides.type) || e.type; |
|
16949 this.clientX = e.clientX; |
|
16950 this.clientY = e.clientY; |
|
16951 this.keyCode = // chained assignment |
|
16952 this.charCode = e.keyCode; |
|
16953 this.which = // chained assignment |
|
16954 this.button = e.keyCode || buttonMap[e.button] || e.button; |
|
16955 |
|
16956 for (prop in lazyProperties) { |
|
16957 if (lazyProperties.hasOwnProperty(prop)) { |
|
16958 define(this, prop, lazyProperties[prop]); |
|
16959 } |
|
16960 } |
|
16961 |
|
16962 if (this._touch) { |
|
16963 this._touch(e, this._currentTarget, this._wrapper); |
|
16964 } |
|
16965 }; |
|
16966 |
|
16967 IELazyFacade._lazyProperties = { |
|
16968 target: function () { |
|
16969 return resolve(this._event.srcElement); |
|
16970 }, |
|
16971 relatedTarget: function () { |
|
16972 var e = this._event, |
|
16973 targetProp = relatedTargetMap[e.type] || 'relatedTarget'; |
|
16974 |
|
16975 // fallback to t.relatedTarget to support simulated events. |
|
16976 // IE doesn't support setting toElement or fromElement on generic |
|
16977 // events, so Y.Event.simulate sets relatedTarget instead. |
|
16978 return resolve(e[targetProp] || e.relatedTarget); |
|
16979 }, |
|
16980 currentTarget: function () { |
|
16981 return resolve(this._currentTarget); |
|
16982 }, |
|
16983 |
|
16984 wheelDelta: function () { |
|
16985 var e = this._event; |
|
16986 |
|
16987 if (e.type === "mousewheel" || e.type === "DOMMouseScroll") { |
|
16988 return (e.detail) ? |
|
16989 (e.detail * -1) : |
|
16990 // wheelDelta between -80 and 80 result in -1 or 1 |
|
16991 Math.round(e.wheelDelta / 80) || ((e.wheelDelta < 0) ? -1 : 1); |
|
16992 } |
|
16993 }, |
|
16994 |
|
16995 pageX: function () { |
|
16996 var e = this._event, |
|
16997 val = e.pageX, |
|
16998 doc, bodyScroll, docScroll; |
|
16999 |
|
17000 if (val === undefined) { |
|
17001 doc = Y.config.doc; |
|
17002 bodyScroll = doc.body && doc.body.scrollLeft; |
|
17003 docScroll = doc.documentElement.scrollLeft; |
|
17004 |
|
17005 val = e.clientX + (docScroll || bodyScroll || 0); |
|
17006 } |
|
17007 |
|
17008 return val; |
|
17009 }, |
|
17010 pageY: function () { |
|
17011 var e = this._event, |
|
17012 val = e.pageY, |
|
17013 doc, bodyScroll, docScroll; |
|
17014 |
|
17015 if (val === undefined) { |
|
17016 doc = Y.config.doc; |
|
17017 bodyScroll = doc.body && doc.body.scrollTop; |
|
17018 docScroll = doc.documentElement.scrollTop; |
|
17019 |
|
17020 val = e.clientY + (docScroll || bodyScroll || 0); |
|
17021 } |
|
17022 |
|
17023 return val; |
|
17024 } |
|
17025 }; |
|
17026 |
|
17027 |
|
17028 /** |
|
17029 * Wrapper function for Object.defineProperty that creates a property whose |
|
17030 * value will be calulated only when asked for. After calculating the value, |
|
17031 * the getter wll be removed, so it will behave as a normal property beyond that |
|
17032 * point. A setter is also assigned so assigning to the property will clear |
|
17033 * the getter, so foo.prop = 'a'; foo.prop; won't trigger the getter, |
|
17034 * overwriting value 'a'. |
|
17035 * |
|
17036 * Used only by the DOMEventFacades used by IE8 when the YUI configuration |
|
17037 * <code>lazyEventFacade</code> is set to true. |
|
17038 * |
|
17039 * @method _define |
|
17040 * @param o {DOMObject} A DOM object to add the property to |
|
17041 * @param prop {String} The name of the new property |
|
17042 * @param valueFn {Function} The function that will return the initial, default |
|
17043 * value for the property. |
|
17044 * @static |
|
17045 * @private |
|
17046 */ |
|
17047 IELazyFacade._define = function (o, prop, valueFn) { |
|
17048 function val(v) { |
|
17049 var ret = (arguments.length) ? v : valueFn.call(this); |
|
17050 |
|
17051 delete o[prop]; |
|
17052 Object.defineProperty(o, prop, { |
|
17053 value: ret, |
|
17054 configurable: true, |
|
17055 writable: true |
|
17056 }); |
|
17057 return ret; |
|
17058 } |
|
17059 Object.defineProperty(o, prop, { |
|
17060 get: val, |
|
17061 set: val, |
|
17062 configurable: true |
|
17063 }); |
|
17064 }; |
|
17065 |
|
17066 if (imp && (!imp.hasFeature('Events', '2.0'))) { |
|
17067 if (useLazyFacade) { |
|
17068 // Make sure we can use the lazy facade logic |
|
17069 try { |
|
17070 Object.defineProperty(Y.config.doc.createEventObject(), 'z', {}); |
|
17071 } catch (e) { |
|
17072 useLazyFacade = false; |
|
17073 } |
|
17074 } |
|
17075 |
|
17076 Y.DOMEventFacade = (useLazyFacade) ? IELazyFacade : IEEventFacade; |
|
17077 } |
|
17078 |
|
17079 |
|
17080 }, '@VERSION@', {"requires": ["node-base"]}); |
|
17081 YUI.add('pluginhost-base', function (Y, NAME) { |
|
17082 |
|
17083 /** |
|
17084 * Provides the augmentable PluginHost interface, which can be added to any class. |
|
17085 * @module pluginhost |
|
17086 */ |
|
17087 |
|
17088 /** |
|
17089 * Provides the augmentable PluginHost interface, which can be added to any class. |
|
17090 * @module pluginhost-base |
|
17091 */ |
|
17092 |
|
17093 /** |
|
17094 * <p> |
|
17095 * An augmentable class, which provides the augmented class with the ability to host plugins. |
|
17096 * It adds <a href="#method_plug">plug</a> and <a href="#method_unplug">unplug</a> methods to the augmented class, which can |
|
17097 * be used to add or remove plugins from instances of the class. |
|
17098 * </p> |
|
17099 * |
|
17100 * <p>Plugins can also be added through the constructor configuration object passed to the host class' constructor using |
|
17101 * the "plugins" property. Supported values for the "plugins" property are those defined by the <a href="#method_plug">plug</a> method. |
|
17102 * |
|
17103 * For example the following code would add the AnimPlugin and IOPlugin to Overlay (the plugin host): |
|
17104 * <xmp> |
|
17105 * var o = new Overlay({plugins: [ AnimPlugin, {fn:IOPlugin, cfg:{section:"header"}}]}); |
|
17106 * </xmp> |
|
17107 * </p> |
|
17108 * <p> |
|
17109 * Plug.Host's protected <a href="#method_initPlugins">_initPlugins</a> and <a href="#method_destroyPlugins">_destroyPlugins</a> |
|
17110 * methods should be invoked by the host class at the appropriate point in the host's lifecyle. |
|
17111 * </p> |
|
17112 * |
|
17113 * @class Plugin.Host |
|
17114 */ |
|
17115 |
|
17116 var L = Y.Lang; |
|
17117 |
|
17118 function PluginHost() { |
|
17119 this._plugins = {}; |
|
17120 } |
|
17121 |
|
17122 PluginHost.prototype = { |
|
17123 |
|
17124 /** |
|
17125 * Adds a plugin to the host object. This will instantiate the |
|
17126 * plugin and attach it to the configured namespace on the host object. |
|
17127 * |
|
17128 * @method plug |
|
17129 * @chainable |
|
17130 * @param P {Function | Object |Array} Accepts the plugin class, or an |
|
17131 * object with a "fn" property specifying the plugin class and |
|
17132 * a "cfg" property specifying the configuration for the Plugin. |
|
17133 * <p> |
|
17134 * Additionally an Array can also be passed in, with the above function or |
|
17135 * object values, allowing the user to add multiple plugins in a single call. |
|
17136 * </p> |
|
17137 * @param config (Optional) If the first argument is the plugin class, the second argument |
|
17138 * can be the configuration for the plugin. |
|
17139 * @return {Base} A reference to the host object |
|
17140 */ |
|
17141 plug: function(Plugin, config) { |
|
17142 var i, ln, ns; |
|
17143 |
|
17144 if (L.isArray(Plugin)) { |
|
17145 for (i = 0, ln = Plugin.length; i < ln; i++) { |
|
17146 this.plug(Plugin[i]); |
|
17147 } |
|
17148 } else { |
|
17149 if (Plugin && !L.isFunction(Plugin)) { |
|
17150 config = Plugin.cfg; |
|
17151 Plugin = Plugin.fn; |
|
17152 } |
|
17153 |
|
17154 // Plugin should be fn by now |
|
17155 if (Plugin && Plugin.NS) { |
|
17156 ns = Plugin.NS; |
|
17157 |
|
17158 config = config || {}; |
|
17159 config.host = this; |
|
17160 |
|
17161 if (this.hasPlugin(ns)) { |
|
17162 // Update config |
|
17163 if (this[ns].setAttrs) { |
|
17164 this[ns].setAttrs(config); |
|
17165 } |
|
17166 } else { |
|
17167 // Create new instance |
|
17168 this[ns] = new Plugin(config); |
|
17169 this._plugins[ns] = Plugin; |
|
17170 } |
|
17171 } |
|
17172 } |
|
17173 return this; |
|
17174 }, |
|
17175 |
|
17176 /** |
|
17177 * Removes a plugin from the host object. This will destroy the |
|
17178 * plugin instance and delete the namespace from the host object. |
|
17179 * |
|
17180 * @method unplug |
|
17181 * @param {String | Function} plugin The namespace of the plugin, or the plugin class with the static NS namespace property defined. If not provided, |
|
17182 * all registered plugins are unplugged. |
|
17183 * @return {Base} A reference to the host object |
|
17184 * @chainable |
|
17185 */ |
|
17186 unplug: function(plugin) { |
|
17187 var ns = plugin, |
|
17188 plugins = this._plugins; |
|
17189 |
|
17190 if (plugin) { |
|
17191 if (L.isFunction(plugin)) { |
|
17192 ns = plugin.NS; |
|
17193 if (ns && (!plugins[ns] || plugins[ns] !== plugin)) { |
|
17194 ns = null; |
|
17195 } |
|
17196 } |
|
17197 |
|
17198 if (ns) { |
|
17199 if (this[ns]) { |
|
17200 if (this[ns].destroy) { |
|
17201 this[ns].destroy(); |
|
17202 } |
|
17203 delete this[ns]; |
|
17204 } |
|
17205 if (plugins[ns]) { |
|
17206 delete plugins[ns]; |
|
17207 } |
|
17208 } |
|
17209 } else { |
|
17210 for (ns in this._plugins) { |
|
17211 if (this._plugins.hasOwnProperty(ns)) { |
|
17212 this.unplug(ns); |
|
17213 } |
|
17214 } |
|
17215 } |
|
17216 return this; |
|
17217 }, |
|
17218 |
|
17219 /** |
|
17220 * Determines if a plugin has plugged into this host. |
|
17221 * |
|
17222 * @method hasPlugin |
|
17223 * @param {String} ns The plugin's namespace |
|
17224 * @return {Plugin} Returns a truthy value (the plugin instance) if present, or undefined if not. |
|
17225 */ |
|
17226 hasPlugin : function(ns) { |
|
17227 return (this._plugins[ns] && this[ns]); |
|
17228 }, |
|
17229 |
|
17230 /** |
|
17231 * Initializes static plugins registered on the host (using the |
|
17232 * Base.plug static method) and any plugins passed to the |
|
17233 * instance through the "plugins" configuration property. |
|
17234 * |
|
17235 * @method _initPlugins |
|
17236 * @param {Config} config The configuration object with property name/value pairs. |
|
17237 * @private |
|
17238 */ |
|
17239 |
|
17240 _initPlugins: function(config) { |
|
17241 this._plugins = this._plugins || {}; |
|
17242 |
|
17243 if (this._initConfigPlugins) { |
|
17244 this._initConfigPlugins(config); |
|
17245 } |
|
17246 }, |
|
17247 |
|
17248 /** |
|
17249 * Unplugs and destroys all plugins on the host |
|
17250 * @method _destroyPlugins |
|
17251 * @private |
|
17252 */ |
|
17253 _destroyPlugins: function() { |
|
17254 this.unplug(); |
|
17255 } |
|
17256 }; |
|
17257 |
|
17258 Y.namespace("Plugin").Host = PluginHost; |
|
17259 |
|
17260 |
|
17261 }, '@VERSION@', {"requires": ["yui-base"]}); |
|
17262 YUI.add('pluginhost-config', function (Y, NAME) { |
|
17263 |
|
17264 /** |
|
17265 * Adds pluginhost constructor configuration and static configuration support |
|
17266 * @submodule pluginhost-config |
|
17267 */ |
|
17268 |
|
17269 var PluginHost = Y.Plugin.Host, |
|
17270 L = Y.Lang; |
|
17271 |
|
17272 /** |
|
17273 * A protected initialization method, used by the host class to initialize |
|
17274 * plugin configurations passed the constructor, through the config object. |
|
17275 * |
|
17276 * Host objects should invoke this method at the appropriate time in their |
|
17277 * construction lifecycle. |
|
17278 * |
|
17279 * @method _initConfigPlugins |
|
17280 * @param {Object} config The configuration object passed to the constructor |
|
17281 * @protected |
|
17282 * @for Plugin.Host |
|
17283 */ |
|
17284 PluginHost.prototype._initConfigPlugins = function(config) { |
|
17285 |
|
17286 // Class Configuration |
|
17287 var classes = (this._getClasses) ? this._getClasses() : [this.constructor], |
|
17288 plug = [], |
|
17289 unplug = {}, |
|
17290 constructor, i, classPlug, classUnplug, pluginClassName; |
|
17291 |
|
17292 // TODO: Room for optimization. Can we apply statically/unplug in same pass? |
|
17293 for (i = classes.length - 1; i >= 0; i--) { |
|
17294 constructor = classes[i]; |
|
17295 |
|
17296 classUnplug = constructor._UNPLUG; |
|
17297 if (classUnplug) { |
|
17298 // subclasses over-write |
|
17299 Y.mix(unplug, classUnplug, true); |
|
17300 } |
|
17301 |
|
17302 classPlug = constructor._PLUG; |
|
17303 if (classPlug) { |
|
17304 // subclasses over-write |
|
17305 Y.mix(plug, classPlug, true); |
|
17306 } |
|
17307 } |
|
17308 |
|
17309 for (pluginClassName in plug) { |
|
17310 if (plug.hasOwnProperty(pluginClassName)) { |
|
17311 if (!unplug[pluginClassName]) { |
|
17312 this.plug(plug[pluginClassName]); |
|
17313 } |
|
17314 } |
|
17315 } |
|
17316 |
|
17317 // User Configuration |
|
17318 if (config && config.plugins) { |
|
17319 this.plug(config.plugins); |
|
17320 } |
|
17321 }; |
|
17322 |
|
17323 /** |
|
17324 * Registers plugins to be instantiated at the class level (plugins |
|
17325 * which should be plugged into every instance of the class by default). |
|
17326 * |
|
17327 * @method plug |
|
17328 * @static |
|
17329 * |
|
17330 * @param {Function} hostClass The host class on which to register the plugins |
|
17331 * @param {Function | Array} plugin Either the plugin class, an array of plugin classes or an array of objects (with fn and cfg properties defined) |
|
17332 * @param {Object} config (Optional) If plugin is the plugin class, the configuration for the plugin |
|
17333 * @for Plugin.Host |
|
17334 */ |
|
17335 PluginHost.plug = function(hostClass, plugin, config) { |
|
17336 // Cannot plug into Base, since Plugins derive from Base [ will cause infinite recurrsion ] |
|
17337 var p, i, l, name; |
|
17338 |
|
17339 if (hostClass !== Y.Base) { |
|
17340 hostClass._PLUG = hostClass._PLUG || {}; |
|
17341 |
|
17342 if (!L.isArray(plugin)) { |
|
17343 if (config) { |
|
17344 plugin = {fn:plugin, cfg:config}; |
|
17345 } |
|
17346 plugin = [plugin]; |
|
17347 } |
|
17348 |
|
17349 for (i = 0, l = plugin.length; i < l;i++) { |
|
17350 p = plugin[i]; |
|
17351 name = p.NAME || p.fn.NAME; |
|
17352 hostClass._PLUG[name] = p; |
|
17353 } |
|
17354 } |
|
17355 }; |
|
17356 |
|
17357 /** |
|
17358 * Unregisters any class level plugins which have been registered by the host class, or any |
|
17359 * other class in the hierarchy. |
|
17360 * |
|
17361 * @method unplug |
|
17362 * @static |
|
17363 * |
|
17364 * @param {Function} hostClass The host class from which to unregister the plugins |
|
17365 * @param {Function | Array} plugin The plugin class, or an array of plugin classes |
|
17366 * @for Plugin.Host |
|
17367 */ |
|
17368 PluginHost.unplug = function(hostClass, plugin) { |
|
17369 var p, i, l, name; |
|
17370 |
|
17371 if (hostClass !== Y.Base) { |
|
17372 hostClass._UNPLUG = hostClass._UNPLUG || {}; |
|
17373 |
|
17374 if (!L.isArray(plugin)) { |
|
17375 plugin = [plugin]; |
|
17376 } |
|
17377 |
|
17378 for (i = 0, l = plugin.length; i < l; i++) { |
|
17379 p = plugin[i]; |
|
17380 name = p.NAME; |
|
17381 if (!hostClass._PLUG[name]) { |
|
17382 hostClass._UNPLUG[name] = p; |
|
17383 } else { |
|
17384 delete hostClass._PLUG[name]; |
|
17385 } |
|
17386 } |
|
17387 } |
|
17388 }; |
|
17389 |
|
17390 |
|
17391 }, '@VERSION@', {"requires": ["pluginhost-base"]}); |
|
17392 YUI.add('event-delegate', function (Y, NAME) { |
|
17393 |
|
17394 /** |
|
17395 * Adds event delegation support to the library. |
|
17396 * |
|
17397 * @module event |
|
17398 * @submodule event-delegate |
|
17399 */ |
|
17400 |
|
17401 var toArray = Y.Array, |
|
17402 YLang = Y.Lang, |
|
17403 isString = YLang.isString, |
|
17404 isObject = YLang.isObject, |
|
17405 isArray = YLang.isArray, |
|
17406 selectorTest = Y.Selector.test, |
|
17407 detachCategories = Y.Env.evt.handles; |
|
17408 |
|
17409 /** |
|
17410 * <p>Sets up event delegation on a container element. The delegated event |
|
17411 * will use a supplied selector or filtering function to test if the event |
|
17412 * references at least one node that should trigger the subscription |
|
17413 * callback.</p> |
|
17414 * |
|
17415 * <p>Selector string filters will trigger the callback if the event originated |
|
17416 * from a node that matches it or is contained in a node that matches it. |
|
17417 * Function filters are called for each Node up the parent axis to the |
|
17418 * subscribing container node, and receive at each level the Node and the event |
|
17419 * object. The function should return true (or a truthy value) if that Node |
|
17420 * should trigger the subscription callback. Note, it is possible for filters |
|
17421 * to match multiple Nodes for a single event. In this case, the delegate |
|
17422 * callback will be executed for each matching Node.</p> |
|
17423 * |
|
17424 * <p>For each matching Node, the callback will be executed with its 'this' |
|
17425 * object set to the Node matched by the filter (unless a specific context was |
|
17426 * provided during subscription), and the provided event's |
|
17427 * <code>currentTarget</code> will also be set to the matching Node. The |
|
17428 * containing Node from which the subscription was originally made can be |
|
17429 * referenced as <code>e.container</code>. |
|
17430 * |
|
17431 * @method delegate |
|
17432 * @param type {String} the event type to delegate |
|
17433 * @param fn {Function} the callback function to execute. This function |
|
17434 * will be provided the event object for the delegated event. |
|
17435 * @param el {String|node} the element that is the delegation container |
|
17436 * @param filter {string|Function} a selector that must match the target of the |
|
17437 * event or a function to test target and its parents for a match |
|
17438 * @param context optional argument that specifies what 'this' refers to. |
|
17439 * @param args* 0..n additional arguments to pass on to the callback function. |
|
17440 * These arguments will be added after the event object. |
|
17441 * @return {EventHandle} the detach handle |
|
17442 * @static |
|
17443 * @for Event |
|
17444 */ |
|
17445 function delegate(type, fn, el, filter) { |
|
17446 var args = toArray(arguments, 0, true), |
|
17447 query = isString(el) ? el : null, |
|
17448 typeBits, synth, container, categories, cat, i, len, handles, handle; |
|
17449 |
|
17450 // Support Y.delegate({ click: fnA, key: fnB }, el, filter, ...); |
|
17451 // and Y.delegate(['click', 'key'], fn, el, filter, ...); |
|
17452 if (isObject(type)) { |
|
17453 handles = []; |
|
17454 |
|
17455 if (isArray(type)) { |
|
17456 for (i = 0, len = type.length; i < len; ++i) { |
|
17457 args[0] = type[i]; |
|
17458 handles.push(Y.delegate.apply(Y, args)); |
|
17459 } |
|
17460 } else { |
|
17461 // Y.delegate({'click', fn}, el, filter) => |
|
17462 // Y.delegate('click', fn, el, filter) |
|
17463 args.unshift(null); // one arg becomes two; need to make space |
|
17464 |
|
17465 for (i in type) { |
|
17466 if (type.hasOwnProperty(i)) { |
|
17467 args[0] = i; |
|
17468 args[1] = type[i]; |
|
17469 handles.push(Y.delegate.apply(Y, args)); |
|
17470 } |
|
17471 } |
|
17472 } |
|
17473 |
|
17474 return new Y.EventHandle(handles); |
|
17475 } |
|
17476 |
|
17477 typeBits = type.split(/\|/); |
|
17478 |
|
17479 if (typeBits.length > 1) { |
|
17480 cat = typeBits.shift(); |
|
17481 args[0] = type = typeBits.shift(); |
|
17482 } |
|
17483 |
|
17484 synth = Y.Node.DOM_EVENTS[type]; |
|
17485 |
|
17486 if (isObject(synth) && synth.delegate) { |
|
17487 handle = synth.delegate.apply(synth, arguments); |
|
17488 } |
|
17489 |
|
17490 if (!handle) { |
|
17491 if (!type || !fn || !el || !filter) { |
|
17492 return; |
|
17493 } |
|
17494 |
|
17495 container = (query) ? Y.Selector.query(query, null, true) : el; |
|
17496 |
|
17497 if (!container && isString(el)) { |
|
17498 handle = Y.on('available', function () { |
|
17499 Y.mix(handle, Y.delegate.apply(Y, args), true); |
|
17500 }, el); |
|
17501 } |
|
17502 |
|
17503 if (!handle && container) { |
|
17504 args.splice(2, 2, container); // remove the filter |
|
17505 |
|
17506 handle = Y.Event._attach(args, { facade: false }); |
|
17507 handle.sub.filter = filter; |
|
17508 handle.sub._notify = delegate.notifySub; |
|
17509 } |
|
17510 } |
|
17511 |
|
17512 if (handle && cat) { |
|
17513 categories = detachCategories[cat] || (detachCategories[cat] = {}); |
|
17514 categories = categories[type] || (categories[type] = []); |
|
17515 categories.push(handle); |
|
17516 } |
|
17517 |
|
17518 return handle; |
|
17519 } |
|
17520 |
|
17521 /** |
|
17522 Overrides the <code>_notify</code> method on the normal DOM subscription to |
|
17523 inject the filtering logic and only proceed in the case of a match. |
|
17524 |
|
17525 This method is hosted as a private property of the `delegate` method |
|
17526 (e.g. `Y.delegate.notifySub`) |
|
17527 |
|
17528 @method notifySub |
|
17529 @param thisObj {Object} default 'this' object for the callback |
|
17530 @param args {Array} arguments passed to the event's <code>fire()</code> |
|
17531 @param ce {CustomEvent} the custom event managing the DOM subscriptions for |
|
17532 the subscribed event on the subscribing node. |
|
17533 @return {Boolean} false if the event was stopped |
|
17534 @private |
|
17535 @static |
|
17536 @since 3.2.0 |
|
17537 **/ |
|
17538 delegate.notifySub = function (thisObj, args, ce) { |
|
17539 // Preserve args for other subscribers |
|
17540 args = args.slice(); |
|
17541 if (this.args) { |
|
17542 args.push.apply(args, this.args); |
|
17543 } |
|
17544 |
|
17545 // Only notify subs if the event occurred on a targeted element |
|
17546 var currentTarget = delegate._applyFilter(this.filter, args, ce), |
|
17547 //container = e.currentTarget, |
|
17548 e, i, len, ret; |
|
17549 |
|
17550 if (currentTarget) { |
|
17551 // Support multiple matches up the the container subtree |
|
17552 currentTarget = toArray(currentTarget); |
|
17553 |
|
17554 // The second arg is the currentTarget, but we'll be reusing this |
|
17555 // facade, replacing the currentTarget for each use, so it doesn't |
|
17556 // matter what element we seed it with. |
|
17557 e = args[0] = new Y.DOMEventFacade(args[0], ce.el, ce); |
|
17558 |
|
17559 e.container = Y.one(ce.el); |
|
17560 |
|
17561 for (i = 0, len = currentTarget.length; i < len && !e.stopped; ++i) { |
|
17562 e.currentTarget = Y.one(currentTarget[i]); |
|
17563 |
|
17564 ret = this.fn.apply(this.context || e.currentTarget, args); |
|
17565 |
|
17566 if (ret === false) { // stop further notifications |
|
17567 break; |
|
17568 } |
|
17569 } |
|
17570 |
|
17571 return ret; |
|
17572 } |
|
17573 }; |
|
17574 |
|
17575 /** |
|
17576 Compiles a selector string into a filter function to identify whether |
|
17577 Nodes along the parent axis of an event's target should trigger event |
|
17578 notification. |
|
17579 |
|
17580 This function is memoized, so previously compiled filter functions are |
|
17581 returned if the same selector string is provided. |
|
17582 |
|
17583 This function may be useful when defining synthetic events for delegate |
|
17584 handling. |
|
17585 |
|
17586 Hosted as a property of the `delegate` method (e.g. `Y.delegate.compileFilter`). |
|
17587 |
|
17588 @method compileFilter |
|
17589 @param selector {String} the selector string to base the filtration on |
|
17590 @return {Function} |
|
17591 @since 3.2.0 |
|
17592 @static |
|
17593 **/ |
|
17594 delegate.compileFilter = Y.cached(function (selector) { |
|
17595 return function (target, e) { |
|
17596 return selectorTest(target._node, selector, |
|
17597 (e.currentTarget === e.target) ? null : e.currentTarget._node); |
|
17598 }; |
|
17599 }); |
|
17600 |
|
17601 /** |
|
17602 Regex to test for disabled elements during filtering. This is only relevant to |
|
17603 IE to normalize behavior with other browsers, which swallow events that occur |
|
17604 to disabled elements. IE fires the event from the parent element instead of the |
|
17605 original target, though it does preserve `event.srcElement` as the disabled |
|
17606 element. IE also supports disabled on `<a>`, but the event still bubbles, so it |
|
17607 acts more like `e.preventDefault()` plus styling. That issue is not handled here |
|
17608 because other browsers fire the event on the `<a>`, so delegate is supported in |
|
17609 both cases. |
|
17610 |
|
17611 @property _disabledRE |
|
17612 @type {RegExp} |
|
17613 @protected |
|
17614 @since 3.8.1 |
|
17615 **/ |
|
17616 delegate._disabledRE = /^(?:button|input|select|textarea)$/i; |
|
17617 |
|
17618 /** |
|
17619 Walks up the parent axis of an event's target, and tests each element |
|
17620 against a supplied filter function. If any Nodes, including the container, |
|
17621 satisfy the filter, the delegated callback will be triggered for each. |
|
17622 |
|
17623 Hosted as a protected property of the `delegate` method (e.g. |
|
17624 `Y.delegate._applyFilter`). |
|
17625 |
|
17626 @method _applyFilter |
|
17627 @param filter {Function} boolean function to test for inclusion in event |
|
17628 notification |
|
17629 @param args {Array} the arguments that would be passed to subscribers |
|
17630 @param ce {CustomEvent} the DOM event wrapper |
|
17631 @return {Node|Node[]|undefined} The Node or Nodes that satisfy the filter |
|
17632 @protected |
|
17633 **/ |
|
17634 delegate._applyFilter = function (filter, args, ce) { |
|
17635 var e = args[0], |
|
17636 container = ce.el, // facadeless events in IE, have no e.currentTarget |
|
17637 target = e.target || e.srcElement, |
|
17638 match = [], |
|
17639 isContainer = false; |
|
17640 |
|
17641 // Resolve text nodes to their containing element |
|
17642 if (target.nodeType === 3) { |
|
17643 target = target.parentNode; |
|
17644 } |
|
17645 |
|
17646 // For IE. IE propagates events from the parent element of disabled |
|
17647 // elements, where other browsers swallow the event entirely. To normalize |
|
17648 // this in IE, filtering for matching elements should abort if the target |
|
17649 // is a disabled form control. |
|
17650 if (target.disabled && delegate._disabledRE.test(target.nodeName)) { |
|
17651 return match; |
|
17652 } |
|
17653 |
|
17654 // passing target as the first arg rather than leaving well enough alone |
|
17655 // making 'this' in the filter function refer to the target. This is to |
|
17656 // support bound filter functions. |
|
17657 args.unshift(target); |
|
17658 |
|
17659 if (isString(filter)) { |
|
17660 while (target) { |
|
17661 isContainer = (target === container); |
|
17662 if (selectorTest(target, filter, (isContainer ? null: container))) { |
|
17663 match.push(target); |
|
17664 } |
|
17665 |
|
17666 if (isContainer) { |
|
17667 break; |
|
17668 } |
|
17669 |
|
17670 target = target.parentNode; |
|
17671 } |
|
17672 } else { |
|
17673 // filter functions are implementer code and should receive wrappers |
|
17674 args[0] = Y.one(target); |
|
17675 args[1] = new Y.DOMEventFacade(e, container, ce); |
|
17676 |
|
17677 while (target) { |
|
17678 // filter(target, e, extra args...) - this === target |
|
17679 if (filter.apply(args[0], args)) { |
|
17680 match.push(target); |
|
17681 } |
|
17682 |
|
17683 if (target === container) { |
|
17684 break; |
|
17685 } |
|
17686 |
|
17687 target = target.parentNode; |
|
17688 args[0] = Y.one(target); |
|
17689 } |
|
17690 args[1] = e; // restore the raw DOM event |
|
17691 } |
|
17692 |
|
17693 if (match.length <= 1) { |
|
17694 match = match[0]; // single match or undefined |
|
17695 } |
|
17696 |
|
17697 // remove the target |
|
17698 args.shift(); |
|
17699 |
|
17700 return match; |
|
17701 }; |
|
17702 |
|
17703 /** |
|
17704 * Sets up event delegation on a container element. The delegated event |
|
17705 * will use a supplied filter to test if the callback should be executed. |
|
17706 * This filter can be either a selector string or a function that returns |
|
17707 * a Node to use as the currentTarget for the event. |
|
17708 * |
|
17709 * The event object for the delegated event is supplied to the callback |
|
17710 * function. It is modified slightly in order to support all properties |
|
17711 * that may be needed for event delegation. 'currentTarget' is set to |
|
17712 * the element that matched the selector string filter or the Node returned |
|
17713 * from the filter function. 'container' is set to the element that the |
|
17714 * listener is delegated from (this normally would be the 'currentTarget'). |
|
17715 * |
|
17716 * Filter functions will be called with the arguments that would be passed to |
|
17717 * the callback function, including the event object as the first parameter. |
|
17718 * The function should return false (or a falsey value) if the success criteria |
|
17719 * aren't met, and the Node to use as the event's currentTarget and 'this' |
|
17720 * object if they are. |
|
17721 * |
|
17722 * @method delegate |
|
17723 * @param type {string} the event type to delegate |
|
17724 * @param fn {function} the callback function to execute. This function |
|
17725 * will be provided the event object for the delegated event. |
|
17726 * @param el {string|node} the element that is the delegation container |
|
17727 * @param filter {string|function} a selector that must match the target of the |
|
17728 * event or a function that returns a Node or false. |
|
17729 * @param context optional argument that specifies what 'this' refers to. |
|
17730 * @param args* 0..n additional arguments to pass on to the callback function. |
|
17731 * These arguments will be added after the event object. |
|
17732 * @return {EventHandle} the detach handle |
|
17733 * @for YUI |
|
17734 */ |
|
17735 Y.delegate = Y.Event.delegate = delegate; |
|
17736 |
|
17737 |
|
17738 }, '@VERSION@', {"requires": ["node-base"]}); |
|
17739 YUI.add('node-event-delegate', function (Y, NAME) { |
|
17740 |
|
17741 /** |
|
17742 * Functionality to make the node a delegated event container |
|
17743 * @module node |
|
17744 * @submodule node-event-delegate |
|
17745 */ |
|
17746 |
|
17747 /** |
|
17748 * <p>Sets up a delegation listener for an event occurring inside the Node. |
|
17749 * The delegated event will be verified against a supplied selector or |
|
17750 * filtering function to test if the event references at least one node that |
|
17751 * should trigger the subscription callback.</p> |
|
17752 * |
|
17753 * <p>Selector string filters will trigger the callback if the event originated |
|
17754 * from a node that matches it or is contained in a node that matches it. |
|
17755 * Function filters are called for each Node up the parent axis to the |
|
17756 * subscribing container node, and receive at each level the Node and the event |
|
17757 * object. The function should return true (or a truthy value) if that Node |
|
17758 * should trigger the subscription callback. Note, it is possible for filters |
|
17759 * to match multiple Nodes for a single event. In this case, the delegate |
|
17760 * callback will be executed for each matching Node.</p> |
|
17761 * |
|
17762 * <p>For each matching Node, the callback will be executed with its 'this' |
|
17763 * object set to the Node matched by the filter (unless a specific context was |
|
17764 * provided during subscription), and the provided event's |
|
17765 * <code>currentTarget</code> will also be set to the matching Node. The |
|
17766 * containing Node from which the subscription was originally made can be |
|
17767 * referenced as <code>e.container</code>. |
|
17768 * |
|
17769 * @method delegate |
|
17770 * @param type {String} the event type to delegate |
|
17771 * @param fn {Function} the callback function to execute. This function |
|
17772 * will be provided the event object for the delegated event. |
|
17773 * @param spec {String|Function} a selector that must match the target of the |
|
17774 * event or a function to test target and its parents for a match |
|
17775 * @param context {Object} optional argument that specifies what 'this' refers to. |
|
17776 * @param args* {any} 0..n additional arguments to pass on to the callback function. |
|
17777 * These arguments will be added after the event object. |
|
17778 * @return {EventHandle} the detach handle |
|
17779 * @for Node |
|
17780 */ |
|
17781 Y.Node.prototype.delegate = function(type) { |
|
17782 |
|
17783 var args = Y.Array(arguments, 0, true), |
|
17784 index = (Y.Lang.isObject(type) && !Y.Lang.isArray(type)) ? 1 : 2; |
|
17785 |
|
17786 args.splice(index, 0, this._node); |
|
17787 |
|
17788 return Y.delegate.apply(Y, args); |
|
17789 }; |
|
17790 |
|
17791 |
|
17792 }, '@VERSION@', {"requires": ["node-base", "event-delegate"]}); |
|
17793 YUI.add('node-pluginhost', function (Y, NAME) { |
|
17794 |
|
17795 /** |
|
17796 * @module node |
|
17797 * @submodule node-pluginhost |
|
17798 */ |
|
17799 |
|
17800 /** |
|
17801 * Registers plugins to be instantiated at the class level (plugins |
|
17802 * which should be plugged into every instance of Node by default). |
|
17803 * |
|
17804 * @method plug |
|
17805 * @static |
|
17806 * @for Node |
|
17807 * @param {Function | Array} plugin Either the plugin class, an array of plugin classes or an array of objects (with fn and cfg properties defined) |
|
17808 * @param {Object} config (Optional) If plugin is the plugin class, the configuration for the plugin |
|
17809 */ |
|
17810 Y.Node.plug = function() { |
|
17811 var args = Y.Array(arguments); |
|
17812 args.unshift(Y.Node); |
|
17813 Y.Plugin.Host.plug.apply(Y.Base, args); |
|
17814 return Y.Node; |
|
17815 }; |
|
17816 |
|
17817 /** |
|
17818 * Unregisters any class level plugins which have been registered by the Node |
|
17819 * |
|
17820 * @method unplug |
|
17821 * @static |
|
17822 * |
|
17823 * @param {Function | Array} plugin The plugin class, or an array of plugin classes |
|
17824 */ |
|
17825 Y.Node.unplug = function() { |
|
17826 var args = Y.Array(arguments); |
|
17827 args.unshift(Y.Node); |
|
17828 Y.Plugin.Host.unplug.apply(Y.Base, args); |
|
17829 return Y.Node; |
|
17830 }; |
|
17831 |
|
17832 Y.mix(Y.Node, Y.Plugin.Host, false, null, 1); |
|
17833 |
|
17834 // allow batching of plug/unplug via NodeList |
|
17835 // doesn't use NodeList.importMethod because we need real Nodes (not tmpNode) |
|
17836 /** |
|
17837 * Adds a plugin to each node in the NodeList. |
|
17838 * This will instantiate the plugin and attach it to the configured namespace on each node |
|
17839 * @method plug |
|
17840 * @for NodeList |
|
17841 * @param P {Function | Object |Array} Accepts the plugin class, or an |
|
17842 * object with a "fn" property specifying the plugin class and |
|
17843 * a "cfg" property specifying the configuration for the Plugin. |
|
17844 * <p> |
|
17845 * Additionally an Array can also be passed in, with the above function or |
|
17846 * object values, allowing the user to add multiple plugins in a single call. |
|
17847 * </p> |
|
17848 * @param config (Optional) If the first argument is the plugin class, the second argument |
|
17849 * can be the configuration for the plugin. |
|
17850 * @chainable |
|
17851 */ |
|
17852 Y.NodeList.prototype.plug = function() { |
|
17853 var args = arguments; |
|
17854 Y.NodeList.each(this, function(node) { |
|
17855 Y.Node.prototype.plug.apply(Y.one(node), args); |
|
17856 }); |
|
17857 return this; |
|
17858 }; |
|
17859 |
|
17860 /** |
|
17861 * Removes a plugin from all nodes in the NodeList. This will destroy the |
|
17862 * plugin instance and delete the namespace each node. |
|
17863 * @method unplug |
|
17864 * @for NodeList |
|
17865 * @param {String | Function} plugin The namespace of the plugin, or the plugin class with the static NS namespace property defined. If not provided, |
|
17866 * all registered plugins are unplugged. |
|
17867 * @chainable |
|
17868 */ |
|
17869 Y.NodeList.prototype.unplug = function() { |
|
17870 var args = arguments; |
|
17871 Y.NodeList.each(this, function(node) { |
|
17872 Y.Node.prototype.unplug.apply(Y.one(node), args); |
|
17873 }); |
|
17874 return this; |
|
17875 }; |
|
17876 |
|
17877 |
|
17878 }, '@VERSION@', {"requires": ["node-base", "pluginhost"]}); |
|
17879 YUI.add('node-screen', function (Y, NAME) { |
|
17880 |
|
17881 /** |
|
17882 * Extended Node interface for managing regions and screen positioning. |
|
17883 * Adds support for positioning elements and normalizes window size and scroll detection. |
|
17884 * @module node |
|
17885 * @submodule node-screen |
|
17886 */ |
|
17887 |
|
17888 // these are all "safe" returns, no wrapping required |
|
17889 Y.each([ |
|
17890 /** |
|
17891 * Returns the inner width of the viewport (exludes scrollbar). |
|
17892 * @config winWidth |
|
17893 * @for Node |
|
17894 * @type {Int} |
|
17895 */ |
|
17896 'winWidth', |
|
17897 |
|
17898 /** |
|
17899 * Returns the inner height of the viewport (exludes scrollbar). |
|
17900 * @config winHeight |
|
17901 * @type {Int} |
|
17902 */ |
|
17903 'winHeight', |
|
17904 |
|
17905 /** |
|
17906 * Document width |
|
17907 * @config docWidth |
|
17908 * @type {Int} |
|
17909 */ |
|
17910 'docWidth', |
|
17911 |
|
17912 /** |
|
17913 * Document height |
|
17914 * @config docHeight |
|
17915 * @type {Int} |
|
17916 */ |
|
17917 'docHeight', |
|
17918 |
|
17919 /** |
|
17920 * Pixel distance the page has been scrolled horizontally |
|
17921 * @config docScrollX |
|
17922 * @type {Int} |
|
17923 */ |
|
17924 'docScrollX', |
|
17925 |
|
17926 /** |
|
17927 * Pixel distance the page has been scrolled vertically |
|
17928 * @config docScrollY |
|
17929 * @type {Int} |
|
17930 */ |
|
17931 'docScrollY' |
|
17932 ], |
|
17933 function(name) { |
|
17934 Y.Node.ATTRS[name] = { |
|
17935 getter: function() { |
|
17936 var args = Array.prototype.slice.call(arguments); |
|
17937 args.unshift(Y.Node.getDOMNode(this)); |
|
17938 |
|
17939 return Y.DOM[name].apply(this, args); |
|
17940 } |
|
17941 }; |
|
17942 } |
|
17943 ); |
|
17944 |
|
17945 Y.Node.ATTRS.scrollLeft = { |
|
17946 getter: function() { |
|
17947 var node = Y.Node.getDOMNode(this); |
|
17948 return ('scrollLeft' in node) ? node.scrollLeft : Y.DOM.docScrollX(node); |
|
17949 }, |
|
17950 |
|
17951 setter: function(val) { |
|
17952 var node = Y.Node.getDOMNode(this); |
|
17953 if (node) { |
|
17954 if ('scrollLeft' in node) { |
|
17955 node.scrollLeft = val; |
|
17956 } else if (node.document || node.nodeType === 9) { |
|
17957 Y.DOM._getWin(node).scrollTo(val, Y.DOM.docScrollY(node)); // scroll window if win or doc |
|
17958 } |
|
17959 } else { |
|
17960 } |
|
17961 } |
|
17962 }; |
|
17963 |
|
17964 Y.Node.ATTRS.scrollTop = { |
|
17965 getter: function() { |
|
17966 var node = Y.Node.getDOMNode(this); |
|
17967 return ('scrollTop' in node) ? node.scrollTop : Y.DOM.docScrollY(node); |
|
17968 }, |
|
17969 |
|
17970 setter: function(val) { |
|
17971 var node = Y.Node.getDOMNode(this); |
|
17972 if (node) { |
|
17973 if ('scrollTop' in node) { |
|
17974 node.scrollTop = val; |
|
17975 } else if (node.document || node.nodeType === 9) { |
|
17976 Y.DOM._getWin(node).scrollTo(Y.DOM.docScrollX(node), val); // scroll window if win or doc |
|
17977 } |
|
17978 } else { |
|
17979 } |
|
17980 } |
|
17981 }; |
|
17982 |
|
17983 Y.Node.importMethod(Y.DOM, [ |
|
17984 /** |
|
17985 * Gets the current position of the node in page coordinates. |
|
17986 * @method getXY |
|
17987 * @for Node |
|
17988 * @return {Array} The XY position of the node |
|
17989 */ |
|
17990 'getXY', |
|
17991 |
|
17992 /** |
|
17993 * Set the position of the node in page coordinates, regardless of how the node is positioned. |
|
17994 * @method setXY |
|
17995 * @param {Array} xy Contains X & Y values for new position (coordinates are page-based) |
|
17996 * @chainable |
|
17997 */ |
|
17998 'setXY', |
|
17999 |
|
18000 /** |
|
18001 * Gets the current position of the node in page coordinates. |
|
18002 * @method getX |
|
18003 * @return {Int} The X position of the node |
|
18004 */ |
|
18005 'getX', |
|
18006 |
|
18007 /** |
|
18008 * Set the position of the node in page coordinates, regardless of how the node is positioned. |
|
18009 * @method setX |
|
18010 * @param {Int} x X value for new position (coordinates are page-based) |
|
18011 * @chainable |
|
18012 */ |
|
18013 'setX', |
|
18014 |
|
18015 /** |
|
18016 * Gets the current position of the node in page coordinates. |
|
18017 * @method getY |
|
18018 * @return {Int} The Y position of the node |
|
18019 */ |
|
18020 'getY', |
|
18021 |
|
18022 /** |
|
18023 * Set the position of the node in page coordinates, regardless of how the node is positioned. |
|
18024 * @method setY |
|
18025 * @param {Int} y Y value for new position (coordinates are page-based) |
|
18026 * @chainable |
|
18027 */ |
|
18028 'setY', |
|
18029 |
|
18030 /** |
|
18031 * Swaps the XY position of this node with another node. |
|
18032 * @method swapXY |
|
18033 * @param {Node | HTMLElement} otherNode The node to swap with. |
|
18034 * @chainable |
|
18035 */ |
|
18036 'swapXY' |
|
18037 ]); |
|
18038 |
|
18039 /** |
|
18040 * @module node |
|
18041 * @submodule node-screen |
|
18042 */ |
|
18043 |
|
18044 /** |
|
18045 * Returns a region object for the node |
|
18046 * @config region |
|
18047 * @for Node |
|
18048 * @type Node |
|
18049 */ |
|
18050 Y.Node.ATTRS.region = { |
|
18051 getter: function() { |
|
18052 var node = this.getDOMNode(), |
|
18053 region; |
|
18054 |
|
18055 if (node && !node.tagName) { |
|
18056 if (node.nodeType === 9) { // document |
|
18057 node = node.documentElement; |
|
18058 } |
|
18059 } |
|
18060 if (Y.DOM.isWindow(node)) { |
|
18061 region = Y.DOM.viewportRegion(node); |
|
18062 } else { |
|
18063 region = Y.DOM.region(node); |
|
18064 } |
|
18065 return region; |
|
18066 } |
|
18067 }; |
|
18068 |
|
18069 /** |
|
18070 * Returns a region object for the node's viewport |
|
18071 * @config viewportRegion |
|
18072 * @type Node |
|
18073 */ |
|
18074 Y.Node.ATTRS.viewportRegion = { |
|
18075 getter: function() { |
|
18076 return Y.DOM.viewportRegion(Y.Node.getDOMNode(this)); |
|
18077 } |
|
18078 }; |
|
18079 |
|
18080 Y.Node.importMethod(Y.DOM, 'inViewportRegion'); |
|
18081 |
|
18082 // these need special treatment to extract 2nd node arg |
|
18083 /** |
|
18084 * Compares the intersection of the node with another node or region |
|
18085 * @method intersect |
|
18086 * @for Node |
|
18087 * @param {Node|Object} node2 The node or region to compare with. |
|
18088 * @param {Object} altRegion An alternate region to use (rather than this node's). |
|
18089 * @return {Object} An object representing the intersection of the regions. |
|
18090 */ |
|
18091 Y.Node.prototype.intersect = function(node2, altRegion) { |
|
18092 var node1 = Y.Node.getDOMNode(this); |
|
18093 if (Y.instanceOf(node2, Y.Node)) { // might be a region object |
|
18094 node2 = Y.Node.getDOMNode(node2); |
|
18095 } |
|
18096 return Y.DOM.intersect(node1, node2, altRegion); |
|
18097 }; |
|
18098 |
|
18099 /** |
|
18100 * Determines whether or not the node is within the giving region. |
|
18101 * @method inRegion |
|
18102 * @param {Node|Object} node2 The node or region to compare with. |
|
18103 * @param {Boolean} all Whether or not all of the node must be in the region. |
|
18104 * @param {Object} altRegion An alternate region to use (rather than this node's). |
|
18105 * @return {Boolean} True if in region, false if not. |
|
18106 */ |
|
18107 Y.Node.prototype.inRegion = function(node2, all, altRegion) { |
|
18108 var node1 = Y.Node.getDOMNode(this); |
|
18109 if (Y.instanceOf(node2, Y.Node)) { // might be a region object |
|
18110 node2 = Y.Node.getDOMNode(node2); |
|
18111 } |
|
18112 return Y.DOM.inRegion(node1, node2, all, altRegion); |
|
18113 }; |
|
18114 |
|
18115 |
|
18116 }, '@VERSION@', {"requires": ["dom-screen", "node-base"]}); |
|
18117 YUI.add('node-style', function (Y, NAME) { |
|
18118 |
|
18119 (function(Y) { |
|
18120 /** |
|
18121 * Extended Node interface for managing node styles. |
|
18122 * @module node |
|
18123 * @submodule node-style |
|
18124 */ |
|
18125 |
|
18126 Y.mix(Y.Node.prototype, { |
|
18127 /** |
|
18128 * Sets a style property of the node. |
|
18129 * Use camelCase (e.g. 'backgroundColor') for multi-word properties. |
|
18130 * @method setStyle |
|
18131 * @param {String} attr The style attribute to set. |
|
18132 * @param {String|Number} val The value. |
|
18133 * @chainable |
|
18134 */ |
|
18135 setStyle: function(attr, val) { |
|
18136 Y.DOM.setStyle(this._node, attr, val); |
|
18137 return this; |
|
18138 }, |
|
18139 |
|
18140 /** |
|
18141 * Sets multiple style properties on the node. |
|
18142 * Use camelCase (e.g. 'backgroundColor') for multi-word properties. |
|
18143 * @method setStyles |
|
18144 * @param {Object} hash An object literal of property:value pairs. |
|
18145 * @chainable |
|
18146 */ |
|
18147 setStyles: function(hash) { |
|
18148 Y.DOM.setStyles(this._node, hash); |
|
18149 return this; |
|
18150 }, |
|
18151 |
|
18152 /** |
|
18153 * Returns the style's current value. |
|
18154 * Use camelCase (e.g. 'backgroundColor') for multi-word properties. |
|
18155 * @method getStyle |
|
18156 * @for Node |
|
18157 * @param {String} attr The style attribute to retrieve. |
|
18158 * @return {String} The current value of the style property for the element. |
|
18159 */ |
|
18160 |
|
18161 getStyle: function(attr) { |
|
18162 return Y.DOM.getStyle(this._node, attr); |
|
18163 }, |
|
18164 |
|
18165 /** |
|
18166 * Returns the computed value for the given style property. |
|
18167 * Use camelCase (e.g. 'backgroundColor') for multi-word properties. |
|
18168 * @method getComputedStyle |
|
18169 * @param {String} attr The style attribute to retrieve. |
|
18170 * @return {String} The computed value of the style property for the element. |
|
18171 */ |
|
18172 getComputedStyle: function(attr) { |
|
18173 return Y.DOM.getComputedStyle(this._node, attr); |
|
18174 } |
|
18175 }); |
|
18176 |
|
18177 /** |
|
18178 * Returns an array of values for each node. |
|
18179 * Use camelCase (e.g. 'backgroundColor') for multi-word properties. |
|
18180 * @method getStyle |
|
18181 * @for NodeList |
|
18182 * @see Node.getStyle |
|
18183 * @param {String} attr The style attribute to retrieve. |
|
18184 * @return {Array} The current values of the style property for the element. |
|
18185 */ |
|
18186 |
|
18187 /** |
|
18188 * Returns an array of the computed value for each node. |
|
18189 * Use camelCase (e.g. 'backgroundColor') for multi-word properties. |
|
18190 * @method getComputedStyle |
|
18191 * @see Node.getComputedStyle |
|
18192 * @param {String} attr The style attribute to retrieve. |
|
18193 * @return {Array} The computed values for each node. |
|
18194 */ |
|
18195 |
|
18196 /** |
|
18197 * Sets a style property on each node. |
|
18198 * Use camelCase (e.g. 'backgroundColor') for multi-word properties. |
|
18199 * @method setStyle |
|
18200 * @see Node.setStyle |
|
18201 * @param {String} attr The style attribute to set. |
|
18202 * @param {String|Number} val The value. |
|
18203 * @chainable |
|
18204 */ |
|
18205 |
|
18206 /** |
|
18207 * Sets multiple style properties on each node. |
|
18208 * Use camelCase (e.g. 'backgroundColor') for multi-word properties. |
|
18209 * @method setStyles |
|
18210 * @see Node.setStyles |
|
18211 * @param {Object} hash An object literal of property:value pairs. |
|
18212 * @chainable |
|
18213 */ |
|
18214 |
|
18215 // These are broken out to handle undefined return (avoid false positive for |
|
18216 // chainable) |
|
18217 |
|
18218 Y.NodeList.importMethod(Y.Node.prototype, ['getStyle', 'getComputedStyle', 'setStyle', 'setStyles']); |
|
18219 })(Y); |
|
18220 |
|
18221 |
|
18222 }, '@VERSION@', {"requires": ["dom-style", "node-base"]}); |
|
18223 YUI.add('querystring-stringify-simple', function (Y, NAME) { |
|
18224 |
|
18225 /*global Y */ |
|
18226 /** |
|
18227 * <p>Provides Y.QueryString.stringify method for converting objects to Query Strings. |
|
18228 * This is a subset implementation of the full querystring-stringify.</p> |
|
18229 * <p>This module provides the bare minimum functionality (encoding a hash of simple values), |
|
18230 * without the additional support for nested data structures. Every key-value pair is |
|
18231 * encoded by encodeURIComponent.</p> |
|
18232 * <p>This module provides a minimalistic way for io to handle single-level objects |
|
18233 * as transaction data.</p> |
|
18234 * |
|
18235 * @module querystring |
|
18236 * @submodule querystring-stringify-simple |
|
18237 */ |
|
18238 |
|
18239 var QueryString = Y.namespace("QueryString"), |
|
18240 EUC = encodeURIComponent; |
|
18241 |
|
18242 |
|
18243 QueryString.stringify = function (obj, c) { |
|
18244 var qs = [], |
|
18245 // Default behavior is false; standard key notation. |
|
18246 s = c && c.arrayKey ? true : false, |
|
18247 key, i, l; |
|
18248 |
|
18249 for (key in obj) { |
|
18250 if (obj.hasOwnProperty(key)) { |
|
18251 if (Y.Lang.isArray(obj[key])) { |
|
18252 for (i = 0, l = obj[key].length; i < l; i++) { |
|
18253 qs.push(EUC(s ? key + '[]' : key) + '=' + EUC(obj[key][i])); |
|
18254 } |
|
18255 } |
|
18256 else { |
|
18257 qs.push(EUC(key) + '=' + EUC(obj[key])); |
|
18258 } |
|
18259 } |
|
18260 } |
|
18261 |
|
18262 return qs.join('&'); |
|
18263 }; |
|
18264 |
|
18265 |
|
18266 }, '@VERSION@', {"requires": ["yui-base"]}); |
|
18267 YUI.add('io-base', function (Y, NAME) { |
|
18268 |
|
18269 /** |
|
18270 Base IO functionality. Provides basic XHR transport support. |
|
18271 |
|
18272 @module io |
|
18273 @submodule io-base |
|
18274 @for IO |
|
18275 **/ |
|
18276 |
|
18277 var // List of events that comprise the IO event lifecycle. |
|
18278 EVENTS = ['start', 'complete', 'end', 'success', 'failure', 'progress'], |
|
18279 |
|
18280 // Whitelist of used XHR response object properties. |
|
18281 XHR_PROPS = ['status', 'statusText', 'responseText', 'responseXML'], |
|
18282 |
|
18283 win = Y.config.win, |
|
18284 uid = 0; |
|
18285 |
|
18286 /** |
|
18287 The IO class is a utility that brokers HTTP requests through a simplified |
|
18288 interface. Specifically, it allows JavaScript to make HTTP requests to |
|
18289 a resource without a page reload. The underlying transport for making |
|
18290 same-domain requests is the XMLHttpRequest object. IO can also use |
|
18291 Flash, if specified as a transport, for cross-domain requests. |
|
18292 |
|
18293 @class IO |
|
18294 @constructor |
|
18295 @param {Object} config Object of EventTarget's publish method configurations |
|
18296 used to configure IO's events. |
|
18297 **/ |
|
18298 function IO (config) { |
|
18299 var io = this; |
|
18300 |
|
18301 io._uid = 'io:' + uid++; |
|
18302 io._init(config); |
|
18303 Y.io._map[io._uid] = io; |
|
18304 } |
|
18305 |
|
18306 IO.prototype = { |
|
18307 //-------------------------------------- |
|
18308 // Properties |
|
18309 //-------------------------------------- |
|
18310 |
|
18311 /** |
|
18312 * A counter that increments for each transaction. |
|
18313 * |
|
18314 * @property _id |
|
18315 * @private |
|
18316 * @type {Number} |
|
18317 */ |
|
18318 _id: 0, |
|
18319 |
|
18320 /** |
|
18321 * Object of IO HTTP headers sent with each transaction. |
|
18322 * |
|
18323 * @property _headers |
|
18324 * @private |
|
18325 * @type {Object} |
|
18326 */ |
|
18327 _headers: { |
|
18328 'X-Requested-With' : 'XMLHttpRequest' |
|
18329 }, |
|
18330 |
|
18331 /** |
|
18332 * Object that stores timeout values for any transaction with a defined |
|
18333 * "timeout" configuration property. |
|
18334 * |
|
18335 * @property _timeout |
|
18336 * @private |
|
18337 * @type {Object} |
|
18338 */ |
|
18339 _timeout: {}, |
|
18340 |
|
18341 //-------------------------------------- |
|
18342 // Methods |
|
18343 //-------------------------------------- |
|
18344 |
|
18345 _init: function(config) { |
|
18346 var io = this, i, len; |
|
18347 |
|
18348 io.cfg = config || {}; |
|
18349 |
|
18350 Y.augment(io, Y.EventTarget); |
|
18351 for (i = 0, len = EVENTS.length; i < len; ++i) { |
|
18352 // Publish IO global events with configurations, if any. |
|
18353 // IO global events are set to broadcast by default. |
|
18354 // These events use the "io:" namespace. |
|
18355 io.publish('io:' + EVENTS[i], Y.merge({ broadcast: 1 }, config)); |
|
18356 // Publish IO transaction events with configurations, if |
|
18357 // any. These events use the "io-trn:" namespace. |
|
18358 io.publish('io-trn:' + EVENTS[i], config); |
|
18359 } |
|
18360 }, |
|
18361 |
|
18362 /** |
|
18363 * Method that creates a unique transaction object for each request. |
|
18364 * |
|
18365 * @method _create |
|
18366 * @private |
|
18367 * @param {Object} cfg Configuration object subset to determine if |
|
18368 * the transaction is an XDR or file upload, |
|
18369 * requiring an alternate transport. |
|
18370 * @param {Number} id Transaction id |
|
18371 * @return {Object} The transaction object |
|
18372 */ |
|
18373 _create: function(config, id) { |
|
18374 var io = this, |
|
18375 transaction = { |
|
18376 id : Y.Lang.isNumber(id) ? id : io._id++, |
|
18377 uid: io._uid |
|
18378 }, |
|
18379 alt = config.xdr ? config.xdr.use : null, |
|
18380 form = config.form && config.form.upload ? 'iframe' : null, |
|
18381 use; |
|
18382 |
|
18383 if (alt === 'native') { |
|
18384 // Non-IE and IE >= 10 can use XHR level 2 and not rely on an |
|
18385 // external transport. |
|
18386 alt = Y.UA.ie && !SUPPORTS_CORS ? 'xdr' : null; |
|
18387 |
|
18388 // Prevent "pre-flight" OPTIONS request by removing the |
|
18389 // `X-Requested-With` HTTP header from CORS requests. This header |
|
18390 // can be added back on a per-request basis, if desired. |
|
18391 io.setHeader('X-Requested-With'); |
|
18392 } |
|
18393 |
|
18394 use = alt || form; |
|
18395 transaction = use ? Y.merge(Y.IO.customTransport(use), transaction) : |
|
18396 Y.merge(Y.IO.defaultTransport(), transaction); |
|
18397 |
|
18398 if (transaction.notify) { |
|
18399 config.notify = function (e, t, c) { io.notify(e, t, c); }; |
|
18400 } |
|
18401 |
|
18402 if (!use) { |
|
18403 if (win && win.FormData && config.data instanceof win.FormData) { |
|
18404 transaction.c.upload.onprogress = function (e) { |
|
18405 io.progress(transaction, e, config); |
|
18406 }; |
|
18407 transaction.c.onload = function (e) { |
|
18408 io.load(transaction, e, config); |
|
18409 }; |
|
18410 transaction.c.onerror = function (e) { |
|
18411 io.error(transaction, e, config); |
|
18412 }; |
|
18413 transaction.upload = true; |
|
18414 } |
|
18415 } |
|
18416 |
|
18417 return transaction; |
|
18418 }, |
|
18419 |
|
18420 _destroy: function(transaction) { |
|
18421 if (win && !transaction.notify && !transaction.xdr) { |
|
18422 if (XHR && !transaction.upload) { |
|
18423 transaction.c.onreadystatechange = null; |
|
18424 } else if (transaction.upload) { |
|
18425 transaction.c.upload.onprogress = null; |
|
18426 transaction.c.onload = null; |
|
18427 transaction.c.onerror = null; |
|
18428 } else if (Y.UA.ie && !transaction.e) { |
|
18429 // IE, when using XMLHttpRequest as an ActiveX Object, will throw |
|
18430 // a "Type Mismatch" error if the event handler is set to "null". |
|
18431 transaction.c.abort(); |
|
18432 } |
|
18433 } |
|
18434 |
|
18435 transaction = transaction.c = null; |
|
18436 }, |
|
18437 |
|
18438 /** |
|
18439 * Method for creating and firing events. |
|
18440 * |
|
18441 * @method _evt |
|
18442 * @private |
|
18443 * @param {String} eventName Event to be published. |
|
18444 * @param {Object} transaction Transaction object. |
|
18445 * @param {Object} config Configuration data subset for event subscription. |
|
18446 */ |
|
18447 _evt: function(eventName, transaction, config) { |
|
18448 var io = this, params, |
|
18449 args = config['arguments'], |
|
18450 emitFacade = io.cfg.emitFacade, |
|
18451 globalEvent = "io:" + eventName, |
|
18452 trnEvent = "io-trn:" + eventName; |
|
18453 |
|
18454 // Workaround for #2532107 |
|
18455 this.detach(trnEvent); |
|
18456 |
|
18457 if (transaction.e) { |
|
18458 transaction.c = { status: 0, statusText: transaction.e }; |
|
18459 } |
|
18460 |
|
18461 // Fire event with parameters or an Event Facade. |
|
18462 params = [ emitFacade ? |
|
18463 { |
|
18464 id: transaction.id, |
|
18465 data: transaction.c, |
|
18466 cfg: config, |
|
18467 'arguments': args |
|
18468 } : |
|
18469 transaction.id |
|
18470 ]; |
|
18471 |
|
18472 if (!emitFacade) { |
|
18473 if (eventName === EVENTS[0] || eventName === EVENTS[2]) { |
|
18474 if (args) { |
|
18475 params.push(args); |
|
18476 } |
|
18477 } else { |
|
18478 if (transaction.evt) { |
|
18479 params.push(transaction.evt); |
|
18480 } else { |
|
18481 params.push(transaction.c); |
|
18482 } |
|
18483 if (args) { |
|
18484 params.push(args); |
|
18485 } |
|
18486 } |
|
18487 } |
|
18488 |
|
18489 params.unshift(globalEvent); |
|
18490 // Fire global events. |
|
18491 io.fire.apply(io, params); |
|
18492 // Fire transaction events, if receivers are defined. |
|
18493 if (config.on) { |
|
18494 params[0] = trnEvent; |
|
18495 io.once(trnEvent, config.on[eventName], config.context || Y); |
|
18496 io.fire.apply(io, params); |
|
18497 } |
|
18498 }, |
|
18499 |
|
18500 /** |
|
18501 * Fires event "io:start" and creates, fires a transaction-specific |
|
18502 * start event, if `config.on.start` is defined. |
|
18503 * |
|
18504 * @method start |
|
18505 * @param {Object} transaction Transaction object. |
|
18506 * @param {Object} config Configuration object for the transaction. |
|
18507 */ |
|
18508 start: function(transaction, config) { |
|
18509 /** |
|
18510 * Signals the start of an IO request. |
|
18511 * @event io:start |
|
18512 */ |
|
18513 this._evt(EVENTS[0], transaction, config); |
|
18514 }, |
|
18515 |
|
18516 /** |
|
18517 * Fires event "io:complete" and creates, fires a |
|
18518 * transaction-specific "complete" event, if config.on.complete is |
|
18519 * defined. |
|
18520 * |
|
18521 * @method complete |
|
18522 * @param {Object} transaction Transaction object. |
|
18523 * @param {Object} config Configuration object for the transaction. |
|
18524 */ |
|
18525 complete: function(transaction, config) { |
|
18526 /** |
|
18527 * Signals the completion of the request-response phase of a |
|
18528 * transaction. Response status and data are accessible, if |
|
18529 * available, in this event. |
|
18530 * @event io:complete |
|
18531 */ |
|
18532 this._evt(EVENTS[1], transaction, config); |
|
18533 }, |
|
18534 |
|
18535 /** |
|
18536 * Fires event "io:end" and creates, fires a transaction-specific "end" |
|
18537 * event, if config.on.end is defined. |
|
18538 * |
|
18539 * @method end |
|
18540 * @param {Object} transaction Transaction object. |
|
18541 * @param {Object} config Configuration object for the transaction. |
|
18542 */ |
|
18543 end: function(transaction, config) { |
|
18544 /** |
|
18545 * Signals the end of the transaction lifecycle. |
|
18546 * @event io:end |
|
18547 */ |
|
18548 this._evt(EVENTS[2], transaction, config); |
|
18549 this._destroy(transaction); |
|
18550 }, |
|
18551 |
|
18552 /** |
|
18553 * Fires event "io:success" and creates, fires a transaction-specific |
|
18554 * "success" event, if config.on.success is defined. |
|
18555 * |
|
18556 * @method success |
|
18557 * @param {Object} transaction Transaction object. |
|
18558 * @param {Object} config Configuration object for the transaction. |
|
18559 */ |
|
18560 success: function(transaction, config) { |
|
18561 /** |
|
18562 * Signals an HTTP response with status in the 2xx range. |
|
18563 * Fires after io:complete. |
|
18564 * @event io:success |
|
18565 */ |
|
18566 this._evt(EVENTS[3], transaction, config); |
|
18567 this.end(transaction, config); |
|
18568 }, |
|
18569 |
|
18570 /** |
|
18571 * Fires event "io:failure" and creates, fires a transaction-specific |
|
18572 * "failure" event, if config.on.failure is defined. |
|
18573 * |
|
18574 * @method failure |
|
18575 * @param {Object} transaction Transaction object. |
|
18576 * @param {Object} config Configuration object for the transaction. |
|
18577 */ |
|
18578 failure: function(transaction, config) { |
|
18579 /** |
|
18580 * Signals an HTTP response with status outside of the 2xx range. |
|
18581 * Fires after io:complete. |
|
18582 * @event io:failure |
|
18583 */ |
|
18584 this._evt(EVENTS[4], transaction, config); |
|
18585 this.end(transaction, config); |
|
18586 }, |
|
18587 |
|
18588 /** |
|
18589 * Fires event "io:progress" and creates, fires a transaction-specific |
|
18590 * "progress" event -- for XMLHttpRequest file upload -- if |
|
18591 * config.on.progress is defined. |
|
18592 * |
|
18593 * @method progress |
|
18594 * @param {Object} transaction Transaction object. |
|
18595 * @param {Object} progress event. |
|
18596 * @param {Object} config Configuration object for the transaction. |
|
18597 */ |
|
18598 progress: function(transaction, e, config) { |
|
18599 /** |
|
18600 * Signals the interactive state during a file upload transaction. |
|
18601 * This event fires after io:start and before io:complete. |
|
18602 * @event io:progress |
|
18603 */ |
|
18604 transaction.evt = e; |
|
18605 this._evt(EVENTS[5], transaction, config); |
|
18606 }, |
|
18607 |
|
18608 /** |
|
18609 * Fires event "io:complete" and creates, fires a transaction-specific |
|
18610 * "complete" event -- for XMLHttpRequest file upload -- if |
|
18611 * config.on.complete is defined. |
|
18612 * |
|
18613 * @method load |
|
18614 * @param {Object} transaction Transaction object. |
|
18615 * @param {Object} load event. |
|
18616 * @param {Object} config Configuration object for the transaction. |
|
18617 */ |
|
18618 load: function (transaction, e, config) { |
|
18619 transaction.evt = e.target; |
|
18620 this._evt(EVENTS[1], transaction, config); |
|
18621 }, |
|
18622 |
|
18623 /** |
|
18624 * Fires event "io:failure" and creates, fires a transaction-specific |
|
18625 * "failure" event -- for XMLHttpRequest file upload -- if |
|
18626 * config.on.failure is defined. |
|
18627 * |
|
18628 * @method error |
|
18629 * @param {Object} transaction Transaction object. |
|
18630 * @param {Object} error event. |
|
18631 * @param {Object} config Configuration object for the transaction. |
|
18632 */ |
|
18633 error: function (transaction, e, config) { |
|
18634 transaction.evt = e; |
|
18635 this._evt(EVENTS[4], transaction, config); |
|
18636 }, |
|
18637 |
|
18638 /** |
|
18639 * Retry an XDR transaction, using the Flash tranport, if the native |
|
18640 * transport fails. |
|
18641 * |
|
18642 * @method _retry |
|
18643 * @private |
|
18644 * @param {Object} transaction Transaction object. |
|
18645 * @param {String} uri Qualified path to transaction resource. |
|
18646 * @param {Object} config Configuration object for the transaction. |
|
18647 */ |
|
18648 _retry: function(transaction, uri, config) { |
|
18649 this._destroy(transaction); |
|
18650 config.xdr.use = 'flash'; |
|
18651 return this.send(uri, config, transaction.id); |
|
18652 }, |
|
18653 |
|
18654 /** |
|
18655 * Method that concatenates string data for HTTP GET transactions. |
|
18656 * |
|
18657 * @method _concat |
|
18658 * @private |
|
18659 * @param {String} uri URI or root data. |
|
18660 * @param {String} data Data to be concatenated onto URI. |
|
18661 * @return {String} |
|
18662 */ |
|
18663 _concat: function(uri, data) { |
|
18664 uri += (uri.indexOf('?') === -1 ? '?' : '&') + data; |
|
18665 return uri; |
|
18666 }, |
|
18667 |
|
18668 /** |
|
18669 * Stores default client headers for all transactions. If a label is |
|
18670 * passed with no value argument, the header will be deleted. |
|
18671 * |
|
18672 * @method setHeader |
|
18673 * @param {String} name HTTP header |
|
18674 * @param {String} value HTTP header value |
|
18675 */ |
|
18676 setHeader: function(name, value) { |
|
18677 if (value) { |
|
18678 this._headers[name] = value; |
|
18679 } else { |
|
18680 delete this._headers[name]; |
|
18681 } |
|
18682 }, |
|
18683 |
|
18684 /** |
|
18685 * Method that sets all HTTP headers to be sent in a transaction. |
|
18686 * |
|
18687 * @method _setHeaders |
|
18688 * @private |
|
18689 * @param {Object} transaction - XHR instance for the specific transaction. |
|
18690 * @param {Object} headers - HTTP headers for the specific transaction, as |
|
18691 * defined in the configuration object passed to YUI.io(). |
|
18692 */ |
|
18693 _setHeaders: function(transaction, headers) { |
|
18694 headers = Y.merge(this._headers, headers); |
|
18695 Y.Object.each(headers, function(value, name) { |
|
18696 if (value !== 'disable') { |
|
18697 transaction.setRequestHeader(name, headers[name]); |
|
18698 } |
|
18699 }); |
|
18700 }, |
|
18701 |
|
18702 /** |
|
18703 * Starts timeout count if the configuration object has a defined |
|
18704 * timeout property. |
|
18705 * |
|
18706 * @method _startTimeout |
|
18707 * @private |
|
18708 * @param {Object} transaction Transaction object generated by _create(). |
|
18709 * @param {Object} timeout Timeout in milliseconds. |
|
18710 */ |
|
18711 _startTimeout: function(transaction, timeout) { |
|
18712 var io = this; |
|
18713 |
|
18714 io._timeout[transaction.id] = setTimeout(function() { |
|
18715 io._abort(transaction, 'timeout'); |
|
18716 }, timeout); |
|
18717 }, |
|
18718 |
|
18719 /** |
|
18720 * Clears the timeout interval started by _startTimeout(). |
|
18721 * |
|
18722 * @method _clearTimeout |
|
18723 * @private |
|
18724 * @param {Number} id - Transaction id. |
|
18725 */ |
|
18726 _clearTimeout: function(id) { |
|
18727 clearTimeout(this._timeout[id]); |
|
18728 delete this._timeout[id]; |
|
18729 }, |
|
18730 |
|
18731 /** |
|
18732 * Method that determines if a transaction response qualifies as success |
|
18733 * or failure, based on the response HTTP status code, and fires the |
|
18734 * appropriate success or failure events. |
|
18735 * |
|
18736 * @method _result |
|
18737 * @private |
|
18738 * @static |
|
18739 * @param {Object} transaction Transaction object generated by _create(). |
|
18740 * @param {Object} config Configuration object passed to io(). |
|
18741 */ |
|
18742 _result: function(transaction, config) { |
|
18743 var status; |
|
18744 // Firefox will throw an exception if attempting to access |
|
18745 // an XHR object's status property, after a request is aborted. |
|
18746 try { |
|
18747 status = transaction.c.status; |
|
18748 } catch(e) { |
|
18749 status = 0; |
|
18750 } |
|
18751 |
|
18752 // IE reports HTTP 204 as HTTP 1223. |
|
18753 if (status >= 200 && status < 300 || status === 304 || status === 1223) { |
|
18754 this.success(transaction, config); |
|
18755 } else { |
|
18756 this.failure(transaction, config); |
|
18757 } |
|
18758 }, |
|
18759 |
|
18760 /** |
|
18761 * Event handler bound to onreadystatechange. |
|
18762 * |
|
18763 * @method _rS |
|
18764 * @private |
|
18765 * @param {Object} transaction Transaction object generated by _create(). |
|
18766 * @param {Object} config Configuration object passed to YUI.io(). |
|
18767 */ |
|
18768 _rS: function(transaction, config) { |
|
18769 var io = this; |
|
18770 |
|
18771 if (transaction.c.readyState === 4) { |
|
18772 if (config.timeout) { |
|
18773 io._clearTimeout(transaction.id); |
|
18774 } |
|
18775 |
|
18776 // Yield in the event of request timeout or abort. |
|
18777 setTimeout(function() { |
|
18778 io.complete(transaction, config); |
|
18779 io._result(transaction, config); |
|
18780 }, 0); |
|
18781 } |
|
18782 }, |
|
18783 |
|
18784 /** |
|
18785 * Terminates a transaction due to an explicit abort or timeout. |
|
18786 * |
|
18787 * @method _abort |
|
18788 * @private |
|
18789 * @param {Object} transaction Transaction object generated by _create(). |
|
18790 * @param {String} type Identifies timed out or aborted transaction. |
|
18791 */ |
|
18792 _abort: function(transaction, type) { |
|
18793 if (transaction && transaction.c) { |
|
18794 transaction.e = type; |
|
18795 transaction.c.abort(); |
|
18796 } |
|
18797 }, |
|
18798 |
|
18799 /** |
|
18800 * Requests a transaction. `send()` is implemented as `Y.io()`. Each |
|
18801 * transaction may include a configuration object. Its properties are: |
|
18802 * |
|
18803 * <dl> |
|
18804 * <dt>method</dt> |
|
18805 * <dd>HTTP method verb (e.g., GET or POST). If this property is not |
|
18806 * not defined, the default value will be GET.</dd> |
|
18807 * |
|
18808 * <dt>data</dt> |
|
18809 * <dd>This is the name-value string that will be sent as the |
|
18810 * transaction data. If the request is HTTP GET, the data become |
|
18811 * part of querystring. If HTTP POST, the data are sent in the |
|
18812 * message body.</dd> |
|
18813 * |
|
18814 * <dt>xdr</dt> |
|
18815 * <dd>Defines the transport to be used for cross-domain requests. |
|
18816 * By setting this property, the transaction will use the specified |
|
18817 * transport instead of XMLHttpRequest. The properties of the |
|
18818 * transport object are: |
|
18819 * <dl> |
|
18820 * <dt>use</dt> |
|
18821 * <dd>The transport to be used: 'flash' or 'native'</dd> |
|
18822 * <dt>dataType</dt> |
|
18823 * <dd>Set the value to 'XML' if that is the expected response |
|
18824 * content type.</dd> |
|
18825 * <dt>credentials</dt> |
|
18826 * <dd>Set the value to 'true' to set XHR.withCredentials property to true.</dd> |
|
18827 * </dl></dd> |
|
18828 * |
|
18829 * <dt>form</dt> |
|
18830 * <dd>Form serialization configuration object. Its properties are: |
|
18831 * <dl> |
|
18832 * <dt>id</dt> |
|
18833 * <dd>Node object or id of HTML form</dd> |
|
18834 * <dt>useDisabled</dt> |
|
18835 * <dd>`true` to also serialize disabled form field values |
|
18836 * (defaults to `false`)</dd> |
|
18837 * </dl></dd> |
|
18838 * |
|
18839 * <dt>on</dt> |
|
18840 * <dd>Assigns transaction event subscriptions. Available events are: |
|
18841 * <dl> |
|
18842 * <dt>start</dt> |
|
18843 * <dd>Fires when a request is sent to a resource.</dd> |
|
18844 * <dt>complete</dt> |
|
18845 * <dd>Fires when the transaction is complete.</dd> |
|
18846 * <dt>success</dt> |
|
18847 * <dd>Fires when the HTTP response status is within the 2xx |
|
18848 * range.</dd> |
|
18849 * <dt>failure</dt> |
|
18850 * <dd>Fires when the HTTP response status is outside the 2xx |
|
18851 * range, if an exception occurs, if the transation is aborted, |
|
18852 * or if the transaction exceeds a configured `timeout`.</dd> |
|
18853 * <dt>end</dt> |
|
18854 * <dd>Fires at the conclusion of the transaction |
|
18855 * lifecycle, after `success` or `failure`.</dd> |
|
18856 * </dl> |
|
18857 * |
|
18858 * <p>Callback functions for `start` and `end` receive the id of the |
|
18859 * transaction as a first argument. For `complete`, `success`, and |
|
18860 * `failure`, callbacks receive the id and the response object |
|
18861 * (usually the XMLHttpRequest instance). If the `arguments` |
|
18862 * property was included in the configuration object passed to |
|
18863 * `Y.io()`, the configured data will be passed to all callbacks as |
|
18864 * the last argument.</p> |
|
18865 * </dd> |
|
18866 * |
|
18867 * <dt>sync</dt> |
|
18868 * <dd>Pass `true` to make a same-domain transaction synchronous. |
|
18869 * <strong>CAVEAT</strong>: This will negatively impact the user |
|
18870 * experience. Have a <em>very</em> good reason if you intend to use |
|
18871 * this.</dd> |
|
18872 * |
|
18873 * <dt>context</dt> |
|
18874 * <dd>The "`this'" object for all configured event handlers. If a |
|
18875 * specific context is needed for individual callbacks, bind the |
|
18876 * callback to a context using `Y.bind()`.</dd> |
|
18877 * |
|
18878 * <dt>headers</dt> |
|
18879 * <dd>Object map of transaction headers to send to the server. The |
|
18880 * object keys are the header names and the values are the header |
|
18881 * values.</dd> |
|
18882 * |
|
18883 * <dt>timeout</dt> |
|
18884 * <dd>Millisecond threshold for the transaction before being |
|
18885 * automatically aborted.</dd> |
|
18886 * |
|
18887 * <dt>arguments</dt> |
|
18888 * <dd>User-defined data passed to all registered event handlers. |
|
18889 * This value is available as the second argument in the "start" and |
|
18890 * "end" event handlers. It is the third argument in the "complete", |
|
18891 * "success", and "failure" event handlers. <strong>Be sure to quote |
|
18892 * this property name in the transaction configuration as |
|
18893 * "arguments" is a reserved word in JavaScript</strong> (e.g. |
|
18894 * `Y.io({ ..., "arguments": stuff })`).</dd> |
|
18895 * </dl> |
|
18896 * |
|
18897 * @method send |
|
18898 * @public |
|
18899 * @param {String} uri Qualified path to transaction resource. |
|
18900 * @param {Object} config Configuration object for the transaction. |
|
18901 * @param {Number} id Transaction id, if already set. |
|
18902 * @return {Object} |
|
18903 */ |
|
18904 send: function(uri, config, id) { |
|
18905 var transaction, method, i, len, sync, data, |
|
18906 io = this, |
|
18907 u = uri, |
|
18908 response = {}; |
|
18909 |
|
18910 config = config ? Y.Object(config) : {}; |
|
18911 transaction = io._create(config, id); |
|
18912 method = config.method ? config.method.toUpperCase() : 'GET'; |
|
18913 sync = config.sync; |
|
18914 data = config.data; |
|
18915 |
|
18916 // Serialize a map object into a key-value string using |
|
18917 // querystring-stringify-simple. |
|
18918 if ((Y.Lang.isObject(data) && !data.nodeType) && !transaction.upload) { |
|
18919 if (Y.QueryString && Y.QueryString.stringify) { |
|
18920 config.data = data = Y.QueryString.stringify(data); |
|
18921 } else { |
|
18922 } |
|
18923 } |
|
18924 |
|
18925 if (config.form) { |
|
18926 if (config.form.upload) { |
|
18927 // This is a file upload transaction, calling |
|
18928 // upload() in io-upload-iframe. |
|
18929 return io.upload(transaction, uri, config); |
|
18930 } else { |
|
18931 // Serialize HTML form data into a key-value string. |
|
18932 data = io._serialize(config.form, data); |
|
18933 } |
|
18934 } |
|
18935 |
|
18936 // Convert falsy values to an empty string. This way IE can't be |
|
18937 // rediculous and translate `undefined` to "undefined". |
|
18938 data || (data = ''); |
|
18939 |
|
18940 if (data) { |
|
18941 switch (method) { |
|
18942 case 'GET': |
|
18943 case 'HEAD': |
|
18944 case 'DELETE': |
|
18945 u = io._concat(u, data); |
|
18946 data = ''; |
|
18947 break; |
|
18948 case 'POST': |
|
18949 case 'PUT': |
|
18950 // If Content-Type is defined in the configuration object, or |
|
18951 // or as a default header, it will be used instead of |
|
18952 // 'application/x-www-form-urlencoded; charset=UTF-8' |
|
18953 config.headers = Y.merge({ |
|
18954 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' |
|
18955 }, config.headers); |
|
18956 break; |
|
18957 } |
|
18958 } |
|
18959 |
|
18960 if (transaction.xdr) { |
|
18961 // Route data to io-xdr module for flash and XDomainRequest. |
|
18962 return io.xdr(u, transaction, config); |
|
18963 } |
|
18964 else if (transaction.notify) { |
|
18965 // Route data to custom transport |
|
18966 return transaction.c.send(transaction, uri, config); |
|
18967 } |
|
18968 |
|
18969 if (!sync && !transaction.upload) { |
|
18970 transaction.c.onreadystatechange = function() { |
|
18971 io._rS(transaction, config); |
|
18972 }; |
|
18973 } |
|
18974 |
|
18975 try { |
|
18976 // Determine if request is to be set as |
|
18977 // synchronous or asynchronous. |
|
18978 transaction.c.open(method, u, !sync, config.username || null, config.password || null); |
|
18979 io._setHeaders(transaction.c, config.headers || {}); |
|
18980 io.start(transaction, config); |
|
18981 |
|
18982 // Will work only in browsers that implement the |
|
18983 // Cross-Origin Resource Sharing draft. |
|
18984 if (config.xdr && config.xdr.credentials && SUPPORTS_CORS) { |
|
18985 transaction.c.withCredentials = true; |
|
18986 } |
|
18987 |
|
18988 // Using "null" with HTTP POST will result in a request |
|
18989 // with no Content-Length header defined. |
|
18990 transaction.c.send(data); |
|
18991 |
|
18992 if (sync) { |
|
18993 // Create a response object for synchronous transactions, |
|
18994 // mixing id and arguments properties with the xhr |
|
18995 // properties whitelist. |
|
18996 for (i = 0, len = XHR_PROPS.length; i < len; ++i) { |
|
18997 response[XHR_PROPS[i]] = transaction.c[XHR_PROPS[i]]; |
|
18998 } |
|
18999 |
|
19000 response.getAllResponseHeaders = function() { |
|
19001 return transaction.c.getAllResponseHeaders(); |
|
19002 }; |
|
19003 |
|
19004 response.getResponseHeader = function(name) { |
|
19005 return transaction.c.getResponseHeader(name); |
|
19006 }; |
|
19007 |
|
19008 io.complete(transaction, config); |
|
19009 io._result(transaction, config); |
|
19010 |
|
19011 return response; |
|
19012 } |
|
19013 } catch(e) { |
|
19014 if (transaction.xdr) { |
|
19015 // This exception is usually thrown by browsers |
|
19016 // that do not support XMLHttpRequest Level 2. |
|
19017 // Retry the request with the XDR transport set |
|
19018 // to 'flash'. If the Flash transport is not |
|
19019 // initialized or available, the transaction |
|
19020 // will resolve to a transport error. |
|
19021 return io._retry(transaction, uri, config); |
|
19022 } else { |
|
19023 io.complete(transaction, config); |
|
19024 io._result(transaction, config); |
|
19025 } |
|
19026 } |
|
19027 |
|
19028 // If config.timeout is defined, and the request is standard XHR, |
|
19029 // initialize timeout polling. |
|
19030 if (config.timeout) { |
|
19031 io._startTimeout(transaction, config.timeout); |
|
19032 } |
|
19033 |
|
19034 return { |
|
19035 id: transaction.id, |
|
19036 abort: function() { |
|
19037 return transaction.c ? io._abort(transaction, 'abort') : false; |
|
19038 }, |
|
19039 isInProgress: function() { |
|
19040 return transaction.c ? (transaction.c.readyState % 4) : false; |
|
19041 }, |
|
19042 io: io |
|
19043 }; |
|
19044 } |
|
19045 }; |
|
19046 |
|
19047 /** |
|
19048 Method for initiating an ajax call. The first argument is the url end |
|
19049 point for the call. The second argument is an object to configure the |
|
19050 transaction and attach event subscriptions. The configuration object |
|
19051 supports the following properties: |
|
19052 |
|
19053 <dl> |
|
19054 <dt>method</dt> |
|
19055 <dd>HTTP method verb (e.g., GET or POST). If this property is not |
|
19056 not defined, the default value will be GET.</dd> |
|
19057 |
|
19058 <dt>data</dt> |
|
19059 <dd>This is the name-value string that will be sent as the |
|
19060 transaction data. If the request is HTTP GET, the data become |
|
19061 part of querystring. If HTTP POST, the data are sent in the |
|
19062 message body.</dd> |
|
19063 |
|
19064 <dt>xdr</dt> |
|
19065 <dd>Defines the transport to be used for cross-domain requests. |
|
19066 By setting this property, the transaction will use the specified |
|
19067 transport instead of XMLHttpRequest. The properties of the |
|
19068 transport object are: |
|
19069 <dl> |
|
19070 <dt>use</dt> |
|
19071 <dd>The transport to be used: 'flash' or 'native'</dd> |
|
19072 <dt>dataType</dt> |
|
19073 <dd>Set the value to 'XML' if that is the expected response |
|
19074 content type.</dd> |
|
19075 </dl></dd> |
|
19076 |
|
19077 <dt>form</dt> |
|
19078 <dd>Form serialization configuration object. Its properties are: |
|
19079 <dl> |
|
19080 <dt>id</dt> |
|
19081 <dd>Node object or id of HTML form</dd> |
|
19082 <dt>useDisabled</dt> |
|
19083 <dd>`true` to also serialize disabled form field values |
|
19084 (defaults to `false`)</dd> |
|
19085 </dl></dd> |
|
19086 |
|
19087 <dt>on</dt> |
|
19088 <dd>Assigns transaction event subscriptions. Available events are: |
|
19089 <dl> |
|
19090 <dt>start</dt> |
|
19091 <dd>Fires when a request is sent to a resource.</dd> |
|
19092 <dt>complete</dt> |
|
19093 <dd>Fires when the transaction is complete.</dd> |
|
19094 <dt>success</dt> |
|
19095 <dd>Fires when the HTTP response status is within the 2xx |
|
19096 range.</dd> |
|
19097 <dt>failure</dt> |
|
19098 <dd>Fires when the HTTP response status is outside the 2xx |
|
19099 range, if an exception occurs, if the transation is aborted, |
|
19100 or if the transaction exceeds a configured `timeout`.</dd> |
|
19101 <dt>end</dt> |
|
19102 <dd>Fires at the conclusion of the transaction |
|
19103 lifecycle, after `success` or `failure`.</dd> |
|
19104 </dl> |
|
19105 |
|
19106 <p>Callback functions for `start` and `end` receive the id of the |
|
19107 transaction as a first argument. For `complete`, `success`, and |
|
19108 `failure`, callbacks receive the id and the response object |
|
19109 (usually the XMLHttpRequest instance). If the `arguments` |
|
19110 property was included in the configuration object passed to |
|
19111 `Y.io()`, the configured data will be passed to all callbacks as |
|
19112 the last argument.</p> |
|
19113 </dd> |
|
19114 |
|
19115 <dt>sync</dt> |
|
19116 <dd>Pass `true` to make a same-domain transaction synchronous. |
|
19117 <strong>CAVEAT</strong>: This will negatively impact the user |
|
19118 experience. Have a <em>very</em> good reason if you intend to use |
|
19119 this.</dd> |
|
19120 |
|
19121 <dt>context</dt> |
|
19122 <dd>The "`this'" object for all configured event handlers. If a |
|
19123 specific context is needed for individual callbacks, bind the |
|
19124 callback to a context using `Y.bind()`.</dd> |
|
19125 |
|
19126 <dt>headers</dt> |
|
19127 <dd>Object map of transaction headers to send to the server. The |
|
19128 object keys are the header names and the values are the header |
|
19129 values.</dd> |
|
19130 |
|
19131 <dt>timeout</dt> |
|
19132 <dd>Millisecond threshold for the transaction before being |
|
19133 automatically aborted.</dd> |
|
19134 |
|
19135 <dt>arguments</dt> |
|
19136 <dd>User-defined data passed to all registered event handlers. |
|
19137 This value is available as the second argument in the "start" and |
|
19138 "end" event handlers. It is the third argument in the "complete", |
|
19139 "success", and "failure" event handlers. <strong>Be sure to quote |
|
19140 this property name in the transaction configuration as |
|
19141 "arguments" is a reserved word in JavaScript</strong> (e.g. |
|
19142 `Y.io({ ..., "arguments": stuff })`).</dd> |
|
19143 </dl> |
|
19144 |
|
19145 @method io |
|
19146 @static |
|
19147 @param {String} url qualified path to transaction resource. |
|
19148 @param {Object} config configuration object for the transaction. |
|
19149 @return {Object} |
|
19150 @for YUI |
|
19151 **/ |
|
19152 Y.io = function(url, config) { |
|
19153 // Calling IO through the static interface will use and reuse |
|
19154 // an instance of IO. |
|
19155 var transaction = Y.io._map['io:0'] || new IO(); |
|
19156 return transaction.send.apply(transaction, [url, config]); |
|
19157 }; |
|
19158 |
|
19159 /** |
|
19160 Method for setting and deleting IO HTTP headers to be sent with every |
|
19161 request. |
|
19162 |
|
19163 Hosted as a property on the `io` function (e.g. `Y.io.header`). |
|
19164 |
|
19165 @method header |
|
19166 @param {String} name HTTP header |
|
19167 @param {String} value HTTP header value |
|
19168 @static |
|
19169 **/ |
|
19170 Y.io.header = function(name, value) { |
|
19171 // Calling IO through the static interface will use and reuse |
|
19172 // an instance of IO. |
|
19173 var transaction = Y.io._map['io:0'] || new IO(); |
|
19174 transaction.setHeader(name, value); |
|
19175 }; |
|
19176 |
|
19177 Y.IO = IO; |
|
19178 // Map of all IO instances created. |
|
19179 Y.io._map = {}; |
|
19180 var XHR = win && win.XMLHttpRequest, |
|
19181 XDR = win && win.XDomainRequest, |
|
19182 AX = win && win.ActiveXObject, |
|
19183 |
|
19184 // Checks for the presence of the `withCredentials` in an XHR instance |
|
19185 // object, which will be present if the environment supports CORS. |
|
19186 SUPPORTS_CORS = XHR && 'withCredentials' in (new XMLHttpRequest()); |
|
19187 |
|
19188 |
|
19189 Y.mix(Y.IO, { |
|
19190 /** |
|
19191 * The ID of the default IO transport, defaults to `xhr` |
|
19192 * @property _default |
|
19193 * @type {String} |
|
19194 * @static |
|
19195 */ |
|
19196 _default: 'xhr', |
|
19197 /** |
|
19198 * |
|
19199 * @method defaultTransport |
|
19200 * @static |
|
19201 * @param {String} [id] The transport to set as the default, if empty a new transport is created. |
|
19202 * @return {Object} The transport object with a `send` method |
|
19203 */ |
|
19204 defaultTransport: function(id) { |
|
19205 if (id) { |
|
19206 Y.IO._default = id; |
|
19207 } else { |
|
19208 var o = { |
|
19209 c: Y.IO.transports[Y.IO._default](), |
|
19210 notify: Y.IO._default === 'xhr' ? false : true |
|
19211 }; |
|
19212 return o; |
|
19213 } |
|
19214 }, |
|
19215 /** |
|
19216 * An object hash of custom transports available to IO |
|
19217 * @property transports |
|
19218 * @type {Object} |
|
19219 * @static |
|
19220 */ |
|
19221 transports: { |
|
19222 xhr: function () { |
|
19223 return XHR ? new XMLHttpRequest() : |
|
19224 AX ? new ActiveXObject('Microsoft.XMLHTTP') : null; |
|
19225 }, |
|
19226 xdr: function () { |
|
19227 return XDR ? new XDomainRequest() : null; |
|
19228 }, |
|
19229 iframe: function () { return {}; }, |
|
19230 flash: null, |
|
19231 nodejs: null |
|
19232 }, |
|
19233 /** |
|
19234 * Create a custom transport of type and return it's object |
|
19235 * @method customTransport |
|
19236 * @param {String} id The id of the transport to create. |
|
19237 * @static |
|
19238 */ |
|
19239 customTransport: function(id) { |
|
19240 var o = { c: Y.IO.transports[id]() }; |
|
19241 |
|
19242 o[(id === 'xdr' || id === 'flash') ? 'xdr' : 'notify'] = true; |
|
19243 return o; |
|
19244 } |
|
19245 }); |
|
19246 |
|
19247 Y.mix(Y.IO.prototype, { |
|
19248 /** |
|
19249 * Fired from the notify method of the transport which in turn fires |
|
19250 * the event on the IO object. |
|
19251 * @method notify |
|
19252 * @param {String} event The name of the event |
|
19253 * @param {Object} transaction The transaction object |
|
19254 * @param {Object} config The configuration object for this transaction |
|
19255 */ |
|
19256 notify: function(event, transaction, config) { |
|
19257 var io = this; |
|
19258 |
|
19259 switch (event) { |
|
19260 case 'timeout': |
|
19261 case 'abort': |
|
19262 case 'transport error': |
|
19263 transaction.c = { status: 0, statusText: event }; |
|
19264 event = 'failure'; |
|
19265 default: |
|
19266 io[event].apply(io, [transaction, config]); |
|
19267 } |
|
19268 } |
|
19269 }); |
|
19270 |
|
19271 |
|
19272 |
|
19273 |
|
19274 }, '@VERSION@', {"requires": ["event-custom-base", "querystring-stringify-simple"]}); |
|
19275 YUI.add('json-parse', function (Y, NAME) { |
|
19276 |
|
19277 var _JSON = Y.config.global.JSON; |
|
19278 |
|
19279 Y.namespace('JSON').parse = function (obj, reviver, space) { |
|
19280 return _JSON.parse((typeof obj === 'string' ? obj : obj + ''), reviver, space); |
|
19281 }; |
|
19282 |
|
19283 |
|
19284 }, '@VERSION@', {"requires": ["yui-base"]}); |
|
19285 YUI.add('transition', function (Y, NAME) { |
|
19286 |
|
19287 /** |
|
19288 * Provides the transition method for Node. |
|
19289 * Transition has no API of its own, but adds the transition method to Node. |
|
19290 * |
|
19291 * @module transition |
|
19292 * @requires node-style |
|
19293 */ |
|
19294 |
|
19295 var CAMEL_VENDOR_PREFIX = '', |
|
19296 VENDOR_PREFIX = '', |
|
19297 DOCUMENT = Y.config.doc, |
|
19298 DOCUMENT_ELEMENT = 'documentElement', |
|
19299 DOCUMENT_STYLE = DOCUMENT[DOCUMENT_ELEMENT].style, |
|
19300 TRANSITION_CAMEL = 'transition', |
|
19301 TRANSITION_PROPERTY_CAMEL = 'transitionProperty', |
|
19302 TRANSITION_PROPERTY, |
|
19303 TRANSITION_DURATION, |
|
19304 TRANSITION_TIMING_FUNCTION, |
|
19305 TRANSITION_DELAY, |
|
19306 TRANSITION_END, |
|
19307 ON_TRANSITION_END, |
|
19308 |
|
19309 EMPTY_OBJ = {}, |
|
19310 |
|
19311 VENDORS = [ |
|
19312 'Webkit', |
|
19313 'Moz' |
|
19314 ], |
|
19315 |
|
19316 VENDOR_TRANSITION_END = { |
|
19317 Webkit: 'webkitTransitionEnd' |
|
19318 }, |
|
19319 |
|
19320 /** |
|
19321 * A class for constructing transition instances. |
|
19322 * Adds the "transition" method to Node. |
|
19323 * @class Transition |
|
19324 * @constructor |
|
19325 */ |
|
19326 |
|
19327 Transition = function() { |
|
19328 this.init.apply(this, arguments); |
|
19329 }; |
|
19330 |
|
19331 // One off handling of transform-prefixing. |
|
19332 Transition._TRANSFORM = 'transform'; |
|
19333 |
|
19334 Transition._toCamel = function(property) { |
|
19335 property = property.replace(/-([a-z])/gi, function(m0, m1) { |
|
19336 return m1.toUpperCase(); |
|
19337 }); |
|
19338 |
|
19339 return property; |
|
19340 }; |
|
19341 |
|
19342 Transition._toHyphen = function(property) { |
|
19343 property = property.replace(/([A-Z]?)([a-z]+)([A-Z]?)/g, function(m0, m1, m2, m3) { |
|
19344 var str = ((m1) ? '-' + m1.toLowerCase() : '') + m2; |
|
19345 |
|
19346 if (m3) { |
|
19347 str += '-' + m3.toLowerCase(); |
|
19348 } |
|
19349 |
|
19350 return str; |
|
19351 }); |
|
19352 |
|
19353 return property; |
|
19354 }; |
|
19355 |
|
19356 Transition.SHOW_TRANSITION = 'fadeIn'; |
|
19357 Transition.HIDE_TRANSITION = 'fadeOut'; |
|
19358 |
|
19359 Transition.useNative = false; |
|
19360 |
|
19361 // Map transition properties to vendor-specific versions. |
|
19362 if ('transition' in DOCUMENT_STYLE |
|
19363 && 'transitionProperty' in DOCUMENT_STYLE |
|
19364 && 'transitionDuration' in DOCUMENT_STYLE |
|
19365 && 'transitionTimingFunction' in DOCUMENT_STYLE |
|
19366 && 'transitionDelay' in DOCUMENT_STYLE) { |
|
19367 Transition.useNative = true; |
|
19368 Transition.supported = true; // TODO: remove |
|
19369 } else { |
|
19370 Y.Array.each(VENDORS, function(val) { // then vendor specific |
|
19371 var property = val + 'Transition'; |
|
19372 if (property in DOCUMENT[DOCUMENT_ELEMENT].style) { |
|
19373 CAMEL_VENDOR_PREFIX = val; |
|
19374 VENDOR_PREFIX = Transition._toHyphen(val) + '-'; |
|
19375 |
|
19376 Transition.useNative = true; |
|
19377 Transition.supported = true; // TODO: remove |
|
19378 Transition._VENDOR_PREFIX = val; |
|
19379 } |
|
19380 }); |
|
19381 } |
|
19382 |
|
19383 // Map transform property to vendor-specific versions. |
|
19384 // One-off required for cssText injection. |
|
19385 if (typeof DOCUMENT_STYLE.transform === 'undefined') { |
|
19386 Y.Array.each(VENDORS, function(val) { // then vendor specific |
|
19387 var property = val + 'Transform'; |
|
19388 if (typeof DOCUMENT_STYLE[property] !== 'undefined') { |
|
19389 Transition._TRANSFORM = property; |
|
19390 } |
|
19391 }); |
|
19392 } |
|
19393 |
|
19394 if (CAMEL_VENDOR_PREFIX) { |
|
19395 TRANSITION_CAMEL = CAMEL_VENDOR_PREFIX + 'Transition'; |
|
19396 TRANSITION_PROPERTY_CAMEL = CAMEL_VENDOR_PREFIX + 'TransitionProperty'; |
|
19397 } |
|
19398 |
|
19399 TRANSITION_PROPERTY = VENDOR_PREFIX + 'transition-property'; |
|
19400 TRANSITION_DURATION = VENDOR_PREFIX + 'transition-duration'; |
|
19401 TRANSITION_TIMING_FUNCTION = VENDOR_PREFIX + 'transition-timing-function'; |
|
19402 TRANSITION_DELAY = VENDOR_PREFIX + 'transition-delay'; |
|
19403 |
|
19404 TRANSITION_END = 'transitionend'; |
|
19405 ON_TRANSITION_END = 'on' + CAMEL_VENDOR_PREFIX.toLowerCase() + 'transitionend'; |
|
19406 TRANSITION_END = VENDOR_TRANSITION_END[CAMEL_VENDOR_PREFIX] || TRANSITION_END; |
|
19407 |
|
19408 Transition.fx = {}; |
|
19409 Transition.toggles = {}; |
|
19410 |
|
19411 Transition._hasEnd = {}; |
|
19412 |
|
19413 Transition._reKeywords = /^(?:node|duration|iterations|easing|delay|on|onstart|onend)$/i; |
|
19414 |
|
19415 Y.Node.DOM_EVENTS[TRANSITION_END] = 1; |
|
19416 |
|
19417 Transition.NAME = 'transition'; |
|
19418 |
|
19419 Transition.DEFAULT_EASING = 'ease'; |
|
19420 Transition.DEFAULT_DURATION = 0.5; |
|
19421 Transition.DEFAULT_DELAY = 0; |
|
19422 |
|
19423 Transition._nodeAttrs = {}; |
|
19424 |
|
19425 Transition.prototype = { |
|
19426 constructor: Transition, |
|
19427 init: function(node, config) { |
|
19428 var anim = this; |
|
19429 anim._node = node; |
|
19430 if (!anim._running && config) { |
|
19431 anim._config = config; |
|
19432 node._transition = anim; // cache for reuse |
|
19433 |
|
19434 anim._duration = ('duration' in config) ? |
|
19435 config.duration: anim.constructor.DEFAULT_DURATION; |
|
19436 |
|
19437 anim._delay = ('delay' in config) ? |
|
19438 config.delay: anim.constructor.DEFAULT_DELAY; |
|
19439 |
|
19440 anim._easing = config.easing || anim.constructor.DEFAULT_EASING; |
|
19441 anim._count = 0; // track number of animated properties |
|
19442 anim._running = false; |
|
19443 |
|
19444 } |
|
19445 |
|
19446 return anim; |
|
19447 }, |
|
19448 |
|
19449 addProperty: function(prop, config) { |
|
19450 var anim = this, |
|
19451 node = this._node, |
|
19452 uid = Y.stamp(node), |
|
19453 nodeInstance = Y.one(node), |
|
19454 attrs = Transition._nodeAttrs[uid], |
|
19455 computed, |
|
19456 compareVal, |
|
19457 dur, |
|
19458 attr, |
|
19459 val; |
|
19460 |
|
19461 if (!attrs) { |
|
19462 attrs = Transition._nodeAttrs[uid] = {}; |
|
19463 } |
|
19464 |
|
19465 attr = attrs[prop]; |
|
19466 |
|
19467 // might just be a value |
|
19468 if (config && config.value !== undefined) { |
|
19469 val = config.value; |
|
19470 } else if (config !== undefined) { |
|
19471 val = config; |
|
19472 config = EMPTY_OBJ; |
|
19473 } |
|
19474 |
|
19475 if (typeof val === 'function') { |
|
19476 val = val.call(nodeInstance, nodeInstance); |
|
19477 } |
|
19478 |
|
19479 if (attr && attr.transition) { |
|
19480 // take control if another transition owns this property |
|
19481 if (attr.transition !== anim) { |
|
19482 attr.transition._count--; // remapping attr to this transition |
|
19483 } |
|
19484 } |
|
19485 |
|
19486 anim._count++; // properties per transition |
|
19487 |
|
19488 // make 0 async and fire events |
|
19489 dur = ((typeof config.duration !== 'undefined') ? config.duration : |
|
19490 anim._duration) || 0.0001; |
|
19491 |
|
19492 attrs[prop] = { |
|
19493 value: val, |
|
19494 duration: dur, |
|
19495 delay: (typeof config.delay !== 'undefined') ? config.delay : |
|
19496 anim._delay, |
|
19497 |
|
19498 easing: config.easing || anim._easing, |
|
19499 |
|
19500 transition: anim |
|
19501 }; |
|
19502 |
|
19503 // native end event doesnt fire when setting to same value |
|
19504 // supplementing with timer |
|
19505 // val may be a string or number (height: 0, etc), but computedStyle is always string |
|
19506 computed = Y.DOM.getComputedStyle(node, prop); |
|
19507 compareVal = (typeof val === 'string') ? computed : parseFloat(computed); |
|
19508 |
|
19509 if (Transition.useNative && compareVal === val) { |
|
19510 setTimeout(function() { |
|
19511 anim._onNativeEnd.call(node, { |
|
19512 propertyName: prop, |
|
19513 elapsedTime: dur |
|
19514 }); |
|
19515 }, dur * 1000); |
|
19516 } |
|
19517 }, |
|
19518 |
|
19519 removeProperty: function(prop) { |
|
19520 var anim = this, |
|
19521 attrs = Transition._nodeAttrs[Y.stamp(anim._node)]; |
|
19522 |
|
19523 if (attrs && attrs[prop]) { |
|
19524 delete attrs[prop]; |
|
19525 anim._count--; |
|
19526 } |
|
19527 |
|
19528 }, |
|
19529 |
|
19530 initAttrs: function(config) { |
|
19531 var attr, |
|
19532 node = this._node; |
|
19533 |
|
19534 if (config.transform && !config[Transition._TRANSFORM]) { |
|
19535 config[Transition._TRANSFORM] = config.transform; |
|
19536 delete config.transform; // TODO: copy |
|
19537 } |
|
19538 |
|
19539 for (attr in config) { |
|
19540 if (config.hasOwnProperty(attr) && !Transition._reKeywords.test(attr)) { |
|
19541 this.addProperty(attr, config[attr]); |
|
19542 |
|
19543 // when size is auto or % webkit starts from zero instead of computed |
|
19544 // (https://bugs.webkit.org/show_bug.cgi?id=16020) |
|
19545 // TODO: selective set |
|
19546 if (node.style[attr] === '') { |
|
19547 Y.DOM.setStyle(node, attr, Y.DOM.getComputedStyle(node, attr)); |
|
19548 } |
|
19549 } |
|
19550 } |
|
19551 }, |
|
19552 |
|
19553 /** |
|
19554 * Starts or an animation. |
|
19555 * @method run |
|
19556 * @chainable |
|
19557 * @private |
|
19558 */ |
|
19559 run: function(callback) { |
|
19560 var anim = this, |
|
19561 node = anim._node, |
|
19562 config = anim._config, |
|
19563 data = { |
|
19564 type: 'transition:start', |
|
19565 config: config |
|
19566 }; |
|
19567 |
|
19568 |
|
19569 if (!anim._running) { |
|
19570 anim._running = true; |
|
19571 |
|
19572 if (config.on && config.on.start) { |
|
19573 config.on.start.call(Y.one(node), data); |
|
19574 } |
|
19575 |
|
19576 anim.initAttrs(anim._config); |
|
19577 |
|
19578 anim._callback = callback; |
|
19579 anim._start(); |
|
19580 } |
|
19581 |
|
19582 |
|
19583 return anim; |
|
19584 }, |
|
19585 |
|
19586 _start: function() { |
|
19587 this._runNative(); |
|
19588 }, |
|
19589 |
|
19590 _prepDur: function(dur) { |
|
19591 dur = parseFloat(dur) * 1000; |
|
19592 |
|
19593 return dur + 'ms'; |
|
19594 }, |
|
19595 |
|
19596 _runNative: function() { |
|
19597 var anim = this, |
|
19598 node = anim._node, |
|
19599 uid = Y.stamp(node), |
|
19600 style = node.style, |
|
19601 computed = node.ownerDocument.defaultView.getComputedStyle(node), |
|
19602 attrs = Transition._nodeAttrs[uid], |
|
19603 cssText = '', |
|
19604 cssTransition = computed[Transition._toCamel(TRANSITION_PROPERTY)], |
|
19605 |
|
19606 transitionText = TRANSITION_PROPERTY + ': ', |
|
19607 duration = TRANSITION_DURATION + ': ', |
|
19608 easing = TRANSITION_TIMING_FUNCTION + ': ', |
|
19609 delay = TRANSITION_DELAY + ': ', |
|
19610 hyphy, |
|
19611 attr, |
|
19612 name; |
|
19613 |
|
19614 // preserve existing transitions |
|
19615 if (cssTransition !== 'all') { |
|
19616 transitionText += cssTransition + ','; |
|
19617 duration += computed[Transition._toCamel(TRANSITION_DURATION)] + ','; |
|
19618 easing += computed[Transition._toCamel(TRANSITION_TIMING_FUNCTION)] + ','; |
|
19619 delay += computed[Transition._toCamel(TRANSITION_DELAY)] + ','; |
|
19620 |
|
19621 } |
|
19622 |
|
19623 // run transitions mapped to this instance |
|
19624 for (name in attrs) { |
|
19625 hyphy = Transition._toHyphen(name); |
|
19626 attr = attrs[name]; |
|
19627 if ((attr = attrs[name]) && attr.transition === anim) { |
|
19628 if (name in node.style) { // only native styles allowed |
|
19629 duration += anim._prepDur(attr.duration) + ','; |
|
19630 delay += anim._prepDur(attr.delay) + ','; |
|
19631 easing += (attr.easing) + ','; |
|
19632 |
|
19633 transitionText += hyphy + ','; |
|
19634 cssText += hyphy + ': ' + attr.value + '; '; |
|
19635 } else { |
|
19636 this.removeProperty(name); |
|
19637 } |
|
19638 } |
|
19639 } |
|
19640 |
|
19641 transitionText = transitionText.replace(/,$/, ';'); |
|
19642 duration = duration.replace(/,$/, ';'); |
|
19643 easing = easing.replace(/,$/, ';'); |
|
19644 delay = delay.replace(/,$/, ';'); |
|
19645 |
|
19646 // only one native end event per node |
|
19647 if (!Transition._hasEnd[uid]) { |
|
19648 node.addEventListener(TRANSITION_END, anim._onNativeEnd, ''); |
|
19649 Transition._hasEnd[uid] = true; |
|
19650 |
|
19651 } |
|
19652 |
|
19653 style.cssText += transitionText + duration + easing + delay + cssText; |
|
19654 |
|
19655 }, |
|
19656 |
|
19657 _end: function(elapsed) { |
|
19658 var anim = this, |
|
19659 node = anim._node, |
|
19660 callback = anim._callback, |
|
19661 config = anim._config, |
|
19662 data = { |
|
19663 type: 'transition:end', |
|
19664 config: config, |
|
19665 elapsedTime: elapsed |
|
19666 }, |
|
19667 |
|
19668 nodeInstance = Y.one(node); |
|
19669 |
|
19670 anim._running = false; |
|
19671 anim._callback = null; |
|
19672 |
|
19673 if (node) { |
|
19674 if (config.on && config.on.end) { |
|
19675 setTimeout(function() { // IE: allow previous update to finish |
|
19676 config.on.end.call(nodeInstance, data); |
|
19677 |
|
19678 // nested to ensure proper fire order |
|
19679 if (callback) { |
|
19680 callback.call(nodeInstance, data); |
|
19681 } |
|
19682 |
|
19683 }, 1); |
|
19684 } else if (callback) { |
|
19685 setTimeout(function() { // IE: allow previous update to finish |
|
19686 callback.call(nodeInstance, data); |
|
19687 }, 1); |
|
19688 } |
|
19689 } |
|
19690 |
|
19691 }, |
|
19692 |
|
19693 _endNative: function(name) { |
|
19694 var node = this._node, |
|
19695 value = node.ownerDocument.defaultView.getComputedStyle(node, '')[Transition._toCamel(TRANSITION_PROPERTY)]; |
|
19696 |
|
19697 name = Transition._toHyphen(name); |
|
19698 if (typeof value === 'string') { |
|
19699 value = value.replace(new RegExp('(?:^|,\\s)' + name + ',?'), ','); |
|
19700 value = value.replace(/^,|,$/, ''); |
|
19701 node.style[TRANSITION_CAMEL] = value; |
|
19702 } |
|
19703 }, |
|
19704 |
|
19705 _onNativeEnd: function(e) { |
|
19706 var node = this, |
|
19707 uid = Y.stamp(node), |
|
19708 event = e,//e._event, |
|
19709 name = Transition._toCamel(event.propertyName), |
|
19710 elapsed = event.elapsedTime, |
|
19711 attrs = Transition._nodeAttrs[uid], |
|
19712 attr = attrs[name], |
|
19713 anim = (attr) ? attr.transition : null, |
|
19714 data, |
|
19715 config; |
|
19716 |
|
19717 if (anim) { |
|
19718 anim.removeProperty(name); |
|
19719 anim._endNative(name); |
|
19720 config = anim._config[name]; |
|
19721 |
|
19722 data = { |
|
19723 type: 'propertyEnd', |
|
19724 propertyName: name, |
|
19725 elapsedTime: elapsed, |
|
19726 config: config |
|
19727 }; |
|
19728 |
|
19729 if (config && config.on && config.on.end) { |
|
19730 config.on.end.call(Y.one(node), data); |
|
19731 } |
|
19732 |
|
19733 if (anim._count <= 0) { // after propertyEnd fires |
|
19734 anim._end(elapsed); |
|
19735 node.style[TRANSITION_PROPERTY_CAMEL] = ''; // clean up style |
|
19736 } |
|
19737 } |
|
19738 }, |
|
19739 |
|
19740 destroy: function() { |
|
19741 var anim = this, |
|
19742 node = anim._node; |
|
19743 |
|
19744 if (node) { |
|
19745 node.removeEventListener(TRANSITION_END, anim._onNativeEnd, false); |
|
19746 anim._node = null; |
|
19747 } |
|
19748 } |
|
19749 }; |
|
19750 |
|
19751 Y.Transition = Transition; |
|
19752 Y.TransitionNative = Transition; // TODO: remove |
|
19753 |
|
19754 /** |
|
19755 * Animate one or more css properties to a given value. Requires the "transition" module. |
|
19756 * <pre>example usage: |
|
19757 * Y.one('#demo').transition({ |
|
19758 * duration: 1, // in seconds, default is 0.5 |
|
19759 * easing: 'ease-out', // default is 'ease' |
|
19760 * delay: '1', // delay start for 1 second, default is 0 |
|
19761 * |
|
19762 * height: '10px', |
|
19763 * width: '10px', |
|
19764 * |
|
19765 * opacity: { // per property |
|
19766 * value: 0, |
|
19767 * duration: 2, |
|
19768 * delay: 2, |
|
19769 * easing: 'ease-in' |
|
19770 * } |
|
19771 * }); |
|
19772 * </pre> |
|
19773 * @for Node |
|
19774 * @method transition |
|
19775 * @param {Object} config An object containing one or more style properties, a duration and an easing. |
|
19776 * @param {Function} callback A function to run after the transition has completed. |
|
19777 * @chainable |
|
19778 */ |
|
19779 Y.Node.prototype.transition = function(name, config, callback) { |
|
19780 var |
|
19781 transitionAttrs = Transition._nodeAttrs[Y.stamp(this._node)], |
|
19782 anim = (transitionAttrs) ? transitionAttrs.transition || null : null, |
|
19783 fxConfig, |
|
19784 prop; |
|
19785 |
|
19786 if (typeof name === 'string') { // named effect, pull config from registry |
|
19787 if (typeof config === 'function') { |
|
19788 callback = config; |
|
19789 config = null; |
|
19790 } |
|
19791 |
|
19792 fxConfig = Transition.fx[name]; |
|
19793 |
|
19794 if (config && typeof config !== 'boolean') { |
|
19795 config = Y.clone(config); |
|
19796 |
|
19797 for (prop in fxConfig) { |
|
19798 if (fxConfig.hasOwnProperty(prop)) { |
|
19799 if (! (prop in config)) { |
|
19800 config[prop] = fxConfig[prop]; |
|
19801 } |
|
19802 } |
|
19803 } |
|
19804 } else { |
|
19805 config = fxConfig; |
|
19806 } |
|
19807 |
|
19808 } else { // name is a config, config is a callback or undefined |
|
19809 callback = config; |
|
19810 config = name; |
|
19811 } |
|
19812 |
|
19813 if (anim && !anim._running) { |
|
19814 anim.init(this, config); |
|
19815 } else { |
|
19816 anim = new Transition(this._node, config); |
|
19817 } |
|
19818 |
|
19819 anim.run(callback); |
|
19820 return this; |
|
19821 }; |
|
19822 |
|
19823 Y.Node.prototype.show = function(name, config, callback) { |
|
19824 this._show(); // show prior to transition |
|
19825 if (name && Y.Transition) { |
|
19826 if (typeof name !== 'string' && !name.push) { // named effect or array of effects supercedes default |
|
19827 if (typeof config === 'function') { |
|
19828 callback = config; |
|
19829 config = name; |
|
19830 } |
|
19831 name = Transition.SHOW_TRANSITION; |
|
19832 } |
|
19833 this.transition(name, config, callback); |
|
19834 } |
|
19835 return this; |
|
19836 }; |
|
19837 |
|
19838 Y.NodeList.prototype.show = function(name, config, callback) { |
|
19839 var nodes = this._nodes, |
|
19840 i = 0, |
|
19841 node; |
|
19842 |
|
19843 while ((node = nodes[i++])) { |
|
19844 Y.one(node).show(name, config, callback); |
|
19845 } |
|
19846 |
|
19847 return this; |
|
19848 }; |
|
19849 |
|
19850 |
|
19851 |
|
19852 var _wrapCallBack = function(anim, fn, callback) { |
|
19853 return function() { |
|
19854 if (fn) { |
|
19855 fn.call(anim); |
|
19856 } |
|
19857 if (callback && typeof callback === 'function') { |
|
19858 callback.apply(anim._node, arguments); |
|
19859 } |
|
19860 }; |
|
19861 }; |
|
19862 |
|
19863 Y.Node.prototype.hide = function(name, config, callback) { |
|
19864 if (name && Y.Transition) { |
|
19865 if (typeof config === 'function') { |
|
19866 callback = config; |
|
19867 config = null; |
|
19868 } |
|
19869 |
|
19870 callback = _wrapCallBack(this, this._hide, callback); // wrap with existing callback |
|
19871 if (typeof name !== 'string' && !name.push) { // named effect or array of effects supercedes default |
|
19872 if (typeof config === 'function') { |
|
19873 callback = config; |
|
19874 config = name; |
|
19875 } |
|
19876 name = Transition.HIDE_TRANSITION; |
|
19877 } |
|
19878 this.transition(name, config, callback); |
|
19879 } else { |
|
19880 this._hide(); |
|
19881 } |
|
19882 return this; |
|
19883 }; |
|
19884 |
|
19885 Y.NodeList.prototype.hide = function(name, config, callback) { |
|
19886 var nodes = this._nodes, |
|
19887 i = 0, |
|
19888 node; |
|
19889 |
|
19890 while ((node = nodes[i++])) { |
|
19891 Y.one(node).hide(name, config, callback); |
|
19892 } |
|
19893 |
|
19894 return this; |
|
19895 }; |
|
19896 |
|
19897 /** |
|
19898 * Animate one or more css properties to a given value. Requires the "transition" module. |
|
19899 * <pre>example usage: |
|
19900 * Y.all('.demo').transition({ |
|
19901 * duration: 1, // in seconds, default is 0.5 |
|
19902 * easing: 'ease-out', // default is 'ease' |
|
19903 * delay: '1', // delay start for 1 second, default is 0 |
|
19904 * |
|
19905 * height: '10px', |
|
19906 * width: '10px', |
|
19907 * |
|
19908 * opacity: { // per property |
|
19909 * value: 0, |
|
19910 * duration: 2, |
|
19911 * delay: 2, |
|
19912 * easing: 'ease-in' |
|
19913 * } |
|
19914 * }); |
|
19915 * </pre> |
|
19916 * @for NodeList |
|
19917 * @method transition |
|
19918 * @param {Object} config An object containing one or more style properties, a duration and an easing. |
|
19919 * @param {Function} callback A function to run after the transition has completed. The callback fires |
|
19920 * once per item in the NodeList. |
|
19921 * @chainable |
|
19922 */ |
|
19923 Y.NodeList.prototype.transition = function(config, callback) { |
|
19924 var nodes = this._nodes, |
|
19925 i = 0, |
|
19926 node; |
|
19927 |
|
19928 while ((node = nodes[i++])) { |
|
19929 Y.one(node).transition(config, callback); |
|
19930 } |
|
19931 |
|
19932 return this; |
|
19933 }; |
|
19934 |
|
19935 Y.Node.prototype.toggleView = function(name, on, callback) { |
|
19936 this._toggles = this._toggles || []; |
|
19937 callback = arguments[arguments.length - 1]; |
|
19938 |
|
19939 if (typeof name !== 'string') { // no transition, just toggle |
|
19940 on = name; |
|
19941 this._toggleView(on, callback); // call original _toggleView in Y.Node |
|
19942 return; |
|
19943 } |
|
19944 |
|
19945 if (typeof on === 'function') { // Ignore "on" if used for callback argument. |
|
19946 on = undefined; |
|
19947 } |
|
19948 |
|
19949 if (typeof on === 'undefined' && name in this._toggles) { // reverse current toggle |
|
19950 on = ! this._toggles[name]; |
|
19951 } |
|
19952 |
|
19953 on = (on) ? 1 : 0; |
|
19954 if (on) { |
|
19955 this._show(); |
|
19956 } else { |
|
19957 callback = _wrapCallBack(this, this._hide, callback); |
|
19958 } |
|
19959 |
|
19960 this._toggles[name] = on; |
|
19961 this.transition(Y.Transition.toggles[name][on], callback); |
|
19962 |
|
19963 return this; |
|
19964 }; |
|
19965 |
|
19966 Y.NodeList.prototype.toggleView = function(name, on, callback) { |
|
19967 var nodes = this._nodes, |
|
19968 i = 0, |
|
19969 node; |
|
19970 |
|
19971 while ((node = nodes[i++])) { |
|
19972 node = Y.one(node); |
|
19973 node.toggleView.apply(node, arguments); |
|
19974 } |
|
19975 |
|
19976 return this; |
|
19977 }; |
|
19978 |
|
19979 Y.mix(Transition.fx, { |
|
19980 fadeOut: { |
|
19981 opacity: 0, |
|
19982 duration: 0.5, |
|
19983 easing: 'ease-out' |
|
19984 }, |
|
19985 |
|
19986 fadeIn: { |
|
19987 opacity: 1, |
|
19988 duration: 0.5, |
|
19989 easing: 'ease-in' |
|
19990 }, |
|
19991 |
|
19992 sizeOut: { |
|
19993 height: 0, |
|
19994 width: 0, |
|
19995 duration: 0.75, |
|
19996 easing: 'ease-out' |
|
19997 }, |
|
19998 |
|
19999 sizeIn: { |
|
20000 height: function(node) { |
|
20001 return node.get('scrollHeight') + 'px'; |
|
20002 }, |
|
20003 width: function(node) { |
|
20004 return node.get('scrollWidth') + 'px'; |
|
20005 }, |
|
20006 duration: 0.5, |
|
20007 easing: 'ease-in', |
|
20008 |
|
20009 on: { |
|
20010 start: function() { |
|
20011 var overflow = this.getStyle('overflow'); |
|
20012 if (overflow !== 'hidden') { // enable scrollHeight/Width |
|
20013 this.setStyle('overflow', 'hidden'); |
|
20014 this._transitionOverflow = overflow; |
|
20015 } |
|
20016 }, |
|
20017 |
|
20018 end: function() { |
|
20019 if (this._transitionOverflow) { // revert overridden value |
|
20020 this.setStyle('overflow', this._transitionOverflow); |
|
20021 delete this._transitionOverflow; |
|
20022 } |
|
20023 } |
|
20024 } |
|
20025 } |
|
20026 }); |
|
20027 |
|
20028 Y.mix(Transition.toggles, { |
|
20029 size: ['sizeOut', 'sizeIn'], |
|
20030 fade: ['fadeOut', 'fadeIn'] |
|
20031 }); |
|
20032 |
|
20033 |
|
20034 }, '@VERSION@', {"requires": ["node-style"]}); |
|
20035 YUI.add('selector-css2', function (Y, NAME) { |
|
20036 |
|
20037 /** |
|
20038 * The selector module provides helper methods allowing CSS2 Selectors to be used with DOM elements. |
|
20039 * @module dom |
|
20040 * @submodule selector-css2 |
|
20041 * @for Selector |
|
20042 */ |
|
20043 |
|
20044 /* |
|
20045 * Provides helper methods for collecting and filtering DOM elements. |
|
20046 */ |
|
20047 |
|
20048 var PARENT_NODE = 'parentNode', |
|
20049 TAG_NAME = 'tagName', |
|
20050 ATTRIBUTES = 'attributes', |
|
20051 COMBINATOR = 'combinator', |
|
20052 PSEUDOS = 'pseudos', |
|
20053 |
|
20054 Selector = Y.Selector, |
|
20055 |
|
20056 SelectorCSS2 = { |
|
20057 _reRegExpTokens: /([\^\$\?\[\]\*\+\-\.\(\)\|\\])/, |
|
20058 SORT_RESULTS: true, |
|
20059 |
|
20060 // TODO: better detection, document specific |
|
20061 _isXML: (function() { |
|
20062 var isXML = (Y.config.doc.createElement('div').tagName !== 'DIV'); |
|
20063 return isXML; |
|
20064 }()), |
|
20065 |
|
20066 /** |
|
20067 * Mapping of shorthand tokens to corresponding attribute selector |
|
20068 * @property shorthand |
|
20069 * @type object |
|
20070 */ |
|
20071 shorthand: { |
|
20072 '\\#(-?[_a-z0-9]+[-\\w\\uE000]*)': '[id=$1]', |
|
20073 '\\.(-?[_a-z]+[-\\w\\uE000]*)': '[className~=$1]' |
|
20074 }, |
|
20075 |
|
20076 /** |
|
20077 * List of operators and corresponding boolean functions. |
|
20078 * These functions are passed the attribute and the current node's value of the attribute. |
|
20079 * @property operators |
|
20080 * @type object |
|
20081 */ |
|
20082 operators: { |
|
20083 '': function(node, attr) { return Y.DOM.getAttribute(node, attr) !== ''; }, // Just test for existence of attribute |
|
20084 '~=': '(?:^|\\s+){val}(?:\\s+|$)', // space-delimited |
|
20085 '|=': '^{val}-?' // optional hyphen-delimited |
|
20086 }, |
|
20087 |
|
20088 pseudos: { |
|
20089 'first-child': function(node) { |
|
20090 return Y.DOM._children(node[PARENT_NODE])[0] === node; |
|
20091 } |
|
20092 }, |
|
20093 |
|
20094 _bruteQuery: function(selector, root, firstOnly) { |
|
20095 var ret = [], |
|
20096 nodes = [], |
|
20097 tokens = Selector._tokenize(selector), |
|
20098 token = tokens[tokens.length - 1], |
|
20099 rootDoc = Y.DOM._getDoc(root), |
|
20100 child, |
|
20101 id, |
|
20102 className, |
|
20103 tagName; |
|
20104 |
|
20105 if (token) { |
|
20106 // prefilter nodes |
|
20107 id = token.id; |
|
20108 className = token.className; |
|
20109 tagName = token.tagName || '*'; |
|
20110 |
|
20111 if (root.getElementsByTagName) { // non-IE lacks DOM api on doc frags |
|
20112 // try ID first, unless no root.all && root not in document |
|
20113 // (root.all works off document, but not getElementById) |
|
20114 if (id && (root.all || (root.nodeType === 9 || Y.DOM.inDoc(root)))) { |
|
20115 nodes = Y.DOM.allById(id, root); |
|
20116 // try className |
|
20117 } else if (className) { |
|
20118 nodes = root.getElementsByClassName(className); |
|
20119 } else { // default to tagName |
|
20120 nodes = root.getElementsByTagName(tagName); |
|
20121 } |
|
20122 |
|
20123 } else { // brute getElementsByTagName() |
|
20124 child = root.firstChild; |
|
20125 while (child) { |
|
20126 // only collect HTMLElements |
|
20127 // match tag to supplement missing getElementsByTagName |
|
20128 if (child.tagName && (tagName === '*' || child.tagName === tagName)) { |
|
20129 nodes.push(child); |
|
20130 } |
|
20131 child = child.nextSibling || child.firstChild; |
|
20132 } |
|
20133 } |
|
20134 if (nodes.length) { |
|
20135 ret = Selector._filterNodes(nodes, tokens, firstOnly); |
|
20136 } |
|
20137 } |
|
20138 |
|
20139 return ret; |
|
20140 }, |
|
20141 |
|
20142 _filterNodes: function(nodes, tokens, firstOnly) { |
|
20143 var i = 0, |
|
20144 j, |
|
20145 len = tokens.length, |
|
20146 n = len - 1, |
|
20147 result = [], |
|
20148 node = nodes[0], |
|
20149 tmpNode = node, |
|
20150 getters = Y.Selector.getters, |
|
20151 operator, |
|
20152 combinator, |
|
20153 token, |
|
20154 path, |
|
20155 pass, |
|
20156 value, |
|
20157 tests, |
|
20158 test; |
|
20159 |
|
20160 for (i = 0; (tmpNode = node = nodes[i++]);) { |
|
20161 n = len - 1; |
|
20162 path = null; |
|
20163 |
|
20164 testLoop: |
|
20165 while (tmpNode && tmpNode.tagName) { |
|
20166 token = tokens[n]; |
|
20167 tests = token.tests; |
|
20168 j = tests.length; |
|
20169 if (j && !pass) { |
|
20170 while ((test = tests[--j])) { |
|
20171 operator = test[1]; |
|
20172 if (getters[test[0]]) { |
|
20173 value = getters[test[0]](tmpNode, test[0]); |
|
20174 } else { |
|
20175 value = tmpNode[test[0]]; |
|
20176 if (test[0] === 'tagName' && !Selector._isXML) { |
|
20177 value = value.toUpperCase(); |
|
20178 } |
|
20179 if (typeof value != 'string' && value !== undefined && value.toString) { |
|
20180 value = value.toString(); // coerce for comparison |
|
20181 } else if (value === undefined && tmpNode.getAttribute) { |
|
20182 // use getAttribute for non-standard attributes |
|
20183 value = tmpNode.getAttribute(test[0], 2); // 2 === force string for IE |
|
20184 } |
|
20185 } |
|
20186 |
|
20187 if ((operator === '=' && value !== test[2]) || // fast path for equality |
|
20188 (typeof operator !== 'string' && // protect against String.test monkey-patch (Moo) |
|
20189 operator.test && !operator.test(value)) || // regex test |
|
20190 (!operator.test && // protect against RegExp as function (webkit) |
|
20191 typeof operator === 'function' && !operator(tmpNode, test[0], test[2]))) { // function test |
|
20192 |
|
20193 // skip non element nodes or non-matching tags |
|
20194 if ((tmpNode = tmpNode[path])) { |
|
20195 while (tmpNode && |
|
20196 (!tmpNode.tagName || |
|
20197 (token.tagName && token.tagName !== tmpNode.tagName)) |
|
20198 ) { |
|
20199 tmpNode = tmpNode[path]; |
|
20200 } |
|
20201 } |
|
20202 continue testLoop; |
|
20203 } |
|
20204 } |
|
20205 } |
|
20206 |
|
20207 n--; // move to next token |
|
20208 // now that we've passed the test, move up the tree by combinator |
|
20209 if (!pass && (combinator = token.combinator)) { |
|
20210 path = combinator.axis; |
|
20211 tmpNode = tmpNode[path]; |
|
20212 |
|
20213 // skip non element nodes |
|
20214 while (tmpNode && !tmpNode.tagName) { |
|
20215 tmpNode = tmpNode[path]; |
|
20216 } |
|
20217 |
|
20218 if (combinator.direct) { // one pass only |
|
20219 path = null; |
|
20220 } |
|
20221 |
|
20222 } else { // success if we made it this far |
|
20223 result.push(node); |
|
20224 if (firstOnly) { |
|
20225 return result; |
|
20226 } |
|
20227 break; |
|
20228 } |
|
20229 } |
|
20230 } |
|
20231 node = tmpNode = null; |
|
20232 return result; |
|
20233 }, |
|
20234 |
|
20235 combinators: { |
|
20236 ' ': { |
|
20237 axis: 'parentNode' |
|
20238 }, |
|
20239 |
|
20240 '>': { |
|
20241 axis: 'parentNode', |
|
20242 direct: true |
|
20243 }, |
|
20244 |
|
20245 |
|
20246 '+': { |
|
20247 axis: 'previousSibling', |
|
20248 direct: true |
|
20249 } |
|
20250 }, |
|
20251 |
|
20252 _parsers: [ |
|
20253 { |
|
20254 name: ATTRIBUTES, |
|
20255 re: /^\uE003(-?[a-z]+[\w\-]*)+([~\|\^\$\*!=]=?)?['"]?([^\uE004'"]*)['"]?\uE004/i, |
|
20256 fn: function(match, token) { |
|
20257 var operator = match[2] || '', |
|
20258 operators = Selector.operators, |
|
20259 escVal = (match[3]) ? match[3].replace(/\\/g, '') : '', |
|
20260 test; |
|
20261 |
|
20262 // add prefiltering for ID and CLASS |
|
20263 if ((match[1] === 'id' && operator === '=') || |
|
20264 (match[1] === 'className' && |
|
20265 Y.config.doc.documentElement.getElementsByClassName && |
|
20266 (operator === '~=' || operator === '='))) { |
|
20267 token.prefilter = match[1]; |
|
20268 |
|
20269 |
|
20270 match[3] = escVal; |
|
20271 |
|
20272 // escape all but ID for prefilter, which may run through QSA (via Dom.allById) |
|
20273 token[match[1]] = (match[1] === 'id') ? match[3] : escVal; |
|
20274 |
|
20275 } |
|
20276 |
|
20277 // add tests |
|
20278 if (operator in operators) { |
|
20279 test = operators[operator]; |
|
20280 if (typeof test === 'string') { |
|
20281 match[3] = escVal.replace(Selector._reRegExpTokens, '\\$1'); |
|
20282 test = new RegExp(test.replace('{val}', match[3])); |
|
20283 } |
|
20284 match[2] = test; |
|
20285 } |
|
20286 if (!token.last || token.prefilter !== match[1]) { |
|
20287 return match.slice(1); |
|
20288 } |
|
20289 } |
|
20290 }, |
|
20291 { |
|
20292 name: TAG_NAME, |
|
20293 re: /^((?:-?[_a-z]+[\w-]*)|\*)/i, |
|
20294 fn: function(match, token) { |
|
20295 var tag = match[1]; |
|
20296 |
|
20297 if (!Selector._isXML) { |
|
20298 tag = tag.toUpperCase(); |
|
20299 } |
|
20300 |
|
20301 token.tagName = tag; |
|
20302 |
|
20303 if (tag !== '*' && (!token.last || token.prefilter)) { |
|
20304 return [TAG_NAME, '=', tag]; |
|
20305 } |
|
20306 if (!token.prefilter) { |
|
20307 token.prefilter = 'tagName'; |
|
20308 } |
|
20309 } |
|
20310 }, |
|
20311 { |
|
20312 name: COMBINATOR, |
|
20313 re: /^\s*([>+~]|\s)\s*/, |
|
20314 fn: function(match, token) { |
|
20315 } |
|
20316 }, |
|
20317 { |
|
20318 name: PSEUDOS, |
|
20319 re: /^:([\-\w]+)(?:\uE005['"]?([^\uE005]*)['"]?\uE006)*/i, |
|
20320 fn: function(match, token) { |
|
20321 var test = Selector[PSEUDOS][match[1]]; |
|
20322 if (test) { // reorder match array and unescape special chars for tests |
|
20323 if (match[2]) { |
|
20324 match[2] = match[2].replace(/\\/g, ''); |
|
20325 } |
|
20326 return [match[2], test]; |
|
20327 } else { // selector token not supported (possibly missing CSS3 module) |
|
20328 return false; |
|
20329 } |
|
20330 } |
|
20331 } |
|
20332 ], |
|
20333 |
|
20334 _getToken: function(token) { |
|
20335 return { |
|
20336 tagName: null, |
|
20337 id: null, |
|
20338 className: null, |
|
20339 attributes: {}, |
|
20340 combinator: null, |
|
20341 tests: [] |
|
20342 }; |
|
20343 }, |
|
20344 |
|
20345 /* |
|
20346 Break selector into token units per simple selector. |
|
20347 Combinator is attached to the previous token. |
|
20348 */ |
|
20349 _tokenize: function(selector) { |
|
20350 selector = selector || ''; |
|
20351 selector = Selector._parseSelector(Y.Lang.trim(selector)); |
|
20352 var token = Selector._getToken(), // one token per simple selector (left selector holds combinator) |
|
20353 query = selector, // original query for debug report |
|
20354 tokens = [], // array of tokens |
|
20355 found = false, // whether or not any matches were found this pass |
|
20356 match, // the regex match |
|
20357 test, |
|
20358 i, parser; |
|
20359 |
|
20360 /* |
|
20361 Search for selector patterns, store, and strip them from the selector string |
|
20362 until no patterns match (invalid selector) or we run out of chars. |
|
20363 |
|
20364 Multiple attributes and pseudos are allowed, in any order. |
|
20365 for example: |
|
20366 'form:first-child[type=button]:not(button)[lang|=en]' |
|
20367 */ |
|
20368 outer: |
|
20369 do { |
|
20370 found = false; // reset after full pass |
|
20371 for (i = 0; (parser = Selector._parsers[i++]);) { |
|
20372 if ( (match = parser.re.exec(selector)) ) { // note assignment |
|
20373 if (parser.name !== COMBINATOR ) { |
|
20374 token.selector = selector; |
|
20375 } |
|
20376 selector = selector.replace(match[0], ''); // strip current match from selector |
|
20377 if (!selector.length) { |
|
20378 token.last = true; |
|
20379 } |
|
20380 |
|
20381 if (Selector._attrFilters[match[1]]) { // convert class to className, etc. |
|
20382 match[1] = Selector._attrFilters[match[1]]; |
|
20383 } |
|
20384 |
|
20385 test = parser.fn(match, token); |
|
20386 if (test === false) { // selector not supported |
|
20387 found = false; |
|
20388 break outer; |
|
20389 } else if (test) { |
|
20390 token.tests.push(test); |
|
20391 } |
|
20392 |
|
20393 if (!selector.length || parser.name === COMBINATOR) { |
|
20394 tokens.push(token); |
|
20395 token = Selector._getToken(token); |
|
20396 if (parser.name === COMBINATOR) { |
|
20397 token.combinator = Y.Selector.combinators[match[1]]; |
|
20398 } |
|
20399 } |
|
20400 found = true; |
|
20401 } |
|
20402 } |
|
20403 } while (found && selector.length); |
|
20404 |
|
20405 if (!found || selector.length) { // not fully parsed |
|
20406 tokens = []; |
|
20407 } |
|
20408 return tokens; |
|
20409 }, |
|
20410 |
|
20411 _replaceMarkers: function(selector) { |
|
20412 selector = selector.replace(/\[/g, '\uE003'); |
|
20413 selector = selector.replace(/\]/g, '\uE004'); |
|
20414 |
|
20415 selector = selector.replace(/\(/g, '\uE005'); |
|
20416 selector = selector.replace(/\)/g, '\uE006'); |
|
20417 return selector; |
|
20418 }, |
|
20419 |
|
20420 _replaceShorthand: function(selector) { |
|
20421 var shorthand = Y.Selector.shorthand, |
|
20422 re; |
|
20423 |
|
20424 for (re in shorthand) { |
|
20425 if (shorthand.hasOwnProperty(re)) { |
|
20426 selector = selector.replace(new RegExp(re, 'gi'), shorthand[re]); |
|
20427 } |
|
20428 } |
|
20429 |
|
20430 return selector; |
|
20431 }, |
|
20432 |
|
20433 _parseSelector: function(selector) { |
|
20434 var replaced = Y.Selector._replaceSelector(selector), |
|
20435 selector = replaced.selector; |
|
20436 |
|
20437 // replace shorthand (".foo, #bar") after pseudos and attrs |
|
20438 // to avoid replacing unescaped chars |
|
20439 selector = Y.Selector._replaceShorthand(selector); |
|
20440 |
|
20441 selector = Y.Selector._restore('attr', selector, replaced.attrs); |
|
20442 selector = Y.Selector._restore('pseudo', selector, replaced.pseudos); |
|
20443 |
|
20444 // replace braces and parens before restoring escaped chars |
|
20445 // to avoid replacing ecaped markers |
|
20446 selector = Y.Selector._replaceMarkers(selector); |
|
20447 selector = Y.Selector._restore('esc', selector, replaced.esc); |
|
20448 |
|
20449 return selector; |
|
20450 }, |
|
20451 |
|
20452 _attrFilters: { |
|
20453 'class': 'className', |
|
20454 'for': 'htmlFor' |
|
20455 }, |
|
20456 |
|
20457 getters: { |
|
20458 href: function(node, attr) { |
|
20459 return Y.DOM.getAttribute(node, attr); |
|
20460 }, |
|
20461 |
|
20462 id: function(node, attr) { |
|
20463 return Y.DOM.getId(node); |
|
20464 } |
|
20465 } |
|
20466 }; |
|
20467 |
|
20468 Y.mix(Y.Selector, SelectorCSS2, true); |
|
20469 Y.Selector.getters.src = Y.Selector.getters.rel = Y.Selector.getters.href; |
|
20470 |
|
20471 // IE wants class with native queries |
|
20472 if (Y.Selector.useNative && Y.config.doc.querySelector) { |
|
20473 Y.Selector.shorthand['\\.(-?[_a-z]+[-\\w]*)'] = '[class~=$1]'; |
|
20474 } |
|
20475 |
|
20476 |
|
20477 |
|
20478 }, '@VERSION@', {"requires": ["selector-native"]}); |
|
20479 YUI.add('selector-css3', function (Y, NAME) { |
|
20480 |
|
20481 /** |
|
20482 * The selector css3 module provides support for css3 selectors. |
|
20483 * @module dom |
|
20484 * @submodule selector-css3 |
|
20485 * @for Selector |
|
20486 */ |
|
20487 |
|
20488 /* |
|
20489 an+b = get every _a_th node starting at the _b_th |
|
20490 0n+b = no repeat ("0" and "n" may both be omitted (together) , e.g. "0n+1" or "1", not "0+1"), return only the _b_th element |
|
20491 1n+b = get every element starting from b ("1" may may be omitted, e.g. "1n+0" or "n+0" or "n") |
|
20492 an+0 = get every _a_th element, "0" may be omitted |
|
20493 */ |
|
20494 |
|
20495 Y.Selector._reNth = /^(?:([\-]?\d*)(n){1}|(odd|even)$)*([\-+]?\d*)$/; |
|
20496 |
|
20497 Y.Selector._getNth = function(node, expr, tag, reverse) { |
|
20498 Y.Selector._reNth.test(expr); |
|
20499 var a = parseInt(RegExp.$1, 10), // include every _a_ elements (zero means no repeat, just first _a_) |
|
20500 n = RegExp.$2, // "n" |
|
20501 oddeven = RegExp.$3, // "odd" or "even" |
|
20502 b = parseInt(RegExp.$4, 10) || 0, // start scan from element _b_ |
|
20503 result = [], |
|
20504 siblings = Y.DOM._children(node.parentNode, tag), |
|
20505 op; |
|
20506 |
|
20507 if (oddeven) { |
|
20508 a = 2; // always every other |
|
20509 op = '+'; |
|
20510 n = 'n'; |
|
20511 b = (oddeven === 'odd') ? 1 : 0; |
|
20512 } else if ( isNaN(a) ) { |
|
20513 a = (n) ? 1 : 0; // start from the first or no repeat |
|
20514 } |
|
20515 |
|
20516 if (a === 0) { // just the first |
|
20517 if (reverse) { |
|
20518 b = siblings.length - b + 1; |
|
20519 } |
|
20520 |
|
20521 if (siblings[b - 1] === node) { |
|
20522 return true; |
|
20523 } else { |
|
20524 return false; |
|
20525 } |
|
20526 |
|
20527 } else if (a < 0) { |
|
20528 reverse = !!reverse; |
|
20529 a = Math.abs(a); |
|
20530 } |
|
20531 |
|
20532 if (!reverse) { |
|
20533 for (var i = b - 1, len = siblings.length; i < len; i += a) { |
|
20534 if ( i >= 0 && siblings[i] === node ) { |
|
20535 return true; |
|
20536 } |
|
20537 } |
|
20538 } else { |
|
20539 for (var i = siblings.length - b, len = siblings.length; i >= 0; i -= a) { |
|
20540 if ( i < len && siblings[i] === node ) { |
|
20541 return true; |
|
20542 } |
|
20543 } |
|
20544 } |
|
20545 return false; |
|
20546 }; |
|
20547 |
|
20548 Y.mix(Y.Selector.pseudos, { |
|
20549 'root': function(node) { |
|
20550 return node === node.ownerDocument.documentElement; |
|
20551 }, |
|
20552 |
|
20553 'nth-child': function(node, expr) { |
|
20554 return Y.Selector._getNth(node, expr); |
|
20555 }, |
|
20556 |
|
20557 'nth-last-child': function(node, expr) { |
|
20558 return Y.Selector._getNth(node, expr, null, true); |
|
20559 }, |
|
20560 |
|
20561 'nth-of-type': function(node, expr) { |
|
20562 return Y.Selector._getNth(node, expr, node.tagName); |
|
20563 }, |
|
20564 |
|
20565 'nth-last-of-type': function(node, expr) { |
|
20566 return Y.Selector._getNth(node, expr, node.tagName, true); |
|
20567 }, |
|
20568 |
|
20569 'last-child': function(node) { |
|
20570 var children = Y.DOM._children(node.parentNode); |
|
20571 return children[children.length - 1] === node; |
|
20572 }, |
|
20573 |
|
20574 'first-of-type': function(node) { |
|
20575 return Y.DOM._children(node.parentNode, node.tagName)[0] === node; |
|
20576 }, |
|
20577 |
|
20578 'last-of-type': function(node) { |
|
20579 var children = Y.DOM._children(node.parentNode, node.tagName); |
|
20580 return children[children.length - 1] === node; |
|
20581 }, |
|
20582 |
|
20583 'only-child': function(node) { |
|
20584 var children = Y.DOM._children(node.parentNode); |
|
20585 return children.length === 1 && children[0] === node; |
|
20586 }, |
|
20587 |
|
20588 'only-of-type': function(node) { |
|
20589 var children = Y.DOM._children(node.parentNode, node.tagName); |
|
20590 return children.length === 1 && children[0] === node; |
|
20591 }, |
|
20592 |
|
20593 'empty': function(node) { |
|
20594 return node.childNodes.length === 0; |
|
20595 }, |
|
20596 |
|
20597 'not': function(node, expr) { |
|
20598 return !Y.Selector.test(node, expr); |
|
20599 }, |
|
20600 |
|
20601 'contains': function(node, expr) { |
|
20602 var text = node.innerText || node.textContent || ''; |
|
20603 return text.indexOf(expr) > -1; |
|
20604 }, |
|
20605 |
|
20606 'checked': function(node) { |
|
20607 return (node.checked === true || node.selected === true); |
|
20608 }, |
|
20609 |
|
20610 enabled: function(node) { |
|
20611 return (node.disabled !== undefined && !node.disabled); |
|
20612 }, |
|
20613 |
|
20614 disabled: function(node) { |
|
20615 return (node.disabled); |
|
20616 } |
|
20617 }); |
|
20618 |
|
20619 Y.mix(Y.Selector.operators, { |
|
20620 '^=': '^{val}', // Match starts with value |
|
20621 '$=': '{val}$', // Match ends with value |
|
20622 '*=': '{val}' // Match contains value as substring |
|
20623 }); |
|
20624 |
|
20625 Y.Selector.combinators['~'] = { |
|
20626 axis: 'previousSibling' |
|
20627 }; |
|
20628 |
|
20629 |
|
20630 }, '@VERSION@', {"requires": ["selector-native", "selector-css2"]}); |
|
20631 YUI.add('yui-log', function (Y, NAME) { |
|
20632 |
|
20633 /** |
|
20634 * Provides console log capability and exposes a custom event for |
|
20635 * console implementations. This module is a `core` YUI module, |
|
20636 * <a href="../classes/YUI.html#method_log">it's documentation is located under the YUI class</a>. |
|
20637 * |
|
20638 * @module yui |
|
20639 * @submodule yui-log |
|
20640 */ |
|
20641 |
|
20642 var INSTANCE = Y, |
|
20643 LOGEVENT = 'yui:log', |
|
20644 UNDEFINED = 'undefined', |
|
20645 LEVELS = { debug: 1, |
|
20646 info: 2, |
|
20647 warn: 4, |
|
20648 error: 8 }; |
|
20649 |
|
20650 /** |
|
20651 * If the 'debug' config is true, a 'yui:log' event will be |
|
20652 * dispatched, which the Console widget and anything else |
|
20653 * can consume. If the 'useBrowserConsole' config is true, it will |
|
20654 * write to the browser console if available. YUI-specific log |
|
20655 * messages will only be present in the -debug versions of the |
|
20656 * JS files. The build system is supposed to remove log statements |
|
20657 * from the raw and minified versions of the files. |
|
20658 * |
|
20659 * @method log |
|
20660 * @for YUI |
|
20661 * @param {String} msg The message to log. |
|
20662 * @param {String} cat The log category for the message. Default |
|
20663 * categories are "info", "warn", "error", time". |
|
20664 * Custom categories can be used as well. (opt). |
|
20665 * @param {String} src The source of the the message (opt). |
|
20666 * @param {boolean} silent If true, the log event won't fire. |
|
20667 * @return {YUI} YUI instance. |
|
20668 */ |
|
20669 INSTANCE.log = function(msg, cat, src, silent) { |
|
20670 var bail, excl, incl, m, f, minlevel, |
|
20671 Y = INSTANCE, |
|
20672 c = Y.config, |
|
20673 publisher = (Y.fire) ? Y : YUI.Env.globalEvents; |
|
20674 // suppress log message if the config is off or the event stack |
|
20675 // or the event call stack contains a consumer of the yui:log event |
|
20676 if (c.debug) { |
|
20677 // apply source filters |
|
20678 src = src || ""; |
|
20679 if (typeof src !== "undefined") { |
|
20680 excl = c.logExclude; |
|
20681 incl = c.logInclude; |
|
20682 if (incl && !(src in incl)) { |
|
20683 bail = 1; |
|
20684 } else if (incl && (src in incl)) { |
|
20685 bail = !incl[src]; |
|
20686 } else if (excl && (src in excl)) { |
|
20687 bail = excl[src]; |
|
20688 } |
|
20689 |
|
20690 // Determine the current minlevel as defined in configuration |
|
20691 Y.config.logLevel = Y.config.logLevel || 'debug'; |
|
20692 minlevel = LEVELS[Y.config.logLevel.toLowerCase()]; |
|
20693 |
|
20694 if (cat in LEVELS && LEVELS[cat] < minlevel) { |
|
20695 // Skip this message if the we don't meet the defined minlevel |
|
20696 bail = 1; |
|
20697 } |
|
20698 } |
|
20699 if (!bail) { |
|
20700 if (c.useBrowserConsole) { |
|
20701 m = (src) ? src + ': ' + msg : msg; |
|
20702 if (Y.Lang.isFunction(c.logFn)) { |
|
20703 c.logFn.call(Y, msg, cat, src); |
|
20704 } else if (typeof console !== UNDEFINED && console.log) { |
|
20705 f = (cat && console[cat] && (cat in LEVELS)) ? cat : 'log'; |
|
20706 console[f](m); |
|
20707 } else if (typeof opera !== UNDEFINED) { |
|
20708 opera.postError(m); |
|
20709 } |
|
20710 } |
|
20711 |
|
20712 if (publisher && !silent) { |
|
20713 |
|
20714 if (publisher === Y && (!publisher.getEvent(LOGEVENT))) { |
|
20715 publisher.publish(LOGEVENT, { |
|
20716 broadcast: 2 |
|
20717 }); |
|
20718 } |
|
20719 |
|
20720 publisher.fire(LOGEVENT, { |
|
20721 msg: msg, |
|
20722 cat: cat, |
|
20723 src: src |
|
20724 }); |
|
20725 } |
|
20726 } |
|
20727 } |
|
20728 |
|
20729 return Y; |
|
20730 }; |
|
20731 |
|
20732 /** |
|
20733 * Write a system message. This message will be preserved in the |
|
20734 * minified and raw versions of the YUI files, unlike log statements. |
|
20735 * @method message |
|
20736 * @for YUI |
|
20737 * @param {String} msg The message to log. |
|
20738 * @param {String} cat The log category for the message. Default |
|
20739 * categories are "info", "warn", "error", time". |
|
20740 * Custom categories can be used as well. (opt). |
|
20741 * @param {String} src The source of the the message (opt). |
|
20742 * @param {boolean} silent If true, the log event won't fire. |
|
20743 * @return {YUI} YUI instance. |
|
20744 */ |
|
20745 INSTANCE.message = function() { |
|
20746 return INSTANCE.log.apply(INSTANCE, arguments); |
|
20747 }; |
|
20748 |
|
20749 |
|
20750 }, '@VERSION@', {"requires": ["yui-base"]}); |
|
20751 YUI.add('dump', function (Y, NAME) { |
|
20752 |
|
20753 /** |
|
20754 * Returns a simple string representation of the object or array. |
|
20755 * Other types of objects will be returned unprocessed. Arrays |
|
20756 * are expected to be indexed. Use object notation for |
|
20757 * associative arrays. |
|
20758 * |
|
20759 * If included, the dump method is added to the YUI instance. |
|
20760 * |
|
20761 * @module dump |
|
20762 */ |
|
20763 |
|
20764 var L = Y.Lang, |
|
20765 OBJ = '{...}', |
|
20766 FUN = 'f(){...}', |
|
20767 COMMA = ', ', |
|
20768 ARROW = ' => ', |
|
20769 |
|
20770 /** |
|
20771 * Returns a simple string representation of the object or array. |
|
20772 * Other types of objects will be returned unprocessed. Arrays |
|
20773 * are expected to be indexed. |
|
20774 * |
|
20775 * @method dump |
|
20776 * @param {Object} o The object to dump. |
|
20777 * @param {Number} d How deep to recurse child objects, default 3. |
|
20778 * @return {String} the dump result. |
|
20779 * @for YUI |
|
20780 */ |
|
20781 dump = function(o, d) { |
|
20782 var i, len, s = [], type = L.type(o); |
|
20783 |
|
20784 // Cast non-objects to string |
|
20785 // Skip dates because the std toString is what we want |
|
20786 // Skip HTMLElement-like objects because trying to dump |
|
20787 // an element will cause an unhandled exception in FF 2.x |
|
20788 if (!L.isObject(o)) { |
|
20789 return o + ''; |
|
20790 } else if (type == 'date') { |
|
20791 return o; |
|
20792 } else if (o.nodeType && o.tagName) { |
|
20793 return o.tagName + '#' + o.id; |
|
20794 } else if (o.document && o.navigator) { |
|
20795 return 'window'; |
|
20796 } else if (o.location && o.body) { |
|
20797 return 'document'; |
|
20798 } else if (type == 'function') { |
|
20799 return FUN; |
|
20800 } |
|
20801 |
|
20802 // dig into child objects the depth specifed. Default 3 |
|
20803 d = (L.isNumber(d)) ? d : 3; |
|
20804 |
|
20805 // arrays [1, 2, 3] |
|
20806 if (type == 'array') { |
|
20807 s.push('['); |
|
20808 for (i = 0, len = o.length; i < len; i = i + 1) { |
|
20809 if (L.isObject(o[i])) { |
|
20810 s.push((d > 0) ? L.dump(o[i], d - 1) : OBJ); |
|
20811 } else { |
|
20812 s.push(o[i]); |
|
20813 } |
|
20814 s.push(COMMA); |
|
20815 } |
|
20816 if (s.length > 1) { |
|
20817 s.pop(); |
|
20818 } |
|
20819 s.push(']'); |
|
20820 // regexp /foo/ |
|
20821 } else if (type == 'regexp') { |
|
20822 s.push(o.toString()); |
|
20823 // objects {k1 => v1, k2 => v2} |
|
20824 } else { |
|
20825 s.push('{'); |
|
20826 for (i in o) { |
|
20827 if (o.hasOwnProperty(i)) { |
|
20828 try { |
|
20829 s.push(i + ARROW); |
|
20830 if (L.isObject(o[i])) { |
|
20831 s.push((d > 0) ? L.dump(o[i], d - 1) : OBJ); |
|
20832 } else { |
|
20833 s.push(o[i]); |
|
20834 } |
|
20835 s.push(COMMA); |
|
20836 } catch (e) { |
|
20837 s.push('Error: ' + e.message); |
|
20838 } |
|
20839 } |
|
20840 } |
|
20841 if (s.length > 1) { |
|
20842 s.pop(); |
|
20843 } |
|
20844 s.push('}'); |
|
20845 } |
|
20846 |
|
20847 return s.join(''); |
|
20848 }; |
|
20849 |
|
20850 Y.dump = dump; |
|
20851 L.dump = dump; |
|
20852 |
|
20853 |
|
20854 |
|
20855 }, '@VERSION@', {"requires": ["yui-base"]}); |
|
20856 YUI.add('transition-timer', function (Y, NAME) { |
|
20857 |
|
20858 /** |
|
20859 * Provides the base Transition class, for animating numeric properties. |
|
20860 * |
|
20861 * @module transition |
|
20862 * @submodule transition-timer |
|
20863 */ |
|
20864 |
|
20865 |
|
20866 var Transition = Y.Transition; |
|
20867 |
|
20868 Y.mix(Transition.prototype, { |
|
20869 _start: function() { |
|
20870 if (Transition.useNative) { |
|
20871 this._runNative(); |
|
20872 } else { |
|
20873 this._runTimer(); |
|
20874 } |
|
20875 }, |
|
20876 |
|
20877 _runTimer: function() { |
|
20878 var anim = this; |
|
20879 anim._initAttrs(); |
|
20880 |
|
20881 Transition._running[Y.stamp(anim)] = anim; |
|
20882 anim._startTime = new Date(); |
|
20883 Transition._startTimer(); |
|
20884 }, |
|
20885 |
|
20886 _endTimer: function() { |
|
20887 var anim = this; |
|
20888 delete Transition._running[Y.stamp(anim)]; |
|
20889 anim._startTime = null; |
|
20890 }, |
|
20891 |
|
20892 _runFrame: function() { |
|
20893 var t = new Date() - this._startTime; |
|
20894 this._runAttrs(t); |
|
20895 }, |
|
20896 |
|
20897 _runAttrs: function(time) { |
|
20898 var anim = this, |
|
20899 node = anim._node, |
|
20900 config = anim._config, |
|
20901 uid = Y.stamp(node), |
|
20902 attrs = Transition._nodeAttrs[uid], |
|
20903 customAttr = Transition.behaviors, |
|
20904 done = false, |
|
20905 allDone = false, |
|
20906 data, |
|
20907 name, |
|
20908 attribute, |
|
20909 setter, |
|
20910 elapsed, |
|
20911 delay, |
|
20912 d, |
|
20913 t, |
|
20914 i; |
|
20915 |
|
20916 for (name in attrs) { |
|
20917 if ((attribute = attrs[name]) && attribute.transition === anim) { |
|
20918 d = attribute.duration; |
|
20919 delay = attribute.delay; |
|
20920 elapsed = (time - delay) / 1000; |
|
20921 t = time; |
|
20922 data = { |
|
20923 type: 'propertyEnd', |
|
20924 propertyName: name, |
|
20925 config: config, |
|
20926 elapsedTime: elapsed |
|
20927 }; |
|
20928 |
|
20929 setter = (i in customAttr && 'set' in customAttr[i]) ? |
|
20930 customAttr[i].set : Transition.DEFAULT_SETTER; |
|
20931 |
|
20932 done = (t >= d); |
|
20933 |
|
20934 if (t > d) { |
|
20935 t = d; |
|
20936 } |
|
20937 |
|
20938 if (!delay || time >= delay) { |
|
20939 setter(anim, name, attribute.from, attribute.to, t - delay, d - delay, |
|
20940 attribute.easing, attribute.unit); |
|
20941 |
|
20942 if (done) { |
|
20943 delete attrs[name]; |
|
20944 anim._count--; |
|
20945 |
|
20946 if (config[name] && config[name].on && config[name].on.end) { |
|
20947 config[name].on.end.call(Y.one(node), data); |
|
20948 } |
|
20949 |
|
20950 //node.fire('transition:propertyEnd', data); |
|
20951 |
|
20952 if (!allDone && anim._count <= 0) { |
|
20953 allDone = true; |
|
20954 anim._end(elapsed); |
|
20955 anim._endTimer(); |
|
20956 } |
|
20957 } |
|
20958 } |
|
20959 |
|
20960 } |
|
20961 } |
|
20962 }, |
|
20963 |
|
20964 _initAttrs: function() { |
|
20965 var anim = this, |
|
20966 customAttr = Transition.behaviors, |
|
20967 uid = Y.stamp(anim._node), |
|
20968 attrs = Transition._nodeAttrs[uid], |
|
20969 attribute, |
|
20970 duration, |
|
20971 delay, |
|
20972 easing, |
|
20973 val, |
|
20974 name, |
|
20975 mTo, |
|
20976 mFrom, |
|
20977 unit, begin, end; |
|
20978 |
|
20979 for (name in attrs) { |
|
20980 if ((attribute = attrs[name]) && attribute.transition === anim) { |
|
20981 duration = attribute.duration * 1000; |
|
20982 delay = attribute.delay * 1000; |
|
20983 easing = attribute.easing; |
|
20984 val = attribute.value; |
|
20985 |
|
20986 // only allow supported properties |
|
20987 if (name in anim._node.style || name in Y.DOM.CUSTOM_STYLES) { |
|
20988 begin = (name in customAttr && 'get' in customAttr[name]) ? |
|
20989 customAttr[name].get(anim, name) : Transition.DEFAULT_GETTER(anim, name); |
|
20990 |
|
20991 mFrom = Transition.RE_UNITS.exec(begin); |
|
20992 mTo = Transition.RE_UNITS.exec(val); |
|
20993 |
|
20994 begin = mFrom ? mFrom[1] : begin; |
|
20995 end = mTo ? mTo[1] : val; |
|
20996 unit = mTo ? mTo[2] : mFrom ? mFrom[2] : ''; // one might be zero TODO: mixed units |
|
20997 |
|
20998 if (!unit && Transition.RE_DEFAULT_UNIT.test(name)) { |
|
20999 unit = Transition.DEFAULT_UNIT; |
|
21000 } |
|
21001 |
|
21002 if (typeof easing === 'string') { |
|
21003 if (easing.indexOf('cubic-bezier') > -1) { |
|
21004 easing = easing.substring(13, easing.length - 1).split(','); |
|
21005 } else if (Transition.easings[easing]) { |
|
21006 easing = Transition.easings[easing]; |
|
21007 } |
|
21008 } |
|
21009 |
|
21010 attribute.from = Number(begin); |
|
21011 attribute.to = Number(end); |
|
21012 attribute.unit = unit; |
|
21013 attribute.easing = easing; |
|
21014 attribute.duration = duration + delay; |
|
21015 attribute.delay = delay; |
|
21016 } else { |
|
21017 delete attrs[name]; |
|
21018 anim._count--; |
|
21019 } |
|
21020 } |
|
21021 } |
|
21022 }, |
|
21023 |
|
21024 destroy: function() { |
|
21025 this.detachAll(); |
|
21026 this._node = null; |
|
21027 } |
|
21028 }, true); |
|
21029 |
|
21030 Y.mix(Y.Transition, { |
|
21031 _runtimeAttrs: {}, |
|
21032 /* |
|
21033 * Regex of properties that should use the default unit. |
|
21034 * |
|
21035 * @property RE_DEFAULT_UNIT |
|
21036 * @static |
|
21037 */ |
|
21038 RE_DEFAULT_UNIT: /^width|height|top|right|bottom|left|margin.*|padding.*|border.*$/i, |
|
21039 |
|
21040 /* |
|
21041 * The default unit to use with properties that pass the RE_DEFAULT_UNIT test. |
|
21042 * |
|
21043 * @property DEFAULT_UNIT |
|
21044 * @static |
|
21045 */ |
|
21046 DEFAULT_UNIT: 'px', |
|
21047 |
|
21048 /* |
|
21049 * Time in milliseconds passed to setInterval for frame processing |
|
21050 * |
|
21051 * @property intervalTime |
|
21052 * @default 20 |
|
21053 * @static |
|
21054 */ |
|
21055 intervalTime: 20, |
|
21056 |
|
21057 /* |
|
21058 * Bucket for custom getters and setters |
|
21059 * |
|
21060 * @property behaviors |
|
21061 * @static |
|
21062 */ |
|
21063 behaviors: { |
|
21064 left: { |
|
21065 get: function(anim, attr) { |
|
21066 return Y.DOM._getAttrOffset(anim._node, attr); |
|
21067 } |
|
21068 } |
|
21069 }, |
|
21070 |
|
21071 /* |
|
21072 * The default setter to use when setting object properties. |
|
21073 * |
|
21074 * @property DEFAULT_SETTER |
|
21075 * @static |
|
21076 */ |
|
21077 DEFAULT_SETTER: function(anim, att, from, to, elapsed, duration, fn, unit) { |
|
21078 from = Number(from); |
|
21079 to = Number(to); |
|
21080 |
|
21081 var node = anim._node, |
|
21082 val = Transition.cubicBezier(fn, elapsed / duration); |
|
21083 |
|
21084 val = from + val[0] * (to - from); |
|
21085 |
|
21086 if (node) { |
|
21087 if (att in node.style || att in Y.DOM.CUSTOM_STYLES) { |
|
21088 unit = unit || ''; |
|
21089 Y.DOM.setStyle(node, att, val + unit); |
|
21090 } |
|
21091 } else { |
|
21092 anim._end(); |
|
21093 } |
|
21094 }, |
|
21095 |
|
21096 /* |
|
21097 * The default getter to use when getting object properties. |
|
21098 * |
|
21099 * @property DEFAULT_GETTER |
|
21100 * @static |
|
21101 */ |
|
21102 DEFAULT_GETTER: function(anim, att) { |
|
21103 var node = anim._node, |
|
21104 val = ''; |
|
21105 |
|
21106 if (att in node.style || att in Y.DOM.CUSTOM_STYLES) { |
|
21107 val = Y.DOM.getComputedStyle(node, att); |
|
21108 } |
|
21109 |
|
21110 return val; |
|
21111 }, |
|
21112 |
|
21113 _startTimer: function() { |
|
21114 if (!Transition._timer) { |
|
21115 Transition._timer = setInterval(Transition._runFrame, Transition.intervalTime); |
|
21116 } |
|
21117 }, |
|
21118 |
|
21119 _stopTimer: function() { |
|
21120 clearInterval(Transition._timer); |
|
21121 Transition._timer = null; |
|
21122 }, |
|
21123 |
|
21124 /* |
|
21125 * Called per Interval to handle each animation frame. |
|
21126 * @method _runFrame |
|
21127 * @private |
|
21128 * @static |
|
21129 */ |
|
21130 _runFrame: function() { |
|
21131 var done = true, |
|
21132 anim; |
|
21133 for (anim in Transition._running) { |
|
21134 if (Transition._running[anim]._runFrame) { |
|
21135 done = false; |
|
21136 Transition._running[anim]._runFrame(); |
|
21137 } |
|
21138 } |
|
21139 |
|
21140 if (done) { |
|
21141 Transition._stopTimer(); |
|
21142 } |
|
21143 }, |
|
21144 |
|
21145 cubicBezier: function(p, t) { |
|
21146 var x0 = 0, |
|
21147 y0 = 0, |
|
21148 x1 = p[0], |
|
21149 y1 = p[1], |
|
21150 x2 = p[2], |
|
21151 y2 = p[3], |
|
21152 x3 = 1, |
|
21153 y3 = 0, |
|
21154 |
|
21155 A = x3 - 3 * x2 + 3 * x1 - x0, |
|
21156 B = 3 * x2 - 6 * x1 + 3 * x0, |
|
21157 C = 3 * x1 - 3 * x0, |
|
21158 D = x0, |
|
21159 E = y3 - 3 * y2 + 3 * y1 - y0, |
|
21160 F = 3 * y2 - 6 * y1 + 3 * y0, |
|
21161 G = 3 * y1 - 3 * y0, |
|
21162 H = y0, |
|
21163 |
|
21164 x = (((A*t) + B)*t + C)*t + D, |
|
21165 y = (((E*t) + F)*t + G)*t + H; |
|
21166 |
|
21167 return [x, y]; |
|
21168 }, |
|
21169 |
|
21170 easings: { |
|
21171 ease: [0.25, 0, 1, 0.25], |
|
21172 linear: [0, 0, 1, 1], |
|
21173 'ease-in': [0.42, 0, 1, 1], |
|
21174 'ease-out': [0, 0, 0.58, 1], |
|
21175 'ease-in-out': [0.42, 0, 0.58, 1] |
|
21176 }, |
|
21177 |
|
21178 _running: {}, |
|
21179 _timer: null, |
|
21180 |
|
21181 RE_UNITS: /^(-?\d*\.?\d*){1}(em|ex|px|in|cm|mm|pt|pc|%)*$/ |
|
21182 }, true); |
|
21183 |
|
21184 Transition.behaviors.top = Transition.behaviors.bottom = Transition.behaviors.right = Transition.behaviors.left; |
|
21185 |
|
21186 Y.Transition = Transition; |
|
21187 |
|
21188 |
|
21189 }, '@VERSION@', {"requires": ["transition"]}); |
|
21190 YUI.add('yui', function (Y, NAME) { |
|
21191 |
|
21192 // empty |
|
21193 |
|
21194 |
|
21195 |
|
21196 }, '@VERSION@', { |
|
21197 "use": [ |
|
21198 "yui", |
|
21199 "oop", |
|
21200 "dom", |
|
21201 "event-custom-base", |
|
21202 "event-base", |
|
21203 "pluginhost", |
|
21204 "node", |
|
21205 "event-delegate", |
|
21206 "io-base", |
|
21207 "json-parse", |
|
21208 "transition", |
|
21209 "selector-css3", |
|
21210 "dom-style-ie", |
|
21211 "querystring-stringify-simple" |
|
21212 ] |
|
21213 }); |
|
21214 var Y = YUI().use('*'); |