|
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 // Y.log(Y.id + ' initialized', 'info', 'yui'); |
|
536 }, |
|
537 |
|
538 /** |
|
539 Executes the named method on the specified YUI instance if that method is |
|
540 whitelisted. |
|
541 |
|
542 @method applyTo |
|
543 @param {String} id YUI instance id. |
|
544 @param {String} method Name of the method to execute. For example: |
|
545 'Object.keys'. |
|
546 @param {Array} args Arguments to apply to the method. |
|
547 @return {Mixed} Return value from the applied method, or `null` if the |
|
548 specified instance was not found or the method was not whitelisted. |
|
549 **/ |
|
550 applyTo: function(id, method, args) { |
|
551 if (!(method in APPLY_TO_AUTH)) { |
|
552 this.log(method + ': applyTo not allowed', 'warn', 'yui'); |
|
553 return null; |
|
554 } |
|
555 |
|
556 var instance = instances[id], nest, m, i; |
|
557 if (instance) { |
|
558 nest = method.split('.'); |
|
559 m = instance; |
|
560 for (i = 0; i < nest.length; i = i + 1) { |
|
561 m = m[nest[i]]; |
|
562 if (!m) { |
|
563 this.log('applyTo not found: ' + method, 'warn', 'yui'); |
|
564 } |
|
565 } |
|
566 return m && m.apply(instance, args); |
|
567 } |
|
568 |
|
569 return null; |
|
570 }, |
|
571 |
|
572 /** |
|
573 Registers a YUI module and makes it available for use in a `YUI().use()` call or |
|
574 as a dependency for other modules. |
|
575 |
|
576 The easiest way to create a first-class YUI module is to use |
|
577 <a href="http://yui.github.com/shifter/">Shifter</a>, the YUI component build |
|
578 tool. |
|
579 |
|
580 Shifter will automatically wrap your module code in a `YUI.add()` call along |
|
581 with any configuration info required for the module. |
|
582 |
|
583 @example |
|
584 |
|
585 YUI.add('davglass', function (Y) { |
|
586 Y.davglass = function () { |
|
587 Y.log('Dav was here!'); |
|
588 }; |
|
589 }, '3.4.0', { |
|
590 requires: ['harley-davidson', 'mt-dew'] |
|
591 }); |
|
592 |
|
593 @method add |
|
594 @param {String} name Module name. |
|
595 @param {Function} fn Function containing module code. This function will be |
|
596 executed whenever the module is attached to a specific YUI instance. |
|
597 |
|
598 @param {YUI} fn.Y The YUI instance to which this module is attached. |
|
599 @param {String} fn.name Name of the module |
|
600 |
|
601 @param {String} version Module version number. This is currently used only for |
|
602 informational purposes, and is not used internally by YUI. |
|
603 |
|
604 @param {Object} [config] Module config. |
|
605 @param {Array} [config.requires] Array of other module names that must be |
|
606 attached before this module can be attached. |
|
607 @param {Array} [config.optional] Array of optional module names that should |
|
608 be attached before this module is attached if they've already been |
|
609 loaded. If the `loadOptional` YUI option is `true`, optional modules |
|
610 that have not yet been loaded will be loaded just as if they were hard |
|
611 requirements. |
|
612 @param {Array} [config.use] Array of module names that are included within |
|
613 or otherwise provided by this module, and which should be attached |
|
614 automatically when this module is attached. This makes it possible to |
|
615 create "virtual rollup" modules that simply attach a collection of other |
|
616 modules or submodules. |
|
617 |
|
618 @return {YUI} This YUI instance. |
|
619 **/ |
|
620 add: function(name, fn, version, details) { |
|
621 details = details || {}; |
|
622 var env = YUI.Env, |
|
623 mod = { |
|
624 name: name, |
|
625 fn: fn, |
|
626 version: version, |
|
627 details: details |
|
628 }, |
|
629 //Instance hash so we don't apply it to the same instance twice |
|
630 applied = {}, |
|
631 loader, inst, |
|
632 i, versions = env.versions; |
|
633 |
|
634 env.mods[name] = mod; |
|
635 versions[version] = versions[version] || {}; |
|
636 versions[version][name] = mod; |
|
637 |
|
638 for (i in instances) { |
|
639 if (instances.hasOwnProperty(i)) { |
|
640 inst = instances[i]; |
|
641 if (!applied[inst.id]) { |
|
642 applied[inst.id] = true; |
|
643 loader = inst.Env._loader; |
|
644 if (loader) { |
|
645 if (!loader.moduleInfo[name] || loader.moduleInfo[name].temp) { |
|
646 loader.addModule(details, name); |
|
647 } |
|
648 } |
|
649 } |
|
650 } |
|
651 } |
|
652 |
|
653 return this; |
|
654 }, |
|
655 |
|
656 /** |
|
657 Executes the callback function associated with each required module, |
|
658 attaching the module to this YUI instance. |
|
659 |
|
660 @method _attach |
|
661 @param {Array} r The array of modules to attach |
|
662 @param {Boolean} [moot=false] If `true`, don't throw a warning if the module |
|
663 is not attached. |
|
664 @private |
|
665 **/ |
|
666 _attach: function(r, moot) { |
|
667 var i, name, mod, details, req, use, after, |
|
668 mods = YUI.Env.mods, |
|
669 aliases = YUI.Env.aliases, |
|
670 Y = this, j, |
|
671 cache = YUI.Env._renderedMods, |
|
672 loader = Y.Env._loader, |
|
673 done = Y.Env._attached, |
|
674 len = r.length, loader, def, go, |
|
675 c = []; |
|
676 |
|
677 //Check for conditional modules (in a second+ instance) and add their requirements |
|
678 //TODO I hate this entire method, it needs to be fixed ASAP (3.5.0) ^davglass |
|
679 for (i = 0; i < len; i++) { |
|
680 name = r[i]; |
|
681 mod = mods[name]; |
|
682 c.push(name); |
|
683 if (loader && loader.conditions[name]) { |
|
684 for (j in loader.conditions[name]) { |
|
685 if (loader.conditions[name].hasOwnProperty(j)) { |
|
686 def = loader.conditions[name][j]; |
|
687 go = def && ((def.ua && Y.UA[def.ua]) || (def.test && def.test(Y))); |
|
688 if (go) { |
|
689 c.push(def.name); |
|
690 } |
|
691 } |
|
692 } |
|
693 } |
|
694 } |
|
695 r = c; |
|
696 len = r.length; |
|
697 |
|
698 for (i = 0; i < len; i++) { |
|
699 if (!done[r[i]]) { |
|
700 name = r[i]; |
|
701 mod = mods[name]; |
|
702 |
|
703 if (aliases && aliases[name] && !mod) { |
|
704 Y._attach(aliases[name]); |
|
705 continue; |
|
706 } |
|
707 if (!mod) { |
|
708 if (loader && loader.moduleInfo[name]) { |
|
709 mod = loader.moduleInfo[name]; |
|
710 moot = true; |
|
711 } |
|
712 |
|
713 // Y.log('no js def for: ' + name, 'info', 'yui'); |
|
714 |
|
715 //if (!loader || !loader.moduleInfo[name]) { |
|
716 //if ((!loader || !loader.moduleInfo[name]) && !moot) { |
|
717 if (!moot && name) { |
|
718 if ((name.indexOf('skin-') === -1) && (name.indexOf('css') === -1)) { |
|
719 Y.Env._missed.push(name); |
|
720 Y.Env._missed = Y.Array.dedupe(Y.Env._missed); |
|
721 Y.message('NOT loaded: ' + name, 'warn', 'yui'); |
|
722 } |
|
723 } |
|
724 } else { |
|
725 done[name] = true; |
|
726 //Don't like this, but in case a mod was asked for once, then we fetch it |
|
727 //We need to remove it from the missed list ^davglass |
|
728 for (j = 0; j < Y.Env._missed.length; j++) { |
|
729 if (Y.Env._missed[j] === name) { |
|
730 Y.message('Found: ' + name + ' (was reported as missing earlier)', 'warn', 'yui'); |
|
731 Y.Env._missed.splice(j, 1); |
|
732 } |
|
733 } |
|
734 /* |
|
735 If it's a temp module, we need to redo it's requirements if it's already loaded |
|
736 since it may have been loaded by another instance and it's dependencies might |
|
737 have been redefined inside the fetched file. |
|
738 */ |
|
739 if (loader && cache && cache[name] && cache[name].temp) { |
|
740 loader.getRequires(cache[name]); |
|
741 req = []; |
|
742 for (j in loader.moduleInfo[name].expanded_map) { |
|
743 if (loader.moduleInfo[name].expanded_map.hasOwnProperty(j)) { |
|
744 req.push(j); |
|
745 } |
|
746 } |
|
747 Y._attach(req); |
|
748 } |
|
749 |
|
750 details = mod.details; |
|
751 req = details.requires; |
|
752 use = details.use; |
|
753 after = details.after; |
|
754 //Force Intl load if there is a language (Loader logic) @todo fix this shit |
|
755 if (details.lang) { |
|
756 req = req || []; |
|
757 req.unshift('intl'); |
|
758 } |
|
759 |
|
760 if (req) { |
|
761 for (j = 0; j < req.length; j++) { |
|
762 if (!done[req[j]]) { |
|
763 if (!Y._attach(req)) { |
|
764 return false; |
|
765 } |
|
766 break; |
|
767 } |
|
768 } |
|
769 } |
|
770 |
|
771 if (after) { |
|
772 for (j = 0; j < after.length; j++) { |
|
773 if (!done[after[j]]) { |
|
774 if (!Y._attach(after, true)) { |
|
775 return false; |
|
776 } |
|
777 break; |
|
778 } |
|
779 } |
|
780 } |
|
781 |
|
782 if (mod.fn) { |
|
783 if (Y.config.throwFail) { |
|
784 mod.fn(Y, name); |
|
785 } else { |
|
786 try { |
|
787 mod.fn(Y, name); |
|
788 } catch (e) { |
|
789 Y.error('Attach error: ' + name, e, name); |
|
790 return false; |
|
791 } |
|
792 } |
|
793 } |
|
794 |
|
795 if (use) { |
|
796 for (j = 0; j < use.length; j++) { |
|
797 if (!done[use[j]]) { |
|
798 if (!Y._attach(use)) { |
|
799 return false; |
|
800 } |
|
801 break; |
|
802 } |
|
803 } |
|
804 } |
|
805 |
|
806 |
|
807 |
|
808 } |
|
809 } |
|
810 } |
|
811 |
|
812 return true; |
|
813 }, |
|
814 |
|
815 /** |
|
816 Delays the `use` callback until another event has taken place such as |
|
817 `window.onload`, `domready`, `contentready`, or `available`. |
|
818 |
|
819 @private |
|
820 @method _delayCallback |
|
821 @param {Function} cb The original `use` callback. |
|
822 @param {String|Object} until Either an event name ('load', 'domready', etc.) |
|
823 or an object containing event/args keys for contentready/available. |
|
824 @return {Function} |
|
825 **/ |
|
826 _delayCallback: function(cb, until) { |
|
827 |
|
828 var Y = this, |
|
829 mod = ['event-base']; |
|
830 |
|
831 until = (Y.Lang.isObject(until) ? until : { event: until }); |
|
832 |
|
833 if (until.event === 'load') { |
|
834 mod.push('event-synthetic'); |
|
835 } |
|
836 |
|
837 Y.log('Delaying use callback until: ' + until.event, 'info', 'yui'); |
|
838 return function() { |
|
839 Y.log('Use callback fired, waiting on delay', 'info', 'yui'); |
|
840 var args = arguments; |
|
841 Y._use(mod, function() { |
|
842 Y.log('Delayed use wrapper callback after dependencies', 'info', 'yui'); |
|
843 Y.on(until.event, function() { |
|
844 args[1].delayUntil = until.event; |
|
845 Y.log('Delayed use callback done after ' + until.event, 'info', 'yui'); |
|
846 cb.apply(Y, args); |
|
847 }, until.args); |
|
848 }); |
|
849 }; |
|
850 }, |
|
851 |
|
852 /** |
|
853 Attaches one or more modules to this YUI instance. When this is executed, |
|
854 the requirements of the desired modules are analyzed, and one of several |
|
855 things can happen: |
|
856 |
|
857 |
|
858 * All required modules have already been loaded, and just need to be |
|
859 attached to this YUI instance. In this case, the `use()` callback will |
|
860 be executed synchronously after the modules are attached. |
|
861 |
|
862 * One or more modules have not yet been loaded, or the Get utility is not |
|
863 available, or the `bootstrap` config option is `false`. In this case, |
|
864 a warning is issued indicating that modules are missing, but all |
|
865 available modules will still be attached and the `use()` callback will |
|
866 be executed synchronously. |
|
867 |
|
868 * One or more modules are missing and the Loader is not available but the |
|
869 Get utility is, and `bootstrap` is not `false`. In this case, the Get |
|
870 utility will be used to load the Loader, and we will then proceed to |
|
871 the following state: |
|
872 |
|
873 * One or more modules are missing and the Loader is available. In this |
|
874 case, the Loader will be used to resolve the dependency tree for the |
|
875 missing modules and load them and their dependencies. When the Loader is |
|
876 finished loading modules, the `use()` callback will be executed |
|
877 asynchronously. |
|
878 |
|
879 @example |
|
880 |
|
881 // Loads and attaches dd and its dependencies. |
|
882 YUI().use('dd', function (Y) { |
|
883 // ... |
|
884 }); |
|
885 |
|
886 // Loads and attaches dd and node as well as all of their dependencies. |
|
887 YUI().use(['dd', 'node'], function (Y) { |
|
888 // ... |
|
889 }); |
|
890 |
|
891 // Attaches all modules that have already been loaded. |
|
892 YUI().use('*', function (Y) { |
|
893 // ... |
|
894 }); |
|
895 |
|
896 // Attaches a gallery module. |
|
897 YUI().use('gallery-yql', function (Y) { |
|
898 // ... |
|
899 }); |
|
900 |
|
901 // Attaches a YUI 2in3 module. |
|
902 YUI().use('yui2-datatable', function (Y) { |
|
903 // ... |
|
904 }); |
|
905 |
|
906 @method use |
|
907 @param {String|Array} modules* One or more module names to attach. |
|
908 @param {Function} [callback] Callback function to be executed once all |
|
909 specified modules and their dependencies have been attached. |
|
910 @param {YUI} callback.Y The YUI instance created for this sandbox. |
|
911 @param {Object} callback.status Object containing `success`, `msg` and |
|
912 `data` properties. |
|
913 @chainable |
|
914 **/ |
|
915 use: function() { |
|
916 var args = SLICE.call(arguments, 0), |
|
917 callback = args[args.length - 1], |
|
918 Y = this, |
|
919 i = 0, |
|
920 name, |
|
921 Env = Y.Env, |
|
922 provisioned = true; |
|
923 |
|
924 // The last argument supplied to use can be a load complete callback |
|
925 if (Y.Lang.isFunction(callback)) { |
|
926 args.pop(); |
|
927 if (Y.config.delayUntil) { |
|
928 callback = Y._delayCallback(callback, Y.config.delayUntil); |
|
929 } |
|
930 } else { |
|
931 callback = null; |
|
932 } |
|
933 if (Y.Lang.isArray(args[0])) { |
|
934 args = args[0]; |
|
935 } |
|
936 |
|
937 if (Y.config.cacheUse) { |
|
938 while ((name = args[i++])) { |
|
939 if (!Env._attached[name]) { |
|
940 provisioned = false; |
|
941 break; |
|
942 } |
|
943 } |
|
944 |
|
945 if (provisioned) { |
|
946 if (args.length) { |
|
947 Y.log('already provisioned: ' + args, 'info', 'yui'); |
|
948 } |
|
949 Y._notify(callback, ALREADY_DONE, args); |
|
950 return Y; |
|
951 } |
|
952 } |
|
953 |
|
954 if (Y._loading) { |
|
955 Y._useQueue = Y._useQueue || new Y.Queue(); |
|
956 Y._useQueue.add([args, callback]); |
|
957 } else { |
|
958 Y._use(args, function(Y, response) { |
|
959 Y._notify(callback, response, args); |
|
960 }); |
|
961 } |
|
962 |
|
963 return Y; |
|
964 }, |
|
965 |
|
966 /** |
|
967 Handles Loader notifications about attachment/load errors. |
|
968 |
|
969 @method _notify |
|
970 @param {Function} callback Callback to pass to `Y.config.loadErrorFn`. |
|
971 @param {Object} response Response returned from Loader. |
|
972 @param {Array} args Arguments passed from Loader. |
|
973 @private |
|
974 **/ |
|
975 _notify: function(callback, response, args) { |
|
976 if (!response.success && this.config.loadErrorFn) { |
|
977 this.config.loadErrorFn.call(this, this, callback, response, args); |
|
978 } else if (callback) { |
|
979 if (this.Env._missed && this.Env._missed.length) { |
|
980 response.msg = 'Missing modules: ' + this.Env._missed.join(); |
|
981 response.success = false; |
|
982 } |
|
983 if (this.config.throwFail) { |
|
984 callback(this, response); |
|
985 } else { |
|
986 try { |
|
987 callback(this, response); |
|
988 } catch (e) { |
|
989 this.error('use callback error', e, args); |
|
990 } |
|
991 } |
|
992 } |
|
993 }, |
|
994 |
|
995 /** |
|
996 Called from the `use` method queue to ensure that only one set of loading |
|
997 logic is performed at a time. |
|
998 |
|
999 @method _use |
|
1000 @param {String} args* One or more modules to attach. |
|
1001 @param {Function} [callback] Function to call once all required modules have |
|
1002 been attached. |
|
1003 @private |
|
1004 **/ |
|
1005 _use: function(args, callback) { |
|
1006 |
|
1007 if (!this.Array) { |
|
1008 this._attach(['yui-base']); |
|
1009 } |
|
1010 |
|
1011 var len, loader, handleBoot, |
|
1012 Y = this, |
|
1013 G_ENV = YUI.Env, |
|
1014 mods = G_ENV.mods, |
|
1015 Env = Y.Env, |
|
1016 used = Env._used, |
|
1017 aliases = G_ENV.aliases, |
|
1018 queue = G_ENV._loaderQueue, |
|
1019 firstArg = args[0], |
|
1020 YArray = Y.Array, |
|
1021 config = Y.config, |
|
1022 boot = config.bootstrap, |
|
1023 missing = [], |
|
1024 i, |
|
1025 r = [], |
|
1026 ret = true, |
|
1027 fetchCSS = config.fetchCSS, |
|
1028 process = function(names, skip) { |
|
1029 |
|
1030 var i = 0, a = [], name, len, m, req, use; |
|
1031 |
|
1032 if (!names.length) { |
|
1033 return; |
|
1034 } |
|
1035 |
|
1036 if (aliases) { |
|
1037 len = names.length; |
|
1038 for (i = 0; i < len; i++) { |
|
1039 if (aliases[names[i]] && !mods[names[i]]) { |
|
1040 a = [].concat(a, aliases[names[i]]); |
|
1041 } else { |
|
1042 a.push(names[i]); |
|
1043 } |
|
1044 } |
|
1045 names = a; |
|
1046 } |
|
1047 |
|
1048 len = names.length; |
|
1049 |
|
1050 for (i = 0; i < len; i++) { |
|
1051 name = names[i]; |
|
1052 if (!skip) { |
|
1053 r.push(name); |
|
1054 } |
|
1055 |
|
1056 // only attach a module once |
|
1057 if (used[name]) { |
|
1058 continue; |
|
1059 } |
|
1060 |
|
1061 m = mods[name]; |
|
1062 req = null; |
|
1063 use = null; |
|
1064 |
|
1065 if (m) { |
|
1066 used[name] = true; |
|
1067 req = m.details.requires; |
|
1068 use = m.details.use; |
|
1069 } else { |
|
1070 // CSS files don't register themselves, see if it has |
|
1071 // been loaded |
|
1072 if (!G_ENV._loaded[VERSION][name]) { |
|
1073 missing.push(name); |
|
1074 } else { |
|
1075 used[name] = true; // probably css |
|
1076 } |
|
1077 } |
|
1078 |
|
1079 // make sure requirements are attached |
|
1080 if (req && req.length) { |
|
1081 process(req); |
|
1082 } |
|
1083 |
|
1084 // make sure we grab the submodule dependencies too |
|
1085 if (use && use.length) { |
|
1086 process(use, 1); |
|
1087 } |
|
1088 } |
|
1089 |
|
1090 }, |
|
1091 |
|
1092 handleLoader = function(fromLoader) { |
|
1093 var response = fromLoader || { |
|
1094 success: true, |
|
1095 msg: 'not dynamic' |
|
1096 }, |
|
1097 redo, origMissing, |
|
1098 ret = true, |
|
1099 data = response.data; |
|
1100 |
|
1101 Y._loading = false; |
|
1102 |
|
1103 if (data) { |
|
1104 origMissing = missing; |
|
1105 missing = []; |
|
1106 r = []; |
|
1107 process(data); |
|
1108 redo = missing.length; |
|
1109 if (redo) { |
|
1110 if ([].concat(missing).sort().join() == |
|
1111 origMissing.sort().join()) { |
|
1112 redo = false; |
|
1113 } |
|
1114 } |
|
1115 } |
|
1116 |
|
1117 if (redo && data) { |
|
1118 Y._loading = true; |
|
1119 Y._use(missing, function() { |
|
1120 Y.log('Nested use callback: ' + data, 'info', 'yui'); |
|
1121 if (Y._attach(data)) { |
|
1122 Y._notify(callback, response, data); |
|
1123 } |
|
1124 }); |
|
1125 } else { |
|
1126 if (data) { |
|
1127 // Y.log('attaching from loader: ' + data, 'info', 'yui'); |
|
1128 ret = Y._attach(data); |
|
1129 } |
|
1130 if (ret) { |
|
1131 Y._notify(callback, response, args); |
|
1132 } |
|
1133 } |
|
1134 |
|
1135 if (Y._useQueue && Y._useQueue.size() && !Y._loading) { |
|
1136 Y._use.apply(Y, Y._useQueue.next()); |
|
1137 } |
|
1138 |
|
1139 }; |
|
1140 |
|
1141 // Y.log(Y.id + ': use called: ' + a + ' :: ' + callback, 'info', 'yui'); |
|
1142 |
|
1143 // YUI().use('*'); // bind everything available |
|
1144 if (firstArg === '*') { |
|
1145 args = []; |
|
1146 for (i in mods) { |
|
1147 if (mods.hasOwnProperty(i)) { |
|
1148 args.push(i); |
|
1149 } |
|
1150 } |
|
1151 ret = Y._attach(args); |
|
1152 if (ret) { |
|
1153 handleLoader(); |
|
1154 } |
|
1155 return Y; |
|
1156 } |
|
1157 |
|
1158 if ((mods.loader || mods['loader-base']) && !Y.Loader) { |
|
1159 Y.log('Loader was found in meta, but it is not attached. Attaching..', 'info', 'yui'); |
|
1160 Y._attach(['loader' + ((!mods.loader) ? '-base' : '')]); |
|
1161 } |
|
1162 |
|
1163 // Y.log('before loader requirements: ' + args, 'info', 'yui'); |
|
1164 |
|
1165 // use loader to expand dependencies and sort the |
|
1166 // requirements if it is available. |
|
1167 if (boot && Y.Loader && args.length) { |
|
1168 Y.log('Using loader to expand dependencies', 'info', 'yui'); |
|
1169 loader = getLoader(Y); |
|
1170 loader.require(args); |
|
1171 loader.ignoreRegistered = true; |
|
1172 loader._boot = true; |
|
1173 loader.calculate(null, (fetchCSS) ? null : 'js'); |
|
1174 args = loader.sorted; |
|
1175 loader._boot = false; |
|
1176 } |
|
1177 |
|
1178 process(args); |
|
1179 |
|
1180 len = missing.length; |
|
1181 |
|
1182 |
|
1183 if (len) { |
|
1184 missing = YArray.dedupe(missing); |
|
1185 len = missing.length; |
|
1186 Y.log('Modules missing: ' + missing + ', ' + missing.length, 'info', 'yui'); |
|
1187 } |
|
1188 |
|
1189 |
|
1190 // dynamic load |
|
1191 if (boot && len && Y.Loader) { |
|
1192 // Y.log('Using loader to fetch missing deps: ' + missing, 'info', 'yui'); |
|
1193 Y.log('Using Loader', 'info', 'yui'); |
|
1194 Y._loading = true; |
|
1195 loader = getLoader(Y); |
|
1196 loader.onEnd = handleLoader; |
|
1197 loader.context = Y; |
|
1198 loader.data = args; |
|
1199 loader.ignoreRegistered = false; |
|
1200 loader.require(missing); |
|
1201 loader.insert(null, (fetchCSS) ? null : 'js'); |
|
1202 |
|
1203 } else if (boot && len && Y.Get && !Env.bootstrapped) { |
|
1204 |
|
1205 Y._loading = true; |
|
1206 |
|
1207 handleBoot = function() { |
|
1208 Y._loading = false; |
|
1209 queue.running = false; |
|
1210 Env.bootstrapped = true; |
|
1211 G_ENV._bootstrapping = false; |
|
1212 if (Y._attach(['loader'])) { |
|
1213 Y._use(args, callback); |
|
1214 } |
|
1215 }; |
|
1216 |
|
1217 if (G_ENV._bootstrapping) { |
|
1218 Y.log('Waiting for loader', 'info', 'yui'); |
|
1219 queue.add(handleBoot); |
|
1220 } else { |
|
1221 G_ENV._bootstrapping = true; |
|
1222 Y.log('Fetching loader: ' + config.base + config.loaderPath, 'info', 'yui'); |
|
1223 Y.Get.script(config.base + config.loaderPath, { |
|
1224 onEnd: handleBoot |
|
1225 }); |
|
1226 } |
|
1227 |
|
1228 } else { |
|
1229 Y.log('Attaching available dependencies: ' + args, 'info', 'yui'); |
|
1230 ret = Y._attach(args); |
|
1231 if (ret) { |
|
1232 handleLoader(); |
|
1233 } |
|
1234 } |
|
1235 |
|
1236 return Y; |
|
1237 }, |
|
1238 |
|
1239 |
|
1240 /** |
|
1241 Utility method for safely creating namespaces if they don't already exist. |
|
1242 May be called statically on the YUI global object or as a method on a YUI |
|
1243 instance. |
|
1244 |
|
1245 When called statically, a namespace will be created on the YUI global |
|
1246 object: |
|
1247 |
|
1248 // Create `YUI.your.namespace.here` as nested objects, preserving any |
|
1249 // objects that already exist instead of overwriting them. |
|
1250 YUI.namespace('your.namespace.here'); |
|
1251 |
|
1252 When called as a method on a YUI instance, a namespace will be created on |
|
1253 that instance: |
|
1254 |
|
1255 // Creates `Y.property.package`. |
|
1256 Y.namespace('property.package'); |
|
1257 |
|
1258 Dots in the input string cause `namespace` to create nested objects for each |
|
1259 token. If any part of the requested namespace already exists, the current |
|
1260 object will be left in place and will not be overwritten. This allows |
|
1261 multiple calls to `namespace` to preserve existing namespaced properties. |
|
1262 |
|
1263 If the first token in the namespace string is "YAHOO", that token is |
|
1264 discarded. This is legacy behavior for backwards compatibility with YUI 2. |
|
1265 |
|
1266 Be careful with namespace tokens. Reserved words may work in some browsers |
|
1267 and not others. For instance, the following will fail in some browsers |
|
1268 because the supported version of JavaScript reserves the word "long": |
|
1269 |
|
1270 Y.namespace('really.long.nested.namespace'); |
|
1271 |
|
1272 Note: If you pass multiple arguments to create multiple namespaces, only the |
|
1273 last one created is returned from this function. |
|
1274 |
|
1275 @method namespace |
|
1276 @param {String} namespace* One or more namespaces to create. |
|
1277 @return {Object} Reference to the last namespace object created. |
|
1278 **/ |
|
1279 namespace: function() { |
|
1280 var a = arguments, o, i = 0, j, d, arg; |
|
1281 |
|
1282 for (; i < a.length; i++) { |
|
1283 o = this; //Reset base object per argument or it will get reused from the last |
|
1284 arg = a[i]; |
|
1285 if (arg.indexOf(PERIOD) > -1) { //Skip this if no "." is present |
|
1286 d = arg.split(PERIOD); |
|
1287 for (j = (d[0] == 'YAHOO') ? 1 : 0; j < d.length; j++) { |
|
1288 o[d[j]] = o[d[j]] || {}; |
|
1289 o = o[d[j]]; |
|
1290 } |
|
1291 } else { |
|
1292 o[arg] = o[arg] || {}; |
|
1293 o = o[arg]; //Reset base object to the new object so it's returned |
|
1294 } |
|
1295 } |
|
1296 return o; |
|
1297 }, |
|
1298 |
|
1299 // this is replaced if the log module is included |
|
1300 log: NOOP, |
|
1301 message: NOOP, |
|
1302 // this is replaced if the dump module is included |
|
1303 dump: function (o) { return ''+o; }, |
|
1304 |
|
1305 /** |
|
1306 Reports an error. |
|
1307 |
|
1308 The reporting mechanism is controlled by the `throwFail` configuration |
|
1309 attribute. If `throwFail` is falsy, the message is logged. If `throwFail` is |
|
1310 truthy, a JS exception is thrown. |
|
1311 |
|
1312 If an `errorFn` is specified in the config it must return `true` to indicate |
|
1313 that the exception was handled and keep it from being thrown. |
|
1314 |
|
1315 @method error |
|
1316 @param {String} msg Error message. |
|
1317 @param {Error|String} [e] JavaScript error object or an error string. |
|
1318 @param {String} [src] Source of the error (such as the name of the module in |
|
1319 which the error occurred). |
|
1320 @chainable |
|
1321 **/ |
|
1322 error: function(msg, e, src) { |
|
1323 //TODO Add check for window.onerror here |
|
1324 |
|
1325 var Y = this, ret; |
|
1326 |
|
1327 if (Y.config.errorFn) { |
|
1328 ret = Y.config.errorFn.apply(Y, arguments); |
|
1329 } |
|
1330 |
|
1331 if (!ret) { |
|
1332 throw (e || new Error(msg)); |
|
1333 } else { |
|
1334 Y.message(msg, 'error', ''+src); // don't scrub this one |
|
1335 } |
|
1336 |
|
1337 return Y; |
|
1338 }, |
|
1339 |
|
1340 /** |
|
1341 Generates an id string that is unique among all YUI instances in this |
|
1342 execution context. |
|
1343 |
|
1344 @method guid |
|
1345 @param {String} [pre] Prefix. |
|
1346 @return {String} Unique id. |
|
1347 **/ |
|
1348 guid: function(pre) { |
|
1349 var id = this.Env._guidp + '_' + (++this.Env._uidx); |
|
1350 return (pre) ? (pre + id) : id; |
|
1351 }, |
|
1352 |
|
1353 /** |
|
1354 Returns a unique id associated with the given object and (if *readOnly* is |
|
1355 falsy) stamps the object with that id so it can be identified in the future. |
|
1356 |
|
1357 Stamping an object involves adding a `_yuid` property to it that contains |
|
1358 the object's id. One exception to this is that in Internet Explorer, DOM |
|
1359 nodes have a `uniqueID` property that contains a browser-generated unique |
|
1360 id, which will be used instead of a YUI-generated id when available. |
|
1361 |
|
1362 @method stamp |
|
1363 @param {Object} o Object to stamp. |
|
1364 @param {Boolean} readOnly If truthy and the given object has not already |
|
1365 been stamped, the object will not be modified and `null` will be |
|
1366 returned. |
|
1367 @return {String} Object's unique id, or `null` if *readOnly* was truthy and |
|
1368 the given object was not already stamped. |
|
1369 **/ |
|
1370 stamp: function(o, readOnly) { |
|
1371 var uid; |
|
1372 if (!o) { |
|
1373 return o; |
|
1374 } |
|
1375 |
|
1376 // IE generates its own unique ID for dom nodes |
|
1377 // The uniqueID property of a document node returns a new ID |
|
1378 if (o.uniqueID && o.nodeType && o.nodeType !== 9) { |
|
1379 uid = o.uniqueID; |
|
1380 } else { |
|
1381 uid = (typeof o === 'string') ? o : o._yuid; |
|
1382 } |
|
1383 |
|
1384 if (!uid) { |
|
1385 uid = this.guid(); |
|
1386 if (!readOnly) { |
|
1387 try { |
|
1388 o._yuid = uid; |
|
1389 } catch (e) { |
|
1390 uid = null; |
|
1391 } |
|
1392 } |
|
1393 } |
|
1394 return uid; |
|
1395 }, |
|
1396 |
|
1397 /** |
|
1398 Destroys this YUI instance. |
|
1399 |
|
1400 @method destroy |
|
1401 @since 3.3.0 |
|
1402 **/ |
|
1403 destroy: function() { |
|
1404 var Y = this; |
|
1405 if (Y.Event) { |
|
1406 Y.Event._unload(); |
|
1407 } |
|
1408 delete instances[Y.id]; |
|
1409 delete Y.Env; |
|
1410 delete Y.config; |
|
1411 } |
|
1412 |
|
1413 /** |
|
1414 Safe `instanceof` wrapper that works around a memory leak in IE when the |
|
1415 object being tested is `window` or `document`. |
|
1416 |
|
1417 Unless you are testing objects that may be `window` or `document`, you |
|
1418 should use the native `instanceof` operator instead of this method. |
|
1419 |
|
1420 @method instanceOf |
|
1421 @param {Object} o Object to check. |
|
1422 @param {Object} type Class to check against. |
|
1423 @since 3.3.0 |
|
1424 **/ |
|
1425 }; |
|
1426 |
|
1427 YUI.prototype = proto; |
|
1428 |
|
1429 // inheritance utilities are not available yet |
|
1430 for (prop in proto) { |
|
1431 if (proto.hasOwnProperty(prop)) { |
|
1432 YUI[prop] = proto[prop]; |
|
1433 } |
|
1434 } |
|
1435 |
|
1436 /** |
|
1437 Applies a configuration to all YUI instances in this execution context. |
|
1438 |
|
1439 The main use case for this method is in "mashups" where several third-party |
|
1440 scripts need to write to a global YUI config, but cannot share a single |
|
1441 centrally-managed config object. This way they can all call |
|
1442 `YUI.applyConfig({})` instead of overwriting the single global config. |
|
1443 |
|
1444 @example |
|
1445 |
|
1446 YUI.applyConfig({ |
|
1447 modules: { |
|
1448 davglass: { |
|
1449 fullpath: './davglass.js' |
|
1450 } |
|
1451 } |
|
1452 }); |
|
1453 |
|
1454 YUI.applyConfig({ |
|
1455 modules: { |
|
1456 foo: { |
|
1457 fullpath: './foo.js' |
|
1458 } |
|
1459 } |
|
1460 }); |
|
1461 |
|
1462 YUI().use('davglass', function (Y) { |
|
1463 // Module davglass will be available here. |
|
1464 }); |
|
1465 |
|
1466 @method applyConfig |
|
1467 @param {Object} o Configuration object to apply. |
|
1468 @static |
|
1469 @since 3.5.0 |
|
1470 **/ |
|
1471 YUI.applyConfig = function(o) { |
|
1472 if (!o) { |
|
1473 return; |
|
1474 } |
|
1475 //If there is a GlobalConfig, apply it first to set the defaults |
|
1476 if (YUI.GlobalConfig) { |
|
1477 this.prototype.applyConfig.call(this, YUI.GlobalConfig); |
|
1478 } |
|
1479 //Apply this config to it |
|
1480 this.prototype.applyConfig.call(this, o); |
|
1481 //Reset GlobalConfig to the combined config |
|
1482 YUI.GlobalConfig = this.config; |
|
1483 }; |
|
1484 |
|
1485 // set up the environment |
|
1486 YUI._init(); |
|
1487 |
|
1488 if (hasWin) { |
|
1489 // add a window load event at load time so we can capture |
|
1490 // the case where it fires before dynamic loading is |
|
1491 // complete. |
|
1492 add(window, 'load', handleLoad); |
|
1493 } else { |
|
1494 handleLoad(); |
|
1495 } |
|
1496 |
|
1497 YUI.Env.add = add; |
|
1498 YUI.Env.remove = remove; |
|
1499 |
|
1500 /*global exports*/ |
|
1501 // Support the CommonJS method for exporting our single global |
|
1502 if (typeof exports == 'object') { |
|
1503 exports.YUI = YUI; |
|
1504 /** |
|
1505 * Set a method to be called when `Get.script` is called in Node.js |
|
1506 * `Get` will open the file, then pass it's content and it's path |
|
1507 * to this method before attaching it. Commonly used for code coverage |
|
1508 * instrumentation. <strong>Calling this multiple times will only |
|
1509 * attach the last hook method</strong>. This method is only |
|
1510 * available in Node.js. |
|
1511 * @method setLoadHook |
|
1512 * @static |
|
1513 * @param {Function} fn The function to set |
|
1514 * @param {String} fn.data The content of the file |
|
1515 * @param {String} fn.path The file path of the file |
|
1516 */ |
|
1517 YUI.setLoadHook = function(fn) { |
|
1518 YUI._getLoadHook = fn; |
|
1519 }; |
|
1520 /** |
|
1521 * Load hook for `Y.Get.script` in Node.js, see `YUI.setLoadHook` |
|
1522 * @method _getLoadHook |
|
1523 * @private |
|
1524 * @param {String} data The content of the file |
|
1525 * @param {String} path The file path of the file |
|
1526 */ |
|
1527 YUI._getLoadHook = null; |
|
1528 } |
|
1529 |
|
1530 }()); |
|
1531 |
|
1532 |
|
1533 /** |
|
1534 Config object that contains all of the configuration options for |
|
1535 this `YUI` instance. |
|
1536 |
|
1537 This object is supplied by the implementer when instantiating YUI. Some |
|
1538 properties have default values if they are not supplied by the implementer. |
|
1539 |
|
1540 This object should not be updated directly because some values are cached. Use |
|
1541 `applyConfig()` to update the config object on a YUI instance that has already |
|
1542 been configured. |
|
1543 |
|
1544 @class config |
|
1545 @static |
|
1546 **/ |
|
1547 |
|
1548 /** |
|
1549 If `true` (the default), YUI will "bootstrap" the YUI Loader and module metadata |
|
1550 if they're needed to load additional dependencies and aren't already available. |
|
1551 |
|
1552 Setting this to `false` will prevent YUI from automatically loading the Loader |
|
1553 and module metadata, so you will need to manually ensure that they're available |
|
1554 or handle dependency resolution yourself. |
|
1555 |
|
1556 @property {Boolean} bootstrap |
|
1557 @default true |
|
1558 **/ |
|
1559 |
|
1560 /** |
|
1561 If `true`, `Y.log()` messages will be written to the browser's debug console |
|
1562 when available and when `useBrowserConsole` is also `true`. |
|
1563 |
|
1564 @property {Boolean} debug |
|
1565 @default true |
|
1566 **/ |
|
1567 |
|
1568 /** |
|
1569 Log messages to the browser console if `debug` is `true` and the browser has a |
|
1570 supported console. |
|
1571 |
|
1572 @property {Boolean} useBrowserConsole |
|
1573 @default true |
|
1574 **/ |
|
1575 |
|
1576 /** |
|
1577 A hash of log sources that should be logged. If specified, only messages from |
|
1578 these sources will be logged. Others will be discarded. |
|
1579 |
|
1580 @property {Object} logInclude |
|
1581 @type object |
|
1582 **/ |
|
1583 |
|
1584 /** |
|
1585 A hash of log sources that should be not be logged. If specified, all sources |
|
1586 will be logged *except* those on this list. |
|
1587 |
|
1588 @property {Object} logExclude |
|
1589 **/ |
|
1590 |
|
1591 /** |
|
1592 When the YUI seed file is dynamically loaded after the `window.onload` event has |
|
1593 fired, set this to `true` to tell YUI that it shouldn't wait for `window.onload` |
|
1594 to occur. |
|
1595 |
|
1596 This ensures that components that rely on `window.onload` and the `domready` |
|
1597 custom event will work as expected even when YUI is dynamically injected. |
|
1598 |
|
1599 @property {Boolean} injected |
|
1600 @default false |
|
1601 **/ |
|
1602 |
|
1603 /** |
|
1604 If `true`, `Y.error()` will generate or re-throw a JavaScript error. Otherwise, |
|
1605 errors are merely logged silently. |
|
1606 |
|
1607 @property {Boolean} throwFail |
|
1608 @default true |
|
1609 **/ |
|
1610 |
|
1611 /** |
|
1612 Reference to the global object for this execution context. |
|
1613 |
|
1614 In a browser, this is the current `window` object. In Node.js, this is the |
|
1615 Node.js `global` object. |
|
1616 |
|
1617 @property {Object} global |
|
1618 **/ |
|
1619 |
|
1620 /** |
|
1621 The browser window or frame that this YUI instance should operate in. |
|
1622 |
|
1623 When running in Node.js, this property is `undefined`, since there is no |
|
1624 `window` object. Use `global` to get a reference to the global object that will |
|
1625 work in both browsers and Node.js. |
|
1626 |
|
1627 @property {Window} win |
|
1628 **/ |
|
1629 |
|
1630 /** |
|
1631 The browser `document` object associated with this YUI instance's `win` object. |
|
1632 |
|
1633 When running in Node.js, this property is `undefined`, since there is no |
|
1634 `document` object. |
|
1635 |
|
1636 @property {Document} doc |
|
1637 **/ |
|
1638 |
|
1639 /** |
|
1640 A list of modules that defines the YUI core (overrides the default list). |
|
1641 |
|
1642 @property {Array} core |
|
1643 @type Array |
|
1644 @default ['get', 'features', 'intl-base', 'yui-log', 'yui-later', 'loader-base', 'loader-rollup', 'loader-yui3'] |
|
1645 **/ |
|
1646 |
|
1647 /** |
|
1648 A list of languages to use in order of preference. |
|
1649 |
|
1650 This list is matched against the list of available languages in modules that the |
|
1651 YUI instance uses to determine the best possible localization of language |
|
1652 sensitive modules. |
|
1653 |
|
1654 Languages are represented using BCP 47 language tags, such as "en-GB" for |
|
1655 English as used in the United Kingdom, or "zh-Hans-CN" for simplified Chinese as |
|
1656 used in China. The list may be provided as a comma-separated string or as an |
|
1657 array. |
|
1658 |
|
1659 @property {String|String[]} lang |
|
1660 **/ |
|
1661 |
|
1662 /** |
|
1663 Default date format. |
|
1664 |
|
1665 @property {String} dateFormat |
|
1666 @deprecated Use configuration in `DataType.Date.format()` instead. |
|
1667 **/ |
|
1668 |
|
1669 /** |
|
1670 Default locale. |
|
1671 |
|
1672 @property {String} locale |
|
1673 @deprecated Use `config.lang` instead. |
|
1674 **/ |
|
1675 |
|
1676 /** |
|
1677 Default generic polling interval in milliseconds. |
|
1678 |
|
1679 @property {Number} pollInterval |
|
1680 @default 20 |
|
1681 **/ |
|
1682 |
|
1683 /** |
|
1684 The number of dynamic `<script>` nodes to insert by default before automatically |
|
1685 removing them when loading scripts. |
|
1686 |
|
1687 This applies only to script nodes because removing the node will not make the |
|
1688 evaluated script unavailable. Dynamic CSS nodes are not auto purged, because |
|
1689 removing a linked style sheet will also remove the style definitions. |
|
1690 |
|
1691 @property {Number} purgethreshold |
|
1692 @default 20 |
|
1693 **/ |
|
1694 |
|
1695 /** |
|
1696 Delay in milliseconds to wait after a window `resize` event before firing the |
|
1697 event. If another `resize` event occurs before this delay has elapsed, the |
|
1698 delay will start over to ensure that `resize` events are throttled. |
|
1699 |
|
1700 @property {Number} windowResizeDelay |
|
1701 @default 40 |
|
1702 **/ |
|
1703 |
|
1704 /** |
|
1705 Base directory for dynamic loading. |
|
1706 |
|
1707 @property {String} base |
|
1708 **/ |
|
1709 |
|
1710 /** |
|
1711 Base URL for a dynamic combo handler. This will be used to make combo-handled |
|
1712 module requests if `combine` is set to `true. |
|
1713 |
|
1714 @property {String} comboBase |
|
1715 @default "http://yui.yahooapis.com/combo?" |
|
1716 **/ |
|
1717 |
|
1718 /** |
|
1719 Root path to prepend to each module path when creating a combo-handled request. |
|
1720 |
|
1721 This is updated for each YUI release to point to a specific version of the |
|
1722 library; for example: "3.8.0/build/". |
|
1723 |
|
1724 @property {String} root |
|
1725 **/ |
|
1726 |
|
1727 /** |
|
1728 Filter to apply to module urls. This filter will modify the default path for all |
|
1729 modules. |
|
1730 |
|
1731 The default path for the YUI library is the minified version of the files (e.g., |
|
1732 event-min.js). The filter property can be a predefined filter or a custom |
|
1733 filter. The valid predefined filters are: |
|
1734 |
|
1735 - **debug**: Loads debug versions of modules (e.g., event-debug.js). |
|
1736 - **raw**: Loads raw, non-minified versions of modules without debug logging |
|
1737 (e.g., event.js). |
|
1738 |
|
1739 You can also define a custom filter, which must be an object literal containing |
|
1740 a search regular expression and a replacement string: |
|
1741 |
|
1742 myFilter: { |
|
1743 searchExp : "-min\\.js", |
|
1744 replaceStr: "-debug.js" |
|
1745 } |
|
1746 |
|
1747 @property {Object|String} filter |
|
1748 **/ |
|
1749 |
|
1750 /** |
|
1751 Skin configuration and customizations. |
|
1752 |
|
1753 @property {Object} skin |
|
1754 @param {String} [skin.defaultSkin='sam'] Default skin name. This skin will be |
|
1755 applied automatically to skinnable components if not overridden by a |
|
1756 component-specific skin name. |
|
1757 @param {String} [skin.base='assets/skins/'] Default base path for a skin, |
|
1758 relative to Loader's `base` path. |
|
1759 @param {Object} [skin.overrides] Component-specific skin name overrides. Specify |
|
1760 a component name as the key and, as the value, a string or array of strings |
|
1761 for a skin or skins that should be loaded for that component instead of the |
|
1762 `defaultSkin`. |
|
1763 **/ |
|
1764 |
|
1765 /** |
|
1766 Hash of per-component filter specifications. If specified for a given component, |
|
1767 this overrides the global `filter` config. |
|
1768 |
|
1769 @property {Object} filters |
|
1770 **/ |
|
1771 |
|
1772 /** |
|
1773 If `true`, YUI will use a combo handler to load multiple modules in as few |
|
1774 requests as possible. |
|
1775 |
|
1776 The YUI CDN (which YUI uses by default) supports combo handling, but other |
|
1777 servers may not. If the server from which you're loading YUI does not support |
|
1778 combo handling, set this to `false`. |
|
1779 |
|
1780 Providing a value for the `base` config property will cause `combine` to default |
|
1781 to `false` instead of `true`. |
|
1782 |
|
1783 @property {Boolean} combine |
|
1784 @default true |
|
1785 */ |
|
1786 |
|
1787 /** |
|
1788 Array of module names that should never be dynamically loaded. |
|
1789 |
|
1790 @property {String[]} ignore |
|
1791 **/ |
|
1792 |
|
1793 /** |
|
1794 Array of module names that should always be loaded when required, even if |
|
1795 already present on the page. |
|
1796 |
|
1797 @property {String[]} force |
|
1798 **/ |
|
1799 |
|
1800 /** |
|
1801 DOM element or id that should be used as the insertion point for dynamically |
|
1802 added `<script>` and `<link>` nodes. |
|
1803 |
|
1804 @property {HTMLElement|String} insertBefore |
|
1805 **/ |
|
1806 |
|
1807 /** |
|
1808 Object hash containing attributes to add to dynamically added `<script>` nodes. |
|
1809 |
|
1810 @property {Object} jsAttributes |
|
1811 **/ |
|
1812 |
|
1813 /** |
|
1814 Object hash containing attributes to add to dynamically added `<link>` nodes. |
|
1815 |
|
1816 @property {Object} cssAttributes |
|
1817 **/ |
|
1818 |
|
1819 /** |
|
1820 Timeout in milliseconds before a dynamic JS or CSS request will be considered a |
|
1821 failure. If not set, no timeout will be enforced. |
|
1822 |
|
1823 @property {Number} timeout |
|
1824 **/ |
|
1825 |
|
1826 /** |
|
1827 Callback for the 'CSSComplete' event. When dynamically loading YUI components |
|
1828 with CSS, this property fires when the CSS is finished loading. |
|
1829 |
|
1830 This provides an opportunity to enhance the presentation of a loading page a |
|
1831 little bit before the entire loading process is done. |
|
1832 |
|
1833 @property {Function} onCSS |
|
1834 **/ |
|
1835 |
|
1836 /** |
|
1837 A hash of module definitions to add to the list of available YUI modules. These |
|
1838 modules can then be dynamically loaded via the `use()` method. |
|
1839 |
|
1840 This is a hash in which keys are module names and values are objects containing |
|
1841 module metadata. |
|
1842 |
|
1843 See `Loader.addModule()` for the supported module metadata fields. Also see |
|
1844 `groups`, which provides a way to configure the base and combo spec for a set of |
|
1845 modules. |
|
1846 |
|
1847 @example |
|
1848 |
|
1849 modules: { |
|
1850 mymod1: { |
|
1851 requires: ['node'], |
|
1852 fullpath: '/mymod1/mymod1.js' |
|
1853 }, |
|
1854 |
|
1855 mymod2: { |
|
1856 requires: ['mymod1'], |
|
1857 fullpath: '/mymod2/mymod2.js' |
|
1858 }, |
|
1859 |
|
1860 mymod3: '/js/mymod3.js', |
|
1861 mycssmod: '/css/mycssmod.css' |
|
1862 } |
|
1863 |
|
1864 @property {Object} modules |
|
1865 **/ |
|
1866 |
|
1867 /** |
|
1868 Aliases are dynamic groups of modules that can be used as shortcuts. |
|
1869 |
|
1870 @example |
|
1871 |
|
1872 YUI({ |
|
1873 aliases: { |
|
1874 davglass: [ 'node', 'yql', 'dd' ], |
|
1875 mine: [ 'davglass', 'autocomplete'] |
|
1876 } |
|
1877 }).use('mine', function (Y) { |
|
1878 // Node, YQL, DD & AutoComplete available here. |
|
1879 }); |
|
1880 |
|
1881 @property {Object} aliases |
|
1882 **/ |
|
1883 |
|
1884 /** |
|
1885 A hash of module group definitions. |
|
1886 |
|
1887 For each group you can specify a list of modules and the base path and |
|
1888 combo spec to use when dynamically loading the modules. |
|
1889 |
|
1890 @example |
|
1891 |
|
1892 groups: { |
|
1893 yui2: { |
|
1894 // specify whether or not this group has a combo service |
|
1895 combine: true, |
|
1896 |
|
1897 // The comboSeperator to use with this group's combo handler |
|
1898 comboSep: ';', |
|
1899 |
|
1900 // The maxURLLength for this server |
|
1901 maxURLLength: 500, |
|
1902 |
|
1903 // the base path for non-combo paths |
|
1904 base: 'http://yui.yahooapis.com/2.8.0r4/build/', |
|
1905 |
|
1906 // the path to the combo service |
|
1907 comboBase: 'http://yui.yahooapis.com/combo?', |
|
1908 |
|
1909 // a fragment to prepend to the path attribute when |
|
1910 // when building combo urls |
|
1911 root: '2.8.0r4/build/', |
|
1912 |
|
1913 // the module definitions |
|
1914 modules: { |
|
1915 yui2_yde: { |
|
1916 path: "yahoo-dom-event/yahoo-dom-event.js" |
|
1917 }, |
|
1918 yui2_anim: { |
|
1919 path: "animation/animation.js", |
|
1920 requires: ['yui2_yde'] |
|
1921 } |
|
1922 } |
|
1923 } |
|
1924 } |
|
1925 |
|
1926 @property {Object} groups |
|
1927 **/ |
|
1928 |
|
1929 /** |
|
1930 Path to the Loader JS file, relative to the `base` path. |
|
1931 |
|
1932 This is used to dynamically bootstrap the Loader when it's needed and isn't yet |
|
1933 available. |
|
1934 |
|
1935 @property {String} loaderPath |
|
1936 @default "loader/loader-min.js" |
|
1937 **/ |
|
1938 |
|
1939 /** |
|
1940 If `true`, YUI will attempt to load CSS dependencies and skins. Set this to |
|
1941 `false` to prevent YUI from loading any CSS, or set it to the string `"force"` |
|
1942 to force CSS dependencies to be loaded even if their associated JS modules are |
|
1943 already loaded. |
|
1944 |
|
1945 @property {Boolean|String} fetchCSS |
|
1946 @default true |
|
1947 **/ |
|
1948 |
|
1949 /** |
|
1950 Default gallery version used to build gallery module urls. |
|
1951 |
|
1952 @property {String} gallery |
|
1953 @since 3.1.0 |
|
1954 **/ |
|
1955 |
|
1956 /** |
|
1957 Default YUI 2 version used to build YUI 2 module urls. |
|
1958 |
|
1959 This is used for intrinsic YUI 2 support via the 2in3 project. Also see the |
|
1960 `2in3` config for pulling different revisions of the wrapped YUI 2 modules. |
|
1961 |
|
1962 @property {String} yui2 |
|
1963 @default "2.9.0" |
|
1964 @since 3.1.0 |
|
1965 **/ |
|
1966 |
|
1967 /** |
|
1968 Revision number of YUI 2in3 modules that should be used when loading YUI 2in3. |
|
1969 |
|
1970 @property {String} 2in3 |
|
1971 @default "4" |
|
1972 @since 3.1.0 |
|
1973 **/ |
|
1974 |
|
1975 /** |
|
1976 Alternate console log function that should be used in environments without a |
|
1977 supported native console. This function is executed with the YUI instance as its |
|
1978 `this` object. |
|
1979 |
|
1980 @property {Function} logFn |
|
1981 @since 3.1.0 |
|
1982 **/ |
|
1983 |
|
1984 /** |
|
1985 The minimum log level to log messages for. Log levels are defined |
|
1986 incrementally. Messages greater than or equal to the level specified will |
|
1987 be shown. All others will be discarded. The order of log levels in |
|
1988 increasing priority is: |
|
1989 |
|
1990 debug |
|
1991 info |
|
1992 warn |
|
1993 error |
|
1994 |
|
1995 @property {String} logLevel |
|
1996 @default 'debug' |
|
1997 @since 3.10.0 |
|
1998 **/ |
|
1999 |
|
2000 /** |
|
2001 Callback to execute when `Y.error()` is called. It receives the error message |
|
2002 and a JavaScript error object if one was provided. |
|
2003 |
|
2004 This function is executed with the YUI instance as its `this` object. |
|
2005 |
|
2006 Returning `true` from this function will prevent an exception from being thrown. |
|
2007 |
|
2008 @property {Function} errorFn |
|
2009 @param {String} errorFn.msg Error message |
|
2010 @param {Object} [errorFn.err] Error object (if one was provided). |
|
2011 @since 3.2.0 |
|
2012 **/ |
|
2013 |
|
2014 /** |
|
2015 A callback to execute when Loader fails to load one or more resources. |
|
2016 |
|
2017 This could be because of a script load failure. It could also be because a |
|
2018 module fails to register itself when the `requireRegistration` config is `true`. |
|
2019 |
|
2020 If this function is defined, the `use()` callback will only be called when the |
|
2021 loader succeeds. Otherwise, `use()` will always executes unless there was a |
|
2022 JavaScript error when attaching a module. |
|
2023 |
|
2024 @property {Function} loadErrorFn |
|
2025 @since 3.3.0 |
|
2026 **/ |
|
2027 |
|
2028 /** |
|
2029 If `true`, Loader will expect all loaded scripts to be first-class YUI modules |
|
2030 that register themselves with the YUI global, and will trigger a failure if a |
|
2031 loaded script does not register a YUI module. |
|
2032 |
|
2033 @property {Boolean} requireRegistration |
|
2034 @default false |
|
2035 @since 3.3.0 |
|
2036 **/ |
|
2037 |
|
2038 /** |
|
2039 Cache serviced use() requests. |
|
2040 |
|
2041 @property {Boolean} cacheUse |
|
2042 @default true |
|
2043 @since 3.3.0 |
|
2044 @deprecated No longer used. |
|
2045 **/ |
|
2046 |
|
2047 /** |
|
2048 Whether or not YUI should use native ES5 functionality when available for |
|
2049 features like `Y.Array.each()`, `Y.Object()`, etc. |
|
2050 |
|
2051 When `false`, YUI will always use its own fallback implementations instead of |
|
2052 relying on ES5 functionality, even when ES5 functionality is available. |
|
2053 |
|
2054 @property {Boolean} useNativeES5 |
|
2055 @default true |
|
2056 @since 3.5.0 |
|
2057 **/ |
|
2058 |
|
2059 /** |
|
2060 * Leverage native JSON stringify if the browser has a native |
|
2061 * implementation. In general, this is a good idea. See the Known Issues |
|
2062 * section in the JSON user guide for caveats. The default value is true |
|
2063 * for browsers with native JSON support. |
|
2064 * |
|
2065 * @property useNativeJSONStringify |
|
2066 * @type Boolean |
|
2067 * @default true |
|
2068 * @since 3.8.0 |
|
2069 */ |
|
2070 |
|
2071 /** |
|
2072 * Leverage native JSON parse if the browser has a native implementation. |
|
2073 * In general, this is a good idea. See the Known Issues section in the |
|
2074 * JSON user guide for caveats. The default value is true for browsers with |
|
2075 * native JSON support. |
|
2076 * |
|
2077 * @property useNativeJSONParse |
|
2078 * @type Boolean |
|
2079 * @default true |
|
2080 * @since 3.8.0 |
|
2081 */ |
|
2082 |
|
2083 /** |
|
2084 Delay the `use` callback until a specific event has passed (`load`, `domready`, `contentready` or `available`) |
|
2085 |
|
2086 @property {Object|String} delayUntil |
|
2087 @since 3.6.0 |
|
2088 @example |
|
2089 |
|
2090 You can use `load` or `domready` strings by default: |
|
2091 |
|
2092 YUI({ |
|
2093 delayUntil: 'domready' |
|
2094 }, function (Y) { |
|
2095 // This will not execute until 'domeready' occurs. |
|
2096 }); |
|
2097 |
|
2098 Or you can delay until a node is available (with `available` or `contentready`): |
|
2099 |
|
2100 YUI({ |
|
2101 delayUntil: { |
|
2102 event: 'available', |
|
2103 args : '#foo' |
|
2104 } |
|
2105 }, function (Y) { |
|
2106 // This will not execute until a node matching the selector "#foo" is |
|
2107 // available in the DOM. |
|
2108 }); |
|
2109 |
|
2110 **/ |
|
2111 YUI.add('yui-base', function (Y, NAME) { |
|
2112 |
|
2113 /* |
|
2114 * YUI stub |
|
2115 * @module yui |
|
2116 * @submodule yui-base |
|
2117 */ |
|
2118 /** |
|
2119 * The YUI module contains the components required for building the YUI |
|
2120 * seed file. This includes the script loading mechanism, a simple queue, |
|
2121 * and the core utilities for the library. |
|
2122 * @module yui |
|
2123 * @submodule yui-base |
|
2124 */ |
|
2125 |
|
2126 /** |
|
2127 * Provides core language utilites and extensions used throughout YUI. |
|
2128 * |
|
2129 * @class Lang |
|
2130 * @static |
|
2131 */ |
|
2132 |
|
2133 var L = Y.Lang || (Y.Lang = {}), |
|
2134 |
|
2135 STRING_PROTO = String.prototype, |
|
2136 TOSTRING = Object.prototype.toString, |
|
2137 |
|
2138 TYPES = { |
|
2139 'undefined' : 'undefined', |
|
2140 'number' : 'number', |
|
2141 'boolean' : 'boolean', |
|
2142 'string' : 'string', |
|
2143 '[object Function]': 'function', |
|
2144 '[object RegExp]' : 'regexp', |
|
2145 '[object Array]' : 'array', |
|
2146 '[object Date]' : 'date', |
|
2147 '[object Error]' : 'error' |
|
2148 }, |
|
2149 |
|
2150 SUBREGEX = /\{\s*([^|}]+?)\s*(?:\|([^}]*))?\s*\}/g, |
|
2151 TRIMREGEX = /^\s+|\s+$/g, |
|
2152 NATIVE_FN_REGEX = /\{\s*\[(?:native code|function)\]\s*\}/i; |
|
2153 |
|
2154 // -- Protected Methods -------------------------------------------------------- |
|
2155 |
|
2156 /** |
|
2157 Returns `true` if the given function appears to be implemented in native code, |
|
2158 `false` otherwise. Will always return `false` -- even in ES5-capable browsers -- |
|
2159 if the `useNativeES5` YUI config option is set to `false`. |
|
2160 |
|
2161 This isn't guaranteed to be 100% accurate and won't work for anything other than |
|
2162 functions, but it can be useful for determining whether a function like |
|
2163 `Array.prototype.forEach` is native or a JS shim provided by another library. |
|
2164 |
|
2165 There's a great article by @kangax discussing certain flaws with this technique: |
|
2166 <http://perfectionkills.com/detecting-built-in-host-methods/> |
|
2167 |
|
2168 While his points are valid, it's still possible to benefit from this function |
|
2169 as long as it's used carefully and sparingly, and in such a way that false |
|
2170 negatives have minimal consequences. It's used internally to avoid using |
|
2171 potentially broken non-native ES5 shims that have been added to the page by |
|
2172 other libraries. |
|
2173 |
|
2174 @method _isNative |
|
2175 @param {Function} fn Function to test. |
|
2176 @return {Boolean} `true` if _fn_ appears to be native, `false` otherwise. |
|
2177 @static |
|
2178 @protected |
|
2179 @since 3.5.0 |
|
2180 **/ |
|
2181 L._isNative = function (fn) { |
|
2182 return !!(Y.config.useNativeES5 && fn && NATIVE_FN_REGEX.test(fn)); |
|
2183 }; |
|
2184 |
|
2185 // -- Public Methods ----------------------------------------------------------- |
|
2186 |
|
2187 /** |
|
2188 * Determines whether or not the provided item is an array. |
|
2189 * |
|
2190 * Returns `false` for array-like collections such as the function `arguments` |
|
2191 * collection or `HTMLElement` collections. Use `Y.Array.test()` if you want to |
|
2192 * test for an array-like collection. |
|
2193 * |
|
2194 * @method isArray |
|
2195 * @param o The object to test. |
|
2196 * @return {boolean} true if o is an array. |
|
2197 * @static |
|
2198 */ |
|
2199 L.isArray = L._isNative(Array.isArray) ? Array.isArray : function (o) { |
|
2200 return L.type(o) === 'array'; |
|
2201 }; |
|
2202 |
|
2203 /** |
|
2204 * Determines whether or not the provided item is a boolean. |
|
2205 * @method isBoolean |
|
2206 * @static |
|
2207 * @param o The object to test. |
|
2208 * @return {boolean} true if o is a boolean. |
|
2209 */ |
|
2210 L.isBoolean = function(o) { |
|
2211 return typeof o === 'boolean'; |
|
2212 }; |
|
2213 |
|
2214 /** |
|
2215 * Determines whether or not the supplied item is a date instance. |
|
2216 * @method isDate |
|
2217 * @static |
|
2218 * @param o The object to test. |
|
2219 * @return {boolean} true if o is a date. |
|
2220 */ |
|
2221 L.isDate = function(o) { |
|
2222 return L.type(o) === 'date' && o.toString() !== 'Invalid Date' && !isNaN(o); |
|
2223 }; |
|
2224 |
|
2225 /** |
|
2226 * <p> |
|
2227 * Determines whether or not the provided item is a function. |
|
2228 * Note: Internet Explorer thinks certain functions are objects: |
|
2229 * </p> |
|
2230 * |
|
2231 * <pre> |
|
2232 * var obj = document.createElement("object"); |
|
2233 * Y.Lang.isFunction(obj.getAttribute) // reports false in IE |
|
2234 * |
|
2235 * var input = document.createElement("input"); // append to body |
|
2236 * Y.Lang.isFunction(input.focus) // reports false in IE |
|
2237 * </pre> |
|
2238 * |
|
2239 * <p> |
|
2240 * You will have to implement additional tests if these functions |
|
2241 * matter to you. |
|
2242 * </p> |
|
2243 * |
|
2244 * @method isFunction |
|
2245 * @static |
|
2246 * @param o The object to test. |
|
2247 * @return {boolean} true if o is a function. |
|
2248 */ |
|
2249 L.isFunction = function(o) { |
|
2250 return L.type(o) === 'function'; |
|
2251 }; |
|
2252 |
|
2253 /** |
|
2254 * Determines whether or not the provided item is null. |
|
2255 * @method isNull |
|
2256 * @static |
|
2257 * @param o The object to test. |
|
2258 * @return {boolean} true if o is null. |
|
2259 */ |
|
2260 L.isNull = function(o) { |
|
2261 return o === null; |
|
2262 }; |
|
2263 |
|
2264 /** |
|
2265 * Determines whether or not the provided item is a legal number. |
|
2266 * @method isNumber |
|
2267 * @static |
|
2268 * @param o The object to test. |
|
2269 * @return {boolean} true if o is a number. |
|
2270 */ |
|
2271 L.isNumber = function(o) { |
|
2272 return typeof o === 'number' && isFinite(o); |
|
2273 }; |
|
2274 |
|
2275 /** |
|
2276 * Determines whether or not the provided item is of type object |
|
2277 * or function. Note that arrays are also objects, so |
|
2278 * <code>Y.Lang.isObject([]) === true</code>. |
|
2279 * @method isObject |
|
2280 * @static |
|
2281 * @param o The object to test. |
|
2282 * @param failfn {boolean} fail if the input is a function. |
|
2283 * @return {boolean} true if o is an object. |
|
2284 * @see isPlainObject |
|
2285 */ |
|
2286 L.isObject = function(o, failfn) { |
|
2287 var t = typeof o; |
|
2288 return (o && (t === 'object' || |
|
2289 (!failfn && (t === 'function' || L.isFunction(o))))) || false; |
|
2290 }; |
|
2291 |
|
2292 /** |
|
2293 * Determines whether or not the provided item is a string. |
|
2294 * @method isString |
|
2295 * @static |
|
2296 * @param o The object to test. |
|
2297 * @return {boolean} true if o is a string. |
|
2298 */ |
|
2299 L.isString = function(o) { |
|
2300 return typeof o === 'string'; |
|
2301 }; |
|
2302 |
|
2303 /** |
|
2304 * Determines whether or not the provided item is undefined. |
|
2305 * @method isUndefined |
|
2306 * @static |
|
2307 * @param o The object to test. |
|
2308 * @return {boolean} true if o is undefined. |
|
2309 */ |
|
2310 L.isUndefined = function(o) { |
|
2311 return typeof o === 'undefined'; |
|
2312 }; |
|
2313 |
|
2314 /** |
|
2315 * A convenience method for detecting a legitimate non-null value. |
|
2316 * Returns false for null/undefined/NaN, true for other values, |
|
2317 * including 0/false/'' |
|
2318 * @method isValue |
|
2319 * @static |
|
2320 * @param o The item to test. |
|
2321 * @return {boolean} true if it is not null/undefined/NaN || false. |
|
2322 */ |
|
2323 L.isValue = function(o) { |
|
2324 var t = L.type(o); |
|
2325 |
|
2326 switch (t) { |
|
2327 case 'number': |
|
2328 return isFinite(o); |
|
2329 |
|
2330 case 'null': // fallthru |
|
2331 case 'undefined': |
|
2332 return false; |
|
2333 |
|
2334 default: |
|
2335 return !!t; |
|
2336 } |
|
2337 }; |
|
2338 |
|
2339 /** |
|
2340 * Returns the current time in milliseconds. |
|
2341 * |
|
2342 * @method now |
|
2343 * @return {Number} Current time in milliseconds. |
|
2344 * @static |
|
2345 * @since 3.3.0 |
|
2346 */ |
|
2347 L.now = Date.now || function () { |
|
2348 return new Date().getTime(); |
|
2349 }; |
|
2350 |
|
2351 /** |
|
2352 * Lightweight version of <code>Y.substitute</code>. Uses the same template |
|
2353 * structure as <code>Y.substitute</code>, but doesn't support recursion, |
|
2354 * auto-object coersion, or formats. |
|
2355 * @method sub |
|
2356 * @param {string} s String to be modified. |
|
2357 * @param {object} o Object containing replacement values. |
|
2358 * @return {string} the substitute result. |
|
2359 * @static |
|
2360 * @since 3.2.0 |
|
2361 */ |
|
2362 L.sub = function(s, o) { |
|
2363 return s.replace ? s.replace(SUBREGEX, function (match, key) { |
|
2364 return L.isUndefined(o[key]) ? match : o[key]; |
|
2365 }) : s; |
|
2366 }; |
|
2367 |
|
2368 /** |
|
2369 * Returns a string without any leading or trailing whitespace. If |
|
2370 * the input is not a string, the input will be returned untouched. |
|
2371 * @method trim |
|
2372 * @static |
|
2373 * @param s {string} the string to trim. |
|
2374 * @return {string} the trimmed string. |
|
2375 */ |
|
2376 L.trim = STRING_PROTO.trim ? function(s) { |
|
2377 return s && s.trim ? s.trim() : s; |
|
2378 } : function (s) { |
|
2379 try { |
|
2380 return s.replace(TRIMREGEX, ''); |
|
2381 } catch (e) { |
|
2382 return s; |
|
2383 } |
|
2384 }; |
|
2385 |
|
2386 /** |
|
2387 * Returns a string without any leading whitespace. |
|
2388 * @method trimLeft |
|
2389 * @static |
|
2390 * @param s {string} the string to trim. |
|
2391 * @return {string} the trimmed string. |
|
2392 */ |
|
2393 L.trimLeft = STRING_PROTO.trimLeft ? function (s) { |
|
2394 return s.trimLeft(); |
|
2395 } : function (s) { |
|
2396 return s.replace(/^\s+/, ''); |
|
2397 }; |
|
2398 |
|
2399 /** |
|
2400 * Returns a string without any trailing whitespace. |
|
2401 * @method trimRight |
|
2402 * @static |
|
2403 * @param s {string} the string to trim. |
|
2404 * @return {string} the trimmed string. |
|
2405 */ |
|
2406 L.trimRight = STRING_PROTO.trimRight ? function (s) { |
|
2407 return s.trimRight(); |
|
2408 } : function (s) { |
|
2409 return s.replace(/\s+$/, ''); |
|
2410 }; |
|
2411 |
|
2412 /** |
|
2413 Returns one of the following strings, representing the type of the item passed |
|
2414 in: |
|
2415 |
|
2416 * "array" |
|
2417 * "boolean" |
|
2418 * "date" |
|
2419 * "error" |
|
2420 * "function" |
|
2421 * "null" |
|
2422 * "number" |
|
2423 * "object" |
|
2424 * "regexp" |
|
2425 * "string" |
|
2426 * "undefined" |
|
2427 |
|
2428 Known issues: |
|
2429 |
|
2430 * `typeof HTMLElementCollection` returns function in Safari, but |
|
2431 `Y.Lang.type()` reports "object", which could be a good thing -- |
|
2432 but it actually caused the logic in <code>Y.Lang.isObject</code> to fail. |
|
2433 |
|
2434 @method type |
|
2435 @param o the item to test. |
|
2436 @return {string} the detected type. |
|
2437 @static |
|
2438 **/ |
|
2439 L.type = function(o) { |
|
2440 return TYPES[typeof o] || TYPES[TOSTRING.call(o)] || (o ? 'object' : 'null'); |
|
2441 }; |
|
2442 /** |
|
2443 @module yui |
|
2444 @submodule yui-base |
|
2445 */ |
|
2446 |
|
2447 var Lang = Y.Lang, |
|
2448 Native = Array.prototype, |
|
2449 |
|
2450 hasOwn = Object.prototype.hasOwnProperty; |
|
2451 |
|
2452 /** |
|
2453 Provides utility methods for working with arrays. Additional array helpers can |
|
2454 be found in the `collection` and `array-extras` modules. |
|
2455 |
|
2456 `Y.Array(thing)` returns a native array created from _thing_. Depending on |
|
2457 _thing_'s type, one of the following will happen: |
|
2458 |
|
2459 * Arrays are returned unmodified unless a non-zero _startIndex_ is |
|
2460 specified. |
|
2461 * Array-like collections (see `Array.test()`) are converted to arrays. |
|
2462 * For everything else, a new array is created with _thing_ as the sole |
|
2463 item. |
|
2464 |
|
2465 Note: elements that are also collections, such as `<form>` and `<select>` |
|
2466 elements, are not automatically converted to arrays. To force a conversion, |
|
2467 pass `true` as the value of the _force_ parameter. |
|
2468 |
|
2469 @class Array |
|
2470 @constructor |
|
2471 @param {Any} thing The thing to arrayify. |
|
2472 @param {Number} [startIndex=0] If non-zero and _thing_ is an array or array-like |
|
2473 collection, a subset of items starting at the specified index will be |
|
2474 returned. |
|
2475 @param {Boolean} [force=false] If `true`, _thing_ will be treated as an |
|
2476 array-like collection no matter what. |
|
2477 @return {Array} A native array created from _thing_, according to the rules |
|
2478 described above. |
|
2479 **/ |
|
2480 function YArray(thing, startIndex, force) { |
|
2481 var len, result; |
|
2482 |
|
2483 /*jshint expr: true*/ |
|
2484 startIndex || (startIndex = 0); |
|
2485 |
|
2486 if (force || YArray.test(thing)) { |
|
2487 // IE throws when trying to slice HTMLElement collections. |
|
2488 try { |
|
2489 return Native.slice.call(thing, startIndex); |
|
2490 } catch (ex) { |
|
2491 result = []; |
|
2492 |
|
2493 for (len = thing.length; startIndex < len; ++startIndex) { |
|
2494 result.push(thing[startIndex]); |
|
2495 } |
|
2496 |
|
2497 return result; |
|
2498 } |
|
2499 } |
|
2500 |
|
2501 return [thing]; |
|
2502 } |
|
2503 |
|
2504 Y.Array = YArray; |
|
2505 |
|
2506 /** |
|
2507 Dedupes an array of strings, returning an array that's guaranteed to contain |
|
2508 only one copy of a given string. |
|
2509 |
|
2510 This method differs from `Array.unique()` in that it's optimized for use only |
|
2511 with strings, whereas `unique` may be used with other types (but is slower). |
|
2512 Using `dedupe()` with non-string values may result in unexpected behavior. |
|
2513 |
|
2514 @method dedupe |
|
2515 @param {String[]} array Array of strings to dedupe. |
|
2516 @return {Array} Deduped copy of _array_. |
|
2517 @static |
|
2518 @since 3.4.0 |
|
2519 **/ |
|
2520 YArray.dedupe = function (array) { |
|
2521 var hash = {}, |
|
2522 results = [], |
|
2523 i, item, len; |
|
2524 |
|
2525 for (i = 0, len = array.length; i < len; ++i) { |
|
2526 item = array[i]; |
|
2527 |
|
2528 if (!hasOwn.call(hash, item)) { |
|
2529 hash[item] = 1; |
|
2530 results.push(item); |
|
2531 } |
|
2532 } |
|
2533 |
|
2534 return results; |
|
2535 }; |
|
2536 |
|
2537 /** |
|
2538 Executes the supplied function on each item in the array. This method wraps |
|
2539 the native ES5 `Array.forEach()` method if available. |
|
2540 |
|
2541 @method each |
|
2542 @param {Array} array Array to iterate. |
|
2543 @param {Function} fn Function to execute on each item in the array. The function |
|
2544 will receive the following arguments: |
|
2545 @param {Any} fn.item Current array item. |
|
2546 @param {Number} fn.index Current array index. |
|
2547 @param {Array} fn.array Array being iterated. |
|
2548 @param {Object} [thisObj] `this` object to use when calling _fn_. |
|
2549 @return {YUI} The YUI instance. |
|
2550 @static |
|
2551 **/ |
|
2552 YArray.each = YArray.forEach = Lang._isNative(Native.forEach) ? function (array, fn, thisObj) { |
|
2553 Native.forEach.call(array || [], fn, thisObj || Y); |
|
2554 return Y; |
|
2555 } : function (array, fn, thisObj) { |
|
2556 for (var i = 0, len = (array && array.length) || 0; i < len; ++i) { |
|
2557 if (i in array) { |
|
2558 fn.call(thisObj || Y, array[i], i, array); |
|
2559 } |
|
2560 } |
|
2561 |
|
2562 return Y; |
|
2563 }; |
|
2564 |
|
2565 /** |
|
2566 Alias for `each()`. |
|
2567 |
|
2568 @method forEach |
|
2569 @static |
|
2570 **/ |
|
2571 |
|
2572 /** |
|
2573 Returns an object using the first array as keys and the second as values. If |
|
2574 the second array is not provided, or if it doesn't contain the same number of |
|
2575 values as the first array, then `true` will be used in place of the missing |
|
2576 values. |
|
2577 |
|
2578 @example |
|
2579 |
|
2580 Y.Array.hash(['a', 'b', 'c'], ['foo', 'bar']); |
|
2581 // => {a: 'foo', b: 'bar', c: true} |
|
2582 |
|
2583 @method hash |
|
2584 @param {String[]} keys Array of strings to use as keys. |
|
2585 @param {Array} [values] Array to use as values. |
|
2586 @return {Object} Hash using the first array as keys and the second as values. |
|
2587 @static |
|
2588 **/ |
|
2589 YArray.hash = function (keys, values) { |
|
2590 var hash = {}, |
|
2591 vlen = (values && values.length) || 0, |
|
2592 i, len; |
|
2593 |
|
2594 for (i = 0, len = keys.length; i < len; ++i) { |
|
2595 if (i in keys) { |
|
2596 hash[keys[i]] = vlen > i && i in values ? values[i] : true; |
|
2597 } |
|
2598 } |
|
2599 |
|
2600 return hash; |
|
2601 }; |
|
2602 |
|
2603 /** |
|
2604 Returns the index of the first item in the array that's equal (using a strict |
|
2605 equality check) to the specified _value_, or `-1` if the value isn't found. |
|
2606 |
|
2607 This method wraps the native ES5 `Array.indexOf()` method if available. |
|
2608 |
|
2609 @method indexOf |
|
2610 @param {Array} array Array to search. |
|
2611 @param {Any} value Value to search for. |
|
2612 @param {Number} [from=0] The index at which to begin the search. |
|
2613 @return {Number} Index of the item strictly equal to _value_, or `-1` if not |
|
2614 found. |
|
2615 @static |
|
2616 **/ |
|
2617 YArray.indexOf = Lang._isNative(Native.indexOf) ? function (array, value, from) { |
|
2618 return Native.indexOf.call(array, value, from); |
|
2619 } : function (array, value, from) { |
|
2620 // http://es5.github.com/#x15.4.4.14 |
|
2621 var len = array.length; |
|
2622 |
|
2623 from = +from || 0; |
|
2624 from = (from > 0 || -1) * Math.floor(Math.abs(from)); |
|
2625 |
|
2626 if (from < 0) { |
|
2627 from += len; |
|
2628 |
|
2629 if (from < 0) { |
|
2630 from = 0; |
|
2631 } |
|
2632 } |
|
2633 |
|
2634 for (; from < len; ++from) { |
|
2635 if (from in array && array[from] === value) { |
|
2636 return from; |
|
2637 } |
|
2638 } |
|
2639 |
|
2640 return -1; |
|
2641 }; |
|
2642 |
|
2643 /** |
|
2644 Numeric sort convenience function. |
|
2645 |
|
2646 The native `Array.prototype.sort()` function converts values to strings and |
|
2647 sorts them in lexicographic order, which is unsuitable for sorting numeric |
|
2648 values. Provide `Array.numericSort` as a custom sort function when you want |
|
2649 to sort values in numeric order. |
|
2650 |
|
2651 @example |
|
2652 |
|
2653 [42, 23, 8, 16, 4, 15].sort(Y.Array.numericSort); |
|
2654 // => [4, 8, 15, 16, 23, 42] |
|
2655 |
|
2656 @method numericSort |
|
2657 @param {Number} a First value to compare. |
|
2658 @param {Number} b Second value to compare. |
|
2659 @return {Number} Difference between _a_ and _b_. |
|
2660 @static |
|
2661 **/ |
|
2662 YArray.numericSort = function (a, b) { |
|
2663 return a - b; |
|
2664 }; |
|
2665 |
|
2666 /** |
|
2667 Executes the supplied function on each item in the array. Returning a truthy |
|
2668 value from the function will stop the processing of remaining items. |
|
2669 |
|
2670 @method some |
|
2671 @param {Array} array Array to iterate over. |
|
2672 @param {Function} fn Function to execute on each item. The function will receive |
|
2673 the following arguments: |
|
2674 @param {Any} fn.value Current array item. |
|
2675 @param {Number} fn.index Current array index. |
|
2676 @param {Array} fn.array Array being iterated over. |
|
2677 @param {Object} [thisObj] `this` object to use when calling _fn_. |
|
2678 @return {Boolean} `true` if the function returns a truthy value on any of the |
|
2679 items in the array; `false` otherwise. |
|
2680 @static |
|
2681 **/ |
|
2682 YArray.some = Lang._isNative(Native.some) ? function (array, fn, thisObj) { |
|
2683 return Native.some.call(array, fn, thisObj); |
|
2684 } : function (array, fn, thisObj) { |
|
2685 for (var i = 0, len = array.length; i < len; ++i) { |
|
2686 if (i in array && fn.call(thisObj, array[i], i, array)) { |
|
2687 return true; |
|
2688 } |
|
2689 } |
|
2690 |
|
2691 return false; |
|
2692 }; |
|
2693 |
|
2694 /** |
|
2695 Evaluates _obj_ to determine if it's an array, an array-like collection, or |
|
2696 something else. This is useful when working with the function `arguments` |
|
2697 collection and `HTMLElement` collections. |
|
2698 |
|
2699 Note: This implementation doesn't consider elements that are also |
|
2700 collections, such as `<form>` and `<select>`, to be array-like. |
|
2701 |
|
2702 @method test |
|
2703 @param {Object} obj Object to test. |
|
2704 @return {Number} A number indicating the results of the test: |
|
2705 |
|
2706 * 0: Neither an array nor an array-like collection. |
|
2707 * 1: Real array. |
|
2708 * 2: Array-like collection. |
|
2709 |
|
2710 @static |
|
2711 **/ |
|
2712 YArray.test = function (obj) { |
|
2713 var result = 0; |
|
2714 |
|
2715 if (Lang.isArray(obj)) { |
|
2716 result = 1; |
|
2717 } else if (Lang.isObject(obj)) { |
|
2718 try { |
|
2719 // indexed, but no tagName (element) or scrollTo/document (window. From DOM.isWindow test which we can't use here), |
|
2720 // or functions without apply/call (Safari |
|
2721 // HTMLElementCollection bug). |
|
2722 if ('length' in obj && !obj.tagName && !(obj.scrollTo && obj.document) && !obj.apply) { |
|
2723 result = 2; |
|
2724 } |
|
2725 } catch (ex) {} |
|
2726 } |
|
2727 |
|
2728 return result; |
|
2729 }; |
|
2730 /** |
|
2731 * The YUI module contains the components required for building the YUI |
|
2732 * seed file. This includes the script loading mechanism, a simple queue, |
|
2733 * and the core utilities for the library. |
|
2734 * @module yui |
|
2735 * @submodule yui-base |
|
2736 */ |
|
2737 |
|
2738 /** |
|
2739 * A simple FIFO queue. Items are added to the Queue with add(1..n items) and |
|
2740 * removed using next(). |
|
2741 * |
|
2742 * @class Queue |
|
2743 * @constructor |
|
2744 * @param {MIXED} item* 0..n items to seed the queue. |
|
2745 */ |
|
2746 function Queue() { |
|
2747 this._init(); |
|
2748 this.add.apply(this, arguments); |
|
2749 } |
|
2750 |
|
2751 Queue.prototype = { |
|
2752 /** |
|
2753 * Initialize the queue |
|
2754 * |
|
2755 * @method _init |
|
2756 * @protected |
|
2757 */ |
|
2758 _init: function() { |
|
2759 /** |
|
2760 * The collection of enqueued items |
|
2761 * |
|
2762 * @property _q |
|
2763 * @type Array |
|
2764 * @protected |
|
2765 */ |
|
2766 this._q = []; |
|
2767 }, |
|
2768 |
|
2769 /** |
|
2770 * Get the next item in the queue. FIFO support |
|
2771 * |
|
2772 * @method next |
|
2773 * @return {MIXED} the next item in the queue. |
|
2774 */ |
|
2775 next: function() { |
|
2776 return this._q.shift(); |
|
2777 }, |
|
2778 |
|
2779 /** |
|
2780 * Get the last in the queue. LIFO support. |
|
2781 * |
|
2782 * @method last |
|
2783 * @return {MIXED} the last item in the queue. |
|
2784 */ |
|
2785 last: function() { |
|
2786 return this._q.pop(); |
|
2787 }, |
|
2788 |
|
2789 /** |
|
2790 * Add 0..n items to the end of the queue. |
|
2791 * |
|
2792 * @method add |
|
2793 * @param {MIXED} item* 0..n items. |
|
2794 * @return {object} this queue. |
|
2795 */ |
|
2796 add: function() { |
|
2797 this._q.push.apply(this._q, arguments); |
|
2798 |
|
2799 return this; |
|
2800 }, |
|
2801 |
|
2802 /** |
|
2803 * Returns the current number of queued items. |
|
2804 * |
|
2805 * @method size |
|
2806 * @return {Number} The size. |
|
2807 */ |
|
2808 size: function() { |
|
2809 return this._q.length; |
|
2810 } |
|
2811 }; |
|
2812 |
|
2813 Y.Queue = Queue; |
|
2814 |
|
2815 YUI.Env._loaderQueue = YUI.Env._loaderQueue || new Queue(); |
|
2816 |
|
2817 /** |
|
2818 The YUI module contains the components required for building the YUI seed file. |
|
2819 This includes the script loading mechanism, a simple queue, and the core |
|
2820 utilities for the library. |
|
2821 |
|
2822 @module yui |
|
2823 @submodule yui-base |
|
2824 **/ |
|
2825 |
|
2826 var CACHED_DELIMITER = '__', |
|
2827 |
|
2828 hasOwn = Object.prototype.hasOwnProperty, |
|
2829 isObject = Y.Lang.isObject; |
|
2830 |
|
2831 /** |
|
2832 Returns a wrapper for a function which caches the return value of that function, |
|
2833 keyed off of the combined string representation of the argument values provided |
|
2834 when the wrapper is called. |
|
2835 |
|
2836 Calling this function again with the same arguments will return the cached value |
|
2837 rather than executing the wrapped function. |
|
2838 |
|
2839 Note that since the cache is keyed off of the string representation of arguments |
|
2840 passed to the wrapper function, arguments that aren't strings and don't provide |
|
2841 a meaningful `toString()` method may result in unexpected caching behavior. For |
|
2842 example, the objects `{}` and `{foo: 'bar'}` would both be converted to the |
|
2843 string `[object Object]` when used as a cache key. |
|
2844 |
|
2845 @method cached |
|
2846 @param {Function} source The function to memoize. |
|
2847 @param {Object} [cache={}] Object in which to store cached values. You may seed |
|
2848 this object with pre-existing cached values if desired. |
|
2849 @param {any} [refetch] If supplied, this value is compared with the cached value |
|
2850 using a `==` comparison. If the values are equal, the wrapped function is |
|
2851 executed again even though a cached value exists. |
|
2852 @return {Function} Wrapped function. |
|
2853 @for YUI |
|
2854 **/ |
|
2855 Y.cached = function (source, cache, refetch) { |
|
2856 /*jshint expr: true*/ |
|
2857 cache || (cache = {}); |
|
2858 |
|
2859 return function (arg) { |
|
2860 var key = arguments.length > 1 ? |
|
2861 Array.prototype.join.call(arguments, CACHED_DELIMITER) : |
|
2862 String(arg); |
|
2863 |
|
2864 /*jshint eqeqeq: false*/ |
|
2865 if (!(key in cache) || (refetch && cache[key] == refetch)) { |
|
2866 cache[key] = source.apply(source, arguments); |
|
2867 } |
|
2868 |
|
2869 return cache[key]; |
|
2870 }; |
|
2871 }; |
|
2872 |
|
2873 /** |
|
2874 Returns the `location` object from the window/frame in which this YUI instance |
|
2875 operates, or `undefined` when executing in a non-browser environment |
|
2876 (e.g. Node.js). |
|
2877 |
|
2878 It is _not_ recommended to hold references to the `window.location` object |
|
2879 outside of the scope of a function in which its properties are being accessed or |
|
2880 its methods are being called. This is because of a nasty bug/issue that exists |
|
2881 in both Safari and MobileSafari browsers: |
|
2882 [WebKit Bug 34679](https://bugs.webkit.org/show_bug.cgi?id=34679). |
|
2883 |
|
2884 @method getLocation |
|
2885 @return {location} The `location` object from the window/frame in which this YUI |
|
2886 instance operates. |
|
2887 @since 3.5.0 |
|
2888 **/ |
|
2889 Y.getLocation = function () { |
|
2890 // It is safer to look this up every time because yui-base is attached to a |
|
2891 // YUI instance before a user's config is applied; i.e. `Y.config.win` does |
|
2892 // not point the correct window object when this file is loaded. |
|
2893 var win = Y.config.win; |
|
2894 |
|
2895 // It is not safe to hold a reference to the `location` object outside the |
|
2896 // scope in which it is being used. The WebKit engine used in Safari and |
|
2897 // MobileSafari will "disconnect" the `location` object from the `window` |
|
2898 // when a page is restored from back/forward history cache. |
|
2899 return win && win.location; |
|
2900 }; |
|
2901 |
|
2902 /** |
|
2903 Returns a new object containing all of the properties of all the supplied |
|
2904 objects. The properties from later objects will overwrite those in earlier |
|
2905 objects. |
|
2906 |
|
2907 Passing in a single object will create a shallow copy of it. For a deep copy, |
|
2908 use `clone()`. |
|
2909 |
|
2910 @method merge |
|
2911 @param {Object} objects* One or more objects to merge. |
|
2912 @return {Object} A new merged object. |
|
2913 **/ |
|
2914 Y.merge = function () { |
|
2915 var i = 0, |
|
2916 len = arguments.length, |
|
2917 result = {}, |
|
2918 key, |
|
2919 obj; |
|
2920 |
|
2921 for (; i < len; ++i) { |
|
2922 obj = arguments[i]; |
|
2923 |
|
2924 for (key in obj) { |
|
2925 if (hasOwn.call(obj, key)) { |
|
2926 result[key] = obj[key]; |
|
2927 } |
|
2928 } |
|
2929 } |
|
2930 |
|
2931 return result; |
|
2932 }; |
|
2933 |
|
2934 /** |
|
2935 Mixes _supplier_'s properties into _receiver_. |
|
2936 |
|
2937 Properties on _receiver_ or _receiver_'s prototype will not be overwritten or |
|
2938 shadowed unless the _overwrite_ parameter is `true`, and will not be merged |
|
2939 unless the _merge_ parameter is `true`. |
|
2940 |
|
2941 In the default mode (0), only properties the supplier owns are copied (prototype |
|
2942 properties are not copied). The following copying modes are available: |
|
2943 |
|
2944 * `0`: _Default_. Object to object. |
|
2945 * `1`: Prototype to prototype. |
|
2946 * `2`: Prototype to prototype and object to object. |
|
2947 * `3`: Prototype to object. |
|
2948 * `4`: Object to prototype. |
|
2949 |
|
2950 @method mix |
|
2951 @param {Function|Object} receiver The object or function to receive the mixed |
|
2952 properties. |
|
2953 @param {Function|Object} supplier The object or function supplying the |
|
2954 properties to be mixed. |
|
2955 @param {Boolean} [overwrite=false] If `true`, properties that already exist |
|
2956 on the receiver will be overwritten with properties from the supplier. |
|
2957 @param {String[]} [whitelist] An array of property names to copy. If |
|
2958 specified, only the whitelisted properties will be copied, and all others |
|
2959 will be ignored. |
|
2960 @param {Number} [mode=0] Mix mode to use. See above for available modes. |
|
2961 @param {Boolean} [merge=false] If `true`, objects and arrays that already |
|
2962 exist on the receiver will have the corresponding object/array from the |
|
2963 supplier merged into them, rather than being skipped or overwritten. When |
|
2964 both _overwrite_ and _merge_ are `true`, _merge_ takes precedence. |
|
2965 @return {Function|Object|YUI} The receiver, or the YUI instance if the |
|
2966 specified receiver is falsy. |
|
2967 **/ |
|
2968 Y.mix = function(receiver, supplier, overwrite, whitelist, mode, merge) { |
|
2969 var alwaysOverwrite, exists, from, i, key, len, to; |
|
2970 |
|
2971 // If no supplier is given, we return the receiver. If no receiver is given, |
|
2972 // we return Y. Returning Y doesn't make much sense to me, but it's |
|
2973 // grandfathered in for backcompat reasons. |
|
2974 if (!receiver || !supplier) { |
|
2975 return receiver || Y; |
|
2976 } |
|
2977 |
|
2978 if (mode) { |
|
2979 // In mode 2 (prototype to prototype and object to object), we recurse |
|
2980 // once to do the proto to proto mix. The object to object mix will be |
|
2981 // handled later on. |
|
2982 if (mode === 2) { |
|
2983 Y.mix(receiver.prototype, supplier.prototype, overwrite, |
|
2984 whitelist, 0, merge); |
|
2985 } |
|
2986 |
|
2987 // Depending on which mode is specified, we may be copying from or to |
|
2988 // the prototypes of the supplier and receiver. |
|
2989 from = mode === 1 || mode === 3 ? supplier.prototype : supplier; |
|
2990 to = mode === 1 || mode === 4 ? receiver.prototype : receiver; |
|
2991 |
|
2992 // If either the supplier or receiver doesn't actually have a |
|
2993 // prototype property, then we could end up with an undefined `from` |
|
2994 // or `to`. If that happens, we abort and return the receiver. |
|
2995 if (!from || !to) { |
|
2996 return receiver; |
|
2997 } |
|
2998 } else { |
|
2999 from = supplier; |
|
3000 to = receiver; |
|
3001 } |
|
3002 |
|
3003 // If `overwrite` is truthy and `merge` is falsy, then we can skip a |
|
3004 // property existence check on each iteration and save some time. |
|
3005 alwaysOverwrite = overwrite && !merge; |
|
3006 |
|
3007 if (whitelist) { |
|
3008 for (i = 0, len = whitelist.length; i < len; ++i) { |
|
3009 key = whitelist[i]; |
|
3010 |
|
3011 // We call `Object.prototype.hasOwnProperty` instead of calling |
|
3012 // `hasOwnProperty` on the object itself, since the object's |
|
3013 // `hasOwnProperty` method may have been overridden or removed. |
|
3014 // Also, some native objects don't implement a `hasOwnProperty` |
|
3015 // method. |
|
3016 if (!hasOwn.call(from, key)) { |
|
3017 continue; |
|
3018 } |
|
3019 |
|
3020 // The `key in to` check here is (sadly) intentional for backwards |
|
3021 // compatibility reasons. It prevents undesired shadowing of |
|
3022 // prototype members on `to`. |
|
3023 exists = alwaysOverwrite ? false : key in to; |
|
3024 |
|
3025 if (merge && exists && isObject(to[key], true) |
|
3026 && isObject(from[key], true)) { |
|
3027 // If we're in merge mode, and the key is present on both |
|
3028 // objects, and the value on both objects is either an object or |
|
3029 // an array (but not a function), then we recurse to merge the |
|
3030 // `from` value into the `to` value instead of overwriting it. |
|
3031 // |
|
3032 // Note: It's intentional that the whitelist isn't passed to the |
|
3033 // recursive call here. This is legacy behavior that lots of |
|
3034 // code still depends on. |
|
3035 Y.mix(to[key], from[key], overwrite, null, 0, merge); |
|
3036 } else if (overwrite || !exists) { |
|
3037 // We're not in merge mode, so we'll only copy the `from` value |
|
3038 // to the `to` value if we're in overwrite mode or if the |
|
3039 // current key doesn't exist on the `to` object. |
|
3040 to[key] = from[key]; |
|
3041 } |
|
3042 } |
|
3043 } else { |
|
3044 for (key in from) { |
|
3045 // The code duplication here is for runtime performance reasons. |
|
3046 // Combining whitelist and non-whitelist operations into a single |
|
3047 // loop or breaking the shared logic out into a function both result |
|
3048 // in worse performance, and Y.mix is critical enough that the byte |
|
3049 // tradeoff is worth it. |
|
3050 if (!hasOwn.call(from, key)) { |
|
3051 continue; |
|
3052 } |
|
3053 |
|
3054 // The `key in to` check here is (sadly) intentional for backwards |
|
3055 // compatibility reasons. It prevents undesired shadowing of |
|
3056 // prototype members on `to`. |
|
3057 exists = alwaysOverwrite ? false : key in to; |
|
3058 |
|
3059 if (merge && exists && isObject(to[key], true) |
|
3060 && isObject(from[key], true)) { |
|
3061 Y.mix(to[key], from[key], overwrite, null, 0, merge); |
|
3062 } else if (overwrite || !exists) { |
|
3063 to[key] = from[key]; |
|
3064 } |
|
3065 } |
|
3066 |
|
3067 // If this is an IE browser with the JScript enumeration bug, force |
|
3068 // enumeration of the buggy properties by making a recursive call with |
|
3069 // the buggy properties as the whitelist. |
|
3070 if (Y.Object._hasEnumBug) { |
|
3071 Y.mix(to, from, overwrite, Y.Object._forceEnum, mode, merge); |
|
3072 } |
|
3073 } |
|
3074 |
|
3075 return receiver; |
|
3076 }; |
|
3077 /** |
|
3078 * The YUI module contains the components required for building the YUI |
|
3079 * seed file. This includes the script loading mechanism, a simple queue, |
|
3080 * and the core utilities for the library. |
|
3081 * @module yui |
|
3082 * @submodule yui-base |
|
3083 */ |
|
3084 |
|
3085 /** |
|
3086 * Adds utilities to the YUI instance for working with objects. |
|
3087 * |
|
3088 * @class Object |
|
3089 */ |
|
3090 |
|
3091 var Lang = Y.Lang, |
|
3092 hasOwn = Object.prototype.hasOwnProperty, |
|
3093 |
|
3094 UNDEFINED, // <-- Note the comma. We're still declaring vars. |
|
3095 |
|
3096 /** |
|
3097 * Returns a new object that uses _obj_ as its prototype. This method wraps the |
|
3098 * native ES5 `Object.create()` method if available, but doesn't currently |
|
3099 * pass through `Object.create()`'s second argument (properties) in order to |
|
3100 * ensure compatibility with older browsers. |
|
3101 * |
|
3102 * @method () |
|
3103 * @param {Object} obj Prototype object. |
|
3104 * @return {Object} New object using _obj_ as its prototype. |
|
3105 * @static |
|
3106 */ |
|
3107 O = Y.Object = Lang._isNative(Object.create) ? function (obj) { |
|
3108 // We currently wrap the native Object.create instead of simply aliasing it |
|
3109 // to ensure consistency with our fallback shim, which currently doesn't |
|
3110 // support Object.create()'s second argument (properties). Once we have a |
|
3111 // safe fallback for the properties arg, we can stop wrapping |
|
3112 // Object.create(). |
|
3113 return Object.create(obj); |
|
3114 } : (function () { |
|
3115 // Reusable constructor function for the Object.create() shim. |
|
3116 function F() {} |
|
3117 |
|
3118 // The actual shim. |
|
3119 return function (obj) { |
|
3120 F.prototype = obj; |
|
3121 return new F(); |
|
3122 }; |
|
3123 }()), |
|
3124 |
|
3125 /** |
|
3126 * Property names that IE doesn't enumerate in for..in loops, even when they |
|
3127 * should be enumerable. When `_hasEnumBug` is `true`, it's necessary to |
|
3128 * manually enumerate these properties. |
|
3129 * |
|
3130 * @property _forceEnum |
|
3131 * @type String[] |
|
3132 * @protected |
|
3133 * @static |
|
3134 */ |
|
3135 forceEnum = O._forceEnum = [ |
|
3136 'hasOwnProperty', |
|
3137 'isPrototypeOf', |
|
3138 'propertyIsEnumerable', |
|
3139 'toString', |
|
3140 'toLocaleString', |
|
3141 'valueOf' |
|
3142 ], |
|
3143 |
|
3144 /** |
|
3145 * `true` if this browser has the JScript enumeration bug that prevents |
|
3146 * enumeration of the properties named in the `_forceEnum` array, `false` |
|
3147 * otherwise. |
|
3148 * |
|
3149 * See: |
|
3150 * - <https://developer.mozilla.org/en/ECMAScript_DontEnum_attribute#JScript_DontEnum_Bug> |
|
3151 * - <http://whattheheadsaid.com/2010/10/a-safer-object-keys-compatibility-implementation> |
|
3152 * |
|
3153 * @property _hasEnumBug |
|
3154 * @type Boolean |
|
3155 * @protected |
|
3156 * @static |
|
3157 */ |
|
3158 hasEnumBug = O._hasEnumBug = !{valueOf: 0}.propertyIsEnumerable('valueOf'), |
|
3159 |
|
3160 /** |
|
3161 * `true` if this browser incorrectly considers the `prototype` property of |
|
3162 * functions to be enumerable. Currently known to affect Opera 11.50. |
|
3163 * |
|
3164 * @property _hasProtoEnumBug |
|
3165 * @type Boolean |
|
3166 * @protected |
|
3167 * @static |
|
3168 */ |
|
3169 hasProtoEnumBug = O._hasProtoEnumBug = (function () {}).propertyIsEnumerable('prototype'), |
|
3170 |
|
3171 /** |
|
3172 * Returns `true` if _key_ exists on _obj_, `false` if _key_ doesn't exist or |
|
3173 * exists only on _obj_'s prototype. This is essentially a safer version of |
|
3174 * `obj.hasOwnProperty()`. |
|
3175 * |
|
3176 * @method owns |
|
3177 * @param {Object} obj Object to test. |
|
3178 * @param {String} key Property name to look for. |
|
3179 * @return {Boolean} `true` if _key_ exists on _obj_, `false` otherwise. |
|
3180 * @static |
|
3181 */ |
|
3182 owns = O.owns = function (obj, key) { |
|
3183 return !!obj && hasOwn.call(obj, key); |
|
3184 }; // <-- End of var declarations. |
|
3185 |
|
3186 /** |
|
3187 * Alias for `owns()`. |
|
3188 * |
|
3189 * @method hasKey |
|
3190 * @param {Object} obj Object to test. |
|
3191 * @param {String} key Property name to look for. |
|
3192 * @return {Boolean} `true` if _key_ exists on _obj_, `false` otherwise. |
|
3193 * @static |
|
3194 */ |
|
3195 O.hasKey = owns; |
|
3196 |
|
3197 /** |
|
3198 * Returns an array containing the object's enumerable keys. Does not include |
|
3199 * prototype keys or non-enumerable keys. |
|
3200 * |
|
3201 * Note that keys are returned in enumeration order (that is, in the same order |
|
3202 * that they would be enumerated by a `for-in` loop), which may not be the same |
|
3203 * as the order in which they were defined. |
|
3204 * |
|
3205 * This method is an alias for the native ES5 `Object.keys()` method if |
|
3206 * available. |
|
3207 * |
|
3208 * @example |
|
3209 * |
|
3210 * Y.Object.keys({a: 'foo', b: 'bar', c: 'baz'}); |
|
3211 * // => ['a', 'b', 'c'] |
|
3212 * |
|
3213 * @method keys |
|
3214 * @param {Object} obj An object. |
|
3215 * @return {String[]} Array of keys. |
|
3216 * @static |
|
3217 */ |
|
3218 O.keys = Lang._isNative(Object.keys) ? Object.keys : function (obj) { |
|
3219 if (!Lang.isObject(obj)) { |
|
3220 throw new TypeError('Object.keys called on a non-object'); |
|
3221 } |
|
3222 |
|
3223 var keys = [], |
|
3224 i, key, len; |
|
3225 |
|
3226 if (hasProtoEnumBug && typeof obj === 'function') { |
|
3227 for (key in obj) { |
|
3228 if (owns(obj, key) && key !== 'prototype') { |
|
3229 keys.push(key); |
|
3230 } |
|
3231 } |
|
3232 } else { |
|
3233 for (key in obj) { |
|
3234 if (owns(obj, key)) { |
|
3235 keys.push(key); |
|
3236 } |
|
3237 } |
|
3238 } |
|
3239 |
|
3240 if (hasEnumBug) { |
|
3241 for (i = 0, len = forceEnum.length; i < len; ++i) { |
|
3242 key = forceEnum[i]; |
|
3243 |
|
3244 if (owns(obj, key)) { |
|
3245 keys.push(key); |
|
3246 } |
|
3247 } |
|
3248 } |
|
3249 |
|
3250 return keys; |
|
3251 }; |
|
3252 |
|
3253 /** |
|
3254 * Returns an array containing the values of the object's enumerable keys. |
|
3255 * |
|
3256 * Note that values are returned in enumeration order (that is, in the same |
|
3257 * order that they would be enumerated by a `for-in` loop), which may not be the |
|
3258 * same as the order in which they were defined. |
|
3259 * |
|
3260 * @example |
|
3261 * |
|
3262 * Y.Object.values({a: 'foo', b: 'bar', c: 'baz'}); |
|
3263 * // => ['foo', 'bar', 'baz'] |
|
3264 * |
|
3265 * @method values |
|
3266 * @param {Object} obj An object. |
|
3267 * @return {Array} Array of values. |
|
3268 * @static |
|
3269 */ |
|
3270 O.values = function (obj) { |
|
3271 var keys = O.keys(obj), |
|
3272 i = 0, |
|
3273 len = keys.length, |
|
3274 values = []; |
|
3275 |
|
3276 for (; i < len; ++i) { |
|
3277 values.push(obj[keys[i]]); |
|
3278 } |
|
3279 |
|
3280 return values; |
|
3281 }; |
|
3282 |
|
3283 /** |
|
3284 * Returns the number of enumerable keys owned by an object. |
|
3285 * |
|
3286 * @method size |
|
3287 * @param {Object} obj An object. |
|
3288 * @return {Number} The object's size. |
|
3289 * @static |
|
3290 */ |
|
3291 O.size = function (obj) { |
|
3292 try { |
|
3293 return O.keys(obj).length; |
|
3294 } catch (ex) { |
|
3295 return 0; // Legacy behavior for non-objects. |
|
3296 } |
|
3297 }; |
|
3298 |
|
3299 /** |
|
3300 * Returns `true` if the object owns an enumerable property with the specified |
|
3301 * value. |
|
3302 * |
|
3303 * @method hasValue |
|
3304 * @param {Object} obj An object. |
|
3305 * @param {any} value The value to search for. |
|
3306 * @return {Boolean} `true` if _obj_ contains _value_, `false` otherwise. |
|
3307 * @static |
|
3308 */ |
|
3309 O.hasValue = function (obj, value) { |
|
3310 return Y.Array.indexOf(O.values(obj), value) > -1; |
|
3311 }; |
|
3312 |
|
3313 /** |
|
3314 * Executes a function on each enumerable property in _obj_. The function |
|
3315 * receives the value, the key, and the object itself as parameters (in that |
|
3316 * order). |
|
3317 * |
|
3318 * By default, only properties owned by _obj_ are enumerated. To include |
|
3319 * prototype properties, set the _proto_ parameter to `true`. |
|
3320 * |
|
3321 * @method each |
|
3322 * @param {Object} obj Object to enumerate. |
|
3323 * @param {Function} fn Function to execute on each enumerable property. |
|
3324 * @param {mixed} fn.value Value of the current property. |
|
3325 * @param {String} fn.key Key of the current property. |
|
3326 * @param {Object} fn.obj Object being enumerated. |
|
3327 * @param {Object} [thisObj] `this` object to use when calling _fn_. |
|
3328 * @param {Boolean} [proto=false] Include prototype properties. |
|
3329 * @return {YUI} the YUI instance. |
|
3330 * @chainable |
|
3331 * @static |
|
3332 */ |
|
3333 O.each = function (obj, fn, thisObj, proto) { |
|
3334 var key; |
|
3335 |
|
3336 for (key in obj) { |
|
3337 if (proto || owns(obj, key)) { |
|
3338 fn.call(thisObj || Y, obj[key], key, obj); |
|
3339 } |
|
3340 } |
|
3341 |
|
3342 return Y; |
|
3343 }; |
|
3344 |
|
3345 /** |
|
3346 * Executes a function on each enumerable property in _obj_, but halts if the |
|
3347 * function returns a truthy value. The function receives the value, the key, |
|
3348 * and the object itself as paramters (in that order). |
|
3349 * |
|
3350 * By default, only properties owned by _obj_ are enumerated. To include |
|
3351 * prototype properties, set the _proto_ parameter to `true`. |
|
3352 * |
|
3353 * @method some |
|
3354 * @param {Object} obj Object to enumerate. |
|
3355 * @param {Function} fn Function to execute on each enumerable property. |
|
3356 * @param {mixed} fn.value Value of the current property. |
|
3357 * @param {String} fn.key Key of the current property. |
|
3358 * @param {Object} fn.obj Object being enumerated. |
|
3359 * @param {Object} [thisObj] `this` object to use when calling _fn_. |
|
3360 * @param {Boolean} [proto=false] Include prototype properties. |
|
3361 * @return {Boolean} `true` if any execution of _fn_ returns a truthy value, |
|
3362 * `false` otherwise. |
|
3363 * @static |
|
3364 */ |
|
3365 O.some = function (obj, fn, thisObj, proto) { |
|
3366 var key; |
|
3367 |
|
3368 for (key in obj) { |
|
3369 if (proto || owns(obj, key)) { |
|
3370 if (fn.call(thisObj || Y, obj[key], key, obj)) { |
|
3371 return true; |
|
3372 } |
|
3373 } |
|
3374 } |
|
3375 |
|
3376 return false; |
|
3377 }; |
|
3378 |
|
3379 /** |
|
3380 * Retrieves the sub value at the provided path, |
|
3381 * from the value object provided. |
|
3382 * |
|
3383 * @method getValue |
|
3384 * @static |
|
3385 * @param o The object from which to extract the property value. |
|
3386 * @param path {Array} A path array, specifying the object traversal path |
|
3387 * from which to obtain the sub value. |
|
3388 * @return {Any} The value stored in the path, undefined if not found, |
|
3389 * undefined if the source is not an object. Returns the source object |
|
3390 * if an empty path is provided. |
|
3391 */ |
|
3392 O.getValue = function(o, path) { |
|
3393 if (!Lang.isObject(o)) { |
|
3394 return UNDEFINED; |
|
3395 } |
|
3396 |
|
3397 var i, |
|
3398 p = Y.Array(path), |
|
3399 l = p.length; |
|
3400 |
|
3401 for (i = 0; o !== UNDEFINED && i < l; i++) { |
|
3402 o = o[p[i]]; |
|
3403 } |
|
3404 |
|
3405 return o; |
|
3406 }; |
|
3407 |
|
3408 /** |
|
3409 * Sets the sub-attribute value at the provided path on the |
|
3410 * value object. Returns the modified value object, or |
|
3411 * undefined if the path is invalid. |
|
3412 * |
|
3413 * @method setValue |
|
3414 * @static |
|
3415 * @param o The object on which to set the sub value. |
|
3416 * @param path {Array} A path array, specifying the object traversal path |
|
3417 * at which to set the sub value. |
|
3418 * @param val {Any} The new value for the sub-attribute. |
|
3419 * @return {Object} The modified object, with the new sub value set, or |
|
3420 * undefined, if the path was invalid. |
|
3421 */ |
|
3422 O.setValue = function(o, path, val) { |
|
3423 var i, |
|
3424 p = Y.Array(path), |
|
3425 leafIdx = p.length - 1, |
|
3426 ref = o; |
|
3427 |
|
3428 if (leafIdx >= 0) { |
|
3429 for (i = 0; ref !== UNDEFINED && i < leafIdx; i++) { |
|
3430 ref = ref[p[i]]; |
|
3431 } |
|
3432 |
|
3433 if (ref !== UNDEFINED) { |
|
3434 ref[p[i]] = val; |
|
3435 } else { |
|
3436 return UNDEFINED; |
|
3437 } |
|
3438 } |
|
3439 |
|
3440 return o; |
|
3441 }; |
|
3442 |
|
3443 /** |
|
3444 * Returns `true` if the object has no enumerable properties of its own. |
|
3445 * |
|
3446 * @method isEmpty |
|
3447 * @param {Object} obj An object. |
|
3448 * @return {Boolean} `true` if the object is empty. |
|
3449 * @static |
|
3450 * @since 3.2.0 |
|
3451 */ |
|
3452 O.isEmpty = function (obj) { |
|
3453 return !O.keys(Object(obj)).length; |
|
3454 }; |
|
3455 /** |
|
3456 * The YUI module contains the components required for building the YUI seed |
|
3457 * file. This includes the script loading mechanism, a simple queue, and the |
|
3458 * core utilities for the library. |
|
3459 * @module yui |
|
3460 * @submodule yui-base |
|
3461 */ |
|
3462 |
|
3463 /** |
|
3464 * YUI user agent detection. |
|
3465 * Do not fork for a browser if it can be avoided. Use feature detection when |
|
3466 * you can. Use the user agent as a last resort. For all fields listed |
|
3467 * as @type float, UA stores a version number for the browser engine, |
|
3468 * 0 otherwise. This value may or may not map to the version number of |
|
3469 * the browser using the engine. The value is presented as a float so |
|
3470 * that it can easily be used for boolean evaluation as well as for |
|
3471 * looking for a particular range of versions. Because of this, |
|
3472 * some of the granularity of the version info may be lost. The fields that |
|
3473 * are @type string default to null. The API docs list the values that |
|
3474 * these fields can have. |
|
3475 * @class UA |
|
3476 * @static |
|
3477 */ |
|
3478 |
|
3479 /** |
|
3480 * Static method on `YUI.Env` for parsing a UA string. Called at instantiation |
|
3481 * to populate `Y.UA`. |
|
3482 * |
|
3483 * @static |
|
3484 * @method parseUA |
|
3485 * @param {String} [subUA=navigator.userAgent] UA string to parse |
|
3486 * @return {Object} The Y.UA object |
|
3487 */ |
|
3488 YUI.Env.parseUA = function(subUA) { |
|
3489 |
|
3490 var numberify = function(s) { |
|
3491 var c = 0; |
|
3492 return parseFloat(s.replace(/\./g, function() { |
|
3493 return (c++ === 1) ? '' : '.'; |
|
3494 })); |
|
3495 }, |
|
3496 |
|
3497 win = Y.config.win, |
|
3498 |
|
3499 nav = win && win.navigator, |
|
3500 |
|
3501 o = { |
|
3502 |
|
3503 /** |
|
3504 * Internet Explorer version number or 0. Example: 6 |
|
3505 * @property ie |
|
3506 * @type float |
|
3507 * @static |
|
3508 */ |
|
3509 ie: 0, |
|
3510 |
|
3511 /** |
|
3512 * Opera version number or 0. Example: 9.2 |
|
3513 * @property opera |
|
3514 * @type float |
|
3515 * @static |
|
3516 */ |
|
3517 opera: 0, |
|
3518 |
|
3519 /** |
|
3520 * Gecko engine revision number. Will evaluate to 1 if Gecko |
|
3521 * is detected but the revision could not be found. Other browsers |
|
3522 * will be 0. Example: 1.8 |
|
3523 * <pre> |
|
3524 * Firefox 1.0.0.4: 1.7.8 <-- Reports 1.7 |
|
3525 * Firefox 1.5.0.9: 1.8.0.9 <-- 1.8 |
|
3526 * Firefox 2.0.0.3: 1.8.1.3 <-- 1.81 |
|
3527 * Firefox 3.0 <-- 1.9 |
|
3528 * Firefox 3.5 <-- 1.91 |
|
3529 * </pre> |
|
3530 * @property gecko |
|
3531 * @type float |
|
3532 * @static |
|
3533 */ |
|
3534 gecko: 0, |
|
3535 |
|
3536 /** |
|
3537 * AppleWebKit version. KHTML browsers that are not WebKit browsers |
|
3538 * will evaluate to 1, other browsers 0. Example: 418.9 |
|
3539 * <pre> |
|
3540 * Safari 1.3.2 (312.6): 312.8.1 <-- Reports 312.8 -- currently the |
|
3541 * latest available for Mac OSX 10.3. |
|
3542 * Safari 2.0.2: 416 <-- hasOwnProperty introduced |
|
3543 * Safari 2.0.4: 418 <-- preventDefault fixed |
|
3544 * Safari 2.0.4 (419.3): 418.9.1 <-- One version of Safari may run |
|
3545 * different versions of webkit |
|
3546 * Safari 2.0.4 (419.3): 419 <-- Tiger installations that have been |
|
3547 * updated, but not updated |
|
3548 * to the latest patch. |
|
3549 * Webkit 212 nightly: 522+ <-- Safari 3.0 precursor (with native |
|
3550 * SVG and many major issues fixed). |
|
3551 * Safari 3.0.4 (523.12) 523.12 <-- First Tiger release - automatic |
|
3552 * update from 2.x via the 10.4.11 OS patch. |
|
3553 * Webkit nightly 1/2008:525+ <-- Supports DOMContentLoaded event. |
|
3554 * yahoo.com user agent hack removed. |
|
3555 * </pre> |
|
3556 * http://en.wikipedia.org/wiki/Safari_version_history |
|
3557 * @property webkit |
|
3558 * @type float |
|
3559 * @static |
|
3560 */ |
|
3561 webkit: 0, |
|
3562 |
|
3563 /** |
|
3564 * Safari will be detected as webkit, but this property will also |
|
3565 * be populated with the Safari version number |
|
3566 * @property safari |
|
3567 * @type float |
|
3568 * @static |
|
3569 */ |
|
3570 safari: 0, |
|
3571 |
|
3572 /** |
|
3573 * Chrome will be detected as webkit, but this property will also |
|
3574 * be populated with the Chrome version number |
|
3575 * @property chrome |
|
3576 * @type float |
|
3577 * @static |
|
3578 */ |
|
3579 chrome: 0, |
|
3580 |
|
3581 /** |
|
3582 * The mobile property will be set to a string containing any relevant |
|
3583 * user agent information when a modern mobile browser is detected. |
|
3584 * Currently limited to Safari on the iPhone/iPod Touch, Nokia N-series |
|
3585 * devices with the WebKit-based browser, and Opera Mini. |
|
3586 * @property mobile |
|
3587 * @type string |
|
3588 * @default null |
|
3589 * @static |
|
3590 */ |
|
3591 mobile: null, |
|
3592 |
|
3593 /** |
|
3594 * Adobe AIR version number or 0. Only populated if webkit is detected. |
|
3595 * Example: 1.0 |
|
3596 * @property air |
|
3597 * @type float |
|
3598 */ |
|
3599 air: 0, |
|
3600 /** |
|
3601 * PhantomJS version number or 0. Only populated if webkit is detected. |
|
3602 * Example: 1.0 |
|
3603 * @property phantomjs |
|
3604 * @type float |
|
3605 */ |
|
3606 phantomjs: 0, |
|
3607 /** |
|
3608 * Detects Apple iPad's OS version |
|
3609 * @property ipad |
|
3610 * @type float |
|
3611 * @static |
|
3612 */ |
|
3613 ipad: 0, |
|
3614 /** |
|
3615 * Detects Apple iPhone's OS version |
|
3616 * @property iphone |
|
3617 * @type float |
|
3618 * @static |
|
3619 */ |
|
3620 iphone: 0, |
|
3621 /** |
|
3622 * Detects Apples iPod's OS version |
|
3623 * @property ipod |
|
3624 * @type float |
|
3625 * @static |
|
3626 */ |
|
3627 ipod: 0, |
|
3628 /** |
|
3629 * General truthy check for iPad, iPhone or iPod |
|
3630 * @property ios |
|
3631 * @type Boolean |
|
3632 * @default null |
|
3633 * @static |
|
3634 */ |
|
3635 ios: null, |
|
3636 /** |
|
3637 * Detects Googles Android OS version |
|
3638 * @property android |
|
3639 * @type float |
|
3640 * @static |
|
3641 */ |
|
3642 android: 0, |
|
3643 /** |
|
3644 * Detects Kindle Silk |
|
3645 * @property silk |
|
3646 * @type float |
|
3647 * @static |
|
3648 */ |
|
3649 silk: 0, |
|
3650 /** |
|
3651 * Detects Kindle Silk Acceleration |
|
3652 * @property accel |
|
3653 * @type Boolean |
|
3654 * @static |
|
3655 */ |
|
3656 accel: false, |
|
3657 /** |
|
3658 * Detects Palms WebOS version |
|
3659 * @property webos |
|
3660 * @type float |
|
3661 * @static |
|
3662 */ |
|
3663 webos: 0, |
|
3664 |
|
3665 /** |
|
3666 * Google Caja version number or 0. |
|
3667 * @property caja |
|
3668 * @type float |
|
3669 */ |
|
3670 caja: nav && nav.cajaVersion, |
|
3671 |
|
3672 /** |
|
3673 * Set to true if the page appears to be in SSL |
|
3674 * @property secure |
|
3675 * @type boolean |
|
3676 * @static |
|
3677 */ |
|
3678 secure: false, |
|
3679 |
|
3680 /** |
|
3681 * The operating system. Currently only detecting windows or macintosh |
|
3682 * @property os |
|
3683 * @type string |
|
3684 * @default null |
|
3685 * @static |
|
3686 */ |
|
3687 os: null, |
|
3688 |
|
3689 /** |
|
3690 * The Nodejs Version |
|
3691 * @property nodejs |
|
3692 * @type float |
|
3693 * @default 0 |
|
3694 * @static |
|
3695 */ |
|
3696 nodejs: 0, |
|
3697 /** |
|
3698 * Window8/IE10 Application host environment |
|
3699 * @property winjs |
|
3700 * @type Boolean |
|
3701 * @static |
|
3702 */ |
|
3703 winjs: !!((typeof Windows !== "undefined") && Windows.System), |
|
3704 /** |
|
3705 * Are touch/msPointer events available on this device |
|
3706 * @property touchEnabled |
|
3707 * @type Boolean |
|
3708 * @static |
|
3709 */ |
|
3710 touchEnabled: false |
|
3711 }, |
|
3712 |
|
3713 ua = subUA || nav && nav.userAgent, |
|
3714 |
|
3715 loc = win && win.location, |
|
3716 |
|
3717 href = loc && loc.href, |
|
3718 |
|
3719 m; |
|
3720 |
|
3721 /** |
|
3722 * The User Agent string that was parsed |
|
3723 * @property userAgent |
|
3724 * @type String |
|
3725 * @static |
|
3726 */ |
|
3727 o.userAgent = ua; |
|
3728 |
|
3729 |
|
3730 o.secure = href && (href.toLowerCase().indexOf('https') === 0); |
|
3731 |
|
3732 if (ua) { |
|
3733 |
|
3734 if ((/windows|win32/i).test(ua)) { |
|
3735 o.os = 'windows'; |
|
3736 } else if ((/macintosh|mac_powerpc/i).test(ua)) { |
|
3737 o.os = 'macintosh'; |
|
3738 } else if ((/android/i).test(ua)) { |
|
3739 o.os = 'android'; |
|
3740 } else if ((/symbos/i).test(ua)) { |
|
3741 o.os = 'symbos'; |
|
3742 } else if ((/linux/i).test(ua)) { |
|
3743 o.os = 'linux'; |
|
3744 } else if ((/rhino/i).test(ua)) { |
|
3745 o.os = 'rhino'; |
|
3746 } |
|
3747 |
|
3748 // Modern KHTML browsers should qualify as Safari X-Grade |
|
3749 if ((/KHTML/).test(ua)) { |
|
3750 o.webkit = 1; |
|
3751 } |
|
3752 if ((/IEMobile|XBLWP7/).test(ua)) { |
|
3753 o.mobile = 'windows'; |
|
3754 } |
|
3755 if ((/Fennec/).test(ua)) { |
|
3756 o.mobile = 'gecko'; |
|
3757 } |
|
3758 // Modern WebKit browsers are at least X-Grade |
|
3759 m = ua.match(/AppleWebKit\/([^\s]*)/); |
|
3760 if (m && m[1]) { |
|
3761 o.webkit = numberify(m[1]); |
|
3762 o.safari = o.webkit; |
|
3763 |
|
3764 if (/PhantomJS/.test(ua)) { |
|
3765 m = ua.match(/PhantomJS\/([^\s]*)/); |
|
3766 if (m && m[1]) { |
|
3767 o.phantomjs = numberify(m[1]); |
|
3768 } |
|
3769 } |
|
3770 |
|
3771 // Mobile browser check |
|
3772 if (/ Mobile\//.test(ua) || (/iPad|iPod|iPhone/).test(ua)) { |
|
3773 o.mobile = 'Apple'; // iPhone or iPod Touch |
|
3774 |
|
3775 m = ua.match(/OS ([^\s]*)/); |
|
3776 if (m && m[1]) { |
|
3777 m = numberify(m[1].replace('_', '.')); |
|
3778 } |
|
3779 o.ios = m; |
|
3780 o.os = 'ios'; |
|
3781 o.ipad = o.ipod = o.iphone = 0; |
|
3782 |
|
3783 m = ua.match(/iPad|iPod|iPhone/); |
|
3784 if (m && m[0]) { |
|
3785 o[m[0].toLowerCase()] = o.ios; |
|
3786 } |
|
3787 } else { |
|
3788 m = ua.match(/NokiaN[^\/]*|webOS\/\d\.\d/); |
|
3789 if (m) { |
|
3790 // Nokia N-series, webOS, ex: NokiaN95 |
|
3791 o.mobile = m[0]; |
|
3792 } |
|
3793 if (/webOS/.test(ua)) { |
|
3794 o.mobile = 'WebOS'; |
|
3795 m = ua.match(/webOS\/([^\s]*);/); |
|
3796 if (m && m[1]) { |
|
3797 o.webos = numberify(m[1]); |
|
3798 } |
|
3799 } |
|
3800 if (/ Android/.test(ua)) { |
|
3801 if (/Mobile/.test(ua)) { |
|
3802 o.mobile = 'Android'; |
|
3803 } |
|
3804 m = ua.match(/Android ([^\s]*);/); |
|
3805 if (m && m[1]) { |
|
3806 o.android = numberify(m[1]); |
|
3807 } |
|
3808 |
|
3809 } |
|
3810 if (/Silk/.test(ua)) { |
|
3811 m = ua.match(/Silk\/([^\s]*)\)/); |
|
3812 if (m && m[1]) { |
|
3813 o.silk = numberify(m[1]); |
|
3814 } |
|
3815 if (!o.android) { |
|
3816 o.android = 2.34; //Hack for desktop mode in Kindle |
|
3817 o.os = 'Android'; |
|
3818 } |
|
3819 if (/Accelerated=true/.test(ua)) { |
|
3820 o.accel = true; |
|
3821 } |
|
3822 } |
|
3823 } |
|
3824 |
|
3825 m = ua.match(/(Chrome|CrMo|CriOS)\/([^\s]*)/); |
|
3826 if (m && m[1] && m[2]) { |
|
3827 o.chrome = numberify(m[2]); // Chrome |
|
3828 o.safari = 0; //Reset safari back to 0 |
|
3829 if (m[1] === 'CrMo') { |
|
3830 o.mobile = 'chrome'; |
|
3831 } |
|
3832 } else { |
|
3833 m = ua.match(/AdobeAIR\/([^\s]*)/); |
|
3834 if (m) { |
|
3835 o.air = m[0]; // Adobe AIR 1.0 or better |
|
3836 } |
|
3837 } |
|
3838 } |
|
3839 |
|
3840 if (!o.webkit) { // not webkit |
|
3841 // @todo check Opera/8.01 (J2ME/MIDP; Opera Mini/2.0.4509/1316; fi; U; ssr) |
|
3842 if (/Opera/.test(ua)) { |
|
3843 m = ua.match(/Opera[\s\/]([^\s]*)/); |
|
3844 if (m && m[1]) { |
|
3845 o.opera = numberify(m[1]); |
|
3846 } |
|
3847 m = ua.match(/Version\/([^\s]*)/); |
|
3848 if (m && m[1]) { |
|
3849 o.opera = numberify(m[1]); // opera 10+ |
|
3850 } |
|
3851 |
|
3852 if (/Opera Mobi/.test(ua)) { |
|
3853 o.mobile = 'opera'; |
|
3854 m = ua.replace('Opera Mobi', '').match(/Opera ([^\s]*)/); |
|
3855 if (m && m[1]) { |
|
3856 o.opera = numberify(m[1]); |
|
3857 } |
|
3858 } |
|
3859 m = ua.match(/Opera Mini[^;]*/); |
|
3860 |
|
3861 if (m) { |
|
3862 o.mobile = m[0]; // ex: Opera Mini/2.0.4509/1316 |
|
3863 } |
|
3864 } else { // not opera or webkit |
|
3865 m = ua.match(/MSIE\s([^;]*)/); |
|
3866 if (m && m[1]) { |
|
3867 o.ie = numberify(m[1]); |
|
3868 } else { // not opera, webkit, or ie |
|
3869 m = ua.match(/Gecko\/([^\s]*)/); |
|
3870 if (m) { |
|
3871 o.gecko = 1; // Gecko detected, look for revision |
|
3872 m = ua.match(/rv:([^\s\)]*)/); |
|
3873 if (m && m[1]) { |
|
3874 o.gecko = numberify(m[1]); |
|
3875 if (/Mobile|Tablet/.test(ua)) { |
|
3876 o.mobile = "ffos"; |
|
3877 } |
|
3878 } |
|
3879 } |
|
3880 } |
|
3881 } |
|
3882 } |
|
3883 } |
|
3884 |
|
3885 //Check for known properties to tell if touch events are enabled on this device or if |
|
3886 //the number of MSPointer touchpoints on this device is greater than 0. |
|
3887 if (win && nav && !(o.chrome && o.chrome < 6)) { |
|
3888 o.touchEnabled = (("ontouchstart" in win) || (("msMaxTouchPoints" in nav) && (nav.msMaxTouchPoints > 0))); |
|
3889 } |
|
3890 |
|
3891 //It was a parsed UA, do not assign the global value. |
|
3892 if (!subUA) { |
|
3893 |
|
3894 if (typeof process === 'object') { |
|
3895 |
|
3896 if (process.versions && process.versions.node) { |
|
3897 //NodeJS |
|
3898 o.os = process.platform; |
|
3899 o.nodejs = numberify(process.versions.node); |
|
3900 } |
|
3901 } |
|
3902 |
|
3903 YUI.Env.UA = o; |
|
3904 |
|
3905 } |
|
3906 |
|
3907 return o; |
|
3908 }; |
|
3909 |
|
3910 |
|
3911 Y.UA = YUI.Env.UA || YUI.Env.parseUA(); |
|
3912 |
|
3913 /** |
|
3914 Performs a simple comparison between two version numbers, accounting for |
|
3915 standard versioning logic such as the fact that "535.8" is a lower version than |
|
3916 "535.24", even though a simple numerical comparison would indicate that it's |
|
3917 greater. Also accounts for cases such as "1.1" vs. "1.1.0", which are |
|
3918 considered equivalent. |
|
3919 |
|
3920 Returns -1 if version _a_ is lower than version _b_, 0 if they're equivalent, |
|
3921 1 if _a_ is higher than _b_. |
|
3922 |
|
3923 Versions may be numbers or strings containing numbers and dots. For example, |
|
3924 both `535` and `"535.8.10"` are acceptable. A version string containing |
|
3925 non-numeric characters, like `"535.8.beta"`, may produce unexpected results. |
|
3926 |
|
3927 @method compareVersions |
|
3928 @param {Number|String} a First version number to compare. |
|
3929 @param {Number|String} b Second version number to compare. |
|
3930 @return -1 if _a_ is lower than _b_, 0 if they're equivalent, 1 if _a_ is |
|
3931 higher than _b_. |
|
3932 **/ |
|
3933 Y.UA.compareVersions = function (a, b) { |
|
3934 var aPart, aParts, bPart, bParts, i, len; |
|
3935 |
|
3936 if (a === b) { |
|
3937 return 0; |
|
3938 } |
|
3939 |
|
3940 aParts = (a + '').split('.'); |
|
3941 bParts = (b + '').split('.'); |
|
3942 |
|
3943 for (i = 0, len = Math.max(aParts.length, bParts.length); i < len; ++i) { |
|
3944 aPart = parseInt(aParts[i], 10); |
|
3945 bPart = parseInt(bParts[i], 10); |
|
3946 |
|
3947 /*jshint expr: true*/ |
|
3948 isNaN(aPart) && (aPart = 0); |
|
3949 isNaN(bPart) && (bPart = 0); |
|
3950 |
|
3951 if (aPart < bPart) { |
|
3952 return -1; |
|
3953 } |
|
3954 |
|
3955 if (aPart > bPart) { |
|
3956 return 1; |
|
3957 } |
|
3958 } |
|
3959 |
|
3960 return 0; |
|
3961 }; |
|
3962 YUI.Env.aliases = { |
|
3963 "anim": ["anim-base","anim-color","anim-curve","anim-easing","anim-node-plugin","anim-scroll","anim-xy"], |
|
3964 "anim-shape-transform": ["anim-shape"], |
|
3965 "app": ["app-base","app-content","app-transitions","lazy-model-list","model","model-list","model-sync-rest","router","view","view-node-map"], |
|
3966 "attribute": ["attribute-base","attribute-complex"], |
|
3967 "attribute-events": ["attribute-observable"], |
|
3968 "autocomplete": ["autocomplete-base","autocomplete-sources","autocomplete-list","autocomplete-plugin"], |
|
3969 "axes": ["axis-numeric","axis-category","axis-time","axis-stacked"], |
|
3970 "axes-base": ["axis-numeric-base","axis-category-base","axis-time-base","axis-stacked-base"], |
|
3971 "base": ["base-base","base-pluginhost","base-build"], |
|
3972 "cache": ["cache-base","cache-offline","cache-plugin"], |
|
3973 "charts": ["charts-base"], |
|
3974 "collection": ["array-extras","arraylist","arraylist-add","arraylist-filter","array-invoke"], |
|
3975 "color": ["color-base","color-hsl","color-harmony"], |
|
3976 "controller": ["router"], |
|
3977 "dataschema": ["dataschema-base","dataschema-json","dataschema-xml","dataschema-array","dataschema-text"], |
|
3978 "datasource": ["datasource-local","datasource-io","datasource-get","datasource-function","datasource-cache","datasource-jsonschema","datasource-xmlschema","datasource-arrayschema","datasource-textschema","datasource-polling"], |
|
3979 "datatable": ["datatable-core","datatable-table","datatable-head","datatable-body","datatable-base","datatable-column-widths","datatable-message","datatable-mutable","datatable-sort","datatable-datasource"], |
|
3980 "datatype": ["datatype-date","datatype-number","datatype-xml"], |
|
3981 "datatype-date": ["datatype-date-parse","datatype-date-format","datatype-date-math"], |
|
3982 "datatype-number": ["datatype-number-parse","datatype-number-format"], |
|
3983 "datatype-xml": ["datatype-xml-parse","datatype-xml-format"], |
|
3984 "dd": ["dd-ddm-base","dd-ddm","dd-ddm-drop","dd-drag","dd-proxy","dd-constrain","dd-drop","dd-scroll","dd-delegate"], |
|
3985 "dom": ["dom-base","dom-screen","dom-style","selector-native","selector"], |
|
3986 "editor": ["frame","editor-selection","exec-command","editor-base","editor-para","editor-br","editor-bidi","editor-tab","createlink-base"], |
|
3987 "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"], |
|
3988 "event-custom": ["event-custom-base","event-custom-complex"], |
|
3989 "event-gestures": ["event-flick","event-move"], |
|
3990 "handlebars": ["handlebars-compiler"], |
|
3991 "highlight": ["highlight-base","highlight-accentfold"], |
|
3992 "history": ["history-base","history-hash","history-hash-ie","history-html5"], |
|
3993 "io": ["io-base","io-xdr","io-form","io-upload-iframe","io-queue"], |
|
3994 "json": ["json-parse","json-stringify"], |
|
3995 "loader": ["loader-base","loader-rollup","loader-yui3"], |
|
3996 "node": ["node-base","node-event-delegate","node-pluginhost","node-screen","node-style"], |
|
3997 "pluginhost": ["pluginhost-base","pluginhost-config"], |
|
3998 "querystring": ["querystring-parse","querystring-stringify"], |
|
3999 "recordset": ["recordset-base","recordset-sort","recordset-filter","recordset-indexer"], |
|
4000 "resize": ["resize-base","resize-proxy","resize-constrain"], |
|
4001 "slider": ["slider-base","slider-value-range","clickable-rail","range-slider"], |
|
4002 "template": ["template-base","template-micro"], |
|
4003 "text": ["text-accentfold","text-wordbreak"], |
|
4004 "widget": ["widget-base","widget-htmlparser","widget-skin","widget-uievents"] |
|
4005 }; |
|
4006 |
|
4007 |
|
4008 }, '@VERSION@', {"use": ["get", "features", "intl-base", "yui-log", "yui-later"]}); |
|
4009 YUI.add('get', function (Y, NAME) { |
|
4010 |
|
4011 /*jslint boss:true, expr:true, laxbreak: true */ |
|
4012 |
|
4013 /** |
|
4014 Provides dynamic loading of remote JavaScript and CSS resources. |
|
4015 |
|
4016 @module get |
|
4017 @class Get |
|
4018 @static |
|
4019 **/ |
|
4020 |
|
4021 var Lang = Y.Lang, |
|
4022 |
|
4023 CUSTOM_ATTRS, // defined lazily in Y.Get.Transaction._createNode() |
|
4024 |
|
4025 Get, Transaction; |
|
4026 |
|
4027 Y.Get = Get = { |
|
4028 // -- Public Properties ---------------------------------------------------- |
|
4029 |
|
4030 /** |
|
4031 Default options for CSS requests. Options specified here will override |
|
4032 global defaults for CSS requests. |
|
4033 |
|
4034 See the `options` property for all available options. |
|
4035 |
|
4036 @property cssOptions |
|
4037 @type Object |
|
4038 @static |
|
4039 @since 3.5.0 |
|
4040 **/ |
|
4041 cssOptions: { |
|
4042 attributes: { |
|
4043 rel: 'stylesheet' |
|
4044 }, |
|
4045 |
|
4046 doc : Y.config.linkDoc || Y.config.doc, |
|
4047 pollInterval: 50 |
|
4048 }, |
|
4049 |
|
4050 /** |
|
4051 Default options for JS requests. Options specified here will override global |
|
4052 defaults for JS requests. |
|
4053 |
|
4054 See the `options` property for all available options. |
|
4055 |
|
4056 @property jsOptions |
|
4057 @type Object |
|
4058 @static |
|
4059 @since 3.5.0 |
|
4060 **/ |
|
4061 jsOptions: { |
|
4062 autopurge: true, |
|
4063 doc : Y.config.scriptDoc || Y.config.doc |
|
4064 }, |
|
4065 |
|
4066 /** |
|
4067 Default options to use for all requests. |
|
4068 |
|
4069 Note that while all available options are documented here for ease of |
|
4070 discovery, some options (like callback functions) only make sense at the |
|
4071 transaction level. |
|
4072 |
|
4073 Callback functions specified via the options object or the `options` |
|
4074 parameter of the `css()`, `js()`, or `load()` methods will receive the |
|
4075 transaction object as a parameter. See `Y.Get.Transaction` for details on |
|
4076 the properties and methods available on transactions. |
|
4077 |
|
4078 @static |
|
4079 @since 3.5.0 |
|
4080 @property {Object} options |
|
4081 |
|
4082 @property {Boolean} [options.async=false] Whether or not to load scripts |
|
4083 asynchronously, meaning they're requested in parallel and execution |
|
4084 order is not guaranteed. Has no effect on CSS, since CSS is always |
|
4085 loaded asynchronously. |
|
4086 |
|
4087 @property {Object} [options.attributes] HTML attribute name/value pairs that |
|
4088 should be added to inserted nodes. By default, the `charset` attribute |
|
4089 will be set to "utf-8" and nodes will be given an auto-generated `id` |
|
4090 attribute, but you can override these with your own values if desired. |
|
4091 |
|
4092 @property {Boolean} [options.autopurge] Whether or not to automatically |
|
4093 purge inserted nodes after the purge threshold is reached. This is |
|
4094 `true` by default for JavaScript, but `false` for CSS since purging a |
|
4095 CSS node will also remove any styling applied by the referenced file. |
|
4096 |
|
4097 @property {Object} [options.context] `this` object to use when calling |
|
4098 callback functions. Defaults to the transaction object. |
|
4099 |
|
4100 @property {Mixed} [options.data] Arbitrary data object to pass to "on*" |
|
4101 callbacks. |
|
4102 |
|
4103 @property {Document} [options.doc] Document into which nodes should be |
|
4104 inserted. By default, the current document is used. |
|
4105 |
|
4106 @property {HTMLElement|String} [options.insertBefore] HTML element or id |
|
4107 string of an element before which all generated nodes should be |
|
4108 inserted. If not specified, Get will automatically determine the best |
|
4109 place to insert nodes for maximum compatibility. |
|
4110 |
|
4111 @property {Function} [options.onEnd] Callback to execute after a transaction |
|
4112 is complete, regardless of whether it succeeded or failed. |
|
4113 |
|
4114 @property {Function} [options.onFailure] Callback to execute after a |
|
4115 transaction fails, times out, or is aborted. |
|
4116 |
|
4117 @property {Function} [options.onProgress] Callback to execute after each |
|
4118 individual request in a transaction either succeeds or fails. |
|
4119 |
|
4120 @property {Function} [options.onSuccess] Callback to execute after a |
|
4121 transaction completes successfully with no errors. Note that in browsers |
|
4122 that don't support the `error` event on CSS `<link>` nodes, a failed CSS |
|
4123 request may still be reported as a success because in these browsers |
|
4124 it can be difficult or impossible to distinguish between success and |
|
4125 failure for CSS resources. |
|
4126 |
|
4127 @property {Function} [options.onTimeout] Callback to execute after a |
|
4128 transaction times out. |
|
4129 |
|
4130 @property {Number} [options.pollInterval=50] Polling interval (in |
|
4131 milliseconds) for detecting CSS load completion in browsers that don't |
|
4132 support the `load` event on `<link>` nodes. This isn't used for |
|
4133 JavaScript. |
|
4134 |
|
4135 @property {Number} [options.purgethreshold=20] Number of nodes to insert |
|
4136 before triggering an automatic purge when `autopurge` is `true`. |
|
4137 |
|
4138 @property {Number} [options.timeout] Number of milliseconds to wait before |
|
4139 aborting a transaction. When a timeout occurs, the `onTimeout` callback |
|
4140 is called, followed by `onFailure` and finally `onEnd`. By default, |
|
4141 there is no timeout. |
|
4142 |
|
4143 @property {String} [options.type] Resource type ("css" or "js"). This option |
|
4144 is set automatically by the `css()` and `js()` functions and will be |
|
4145 ignored there, but may be useful when using the `load()` function. If |
|
4146 not specified, the type will be inferred from the URL, defaulting to |
|
4147 "js" if the URL doesn't contain a recognizable file extension. |
|
4148 **/ |
|
4149 options: { |
|
4150 attributes: { |
|
4151 charset: 'utf-8' |
|
4152 }, |
|
4153 |
|
4154 purgethreshold: 20 |
|
4155 }, |
|
4156 |
|
4157 // -- Protected Properties ------------------------------------------------- |
|
4158 |
|
4159 /** |
|
4160 Regex that matches a CSS URL. Used to guess the file type when it's not |
|
4161 specified. |
|
4162 |
|
4163 @property REGEX_CSS |
|
4164 @type RegExp |
|
4165 @final |
|
4166 @protected |
|
4167 @static |
|
4168 @since 3.5.0 |
|
4169 **/ |
|
4170 REGEX_CSS: /\.css(?:[?;].*)?$/i, |
|
4171 |
|
4172 /** |
|
4173 Regex that matches a JS URL. Used to guess the file type when it's not |
|
4174 specified. |
|
4175 |
|
4176 @property REGEX_JS |
|
4177 @type RegExp |
|
4178 @final |
|
4179 @protected |
|
4180 @static |
|
4181 @since 3.5.0 |
|
4182 **/ |
|
4183 REGEX_JS : /\.js(?:[?;].*)?$/i, |
|
4184 |
|
4185 /** |
|
4186 Contains information about the current environment, such as what script and |
|
4187 link injection features it supports. |
|
4188 |
|
4189 This object is created and populated the first time the `_getEnv()` method |
|
4190 is called. |
|
4191 |
|
4192 @property _env |
|
4193 @type Object |
|
4194 @protected |
|
4195 @static |
|
4196 @since 3.5.0 |
|
4197 **/ |
|
4198 |
|
4199 /** |
|
4200 Mapping of document _yuid strings to <head> or <base> node references so we |
|
4201 don't have to look the node up each time we want to insert a request node. |
|
4202 |
|
4203 @property _insertCache |
|
4204 @type Object |
|
4205 @protected |
|
4206 @static |
|
4207 @since 3.5.0 |
|
4208 **/ |
|
4209 _insertCache: {}, |
|
4210 |
|
4211 /** |
|
4212 Information about the currently pending transaction, if any. |
|
4213 |
|
4214 This is actually an object with two properties: `callback`, containing the |
|
4215 optional callback passed to `css()`, `load()`, or `js()`; and `transaction`, |
|
4216 containing the actual transaction instance. |
|
4217 |
|
4218 @property _pending |
|
4219 @type Object |
|
4220 @protected |
|
4221 @static |
|
4222 @since 3.5.0 |
|
4223 **/ |
|
4224 _pending: null, |
|
4225 |
|
4226 /** |
|
4227 HTML nodes eligible to be purged next time autopurge is triggered. |
|
4228 |
|
4229 @property _purgeNodes |
|
4230 @type HTMLElement[] |
|
4231 @protected |
|
4232 @static |
|
4233 @since 3.5.0 |
|
4234 **/ |
|
4235 _purgeNodes: [], |
|
4236 |
|
4237 /** |
|
4238 Queued transactions and associated callbacks. |
|
4239 |
|
4240 @property _queue |
|
4241 @type Object[] |
|
4242 @protected |
|
4243 @static |
|
4244 @since 3.5.0 |
|
4245 **/ |
|
4246 _queue: [], |
|
4247 |
|
4248 // -- Public Methods ------------------------------------------------------- |
|
4249 |
|
4250 /** |
|
4251 Aborts the specified transaction. |
|
4252 |
|
4253 This will cause the transaction's `onFailure` callback to be called and |
|
4254 will prevent any new script and link nodes from being added to the document, |
|
4255 but any resources that have already been requested will continue loading |
|
4256 (there's no safe way to prevent this, unfortunately). |
|
4257 |
|
4258 *Note:* This method is deprecated as of 3.5.0, and will be removed in a |
|
4259 future version of YUI. Use the transaction-level `abort()` method instead. |
|
4260 |
|
4261 @method abort |
|
4262 @param {Get.Transaction} transaction Transaction to abort. |
|
4263 @deprecated Use the `abort()` method on the transaction instead. |
|
4264 @static |
|
4265 **/ |
|
4266 abort: function (transaction) { |
|
4267 var i, id, item, len, pending; |
|
4268 |
|
4269 Y.log('`Y.Get.abort()` is deprecated as of 3.5.0. Use the `abort()` method on the transaction instead.', 'warn', 'get'); |
|
4270 |
|
4271 if (!transaction.abort) { |
|
4272 id = transaction; |
|
4273 pending = this._pending; |
|
4274 transaction = null; |
|
4275 |
|
4276 if (pending && pending.transaction.id === id) { |
|
4277 transaction = pending.transaction; |
|
4278 this._pending = null; |
|
4279 } else { |
|
4280 for (i = 0, len = this._queue.length; i < len; ++i) { |
|
4281 item = this._queue[i].transaction; |
|
4282 |
|
4283 if (item.id === id) { |
|
4284 transaction = item; |
|
4285 this._queue.splice(i, 1); |
|
4286 break; |
|
4287 } |
|
4288 } |
|
4289 } |
|
4290 } |
|
4291 |
|
4292 transaction && transaction.abort(); |
|
4293 }, |
|
4294 |
|
4295 /** |
|
4296 Loads one or more CSS files. |
|
4297 |
|
4298 The _urls_ parameter may be provided as a URL string, a request object, |
|
4299 or an array of URL strings and/or request objects. |
|
4300 |
|
4301 A request object is just an object that contains a `url` property and zero |
|
4302 or more options that should apply specifically to that request. |
|
4303 Request-specific options take priority over transaction-level options and |
|
4304 default options. |
|
4305 |
|
4306 URLs may be relative or absolute, and do not have to have the same origin |
|
4307 as the current page. |
|
4308 |
|
4309 The `options` parameter may be omitted completely and a callback passed in |
|
4310 its place, if desired. |
|
4311 |
|
4312 @example |
|
4313 |
|
4314 // Load a single CSS file and log a message on completion. |
|
4315 Y.Get.css('foo.css', function (err) { |
|
4316 if (err) { |
|
4317 Y.log('foo.css failed to load!'); |
|
4318 } else { |
|
4319 Y.log('foo.css was loaded successfully'); |
|
4320 } |
|
4321 }); |
|
4322 |
|
4323 // Load multiple CSS files and log a message when all have finished |
|
4324 // loading. |
|
4325 var urls = ['foo.css', 'http://example.com/bar.css', 'baz/quux.css']; |
|
4326 |
|
4327 Y.Get.css(urls, function (err) { |
|
4328 if (err) { |
|
4329 Y.log('one or more files failed to load!'); |
|
4330 } else { |
|
4331 Y.log('all files loaded successfully'); |
|
4332 } |
|
4333 }); |
|
4334 |
|
4335 // Specify transaction-level options, which will apply to all requests |
|
4336 // within the transaction. |
|
4337 Y.Get.css(urls, { |
|
4338 attributes: {'class': 'my-css'}, |
|
4339 timeout : 5000 |
|
4340 }); |
|
4341 |
|
4342 // Specify per-request options, which override transaction-level and |
|
4343 // default options. |
|
4344 Y.Get.css([ |
|
4345 {url: 'foo.css', attributes: {id: 'foo'}}, |
|
4346 {url: 'bar.css', attributes: {id: 'bar', charset: 'iso-8859-1'}} |
|
4347 ]); |
|
4348 |
|
4349 @method css |
|
4350 @param {String|Object|Array} urls URL string, request object, or array |
|
4351 of URLs and/or request objects to load. |
|
4352 @param {Object} [options] Options for this transaction. See the |
|
4353 `Y.Get.options` property for a complete list of available options. |
|
4354 @param {Function} [callback] Callback function to be called on completion. |
|
4355 This is a general callback and will be called before any more granular |
|
4356 callbacks (`onSuccess`, `onFailure`, etc.) specified in the `options` |
|
4357 object. |
|
4358 |
|
4359 @param {Array|null} callback.err Array of errors that occurred during |
|
4360 the transaction, or `null` on success. |
|
4361 @param {Get.Transaction} callback.transaction Transaction object. |
|
4362 |
|
4363 @return {Get.Transaction} Transaction object. |
|
4364 @static |
|
4365 **/ |
|
4366 css: function (urls, options, callback) { |
|
4367 return this._load('css', urls, options, callback); |
|
4368 }, |
|
4369 |
|
4370 /** |
|
4371 Loads one or more JavaScript resources. |
|
4372 |
|
4373 The _urls_ parameter may be provided as a URL string, a request object, |
|
4374 or an array of URL strings and/or request objects. |
|
4375 |
|
4376 A request object is just an object that contains a `url` property and zero |
|
4377 or more options that should apply specifically to that request. |
|
4378 Request-specific options take priority over transaction-level options and |
|
4379 default options. |
|
4380 |
|
4381 URLs may be relative or absolute, and do not have to have the same origin |
|
4382 as the current page. |
|
4383 |
|
4384 The `options` parameter may be omitted completely and a callback passed in |
|
4385 its place, if desired. |
|
4386 |
|
4387 Scripts will be executed in the order they're specified unless the `async` |
|
4388 option is `true`, in which case they'll be loaded in parallel and executed |
|
4389 in whatever order they finish loading. |
|
4390 |
|
4391 @example |
|
4392 |
|
4393 // Load a single JS file and log a message on completion. |
|
4394 Y.Get.js('foo.js', function (err) { |
|
4395 if (err) { |
|
4396 Y.log('foo.js failed to load!'); |
|
4397 } else { |
|
4398 Y.log('foo.js was loaded successfully'); |
|
4399 } |
|
4400 }); |
|
4401 |
|
4402 // Load multiple JS files, execute them in order, and log a message when |
|
4403 // all have finished loading. |
|
4404 var urls = ['foo.js', 'http://example.com/bar.js', 'baz/quux.js']; |
|
4405 |
|
4406 Y.Get.js(urls, function (err) { |
|
4407 if (err) { |
|
4408 Y.log('one or more files failed to load!'); |
|
4409 } else { |
|
4410 Y.log('all files loaded successfully'); |
|
4411 } |
|
4412 }); |
|
4413 |
|
4414 // Specify transaction-level options, which will apply to all requests |
|
4415 // within the transaction. |
|
4416 Y.Get.js(urls, { |
|
4417 attributes: {'class': 'my-js'}, |
|
4418 timeout : 5000 |
|
4419 }); |
|
4420 |
|
4421 // Specify per-request options, which override transaction-level and |
|
4422 // default options. |
|
4423 Y.Get.js([ |
|
4424 {url: 'foo.js', attributes: {id: 'foo'}}, |
|
4425 {url: 'bar.js', attributes: {id: 'bar', charset: 'iso-8859-1'}} |
|
4426 ]); |
|
4427 |
|
4428 @method js |
|
4429 @param {String|Object|Array} urls URL string, request object, or array |
|
4430 of URLs and/or request objects to load. |
|
4431 @param {Object} [options] Options for this transaction. See the |
|
4432 `Y.Get.options` property for a complete list of available options. |
|
4433 @param {Function} [callback] Callback function to be called on completion. |
|
4434 This is a general callback and will be called before any more granular |
|
4435 callbacks (`onSuccess`, `onFailure`, etc.) specified in the `options` |
|
4436 object. |
|
4437 |
|
4438 @param {Array|null} callback.err Array of errors that occurred during |
|
4439 the transaction, or `null` on success. |
|
4440 @param {Get.Transaction} callback.transaction Transaction object. |
|
4441 |
|
4442 @return {Get.Transaction} Transaction object. |
|
4443 @since 3.5.0 |
|
4444 @static |
|
4445 **/ |
|
4446 js: function (urls, options, callback) { |
|
4447 return this._load('js', urls, options, callback); |
|
4448 }, |
|
4449 |
|
4450 /** |
|
4451 Loads one or more CSS and/or JavaScript resources in the same transaction. |
|
4452 |
|
4453 Use this method when you want to load both CSS and JavaScript in a single |
|
4454 transaction and be notified when all requested URLs have finished loading, |
|
4455 regardless of type. |
|
4456 |
|
4457 Behavior and options are the same as for the `css()` and `js()` methods. If |
|
4458 a resource type isn't specified in per-request options or transaction-level |
|
4459 options, Get will guess the file type based on the URL's extension (`.css` |
|
4460 or `.js`, with or without a following query string). If the file type can't |
|
4461 be guessed from the URL, a warning will be logged and Get will assume the |
|
4462 URL is a JavaScript resource. |
|
4463 |
|
4464 @example |
|
4465 |
|
4466 // Load both CSS and JS files in a single transaction, and log a message |
|
4467 // when all files have finished loading. |
|
4468 Y.Get.load(['foo.css', 'bar.js', 'baz.css'], function (err) { |
|
4469 if (err) { |
|
4470 Y.log('one or more files failed to load!'); |
|
4471 } else { |
|
4472 Y.log('all files loaded successfully'); |
|
4473 } |
|
4474 }); |
|
4475 |
|
4476 @method load |
|
4477 @param {String|Object|Array} urls URL string, request object, or array |
|
4478 of URLs and/or request objects to load. |
|
4479 @param {Object} [options] Options for this transaction. See the |
|
4480 `Y.Get.options` property for a complete list of available options. |
|
4481 @param {Function} [callback] Callback function to be called on completion. |
|
4482 This is a general callback and will be called before any more granular |
|
4483 callbacks (`onSuccess`, `onFailure`, etc.) specified in the `options` |
|
4484 object. |
|
4485 |
|
4486 @param {Array|null} err Array of errors that occurred during the |
|
4487 transaction, or `null` on success. |
|
4488 @param {Get.Transaction} Transaction object. |
|
4489 |
|
4490 @return {Get.Transaction} Transaction object. |
|
4491 @since 3.5.0 |
|
4492 @static |
|
4493 **/ |
|
4494 load: function (urls, options, callback) { |
|
4495 return this._load(null, urls, options, callback); |
|
4496 }, |
|
4497 |
|
4498 // -- Protected Methods ---------------------------------------------------- |
|
4499 |
|
4500 /** |
|
4501 Triggers an automatic purge if the purge threshold has been reached. |
|
4502 |
|
4503 @method _autoPurge |
|
4504 @param {Number} threshold Purge threshold to use, in milliseconds. |
|
4505 @protected |
|
4506 @since 3.5.0 |
|
4507 @static |
|
4508 **/ |
|
4509 _autoPurge: function (threshold) { |
|
4510 if (threshold && this._purgeNodes.length >= threshold) { |
|
4511 Y.log('autopurge triggered after ' + this._purgeNodes.length + ' nodes', 'info', 'get'); |
|
4512 this._purge(this._purgeNodes); |
|
4513 } |
|
4514 }, |
|
4515 |
|
4516 /** |
|
4517 Populates the `_env` property with information about the current |
|
4518 environment. |
|
4519 |
|
4520 @method _getEnv |
|
4521 @return {Object} Environment information. |
|
4522 @protected |
|
4523 @since 3.5.0 |
|
4524 @static |
|
4525 **/ |
|
4526 _getEnv: function () { |
|
4527 var doc = Y.config.doc, |
|
4528 ua = Y.UA; |
|
4529 |
|
4530 // Note: some of these checks require browser sniffs since it's not |
|
4531 // feasible to load test files on every pageview just to perform a |
|
4532 // feature test. I'm sorry if this makes you sad. |
|
4533 return (this._env = { |
|
4534 |
|
4535 // True if this is a browser that supports disabling async mode on |
|
4536 // dynamically created script nodes. See |
|
4537 // https://developer.mozilla.org/En/HTML/Element/Script#Attributes |
|
4538 |
|
4539 // IE10 doesn't return true for the MDN feature test, so setting it explicitly, |
|
4540 // because it is async by default, and allows you to disable async by setting it to false |
|
4541 async: (doc && doc.createElement('script').async === true) || (ua.ie >= 10), |
|
4542 |
|
4543 // True if this browser fires an event when a dynamically injected |
|
4544 // link node fails to load. This is currently true for Firefox 9+ |
|
4545 // and WebKit 535.24+ |
|
4546 cssFail: ua.gecko >= 9 || ua.compareVersions(ua.webkit, 535.24) >= 0, |
|
4547 |
|
4548 // True if this browser fires an event when a dynamically injected |
|
4549 // link node finishes loading. This is currently true for IE, Opera, |
|
4550 // Firefox 9+, and WebKit 535.24+. Note that IE versions <9 fire the |
|
4551 // DOM 0 "onload" event, but not "load". All versions of IE fire |
|
4552 // "onload". |
|
4553 // davglass: Seems that Chrome on Android needs this to be false. |
|
4554 cssLoad: ( |
|
4555 (!ua.gecko && !ua.webkit) || ua.gecko >= 9 || |
|
4556 ua.compareVersions(ua.webkit, 535.24) >= 0 |
|
4557 ) && !(ua.chrome && ua.chrome <= 18), |
|
4558 |
|
4559 // True if this browser preserves script execution order while |
|
4560 // loading scripts in parallel as long as the script node's `async` |
|
4561 // attribute is set to false to explicitly disable async execution. |
|
4562 preservesScriptOrder: !!(ua.gecko || ua.opera || (ua.ie && ua.ie >= 10)) |
|
4563 }); |
|
4564 }, |
|
4565 |
|
4566 _getTransaction: function (urls, options) { |
|
4567 var requests = [], |
|
4568 i, len, req, url; |
|
4569 |
|
4570 if (!Lang.isArray(urls)) { |
|
4571 urls = [urls]; |
|
4572 } |
|
4573 |
|
4574 options = Y.merge(this.options, options); |
|
4575 |
|
4576 // Clone the attributes object so we don't end up modifying it by ref. |
|
4577 options.attributes = Y.merge(this.options.attributes, |
|
4578 options.attributes); |
|
4579 |
|
4580 for (i = 0, len = urls.length; i < len; ++i) { |
|
4581 url = urls[i]; |
|
4582 req = {attributes: {}}; |
|
4583 |
|
4584 // If `url` is a string, we create a URL object for it, then mix in |
|
4585 // global options and request-specific options. If it's an object |
|
4586 // with a "url" property, we assume it's a request object containing |
|
4587 // URL-specific options. |
|
4588 if (typeof url === 'string') { |
|
4589 req.url = url; |
|
4590 } else if (url.url) { |
|
4591 // URL-specific options override both global defaults and |
|
4592 // request-specific options. |
|
4593 Y.mix(req, url, false, null, 0, true); |
|
4594 url = url.url; // Make url a string so we can use it later. |
|
4595 } else { |
|
4596 Y.log('URL must be a string or an object with a `url` property.', 'error', 'get'); |
|
4597 continue; |
|
4598 } |
|
4599 |
|
4600 Y.mix(req, options, false, null, 0, true); |
|
4601 |
|
4602 // If we didn't get an explicit type for this URL either in the |
|
4603 // request options or the URL-specific options, try to determine |
|
4604 // one from the file extension. |
|
4605 if (!req.type) { |
|
4606 if (this.REGEX_CSS.test(url)) { |
|
4607 req.type = 'css'; |
|
4608 } else { |
|
4609 if (!this.REGEX_JS.test(url)) { |
|
4610 Y.log("Can't guess file type from URL. Assuming JS: " + url, 'warn', 'get'); |
|
4611 } |
|
4612 |
|
4613 req.type = 'js'; |
|
4614 } |
|
4615 } |
|
4616 |
|
4617 // Mix in type-specific default options, but don't overwrite any |
|
4618 // options that have already been set. |
|
4619 Y.mix(req, req.type === 'js' ? this.jsOptions : this.cssOptions, |
|
4620 false, null, 0, true); |
|
4621 |
|
4622 // Give the node an id attribute if it doesn't already have one. |
|
4623 req.attributes.id || (req.attributes.id = Y.guid()); |
|
4624 |
|
4625 // Backcompat for <3.5.0 behavior. |
|
4626 if (req.win) { |
|
4627 Y.log('The `win` option is deprecated as of 3.5.0. Use `doc` instead.', 'warn', 'get'); |
|
4628 req.doc = req.win.document; |
|
4629 } else { |
|
4630 req.win = req.doc.defaultView || req.doc.parentWindow; |
|
4631 } |
|
4632 |
|
4633 if (req.charset) { |
|
4634 Y.log('The `charset` option is deprecated as of 3.5.0. Set `attributes.charset` instead.', 'warn', 'get'); |
|
4635 req.attributes.charset = req.charset; |
|
4636 } |
|
4637 |
|
4638 requests.push(req); |
|
4639 } |
|
4640 |
|
4641 return new Transaction(requests, options); |
|
4642 }, |
|
4643 |
|
4644 _load: function (type, urls, options, callback) { |
|
4645 var transaction; |
|
4646 |
|
4647 // Allow callback as third param. |
|
4648 if (typeof options === 'function') { |
|
4649 callback = options; |
|
4650 options = {}; |
|
4651 } |
|
4652 |
|
4653 options || (options = {}); |
|
4654 options.type = type; |
|
4655 |
|
4656 options._onFinish = Get._onTransactionFinish; |
|
4657 |
|
4658 if (!this._env) { |
|
4659 this._getEnv(); |
|
4660 } |
|
4661 |
|
4662 transaction = this._getTransaction(urls, options); |
|
4663 |
|
4664 this._queue.push({ |
|
4665 callback : callback, |
|
4666 transaction: transaction |
|
4667 }); |
|
4668 |
|
4669 this._next(); |
|
4670 |
|
4671 return transaction; |
|
4672 }, |
|
4673 |
|
4674 _onTransactionFinish : function() { |
|
4675 Get._pending = null; |
|
4676 Get._next(); |
|
4677 }, |
|
4678 |
|
4679 _next: function () { |
|
4680 var item; |
|
4681 |
|
4682 if (this._pending) { |
|
4683 return; |
|
4684 } |
|
4685 |
|
4686 item = this._queue.shift(); |
|
4687 |
|
4688 if (item) { |
|
4689 this._pending = item; |
|
4690 item.transaction.execute(item.callback); |
|
4691 } |
|
4692 }, |
|
4693 |
|
4694 _purge: function (nodes) { |
|
4695 var purgeNodes = this._purgeNodes, |
|
4696 isTransaction = nodes !== purgeNodes, |
|
4697 index, node; |
|
4698 |
|
4699 while (node = nodes.pop()) { // assignment |
|
4700 // Don't purge nodes that haven't finished loading (or errored out), |
|
4701 // since this can hang the transaction. |
|
4702 if (!node._yuiget_finished) { |
|
4703 continue; |
|
4704 } |
|
4705 |
|
4706 node.parentNode && node.parentNode.removeChild(node); |
|
4707 |
|
4708 // If this is a transaction-level purge and this node also exists in |
|
4709 // the Get-level _purgeNodes array, we need to remove it from |
|
4710 // _purgeNodes to avoid creating a memory leak. The indexOf lookup |
|
4711 // sucks, but until we get WeakMaps, this is the least troublesome |
|
4712 // way to do this (we can't just hold onto node ids because they may |
|
4713 // not be in the same document). |
|
4714 if (isTransaction) { |
|
4715 index = Y.Array.indexOf(purgeNodes, node); |
|
4716 |
|
4717 if (index > -1) { |
|
4718 purgeNodes.splice(index, 1); |
|
4719 } |
|
4720 } |
|
4721 } |
|
4722 } |
|
4723 }; |
|
4724 |
|
4725 /** |
|
4726 Alias for `js()`. |
|
4727 |
|
4728 @method script |
|
4729 @static |
|
4730 **/ |
|
4731 Get.script = Get.js; |
|
4732 |
|
4733 /** |
|
4734 Represents a Get transaction, which may contain requests for one or more JS or |
|
4735 CSS files. |
|
4736 |
|
4737 This class should not be instantiated manually. Instances will be created and |
|
4738 returned as needed by Y.Get's `css()`, `js()`, and `load()` methods. |
|
4739 |
|
4740 @class Get.Transaction |
|
4741 @constructor |
|
4742 @since 3.5.0 |
|
4743 **/ |
|
4744 Get.Transaction = Transaction = function (requests, options) { |
|
4745 var self = this; |
|
4746 |
|
4747 self.id = Transaction._lastId += 1; |
|
4748 self.data = options.data; |
|
4749 self.errors = []; |
|
4750 self.nodes = []; |
|
4751 self.options = options; |
|
4752 self.requests = requests; |
|
4753 |
|
4754 self._callbacks = []; // callbacks to call after execution finishes |
|
4755 self._queue = []; |
|
4756 self._reqsWaiting = 0; |
|
4757 |
|
4758 // Deprecated pre-3.5.0 properties. |
|
4759 self.tId = self.id; // Use `id` instead. |
|
4760 self.win = options.win || Y.config.win; |
|
4761 }; |
|
4762 |
|
4763 /** |
|
4764 Arbitrary data object associated with this transaction. |
|
4765 |
|
4766 This object comes from the options passed to `Get.css()`, `Get.js()`, or |
|
4767 `Get.load()`, and will be `undefined` if no data object was specified. |
|
4768 |
|
4769 @property {Object} data |
|
4770 **/ |
|
4771 |
|
4772 /** |
|
4773 Array of errors that have occurred during this transaction, if any. |
|
4774 |
|
4775 @since 3.5.0 |
|
4776 @property {Object[]} errors |
|
4777 @property {String} errors.error Error message. |
|
4778 @property {Object} errors.request Request object related to the error. |
|
4779 **/ |
|
4780 |
|
4781 /** |
|
4782 Numeric id for this transaction, unique among all transactions within the same |
|
4783 YUI sandbox in the current pageview. |
|
4784 |
|
4785 @property {Number} id |
|
4786 @since 3.5.0 |
|
4787 **/ |
|
4788 |
|
4789 /** |
|
4790 HTMLElement nodes (native ones, not YUI Node instances) that have been inserted |
|
4791 during the current transaction. |
|
4792 |
|
4793 @property {HTMLElement[]} nodes |
|
4794 **/ |
|
4795 |
|
4796 /** |
|
4797 Options associated with this transaction. |
|
4798 |
|
4799 See `Get.options` for the full list of available options. |
|
4800 |
|
4801 @property {Object} options |
|
4802 @since 3.5.0 |
|
4803 **/ |
|
4804 |
|
4805 /** |
|
4806 Request objects contained in this transaction. Each request object represents |
|
4807 one CSS or JS URL that will be (or has been) requested and loaded into the page. |
|
4808 |
|
4809 @property {Object} requests |
|
4810 @since 3.5.0 |
|
4811 **/ |
|
4812 |
|
4813 /** |
|
4814 Id of the most recent transaction. |
|
4815 |
|
4816 @property _lastId |
|
4817 @type Number |
|
4818 @protected |
|
4819 @static |
|
4820 **/ |
|
4821 Transaction._lastId = 0; |
|
4822 |
|
4823 Transaction.prototype = { |
|
4824 // -- Public Properties ---------------------------------------------------- |
|
4825 |
|
4826 /** |
|
4827 Current state of this transaction. One of "new", "executing", or "done". |
|
4828 |
|
4829 @property _state |
|
4830 @type String |
|
4831 @protected |
|
4832 **/ |
|
4833 _state: 'new', // "new", "executing", or "done" |
|
4834 |
|
4835 // -- Public Methods ------------------------------------------------------- |
|
4836 |
|
4837 /** |
|
4838 Aborts this transaction. |
|
4839 |
|
4840 This will cause the transaction's `onFailure` callback to be called and |
|
4841 will prevent any new script and link nodes from being added to the document, |
|
4842 but any resources that have already been requested will continue loading |
|
4843 (there's no safe way to prevent this, unfortunately). |
|
4844 |
|
4845 @method abort |
|
4846 @param {String} [msg="Aborted."] Optional message to use in the `errors` |
|
4847 array describing why the transaction was aborted. |
|
4848 **/ |
|
4849 abort: function (msg) { |
|
4850 this._pending = null; |
|
4851 this._pendingCSS = null; |
|
4852 this._pollTimer = clearTimeout(this._pollTimer); |
|
4853 this._queue = []; |
|
4854 this._reqsWaiting = 0; |
|
4855 |
|
4856 this.errors.push({error: msg || 'Aborted'}); |
|
4857 this._finish(); |
|
4858 }, |
|
4859 |
|
4860 /** |
|
4861 Begins execting the transaction. |
|
4862 |
|
4863 There's usually no reason to call this manually, since Get will call it |
|
4864 automatically when other pending transactions have finished. If you really |
|
4865 want to execute your transaction before Get does, you can, but be aware that |
|
4866 this transaction's scripts may end up executing before the scripts in other |
|
4867 pending transactions. |
|
4868 |
|
4869 If the transaction is already executing, the specified callback (if any) |
|
4870 will be queued and called after execution finishes. If the transaction has |
|
4871 already finished, the callback will be called immediately (the transaction |
|
4872 will not be executed again). |
|
4873 |
|
4874 @method execute |
|
4875 @param {Function} callback Callback function to execute after all requests |
|
4876 in the transaction are complete, or after the transaction is aborted. |
|
4877 **/ |
|
4878 execute: function (callback) { |
|
4879 var self = this, |
|
4880 requests = self.requests, |
|
4881 state = self._state, |
|
4882 i, len, queue, req; |
|
4883 |
|
4884 if (state === 'done') { |
|
4885 callback && callback(self.errors.length ? self.errors : null, self); |
|
4886 return; |
|
4887 } else { |
|
4888 callback && self._callbacks.push(callback); |
|
4889 |
|
4890 if (state === 'executing') { |
|
4891 return; |
|
4892 } |
|
4893 } |
|
4894 |
|
4895 self._state = 'executing'; |
|
4896 self._queue = queue = []; |
|
4897 |
|
4898 if (self.options.timeout) { |
|
4899 self._timeout = setTimeout(function () { |
|
4900 self.abort('Timeout'); |
|
4901 }, self.options.timeout); |
|
4902 } |
|
4903 |
|
4904 self._reqsWaiting = requests.length; |
|
4905 |
|
4906 for (i = 0, len = requests.length; i < len; ++i) { |
|
4907 req = requests[i]; |
|
4908 |
|
4909 if (req.async || req.type === 'css') { |
|
4910 // No need to queue CSS or fully async JS. |
|
4911 self._insert(req); |
|
4912 } else { |
|
4913 queue.push(req); |
|
4914 } |
|
4915 } |
|
4916 |
|
4917 self._next(); |
|
4918 }, |
|
4919 |
|
4920 /** |
|
4921 Manually purges any `<script>` or `<link>` nodes this transaction has |
|
4922 created. |
|
4923 |
|
4924 Be careful when purging a transaction that contains CSS requests, since |
|
4925 removing `<link>` nodes will also remove any styles they applied. |
|
4926 |
|
4927 @method purge |
|
4928 **/ |
|
4929 purge: function () { |
|
4930 Get._purge(this.nodes); |
|
4931 }, |
|
4932 |
|
4933 // -- Protected Methods ---------------------------------------------------- |
|
4934 _createNode: function (name, attrs, doc) { |
|
4935 var node = doc.createElement(name), |
|
4936 attr, testEl; |
|
4937 |
|
4938 if (!CUSTOM_ATTRS) { |
|
4939 // IE6 and IE7 expect property names rather than attribute names for |
|
4940 // certain attributes. Rather than sniffing, we do a quick feature |
|
4941 // test the first time _createNode() runs to determine whether we |
|
4942 // need to provide a workaround. |
|
4943 testEl = doc.createElement('div'); |
|
4944 testEl.setAttribute('class', 'a'); |
|
4945 |
|
4946 CUSTOM_ATTRS = testEl.className === 'a' ? {} : { |
|
4947 'for' : 'htmlFor', |
|
4948 'class': 'className' |
|
4949 }; |
|
4950 } |
|
4951 |
|
4952 for (attr in attrs) { |
|
4953 if (attrs.hasOwnProperty(attr)) { |
|
4954 node.setAttribute(CUSTOM_ATTRS[attr] || attr, attrs[attr]); |
|
4955 } |
|
4956 } |
|
4957 |
|
4958 return node; |
|
4959 }, |
|
4960 |
|
4961 _finish: function () { |
|
4962 var errors = this.errors.length ? this.errors : null, |
|
4963 options = this.options, |
|
4964 thisObj = options.context || this, |
|
4965 data, i, len; |
|
4966 |
|
4967 if (this._state === 'done') { |
|
4968 return; |
|
4969 } |
|
4970 |
|
4971 this._state = 'done'; |
|
4972 |
|
4973 for (i = 0, len = this._callbacks.length; i < len; ++i) { |
|
4974 this._callbacks[i].call(thisObj, errors, this); |
|
4975 } |
|
4976 |
|
4977 data = this._getEventData(); |
|
4978 |
|
4979 if (errors) { |
|
4980 if (options.onTimeout && errors[errors.length - 1].error === 'Timeout') { |
|
4981 options.onTimeout.call(thisObj, data); |
|
4982 } |
|
4983 |
|
4984 if (options.onFailure) { |
|
4985 options.onFailure.call(thisObj, data); |
|
4986 } |
|
4987 } else if (options.onSuccess) { |
|
4988 options.onSuccess.call(thisObj, data); |
|
4989 } |
|
4990 |
|
4991 if (options.onEnd) { |
|
4992 options.onEnd.call(thisObj, data); |
|
4993 } |
|
4994 |
|
4995 if (options._onFinish) { |
|
4996 options._onFinish(); |
|
4997 } |
|
4998 }, |
|
4999 |
|
5000 _getEventData: function (req) { |
|
5001 if (req) { |
|
5002 // This merge is necessary for backcompat. I hate it. |
|
5003 return Y.merge(this, { |
|
5004 abort : this.abort, // have to copy these because the prototype isn't preserved |
|
5005 purge : this.purge, |
|
5006 request: req, |
|
5007 url : req.url, |
|
5008 win : req.win |
|
5009 }); |
|
5010 } else { |
|
5011 return this; |
|
5012 } |
|
5013 }, |
|
5014 |
|
5015 _getInsertBefore: function (req) { |
|
5016 var doc = req.doc, |
|
5017 el = req.insertBefore, |
|
5018 cache, docStamp; |
|
5019 |
|
5020 if (el) { |
|
5021 return typeof el === 'string' ? doc.getElementById(el) : el; |
|
5022 } |
|
5023 |
|
5024 cache = Get._insertCache; |
|
5025 docStamp = Y.stamp(doc); |
|
5026 |
|
5027 if ((el = cache[docStamp])) { // assignment |
|
5028 return el; |
|
5029 } |
|
5030 |
|
5031 // Inserting before a <base> tag apparently works around an IE bug |
|
5032 // (according to a comment from pre-3.5.0 Y.Get), but I'm not sure what |
|
5033 // bug that is, exactly. Better safe than sorry? |
|
5034 if ((el = doc.getElementsByTagName('base')[0])) { // assignment |
|
5035 return (cache[docStamp] = el); |
|
5036 } |
|
5037 |
|
5038 // Look for a <head> element. |
|
5039 el = doc.head || doc.getElementsByTagName('head')[0]; |
|
5040 |
|
5041 if (el) { |
|
5042 // Create a marker node at the end of <head> to use as an insertion |
|
5043 // point. Inserting before this node will ensure that all our CSS |
|
5044 // gets inserted in the correct order, to maintain style precedence. |
|
5045 el.appendChild(doc.createTextNode('')); |
|
5046 return (cache[docStamp] = el.lastChild); |
|
5047 } |
|
5048 |
|
5049 // If all else fails, just insert before the first script node on the |
|
5050 // page, which is virtually guaranteed to exist. |
|
5051 return (cache[docStamp] = doc.getElementsByTagName('script')[0]); |
|
5052 }, |
|
5053 |
|
5054 _insert: function (req) { |
|
5055 var env = Get._env, |
|
5056 insertBefore = this._getInsertBefore(req), |
|
5057 isScript = req.type === 'js', |
|
5058 node = req.node, |
|
5059 self = this, |
|
5060 ua = Y.UA, |
|
5061 cssTimeout, nodeType; |
|
5062 |
|
5063 if (!node) { |
|
5064 if (isScript) { |
|
5065 nodeType = 'script'; |
|
5066 } else if (!env.cssLoad && ua.gecko) { |
|
5067 nodeType = 'style'; |
|
5068 } else { |
|
5069 nodeType = 'link'; |
|
5070 } |
|
5071 |
|
5072 node = req.node = this._createNode(nodeType, req.attributes, |
|
5073 req.doc); |
|
5074 } |
|
5075 |
|
5076 function onError() { |
|
5077 self._progress('Failed to load ' + req.url, req); |
|
5078 } |
|
5079 |
|
5080 function onLoad() { |
|
5081 if (cssTimeout) { |
|
5082 clearTimeout(cssTimeout); |
|
5083 } |
|
5084 |
|
5085 self._progress(null, req); |
|
5086 } |
|
5087 |
|
5088 // Deal with script asynchronicity. |
|
5089 if (isScript) { |
|
5090 node.setAttribute('src', req.url); |
|
5091 |
|
5092 if (req.async) { |
|
5093 // Explicitly indicate that we want the browser to execute this |
|
5094 // script asynchronously. This is necessary for older browsers |
|
5095 // like Firefox <4. |
|
5096 node.async = true; |
|
5097 } else { |
|
5098 if (env.async) { |
|
5099 // This browser treats injected scripts as async by default |
|
5100 // (standard HTML5 behavior) but asynchronous loading isn't |
|
5101 // desired, so tell the browser not to mark this script as |
|
5102 // async. |
|
5103 node.async = false; |
|
5104 } |
|
5105 |
|
5106 // If this browser doesn't preserve script execution order based |
|
5107 // on insertion order, we'll need to avoid inserting other |
|
5108 // scripts until this one finishes loading. |
|
5109 if (!env.preservesScriptOrder) { |
|
5110 this._pending = req; |
|
5111 } |
|
5112 } |
|
5113 } else { |
|
5114 if (!env.cssLoad && ua.gecko) { |
|
5115 // In Firefox <9, we can import the requested URL into a <style> |
|
5116 // node and poll for the existence of node.sheet.cssRules. This |
|
5117 // gives us a reliable way to determine CSS load completion that |
|
5118 // also works for cross-domain stylesheets. |
|
5119 // |
|
5120 // Props to Zach Leatherman for calling my attention to this |
|
5121 // technique. |
|
5122 node.innerHTML = (req.attributes.charset ? |
|
5123 '@charset "' + req.attributes.charset + '";' : '') + |
|
5124 '@import "' + req.url + '";'; |
|
5125 } else { |
|
5126 node.setAttribute('href', req.url); |
|
5127 } |
|
5128 } |
|
5129 |
|
5130 // Inject the node. |
|
5131 if (isScript && ua.ie && (ua.ie < 9 || (document.documentMode && document.documentMode < 9))) { |
|
5132 // Script on IE < 9, and IE 9+ when in IE 8 or older modes, including quirks mode. |
|
5133 node.onreadystatechange = function () { |
|
5134 if (/loaded|complete/.test(node.readyState)) { |
|
5135 node.onreadystatechange = null; |
|
5136 onLoad(); |
|
5137 } |
|
5138 }; |
|
5139 } else if (!isScript && !env.cssLoad) { |
|
5140 // CSS on Firefox <9 or WebKit. |
|
5141 this._poll(req); |
|
5142 } else { |
|
5143 // Script or CSS on everything else. Using DOM 0 events because that |
|
5144 // evens the playing field with older IEs. |
|
5145 |
|
5146 if (ua.ie >= 10) { |
|
5147 |
|
5148 // We currently need to introduce a timeout for IE10, since it |
|
5149 // calls onerror/onload synchronously for 304s - messing up existing |
|
5150 // program flow. |
|
5151 |
|
5152 // Remove this block if the following bug gets fixed by GA |
|
5153 /*jshint maxlen: 1500 */ |
|
5154 // https://connect.microsoft.com/IE/feedback/details/763871/dynamically-loaded-scripts-with-304s-responses-interrupt-the-currently-executing-js-thread-onload |
|
5155 node.onerror = function() { setTimeout(onError, 0); }; |
|
5156 node.onload = function() { setTimeout(onLoad, 0); }; |
|
5157 } else { |
|
5158 node.onerror = onError; |
|
5159 node.onload = onLoad; |
|
5160 } |
|
5161 |
|
5162 // If this browser doesn't fire an event when CSS fails to load, |
|
5163 // fail after a timeout to avoid blocking the transaction queue. |
|
5164 if (!env.cssFail && !isScript) { |
|
5165 cssTimeout = setTimeout(onError, req.timeout || 3000); |
|
5166 } |
|
5167 } |
|
5168 |
|
5169 this.nodes.push(node); |
|
5170 insertBefore.parentNode.insertBefore(node, insertBefore); |
|
5171 }, |
|
5172 |
|
5173 _next: function () { |
|
5174 if (this._pending) { |
|
5175 return; |
|
5176 } |
|
5177 |
|
5178 // If there are requests in the queue, insert the next queued request. |
|
5179 // Otherwise, if we're waiting on already-inserted requests to finish, |
|
5180 // wait longer. If there are no queued requests and we're not waiting |
|
5181 // for anything to load, then we're done! |
|
5182 if (this._queue.length) { |
|
5183 this._insert(this._queue.shift()); |
|
5184 } else if (!this._reqsWaiting) { |
|
5185 this._finish(); |
|
5186 } |
|
5187 }, |
|
5188 |
|
5189 _poll: function (newReq) { |
|
5190 var self = this, |
|
5191 pendingCSS = self._pendingCSS, |
|
5192 isWebKit = Y.UA.webkit, |
|
5193 i, hasRules, j, nodeHref, req, sheets; |
|
5194 |
|
5195 if (newReq) { |
|
5196 pendingCSS || (pendingCSS = self._pendingCSS = []); |
|
5197 pendingCSS.push(newReq); |
|
5198 |
|
5199 if (self._pollTimer) { |
|
5200 // A poll timeout is already pending, so no need to create a |
|
5201 // new one. |
|
5202 return; |
|
5203 } |
|
5204 } |
|
5205 |
|
5206 self._pollTimer = null; |
|
5207 |
|
5208 // Note: in both the WebKit and Gecko hacks below, a CSS URL that 404s |
|
5209 // will still be treated as a success. There's no good workaround for |
|
5210 // this. |
|
5211 |
|
5212 for (i = 0; i < pendingCSS.length; ++i) { |
|
5213 req = pendingCSS[i]; |
|
5214 |
|
5215 if (isWebKit) { |
|
5216 // Look for a stylesheet matching the pending URL. |
|
5217 sheets = req.doc.styleSheets; |
|
5218 j = sheets.length; |
|
5219 nodeHref = req.node.href; |
|
5220 |
|
5221 while (--j >= 0) { |
|
5222 if (sheets[j].href === nodeHref) { |
|
5223 pendingCSS.splice(i, 1); |
|
5224 i -= 1; |
|
5225 self._progress(null, req); |
|
5226 break; |
|
5227 } |
|
5228 } |
|
5229 } else { |
|
5230 // Many thanks to Zach Leatherman for calling my attention to |
|
5231 // the @import-based cross-domain technique used here, and to |
|
5232 // Oleg Slobodskoi for an earlier same-domain implementation. |
|
5233 // |
|
5234 // See Zach's blog for more details: |
|
5235 // http://www.zachleat.com/web/2010/07/29/load-css-dynamically/ |
|
5236 try { |
|
5237 // We don't really need to store this value since we never |
|
5238 // use it again, but if we don't store it, Closure Compiler |
|
5239 // assumes the code is useless and removes it. |
|
5240 hasRules = !!req.node.sheet.cssRules; |
|
5241 |
|
5242 // If we get here, the stylesheet has loaded. |
|
5243 pendingCSS.splice(i, 1); |
|
5244 i -= 1; |
|
5245 self._progress(null, req); |
|
5246 } catch (ex) { |
|
5247 // An exception means the stylesheet is still loading. |
|
5248 } |
|
5249 } |
|
5250 } |
|
5251 |
|
5252 if (pendingCSS.length) { |
|
5253 self._pollTimer = setTimeout(function () { |
|
5254 self._poll.call(self); |
|
5255 }, self.options.pollInterval); |
|
5256 } |
|
5257 }, |
|
5258 |
|
5259 _progress: function (err, req) { |
|
5260 var options = this.options; |
|
5261 |
|
5262 if (err) { |
|
5263 req.error = err; |
|
5264 |
|
5265 this.errors.push({ |
|
5266 error : err, |
|
5267 request: req |
|
5268 }); |
|
5269 |
|
5270 Y.log(err, 'error', 'get'); |
|
5271 } |
|
5272 |
|
5273 req.node._yuiget_finished = req.finished = true; |
|
5274 |
|
5275 if (options.onProgress) { |
|
5276 options.onProgress.call(options.context || this, |
|
5277 this._getEventData(req)); |
|
5278 } |
|
5279 |
|
5280 if (req.autopurge) { |
|
5281 // Pre-3.5.0 Get always excludes the most recent node from an |
|
5282 // autopurge. I find this odd, but I'm keeping that behavior for |
|
5283 // the sake of backcompat. |
|
5284 Get._autoPurge(this.options.purgethreshold); |
|
5285 Get._purgeNodes.push(req.node); |
|
5286 } |
|
5287 |
|
5288 if (this._pending === req) { |
|
5289 this._pending = null; |
|
5290 } |
|
5291 |
|
5292 this._reqsWaiting -= 1; |
|
5293 |
|
5294 this._next(); |
|
5295 } |
|
5296 }; |
|
5297 |
|
5298 |
|
5299 }, '@VERSION@', {"requires": ["yui-base"]}); |
|
5300 YUI.add('features', function (Y, NAME) { |
|
5301 |
|
5302 var feature_tests = {}; |
|
5303 |
|
5304 /** |
|
5305 Contains the core of YUI's feature test architecture. |
|
5306 @module features |
|
5307 */ |
|
5308 |
|
5309 /** |
|
5310 * Feature detection |
|
5311 * @class Features |
|
5312 * @static |
|
5313 */ |
|
5314 |
|
5315 Y.mix(Y.namespace('Features'), { |
|
5316 |
|
5317 /** |
|
5318 * Object hash of all registered feature tests |
|
5319 * @property tests |
|
5320 * @type Object |
|
5321 */ |
|
5322 tests: feature_tests, |
|
5323 |
|
5324 /** |
|
5325 * Add a test to the system |
|
5326 * |
|
5327 * ``` |
|
5328 * Y.Features.add("load", "1", {}); |
|
5329 * ``` |
|
5330 * |
|
5331 * @method add |
|
5332 * @param {String} cat The category, right now only 'load' is supported |
|
5333 * @param {String} name The number sequence of the test, how it's reported in the URL or config: 1, 2, 3 |
|
5334 * @param {Object} o Object containing test properties |
|
5335 * @param {String} o.name The name of the test |
|
5336 * @param {Function} o.test The test function to execute, the only argument to the function is the `Y` instance |
|
5337 * @param {String} o.trigger The module that triggers this test. |
|
5338 */ |
|
5339 add: function(cat, name, o) { |
|
5340 feature_tests[cat] = feature_tests[cat] || {}; |
|
5341 feature_tests[cat][name] = o; |
|
5342 }, |
|
5343 /** |
|
5344 * Execute all tests of a given category and return the serialized results |
|
5345 * |
|
5346 * ``` |
|
5347 * caps=1:1;2:1;3:0 |
|
5348 * ``` |
|
5349 * @method all |
|
5350 * @param {String} cat The category to execute |
|
5351 * @param {Array} args The arguments to pass to the test function |
|
5352 * @return {String} A semi-colon separated string of tests and their success/failure: 1:1;2:1;3:0 |
|
5353 */ |
|
5354 all: function(cat, args) { |
|
5355 var cat_o = feature_tests[cat], |
|
5356 // results = {}; |
|
5357 result = []; |
|
5358 if (cat_o) { |
|
5359 Y.Object.each(cat_o, function(v, k) { |
|
5360 result.push(k + ':' + (Y.Features.test(cat, k, args) ? 1 : 0)); |
|
5361 }); |
|
5362 } |
|
5363 |
|
5364 return (result.length) ? result.join(';') : ''; |
|
5365 }, |
|
5366 /** |
|
5367 * Run a sepecific test and return a Boolean response. |
|
5368 * |
|
5369 * ``` |
|
5370 * Y.Features.test("load", "1"); |
|
5371 * ``` |
|
5372 * |
|
5373 * @method test |
|
5374 * @param {String} cat The category of the test to run |
|
5375 * @param {String} name The name of the test to run |
|
5376 * @param {Array} args The arguments to pass to the test function |
|
5377 * @return {Boolean} True or false if the test passed/failed. |
|
5378 */ |
|
5379 test: function(cat, name, args) { |
|
5380 args = args || []; |
|
5381 var result, ua, test, |
|
5382 cat_o = feature_tests[cat], |
|
5383 feature = cat_o && cat_o[name]; |
|
5384 |
|
5385 if (!feature) { |
|
5386 Y.log('Feature test ' + cat + ', ' + name + ' not found'); |
|
5387 } else { |
|
5388 |
|
5389 result = feature.result; |
|
5390 |
|
5391 if (Y.Lang.isUndefined(result)) { |
|
5392 |
|
5393 ua = feature.ua; |
|
5394 if (ua) { |
|
5395 result = (Y.UA[ua]); |
|
5396 } |
|
5397 |
|
5398 test = feature.test; |
|
5399 if (test && ((!ua) || result)) { |
|
5400 result = test.apply(Y, args); |
|
5401 } |
|
5402 |
|
5403 feature.result = result; |
|
5404 } |
|
5405 } |
|
5406 |
|
5407 return result; |
|
5408 } |
|
5409 }); |
|
5410 |
|
5411 // Y.Features.add("load", "1", {}); |
|
5412 // Y.Features.test("load", "1"); |
|
5413 // caps=1:1;2:0;3:1; |
|
5414 |
|
5415 /* This file is auto-generated by (yogi loader --yes --mix --start ../) */ |
|
5416 /*jshint maxlen:900, eqeqeq: false */ |
|
5417 var add = Y.Features.add; |
|
5418 // app-transitions-native |
|
5419 add('load', '0', { |
|
5420 "name": "app-transitions-native", |
|
5421 "test": function (Y) { |
|
5422 var doc = Y.config.doc, |
|
5423 node = doc ? doc.documentElement : null; |
|
5424 |
|
5425 if (node && node.style) { |
|
5426 return ('MozTransition' in node.style || 'WebkitTransition' in node.style || 'transition' in node.style); |
|
5427 } |
|
5428 |
|
5429 return false; |
|
5430 }, |
|
5431 "trigger": "app-transitions" |
|
5432 }); |
|
5433 // autocomplete-list-keys |
|
5434 add('load', '1', { |
|
5435 "name": "autocomplete-list-keys", |
|
5436 "test": function (Y) { |
|
5437 // Only add keyboard support to autocomplete-list if this doesn't appear to |
|
5438 // be an iOS or Android-based mobile device. |
|
5439 // |
|
5440 // There's currently no feasible way to actually detect whether a device has |
|
5441 // a hardware keyboard, so this sniff will have to do. It can easily be |
|
5442 // overridden by manually loading the autocomplete-list-keys module. |
|
5443 // |
|
5444 // Worth noting: even though iOS supports bluetooth keyboards, Mobile Safari |
|
5445 // doesn't fire the keyboard events used by AutoCompleteList, so there's |
|
5446 // no point loading the -keys module even when a bluetooth keyboard may be |
|
5447 // available. |
|
5448 return !(Y.UA.ios || Y.UA.android); |
|
5449 }, |
|
5450 "trigger": "autocomplete-list" |
|
5451 }); |
|
5452 // dd-gestures |
|
5453 add('load', '2', { |
|
5454 "name": "dd-gestures", |
|
5455 "trigger": "dd-drag", |
|
5456 "ua": "touchEnabled" |
|
5457 }); |
|
5458 // dom-style-ie |
|
5459 add('load', '3', { |
|
5460 "name": "dom-style-ie", |
|
5461 "test": function (Y) { |
|
5462 |
|
5463 var testFeature = Y.Features.test, |
|
5464 addFeature = Y.Features.add, |
|
5465 WINDOW = Y.config.win, |
|
5466 DOCUMENT = Y.config.doc, |
|
5467 DOCUMENT_ELEMENT = 'documentElement', |
|
5468 ret = false; |
|
5469 |
|
5470 addFeature('style', 'computedStyle', { |
|
5471 test: function() { |
|
5472 return WINDOW && 'getComputedStyle' in WINDOW; |
|
5473 } |
|
5474 }); |
|
5475 |
|
5476 addFeature('style', 'opacity', { |
|
5477 test: function() { |
|
5478 return DOCUMENT && 'opacity' in DOCUMENT[DOCUMENT_ELEMENT].style; |
|
5479 } |
|
5480 }); |
|
5481 |
|
5482 ret = (!testFeature('style', 'opacity') && |
|
5483 !testFeature('style', 'computedStyle')); |
|
5484 |
|
5485 return ret; |
|
5486 }, |
|
5487 "trigger": "dom-style" |
|
5488 }); |
|
5489 // editor-para-ie |
|
5490 add('load', '4', { |
|
5491 "name": "editor-para-ie", |
|
5492 "trigger": "editor-para", |
|
5493 "ua": "ie", |
|
5494 "when": "instead" |
|
5495 }); |
|
5496 // event-base-ie |
|
5497 add('load', '5', { |
|
5498 "name": "event-base-ie", |
|
5499 "test": function(Y) { |
|
5500 var imp = Y.config.doc && Y.config.doc.implementation; |
|
5501 return (imp && (!imp.hasFeature('Events', '2.0'))); |
|
5502 }, |
|
5503 "trigger": "node-base" |
|
5504 }); |
|
5505 // graphics-canvas |
|
5506 add('load', '6', { |
|
5507 "name": "graphics-canvas", |
|
5508 "test": function(Y) { |
|
5509 var DOCUMENT = Y.config.doc, |
|
5510 useCanvas = Y.config.defaultGraphicEngine && Y.config.defaultGraphicEngine == "canvas", |
|
5511 canvas = DOCUMENT && DOCUMENT.createElement("canvas"), |
|
5512 svg = (DOCUMENT && DOCUMENT.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1")); |
|
5513 return (!svg || useCanvas) && (canvas && canvas.getContext && canvas.getContext("2d")); |
|
5514 }, |
|
5515 "trigger": "graphics" |
|
5516 }); |
|
5517 // graphics-canvas-default |
|
5518 add('load', '7', { |
|
5519 "name": "graphics-canvas-default", |
|
5520 "test": function(Y) { |
|
5521 var DOCUMENT = Y.config.doc, |
|
5522 useCanvas = Y.config.defaultGraphicEngine && Y.config.defaultGraphicEngine == "canvas", |
|
5523 canvas = DOCUMENT && DOCUMENT.createElement("canvas"), |
|
5524 svg = (DOCUMENT && DOCUMENT.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1")); |
|
5525 return (!svg || useCanvas) && (canvas && canvas.getContext && canvas.getContext("2d")); |
|
5526 }, |
|
5527 "trigger": "graphics" |
|
5528 }); |
|
5529 // graphics-svg |
|
5530 add('load', '8', { |
|
5531 "name": "graphics-svg", |
|
5532 "test": function(Y) { |
|
5533 var DOCUMENT = Y.config.doc, |
|
5534 useSVG = !Y.config.defaultGraphicEngine || Y.config.defaultGraphicEngine != "canvas", |
|
5535 canvas = DOCUMENT && DOCUMENT.createElement("canvas"), |
|
5536 svg = (DOCUMENT && DOCUMENT.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1")); |
|
5537 |
|
5538 return svg && (useSVG || !canvas); |
|
5539 }, |
|
5540 "trigger": "graphics" |
|
5541 }); |
|
5542 // graphics-svg-default |
|
5543 add('load', '9', { |
|
5544 "name": "graphics-svg-default", |
|
5545 "test": function(Y) { |
|
5546 var DOCUMENT = Y.config.doc, |
|
5547 useSVG = !Y.config.defaultGraphicEngine || Y.config.defaultGraphicEngine != "canvas", |
|
5548 canvas = DOCUMENT && DOCUMENT.createElement("canvas"), |
|
5549 svg = (DOCUMENT && DOCUMENT.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1")); |
|
5550 |
|
5551 return svg && (useSVG || !canvas); |
|
5552 }, |
|
5553 "trigger": "graphics" |
|
5554 }); |
|
5555 // graphics-vml |
|
5556 add('load', '10', { |
|
5557 "name": "graphics-vml", |
|
5558 "test": function(Y) { |
|
5559 var DOCUMENT = Y.config.doc, |
|
5560 canvas = DOCUMENT && DOCUMENT.createElement("canvas"); |
|
5561 return (DOCUMENT && !DOCUMENT.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1") && (!canvas || !canvas.getContext || !canvas.getContext("2d"))); |
|
5562 }, |
|
5563 "trigger": "graphics" |
|
5564 }); |
|
5565 // graphics-vml-default |
|
5566 add('load', '11', { |
|
5567 "name": "graphics-vml-default", |
|
5568 "test": function(Y) { |
|
5569 var DOCUMENT = Y.config.doc, |
|
5570 canvas = DOCUMENT && DOCUMENT.createElement("canvas"); |
|
5571 return (DOCUMENT && !DOCUMENT.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1") && (!canvas || !canvas.getContext || !canvas.getContext("2d"))); |
|
5572 }, |
|
5573 "trigger": "graphics" |
|
5574 }); |
|
5575 // history-hash-ie |
|
5576 add('load', '12', { |
|
5577 "name": "history-hash-ie", |
|
5578 "test": function (Y) { |
|
5579 var docMode = Y.config.doc && Y.config.doc.documentMode; |
|
5580 |
|
5581 return Y.UA.ie && (!('onhashchange' in Y.config.win) || |
|
5582 !docMode || docMode < 8); |
|
5583 }, |
|
5584 "trigger": "history-hash" |
|
5585 }); |
|
5586 // io-nodejs |
|
5587 add('load', '13', { |
|
5588 "name": "io-nodejs", |
|
5589 "trigger": "io-base", |
|
5590 "ua": "nodejs" |
|
5591 }); |
|
5592 // json-parse-shim |
|
5593 add('load', '14', { |
|
5594 "name": "json-parse-shim", |
|
5595 "test": function (Y) { |
|
5596 var _JSON = Y.config.global.JSON, |
|
5597 Native = Object.prototype.toString.call(_JSON) === '[object JSON]' && _JSON, |
|
5598 nativeSupport = Y.config.useNativeJSONParse !== false && !!Native; |
|
5599 |
|
5600 function workingNative( k, v ) { |
|
5601 return k === "ok" ? true : v; |
|
5602 } |
|
5603 |
|
5604 // Double check basic functionality. This is mainly to catch early broken |
|
5605 // implementations of the JSON API in Firefox 3.1 beta1 and beta2 |
|
5606 if ( nativeSupport ) { |
|
5607 try { |
|
5608 nativeSupport = ( Native.parse( '{"ok":false}', workingNative ) ).ok; |
|
5609 } |
|
5610 catch ( e ) { |
|
5611 nativeSupport = false; |
|
5612 } |
|
5613 } |
|
5614 |
|
5615 return !nativeSupport; |
|
5616 }, |
|
5617 "trigger": "json-parse" |
|
5618 }); |
|
5619 // json-stringify-shim |
|
5620 add('load', '15', { |
|
5621 "name": "json-stringify-shim", |
|
5622 "test": function (Y) { |
|
5623 var _JSON = Y.config.global.JSON, |
|
5624 Native = Object.prototype.toString.call(_JSON) === '[object JSON]' && _JSON, |
|
5625 nativeSupport = Y.config.useNativeJSONStringify !== false && !!Native; |
|
5626 |
|
5627 // Double check basic native functionality. This is primarily to catch broken |
|
5628 // early JSON API implementations in Firefox 3.1 beta1 and beta2. |
|
5629 if ( nativeSupport ) { |
|
5630 try { |
|
5631 nativeSupport = ( '0' === Native.stringify(0) ); |
|
5632 } catch ( e ) { |
|
5633 nativeSupport = false; |
|
5634 } |
|
5635 } |
|
5636 |
|
5637 |
|
5638 return !nativeSupport; |
|
5639 }, |
|
5640 "trigger": "json-stringify" |
|
5641 }); |
|
5642 // scrollview-base-ie |
|
5643 add('load', '16', { |
|
5644 "name": "scrollview-base-ie", |
|
5645 "trigger": "scrollview-base", |
|
5646 "ua": "ie" |
|
5647 }); |
|
5648 // selector-css2 |
|
5649 add('load', '17', { |
|
5650 "name": "selector-css2", |
|
5651 "test": function (Y) { |
|
5652 var DOCUMENT = Y.config.doc, |
|
5653 ret = DOCUMENT && !('querySelectorAll' in DOCUMENT); |
|
5654 |
|
5655 return ret; |
|
5656 }, |
|
5657 "trigger": "selector" |
|
5658 }); |
|
5659 // transition-timer |
|
5660 add('load', '18', { |
|
5661 "name": "transition-timer", |
|
5662 "test": function (Y) { |
|
5663 var DOCUMENT = Y.config.doc, |
|
5664 node = (DOCUMENT) ? DOCUMENT.documentElement: null, |
|
5665 ret = true; |
|
5666 |
|
5667 if (node && node.style) { |
|
5668 ret = !('MozTransition' in node.style || 'WebkitTransition' in node.style || 'transition' in node.style); |
|
5669 } |
|
5670 |
|
5671 return ret; |
|
5672 }, |
|
5673 "trigger": "transition" |
|
5674 }); |
|
5675 // widget-base-ie |
|
5676 add('load', '19', { |
|
5677 "name": "widget-base-ie", |
|
5678 "trigger": "widget-base", |
|
5679 "ua": "ie" |
|
5680 }); |
|
5681 // yql-jsonp |
|
5682 add('load', '20', { |
|
5683 "name": "yql-jsonp", |
|
5684 "test": function (Y) { |
|
5685 /* Only load the JSONP module when not in nodejs or winjs |
|
5686 TODO Make the winjs module a CORS module |
|
5687 */ |
|
5688 return (!Y.UA.nodejs && !Y.UA.winjs); |
|
5689 }, |
|
5690 "trigger": "yql", |
|
5691 "when": "after" |
|
5692 }); |
|
5693 // yql-nodejs |
|
5694 add('load', '21', { |
|
5695 "name": "yql-nodejs", |
|
5696 "trigger": "yql", |
|
5697 "ua": "nodejs", |
|
5698 "when": "after" |
|
5699 }); |
|
5700 // yql-winjs |
|
5701 add('load', '22', { |
|
5702 "name": "yql-winjs", |
|
5703 "trigger": "yql", |
|
5704 "ua": "winjs", |
|
5705 "when": "after" |
|
5706 }); |
|
5707 |
|
5708 }, '@VERSION@', {"requires": ["yui-base"]}); |
|
5709 YUI.add('intl-base', function (Y, NAME) { |
|
5710 |
|
5711 /** |
|
5712 * The Intl utility provides a central location for managing sets of |
|
5713 * localized resources (strings and formatting patterns). |
|
5714 * |
|
5715 * @class Intl |
|
5716 * @uses EventTarget |
|
5717 * @static |
|
5718 */ |
|
5719 |
|
5720 var SPLIT_REGEX = /[, ]/; |
|
5721 |
|
5722 Y.mix(Y.namespace('Intl'), { |
|
5723 |
|
5724 /** |
|
5725 * Returns the language among those available that |
|
5726 * best matches the preferred language list, using the Lookup |
|
5727 * algorithm of BCP 47. |
|
5728 * If none of the available languages meets the user's preferences, |
|
5729 * then "" is returned. |
|
5730 * Extended language ranges are not supported. |
|
5731 * |
|
5732 * @method lookupBestLang |
|
5733 * @param {String[] | String} preferredLanguages The list of preferred |
|
5734 * languages in descending preference order, represented as BCP 47 |
|
5735 * language tags. A string array or a comma-separated list. |
|
5736 * @param {String[]} availableLanguages The list of languages |
|
5737 * that the application supports, represented as BCP 47 language |
|
5738 * tags. |
|
5739 * |
|
5740 * @return {String} The available language that best matches the |
|
5741 * preferred language list, or "". |
|
5742 * @since 3.1.0 |
|
5743 */ |
|
5744 lookupBestLang: function(preferredLanguages, availableLanguages) { |
|
5745 |
|
5746 var i, language, result, index; |
|
5747 |
|
5748 // check whether the list of available languages contains language; |
|
5749 // if so return it |
|
5750 function scan(language) { |
|
5751 var i; |
|
5752 for (i = 0; i < availableLanguages.length; i += 1) { |
|
5753 if (language.toLowerCase() === |
|
5754 availableLanguages[i].toLowerCase()) { |
|
5755 return availableLanguages[i]; |
|
5756 } |
|
5757 } |
|
5758 } |
|
5759 |
|
5760 if (Y.Lang.isString(preferredLanguages)) { |
|
5761 preferredLanguages = preferredLanguages.split(SPLIT_REGEX); |
|
5762 } |
|
5763 |
|
5764 for (i = 0; i < preferredLanguages.length; i += 1) { |
|
5765 language = preferredLanguages[i]; |
|
5766 if (!language || language === '*') { |
|
5767 continue; |
|
5768 } |
|
5769 // check the fallback sequence for one language |
|
5770 while (language.length > 0) { |
|
5771 result = scan(language); |
|
5772 if (result) { |
|
5773 return result; |
|
5774 } else { |
|
5775 index = language.lastIndexOf('-'); |
|
5776 if (index >= 0) { |
|
5777 language = language.substring(0, index); |
|
5778 // one-character subtags get cut along with the |
|
5779 // following subtag |
|
5780 if (index >= 2 && language.charAt(index - 2) === '-') { |
|
5781 language = language.substring(0, index - 2); |
|
5782 } |
|
5783 } else { |
|
5784 // nothing available for this language |
|
5785 break; |
|
5786 } |
|
5787 } |
|
5788 } |
|
5789 } |
|
5790 |
|
5791 return ''; |
|
5792 } |
|
5793 }); |
|
5794 |
|
5795 |
|
5796 }, '@VERSION@', {"requires": ["yui-base"]}); |
|
5797 YUI.add('yui-log', function (Y, NAME) { |
|
5798 |
|
5799 /** |
|
5800 * Provides console log capability and exposes a custom event for |
|
5801 * console implementations. This module is a `core` YUI module, |
|
5802 * <a href="../classes/YUI.html#method_log">it's documentation is located under the YUI class</a>. |
|
5803 * |
|
5804 * @module yui |
|
5805 * @submodule yui-log |
|
5806 */ |
|
5807 |
|
5808 var INSTANCE = Y, |
|
5809 LOGEVENT = 'yui:log', |
|
5810 UNDEFINED = 'undefined', |
|
5811 LEVELS = { debug: 1, |
|
5812 info: 2, |
|
5813 warn: 4, |
|
5814 error: 8 }; |
|
5815 |
|
5816 /** |
|
5817 * If the 'debug' config is true, a 'yui:log' event will be |
|
5818 * dispatched, which the Console widget and anything else |
|
5819 * can consume. If the 'useBrowserConsole' config is true, it will |
|
5820 * write to the browser console if available. YUI-specific log |
|
5821 * messages will only be present in the -debug versions of the |
|
5822 * JS files. The build system is supposed to remove log statements |
|
5823 * from the raw and minified versions of the files. |
|
5824 * |
|
5825 * @method log |
|
5826 * @for YUI |
|
5827 * @param {String} msg The message to log. |
|
5828 * @param {String} cat The log category for the message. Default |
|
5829 * categories are "info", "warn", "error", time". |
|
5830 * Custom categories can be used as well. (opt). |
|
5831 * @param {String} src The source of the the message (opt). |
|
5832 * @param {boolean} silent If true, the log event won't fire. |
|
5833 * @return {YUI} YUI instance. |
|
5834 */ |
|
5835 INSTANCE.log = function(msg, cat, src, silent) { |
|
5836 var bail, excl, incl, m, f, minlevel, |
|
5837 Y = INSTANCE, |
|
5838 c = Y.config, |
|
5839 publisher = (Y.fire) ? Y : YUI.Env.globalEvents; |
|
5840 // suppress log message if the config is off or the event stack |
|
5841 // or the event call stack contains a consumer of the yui:log event |
|
5842 if (c.debug) { |
|
5843 // apply source filters |
|
5844 src = src || ""; |
|
5845 if (typeof src !== "undefined") { |
|
5846 excl = c.logExclude; |
|
5847 incl = c.logInclude; |
|
5848 if (incl && !(src in incl)) { |
|
5849 bail = 1; |
|
5850 } else if (incl && (src in incl)) { |
|
5851 bail = !incl[src]; |
|
5852 } else if (excl && (src in excl)) { |
|
5853 bail = excl[src]; |
|
5854 } |
|
5855 |
|
5856 // Determine the current minlevel as defined in configuration |
|
5857 Y.config.logLevel = Y.config.logLevel || 'debug'; |
|
5858 minlevel = LEVELS[Y.config.logLevel.toLowerCase()]; |
|
5859 |
|
5860 if (cat in LEVELS && LEVELS[cat] < minlevel) { |
|
5861 // Skip this message if the we don't meet the defined minlevel |
|
5862 bail = 1; |
|
5863 } |
|
5864 } |
|
5865 if (!bail) { |
|
5866 if (c.useBrowserConsole) { |
|
5867 m = (src) ? src + ': ' + msg : msg; |
|
5868 if (Y.Lang.isFunction(c.logFn)) { |
|
5869 c.logFn.call(Y, msg, cat, src); |
|
5870 } else if (typeof console !== UNDEFINED && console.log) { |
|
5871 f = (cat && console[cat] && (cat in LEVELS)) ? cat : 'log'; |
|
5872 console[f](m); |
|
5873 } else if (typeof opera !== UNDEFINED) { |
|
5874 opera.postError(m); |
|
5875 } |
|
5876 } |
|
5877 |
|
5878 if (publisher && !silent) { |
|
5879 |
|
5880 if (publisher === Y && (!publisher.getEvent(LOGEVENT))) { |
|
5881 publisher.publish(LOGEVENT, { |
|
5882 broadcast: 2 |
|
5883 }); |
|
5884 } |
|
5885 |
|
5886 publisher.fire(LOGEVENT, { |
|
5887 msg: msg, |
|
5888 cat: cat, |
|
5889 src: src |
|
5890 }); |
|
5891 } |
|
5892 } |
|
5893 } |
|
5894 |
|
5895 return Y; |
|
5896 }; |
|
5897 |
|
5898 /** |
|
5899 * Write a system message. This message will be preserved in the |
|
5900 * minified and raw versions of the YUI files, unlike log statements. |
|
5901 * @method message |
|
5902 * @for YUI |
|
5903 * @param {String} msg The message to log. |
|
5904 * @param {String} cat The log category for the message. Default |
|
5905 * categories are "info", "warn", "error", time". |
|
5906 * Custom categories can be used as well. (opt). |
|
5907 * @param {String} src The source of the the message (opt). |
|
5908 * @param {boolean} silent If true, the log event won't fire. |
|
5909 * @return {YUI} YUI instance. |
|
5910 */ |
|
5911 INSTANCE.message = function() { |
|
5912 return INSTANCE.log.apply(INSTANCE, arguments); |
|
5913 }; |
|
5914 |
|
5915 |
|
5916 }, '@VERSION@', {"requires": ["yui-base"]}); |
|
5917 YUI.add('yui-later', function (Y, NAME) { |
|
5918 |
|
5919 /** |
|
5920 * Provides a setTimeout/setInterval wrapper. This module is a `core` YUI module, |
|
5921 * <a href="../classes/YUI.html#method_later">it's documentation is located under the YUI class</a>. |
|
5922 * |
|
5923 * @module yui |
|
5924 * @submodule yui-later |
|
5925 */ |
|
5926 |
|
5927 var NO_ARGS = []; |
|
5928 |
|
5929 /** |
|
5930 * Executes the supplied function in the context of the supplied |
|
5931 * object 'when' milliseconds later. Executes the function a |
|
5932 * single time unless periodic is set to true. |
|
5933 * @for YUI |
|
5934 * @method later |
|
5935 * @param when {int} the number of milliseconds to wait until the fn |
|
5936 * is executed. |
|
5937 * @param o the context object. |
|
5938 * @param fn {Function|String} the function to execute or the name of |
|
5939 * the method in the 'o' object to execute. |
|
5940 * @param data [Array] data that is provided to the function. This |
|
5941 * accepts either a single item or an array. If an array is provided, |
|
5942 * the function is executed with one parameter for each array item. |
|
5943 * If you need to pass a single array parameter, it needs to be wrapped |
|
5944 * in an array [myarray]. |
|
5945 * |
|
5946 * Note: native methods in IE may not have the call and apply methods. |
|
5947 * In this case, it will work, but you are limited to four arguments. |
|
5948 * |
|
5949 * @param periodic {boolean} if true, executes continuously at supplied |
|
5950 * interval until canceled. |
|
5951 * @return {object} a timer object. Call the cancel() method on this |
|
5952 * object to stop the timer. |
|
5953 */ |
|
5954 Y.later = function(when, o, fn, data, periodic) { |
|
5955 when = when || 0; |
|
5956 data = (!Y.Lang.isUndefined(data)) ? Y.Array(data) : NO_ARGS; |
|
5957 o = o || Y.config.win || Y; |
|
5958 |
|
5959 var cancelled = false, |
|
5960 method = (o && Y.Lang.isString(fn)) ? o[fn] : fn, |
|
5961 wrapper = function() { |
|
5962 // IE 8- may execute a setInterval callback one last time |
|
5963 // after clearInterval was called, so in order to preserve |
|
5964 // the cancel() === no more runny-run, we have to jump through |
|
5965 // an extra hoop. |
|
5966 if (!cancelled) { |
|
5967 if (!method.apply) { |
|
5968 method(data[0], data[1], data[2], data[3]); |
|
5969 } else { |
|
5970 method.apply(o, data || NO_ARGS); |
|
5971 } |
|
5972 } |
|
5973 }, |
|
5974 id = (periodic) ? setInterval(wrapper, when) : setTimeout(wrapper, when); |
|
5975 |
|
5976 return { |
|
5977 id: id, |
|
5978 interval: periodic, |
|
5979 cancel: function() { |
|
5980 cancelled = true; |
|
5981 if (this.interval) { |
|
5982 clearInterval(id); |
|
5983 } else { |
|
5984 clearTimeout(id); |
|
5985 } |
|
5986 } |
|
5987 }; |
|
5988 }; |
|
5989 |
|
5990 Y.Lang.later = Y.later; |
|
5991 |
|
5992 |
|
5993 |
|
5994 }, '@VERSION@', {"requires": ["yui-base"]}); |
|
5995 YUI.add('yui', function (Y, NAME) {}, '@VERSION@', {"use": ["get", "features", "intl-base", "yui-log", "yui-later"]}); |
|
5996 YUI.add('oop', function (Y, NAME) { |
|
5997 |
|
5998 /** |
|
5999 Adds object inheritance and manipulation utilities to the YUI instance. This |
|
6000 module is required by most YUI components. |
|
6001 |
|
6002 @module oop |
|
6003 **/ |
|
6004 |
|
6005 var L = Y.Lang, |
|
6006 A = Y.Array, |
|
6007 OP = Object.prototype, |
|
6008 CLONE_MARKER = '_~yuim~_', |
|
6009 |
|
6010 hasOwn = OP.hasOwnProperty, |
|
6011 toString = OP.toString; |
|
6012 |
|
6013 function dispatch(o, f, c, proto, action) { |
|
6014 if (o && o[action] && o !== Y) { |
|
6015 return o[action].call(o, f, c); |
|
6016 } else { |
|
6017 switch (A.test(o)) { |
|
6018 case 1: |
|
6019 return A[action](o, f, c); |
|
6020 case 2: |
|
6021 return A[action](Y.Array(o, 0, true), f, c); |
|
6022 default: |
|
6023 return Y.Object[action](o, f, c, proto); |
|
6024 } |
|
6025 } |
|
6026 } |
|
6027 |
|
6028 /** |
|
6029 Augments the _receiver_ with prototype properties from the _supplier_. The |
|
6030 receiver may be a constructor function or an object. The supplier must be a |
|
6031 constructor function. |
|
6032 |
|
6033 If the _receiver_ is an object, then the _supplier_ constructor will be called |
|
6034 immediately after _receiver_ is augmented, with _receiver_ as the `this` object. |
|
6035 |
|
6036 If the _receiver_ is a constructor function, then all prototype methods of |
|
6037 _supplier_ that are copied to _receiver_ will be sequestered, and the |
|
6038 _supplier_ constructor will not be called immediately. The first time any |
|
6039 sequestered method is called on the _receiver_'s prototype, all sequestered |
|
6040 methods will be immediately copied to the _receiver_'s prototype, the |
|
6041 _supplier_'s constructor will be executed, and finally the newly unsequestered |
|
6042 method that was called will be executed. |
|
6043 |
|
6044 This sequestering logic sounds like a bunch of complicated voodoo, but it makes |
|
6045 it cheap to perform frequent augmentation by ensuring that suppliers' |
|
6046 constructors are only called if a supplied method is actually used. If none of |
|
6047 the supplied methods is ever used, then there's no need to take the performance |
|
6048 hit of calling the _supplier_'s constructor. |
|
6049 |
|
6050 @method augment |
|
6051 @param {Function|Object} receiver Object or function to be augmented. |
|
6052 @param {Function} supplier Function that supplies the prototype properties with |
|
6053 which to augment the _receiver_. |
|
6054 @param {Boolean} [overwrite=false] If `true`, properties already on the receiver |
|
6055 will be overwritten if found on the supplier's prototype. |
|
6056 @param {String[]} [whitelist] An array of property names. If specified, |
|
6057 only the whitelisted prototype properties will be applied to the receiver, and |
|
6058 all others will be ignored. |
|
6059 @param {Array|any} [args] Argument or array of arguments to pass to the |
|
6060 supplier's constructor when initializing. |
|
6061 @return {Function} Augmented object. |
|
6062 @for YUI |
|
6063 **/ |
|
6064 Y.augment = function (receiver, supplier, overwrite, whitelist, args) { |
|
6065 var rProto = receiver.prototype, |
|
6066 sequester = rProto && supplier, |
|
6067 sProto = supplier.prototype, |
|
6068 to = rProto || receiver, |
|
6069 |
|
6070 copy, |
|
6071 newPrototype, |
|
6072 replacements, |
|
6073 sequestered, |
|
6074 unsequester; |
|
6075 |
|
6076 args = args ? Y.Array(args) : []; |
|
6077 |
|
6078 if (sequester) { |
|
6079 newPrototype = {}; |
|
6080 replacements = {}; |
|
6081 sequestered = {}; |
|
6082 |
|
6083 copy = function (value, key) { |
|
6084 if (overwrite || !(key in rProto)) { |
|
6085 if (toString.call(value) === '[object Function]') { |
|
6086 sequestered[key] = value; |
|
6087 |
|
6088 newPrototype[key] = replacements[key] = function () { |
|
6089 return unsequester(this, value, arguments); |
|
6090 }; |
|
6091 } else { |
|
6092 newPrototype[key] = value; |
|
6093 } |
|
6094 } |
|
6095 }; |
|
6096 |
|
6097 unsequester = function (instance, fn, fnArgs) { |
|
6098 // Unsequester all sequestered functions. |
|
6099 for (var key in sequestered) { |
|
6100 if (hasOwn.call(sequestered, key) |
|
6101 && instance[key] === replacements[key]) { |
|
6102 |
|
6103 instance[key] = sequestered[key]; |
|
6104 } |
|
6105 } |
|
6106 |
|
6107 // Execute the supplier constructor. |
|
6108 supplier.apply(instance, args); |
|
6109 |
|
6110 // Finally, execute the original sequestered function. |
|
6111 return fn.apply(instance, fnArgs); |
|
6112 }; |
|
6113 |
|
6114 if (whitelist) { |
|
6115 Y.Array.each(whitelist, function (name) { |
|
6116 if (name in sProto) { |
|
6117 copy(sProto[name], name); |
|
6118 } |
|
6119 }); |
|
6120 } else { |
|
6121 Y.Object.each(sProto, copy, null, true); |
|
6122 } |
|
6123 } |
|
6124 |
|
6125 Y.mix(to, newPrototype || sProto, overwrite, whitelist); |
|
6126 |
|
6127 if (!sequester) { |
|
6128 supplier.apply(to, args); |
|
6129 } |
|
6130 |
|
6131 return receiver; |
|
6132 }; |
|
6133 |
|
6134 /** |
|
6135 * Copies object properties from the supplier to the receiver. If the target has |
|
6136 * the property, and the property is an object, the target object will be |
|
6137 * augmented with the supplier's value. |
|
6138 * |
|
6139 * @method aggregate |
|
6140 * @param {Object} receiver Object to receive the augmentation. |
|
6141 * @param {Object} supplier Object that supplies the properties with which to |
|
6142 * augment the receiver. |
|
6143 * @param {Boolean} [overwrite=false] If `true`, properties already on the receiver |
|
6144 * will be overwritten if found on the supplier. |
|
6145 * @param {String[]} [whitelist] Whitelist. If supplied, only properties in this |
|
6146 * list will be applied to the receiver. |
|
6147 * @return {Object} Augmented object. |
|
6148 */ |
|
6149 Y.aggregate = function(r, s, ov, wl) { |
|
6150 return Y.mix(r, s, ov, wl, 0, true); |
|
6151 }; |
|
6152 |
|
6153 /** |
|
6154 * Utility to set up the prototype, constructor and superclass properties to |
|
6155 * support an inheritance strategy that can chain constructors and methods. |
|
6156 * Static members will not be inherited. |
|
6157 * |
|
6158 * @method extend |
|
6159 * @param {function} r the object to modify. |
|
6160 * @param {function} s the object to inherit. |
|
6161 * @param {object} px prototype properties to add/override. |
|
6162 * @param {object} sx static properties to add/override. |
|
6163 * @return {object} the extended object. |
|
6164 */ |
|
6165 Y.extend = function(r, s, px, sx) { |
|
6166 if (!s || !r) { |
|
6167 Y.error('extend failed, verify dependencies'); |
|
6168 } |
|
6169 |
|
6170 var sp = s.prototype, rp = Y.Object(sp); |
|
6171 r.prototype = rp; |
|
6172 |
|
6173 rp.constructor = r; |
|
6174 r.superclass = sp; |
|
6175 |
|
6176 // assign constructor property |
|
6177 if (s != Object && sp.constructor == OP.constructor) { |
|
6178 sp.constructor = s; |
|
6179 } |
|
6180 |
|
6181 // add prototype overrides |
|
6182 if (px) { |
|
6183 Y.mix(rp, px, true); |
|
6184 } |
|
6185 |
|
6186 // add object overrides |
|
6187 if (sx) { |
|
6188 Y.mix(r, sx, true); |
|
6189 } |
|
6190 |
|
6191 return r; |
|
6192 }; |
|
6193 |
|
6194 /** |
|
6195 * Executes the supplied function for each item in |
|
6196 * a collection. Supports arrays, objects, and |
|
6197 * NodeLists |
|
6198 * @method each |
|
6199 * @param {object} o the object to iterate. |
|
6200 * @param {function} f the function to execute. This function |
|
6201 * receives the value, key, and object as parameters. |
|
6202 * @param {object} c the execution context for the function. |
|
6203 * @param {boolean} proto if true, prototype properties are |
|
6204 * iterated on objects. |
|
6205 * @return {YUI} the YUI instance. |
|
6206 */ |
|
6207 Y.each = function(o, f, c, proto) { |
|
6208 return dispatch(o, f, c, proto, 'each'); |
|
6209 }; |
|
6210 |
|
6211 /** |
|
6212 * Executes the supplied function for each item in |
|
6213 * a collection. The operation stops if the function |
|
6214 * returns true. Supports arrays, objects, and |
|
6215 * NodeLists. |
|
6216 * @method some |
|
6217 * @param {object} o the object to iterate. |
|
6218 * @param {function} f the function to execute. This function |
|
6219 * receives the value, key, and object as parameters. |
|
6220 * @param {object} c the execution context for the function. |
|
6221 * @param {boolean} proto if true, prototype properties are |
|
6222 * iterated on objects. |
|
6223 * @return {boolean} true if the function ever returns true, |
|
6224 * false otherwise. |
|
6225 */ |
|
6226 Y.some = function(o, f, c, proto) { |
|
6227 return dispatch(o, f, c, proto, 'some'); |
|
6228 }; |
|
6229 |
|
6230 /** |
|
6231 Deep object/array copy. Function clones are actually wrappers around the |
|
6232 original function. Array-like objects are treated as arrays. Primitives are |
|
6233 returned untouched. Optionally, a function can be provided to handle other data |
|
6234 types, filter keys, validate values, etc. |
|
6235 |
|
6236 **Note:** Cloning a non-trivial object is a reasonably heavy operation, due to |
|
6237 the need to recursively iterate down non-primitive properties. Clone should be |
|
6238 used only when a deep clone down to leaf level properties is explicitly |
|
6239 required. This method will also |
|
6240 |
|
6241 In many cases (for example, when trying to isolate objects used as hashes for |
|
6242 configuration properties), a shallow copy, using `Y.merge()` is normally |
|
6243 sufficient. If more than one level of isolation is required, `Y.merge()` can be |
|
6244 used selectively at each level which needs to be isolated from the original |
|
6245 without going all the way to leaf properties. |
|
6246 |
|
6247 @method clone |
|
6248 @param {object} o what to clone. |
|
6249 @param {boolean} safe if true, objects will not have prototype items from the |
|
6250 source. If false, they will. In this case, the original is initially |
|
6251 protected, but the clone is not completely immune from changes to the source |
|
6252 object prototype. Also, cloned prototype items that are deleted from the |
|
6253 clone will result in the value of the source prototype being exposed. If |
|
6254 operating on a non-safe clone, items should be nulled out rather than |
|
6255 deleted. |
|
6256 @param {function} f optional function to apply to each item in a collection; it |
|
6257 will be executed prior to applying the value to the new object. |
|
6258 Return false to prevent the copy. |
|
6259 @param {object} c optional execution context for f. |
|
6260 @param {object} owner Owner object passed when clone is iterating an object. |
|
6261 Used to set up context for cloned functions. |
|
6262 @param {object} cloned hash of previously cloned objects to avoid multiple |
|
6263 clones. |
|
6264 @return {Array|Object} the cloned object. |
|
6265 **/ |
|
6266 Y.clone = function(o, safe, f, c, owner, cloned) { |
|
6267 var o2, marked, stamp; |
|
6268 |
|
6269 // Does not attempt to clone: |
|
6270 // |
|
6271 // * Non-typeof-object values, "primitive" values don't need cloning. |
|
6272 // |
|
6273 // * YUI instances, cloning complex object like YUI instances is not |
|
6274 // advised, this is like cloning the world. |
|
6275 // |
|
6276 // * DOM nodes (#2528250), common host objects like DOM nodes cannot be |
|
6277 // "subclassed" in Firefox and old versions of IE. Trying to use |
|
6278 // `Object.create()` or `Y.extend()` on a DOM node will throw an error in |
|
6279 // these browsers. |
|
6280 // |
|
6281 // Instad, the passed-in `o` will be return as-is when it matches one of the |
|
6282 // above criteria. |
|
6283 if (!L.isObject(o) || |
|
6284 Y.instanceOf(o, YUI) || |
|
6285 (o.addEventListener || o.attachEvent)) { |
|
6286 |
|
6287 return o; |
|
6288 } |
|
6289 |
|
6290 marked = cloned || {}; |
|
6291 |
|
6292 switch (L.type(o)) { |
|
6293 case 'date': |
|
6294 return new Date(o); |
|
6295 case 'regexp': |
|
6296 // if we do this we need to set the flags too |
|
6297 // return new RegExp(o.source); |
|
6298 return o; |
|
6299 case 'function': |
|
6300 // o2 = Y.bind(o, owner); |
|
6301 // break; |
|
6302 return o; |
|
6303 case 'array': |
|
6304 o2 = []; |
|
6305 break; |
|
6306 default: |
|
6307 |
|
6308 // #2528250 only one clone of a given object should be created. |
|
6309 if (o[CLONE_MARKER]) { |
|
6310 return marked[o[CLONE_MARKER]]; |
|
6311 } |
|
6312 |
|
6313 stamp = Y.guid(); |
|
6314 |
|
6315 o2 = (safe) ? {} : Y.Object(o); |
|
6316 |
|
6317 o[CLONE_MARKER] = stamp; |
|
6318 marked[stamp] = o; |
|
6319 } |
|
6320 |
|
6321 Y.each(o, function(v, k) { |
|
6322 if ((k || k === 0) && (!f || (f.call(c || this, v, k, this, o) !== false))) { |
|
6323 if (k !== CLONE_MARKER) { |
|
6324 if (k == 'prototype') { |
|
6325 // skip the prototype |
|
6326 // } else if (o[k] === o) { |
|
6327 // this[k] = this; |
|
6328 } else { |
|
6329 this[k] = |
|
6330 Y.clone(v, safe, f, c, owner || o, marked); |
|
6331 } |
|
6332 } |
|
6333 } |
|
6334 }, o2); |
|
6335 |
|
6336 if (!cloned) { |
|
6337 Y.Object.each(marked, function(v, k) { |
|
6338 if (v[CLONE_MARKER]) { |
|
6339 try { |
|
6340 delete v[CLONE_MARKER]; |
|
6341 } catch (e) { |
|
6342 v[CLONE_MARKER] = null; |
|
6343 } |
|
6344 } |
|
6345 }, this); |
|
6346 marked = null; |
|
6347 } |
|
6348 |
|
6349 return o2; |
|
6350 }; |
|
6351 |
|
6352 /** |
|
6353 * Returns a function that will execute the supplied function in the |
|
6354 * supplied object's context, optionally adding any additional |
|
6355 * supplied parameters to the beginning of the arguments collection the |
|
6356 * supplied to the function. |
|
6357 * |
|
6358 * @method bind |
|
6359 * @param {Function|String} f the function to bind, or a function name |
|
6360 * to execute on the context object. |
|
6361 * @param {object} c the execution context. |
|
6362 * @param {any} args* 0..n arguments to include before the arguments the |
|
6363 * function is executed with. |
|
6364 * @return {function} the wrapped function. |
|
6365 */ |
|
6366 Y.bind = function(f, c) { |
|
6367 var xargs = arguments.length > 2 ? |
|
6368 Y.Array(arguments, 2, true) : null; |
|
6369 return function() { |
|
6370 var fn = L.isString(f) ? c[f] : f, |
|
6371 args = (xargs) ? |
|
6372 xargs.concat(Y.Array(arguments, 0, true)) : arguments; |
|
6373 return fn.apply(c || fn, args); |
|
6374 }; |
|
6375 }; |
|
6376 |
|
6377 /** |
|
6378 * Returns a function that will execute the supplied function in the |
|
6379 * supplied object's context, optionally adding any additional |
|
6380 * supplied parameters to the end of the arguments the function |
|
6381 * is executed with. |
|
6382 * |
|
6383 * @method rbind |
|
6384 * @param {Function|String} f the function to bind, or a function name |
|
6385 * to execute on the context object. |
|
6386 * @param {object} c the execution context. |
|
6387 * @param {any} args* 0..n arguments to append to the end of |
|
6388 * arguments collection supplied to the function. |
|
6389 * @return {function} the wrapped function. |
|
6390 */ |
|
6391 Y.rbind = function(f, c) { |
|
6392 var xargs = arguments.length > 2 ? Y.Array(arguments, 2, true) : null; |
|
6393 return function() { |
|
6394 var fn = L.isString(f) ? c[f] : f, |
|
6395 args = (xargs) ? |
|
6396 Y.Array(arguments, 0, true).concat(xargs) : arguments; |
|
6397 return fn.apply(c || fn, args); |
|
6398 }; |
|
6399 }; |
|
6400 |
|
6401 |
|
6402 }, '@VERSION@', {"requires": ["yui-base"]}); |
|
6403 YUI.add('features', function (Y, NAME) { |
|
6404 |
|
6405 var feature_tests = {}; |
|
6406 |
|
6407 /** |
|
6408 Contains the core of YUI's feature test architecture. |
|
6409 @module features |
|
6410 */ |
|
6411 |
|
6412 /** |
|
6413 * Feature detection |
|
6414 * @class Features |
|
6415 * @static |
|
6416 */ |
|
6417 |
|
6418 Y.mix(Y.namespace('Features'), { |
|
6419 |
|
6420 /** |
|
6421 * Object hash of all registered feature tests |
|
6422 * @property tests |
|
6423 * @type Object |
|
6424 */ |
|
6425 tests: feature_tests, |
|
6426 |
|
6427 /** |
|
6428 * Add a test to the system |
|
6429 * |
|
6430 * ``` |
|
6431 * Y.Features.add("load", "1", {}); |
|
6432 * ``` |
|
6433 * |
|
6434 * @method add |
|
6435 * @param {String} cat The category, right now only 'load' is supported |
|
6436 * @param {String} name The number sequence of the test, how it's reported in the URL or config: 1, 2, 3 |
|
6437 * @param {Object} o Object containing test properties |
|
6438 * @param {String} o.name The name of the test |
|
6439 * @param {Function} o.test The test function to execute, the only argument to the function is the `Y` instance |
|
6440 * @param {String} o.trigger The module that triggers this test. |
|
6441 */ |
|
6442 add: function(cat, name, o) { |
|
6443 feature_tests[cat] = feature_tests[cat] || {}; |
|
6444 feature_tests[cat][name] = o; |
|
6445 }, |
|
6446 /** |
|
6447 * Execute all tests of a given category and return the serialized results |
|
6448 * |
|
6449 * ``` |
|
6450 * caps=1:1;2:1;3:0 |
|
6451 * ``` |
|
6452 * @method all |
|
6453 * @param {String} cat The category to execute |
|
6454 * @param {Array} args The arguments to pass to the test function |
|
6455 * @return {String} A semi-colon separated string of tests and their success/failure: 1:1;2:1;3:0 |
|
6456 */ |
|
6457 all: function(cat, args) { |
|
6458 var cat_o = feature_tests[cat], |
|
6459 // results = {}; |
|
6460 result = []; |
|
6461 if (cat_o) { |
|
6462 Y.Object.each(cat_o, function(v, k) { |
|
6463 result.push(k + ':' + (Y.Features.test(cat, k, args) ? 1 : 0)); |
|
6464 }); |
|
6465 } |
|
6466 |
|
6467 return (result.length) ? result.join(';') : ''; |
|
6468 }, |
|
6469 /** |
|
6470 * Run a sepecific test and return a Boolean response. |
|
6471 * |
|
6472 * ``` |
|
6473 * Y.Features.test("load", "1"); |
|
6474 * ``` |
|
6475 * |
|
6476 * @method test |
|
6477 * @param {String} cat The category of the test to run |
|
6478 * @param {String} name The name of the test to run |
|
6479 * @param {Array} args The arguments to pass to the test function |
|
6480 * @return {Boolean} True or false if the test passed/failed. |
|
6481 */ |
|
6482 test: function(cat, name, args) { |
|
6483 args = args || []; |
|
6484 var result, ua, test, |
|
6485 cat_o = feature_tests[cat], |
|
6486 feature = cat_o && cat_o[name]; |
|
6487 |
|
6488 if (!feature) { |
|
6489 Y.log('Feature test ' + cat + ', ' + name + ' not found'); |
|
6490 } else { |
|
6491 |
|
6492 result = feature.result; |
|
6493 |
|
6494 if (Y.Lang.isUndefined(result)) { |
|
6495 |
|
6496 ua = feature.ua; |
|
6497 if (ua) { |
|
6498 result = (Y.UA[ua]); |
|
6499 } |
|
6500 |
|
6501 test = feature.test; |
|
6502 if (test && ((!ua) || result)) { |
|
6503 result = test.apply(Y, args); |
|
6504 } |
|
6505 |
|
6506 feature.result = result; |
|
6507 } |
|
6508 } |
|
6509 |
|
6510 return result; |
|
6511 } |
|
6512 }); |
|
6513 |
|
6514 // Y.Features.add("load", "1", {}); |
|
6515 // Y.Features.test("load", "1"); |
|
6516 // caps=1:1;2:0;3:1; |
|
6517 |
|
6518 /* This file is auto-generated by (yogi loader --yes --mix --start ../) */ |
|
6519 /*jshint maxlen:900, eqeqeq: false */ |
|
6520 var add = Y.Features.add; |
|
6521 // app-transitions-native |
|
6522 add('load', '0', { |
|
6523 "name": "app-transitions-native", |
|
6524 "test": function (Y) { |
|
6525 var doc = Y.config.doc, |
|
6526 node = doc ? doc.documentElement : null; |
|
6527 |
|
6528 if (node && node.style) { |
|
6529 return ('MozTransition' in node.style || 'WebkitTransition' in node.style || 'transition' in node.style); |
|
6530 } |
|
6531 |
|
6532 return false; |
|
6533 }, |
|
6534 "trigger": "app-transitions" |
|
6535 }); |
|
6536 // autocomplete-list-keys |
|
6537 add('load', '1', { |
|
6538 "name": "autocomplete-list-keys", |
|
6539 "test": function (Y) { |
|
6540 // Only add keyboard support to autocomplete-list if this doesn't appear to |
|
6541 // be an iOS or Android-based mobile device. |
|
6542 // |
|
6543 // There's currently no feasible way to actually detect whether a device has |
|
6544 // a hardware keyboard, so this sniff will have to do. It can easily be |
|
6545 // overridden by manually loading the autocomplete-list-keys module. |
|
6546 // |
|
6547 // Worth noting: even though iOS supports bluetooth keyboards, Mobile Safari |
|
6548 // doesn't fire the keyboard events used by AutoCompleteList, so there's |
|
6549 // no point loading the -keys module even when a bluetooth keyboard may be |
|
6550 // available. |
|
6551 return !(Y.UA.ios || Y.UA.android); |
|
6552 }, |
|
6553 "trigger": "autocomplete-list" |
|
6554 }); |
|
6555 // dd-gestures |
|
6556 add('load', '2', { |
|
6557 "name": "dd-gestures", |
|
6558 "trigger": "dd-drag", |
|
6559 "ua": "touchEnabled" |
|
6560 }); |
|
6561 // dom-style-ie |
|
6562 add('load', '3', { |
|
6563 "name": "dom-style-ie", |
|
6564 "test": function (Y) { |
|
6565 |
|
6566 var testFeature = Y.Features.test, |
|
6567 addFeature = Y.Features.add, |
|
6568 WINDOW = Y.config.win, |
|
6569 DOCUMENT = Y.config.doc, |
|
6570 DOCUMENT_ELEMENT = 'documentElement', |
|
6571 ret = false; |
|
6572 |
|
6573 addFeature('style', 'computedStyle', { |
|
6574 test: function() { |
|
6575 return WINDOW && 'getComputedStyle' in WINDOW; |
|
6576 } |
|
6577 }); |
|
6578 |
|
6579 addFeature('style', 'opacity', { |
|
6580 test: function() { |
|
6581 return DOCUMENT && 'opacity' in DOCUMENT[DOCUMENT_ELEMENT].style; |
|
6582 } |
|
6583 }); |
|
6584 |
|
6585 ret = (!testFeature('style', 'opacity') && |
|
6586 !testFeature('style', 'computedStyle')); |
|
6587 |
|
6588 return ret; |
|
6589 }, |
|
6590 "trigger": "dom-style" |
|
6591 }); |
|
6592 // editor-para-ie |
|
6593 add('load', '4', { |
|
6594 "name": "editor-para-ie", |
|
6595 "trigger": "editor-para", |
|
6596 "ua": "ie", |
|
6597 "when": "instead" |
|
6598 }); |
|
6599 // event-base-ie |
|
6600 add('load', '5', { |
|
6601 "name": "event-base-ie", |
|
6602 "test": function(Y) { |
|
6603 var imp = Y.config.doc && Y.config.doc.implementation; |
|
6604 return (imp && (!imp.hasFeature('Events', '2.0'))); |
|
6605 }, |
|
6606 "trigger": "node-base" |
|
6607 }); |
|
6608 // graphics-canvas |
|
6609 add('load', '6', { |
|
6610 "name": "graphics-canvas", |
|
6611 "test": function(Y) { |
|
6612 var DOCUMENT = Y.config.doc, |
|
6613 useCanvas = Y.config.defaultGraphicEngine && Y.config.defaultGraphicEngine == "canvas", |
|
6614 canvas = DOCUMENT && DOCUMENT.createElement("canvas"), |
|
6615 svg = (DOCUMENT && DOCUMENT.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1")); |
|
6616 return (!svg || useCanvas) && (canvas && canvas.getContext && canvas.getContext("2d")); |
|
6617 }, |
|
6618 "trigger": "graphics" |
|
6619 }); |
|
6620 // graphics-canvas-default |
|
6621 add('load', '7', { |
|
6622 "name": "graphics-canvas-default", |
|
6623 "test": function(Y) { |
|
6624 var DOCUMENT = Y.config.doc, |
|
6625 useCanvas = Y.config.defaultGraphicEngine && Y.config.defaultGraphicEngine == "canvas", |
|
6626 canvas = DOCUMENT && DOCUMENT.createElement("canvas"), |
|
6627 svg = (DOCUMENT && DOCUMENT.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1")); |
|
6628 return (!svg || useCanvas) && (canvas && canvas.getContext && canvas.getContext("2d")); |
|
6629 }, |
|
6630 "trigger": "graphics" |
|
6631 }); |
|
6632 // graphics-svg |
|
6633 add('load', '8', { |
|
6634 "name": "graphics-svg", |
|
6635 "test": function(Y) { |
|
6636 var DOCUMENT = Y.config.doc, |
|
6637 useSVG = !Y.config.defaultGraphicEngine || Y.config.defaultGraphicEngine != "canvas", |
|
6638 canvas = DOCUMENT && DOCUMENT.createElement("canvas"), |
|
6639 svg = (DOCUMENT && DOCUMENT.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1")); |
|
6640 |
|
6641 return svg && (useSVG || !canvas); |
|
6642 }, |
|
6643 "trigger": "graphics" |
|
6644 }); |
|
6645 // graphics-svg-default |
|
6646 add('load', '9', { |
|
6647 "name": "graphics-svg-default", |
|
6648 "test": function(Y) { |
|
6649 var DOCUMENT = Y.config.doc, |
|
6650 useSVG = !Y.config.defaultGraphicEngine || Y.config.defaultGraphicEngine != "canvas", |
|
6651 canvas = DOCUMENT && DOCUMENT.createElement("canvas"), |
|
6652 svg = (DOCUMENT && DOCUMENT.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1")); |
|
6653 |
|
6654 return svg && (useSVG || !canvas); |
|
6655 }, |
|
6656 "trigger": "graphics" |
|
6657 }); |
|
6658 // graphics-vml |
|
6659 add('load', '10', { |
|
6660 "name": "graphics-vml", |
|
6661 "test": function(Y) { |
|
6662 var DOCUMENT = Y.config.doc, |
|
6663 canvas = DOCUMENT && DOCUMENT.createElement("canvas"); |
|
6664 return (DOCUMENT && !DOCUMENT.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1") && (!canvas || !canvas.getContext || !canvas.getContext("2d"))); |
|
6665 }, |
|
6666 "trigger": "graphics" |
|
6667 }); |
|
6668 // graphics-vml-default |
|
6669 add('load', '11', { |
|
6670 "name": "graphics-vml-default", |
|
6671 "test": function(Y) { |
|
6672 var DOCUMENT = Y.config.doc, |
|
6673 canvas = DOCUMENT && DOCUMENT.createElement("canvas"); |
|
6674 return (DOCUMENT && !DOCUMENT.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1") && (!canvas || !canvas.getContext || !canvas.getContext("2d"))); |
|
6675 }, |
|
6676 "trigger": "graphics" |
|
6677 }); |
|
6678 // history-hash-ie |
|
6679 add('load', '12', { |
|
6680 "name": "history-hash-ie", |
|
6681 "test": function (Y) { |
|
6682 var docMode = Y.config.doc && Y.config.doc.documentMode; |
|
6683 |
|
6684 return Y.UA.ie && (!('onhashchange' in Y.config.win) || |
|
6685 !docMode || docMode < 8); |
|
6686 }, |
|
6687 "trigger": "history-hash" |
|
6688 }); |
|
6689 // io-nodejs |
|
6690 add('load', '13', { |
|
6691 "name": "io-nodejs", |
|
6692 "trigger": "io-base", |
|
6693 "ua": "nodejs" |
|
6694 }); |
|
6695 // json-parse-shim |
|
6696 add('load', '14', { |
|
6697 "name": "json-parse-shim", |
|
6698 "test": function (Y) { |
|
6699 var _JSON = Y.config.global.JSON, |
|
6700 Native = Object.prototype.toString.call(_JSON) === '[object JSON]' && _JSON, |
|
6701 nativeSupport = Y.config.useNativeJSONParse !== false && !!Native; |
|
6702 |
|
6703 function workingNative( k, v ) { |
|
6704 return k === "ok" ? true : v; |
|
6705 } |
|
6706 |
|
6707 // Double check basic functionality. This is mainly to catch early broken |
|
6708 // implementations of the JSON API in Firefox 3.1 beta1 and beta2 |
|
6709 if ( nativeSupport ) { |
|
6710 try { |
|
6711 nativeSupport = ( Native.parse( '{"ok":false}', workingNative ) ).ok; |
|
6712 } |
|
6713 catch ( e ) { |
|
6714 nativeSupport = false; |
|
6715 } |
|
6716 } |
|
6717 |
|
6718 return !nativeSupport; |
|
6719 }, |
|
6720 "trigger": "json-parse" |
|
6721 }); |
|
6722 // json-stringify-shim |
|
6723 add('load', '15', { |
|
6724 "name": "json-stringify-shim", |
|
6725 "test": function (Y) { |
|
6726 var _JSON = Y.config.global.JSON, |
|
6727 Native = Object.prototype.toString.call(_JSON) === '[object JSON]' && _JSON, |
|
6728 nativeSupport = Y.config.useNativeJSONStringify !== false && !!Native; |
|
6729 |
|
6730 // Double check basic native functionality. This is primarily to catch broken |
|
6731 // early JSON API implementations in Firefox 3.1 beta1 and beta2. |
|
6732 if ( nativeSupport ) { |
|
6733 try { |
|
6734 nativeSupport = ( '0' === Native.stringify(0) ); |
|
6735 } catch ( e ) { |
|
6736 nativeSupport = false; |
|
6737 } |
|
6738 } |
|
6739 |
|
6740 |
|
6741 return !nativeSupport; |
|
6742 }, |
|
6743 "trigger": "json-stringify" |
|
6744 }); |
|
6745 // scrollview-base-ie |
|
6746 add('load', '16', { |
|
6747 "name": "scrollview-base-ie", |
|
6748 "trigger": "scrollview-base", |
|
6749 "ua": "ie" |
|
6750 }); |
|
6751 // selector-css2 |
|
6752 add('load', '17', { |
|
6753 "name": "selector-css2", |
|
6754 "test": function (Y) { |
|
6755 var DOCUMENT = Y.config.doc, |
|
6756 ret = DOCUMENT && !('querySelectorAll' in DOCUMENT); |
|
6757 |
|
6758 return ret; |
|
6759 }, |
|
6760 "trigger": "selector" |
|
6761 }); |
|
6762 // transition-timer |
|
6763 add('load', '18', { |
|
6764 "name": "transition-timer", |
|
6765 "test": function (Y) { |
|
6766 var DOCUMENT = Y.config.doc, |
|
6767 node = (DOCUMENT) ? DOCUMENT.documentElement: null, |
|
6768 ret = true; |
|
6769 |
|
6770 if (node && node.style) { |
|
6771 ret = !('MozTransition' in node.style || 'WebkitTransition' in node.style || 'transition' in node.style); |
|
6772 } |
|
6773 |
|
6774 return ret; |
|
6775 }, |
|
6776 "trigger": "transition" |
|
6777 }); |
|
6778 // widget-base-ie |
|
6779 add('load', '19', { |
|
6780 "name": "widget-base-ie", |
|
6781 "trigger": "widget-base", |
|
6782 "ua": "ie" |
|
6783 }); |
|
6784 // yql-jsonp |
|
6785 add('load', '20', { |
|
6786 "name": "yql-jsonp", |
|
6787 "test": function (Y) { |
|
6788 /* Only load the JSONP module when not in nodejs or winjs |
|
6789 TODO Make the winjs module a CORS module |
|
6790 */ |
|
6791 return (!Y.UA.nodejs && !Y.UA.winjs); |
|
6792 }, |
|
6793 "trigger": "yql", |
|
6794 "when": "after" |
|
6795 }); |
|
6796 // yql-nodejs |
|
6797 add('load', '21', { |
|
6798 "name": "yql-nodejs", |
|
6799 "trigger": "yql", |
|
6800 "ua": "nodejs", |
|
6801 "when": "after" |
|
6802 }); |
|
6803 // yql-winjs |
|
6804 add('load', '22', { |
|
6805 "name": "yql-winjs", |
|
6806 "trigger": "yql", |
|
6807 "ua": "winjs", |
|
6808 "when": "after" |
|
6809 }); |
|
6810 |
|
6811 }, '@VERSION@', {"requires": ["yui-base"]}); |
|
6812 YUI.add('dom-core', function (Y, NAME) { |
|
6813 |
|
6814 var NODE_TYPE = 'nodeType', |
|
6815 OWNER_DOCUMENT = 'ownerDocument', |
|
6816 DOCUMENT_ELEMENT = 'documentElement', |
|
6817 DEFAULT_VIEW = 'defaultView', |
|
6818 PARENT_WINDOW = 'parentWindow', |
|
6819 TAG_NAME = 'tagName', |
|
6820 PARENT_NODE = 'parentNode', |
|
6821 PREVIOUS_SIBLING = 'previousSibling', |
|
6822 NEXT_SIBLING = 'nextSibling', |
|
6823 CONTAINS = 'contains', |
|
6824 COMPARE_DOCUMENT_POSITION = 'compareDocumentPosition', |
|
6825 EMPTY_ARRAY = [], |
|
6826 |
|
6827 // IE < 8 throws on node.contains(textNode) |
|
6828 supportsContainsTextNode = (function() { |
|
6829 var node = Y.config.doc.createElement('div'), |
|
6830 textNode = node.appendChild(Y.config.doc.createTextNode('')), |
|
6831 result = false; |
|
6832 |
|
6833 try { |
|
6834 result = node.contains(textNode); |
|
6835 } catch(e) {} |
|
6836 |
|
6837 return result; |
|
6838 })(), |
|
6839 |
|
6840 /** |
|
6841 * The DOM utility provides a cross-browser abtraction layer |
|
6842 * normalizing DOM tasks, and adds extra helper functionality |
|
6843 * for other common tasks. |
|
6844 * @module dom |
|
6845 * @main dom |
|
6846 * @submodule dom-base |
|
6847 * @for DOM |
|
6848 * |
|
6849 */ |
|
6850 |
|
6851 /** |
|
6852 * Provides DOM helper methods. |
|
6853 * @class DOM |
|
6854 * |
|
6855 */ |
|
6856 |
|
6857 Y_DOM = { |
|
6858 /** |
|
6859 * Returns the HTMLElement with the given ID (Wrapper for document.getElementById). |
|
6860 * @method byId |
|
6861 * @param {String} id the id attribute |
|
6862 * @param {Object} doc optional The document to search. Defaults to current document |
|
6863 * @return {HTMLElement | null} The HTMLElement with the id, or null if none found. |
|
6864 */ |
|
6865 byId: function(id, doc) { |
|
6866 // handle dupe IDs and IE name collision |
|
6867 return Y_DOM.allById(id, doc)[0] || null; |
|
6868 }, |
|
6869 |
|
6870 getId: function(node) { |
|
6871 var id; |
|
6872 // HTMLElement returned from FORM when INPUT name === "id" |
|
6873 // IE < 8: HTMLCollection returned when INPUT id === "id" |
|
6874 // via both getAttribute and form.id |
|
6875 if (node.id && !node.id.tagName && !node.id.item) { |
|
6876 id = node.id; |
|
6877 } else if (node.attributes && node.attributes.id) { |
|
6878 id = node.attributes.id.value; |
|
6879 } |
|
6880 |
|
6881 return id; |
|
6882 }, |
|
6883 |
|
6884 setId: function(node, id) { |
|
6885 if (node.setAttribute) { |
|
6886 node.setAttribute('id', id); |
|
6887 } else { |
|
6888 node.id = id; |
|
6889 } |
|
6890 }, |
|
6891 |
|
6892 /* |
|
6893 * Finds the ancestor of the element. |
|
6894 * @method ancestor |
|
6895 * @param {HTMLElement} element The html element. |
|
6896 * @param {Function} fn optional An optional boolean test to apply. |
|
6897 * The optional function is passed the current DOM node being tested as its only argument. |
|
6898 * If no function is given, the parentNode is returned. |
|
6899 * @param {Boolean} testSelf optional Whether or not to include the element in the scan |
|
6900 * @return {HTMLElement | null} The matching DOM node or null if none found. |
|
6901 */ |
|
6902 ancestor: function(element, fn, testSelf, stopFn) { |
|
6903 var ret = null; |
|
6904 if (testSelf) { |
|
6905 ret = (!fn || fn(element)) ? element : null; |
|
6906 |
|
6907 } |
|
6908 return ret || Y_DOM.elementByAxis(element, PARENT_NODE, fn, null, stopFn); |
|
6909 }, |
|
6910 |
|
6911 /* |
|
6912 * Finds the ancestors of the element. |
|
6913 * @method ancestors |
|
6914 * @param {HTMLElement} element The html element. |
|
6915 * @param {Function} fn optional An optional boolean test to apply. |
|
6916 * The optional function is passed the current DOM node being tested as its only argument. |
|
6917 * If no function is given, all ancestors are returned. |
|
6918 * @param {Boolean} testSelf optional Whether or not to include the element in the scan |
|
6919 * @return {Array} An array containing all matching DOM nodes. |
|
6920 */ |
|
6921 ancestors: function(element, fn, testSelf, stopFn) { |
|
6922 var ancestor = element, |
|
6923 ret = []; |
|
6924 |
|
6925 while ((ancestor = Y_DOM.ancestor(ancestor, fn, testSelf, stopFn))) { |
|
6926 testSelf = false; |
|
6927 if (ancestor) { |
|
6928 ret.unshift(ancestor); |
|
6929 |
|
6930 if (stopFn && stopFn(ancestor)) { |
|
6931 return ret; |
|
6932 } |
|
6933 } |
|
6934 } |
|
6935 |
|
6936 return ret; |
|
6937 }, |
|
6938 |
|
6939 /** |
|
6940 * Searches the element by the given axis for the first matching element. |
|
6941 * @method elementByAxis |
|
6942 * @param {HTMLElement} element The html element. |
|
6943 * @param {String} axis The axis to search (parentNode, nextSibling, previousSibling). |
|
6944 * @param {Function} fn optional An optional boolean test to apply. |
|
6945 * @param {Boolean} all optional Whether all node types should be returned, or just element nodes. |
|
6946 * The optional function is passed the current HTMLElement being tested as its only argument. |
|
6947 * If no function is given, the first element is returned. |
|
6948 * @return {HTMLElement | null} The matching element or null if none found. |
|
6949 */ |
|
6950 elementByAxis: function(element, axis, fn, all, stopAt) { |
|
6951 while (element && (element = element[axis])) { // NOTE: assignment |
|
6952 if ( (all || element[TAG_NAME]) && (!fn || fn(element)) ) { |
|
6953 return element; |
|
6954 } |
|
6955 |
|
6956 if (stopAt && stopAt(element)) { |
|
6957 return null; |
|
6958 } |
|
6959 } |
|
6960 return null; |
|
6961 }, |
|
6962 |
|
6963 /** |
|
6964 * Determines whether or not one HTMLElement is or contains another HTMLElement. |
|
6965 * @method contains |
|
6966 * @param {HTMLElement} element The containing html element. |
|
6967 * @param {HTMLElement} needle The html element that may be contained. |
|
6968 * @return {Boolean} Whether or not the element is or contains the needle. |
|
6969 */ |
|
6970 contains: function(element, needle) { |
|
6971 var ret = false; |
|
6972 |
|
6973 if ( !needle || !element || !needle[NODE_TYPE] || !element[NODE_TYPE]) { |
|
6974 ret = false; |
|
6975 } else if (element[CONTAINS] && |
|
6976 // IE < 8 throws on node.contains(textNode) so fall back to brute. |
|
6977 // Falling back for other nodeTypes as well. |
|
6978 (needle[NODE_TYPE] === 1 || supportsContainsTextNode)) { |
|
6979 ret = element[CONTAINS](needle); |
|
6980 } else if (element[COMPARE_DOCUMENT_POSITION]) { |
|
6981 // Match contains behavior (node.contains(node) === true). |
|
6982 // Needed for Firefox < 4. |
|
6983 if (element === needle || !!(element[COMPARE_DOCUMENT_POSITION](needle) & 16)) { |
|
6984 ret = true; |
|
6985 } |
|
6986 } else { |
|
6987 ret = Y_DOM._bruteContains(element, needle); |
|
6988 } |
|
6989 |
|
6990 return ret; |
|
6991 }, |
|
6992 |
|
6993 /** |
|
6994 * Determines whether or not the HTMLElement is part of the document. |
|
6995 * @method inDoc |
|
6996 * @param {HTMLElement} element The containing html element. |
|
6997 * @param {HTMLElement} doc optional The document to check. |
|
6998 * @return {Boolean} Whether or not the element is attached to the document. |
|
6999 */ |
|
7000 inDoc: function(element, doc) { |
|
7001 var ret = false, |
|
7002 rootNode; |
|
7003 |
|
7004 if (element && element.nodeType) { |
|
7005 (doc) || (doc = element[OWNER_DOCUMENT]); |
|
7006 |
|
7007 rootNode = doc[DOCUMENT_ELEMENT]; |
|
7008 |
|
7009 // contains only works with HTML_ELEMENT |
|
7010 if (rootNode && rootNode.contains && element.tagName) { |
|
7011 ret = rootNode.contains(element); |
|
7012 } else { |
|
7013 ret = Y_DOM.contains(rootNode, element); |
|
7014 } |
|
7015 } |
|
7016 |
|
7017 return ret; |
|
7018 |
|
7019 }, |
|
7020 |
|
7021 allById: function(id, root) { |
|
7022 root = root || Y.config.doc; |
|
7023 var nodes = [], |
|
7024 ret = [], |
|
7025 i, |
|
7026 node; |
|
7027 |
|
7028 if (root.querySelectorAll) { |
|
7029 ret = root.querySelectorAll('[id="' + id + '"]'); |
|
7030 } else if (root.all) { |
|
7031 nodes = root.all(id); |
|
7032 |
|
7033 if (nodes) { |
|
7034 // root.all may return HTMLElement or HTMLCollection. |
|
7035 // some elements are also HTMLCollection (FORM, SELECT). |
|
7036 if (nodes.nodeName) { |
|
7037 if (nodes.id === id) { // avoid false positive on name |
|
7038 ret.push(nodes); |
|
7039 nodes = EMPTY_ARRAY; // done, no need to filter |
|
7040 } else { // prep for filtering |
|
7041 nodes = [nodes]; |
|
7042 } |
|
7043 } |
|
7044 |
|
7045 if (nodes.length) { |
|
7046 // filter out matches on node.name |
|
7047 // and element.id as reference to element with id === 'id' |
|
7048 for (i = 0; node = nodes[i++];) { |
|
7049 if (node.id === id || |
|
7050 (node.attributes && node.attributes.id && |
|
7051 node.attributes.id.value === id)) { |
|
7052 ret.push(node); |
|
7053 } |
|
7054 } |
|
7055 } |
|
7056 } |
|
7057 } else { |
|
7058 ret = [Y_DOM._getDoc(root).getElementById(id)]; |
|
7059 } |
|
7060 |
|
7061 return ret; |
|
7062 }, |
|
7063 |
|
7064 |
|
7065 isWindow: function(obj) { |
|
7066 return !!(obj && obj.scrollTo && obj.document); |
|
7067 }, |
|
7068 |
|
7069 _removeChildNodes: function(node) { |
|
7070 while (node.firstChild) { |
|
7071 node.removeChild(node.firstChild); |
|
7072 } |
|
7073 }, |
|
7074 |
|
7075 siblings: function(node, fn) { |
|
7076 var nodes = [], |
|
7077 sibling = node; |
|
7078 |
|
7079 while ((sibling = sibling[PREVIOUS_SIBLING])) { |
|
7080 if (sibling[TAG_NAME] && (!fn || fn(sibling))) { |
|
7081 nodes.unshift(sibling); |
|
7082 } |
|
7083 } |
|
7084 |
|
7085 sibling = node; |
|
7086 while ((sibling = sibling[NEXT_SIBLING])) { |
|
7087 if (sibling[TAG_NAME] && (!fn || fn(sibling))) { |
|
7088 nodes.push(sibling); |
|
7089 } |
|
7090 } |
|
7091 |
|
7092 return nodes; |
|
7093 }, |
|
7094 |
|
7095 /** |
|
7096 * Brute force version of contains. |
|
7097 * Used for browsers without contains support for non-HTMLElement Nodes (textNodes, etc). |
|
7098 * @method _bruteContains |
|
7099 * @private |
|
7100 * @param {HTMLElement} element The containing html element. |
|
7101 * @param {HTMLElement} needle The html element that may be contained. |
|
7102 * @return {Boolean} Whether or not the element is or contains the needle. |
|
7103 */ |
|
7104 _bruteContains: function(element, needle) { |
|
7105 while (needle) { |
|
7106 if (element === needle) { |
|
7107 return true; |
|
7108 } |
|
7109 needle = needle.parentNode; |
|
7110 } |
|
7111 return false; |
|
7112 }, |
|
7113 |
|
7114 // TODO: move to Lang? |
|
7115 /** |
|
7116 * Memoizes dynamic regular expressions to boost runtime performance. |
|
7117 * @method _getRegExp |
|
7118 * @private |
|
7119 * @param {String} str The string to convert to a regular expression. |
|
7120 * @param {String} flags optional An optinal string of flags. |
|
7121 * @return {RegExp} An instance of RegExp |
|
7122 */ |
|
7123 _getRegExp: function(str, flags) { |
|
7124 flags = flags || ''; |
|
7125 Y_DOM._regexCache = Y_DOM._regexCache || {}; |
|
7126 if (!Y_DOM._regexCache[str + flags]) { |
|
7127 Y_DOM._regexCache[str + flags] = new RegExp(str, flags); |
|
7128 } |
|
7129 return Y_DOM._regexCache[str + flags]; |
|
7130 }, |
|
7131 |
|
7132 // TODO: make getDoc/Win true privates? |
|
7133 /** |
|
7134 * returns the appropriate document. |
|
7135 * @method _getDoc |
|
7136 * @private |
|
7137 * @param {HTMLElement} element optional Target element. |
|
7138 * @return {Object} The document for the given element or the default document. |
|
7139 */ |
|
7140 _getDoc: function(element) { |
|
7141 var doc = Y.config.doc; |
|
7142 if (element) { |
|
7143 doc = (element[NODE_TYPE] === 9) ? element : // element === document |
|
7144 element[OWNER_DOCUMENT] || // element === DOM node |
|
7145 element.document || // element === window |
|
7146 Y.config.doc; // default |
|
7147 } |
|
7148 |
|
7149 return doc; |
|
7150 }, |
|
7151 |
|
7152 /** |
|
7153 * returns the appropriate window. |
|
7154 * @method _getWin |
|
7155 * @private |
|
7156 * @param {HTMLElement} element optional Target element. |
|
7157 * @return {Object} The window for the given element or the default window. |
|
7158 */ |
|
7159 _getWin: function(element) { |
|
7160 var doc = Y_DOM._getDoc(element); |
|
7161 return doc[DEFAULT_VIEW] || doc[PARENT_WINDOW] || Y.config.win; |
|
7162 }, |
|
7163 |
|
7164 _batch: function(nodes, fn, arg1, arg2, arg3, etc) { |
|
7165 fn = (typeof fn === 'string') ? Y_DOM[fn] : fn; |
|
7166 var result, |
|
7167 i = 0, |
|
7168 node, |
|
7169 ret; |
|
7170 |
|
7171 if (fn && nodes) { |
|
7172 while ((node = nodes[i++])) { |
|
7173 result = result = fn.call(Y_DOM, node, arg1, arg2, arg3, etc); |
|
7174 if (typeof result !== 'undefined') { |
|
7175 (ret) || (ret = []); |
|
7176 ret.push(result); |
|
7177 } |
|
7178 } |
|
7179 } |
|
7180 |
|
7181 return (typeof ret !== 'undefined') ? ret : nodes; |
|
7182 }, |
|
7183 |
|
7184 generateID: function(el) { |
|
7185 var id = el.id; |
|
7186 |
|
7187 if (!id) { |
|
7188 id = Y.stamp(el); |
|
7189 el.id = id; |
|
7190 } |
|
7191 |
|
7192 return id; |
|
7193 } |
|
7194 }; |
|
7195 |
|
7196 |
|
7197 Y.DOM = Y_DOM; |
|
7198 |
|
7199 |
|
7200 }, '@VERSION@', {"requires": ["oop", "features"]}); |
|
7201 YUI.add('dom-base', function (Y, NAME) { |
|
7202 |
|
7203 /** |
|
7204 * @for DOM |
|
7205 * @module dom |
|
7206 */ |
|
7207 var documentElement = Y.config.doc.documentElement, |
|
7208 Y_DOM = Y.DOM, |
|
7209 TAG_NAME = 'tagName', |
|
7210 OWNER_DOCUMENT = 'ownerDocument', |
|
7211 EMPTY_STRING = '', |
|
7212 addFeature = Y.Features.add, |
|
7213 testFeature = Y.Features.test; |
|
7214 |
|
7215 Y.mix(Y_DOM, { |
|
7216 /** |
|
7217 * Returns the text content of the HTMLElement. |
|
7218 * @method getText |
|
7219 * @param {HTMLElement} element The html element. |
|
7220 * @return {String} The text content of the element (includes text of any descending elements). |
|
7221 */ |
|
7222 getText: (documentElement.textContent !== undefined) ? |
|
7223 function(element) { |
|
7224 var ret = ''; |
|
7225 if (element) { |
|
7226 ret = element.textContent; |
|
7227 } |
|
7228 return ret || ''; |
|
7229 } : function(element) { |
|
7230 var ret = ''; |
|
7231 if (element) { |
|
7232 ret = element.innerText || element.nodeValue; // might be a textNode |
|
7233 } |
|
7234 return ret || ''; |
|
7235 }, |
|
7236 |
|
7237 /** |
|
7238 * Sets the text content of the HTMLElement. |
|
7239 * @method setText |
|
7240 * @param {HTMLElement} element The html element. |
|
7241 * @param {String} content The content to add. |
|
7242 */ |
|
7243 setText: (documentElement.textContent !== undefined) ? |
|
7244 function(element, content) { |
|
7245 if (element) { |
|
7246 element.textContent = content; |
|
7247 } |
|
7248 } : function(element, content) { |
|
7249 if ('innerText' in element) { |
|
7250 element.innerText = content; |
|
7251 } else if ('nodeValue' in element) { |
|
7252 element.nodeValue = content; |
|
7253 } |
|
7254 }, |
|
7255 |
|
7256 CUSTOM_ATTRIBUTES: (!documentElement.hasAttribute) ? { // IE < 8 |
|
7257 'for': 'htmlFor', |
|
7258 'class': 'className' |
|
7259 } : { // w3c |
|
7260 'htmlFor': 'for', |
|
7261 'className': 'class' |
|
7262 }, |
|
7263 |
|
7264 /** |
|
7265 * Provides a normalized attribute interface. |
|
7266 * @method setAttribute |
|
7267 * @param {HTMLElement} el The target element for the attribute. |
|
7268 * @param {String} attr The attribute to set. |
|
7269 * @param {String} val The value of the attribute. |
|
7270 */ |
|
7271 setAttribute: function(el, attr, val, ieAttr) { |
|
7272 if (el && attr && el.setAttribute) { |
|
7273 attr = Y_DOM.CUSTOM_ATTRIBUTES[attr] || attr; |
|
7274 el.setAttribute(attr, val, ieAttr); |
|
7275 } |
|
7276 else { Y.log('bad input to setAttribute', 'warn', 'dom'); } |
|
7277 }, |
|
7278 |
|
7279 |
|
7280 /** |
|
7281 * Provides a normalized attribute interface. |
|
7282 * @method getAttribute |
|
7283 * @param {HTMLElement} el The target element for the attribute. |
|
7284 * @param {String} attr The attribute to get. |
|
7285 * @return {String} The current value of the attribute. |
|
7286 */ |
|
7287 getAttribute: function(el, attr, ieAttr) { |
|
7288 ieAttr = (ieAttr !== undefined) ? ieAttr : 2; |
|
7289 var ret = ''; |
|
7290 if (el && attr && el.getAttribute) { |
|
7291 attr = Y_DOM.CUSTOM_ATTRIBUTES[attr] || attr; |
|
7292 ret = el.getAttribute(attr, ieAttr); |
|
7293 |
|
7294 if (ret === null) { |
|
7295 ret = ''; // per DOM spec |
|
7296 } |
|
7297 } |
|
7298 else { Y.log('bad input to getAttribute', 'warn', 'dom'); } |
|
7299 return ret; |
|
7300 }, |
|
7301 |
|
7302 VALUE_SETTERS: {}, |
|
7303 |
|
7304 VALUE_GETTERS: {}, |
|
7305 |
|
7306 getValue: function(node) { |
|
7307 var ret = '', // TODO: return null? |
|
7308 getter; |
|
7309 |
|
7310 if (node && node[TAG_NAME]) { |
|
7311 getter = Y_DOM.VALUE_GETTERS[node[TAG_NAME].toLowerCase()]; |
|
7312 |
|
7313 if (getter) { |
|
7314 ret = getter(node); |
|
7315 } else { |
|
7316 ret = node.value; |
|
7317 } |
|
7318 } |
|
7319 |
|
7320 // workaround for IE8 JSON stringify bug |
|
7321 // which converts empty string values to null |
|
7322 if (ret === EMPTY_STRING) { |
|
7323 ret = EMPTY_STRING; // for real |
|
7324 } |
|
7325 |
|
7326 return (typeof ret === 'string') ? ret : ''; |
|
7327 }, |
|
7328 |
|
7329 setValue: function(node, val) { |
|
7330 var setter; |
|
7331 |
|
7332 if (node && node[TAG_NAME]) { |
|
7333 setter = Y_DOM.VALUE_SETTERS[node[TAG_NAME].toLowerCase()]; |
|
7334 |
|
7335 if (setter) { |
|
7336 setter(node, val); |
|
7337 } else { |
|
7338 node.value = val; |
|
7339 } |
|
7340 } |
|
7341 }, |
|
7342 |
|
7343 creators: {} |
|
7344 }); |
|
7345 |
|
7346 addFeature('value-set', 'select', { |
|
7347 test: function() { |
|
7348 var node = Y.config.doc.createElement('select'); |
|
7349 node.innerHTML = '<option>1</option><option>2</option>'; |
|
7350 node.value = '2'; |
|
7351 return (node.value && node.value === '2'); |
|
7352 } |
|
7353 }); |
|
7354 |
|
7355 if (!testFeature('value-set', 'select')) { |
|
7356 Y_DOM.VALUE_SETTERS.select = function(node, val) { |
|
7357 for (var i = 0, options = node.getElementsByTagName('option'), option; |
|
7358 option = options[i++];) { |
|
7359 if (Y_DOM.getValue(option) === val) { |
|
7360 option.selected = true; |
|
7361 //Y_DOM.setAttribute(option, 'selected', 'selected'); |
|
7362 break; |
|
7363 } |
|
7364 } |
|
7365 }; |
|
7366 } |
|
7367 |
|
7368 Y.mix(Y_DOM.VALUE_GETTERS, { |
|
7369 button: function(node) { |
|
7370 return (node.attributes && node.attributes.value) ? node.attributes.value.value : ''; |
|
7371 } |
|
7372 }); |
|
7373 |
|
7374 Y.mix(Y_DOM.VALUE_SETTERS, { |
|
7375 // IE: node.value changes the button text, which should be handled via innerHTML |
|
7376 button: function(node, val) { |
|
7377 var attr = node.attributes.value; |
|
7378 if (!attr) { |
|
7379 attr = node[OWNER_DOCUMENT].createAttribute('value'); |
|
7380 node.setAttributeNode(attr); |
|
7381 } |
|
7382 |
|
7383 attr.value = val; |
|
7384 } |
|
7385 }); |
|
7386 |
|
7387 |
|
7388 Y.mix(Y_DOM.VALUE_GETTERS, { |
|
7389 option: function(node) { |
|
7390 var attrs = node.attributes; |
|
7391 return (attrs.value && attrs.value.specified) ? node.value : node.text; |
|
7392 }, |
|
7393 |
|
7394 select: function(node) { |
|
7395 var val = node.value, |
|
7396 options = node.options; |
|
7397 |
|
7398 if (options && options.length) { |
|
7399 // TODO: implement multipe select |
|
7400 if (node.multiple) { |
|
7401 Y.log('multiple select normalization not implemented', 'warn', 'DOM'); |
|
7402 } else if (node.selectedIndex > -1) { |
|
7403 val = Y_DOM.getValue(options[node.selectedIndex]); |
|
7404 } |
|
7405 } |
|
7406 |
|
7407 return val; |
|
7408 } |
|
7409 }); |
|
7410 var addClass, hasClass, removeClass; |
|
7411 |
|
7412 Y.mix(Y.DOM, { |
|
7413 /** |
|
7414 * Determines whether a DOM element has the given className. |
|
7415 * @method hasClass |
|
7416 * @for DOM |
|
7417 * @param {HTMLElement} element The DOM element. |
|
7418 * @param {String} className the class name to search for |
|
7419 * @return {Boolean} Whether or not the element has the given class. |
|
7420 */ |
|
7421 hasClass: function(node, className) { |
|
7422 var re = Y.DOM._getRegExp('(?:^|\\s+)' + className + '(?:\\s+|$)'); |
|
7423 return re.test(node.className); |
|
7424 }, |
|
7425 |
|
7426 /** |
|
7427 * Adds a class name to a given DOM element. |
|
7428 * @method addClass |
|
7429 * @for DOM |
|
7430 * @param {HTMLElement} element The DOM element. |
|
7431 * @param {String} className the class name to add to the class attribute |
|
7432 */ |
|
7433 addClass: function(node, className) { |
|
7434 if (!Y.DOM.hasClass(node, className)) { // skip if already present |
|
7435 node.className = Y.Lang.trim([node.className, className].join(' ')); |
|
7436 } |
|
7437 }, |
|
7438 |
|
7439 /** |
|
7440 * Removes a class name from a given element. |
|
7441 * @method removeClass |
|
7442 * @for DOM |
|
7443 * @param {HTMLElement} element The DOM element. |
|
7444 * @param {String} className the class name to remove from the class attribute |
|
7445 */ |
|
7446 removeClass: function(node, className) { |
|
7447 if (className && hasClass(node, className)) { |
|
7448 node.className = Y.Lang.trim(node.className.replace(Y.DOM._getRegExp('(?:^|\\s+)' + |
|
7449 className + '(?:\\s+|$)'), ' ')); |
|
7450 |
|
7451 if ( hasClass(node, className) ) { // in case of multiple adjacent |
|
7452 removeClass(node, className); |
|
7453 } |
|
7454 } |
|
7455 }, |
|
7456 |
|
7457 /** |
|
7458 * Replace a class with another class for a given element. |
|
7459 * If no oldClassName is present, the newClassName is simply added. |
|
7460 * @method replaceClass |
|
7461 * @for DOM |
|
7462 * @param {HTMLElement} element The DOM element |
|
7463 * @param {String} oldClassName the class name to be replaced |
|
7464 * @param {String} newClassName the class name that will be replacing the old class name |
|
7465 */ |
|
7466 replaceClass: function(node, oldC, newC) { |
|
7467 //Y.log('replaceClass replacing ' + oldC + ' with ' + newC, 'info', 'Node'); |
|
7468 removeClass(node, oldC); // remove first in case oldC === newC |
|
7469 addClass(node, newC); |
|
7470 }, |
|
7471 |
|
7472 /** |
|
7473 * If the className exists on the node it is removed, if it doesn't exist it is added. |
|
7474 * @method toggleClass |
|
7475 * @for DOM |
|
7476 * @param {HTMLElement} element The DOM element |
|
7477 * @param {String} className the class name to be toggled |
|
7478 * @param {Boolean} addClass optional boolean to indicate whether class |
|
7479 * should be added or removed regardless of current state |
|
7480 */ |
|
7481 toggleClass: function(node, className, force) { |
|
7482 var add = (force !== undefined) ? force : |
|
7483 !(hasClass(node, className)); |
|
7484 |
|
7485 if (add) { |
|
7486 addClass(node, className); |
|
7487 } else { |
|
7488 removeClass(node, className); |
|
7489 } |
|
7490 } |
|
7491 }); |
|
7492 |
|
7493 hasClass = Y.DOM.hasClass; |
|
7494 removeClass = Y.DOM.removeClass; |
|
7495 addClass = Y.DOM.addClass; |
|
7496 |
|
7497 var re_tag = /<([a-z]+)/i, |
|
7498 |
|
7499 Y_DOM = Y.DOM, |
|
7500 |
|
7501 addFeature = Y.Features.add, |
|
7502 testFeature = Y.Features.test, |
|
7503 |
|
7504 creators = {}, |
|
7505 |
|
7506 createFromDIV = function(html, tag) { |
|
7507 var div = Y.config.doc.createElement('div'), |
|
7508 ret = true; |
|
7509 |
|
7510 div.innerHTML = html; |
|
7511 if (!div.firstChild || div.firstChild.tagName !== tag.toUpperCase()) { |
|
7512 ret = false; |
|
7513 } |
|
7514 |
|
7515 return ret; |
|
7516 }, |
|
7517 |
|
7518 re_tbody = /(?:\/(?:thead|tfoot|tbody|caption|col|colgroup)>)+\s*<tbody/, |
|
7519 |
|
7520 TABLE_OPEN = '<table>', |
|
7521 TABLE_CLOSE = '</table>'; |
|
7522 |
|
7523 Y.mix(Y.DOM, { |
|
7524 _fragClones: {}, |
|
7525 |
|
7526 _create: function(html, doc, tag) { |
|
7527 tag = tag || 'div'; |
|
7528 |
|
7529 var frag = Y_DOM._fragClones[tag]; |
|
7530 if (frag) { |
|
7531 frag = frag.cloneNode(false); |
|
7532 } else { |
|
7533 frag = Y_DOM._fragClones[tag] = doc.createElement(tag); |
|
7534 } |
|
7535 frag.innerHTML = html; |
|
7536 return frag; |
|
7537 }, |
|
7538 |
|
7539 _children: function(node, tag) { |
|
7540 var i = 0, |
|
7541 children = node.children, |
|
7542 childNodes, |
|
7543 hasComments, |
|
7544 child; |
|
7545 |
|
7546 if (children && children.tags) { // use tags filter when possible |
|
7547 if (tag) { |
|
7548 children = node.children.tags(tag); |
|
7549 } else { // IE leaks comments into children |
|
7550 hasComments = children.tags('!').length; |
|
7551 } |
|
7552 } |
|
7553 |
|
7554 if (!children || (!children.tags && tag) || hasComments) { |
|
7555 childNodes = children || node.childNodes; |
|
7556 children = []; |
|
7557 while ((child = childNodes[i++])) { |
|
7558 if (child.nodeType === 1) { |
|
7559 if (!tag || tag === child.tagName) { |
|
7560 children.push(child); |
|
7561 } |
|
7562 } |
|
7563 } |
|
7564 } |
|
7565 |
|
7566 return children || []; |
|
7567 }, |
|
7568 |
|
7569 /** |
|
7570 * Creates a new dom node using the provided markup string. |
|
7571 * @method create |
|
7572 * @param {String} html The markup used to create the element |
|
7573 * @param {HTMLDocument} doc An optional document context |
|
7574 * @return {HTMLElement|DocumentFragment} returns a single HTMLElement |
|
7575 * when creating one node, and a documentFragment when creating |
|
7576 * multiple nodes. |
|
7577 */ |
|
7578 create: function(html, doc) { |
|
7579 if (typeof html === 'string') { |
|
7580 html = Y.Lang.trim(html); // match IE which trims whitespace from innerHTML |
|
7581 |
|
7582 } |
|
7583 |
|
7584 doc = doc || Y.config.doc; |
|
7585 var m = re_tag.exec(html), |
|
7586 create = Y_DOM._create, |
|
7587 custom = creators, |
|
7588 ret = null, |
|
7589 creator, |
|
7590 tag, nodes; |
|
7591 |
|
7592 if (html != undefined) { // not undefined or null |
|
7593 if (m && m[1]) { |
|
7594 creator = custom[m[1].toLowerCase()]; |
|
7595 if (typeof creator === 'function') { |
|
7596 create = creator; |
|
7597 } else { |
|
7598 tag = creator; |
|
7599 } |
|
7600 } |
|
7601 |
|
7602 nodes = create(html, doc, tag).childNodes; |
|
7603 |
|
7604 if (nodes.length === 1) { // return single node, breaking parentNode ref from "fragment" |
|
7605 ret = nodes[0].parentNode.removeChild(nodes[0]); |
|
7606 } else if (nodes[0] && nodes[0].className === 'yui3-big-dummy') { // using dummy node to preserve some attributes (e.g. OPTION not selected) |
|
7607 if (nodes.length === 2) { |
|
7608 ret = nodes[0].nextSibling; |
|
7609 } else { |
|
7610 nodes[0].parentNode.removeChild(nodes[0]); |
|
7611 ret = Y_DOM._nl2frag(nodes, doc); |
|
7612 } |
|
7613 } else { // return multiple nodes as a fragment |
|
7614 ret = Y_DOM._nl2frag(nodes, doc); |
|
7615 } |
|
7616 |
|
7617 } |
|
7618 |
|
7619 return ret; |
|
7620 }, |
|
7621 |
|
7622 _nl2frag: function(nodes, doc) { |
|
7623 var ret = null, |
|
7624 i, len; |
|
7625 |
|
7626 if (nodes && (nodes.push || nodes.item) && nodes[0]) { |
|
7627 doc = doc || nodes[0].ownerDocument; |
|
7628 ret = doc.createDocumentFragment(); |
|
7629 |
|
7630 if (nodes.item) { // convert live list to static array |
|
7631 nodes = Y.Array(nodes, 0, true); |
|
7632 } |
|
7633 |
|
7634 for (i = 0, len = nodes.length; i < len; i++) { |
|
7635 ret.appendChild(nodes[i]); |
|
7636 } |
|
7637 } // else inline with log for minification |
|
7638 return ret; |
|
7639 }, |
|
7640 |
|
7641 /** |
|
7642 * Inserts content in a node at the given location |
|
7643 * @method addHTML |
|
7644 * @param {HTMLElement} node The node to insert into |
|
7645 * @param {HTMLElement | Array | HTMLCollection} content The content to be inserted |
|
7646 * @param {HTMLElement} where Where to insert the content |
|
7647 * If no "where" is given, content is appended to the node |
|
7648 * Possible values for "where" |
|
7649 * <dl> |
|
7650 * <dt>HTMLElement</dt> |
|
7651 * <dd>The element to insert before</dd> |
|
7652 * <dt>"replace"</dt> |
|
7653 * <dd>Replaces the existing HTML</dd> |
|
7654 * <dt>"before"</dt> |
|
7655 * <dd>Inserts before the existing HTML</dd> |
|
7656 * <dt>"before"</dt> |
|
7657 * <dd>Inserts content before the node</dd> |
|
7658 * <dt>"after"</dt> |
|
7659 * <dd>Inserts content after the node</dd> |
|
7660 * </dl> |
|
7661 */ |
|
7662 addHTML: function(node, content, where) { |
|
7663 var nodeParent = node.parentNode, |
|
7664 i = 0, |
|
7665 item, |
|
7666 ret = content, |
|
7667 newNode; |
|
7668 |
|
7669 |
|
7670 if (content != undefined) { // not null or undefined (maybe 0) |
|
7671 if (content.nodeType) { // DOM node, just add it |
|
7672 newNode = content; |
|
7673 } else if (typeof content == 'string' || typeof content == 'number') { |
|
7674 ret = newNode = Y_DOM.create(content); |
|
7675 } else if (content[0] && content[0].nodeType) { // array or collection |
|
7676 newNode = Y.config.doc.createDocumentFragment(); |
|
7677 while ((item = content[i++])) { |
|
7678 newNode.appendChild(item); // append to fragment for insertion |
|
7679 } |
|
7680 } |
|
7681 } |
|
7682 |
|
7683 if (where) { |
|
7684 if (newNode && where.parentNode) { // insert regardless of relationship to node |
|
7685 where.parentNode.insertBefore(newNode, where); |
|
7686 } else { |
|
7687 switch (where) { |
|
7688 case 'replace': |
|
7689 while (node.firstChild) { |
|
7690 node.removeChild(node.firstChild); |
|
7691 } |
|
7692 if (newNode) { // allow empty content to clear node |
|
7693 node.appendChild(newNode); |
|
7694 } |
|
7695 break; |
|
7696 case 'before': |
|
7697 if (newNode) { |
|
7698 nodeParent.insertBefore(newNode, node); |
|
7699 } |
|
7700 break; |
|
7701 case 'after': |
|
7702 if (newNode) { |
|
7703 if (node.nextSibling) { // IE errors if refNode is null |
|
7704 nodeParent.insertBefore(newNode, node.nextSibling); |
|
7705 } else { |
|
7706 nodeParent.appendChild(newNode); |
|
7707 } |
|
7708 } |
|
7709 break; |
|
7710 default: |
|
7711 if (newNode) { |
|
7712 node.appendChild(newNode); |
|
7713 } |
|
7714 } |
|
7715 } |
|
7716 } else if (newNode) { |
|
7717 node.appendChild(newNode); |
|
7718 } |
|
7719 |
|
7720 return ret; |
|
7721 }, |
|
7722 |
|
7723 wrap: function(node, html) { |
|
7724 var parent = (html && html.nodeType) ? html : Y.DOM.create(html), |
|
7725 nodes = parent.getElementsByTagName('*'); |
|
7726 |
|
7727 if (nodes.length) { |
|
7728 parent = nodes[nodes.length - 1]; |
|
7729 } |
|
7730 |
|
7731 if (node.parentNode) { |
|
7732 node.parentNode.replaceChild(parent, node); |
|
7733 } |
|
7734 parent.appendChild(node); |
|
7735 }, |
|
7736 |
|
7737 unwrap: function(node) { |
|
7738 var parent = node.parentNode, |
|
7739 lastChild = parent.lastChild, |
|
7740 next = node, |
|
7741 grandparent; |
|
7742 |
|
7743 if (parent) { |
|
7744 grandparent = parent.parentNode; |
|
7745 if (grandparent) { |
|
7746 node = parent.firstChild; |
|
7747 while (node !== lastChild) { |
|
7748 next = node.nextSibling; |
|
7749 grandparent.insertBefore(node, parent); |
|
7750 node = next; |
|
7751 } |
|
7752 grandparent.replaceChild(lastChild, parent); |
|
7753 } else { |
|
7754 parent.removeChild(node); |
|
7755 } |
|
7756 } |
|
7757 } |
|
7758 }); |
|
7759 |
|
7760 addFeature('innerhtml', 'table', { |
|
7761 test: function() { |
|
7762 var node = Y.config.doc.createElement('table'); |
|
7763 try { |
|
7764 node.innerHTML = '<tbody></tbody>'; |
|
7765 } catch(e) { |
|
7766 return false; |
|
7767 } |
|
7768 return (node.firstChild && node.firstChild.nodeName === 'TBODY'); |
|
7769 } |
|
7770 }); |
|
7771 |
|
7772 addFeature('innerhtml-div', 'tr', { |
|
7773 test: function() { |
|
7774 return createFromDIV('<tr></tr>', 'tr'); |
|
7775 } |
|
7776 }); |
|
7777 |
|
7778 addFeature('innerhtml-div', 'script', { |
|
7779 test: function() { |
|
7780 return createFromDIV('<script></script>', 'script'); |
|
7781 } |
|
7782 }); |
|
7783 |
|
7784 if (!testFeature('innerhtml', 'table')) { |
|
7785 // TODO: thead/tfoot with nested tbody |
|
7786 // IE adds TBODY when creating TABLE elements (which may share this impl) |
|
7787 creators.tbody = function(html, doc) { |
|
7788 var frag = Y_DOM.create(TABLE_OPEN + html + TABLE_CLOSE, doc), |
|
7789 tb = Y.DOM._children(frag, 'tbody')[0]; |
|
7790 |
|
7791 if (frag.children.length > 1 && tb && !re_tbody.test(html)) { |
|
7792 tb.parentNode.removeChild(tb); // strip extraneous tbody |
|
7793 } |
|
7794 return frag; |
|
7795 }; |
|
7796 } |
|
7797 |
|
7798 if (!testFeature('innerhtml-div', 'script')) { |
|
7799 creators.script = function(html, doc) { |
|
7800 var frag = doc.createElement('div'); |
|
7801 |
|
7802 frag.innerHTML = '-' + html; |
|
7803 frag.removeChild(frag.firstChild); |
|
7804 return frag; |
|
7805 }; |
|
7806 |
|
7807 creators.link = creators.style = creators.script; |
|
7808 } |
|
7809 |
|
7810 if (!testFeature('innerhtml-div', 'tr')) { |
|
7811 Y.mix(creators, { |
|
7812 option: function(html, doc) { |
|
7813 return Y_DOM.create('<select><option class="yui3-big-dummy" selected></option>' + html + '</select>', doc); |
|
7814 }, |
|
7815 |
|
7816 tr: function(html, doc) { |
|
7817 return Y_DOM.create('<tbody>' + html + '</tbody>', doc); |
|
7818 }, |
|
7819 |
|
7820 td: function(html, doc) { |
|
7821 return Y_DOM.create('<tr>' + html + '</tr>', doc); |
|
7822 }, |
|
7823 |
|
7824 col: function(html, doc) { |
|
7825 return Y_DOM.create('<colgroup>' + html + '</colgroup>', doc); |
|
7826 }, |
|
7827 |
|
7828 tbody: 'table' |
|
7829 }); |
|
7830 |
|
7831 Y.mix(creators, { |
|
7832 legend: 'fieldset', |
|
7833 th: creators.td, |
|
7834 thead: creators.tbody, |
|
7835 tfoot: creators.tbody, |
|
7836 caption: creators.tbody, |
|
7837 colgroup: creators.tbody, |
|
7838 optgroup: creators.option |
|
7839 }); |
|
7840 } |
|
7841 |
|
7842 Y_DOM.creators = creators; |
|
7843 Y.mix(Y.DOM, { |
|
7844 /** |
|
7845 * Sets the width of the element to the given size, regardless |
|
7846 * of box model, border, padding, etc. |
|
7847 * @method setWidth |
|
7848 * @param {HTMLElement} element The DOM element. |
|
7849 * @param {String|Number} size The pixel height to size to |
|
7850 */ |
|
7851 |
|
7852 setWidth: function(node, size) { |
|
7853 Y.DOM._setSize(node, 'width', size); |
|
7854 }, |
|
7855 |
|
7856 /** |
|
7857 * Sets the height of the element to the given size, regardless |
|
7858 * of box model, border, padding, etc. |
|
7859 * @method setHeight |
|
7860 * @param {HTMLElement} element The DOM element. |
|
7861 * @param {String|Number} size The pixel height to size to |
|
7862 */ |
|
7863 |
|
7864 setHeight: function(node, size) { |
|
7865 Y.DOM._setSize(node, 'height', size); |
|
7866 }, |
|
7867 |
|
7868 _setSize: function(node, prop, val) { |
|
7869 val = (val > 0) ? val : 0; |
|
7870 var size = 0; |
|
7871 |
|
7872 node.style[prop] = val + 'px'; |
|
7873 size = (prop === 'height') ? node.offsetHeight : node.offsetWidth; |
|
7874 |
|
7875 if (size > val) { |
|
7876 val = val - (size - val); |
|
7877 |
|
7878 if (val < 0) { |
|
7879 val = 0; |
|
7880 } |
|
7881 |
|
7882 node.style[prop] = val + 'px'; |
|
7883 } |
|
7884 } |
|
7885 }); |
|
7886 |
|
7887 |
|
7888 }, '@VERSION@', {"requires": ["dom-core"]}); |
|
7889 YUI.add('color-base', function (Y, NAME) { |
|
7890 |
|
7891 /** |
|
7892 Color provides static methods for color conversion. |
|
7893 |
|
7894 Y.Color.toRGB('f00'); // rgb(255, 0, 0) |
|
7895 |
|
7896 Y.Color.toHex('rgb(255, 255, 0)'); // #ffff00 |
|
7897 |
|
7898 @module color |
|
7899 @submodule color-base |
|
7900 @class Color |
|
7901 @since 3.8.0 |
|
7902 **/ |
|
7903 |
|
7904 var REGEX_HEX = /^#?([\da-fA-F]{2})([\da-fA-F]{2})([\da-fA-F]{2})(\ufffe)?/, |
|
7905 REGEX_HEX3 = /^#?([\da-fA-F]{1})([\da-fA-F]{1})([\da-fA-F]{1})(\ufffe)?/, |
|
7906 REGEX_RGB = /rgba?\(([\d]{1,3}), ?([\d]{1,3}), ?([\d]{1,3}),? ?([.\d]*)?\)/, |
|
7907 TYPES = { 'HEX': 'hex', 'RGB': 'rgb', 'RGBA': 'rgba' }, |
|
7908 CONVERTS = { 'hex': 'toHex', 'rgb': 'toRGB', 'rgba': 'toRGBA' }; |
|
7909 |
|
7910 |
|
7911 Y.Color = { |
|
7912 /** |
|
7913 @static |
|
7914 @property KEYWORDS |
|
7915 @type Object |
|
7916 @since 3.8.0 |
|
7917 **/ |
|
7918 KEYWORDS: { |
|
7919 'black': '000', 'silver': 'c0c0c0', 'gray': '808080', 'white': 'fff', |
|
7920 'maroon': '800000', 'red': 'f00', 'purple': '800080', 'fuchsia': 'f0f', |
|
7921 'green': '008000', 'lime': '0f0', 'olive': '808000', 'yellow': 'ff0', |
|
7922 'navy': '000080', 'blue': '00f', 'teal': '008080', 'aqua': '0ff' |
|
7923 }, |
|
7924 |
|
7925 /** |
|
7926 NOTE: `(\ufffe)?` is added to the Regular Expression to carve out a |
|
7927 place for the alpha channel that is returned from toArray |
|
7928 without compromising any usage of the Regular Expression |
|
7929 |
|
7930 @static |
|
7931 @property REGEX_HEX |
|
7932 @type RegExp |
|
7933 @default /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})(\ufffe)?/ |
|
7934 @since 3.8.0 |
|
7935 **/ |
|
7936 REGEX_HEX: REGEX_HEX, |
|
7937 |
|
7938 /** |
|
7939 NOTE: `(\ufffe)?` is added to the Regular Expression to carve out a |
|
7940 place for the alpha channel that is returned from toArray |
|
7941 without compromising any usage of the Regular Expression |
|
7942 |
|
7943 @static |
|
7944 @property REGEX_HEX3 |
|
7945 @type RegExp |
|
7946 @default /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})(\ufffe)?/ |
|
7947 @since 3.8.0 |
|
7948 **/ |
|
7949 REGEX_HEX3: REGEX_HEX3, |
|
7950 |
|
7951 /** |
|
7952 @static |
|
7953 @property REGEX_RGB |
|
7954 @type RegExp |
|
7955 @default /rgba?\(([0-9]{1,3}), ?([0-9]{1,3}), ?([0-9]{1,3}),? ?([.0-9]{1,3})?\)/ |
|
7956 @since 3.8.0 |
|
7957 **/ |
|
7958 REGEX_RGB: REGEX_RGB, |
|
7959 |
|
7960 re_RGB: REGEX_RGB, |
|
7961 |
|
7962 re_hex: REGEX_HEX, |
|
7963 |
|
7964 re_hex3: REGEX_HEX3, |
|
7965 |
|
7966 /** |
|
7967 @static |
|
7968 @property STR_HEX |
|
7969 @type String |
|
7970 @default #{*}{*}{*} |
|
7971 @since 3.8.0 |
|
7972 **/ |
|
7973 STR_HEX: '#{*}{*}{*}', |
|
7974 |
|
7975 /** |
|
7976 @static |
|
7977 @property STR_RGB |
|
7978 @type String |
|
7979 @default rgb({*}, {*}, {*}) |
|
7980 @since 3.8.0 |
|
7981 **/ |
|
7982 STR_RGB: 'rgb({*}, {*}, {*})', |
|
7983 |
|
7984 /** |
|
7985 @static |
|
7986 @property STR_RGBA |
|
7987 @type String |
|
7988 @default rgba({*}, {*}, {*}, {*}) |
|
7989 @since 3.8.0 |
|
7990 **/ |
|
7991 STR_RGBA: 'rgba({*}, {*}, {*}, {*})', |
|
7992 |
|
7993 /** |
|
7994 @static |
|
7995 @property TYPES |
|
7996 @type Object |
|
7997 @default {'rgb':'rgb', 'rgba':'rgba'} |
|
7998 @since 3.8.0 |
|
7999 **/ |
|
8000 TYPES: TYPES, |
|
8001 |
|
8002 /** |
|
8003 @static |
|
8004 @property CONVERTS |
|
8005 @type Object |
|
8006 @default {} |
|
8007 @since 3.8.0 |
|
8008 **/ |
|
8009 CONVERTS: CONVERTS, |
|
8010 |
|
8011 /** |
|
8012 Converts the provided string to the provided type. |
|
8013 You can use the `Y.Color.TYPES` to get a valid `to` type. |
|
8014 If the color cannot be converted, the original color will be returned. |
|
8015 |
|
8016 @public |
|
8017 @method convert |
|
8018 @param {String} str |
|
8019 @param {String} to |
|
8020 @return {String} |
|
8021 @since 3.8.0 |
|
8022 **/ |
|
8023 convert: function (str, to) { |
|
8024 var convert = Y.Color.CONVERTS[to.toLowerCase()], |
|
8025 clr = str; |
|
8026 |
|
8027 if (convert && Y.Color[convert]) { |
|
8028 clr = Y.Color[convert](str); |
|
8029 } |
|
8030 |
|
8031 return clr; |
|
8032 }, |
|
8033 |
|
8034 /** |
|
8035 Converts provided color value to a hex value string |
|
8036 |
|
8037 @public |
|
8038 @method toHex |
|
8039 @param {String} str Hex or RGB value string |
|
8040 @return {String} returns array of values or CSS string if options.css is true |
|
8041 @since 3.8.0 |
|
8042 **/ |
|
8043 toHex: function (str) { |
|
8044 var clr = Y.Color._convertTo(str, 'hex'), |
|
8045 isTransparent = clr.toLowerCase() === 'transparent'; |
|
8046 |
|
8047 if (clr.charAt(0) !== '#' && !isTransparent) { |
|
8048 clr = '#' + clr; |
|
8049 } |
|
8050 |
|
8051 return isTransparent ? clr.toLowerCase() : clr.toUpperCase(); |
|
8052 }, |
|
8053 |
|
8054 /** |
|
8055 Converts provided color value to an RGB value string |
|
8056 @public |
|
8057 @method toRGB |
|
8058 @param {String} str Hex or RGB value string |
|
8059 @return {String} |
|
8060 @since 3.8.0 |
|
8061 **/ |
|
8062 toRGB: function (str) { |
|
8063 var clr = Y.Color._convertTo(str, 'rgb'); |
|
8064 return clr.toLowerCase(); |
|
8065 }, |
|
8066 |
|
8067 /** |
|
8068 Converts provided color value to an RGB value string |
|
8069 @public |
|
8070 @method toRGBA |
|
8071 @param {String} str Hex or RGB value string |
|
8072 @return {String} |
|
8073 @since 3.8.0 |
|
8074 **/ |
|
8075 toRGBA: function (str) { |
|
8076 var clr = Y.Color._convertTo(str, 'rgba' ); |
|
8077 return clr.toLowerCase(); |
|
8078 }, |
|
8079 |
|
8080 /** |
|
8081 Converts the provided color string to an array of values where the |
|
8082 last value is the alpha value. Will return an empty array if |
|
8083 the provided string is not able to be parsed. |
|
8084 |
|
8085 NOTE: `(\ufffe)?` is added to `HEX` and `HEX3` Regular Expressions to |
|
8086 carve out a place for the alpha channel that is returned from |
|
8087 toArray without compromising any usage of the Regular Expression |
|
8088 |
|
8089 Y.Color.toArray('fff'); // ['ff', 'ff', 'ff', 1] |
|
8090 Y.Color.toArray('rgb(0, 0, 0)'); // ['0', '0', '0', 1] |
|
8091 Y.Color.toArray('rgba(0, 0, 0, 0)'); // ['0', '0', '0', 1] |
|
8092 |
|
8093 |
|
8094 |
|
8095 @public |
|
8096 @method toArray |
|
8097 @param {String} str |
|
8098 @return {Array} |
|
8099 @since 3.8.0 |
|
8100 **/ |
|
8101 toArray: function(str) { |
|
8102 // parse with regex and return "matches" array |
|
8103 var type = Y.Color.findType(str).toUpperCase(), |
|
8104 regex, |
|
8105 arr, |
|
8106 length, |
|
8107 lastItem; |
|
8108 |
|
8109 if (type === 'HEX' && str.length < 5) { |
|
8110 type = 'HEX3'; |
|
8111 } |
|
8112 |
|
8113 if (type.charAt(type.length - 1) === 'A') { |
|
8114 type = type.slice(0, -1); |
|
8115 } |
|
8116 |
|
8117 regex = Y.Color['REGEX_' + type]; |
|
8118 |
|
8119 if (regex) { |
|
8120 arr = regex.exec(str) || []; |
|
8121 length = arr.length; |
|
8122 |
|
8123 if (length) { |
|
8124 |
|
8125 arr.shift(); |
|
8126 length--; |
|
8127 |
|
8128 if (type === 'HEX3') { |
|
8129 arr[0] += arr[0]; |
|
8130 arr[1] += arr[1]; |
|
8131 arr[2] += arr[2]; |
|
8132 } |
|
8133 |
|
8134 lastItem = arr[length - 1]; |
|
8135 if (!lastItem) { |
|
8136 arr[length - 1] = 1; |
|
8137 } |
|
8138 } |
|
8139 } |
|
8140 |
|
8141 return arr; |
|
8142 |
|
8143 }, |
|
8144 |
|
8145 /** |
|
8146 Converts the array of values to a string based on the provided template. |
|
8147 @public |
|
8148 @method fromArray |
|
8149 @param {Array} arr |
|
8150 @param {String} template |
|
8151 @return {String} |
|
8152 @since 3.8.0 |
|
8153 **/ |
|
8154 fromArray: function(arr, template) { |
|
8155 arr = arr.concat(); |
|
8156 |
|
8157 if (typeof template === 'undefined') { |
|
8158 return arr.join(', '); |
|
8159 } |
|
8160 |
|
8161 var replace = '{*}'; |
|
8162 |
|
8163 template = Y.Color['STR_' + template.toUpperCase()]; |
|
8164 |
|
8165 if (arr.length === 3 && template.match(/\{\*\}/g).length === 4) { |
|
8166 arr.push(1); |
|
8167 } |
|
8168 |
|
8169 while ( template.indexOf(replace) >= 0 && arr.length > 0) { |
|
8170 template = template.replace(replace, arr.shift()); |
|
8171 } |
|
8172 |
|
8173 return template; |
|
8174 }, |
|
8175 |
|
8176 /** |
|
8177 Finds the value type based on the str value provided. |
|
8178 @public |
|
8179 @method findType |
|
8180 @param {String} str |
|
8181 @return {String} |
|
8182 @since 3.8.0 |
|
8183 **/ |
|
8184 findType: function (str) { |
|
8185 if (Y.Color.KEYWORDS[str]) { |
|
8186 return 'keyword'; |
|
8187 } |
|
8188 |
|
8189 var index = str.indexOf('('), |
|
8190 key; |
|
8191 |
|
8192 if (index > 0) { |
|
8193 key = str.substr(0, index); |
|
8194 } |
|
8195 |
|
8196 if (key && Y.Color.TYPES[key.toUpperCase()]) { |
|
8197 return Y.Color.TYPES[key.toUpperCase()]; |
|
8198 } |
|
8199 |
|
8200 return 'hex'; |
|
8201 |
|
8202 }, // return 'keyword', 'hex', 'rgb' |
|
8203 |
|
8204 /** |
|
8205 Retrives the alpha channel from the provided string. If no alpha |
|
8206 channel is present, `1` will be returned. |
|
8207 @protected |
|
8208 @method _getAlpha |
|
8209 @param {String} clr |
|
8210 @return {Number} |
|
8211 @since 3.8.0 |
|
8212 **/ |
|
8213 _getAlpha: function (clr) { |
|
8214 var alpha, |
|
8215 arr = Y.Color.toArray(clr); |
|
8216 |
|
8217 if (arr.length > 3) { |
|
8218 alpha = arr.pop(); |
|
8219 } |
|
8220 |
|
8221 return +alpha || 1; |
|
8222 }, |
|
8223 |
|
8224 /** |
|
8225 Returns the hex value string if found in the KEYWORDS object |
|
8226 @protected |
|
8227 @method _keywordToHex |
|
8228 @param {String} clr |
|
8229 @return {String} |
|
8230 @since 3.8.0 |
|
8231 **/ |
|
8232 _keywordToHex: function (clr) { |
|
8233 var keyword = Y.Color.KEYWORDS[clr]; |
|
8234 |
|
8235 if (keyword) { |
|
8236 return keyword; |
|
8237 } |
|
8238 }, |
|
8239 |
|
8240 /** |
|
8241 Converts the provided color string to the value type provided as `to` |
|
8242 @protected |
|
8243 @method _convertTo |
|
8244 @param {String} clr |
|
8245 @param {String} to |
|
8246 @return {String} |
|
8247 @since 3.8.0 |
|
8248 **/ |
|
8249 _convertTo: function(clr, to) { |
|
8250 |
|
8251 if (clr === 'transparent') { |
|
8252 return clr; |
|
8253 } |
|
8254 |
|
8255 var from = Y.Color.findType(clr), |
|
8256 originalTo = to, |
|
8257 needsAlpha, |
|
8258 alpha, |
|
8259 method, |
|
8260 ucTo; |
|
8261 |
|
8262 if (from === 'keyword') { |
|
8263 clr = Y.Color._keywordToHex(clr); |
|
8264 from = 'hex'; |
|
8265 } |
|
8266 |
|
8267 if (from === 'hex' && clr.length < 5) { |
|
8268 if (clr.charAt(0) === '#') { |
|
8269 clr = clr.substr(1); |
|
8270 } |
|
8271 |
|
8272 clr = '#' + clr.charAt(0) + clr.charAt(0) + |
|
8273 clr.charAt(1) + clr.charAt(1) + |
|
8274 clr.charAt(2) + clr.charAt(2); |
|
8275 } |
|
8276 |
|
8277 if (from === to) { |
|
8278 return clr; |
|
8279 } |
|
8280 |
|
8281 if (from.charAt(from.length - 1) === 'a') { |
|
8282 from = from.slice(0, -1); |
|
8283 } |
|
8284 |
|
8285 needsAlpha = (to.charAt(to.length - 1) === 'a'); |
|
8286 if (needsAlpha) { |
|
8287 to = to.slice(0, -1); |
|
8288 alpha = Y.Color._getAlpha(clr); |
|
8289 } |
|
8290 |
|
8291 ucTo = to.charAt(0).toUpperCase() + to.substr(1).toLowerCase(); |
|
8292 method = Y.Color['_' + from + 'To' + ucTo ]; |
|
8293 |
|
8294 // check to see if need conversion to rgb first |
|
8295 // check to see if there is a direct conversion method |
|
8296 // convertions are: hex <-> rgb <-> hsl |
|
8297 if (!method) { |
|
8298 if (from !== 'rgb' && to !== 'rgb') { |
|
8299 clr = Y.Color['_' + from + 'ToRgb'](clr); |
|
8300 from = 'rgb'; |
|
8301 method = Y.Color['_' + from + 'To' + ucTo ]; |
|
8302 } |
|
8303 } |
|
8304 |
|
8305 if (method) { |
|
8306 clr = ((method)(clr, needsAlpha)); |
|
8307 } |
|
8308 |
|
8309 // process clr from arrays to strings after conversions if alpha is needed |
|
8310 if (needsAlpha) { |
|
8311 if (!Y.Lang.isArray(clr)) { |
|
8312 clr = Y.Color.toArray(clr); |
|
8313 } |
|
8314 clr.push(alpha); |
|
8315 clr = Y.Color.fromArray(clr, originalTo.toUpperCase()); |
|
8316 } |
|
8317 |
|
8318 return clr; |
|
8319 }, |
|
8320 |
|
8321 /** |
|
8322 Processes the hex string into r, g, b values. Will return values as |
|
8323 an array, or as an rgb string. |
|
8324 @protected |
|
8325 @method _hexToRgb |
|
8326 @param {String} str |
|
8327 @param {Boolean} [toArray] |
|
8328 @return {String|Array} |
|
8329 @since 3.8.0 |
|
8330 **/ |
|
8331 _hexToRgb: function (str, toArray) { |
|
8332 var r, g, b; |
|
8333 |
|
8334 /*jshint bitwise:false*/ |
|
8335 if (str.charAt(0) === '#') { |
|
8336 str = str.substr(1); |
|
8337 } |
|
8338 |
|
8339 str = parseInt(str, 16); |
|
8340 |
|
8341 r = str >> 16; |
|
8342 g = str >> 8 & 0xFF; |
|
8343 b = str & 0xFF; |
|
8344 |
|
8345 if (toArray) { |
|
8346 return [r, g, b]; |
|
8347 } |
|
8348 |
|
8349 return 'rgb(' + r + ', ' + g + ', ' + b + ')'; |
|
8350 }, |
|
8351 |
|
8352 /** |
|
8353 Processes the rgb string into r, g, b values. Will return values as |
|
8354 an array, or as a hex string. |
|
8355 @protected |
|
8356 @method _rgbToHex |
|
8357 @param {String} str |
|
8358 @param {Boolean} [toArray] |
|
8359 @return {String|Array} |
|
8360 @since 3.8.0 |
|
8361 **/ |
|
8362 _rgbToHex: function (str) { |
|
8363 /*jshint bitwise:false*/ |
|
8364 var rgb = Y.Color.toArray(str), |
|
8365 hex = rgb[2] | (rgb[1] << 8) | (rgb[0] << 16); |
|
8366 |
|
8367 hex = (+hex).toString(16); |
|
8368 |
|
8369 while (hex.length < 6) { |
|
8370 hex = '0' + hex; |
|
8371 } |
|
8372 |
|
8373 return '#' + hex; |
|
8374 } |
|
8375 |
|
8376 }; |
|
8377 |
|
8378 |
|
8379 |
|
8380 }, '@VERSION@', {"requires": ["yui-base"]}); |
|
8381 YUI.add('dom-style', function (Y, NAME) { |
|
8382 |
|
8383 (function(Y) { |
|
8384 /** |
|
8385 * Add style management functionality to DOM. |
|
8386 * @module dom |
|
8387 * @submodule dom-style |
|
8388 * @for DOM |
|
8389 */ |
|
8390 |
|
8391 var DOCUMENT_ELEMENT = 'documentElement', |
|
8392 DEFAULT_VIEW = 'defaultView', |
|
8393 OWNER_DOCUMENT = 'ownerDocument', |
|
8394 STYLE = 'style', |
|
8395 FLOAT = 'float', |
|
8396 CSS_FLOAT = 'cssFloat', |
|
8397 STYLE_FLOAT = 'styleFloat', |
|
8398 TRANSPARENT = 'transparent', |
|
8399 GET_COMPUTED_STYLE = 'getComputedStyle', |
|
8400 GET_BOUNDING_CLIENT_RECT = 'getBoundingClientRect', |
|
8401 |
|
8402 WINDOW = Y.config.win, |
|
8403 DOCUMENT = Y.config.doc, |
|
8404 UNDEFINED = undefined, |
|
8405 |
|
8406 Y_DOM = Y.DOM, |
|
8407 |
|
8408 TRANSFORM = 'transform', |
|
8409 TRANSFORMORIGIN = 'transformOrigin', |
|
8410 VENDOR_TRANSFORM = [ |
|
8411 'WebkitTransform', |
|
8412 'MozTransform', |
|
8413 'OTransform', |
|
8414 'msTransform' |
|
8415 ], |
|
8416 |
|
8417 re_color = /color$/i, |
|
8418 re_unit = /width|height|top|left|right|bottom|margin|padding/i; |
|
8419 |
|
8420 Y.Array.each(VENDOR_TRANSFORM, function(val) { |
|
8421 if (val in DOCUMENT[DOCUMENT_ELEMENT].style) { |
|
8422 TRANSFORM = val; |
|
8423 TRANSFORMORIGIN = val + "Origin"; |
|
8424 } |
|
8425 }); |
|
8426 |
|
8427 Y.mix(Y_DOM, { |
|
8428 DEFAULT_UNIT: 'px', |
|
8429 |
|
8430 CUSTOM_STYLES: { |
|
8431 }, |
|
8432 |
|
8433 |
|
8434 /** |
|
8435 * Sets a style property for a given element. |
|
8436 * @method setStyle |
|
8437 * @param {HTMLElement} An HTMLElement to apply the style to. |
|
8438 * @param {String} att The style property to set. |
|
8439 * @param {String|Number} val The value. |
|
8440 */ |
|
8441 setStyle: function(node, att, val, style) { |
|
8442 style = style || node.style; |
|
8443 var CUSTOM_STYLES = Y_DOM.CUSTOM_STYLES; |
|
8444 |
|
8445 if (style) { |
|
8446 if (val === null || val === '') { // normalize unsetting |
|
8447 val = ''; |
|
8448 } else if (!isNaN(new Number(val)) && re_unit.test(att)) { // number values may need a unit |
|
8449 val += Y_DOM.DEFAULT_UNIT; |
|
8450 } |
|
8451 |
|
8452 if (att in CUSTOM_STYLES) { |
|
8453 if (CUSTOM_STYLES[att].set) { |
|
8454 CUSTOM_STYLES[att].set(node, val, style); |
|
8455 return; // NOTE: return |
|
8456 } else if (typeof CUSTOM_STYLES[att] === 'string') { |
|
8457 att = CUSTOM_STYLES[att]; |
|
8458 } |
|
8459 } else if (att === '') { // unset inline styles |
|
8460 att = 'cssText'; |
|
8461 val = ''; |
|
8462 } |
|
8463 style[att] = val; |
|
8464 } |
|
8465 }, |
|
8466 |
|
8467 /** |
|
8468 * Returns the current style value for the given property. |
|
8469 * @method getStyle |
|
8470 * @param {HTMLElement} An HTMLElement to get the style from. |
|
8471 * @param {String} att The style property to get. |
|
8472 */ |
|
8473 getStyle: function(node, att, style) { |
|
8474 style = style || node.style; |
|
8475 var CUSTOM_STYLES = Y_DOM.CUSTOM_STYLES, |
|
8476 val = ''; |
|
8477 |
|
8478 if (style) { |
|
8479 if (att in CUSTOM_STYLES) { |
|
8480 if (CUSTOM_STYLES[att].get) { |
|
8481 return CUSTOM_STYLES[att].get(node, att, style); // NOTE: return |
|
8482 } else if (typeof CUSTOM_STYLES[att] === 'string') { |
|
8483 att = CUSTOM_STYLES[att]; |
|
8484 } |
|
8485 } |
|
8486 val = style[att]; |
|
8487 if (val === '') { // TODO: is empty string sufficient? |
|
8488 val = Y_DOM[GET_COMPUTED_STYLE](node, att); |
|
8489 } |
|
8490 } |
|
8491 |
|
8492 return val; |
|
8493 }, |
|
8494 |
|
8495 /** |
|
8496 * Sets multiple style properties. |
|
8497 * @method setStyles |
|
8498 * @param {HTMLElement} node An HTMLElement to apply the styles to. |
|
8499 * @param {Object} hash An object literal of property:value pairs. |
|
8500 */ |
|
8501 setStyles: function(node, hash) { |
|
8502 var style = node.style; |
|
8503 Y.each(hash, function(v, n) { |
|
8504 Y_DOM.setStyle(node, n, v, style); |
|
8505 }, Y_DOM); |
|
8506 }, |
|
8507 |
|
8508 /** |
|
8509 * Returns the computed style for the given node. |
|
8510 * @method getComputedStyle |
|
8511 * @param {HTMLElement} An HTMLElement to get the style from. |
|
8512 * @param {String} att The style property to get. |
|
8513 * @return {String} The computed value of the style property. |
|
8514 */ |
|
8515 getComputedStyle: function(node, att) { |
|
8516 var val = '', |
|
8517 doc = node[OWNER_DOCUMENT], |
|
8518 computed; |
|
8519 |
|
8520 if (node[STYLE] && doc[DEFAULT_VIEW] && doc[DEFAULT_VIEW][GET_COMPUTED_STYLE]) { |
|
8521 computed = doc[DEFAULT_VIEW][GET_COMPUTED_STYLE](node, null); |
|
8522 if (computed) { // FF may be null in some cases (ticket #2530548) |
|
8523 val = computed[att]; |
|
8524 } |
|
8525 } |
|
8526 return val; |
|
8527 } |
|
8528 }); |
|
8529 |
|
8530 // normalize reserved word float alternatives ("cssFloat" or "styleFloat") |
|
8531 if (DOCUMENT[DOCUMENT_ELEMENT][STYLE][CSS_FLOAT] !== UNDEFINED) { |
|
8532 Y_DOM.CUSTOM_STYLES[FLOAT] = CSS_FLOAT; |
|
8533 } else if (DOCUMENT[DOCUMENT_ELEMENT][STYLE][STYLE_FLOAT] !== UNDEFINED) { |
|
8534 Y_DOM.CUSTOM_STYLES[FLOAT] = STYLE_FLOAT; |
|
8535 } |
|
8536 |
|
8537 // fix opera computedStyle default color unit (convert to rgb) |
|
8538 if (Y.UA.opera) { |
|
8539 Y_DOM[GET_COMPUTED_STYLE] = function(node, att) { |
|
8540 var view = node[OWNER_DOCUMENT][DEFAULT_VIEW], |
|
8541 val = view[GET_COMPUTED_STYLE](node, '')[att]; |
|
8542 |
|
8543 if (re_color.test(att)) { |
|
8544 val = Y.Color.toRGB(val); |
|
8545 } |
|
8546 |
|
8547 return val; |
|
8548 }; |
|
8549 |
|
8550 } |
|
8551 |
|
8552 // safari converts transparent to rgba(), others use "transparent" |
|
8553 if (Y.UA.webkit) { |
|
8554 Y_DOM[GET_COMPUTED_STYLE] = function(node, att) { |
|
8555 var view = node[OWNER_DOCUMENT][DEFAULT_VIEW], |
|
8556 val = view[GET_COMPUTED_STYLE](node, '')[att]; |
|
8557 |
|
8558 if (val === 'rgba(0, 0, 0, 0)') { |
|
8559 val = TRANSPARENT; |
|
8560 } |
|
8561 |
|
8562 return val; |
|
8563 }; |
|
8564 |
|
8565 } |
|
8566 |
|
8567 Y.DOM._getAttrOffset = function(node, attr) { |
|
8568 var val = Y.DOM[GET_COMPUTED_STYLE](node, attr), |
|
8569 offsetParent = node.offsetParent, |
|
8570 position, |
|
8571 parentOffset, |
|
8572 offset; |
|
8573 |
|
8574 if (val === 'auto') { |
|
8575 position = Y.DOM.getStyle(node, 'position'); |
|
8576 if (position === 'static' || position === 'relative') { |
|
8577 val = 0; |
|
8578 } else if (offsetParent && offsetParent[GET_BOUNDING_CLIENT_RECT]) { |
|
8579 parentOffset = offsetParent[GET_BOUNDING_CLIENT_RECT]()[attr]; |
|
8580 offset = node[GET_BOUNDING_CLIENT_RECT]()[attr]; |
|
8581 if (attr === 'left' || attr === 'top') { |
|
8582 val = offset - parentOffset; |
|
8583 } else { |
|
8584 val = parentOffset - node[GET_BOUNDING_CLIENT_RECT]()[attr]; |
|
8585 } |
|
8586 } |
|
8587 } |
|
8588 |
|
8589 return val; |
|
8590 }; |
|
8591 |
|
8592 Y.DOM._getOffset = function(node) { |
|
8593 var pos, |
|
8594 xy = null; |
|
8595 |
|
8596 if (node) { |
|
8597 pos = Y_DOM.getStyle(node, 'position'); |
|
8598 xy = [ |
|
8599 parseInt(Y_DOM[GET_COMPUTED_STYLE](node, 'left'), 10), |
|
8600 parseInt(Y_DOM[GET_COMPUTED_STYLE](node, 'top'), 10) |
|
8601 ]; |
|
8602 |
|
8603 if ( isNaN(xy[0]) ) { // in case of 'auto' |
|
8604 xy[0] = parseInt(Y_DOM.getStyle(node, 'left'), 10); // try inline |
|
8605 if ( isNaN(xy[0]) ) { // default to offset value |
|
8606 xy[0] = (pos === 'relative') ? 0 : node.offsetLeft || 0; |
|
8607 } |
|
8608 } |
|
8609 |
|
8610 if ( isNaN(xy[1]) ) { // in case of 'auto' |
|
8611 xy[1] = parseInt(Y_DOM.getStyle(node, 'top'), 10); // try inline |
|
8612 if ( isNaN(xy[1]) ) { // default to offset value |
|
8613 xy[1] = (pos === 'relative') ? 0 : node.offsetTop || 0; |
|
8614 } |
|
8615 } |
|
8616 } |
|
8617 |
|
8618 return xy; |
|
8619 |
|
8620 }; |
|
8621 |
|
8622 Y_DOM.CUSTOM_STYLES.transform = { |
|
8623 set: function(node, val, style) { |
|
8624 style[TRANSFORM] = val; |
|
8625 }, |
|
8626 |
|
8627 get: function(node, style) { |
|
8628 return Y_DOM[GET_COMPUTED_STYLE](node, TRANSFORM); |
|
8629 } |
|
8630 }; |
|
8631 |
|
8632 Y_DOM.CUSTOM_STYLES.transformOrigin = { |
|
8633 set: function(node, val, style) { |
|
8634 style[TRANSFORMORIGIN] = val; |
|
8635 }, |
|
8636 |
|
8637 get: function(node, style) { |
|
8638 return Y_DOM[GET_COMPUTED_STYLE](node, TRANSFORMORIGIN); |
|
8639 } |
|
8640 }; |
|
8641 |
|
8642 |
|
8643 })(Y); |
|
8644 |
|
8645 |
|
8646 }, '@VERSION@', {"requires": ["dom-base", "color-base"]}); |
|
8647 YUI.add('dom-style-ie', function (Y, NAME) { |
|
8648 |
|
8649 (function(Y) { |
|
8650 var HAS_LAYOUT = 'hasLayout', |
|
8651 PX = 'px', |
|
8652 FILTER = 'filter', |
|
8653 FILTERS = 'filters', |
|
8654 OPACITY = 'opacity', |
|
8655 AUTO = 'auto', |
|
8656 |
|
8657 BORDER_WIDTH = 'borderWidth', |
|
8658 BORDER_TOP_WIDTH = 'borderTopWidth', |
|
8659 BORDER_RIGHT_WIDTH = 'borderRightWidth', |
|
8660 BORDER_BOTTOM_WIDTH = 'borderBottomWidth', |
|
8661 BORDER_LEFT_WIDTH = 'borderLeftWidth', |
|
8662 WIDTH = 'width', |
|
8663 HEIGHT = 'height', |
|
8664 TRANSPARENT = 'transparent', |
|
8665 VISIBLE = 'visible', |
|
8666 GET_COMPUTED_STYLE = 'getComputedStyle', |
|
8667 UNDEFINED = undefined, |
|
8668 documentElement = Y.config.doc.documentElement, |
|
8669 |
|
8670 testFeature = Y.Features.test, |
|
8671 addFeature = Y.Features.add, |
|
8672 |
|
8673 // TODO: unit-less lineHeight (e.g. 1.22) |
|
8674 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, |
|
8675 |
|
8676 isIE8 = (Y.UA.ie >= 8), |
|
8677 |
|
8678 _getStyleObj = function(node) { |
|
8679 return node.currentStyle || node.style; |
|
8680 }, |
|
8681 |
|
8682 ComputedStyle = { |
|
8683 CUSTOM_STYLES: {}, |
|
8684 |
|
8685 get: function(el, property) { |
|
8686 var value = '', |
|
8687 current; |
|
8688 |
|
8689 if (el) { |
|
8690 current = _getStyleObj(el)[property]; |
|
8691 |
|
8692 if (property === OPACITY && Y.DOM.CUSTOM_STYLES[OPACITY]) { |
|
8693 value = Y.DOM.CUSTOM_STYLES[OPACITY].get(el); |
|
8694 } else if (!current || (current.indexOf && current.indexOf(PX) > -1)) { // no need to convert |
|
8695 value = current; |
|
8696 } else if (Y.DOM.IE.COMPUTED[property]) { // use compute function |
|
8697 value = Y.DOM.IE.COMPUTED[property](el, property); |
|
8698 } else if (re_unit.test(current)) { // convert to pixel |
|
8699 value = ComputedStyle.getPixel(el, property) + PX; |
|
8700 } else { |
|
8701 value = current; |
|
8702 } |
|
8703 } |
|
8704 |
|
8705 return value; |
|
8706 }, |
|
8707 |
|
8708 sizeOffsets: { |
|
8709 width: ['Left', 'Right'], |
|
8710 height: ['Top', 'Bottom'], |
|
8711 top: ['Top'], |
|
8712 bottom: ['Bottom'] |
|
8713 }, |
|
8714 |
|
8715 getOffset: function(el, prop) { |
|
8716 var current = _getStyleObj(el)[prop], // value of "width", "top", etc. |
|
8717 capped = prop.charAt(0).toUpperCase() + prop.substr(1), // "Width", "Top", etc. |
|
8718 offset = 'offset' + capped, // "offsetWidth", "offsetTop", etc. |
|
8719 pixel = 'pixel' + capped, // "pixelWidth", "pixelTop", etc. |
|
8720 sizeOffsets = ComputedStyle.sizeOffsets[prop], |
|
8721 mode = el.ownerDocument.compatMode, |
|
8722 value = ''; |
|
8723 |
|
8724 // IE pixelWidth incorrect for percent |
|
8725 // manually compute by subtracting padding and border from offset size |
|
8726 // NOTE: clientWidth/Height (size minus border) is 0 when current === AUTO so offsetHeight is used |
|
8727 // reverting to auto from auto causes position stacking issues (old impl) |
|
8728 if (current === AUTO || current.indexOf('%') > -1) { |
|
8729 value = el['offset' + capped]; |
|
8730 |
|
8731 if (mode !== 'BackCompat') { |
|
8732 if (sizeOffsets[0]) { |
|
8733 value -= ComputedStyle.getPixel(el, 'padding' + sizeOffsets[0]); |
|
8734 value -= ComputedStyle.getBorderWidth(el, 'border' + sizeOffsets[0] + 'Width', 1); |
|
8735 } |
|
8736 |
|
8737 if (sizeOffsets[1]) { |
|
8738 value -= ComputedStyle.getPixel(el, 'padding' + sizeOffsets[1]); |
|
8739 value -= ComputedStyle.getBorderWidth(el, 'border' + sizeOffsets[1] + 'Width', 1); |
|
8740 } |
|
8741 } |
|
8742 |
|
8743 } else { // use style.pixelWidth, etc. to convert to pixels |
|
8744 // need to map style.width to currentStyle (no currentStyle.pixelWidth) |
|
8745 if (!el.style[pixel] && !el.style[prop]) { |
|
8746 el.style[prop] = current; |
|
8747 } |
|
8748 value = el.style[pixel]; |
|
8749 |
|
8750 } |
|
8751 return value + PX; |
|
8752 }, |
|
8753 |
|
8754 borderMap: { |
|
8755 thin: (isIE8) ? '1px' : '2px', |
|
8756 medium: (isIE8) ? '3px': '4px', |
|
8757 thick: (isIE8) ? '5px' : '6px' |
|
8758 }, |
|
8759 |
|
8760 getBorderWidth: function(el, property, omitUnit) { |
|
8761 var unit = omitUnit ? '' : PX, |
|
8762 current = el.currentStyle[property]; |
|
8763 |
|
8764 if (current.indexOf(PX) < 0) { // look up keywords if a border exists |
|
8765 if (ComputedStyle.borderMap[current] && |
|
8766 el.currentStyle.borderStyle !== 'none') { |
|
8767 current = ComputedStyle.borderMap[current]; |
|
8768 } else { // otherwise no border (default is "medium") |
|
8769 current = 0; |
|
8770 } |
|
8771 } |
|
8772 return (omitUnit) ? parseFloat(current) : current; |
|
8773 }, |
|
8774 |
|
8775 getPixel: function(node, att) { |
|
8776 // use pixelRight to convert to px |
|
8777 var val = null, |
|
8778 style = _getStyleObj(node), |
|
8779 styleRight = style.right, |
|
8780 current = style[att]; |
|
8781 |
|
8782 node.style.right = current; |
|
8783 val = node.style.pixelRight; |
|
8784 node.style.right = styleRight; // revert |
|
8785 |
|
8786 return val; |
|
8787 }, |
|
8788 |
|
8789 getMargin: function(node, att) { |
|
8790 var val, |
|
8791 style = _getStyleObj(node); |
|
8792 |
|
8793 if (style[att] == AUTO) { |
|
8794 val = 0; |
|
8795 } else { |
|
8796 val = ComputedStyle.getPixel(node, att); |
|
8797 } |
|
8798 return val + PX; |
|
8799 }, |
|
8800 |
|
8801 getVisibility: function(node, att) { |
|
8802 var current; |
|
8803 while ( (current = node.currentStyle) && current[att] == 'inherit') { // NOTE: assignment in test |
|
8804 node = node.parentNode; |
|
8805 } |
|
8806 return (current) ? current[att] : VISIBLE; |
|
8807 }, |
|
8808 |
|
8809 getColor: function(node, att) { |
|
8810 var current = _getStyleObj(node)[att]; |
|
8811 |
|
8812 if (!current || current === TRANSPARENT) { |
|
8813 Y.DOM.elementByAxis(node, 'parentNode', null, function(parent) { |
|
8814 current = _getStyleObj(parent)[att]; |
|
8815 if (current && current !== TRANSPARENT) { |
|
8816 node = parent; |
|
8817 return true; |
|
8818 } |
|
8819 }); |
|
8820 } |
|
8821 |
|
8822 return Y.Color.toRGB(current); |
|
8823 }, |
|
8824 |
|
8825 getBorderColor: function(node, att) { |
|
8826 var current = _getStyleObj(node), |
|
8827 val = current[att] || current.color; |
|
8828 return Y.Color.toRGB(Y.Color.toHex(val)); |
|
8829 } |
|
8830 }, |
|
8831 |
|
8832 //fontSize: getPixelFont, |
|
8833 IEComputed = {}; |
|
8834 |
|
8835 addFeature('style', 'computedStyle', { |
|
8836 test: function() { |
|
8837 return 'getComputedStyle' in Y.config.win; |
|
8838 } |
|
8839 }); |
|
8840 |
|
8841 addFeature('style', 'opacity', { |
|
8842 test: function() { |
|
8843 return 'opacity' in documentElement.style; |
|
8844 } |
|
8845 }); |
|
8846 |
|
8847 addFeature('style', 'filter', { |
|
8848 test: function() { |
|
8849 return 'filters' in documentElement; |
|
8850 } |
|
8851 }); |
|
8852 |
|
8853 // use alpha filter for IE opacity |
|
8854 if (!testFeature('style', 'opacity') && testFeature('style', 'filter')) { |
|
8855 Y.DOM.CUSTOM_STYLES[OPACITY] = { |
|
8856 get: function(node) { |
|
8857 var val = 100; |
|
8858 try { // will error if no DXImageTransform |
|
8859 val = node[FILTERS]['DXImageTransform.Microsoft.Alpha'][OPACITY]; |
|
8860 |
|
8861 } catch(e) { |
|
8862 try { // make sure its in the document |
|
8863 val = node[FILTERS]('alpha')[OPACITY]; |
|
8864 } catch(err) { |
|
8865 Y.log('getStyle: IE opacity filter not found; returning 1', 'warn', 'dom-style'); |
|
8866 } |
|
8867 } |
|
8868 return val / 100; |
|
8869 }, |
|
8870 |
|
8871 set: function(node, val, style) { |
|
8872 var current, |
|
8873 styleObj = _getStyleObj(node), |
|
8874 currentFilter = styleObj[FILTER]; |
|
8875 |
|
8876 style = style || node.style; |
|
8877 if (val === '') { // normalize inline style behavior |
|
8878 current = (OPACITY in styleObj) ? styleObj[OPACITY] : 1; // revert to original opacity |
|
8879 val = current; |
|
8880 } |
|
8881 |
|
8882 if (typeof currentFilter == 'string') { // in case not appended |
|
8883 style[FILTER] = currentFilter.replace(/alpha([^)]*\))/gi, '') + |
|
8884 ((val < 1) ? 'alpha(' + OPACITY + '=' + val * 100 + ')' : ''); |
|
8885 |
|
8886 if (!style[FILTER]) { |
|
8887 style.removeAttribute(FILTER); |
|
8888 } |
|
8889 |
|
8890 if (!styleObj[HAS_LAYOUT]) { |
|
8891 style.zoom = 1; // needs layout |
|
8892 } |
|
8893 } |
|
8894 } |
|
8895 }; |
|
8896 } |
|
8897 |
|
8898 try { |
|
8899 Y.config.doc.createElement('div').style.height = '-1px'; |
|
8900 } catch(e) { // IE throws error on invalid style set; trap common cases |
|
8901 Y.DOM.CUSTOM_STYLES.height = { |
|
8902 set: function(node, val, style) { |
|
8903 var floatVal = parseFloat(val); |
|
8904 if (floatVal >= 0 || val === 'auto' || val === '') { |
|
8905 style.height = val; |
|
8906 } else { |
|
8907 Y.log('invalid style value for height: ' + val, 'warn', 'dom-style'); |
|
8908 } |
|
8909 } |
|
8910 }; |
|
8911 |
|
8912 Y.DOM.CUSTOM_STYLES.width = { |
|
8913 set: function(node, val, style) { |
|
8914 var floatVal = parseFloat(val); |
|
8915 if (floatVal >= 0 || val === 'auto' || val === '') { |
|
8916 style.width = val; |
|
8917 } else { |
|
8918 Y.log('invalid style value for width: ' + val, 'warn', 'dom-style'); |
|
8919 } |
|
8920 } |
|
8921 }; |
|
8922 } |
|
8923 |
|
8924 if (!testFeature('style', 'computedStyle')) { |
|
8925 // TODO: top, right, bottom, left |
|
8926 IEComputed[WIDTH] = IEComputed[HEIGHT] = ComputedStyle.getOffset; |
|
8927 |
|
8928 IEComputed.color = IEComputed.backgroundColor = ComputedStyle.getColor; |
|
8929 |
|
8930 IEComputed[BORDER_WIDTH] = IEComputed[BORDER_TOP_WIDTH] = IEComputed[BORDER_RIGHT_WIDTH] = |
|
8931 IEComputed[BORDER_BOTTOM_WIDTH] = IEComputed[BORDER_LEFT_WIDTH] = |
|
8932 ComputedStyle.getBorderWidth; |
|
8933 |
|
8934 IEComputed.marginTop = IEComputed.marginRight = IEComputed.marginBottom = |
|
8935 IEComputed.marginLeft = ComputedStyle.getMargin; |
|
8936 |
|
8937 IEComputed.visibility = ComputedStyle.getVisibility; |
|
8938 IEComputed.borderColor = IEComputed.borderTopColor = |
|
8939 IEComputed.borderRightColor = IEComputed.borderBottomColor = |
|
8940 IEComputed.borderLeftColor = ComputedStyle.getBorderColor; |
|
8941 |
|
8942 Y.DOM[GET_COMPUTED_STYLE] = ComputedStyle.get; |
|
8943 |
|
8944 Y.namespace('DOM.IE'); |
|
8945 Y.DOM.IE.COMPUTED = IEComputed; |
|
8946 Y.DOM.IE.ComputedStyle = ComputedStyle; |
|
8947 } |
|
8948 |
|
8949 })(Y); |
|
8950 |
|
8951 |
|
8952 }, '@VERSION@', {"requires": ["dom-style"]}); |
|
8953 YUI.add('dom-screen', function (Y, NAME) { |
|
8954 |
|
8955 (function(Y) { |
|
8956 |
|
8957 /** |
|
8958 * Adds position and region management functionality to DOM. |
|
8959 * @module dom |
|
8960 * @submodule dom-screen |
|
8961 * @for DOM |
|
8962 */ |
|
8963 |
|
8964 var DOCUMENT_ELEMENT = 'documentElement', |
|
8965 COMPAT_MODE = 'compatMode', |
|
8966 POSITION = 'position', |
|
8967 FIXED = 'fixed', |
|
8968 RELATIVE = 'relative', |
|
8969 LEFT = 'left', |
|
8970 TOP = 'top', |
|
8971 _BACK_COMPAT = 'BackCompat', |
|
8972 MEDIUM = 'medium', |
|
8973 BORDER_LEFT_WIDTH = 'borderLeftWidth', |
|
8974 BORDER_TOP_WIDTH = 'borderTopWidth', |
|
8975 GET_BOUNDING_CLIENT_RECT = 'getBoundingClientRect', |
|
8976 GET_COMPUTED_STYLE = 'getComputedStyle', |
|
8977 |
|
8978 Y_DOM = Y.DOM, |
|
8979 |
|
8980 // TODO: how about thead/tbody/tfoot/tr? |
|
8981 // TODO: does caption matter? |
|
8982 RE_TABLE = /^t(?:able|d|h)$/i, |
|
8983 |
|
8984 SCROLL_NODE; |
|
8985 |
|
8986 if (Y.UA.ie) { |
|
8987 if (Y.config.doc[COMPAT_MODE] !== 'BackCompat') { |
|
8988 SCROLL_NODE = DOCUMENT_ELEMENT; |
|
8989 } else { |
|
8990 SCROLL_NODE = 'body'; |
|
8991 } |
|
8992 } |
|
8993 |
|
8994 Y.mix(Y_DOM, { |
|
8995 /** |
|
8996 * Returns the inner height of the viewport (exludes scrollbar). |
|
8997 * @method winHeight |
|
8998 * @return {Number} The current height of the viewport. |
|
8999 */ |
|
9000 winHeight: function(node) { |
|
9001 var h = Y_DOM._getWinSize(node).height; |
|
9002 Y.log('winHeight returning ' + h, 'info', 'dom-screen'); |
|
9003 return h; |
|
9004 }, |
|
9005 |
|
9006 /** |
|
9007 * Returns the inner width of the viewport (exludes scrollbar). |
|
9008 * @method winWidth |
|
9009 * @return {Number} The current width of the viewport. |
|
9010 */ |
|
9011 winWidth: function(node) { |
|
9012 var w = Y_DOM._getWinSize(node).width; |
|
9013 Y.log('winWidth returning ' + w, 'info', 'dom-screen'); |
|
9014 return w; |
|
9015 }, |
|
9016 |
|
9017 /** |
|
9018 * Document height |
|
9019 * @method docHeight |
|
9020 * @return {Number} The current height of the document. |
|
9021 */ |
|
9022 docHeight: function(node) { |
|
9023 var h = Y_DOM._getDocSize(node).height; |
|
9024 Y.log('docHeight returning ' + h, 'info', 'dom-screen'); |
|
9025 return Math.max(h, Y_DOM._getWinSize(node).height); |
|
9026 }, |
|
9027 |
|
9028 /** |
|
9029 * Document width |
|
9030 * @method docWidth |
|
9031 * @return {Number} The current width of the document. |
|
9032 */ |
|
9033 docWidth: function(node) { |
|
9034 var w = Y_DOM._getDocSize(node).width; |
|
9035 Y.log('docWidth returning ' + w, 'info', 'dom-screen'); |
|
9036 return Math.max(w, Y_DOM._getWinSize(node).width); |
|
9037 }, |
|
9038 |
|
9039 /** |
|
9040 * Amount page has been scroll horizontally |
|
9041 * @method docScrollX |
|
9042 * @return {Number} The current amount the screen is scrolled horizontally. |
|
9043 */ |
|
9044 docScrollX: function(node, doc) { |
|
9045 doc = doc || (node) ? Y_DOM._getDoc(node) : Y.config.doc; // perf optimization |
|
9046 var dv = doc.defaultView, |
|
9047 pageOffset = (dv) ? dv.pageXOffset : 0; |
|
9048 return Math.max(doc[DOCUMENT_ELEMENT].scrollLeft, doc.body.scrollLeft, pageOffset); |
|
9049 }, |
|
9050 |
|
9051 /** |
|
9052 * Amount page has been scroll vertically |
|
9053 * @method docScrollY |
|
9054 * @return {Number} The current amount the screen is scrolled vertically. |
|
9055 */ |
|
9056 docScrollY: function(node, doc) { |
|
9057 doc = doc || (node) ? Y_DOM._getDoc(node) : Y.config.doc; // perf optimization |
|
9058 var dv = doc.defaultView, |
|
9059 pageOffset = (dv) ? dv.pageYOffset : 0; |
|
9060 return Math.max(doc[DOCUMENT_ELEMENT].scrollTop, doc.body.scrollTop, pageOffset); |
|
9061 }, |
|
9062 |
|
9063 /** |
|
9064 * Gets the current position of an element based on page coordinates. |
|
9065 * Element must be part of the DOM tree to have page coordinates |
|
9066 * (display:none or elements not appended return false). |
|
9067 * @method getXY |
|
9068 * @param element The target element |
|
9069 * @return {Array} The XY position of the element |
|
9070 |
|
9071 TODO: test inDocument/display? |
|
9072 */ |
|
9073 getXY: function() { |
|
9074 if (Y.config.doc[DOCUMENT_ELEMENT][GET_BOUNDING_CLIENT_RECT]) { |
|
9075 return function(node) { |
|
9076 var xy = null, |
|
9077 scrollLeft, |
|
9078 scrollTop, |
|
9079 mode, |
|
9080 box, |
|
9081 offX, |
|
9082 offY, |
|
9083 doc, |
|
9084 win, |
|
9085 inDoc, |
|
9086 rootNode; |
|
9087 |
|
9088 if (node && node.tagName) { |
|
9089 doc = node.ownerDocument; |
|
9090 mode = doc[COMPAT_MODE]; |
|
9091 |
|
9092 if (mode !== _BACK_COMPAT) { |
|
9093 rootNode = doc[DOCUMENT_ELEMENT]; |
|
9094 } else { |
|
9095 rootNode = doc.body; |
|
9096 } |
|
9097 |
|
9098 // inline inDoc check for perf |
|
9099 if (rootNode.contains) { |
|
9100 inDoc = rootNode.contains(node); |
|
9101 } else { |
|
9102 inDoc = Y.DOM.contains(rootNode, node); |
|
9103 } |
|
9104 |
|
9105 if (inDoc) { |
|
9106 win = doc.defaultView; |
|
9107 |
|
9108 // inline scroll calc for perf |
|
9109 if (win && 'pageXOffset' in win) { |
|
9110 scrollLeft = win.pageXOffset; |
|
9111 scrollTop = win.pageYOffset; |
|
9112 } else { |
|
9113 scrollLeft = (SCROLL_NODE) ? doc[SCROLL_NODE].scrollLeft : Y_DOM.docScrollX(node, doc); |
|
9114 scrollTop = (SCROLL_NODE) ? doc[SCROLL_NODE].scrollTop : Y_DOM.docScrollY(node, doc); |
|
9115 } |
|
9116 |
|
9117 if (Y.UA.ie) { // IE < 8, quirks, or compatMode |
|
9118 if (!doc.documentMode || doc.documentMode < 8 || mode === _BACK_COMPAT) { |
|
9119 offX = rootNode.clientLeft; |
|
9120 offY = rootNode.clientTop; |
|
9121 } |
|
9122 } |
|
9123 box = node[GET_BOUNDING_CLIENT_RECT](); |
|
9124 xy = [box.left, box.top]; |
|
9125 |
|
9126 if (offX || offY) { |
|
9127 xy[0] -= offX; |
|
9128 xy[1] -= offY; |
|
9129 |
|
9130 } |
|
9131 if ((scrollTop || scrollLeft)) { |
|
9132 if (!Y.UA.ios || (Y.UA.ios >= 4.2)) { |
|
9133 xy[0] += scrollLeft; |
|
9134 xy[1] += scrollTop; |
|
9135 } |
|
9136 |
|
9137 } |
|
9138 } else { |
|
9139 xy = Y_DOM._getOffset(node); |
|
9140 } |
|
9141 } |
|
9142 return xy; |
|
9143 }; |
|
9144 } else { |
|
9145 return function(node) { // manually calculate by crawling up offsetParents |
|
9146 //Calculate the Top and Left border sizes (assumes pixels) |
|
9147 var xy = null, |
|
9148 doc, |
|
9149 parentNode, |
|
9150 bCheck, |
|
9151 scrollTop, |
|
9152 scrollLeft; |
|
9153 |
|
9154 if (node) { |
|
9155 if (Y_DOM.inDoc(node)) { |
|
9156 xy = [node.offsetLeft, node.offsetTop]; |
|
9157 doc = node.ownerDocument; |
|
9158 parentNode = node; |
|
9159 // TODO: refactor with !! or just falsey |
|
9160 bCheck = ((Y.UA.gecko || Y.UA.webkit > 519) ? true : false); |
|
9161 |
|
9162 // TODO: worth refactoring for TOP/LEFT only? |
|
9163 while ((parentNode = parentNode.offsetParent)) { |
|
9164 xy[0] += parentNode.offsetLeft; |
|
9165 xy[1] += parentNode.offsetTop; |
|
9166 if (bCheck) { |
|
9167 xy = Y_DOM._calcBorders(parentNode, xy); |
|
9168 } |
|
9169 } |
|
9170 |
|
9171 // account for any scrolled ancestors |
|
9172 if (Y_DOM.getStyle(node, POSITION) != FIXED) { |
|
9173 parentNode = node; |
|
9174 |
|
9175 while ((parentNode = parentNode.parentNode)) { |
|
9176 scrollTop = parentNode.scrollTop; |
|
9177 scrollLeft = parentNode.scrollLeft; |
|
9178 |
|
9179 //Firefox does something funky with borders when overflow is not visible. |
|
9180 if (Y.UA.gecko && (Y_DOM.getStyle(parentNode, 'overflow') !== 'visible')) { |
|
9181 xy = Y_DOM._calcBorders(parentNode, xy); |
|
9182 } |
|
9183 |
|
9184 |
|
9185 if (scrollTop || scrollLeft) { |
|
9186 xy[0] -= scrollLeft; |
|
9187 xy[1] -= scrollTop; |
|
9188 } |
|
9189 } |
|
9190 xy[0] += Y_DOM.docScrollX(node, doc); |
|
9191 xy[1] += Y_DOM.docScrollY(node, doc); |
|
9192 |
|
9193 } else { |
|
9194 //Fix FIXED position -- add scrollbars |
|
9195 xy[0] += Y_DOM.docScrollX(node, doc); |
|
9196 xy[1] += Y_DOM.docScrollY(node, doc); |
|
9197 } |
|
9198 } else { |
|
9199 xy = Y_DOM._getOffset(node); |
|
9200 } |
|
9201 } |
|
9202 |
|
9203 return xy; |
|
9204 }; |
|
9205 } |
|
9206 }(),// NOTE: Executing for loadtime branching |
|
9207 |
|
9208 /** |
|
9209 Gets the width of vertical scrollbars on overflowed containers in the body |
|
9210 content. |
|
9211 |
|
9212 @method getScrollbarWidth |
|
9213 @return {Number} Pixel width of a scrollbar in the current browser |
|
9214 **/ |
|
9215 getScrollbarWidth: Y.cached(function () { |
|
9216 var doc = Y.config.doc, |
|
9217 testNode = doc.createElement('div'), |
|
9218 body = doc.getElementsByTagName('body')[0], |
|
9219 // 0.1 because cached doesn't support falsy refetch values |
|
9220 width = 0.1; |
|
9221 |
|
9222 if (body) { |
|
9223 testNode.style.cssText = "position:absolute;visibility:hidden;overflow:scroll;width:20px;"; |
|
9224 testNode.appendChild(doc.createElement('p')).style.height = '1px'; |
|
9225 body.insertBefore(testNode, body.firstChild); |
|
9226 width = testNode.offsetWidth - testNode.clientWidth; |
|
9227 |
|
9228 body.removeChild(testNode); |
|
9229 } |
|
9230 |
|
9231 return width; |
|
9232 }, null, 0.1), |
|
9233 |
|
9234 /** |
|
9235 * Gets the current X position of an element based on page coordinates. |
|
9236 * Element must be part of the DOM tree to have page coordinates |
|
9237 * (display:none or elements not appended return false). |
|
9238 * @method getX |
|
9239 * @param element The target element |
|
9240 * @return {Number} The X position of the element |
|
9241 */ |
|
9242 |
|
9243 getX: function(node) { |
|
9244 return Y_DOM.getXY(node)[0]; |
|
9245 }, |
|
9246 |
|
9247 /** |
|
9248 * Gets the current Y position of an element based on page coordinates. |
|
9249 * Element must be part of the DOM tree to have page coordinates |
|
9250 * (display:none or elements not appended return false). |
|
9251 * @method getY |
|
9252 * @param element The target element |
|
9253 * @return {Number} The Y position of the element |
|
9254 */ |
|
9255 |
|
9256 getY: function(node) { |
|
9257 return Y_DOM.getXY(node)[1]; |
|
9258 }, |
|
9259 |
|
9260 /** |
|
9261 * Set the position of an html element in page coordinates. |
|
9262 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false). |
|
9263 * @method setXY |
|
9264 * @param element The target element |
|
9265 * @param {Array} xy Contains X & Y values for new position (coordinates are page-based) |
|
9266 * @param {Boolean} noRetry By default we try and set the position a second time if the first fails |
|
9267 */ |
|
9268 setXY: function(node, xy, noRetry) { |
|
9269 var setStyle = Y_DOM.setStyle, |
|
9270 pos, |
|
9271 delta, |
|
9272 newXY, |
|
9273 currentXY; |
|
9274 |
|
9275 if (node && xy) { |
|
9276 pos = Y_DOM.getStyle(node, POSITION); |
|
9277 |
|
9278 delta = Y_DOM._getOffset(node); |
|
9279 if (pos == 'static') { // default to relative |
|
9280 pos = RELATIVE; |
|
9281 setStyle(node, POSITION, pos); |
|
9282 } |
|
9283 currentXY = Y_DOM.getXY(node); |
|
9284 |
|
9285 if (xy[0] !== null) { |
|
9286 setStyle(node, LEFT, xy[0] - currentXY[0] + delta[0] + 'px'); |
|
9287 } |
|
9288 |
|
9289 if (xy[1] !== null) { |
|
9290 setStyle(node, TOP, xy[1] - currentXY[1] + delta[1] + 'px'); |
|
9291 } |
|
9292 |
|
9293 if (!noRetry) { |
|
9294 newXY = Y_DOM.getXY(node); |
|
9295 if (newXY[0] !== xy[0] || newXY[1] !== xy[1]) { |
|
9296 Y_DOM.setXY(node, xy, true); |
|
9297 } |
|
9298 } |
|
9299 |
|
9300 Y.log('setXY setting position to ' + xy, 'info', 'dom-screen'); |
|
9301 } else { |
|
9302 Y.log('setXY failed to set ' + node + ' to ' + xy, 'info', 'dom-screen'); |
|
9303 } |
|
9304 }, |
|
9305 |
|
9306 /** |
|
9307 * Set the X position of an html element in page coordinates, regardless of how the element is positioned. |
|
9308 * The element(s) must be part of the DOM tree to have page coordinates (display:none or elements not appended return false). |
|
9309 * @method setX |
|
9310 * @param element The target element |
|
9311 * @param {Number} x The X values for new position (coordinates are page-based) |
|
9312 */ |
|
9313 setX: function(node, x) { |
|
9314 return Y_DOM.setXY(node, [x, null]); |
|
9315 }, |
|
9316 |
|
9317 /** |
|
9318 * Set the Y position of an html element in page coordinates, regardless of how the element is positioned. |
|
9319 * The element(s) must be part of the DOM tree to have page coordinates (display:none or elements not appended return false). |
|
9320 * @method setY |
|
9321 * @param element The target element |
|
9322 * @param {Number} y The Y values for new position (coordinates are page-based) |
|
9323 */ |
|
9324 setY: function(node, y) { |
|
9325 return Y_DOM.setXY(node, [null, y]); |
|
9326 }, |
|
9327 |
|
9328 /** |
|
9329 * @method swapXY |
|
9330 * @description Swap the xy position with another node |
|
9331 * @param {Node} node The node to swap with |
|
9332 * @param {Node} otherNode The other node to swap with |
|
9333 * @return {Node} |
|
9334 */ |
|
9335 swapXY: function(node, otherNode) { |
|
9336 var xy = Y_DOM.getXY(node); |
|
9337 Y_DOM.setXY(node, Y_DOM.getXY(otherNode)); |
|
9338 Y_DOM.setXY(otherNode, xy); |
|
9339 }, |
|
9340 |
|
9341 _calcBorders: function(node, xy2) { |
|
9342 var t = parseInt(Y_DOM[GET_COMPUTED_STYLE](node, BORDER_TOP_WIDTH), 10) || 0, |
|
9343 l = parseInt(Y_DOM[GET_COMPUTED_STYLE](node, BORDER_LEFT_WIDTH), 10) || 0; |
|
9344 if (Y.UA.gecko) { |
|
9345 if (RE_TABLE.test(node.tagName)) { |
|
9346 t = 0; |
|
9347 l = 0; |
|
9348 } |
|
9349 } |
|
9350 xy2[0] += l; |
|
9351 xy2[1] += t; |
|
9352 return xy2; |
|
9353 }, |
|
9354 |
|
9355 _getWinSize: function(node, doc) { |
|
9356 doc = doc || (node) ? Y_DOM._getDoc(node) : Y.config.doc; |
|
9357 var win = doc.defaultView || doc.parentWindow, |
|
9358 mode = doc[COMPAT_MODE], |
|
9359 h = win.innerHeight, |
|
9360 w = win.innerWidth, |
|
9361 root = doc[DOCUMENT_ELEMENT]; |
|
9362 |
|
9363 if ( mode && !Y.UA.opera ) { // IE, Gecko |
|
9364 if (mode != 'CSS1Compat') { // Quirks |
|
9365 root = doc.body; |
|
9366 } |
|
9367 h = root.clientHeight; |
|
9368 w = root.clientWidth; |
|
9369 } |
|
9370 return { height: h, width: w }; |
|
9371 }, |
|
9372 |
|
9373 _getDocSize: function(node) { |
|
9374 var doc = (node) ? Y_DOM._getDoc(node) : Y.config.doc, |
|
9375 root = doc[DOCUMENT_ELEMENT]; |
|
9376 |
|
9377 if (doc[COMPAT_MODE] != 'CSS1Compat') { |
|
9378 root = doc.body; |
|
9379 } |
|
9380 |
|
9381 return { height: root.scrollHeight, width: root.scrollWidth }; |
|
9382 } |
|
9383 }); |
|
9384 |
|
9385 })(Y); |
|
9386 (function(Y) { |
|
9387 var TOP = 'top', |
|
9388 RIGHT = 'right', |
|
9389 BOTTOM = 'bottom', |
|
9390 LEFT = 'left', |
|
9391 |
|
9392 getOffsets = function(r1, r2) { |
|
9393 var t = Math.max(r1[TOP], r2[TOP]), |
|
9394 r = Math.min(r1[RIGHT], r2[RIGHT]), |
|
9395 b = Math.min(r1[BOTTOM], r2[BOTTOM]), |
|
9396 l = Math.max(r1[LEFT], r2[LEFT]), |
|
9397 ret = {}; |
|
9398 |
|
9399 ret[TOP] = t; |
|
9400 ret[RIGHT] = r; |
|
9401 ret[BOTTOM] = b; |
|
9402 ret[LEFT] = l; |
|
9403 return ret; |
|
9404 }, |
|
9405 |
|
9406 DOM = Y.DOM; |
|
9407 |
|
9408 Y.mix(DOM, { |
|
9409 /** |
|
9410 * Returns an Object literal containing the following about this element: (top, right, bottom, left) |
|
9411 * @for DOM |
|
9412 * @method region |
|
9413 * @param {HTMLElement} element The DOM element. |
|
9414 * @return {Object} Object literal containing the following about this element: (top, right, bottom, left) |
|
9415 */ |
|
9416 region: function(node) { |
|
9417 var xy = DOM.getXY(node), |
|
9418 ret = false; |
|
9419 |
|
9420 if (node && xy) { |
|
9421 ret = DOM._getRegion( |
|
9422 xy[1], // top |
|
9423 xy[0] + node.offsetWidth, // right |
|
9424 xy[1] + node.offsetHeight, // bottom |
|
9425 xy[0] // left |
|
9426 ); |
|
9427 } |
|
9428 |
|
9429 return ret; |
|
9430 }, |
|
9431 |
|
9432 /** |
|
9433 * Find the intersect information for the passed nodes. |
|
9434 * @method intersect |
|
9435 * @for DOM |
|
9436 * @param {HTMLElement} element The first element |
|
9437 * @param {HTMLElement | Object} element2 The element or region to check the interect with |
|
9438 * @param {Object} altRegion An object literal containing the region for the first element if we already have the data (for performance e.g. DragDrop) |
|
9439 * @return {Object} Object literal containing the following intersection data: (top, right, bottom, left, area, yoff, xoff, inRegion) |
|
9440 */ |
|
9441 intersect: function(node, node2, altRegion) { |
|
9442 var r = altRegion || DOM.region(node), region = {}, |
|
9443 n = node2, |
|
9444 off; |
|
9445 |
|
9446 if (n.tagName) { |
|
9447 region = DOM.region(n); |
|
9448 } else if (Y.Lang.isObject(node2)) { |
|
9449 region = node2; |
|
9450 } else { |
|
9451 return false; |
|
9452 } |
|
9453 |
|
9454 off = getOffsets(region, r); |
|
9455 return { |
|
9456 top: off[TOP], |
|
9457 right: off[RIGHT], |
|
9458 bottom: off[BOTTOM], |
|
9459 left: off[LEFT], |
|
9460 area: ((off[BOTTOM] - off[TOP]) * (off[RIGHT] - off[LEFT])), |
|
9461 yoff: ((off[BOTTOM] - off[TOP])), |
|
9462 xoff: (off[RIGHT] - off[LEFT]), |
|
9463 inRegion: DOM.inRegion(node, node2, false, altRegion) |
|
9464 }; |
|
9465 |
|
9466 }, |
|
9467 /** |
|
9468 * Check if any part of this node is in the passed region |
|
9469 * @method inRegion |
|
9470 * @for DOM |
|
9471 * @param {Object} node The node to get the region from |
|
9472 * @param {Object} node2 The second node to get the region from or an Object literal of the region |
|
9473 * @param {Boolean} all Should all of the node be inside the region |
|
9474 * @param {Object} altRegion An object literal containing the region for this node if we already have the data (for performance e.g. DragDrop) |
|
9475 * @return {Boolean} True if in region, false if not. |
|
9476 */ |
|
9477 inRegion: function(node, node2, all, altRegion) { |
|
9478 var region = {}, |
|
9479 r = altRegion || DOM.region(node), |
|
9480 n = node2, |
|
9481 off; |
|
9482 |
|
9483 if (n.tagName) { |
|
9484 region = DOM.region(n); |
|
9485 } else if (Y.Lang.isObject(node2)) { |
|
9486 region = node2; |
|
9487 } else { |
|
9488 return false; |
|
9489 } |
|
9490 |
|
9491 if (all) { |
|
9492 return ( |
|
9493 r[LEFT] >= region[LEFT] && |
|
9494 r[RIGHT] <= region[RIGHT] && |
|
9495 r[TOP] >= region[TOP] && |
|
9496 r[BOTTOM] <= region[BOTTOM] ); |
|
9497 } else { |
|
9498 off = getOffsets(region, r); |
|
9499 if (off[BOTTOM] >= off[TOP] && off[RIGHT] >= off[LEFT]) { |
|
9500 return true; |
|
9501 } else { |
|
9502 return false; |
|
9503 } |
|
9504 |
|
9505 } |
|
9506 }, |
|
9507 |
|
9508 /** |
|
9509 * Check if any part of this element is in the viewport |
|
9510 * @method inViewportRegion |
|
9511 * @for DOM |
|
9512 * @param {HTMLElement} element The DOM element. |
|
9513 * @param {Boolean} all Should all of the node be inside the region |
|
9514 * @param {Object} altRegion An object literal containing the region for this node if we already have the data (for performance e.g. DragDrop) |
|
9515 * @return {Boolean} True if in region, false if not. |
|
9516 */ |
|
9517 inViewportRegion: function(node, all, altRegion) { |
|
9518 return DOM.inRegion(node, DOM.viewportRegion(node), all, altRegion); |
|
9519 |
|
9520 }, |
|
9521 |
|
9522 _getRegion: function(t, r, b, l) { |
|
9523 var region = {}; |
|
9524 |
|
9525 region[TOP] = region[1] = t; |
|
9526 region[LEFT] = region[0] = l; |
|
9527 region[BOTTOM] = b; |
|
9528 region[RIGHT] = r; |
|
9529 region.width = region[RIGHT] - region[LEFT]; |
|
9530 region.height = region[BOTTOM] - region[TOP]; |
|
9531 |
|
9532 return region; |
|
9533 }, |
|
9534 |
|
9535 /** |
|
9536 * Returns an Object literal containing the following about the visible region of viewport: (top, right, bottom, left) |
|
9537 * @method viewportRegion |
|
9538 * @for DOM |
|
9539 * @return {Object} Object literal containing the following about the visible region of the viewport: (top, right, bottom, left) |
|
9540 */ |
|
9541 viewportRegion: function(node) { |
|
9542 node = node || Y.config.doc.documentElement; |
|
9543 var ret = false, |
|
9544 scrollX, |
|
9545 scrollY; |
|
9546 |
|
9547 if (node) { |
|
9548 scrollX = DOM.docScrollX(node); |
|
9549 scrollY = DOM.docScrollY(node); |
|
9550 |
|
9551 ret = DOM._getRegion(scrollY, // top |
|
9552 DOM.winWidth(node) + scrollX, // right |
|
9553 scrollY + DOM.winHeight(node), // bottom |
|
9554 scrollX); // left |
|
9555 } |
|
9556 |
|
9557 return ret; |
|
9558 } |
|
9559 }); |
|
9560 })(Y); |
|
9561 |
|
9562 |
|
9563 }, '@VERSION@', {"requires": ["dom-base", "dom-style"]}); |
|
9564 YUI.add('selector-native', function (Y, NAME) { |
|
9565 |
|
9566 (function(Y) { |
|
9567 /** |
|
9568 * The selector-native module provides support for native querySelector |
|
9569 * @module dom |
|
9570 * @submodule selector-native |
|
9571 * @for Selector |
|
9572 */ |
|
9573 |
|
9574 /** |
|
9575 * Provides support for using CSS selectors to query the DOM |
|
9576 * @class Selector |
|
9577 * @static |
|
9578 * @for Selector |
|
9579 */ |
|
9580 |
|
9581 Y.namespace('Selector'); // allow native module to standalone |
|
9582 |
|
9583 var COMPARE_DOCUMENT_POSITION = 'compareDocumentPosition', |
|
9584 OWNER_DOCUMENT = 'ownerDocument'; |
|
9585 |
|
9586 var Selector = { |
|
9587 _types: { |
|
9588 esc: { |
|
9589 token: '\uE000', |
|
9590 re: /\\[:\[\]\(\)#\.\'\>+~"]/gi |
|
9591 }, |
|
9592 |
|
9593 attr: { |
|
9594 token: '\uE001', |
|
9595 re: /(\[[^\]]*\])/g |
|
9596 }, |
|
9597 |
|
9598 pseudo: { |
|
9599 token: '\uE002', |
|
9600 re: /(\([^\)]*\))/g |
|
9601 } |
|
9602 }, |
|
9603 |
|
9604 useNative: true, |
|
9605 |
|
9606 _escapeId: function(id) { |
|
9607 if (id) { |
|
9608 id = id.replace(/([:\[\]\(\)#\.'<>+~"])/g,'\\$1'); |
|
9609 } |
|
9610 return id; |
|
9611 }, |
|
9612 |
|
9613 _compare: ('sourceIndex' in Y.config.doc.documentElement) ? |
|
9614 function(nodeA, nodeB) { |
|
9615 var a = nodeA.sourceIndex, |
|
9616 b = nodeB.sourceIndex; |
|
9617 |
|
9618 if (a === b) { |
|
9619 return 0; |
|
9620 } else if (a > b) { |
|
9621 return 1; |
|
9622 } |
|
9623 |
|
9624 return -1; |
|
9625 |
|
9626 } : (Y.config.doc.documentElement[COMPARE_DOCUMENT_POSITION] ? |
|
9627 function(nodeA, nodeB) { |
|
9628 if (nodeA[COMPARE_DOCUMENT_POSITION](nodeB) & 4) { |
|
9629 return -1; |
|
9630 } else { |
|
9631 return 1; |
|
9632 } |
|
9633 } : |
|
9634 function(nodeA, nodeB) { |
|
9635 var rangeA, rangeB, compare; |
|
9636 if (nodeA && nodeB) { |
|
9637 rangeA = nodeA[OWNER_DOCUMENT].createRange(); |
|
9638 rangeA.setStart(nodeA, 0); |
|
9639 rangeB = nodeB[OWNER_DOCUMENT].createRange(); |
|
9640 rangeB.setStart(nodeB, 0); |
|
9641 compare = rangeA.compareBoundaryPoints(1, rangeB); // 1 === Range.START_TO_END |
|
9642 } |
|
9643 |
|
9644 return compare; |
|
9645 |
|
9646 }), |
|
9647 |
|
9648 _sort: function(nodes) { |
|
9649 if (nodes) { |
|
9650 nodes = Y.Array(nodes, 0, true); |
|
9651 if (nodes.sort) { |
|
9652 nodes.sort(Selector._compare); |
|
9653 } |
|
9654 } |
|
9655 |
|
9656 return nodes; |
|
9657 }, |
|
9658 |
|
9659 _deDupe: function(nodes) { |
|
9660 var ret = [], |
|
9661 i, node; |
|
9662 |
|
9663 for (i = 0; (node = nodes[i++]);) { |
|
9664 if (!node._found) { |
|
9665 ret[ret.length] = node; |
|
9666 node._found = true; |
|
9667 } |
|
9668 } |
|
9669 |
|
9670 for (i = 0; (node = ret[i++]);) { |
|
9671 node._found = null; |
|
9672 node.removeAttribute('_found'); |
|
9673 } |
|
9674 |
|
9675 return ret; |
|
9676 }, |
|
9677 |
|
9678 /** |
|
9679 * Retrieves a set of nodes based on a given CSS selector. |
|
9680 * @method query |
|
9681 * |
|
9682 * @param {string} selector The CSS Selector to test the node against. |
|
9683 * @param {HTMLElement} root optional An HTMLElement to start the query from. Defaults to Y.config.doc |
|
9684 * @param {Boolean} firstOnly optional Whether or not to return only the first match. |
|
9685 * @return {Array} An array of nodes that match the given selector. |
|
9686 * @static |
|
9687 */ |
|
9688 query: function(selector, root, firstOnly, skipNative) { |
|
9689 root = root || Y.config.doc; |
|
9690 var ret = [], |
|
9691 useNative = (Y.Selector.useNative && Y.config.doc.querySelector && !skipNative), |
|
9692 queries = [[selector, root]], |
|
9693 query, |
|
9694 result, |
|
9695 i, |
|
9696 fn = (useNative) ? Y.Selector._nativeQuery : Y.Selector._bruteQuery; |
|
9697 |
|
9698 if (selector && fn) { |
|
9699 // split group into seperate queries |
|
9700 if (!skipNative && // already done if skipping |
|
9701 (!useNative || root.tagName)) { // split native when element scoping is needed |
|
9702 queries = Selector._splitQueries(selector, root); |
|
9703 } |
|
9704 |
|
9705 for (i = 0; (query = queries[i++]);) { |
|
9706 result = fn(query[0], query[1], firstOnly); |
|
9707 if (!firstOnly) { // coerce DOM Collection to Array |
|
9708 result = Y.Array(result, 0, true); |
|
9709 } |
|
9710 if (result) { |
|
9711 ret = ret.concat(result); |
|
9712 } |
|
9713 } |
|
9714 |
|
9715 if (queries.length > 1) { // remove dupes and sort by doc order |
|
9716 ret = Selector._sort(Selector._deDupe(ret)); |
|
9717 } |
|
9718 } |
|
9719 |
|
9720 Y.log('query: ' + selector + ' returning: ' + ret.length, 'info', 'Selector'); |
|
9721 return (firstOnly) ? (ret[0] || null) : ret; |
|
9722 |
|
9723 }, |
|
9724 |
|
9725 _replaceSelector: function(selector) { |
|
9726 var esc = Y.Selector._parse('esc', selector), // pull escaped colon, brackets, etc. |
|
9727 attrs, |
|
9728 pseudos; |
|
9729 |
|
9730 // first replace escaped chars, which could be present in attrs or pseudos |
|
9731 selector = Y.Selector._replace('esc', selector); |
|
9732 |
|
9733 // then replace pseudos before attrs to avoid replacing :not([foo]) |
|
9734 pseudos = Y.Selector._parse('pseudo', selector); |
|
9735 selector = Selector._replace('pseudo', selector); |
|
9736 |
|
9737 attrs = Y.Selector._parse('attr', selector); |
|
9738 selector = Y.Selector._replace('attr', selector); |
|
9739 |
|
9740 return { |
|
9741 esc: esc, |
|
9742 attrs: attrs, |
|
9743 pseudos: pseudos, |
|
9744 selector: selector |
|
9745 }; |
|
9746 }, |
|
9747 |
|
9748 _restoreSelector: function(replaced) { |
|
9749 var selector = replaced.selector; |
|
9750 selector = Y.Selector._restore('attr', selector, replaced.attrs); |
|
9751 selector = Y.Selector._restore('pseudo', selector, replaced.pseudos); |
|
9752 selector = Y.Selector._restore('esc', selector, replaced.esc); |
|
9753 return selector; |
|
9754 }, |
|
9755 |
|
9756 _replaceCommas: function(selector) { |
|
9757 var replaced = Y.Selector._replaceSelector(selector), |
|
9758 selector = replaced.selector; |
|
9759 |
|
9760 if (selector) { |
|
9761 selector = selector.replace(/,/g, '\uE007'); |
|
9762 replaced.selector = selector; |
|
9763 selector = Y.Selector._restoreSelector(replaced); |
|
9764 } |
|
9765 return selector; |
|
9766 }, |
|
9767 |
|
9768 // allows element scoped queries to begin with combinator |
|
9769 // e.g. query('> p', document.body) === query('body > p') |
|
9770 _splitQueries: function(selector, node) { |
|
9771 if (selector.indexOf(',') > -1) { |
|
9772 selector = Y.Selector._replaceCommas(selector); |
|
9773 } |
|
9774 |
|
9775 var groups = selector.split('\uE007'), // split on replaced comma token |
|
9776 queries = [], |
|
9777 prefix = '', |
|
9778 id, |
|
9779 i, |
|
9780 len; |
|
9781 |
|
9782 if (node) { |
|
9783 // enforce for element scoping |
|
9784 if (node.nodeType === 1) { // Elements only |
|
9785 id = Y.Selector._escapeId(Y.DOM.getId(node)); |
|
9786 |
|
9787 if (!id) { |
|
9788 id = Y.guid(); |
|
9789 Y.DOM.setId(node, id); |
|
9790 } |
|
9791 |
|
9792 prefix = '[id="' + id + '"] '; |
|
9793 } |
|
9794 |
|
9795 for (i = 0, len = groups.length; i < len; ++i) { |
|
9796 selector = prefix + groups[i]; |
|
9797 queries.push([selector, node]); |
|
9798 } |
|
9799 } |
|
9800 |
|
9801 return queries; |
|
9802 }, |
|
9803 |
|
9804 _nativeQuery: function(selector, root, one) { |
|
9805 if (Y.UA.webkit && selector.indexOf(':checked') > -1 && |
|
9806 (Y.Selector.pseudos && Y.Selector.pseudos.checked)) { // webkit (chrome, safari) fails to pick up "selected" with "checked" |
|
9807 return Y.Selector.query(selector, root, one, true); // redo with skipNative true to try brute query |
|
9808 } |
|
9809 try { |
|
9810 //Y.log('trying native query with: ' + selector, 'info', 'selector-native'); |
|
9811 return root['querySelector' + (one ? '' : 'All')](selector); |
|
9812 } catch(e) { // fallback to brute if available |
|
9813 //Y.log('native query error; reverting to brute query with: ' + selector, 'info', 'selector-native'); |
|
9814 return Y.Selector.query(selector, root, one, true); // redo with skipNative true |
|
9815 } |
|
9816 }, |
|
9817 |
|
9818 filter: function(nodes, selector) { |
|
9819 var ret = [], |
|
9820 i, node; |
|
9821 |
|
9822 if (nodes && selector) { |
|
9823 for (i = 0; (node = nodes[i++]);) { |
|
9824 if (Y.Selector.test(node, selector)) { |
|
9825 ret[ret.length] = node; |
|
9826 } |
|
9827 } |
|
9828 } else { |
|
9829 Y.log('invalid filter input (nodes: ' + nodes + |
|
9830 ', selector: ' + selector + ')', 'warn', 'Selector'); |
|
9831 } |
|
9832 |
|
9833 return ret; |
|
9834 }, |
|
9835 |
|
9836 test: function(node, selector, root) { |
|
9837 var ret = false, |
|
9838 useFrag = false, |
|
9839 groups, |
|
9840 parent, |
|
9841 item, |
|
9842 items, |
|
9843 frag, |
|
9844 id, |
|
9845 i, j, group; |
|
9846 |
|
9847 if (node && node.tagName) { // only test HTMLElements |
|
9848 |
|
9849 if (typeof selector == 'function') { // test with function |
|
9850 ret = selector.call(node, node); |
|
9851 } else { // test with query |
|
9852 // we need a root if off-doc |
|
9853 groups = selector.split(','); |
|
9854 if (!root && !Y.DOM.inDoc(node)) { |
|
9855 parent = node.parentNode; |
|
9856 if (parent) { |
|
9857 root = parent; |
|
9858 } else { // only use frag when no parent to query |
|
9859 frag = node[OWNER_DOCUMENT].createDocumentFragment(); |
|
9860 frag.appendChild(node); |
|
9861 root = frag; |
|
9862 useFrag = true; |
|
9863 } |
|
9864 } |
|
9865 root = root || node[OWNER_DOCUMENT]; |
|
9866 |
|
9867 id = Y.Selector._escapeId(Y.DOM.getId(node)); |
|
9868 if (!id) { |
|
9869 id = Y.guid(); |
|
9870 Y.DOM.setId(node, id); |
|
9871 } |
|
9872 |
|
9873 for (i = 0; (group = groups[i++]);) { // TODO: off-dom test |
|
9874 group += '[id="' + id + '"]'; |
|
9875 items = Y.Selector.query(group, root); |
|
9876 |
|
9877 for (j = 0; item = items[j++];) { |
|
9878 if (item === node) { |
|
9879 ret = true; |
|
9880 break; |
|
9881 } |
|
9882 } |
|
9883 if (ret) { |
|
9884 break; |
|
9885 } |
|
9886 } |
|
9887 |
|
9888 if (useFrag) { // cleanup |
|
9889 frag.removeChild(node); |
|
9890 } |
|
9891 }; |
|
9892 } |
|
9893 |
|
9894 return ret; |
|
9895 }, |
|
9896 |
|
9897 /** |
|
9898 * A convenience function to emulate Y.Node's aNode.ancestor(selector). |
|
9899 * @param {HTMLElement} element An HTMLElement to start the query from. |
|
9900 * @param {String} selector The CSS selector to test the node against. |
|
9901 * @return {HTMLElement} The ancestor node matching the selector, or null. |
|
9902 * @param {Boolean} testSelf optional Whether or not to include the element in the scan |
|
9903 * @static |
|
9904 * @method ancestor |
|
9905 */ |
|
9906 ancestor: function (element, selector, testSelf) { |
|
9907 return Y.DOM.ancestor(element, function(n) { |
|
9908 return Y.Selector.test(n, selector); |
|
9909 }, testSelf); |
|
9910 }, |
|
9911 |
|
9912 _parse: function(name, selector) { |
|
9913 return selector.match(Y.Selector._types[name].re); |
|
9914 }, |
|
9915 |
|
9916 _replace: function(name, selector) { |
|
9917 var o = Y.Selector._types[name]; |
|
9918 return selector.replace(o.re, o.token); |
|
9919 }, |
|
9920 |
|
9921 _restore: function(name, selector, items) { |
|
9922 if (items) { |
|
9923 var token = Y.Selector._types[name].token, |
|
9924 i, len; |
|
9925 for (i = 0, len = items.length; i < len; ++i) { |
|
9926 selector = selector.replace(token, items[i]); |
|
9927 } |
|
9928 } |
|
9929 return selector; |
|
9930 } |
|
9931 }; |
|
9932 |
|
9933 Y.mix(Y.Selector, Selector, true); |
|
9934 |
|
9935 })(Y); |
|
9936 |
|
9937 |
|
9938 }, '@VERSION@', {"requires": ["dom-base"]}); |
|
9939 YUI.add('selector', function (Y, NAME) { |
|
9940 |
|
9941 |
|
9942 |
|
9943 }, '@VERSION@', {"requires": ["selector-native"]}); |
|
9944 YUI.add('event-custom-base', function (Y, NAME) { |
|
9945 |
|
9946 /** |
|
9947 * Custom event engine, DOM event listener abstraction layer, synthetic DOM |
|
9948 * events. |
|
9949 * @module event-custom |
|
9950 */ |
|
9951 |
|
9952 Y.Env.evt = { |
|
9953 handles: {}, |
|
9954 plugins: {} |
|
9955 }; |
|
9956 |
|
9957 |
|
9958 /** |
|
9959 * Custom event engine, DOM event listener abstraction layer, synthetic DOM |
|
9960 * events. |
|
9961 * @module event-custom |
|
9962 * @submodule event-custom-base |
|
9963 */ |
|
9964 |
|
9965 /** |
|
9966 * Allows for the insertion of methods that are executed before or after |
|
9967 * a specified method |
|
9968 * @class Do |
|
9969 * @static |
|
9970 */ |
|
9971 |
|
9972 var DO_BEFORE = 0, |
|
9973 DO_AFTER = 1, |
|
9974 |
|
9975 DO = { |
|
9976 |
|
9977 /** |
|
9978 * Cache of objects touched by the utility |
|
9979 * @property objs |
|
9980 * @static |
|
9981 * @deprecated Since 3.6.0. The `_yuiaop` property on the AOP'd object |
|
9982 * replaces the role of this property, but is considered to be private, and |
|
9983 * is only mentioned to provide a migration path. |
|
9984 * |
|
9985 * If you have a use case which warrants migration to the _yuiaop property, |
|
9986 * please file a ticket to let us know what it's used for and we can see if |
|
9987 * we need to expose hooks for that functionality more formally. |
|
9988 */ |
|
9989 objs: null, |
|
9990 |
|
9991 /** |
|
9992 * <p>Execute the supplied method before the specified function. Wrapping |
|
9993 * function may optionally return an instance of the following classes to |
|
9994 * further alter runtime behavior:</p> |
|
9995 * <dl> |
|
9996 * <dt></code>Y.Do.Halt(message, returnValue)</code></dt> |
|
9997 * <dd>Immediatly stop execution and return |
|
9998 * <code>returnValue</code>. No other wrapping functions will be |
|
9999 * executed.</dd> |
|
10000 * <dt></code>Y.Do.AlterArgs(message, newArgArray)</code></dt> |
|
10001 * <dd>Replace the arguments that the original function will be |
|
10002 * called with.</dd> |
|
10003 * <dt></code>Y.Do.Prevent(message)</code></dt> |
|
10004 * <dd>Don't execute the wrapped function. Other before phase |
|
10005 * wrappers will be executed.</dd> |
|
10006 * </dl> |
|
10007 * |
|
10008 * @method before |
|
10009 * @param fn {Function} the function to execute |
|
10010 * @param obj the object hosting the method to displace |
|
10011 * @param sFn {string} the name of the method to displace |
|
10012 * @param c The execution context for fn |
|
10013 * @param arg* {mixed} 0..n additional arguments to supply to the subscriber |
|
10014 * when the event fires. |
|
10015 * @return {string} handle for the subscription |
|
10016 * @static |
|
10017 */ |
|
10018 before: function(fn, obj, sFn, c) { |
|
10019 // Y.log('Do before: ' + sFn, 'info', 'event'); |
|
10020 var f = fn, a; |
|
10021 if (c) { |
|
10022 a = [fn, c].concat(Y.Array(arguments, 4, true)); |
|
10023 f = Y.rbind.apply(Y, a); |
|
10024 } |
|
10025 |
|
10026 return this._inject(DO_BEFORE, f, obj, sFn); |
|
10027 }, |
|
10028 |
|
10029 /** |
|
10030 * <p>Execute the supplied method after the specified function. Wrapping |
|
10031 * function may optionally return an instance of the following classes to |
|
10032 * further alter runtime behavior:</p> |
|
10033 * <dl> |
|
10034 * <dt></code>Y.Do.Halt(message, returnValue)</code></dt> |
|
10035 * <dd>Immediatly stop execution and return |
|
10036 * <code>returnValue</code>. No other wrapping functions will be |
|
10037 * executed.</dd> |
|
10038 * <dt></code>Y.Do.AlterReturn(message, returnValue)</code></dt> |
|
10039 * <dd>Return <code>returnValue</code> instead of the wrapped |
|
10040 * method's original return value. This can be further altered by |
|
10041 * other after phase wrappers.</dd> |
|
10042 * </dl> |
|
10043 * |
|
10044 * <p>The static properties <code>Y.Do.originalRetVal</code> and |
|
10045 * <code>Y.Do.currentRetVal</code> will be populated for reference.</p> |
|
10046 * |
|
10047 * @method after |
|
10048 * @param fn {Function} the function to execute |
|
10049 * @param obj the object hosting the method to displace |
|
10050 * @param sFn {string} the name of the method to displace |
|
10051 * @param c The execution context for fn |
|
10052 * @param arg* {mixed} 0..n additional arguments to supply to the subscriber |
|
10053 * @return {string} handle for the subscription |
|
10054 * @static |
|
10055 */ |
|
10056 after: function(fn, obj, sFn, c) { |
|
10057 var f = fn, a; |
|
10058 if (c) { |
|
10059 a = [fn, c].concat(Y.Array(arguments, 4, true)); |
|
10060 f = Y.rbind.apply(Y, a); |
|
10061 } |
|
10062 |
|
10063 return this._inject(DO_AFTER, f, obj, sFn); |
|
10064 }, |
|
10065 |
|
10066 /** |
|
10067 * Execute the supplied method before or after the specified function. |
|
10068 * Used by <code>before</code> and <code>after</code>. |
|
10069 * |
|
10070 * @method _inject |
|
10071 * @param when {string} before or after |
|
10072 * @param fn {Function} the function to execute |
|
10073 * @param obj the object hosting the method to displace |
|
10074 * @param sFn {string} the name of the method to displace |
|
10075 * @param c The execution context for fn |
|
10076 * @return {string} handle for the subscription |
|
10077 * @private |
|
10078 * @static |
|
10079 */ |
|
10080 _inject: function(when, fn, obj, sFn) { |
|
10081 // object id |
|
10082 var id = Y.stamp(obj), o, sid; |
|
10083 |
|
10084 if (!obj._yuiaop) { |
|
10085 // create a map entry for the obj if it doesn't exist, to hold overridden methods |
|
10086 obj._yuiaop = {}; |
|
10087 } |
|
10088 |
|
10089 o = obj._yuiaop; |
|
10090 |
|
10091 if (!o[sFn]) { |
|
10092 // create a map entry for the method if it doesn't exist |
|
10093 o[sFn] = new Y.Do.Method(obj, sFn); |
|
10094 |
|
10095 // re-route the method to our wrapper |
|
10096 obj[sFn] = function() { |
|
10097 return o[sFn].exec.apply(o[sFn], arguments); |
|
10098 }; |
|
10099 } |
|
10100 |
|
10101 // subscriber id |
|
10102 sid = id + Y.stamp(fn) + sFn; |
|
10103 |
|
10104 // register the callback |
|
10105 o[sFn].register(sid, fn, when); |
|
10106 |
|
10107 return new Y.EventHandle(o[sFn], sid); |
|
10108 }, |
|
10109 |
|
10110 /** |
|
10111 * Detach a before or after subscription. |
|
10112 * |
|
10113 * @method detach |
|
10114 * @param handle {string} the subscription handle |
|
10115 * @static |
|
10116 */ |
|
10117 detach: function(handle) { |
|
10118 if (handle.detach) { |
|
10119 handle.detach(); |
|
10120 } |
|
10121 } |
|
10122 }; |
|
10123 |
|
10124 Y.Do = DO; |
|
10125 |
|
10126 ////////////////////////////////////////////////////////////////////////// |
|
10127 |
|
10128 /** |
|
10129 * Contains the return value from the wrapped method, accessible |
|
10130 * by 'after' event listeners. |
|
10131 * |
|
10132 * @property originalRetVal |
|
10133 * @static |
|
10134 * @since 3.2.0 |
|
10135 */ |
|
10136 |
|
10137 /** |
|
10138 * Contains the current state of the return value, consumable by |
|
10139 * 'after' event listeners, and updated if an after subscriber |
|
10140 * changes the return value generated by the wrapped function. |
|
10141 * |
|
10142 * @property currentRetVal |
|
10143 * @static |
|
10144 * @since 3.2.0 |
|
10145 */ |
|
10146 |
|
10147 ////////////////////////////////////////////////////////////////////////// |
|
10148 |
|
10149 /** |
|
10150 * Wrapper for a displaced method with aop enabled |
|
10151 * @class Do.Method |
|
10152 * @constructor |
|
10153 * @param obj The object to operate on |
|
10154 * @param sFn The name of the method to displace |
|
10155 */ |
|
10156 DO.Method = function(obj, sFn) { |
|
10157 this.obj = obj; |
|
10158 this.methodName = sFn; |
|
10159 this.method = obj[sFn]; |
|
10160 this.before = {}; |
|
10161 this.after = {}; |
|
10162 }; |
|
10163 |
|
10164 /** |
|
10165 * Register a aop subscriber |
|
10166 * @method register |
|
10167 * @param sid {string} the subscriber id |
|
10168 * @param fn {Function} the function to execute |
|
10169 * @param when {string} when to execute the function |
|
10170 */ |
|
10171 DO.Method.prototype.register = function (sid, fn, when) { |
|
10172 if (when) { |
|
10173 this.after[sid] = fn; |
|
10174 } else { |
|
10175 this.before[sid] = fn; |
|
10176 } |
|
10177 }; |
|
10178 |
|
10179 /** |
|
10180 * Unregister a aop subscriber |
|
10181 * @method delete |
|
10182 * @param sid {string} the subscriber id |
|
10183 * @param fn {Function} the function to execute |
|
10184 * @param when {string} when to execute the function |
|
10185 */ |
|
10186 DO.Method.prototype._delete = function (sid) { |
|
10187 // Y.log('Y.Do._delete: ' + sid, 'info', 'Event'); |
|
10188 delete this.before[sid]; |
|
10189 delete this.after[sid]; |
|
10190 }; |
|
10191 |
|
10192 /** |
|
10193 * <p>Execute the wrapped method. All arguments are passed into the wrapping |
|
10194 * functions. If any of the before wrappers return an instance of |
|
10195 * <code>Y.Do.Halt</code> or <code>Y.Do.Prevent</code>, neither the wrapped |
|
10196 * function nor any after phase subscribers will be executed.</p> |
|
10197 * |
|
10198 * <p>The return value will be the return value of the wrapped function or one |
|
10199 * provided by a wrapper function via an instance of <code>Y.Do.Halt</code> or |
|
10200 * <code>Y.Do.AlterReturn</code>. |
|
10201 * |
|
10202 * @method exec |
|
10203 * @param arg* {any} Arguments are passed to the wrapping and wrapped functions |
|
10204 * @return {any} Return value of wrapped function unless overwritten (see above) |
|
10205 */ |
|
10206 DO.Method.prototype.exec = function () { |
|
10207 |
|
10208 var args = Y.Array(arguments, 0, true), |
|
10209 i, ret, newRet, |
|
10210 bf = this.before, |
|
10211 af = this.after, |
|
10212 prevented = false; |
|
10213 |
|
10214 // execute before |
|
10215 for (i in bf) { |
|
10216 if (bf.hasOwnProperty(i)) { |
|
10217 ret = bf[i].apply(this.obj, args); |
|
10218 if (ret) { |
|
10219 switch (ret.constructor) { |
|
10220 case DO.Halt: |
|
10221 return ret.retVal; |
|
10222 case DO.AlterArgs: |
|
10223 args = ret.newArgs; |
|
10224 break; |
|
10225 case DO.Prevent: |
|
10226 prevented = true; |
|
10227 break; |
|
10228 default: |
|
10229 } |
|
10230 } |
|
10231 } |
|
10232 } |
|
10233 |
|
10234 // execute method |
|
10235 if (!prevented) { |
|
10236 ret = this.method.apply(this.obj, args); |
|
10237 } |
|
10238 |
|
10239 DO.originalRetVal = ret; |
|
10240 DO.currentRetVal = ret; |
|
10241 |
|
10242 // execute after methods. |
|
10243 for (i in af) { |
|
10244 if (af.hasOwnProperty(i)) { |
|
10245 newRet = af[i].apply(this.obj, args); |
|
10246 // Stop processing if a Halt object is returned |
|
10247 if (newRet && newRet.constructor === DO.Halt) { |
|
10248 return newRet.retVal; |
|
10249 // Check for a new return value |
|
10250 } else if (newRet && newRet.constructor === DO.AlterReturn) { |
|
10251 ret = newRet.newRetVal; |
|
10252 // Update the static retval state |
|
10253 DO.currentRetVal = ret; |
|
10254 } |
|
10255 } |
|
10256 } |
|
10257 |
|
10258 return ret; |
|
10259 }; |
|
10260 |
|
10261 ////////////////////////////////////////////////////////////////////////// |
|
10262 |
|
10263 /** |
|
10264 * Return an AlterArgs object when you want to change the arguments that |
|
10265 * were passed into the function. Useful for Do.before subscribers. An |
|
10266 * example would be a service that scrubs out illegal characters prior to |
|
10267 * executing the core business logic. |
|
10268 * @class Do.AlterArgs |
|
10269 * @constructor |
|
10270 * @param msg {String} (optional) Explanation of the altered return value |
|
10271 * @param newArgs {Array} Call parameters to be used for the original method |
|
10272 * instead of the arguments originally passed in. |
|
10273 */ |
|
10274 DO.AlterArgs = function(msg, newArgs) { |
|
10275 this.msg = msg; |
|
10276 this.newArgs = newArgs; |
|
10277 }; |
|
10278 |
|
10279 /** |
|
10280 * Return an AlterReturn object when you want to change the result returned |
|
10281 * from the core method to the caller. Useful for Do.after subscribers. |
|
10282 * @class Do.AlterReturn |
|
10283 * @constructor |
|
10284 * @param msg {String} (optional) Explanation of the altered return value |
|
10285 * @param newRetVal {any} Return value passed to code that invoked the wrapped |
|
10286 * function. |
|
10287 */ |
|
10288 DO.AlterReturn = function(msg, newRetVal) { |
|
10289 this.msg = msg; |
|
10290 this.newRetVal = newRetVal; |
|
10291 }; |
|
10292 |
|
10293 /** |
|
10294 * Return a Halt object when you want to terminate the execution |
|
10295 * of all subsequent subscribers as well as the wrapped method |
|
10296 * if it has not exectued yet. Useful for Do.before subscribers. |
|
10297 * @class Do.Halt |
|
10298 * @constructor |
|
10299 * @param msg {String} (optional) Explanation of why the termination was done |
|
10300 * @param retVal {any} Return value passed to code that invoked the wrapped |
|
10301 * function. |
|
10302 */ |
|
10303 DO.Halt = function(msg, retVal) { |
|
10304 this.msg = msg; |
|
10305 this.retVal = retVal; |
|
10306 }; |
|
10307 |
|
10308 /** |
|
10309 * Return a Prevent object when you want to prevent the wrapped function |
|
10310 * from executing, but want the remaining listeners to execute. Useful |
|
10311 * for Do.before subscribers. |
|
10312 * @class Do.Prevent |
|
10313 * @constructor |
|
10314 * @param msg {String} (optional) Explanation of why the termination was done |
|
10315 */ |
|
10316 DO.Prevent = function(msg) { |
|
10317 this.msg = msg; |
|
10318 }; |
|
10319 |
|
10320 /** |
|
10321 * Return an Error object when you want to terminate the execution |
|
10322 * of all subsequent method calls. |
|
10323 * @class Do.Error |
|
10324 * @constructor |
|
10325 * @param msg {String} (optional) Explanation of the altered return value |
|
10326 * @param retVal {any} Return value passed to code that invoked the wrapped |
|
10327 * function. |
|
10328 * @deprecated use Y.Do.Halt or Y.Do.Prevent |
|
10329 */ |
|
10330 DO.Error = DO.Halt; |
|
10331 |
|
10332 |
|
10333 ////////////////////////////////////////////////////////////////////////// |
|
10334 |
|
10335 /** |
|
10336 * Custom event engine, DOM event listener abstraction layer, synthetic DOM |
|
10337 * events. |
|
10338 * @module event-custom |
|
10339 * @submodule event-custom-base |
|
10340 */ |
|
10341 |
|
10342 |
|
10343 // var onsubscribeType = "_event:onsub", |
|
10344 var YArray = Y.Array, |
|
10345 |
|
10346 AFTER = 'after', |
|
10347 CONFIGS = [ |
|
10348 'broadcast', |
|
10349 'monitored', |
|
10350 'bubbles', |
|
10351 'context', |
|
10352 'contextFn', |
|
10353 'currentTarget', |
|
10354 'defaultFn', |
|
10355 'defaultTargetOnly', |
|
10356 'details', |
|
10357 'emitFacade', |
|
10358 'fireOnce', |
|
10359 'async', |
|
10360 'host', |
|
10361 'preventable', |
|
10362 'preventedFn', |
|
10363 'queuable', |
|
10364 'silent', |
|
10365 'stoppedFn', |
|
10366 'target', |
|
10367 'type' |
|
10368 ], |
|
10369 |
|
10370 CONFIGS_HASH = YArray.hash(CONFIGS), |
|
10371 |
|
10372 nativeSlice = Array.prototype.slice, |
|
10373 |
|
10374 YUI3_SIGNATURE = 9, |
|
10375 YUI_LOG = 'yui:log', |
|
10376 |
|
10377 mixConfigs = function(r, s, ov) { |
|
10378 var p; |
|
10379 |
|
10380 for (p in s) { |
|
10381 if (CONFIGS_HASH[p] && (ov || !(p in r))) { |
|
10382 r[p] = s[p]; |
|
10383 } |
|
10384 } |
|
10385 |
|
10386 return r; |
|
10387 }; |
|
10388 |
|
10389 /** |
|
10390 * The CustomEvent class lets you define events for your application |
|
10391 * that can be subscribed to by one or more independent component. |
|
10392 * |
|
10393 * @param {String} type The type of event, which is passed to the callback |
|
10394 * when the event fires. |
|
10395 * @param {object} defaults configuration object. |
|
10396 * @class CustomEvent |
|
10397 * @constructor |
|
10398 */ |
|
10399 |
|
10400 /** |
|
10401 * The type of event, returned to subscribers when the event fires |
|
10402 * @property type |
|
10403 * @type string |
|
10404 */ |
|
10405 |
|
10406 /** |
|
10407 * By default all custom events are logged in the debug build, set silent |
|
10408 * to true to disable debug outpu for this event. |
|
10409 * @property silent |
|
10410 * @type boolean |
|
10411 */ |
|
10412 |
|
10413 Y.CustomEvent = function(type, defaults) { |
|
10414 |
|
10415 this._kds = Y.CustomEvent.keepDeprecatedSubs; |
|
10416 |
|
10417 this.id = Y.guid(); |
|
10418 |
|
10419 this.type = type; |
|
10420 this.silent = this.logSystem = (type === YUI_LOG); |
|
10421 |
|
10422 if (this._kds) { |
|
10423 /** |
|
10424 * The subscribers to this event |
|
10425 * @property subscribers |
|
10426 * @type Subscriber {} |
|
10427 * @deprecated |
|
10428 */ |
|
10429 |
|
10430 /** |
|
10431 * 'After' subscribers |
|
10432 * @property afters |
|
10433 * @type Subscriber {} |
|
10434 * @deprecated |
|
10435 */ |
|
10436 this.subscribers = {}; |
|
10437 this.afters = {}; |
|
10438 } |
|
10439 |
|
10440 if (defaults) { |
|
10441 mixConfigs(this, defaults, true); |
|
10442 } |
|
10443 }; |
|
10444 |
|
10445 /** |
|
10446 * Static flag to enable population of the <a href="#property_subscribers">`subscribers`</a> |
|
10447 * and <a href="#property_subscribers">`afters`</a> properties held on a `CustomEvent` instance. |
|
10448 * |
|
10449 * These properties were changed to private properties (`_subscribers` and `_afters`), and |
|
10450 * converted from objects to arrays for performance reasons. |
|
10451 * |
|
10452 * Setting this property to true will populate the deprecated `subscribers` and `afters` |
|
10453 * properties for people who may be using them (which is expected to be rare). There will |
|
10454 * be a performance hit, compared to the new array based implementation. |
|
10455 * |
|
10456 * If you are using these deprecated properties for a use case which the public API |
|
10457 * does not support, please file an enhancement request, and we can provide an alternate |
|
10458 * public implementation which doesn't have the performance cost required to maintiain the |
|
10459 * properties as objects. |
|
10460 * |
|
10461 * @property keepDeprecatedSubs |
|
10462 * @static |
|
10463 * @for CustomEvent |
|
10464 * @type boolean |
|
10465 * @default false |
|
10466 * @deprecated |
|
10467 */ |
|
10468 Y.CustomEvent.keepDeprecatedSubs = false; |
|
10469 |
|
10470 Y.CustomEvent.mixConfigs = mixConfigs; |
|
10471 |
|
10472 Y.CustomEvent.prototype = { |
|
10473 |
|
10474 constructor: Y.CustomEvent, |
|
10475 |
|
10476 /** |
|
10477 * Monitor when an event is attached or detached. |
|
10478 * |
|
10479 * @property monitored |
|
10480 * @type boolean |
|
10481 */ |
|
10482 |
|
10483 /** |
|
10484 * If 0, this event does not broadcast. If 1, the YUI instance is notified |
|
10485 * every time this event fires. If 2, the YUI instance and the YUI global |
|
10486 * (if event is enabled on the global) are notified every time this event |
|
10487 * fires. |
|
10488 * @property broadcast |
|
10489 * @type int |
|
10490 */ |
|
10491 |
|
10492 /** |
|
10493 * Specifies whether this event should be queued when the host is actively |
|
10494 * processing an event. This will effect exectution order of the callbacks |
|
10495 * for the various events. |
|
10496 * @property queuable |
|
10497 * @type boolean |
|
10498 * @default false |
|
10499 */ |
|
10500 |
|
10501 /** |
|
10502 * This event has fired if true |
|
10503 * |
|
10504 * @property fired |
|
10505 * @type boolean |
|
10506 * @default false; |
|
10507 */ |
|
10508 |
|
10509 /** |
|
10510 * An array containing the arguments the custom event |
|
10511 * was last fired with. |
|
10512 * @property firedWith |
|
10513 * @type Array |
|
10514 */ |
|
10515 |
|
10516 /** |
|
10517 * This event should only fire one time if true, and if |
|
10518 * it has fired, any new subscribers should be notified |
|
10519 * immediately. |
|
10520 * |
|
10521 * @property fireOnce |
|
10522 * @type boolean |
|
10523 * @default false; |
|
10524 */ |
|
10525 |
|
10526 /** |
|
10527 * fireOnce listeners will fire syncronously unless async |
|
10528 * is set to true |
|
10529 * @property async |
|
10530 * @type boolean |
|
10531 * @default false |
|
10532 */ |
|
10533 |
|
10534 /** |
|
10535 * Flag for stopPropagation that is modified during fire() |
|
10536 * 1 means to stop propagation to bubble targets. 2 means |
|
10537 * to also stop additional subscribers on this target. |
|
10538 * @property stopped |
|
10539 * @type int |
|
10540 */ |
|
10541 |
|
10542 /** |
|
10543 * Flag for preventDefault that is modified during fire(). |
|
10544 * if it is not 0, the default behavior for this event |
|
10545 * @property prevented |
|
10546 * @type int |
|
10547 */ |
|
10548 |
|
10549 /** |
|
10550 * Specifies the host for this custom event. This is used |
|
10551 * to enable event bubbling |
|
10552 * @property host |
|
10553 * @type EventTarget |
|
10554 */ |
|
10555 |
|
10556 /** |
|
10557 * The default function to execute after event listeners |
|
10558 * have fire, but only if the default action was not |
|
10559 * prevented. |
|
10560 * @property defaultFn |
|
10561 * @type Function |
|
10562 */ |
|
10563 |
|
10564 /** |
|
10565 * The function to execute if a subscriber calls |
|
10566 * stopPropagation or stopImmediatePropagation |
|
10567 * @property stoppedFn |
|
10568 * @type Function |
|
10569 */ |
|
10570 |
|
10571 /** |
|
10572 * The function to execute if a subscriber calls |
|
10573 * preventDefault |
|
10574 * @property preventedFn |
|
10575 * @type Function |
|
10576 */ |
|
10577 |
|
10578 /** |
|
10579 * The subscribers to this event |
|
10580 * @property _subscribers |
|
10581 * @type Subscriber [] |
|
10582 * @private |
|
10583 */ |
|
10584 |
|
10585 /** |
|
10586 * 'After' subscribers |
|
10587 * @property _afters |
|
10588 * @type Subscriber [] |
|
10589 * @private |
|
10590 */ |
|
10591 |
|
10592 /** |
|
10593 * If set to true, the custom event will deliver an EventFacade object |
|
10594 * that is similar to a DOM event object. |
|
10595 * @property emitFacade |
|
10596 * @type boolean |
|
10597 * @default false |
|
10598 */ |
|
10599 |
|
10600 /** |
|
10601 * Supports multiple options for listener signatures in order to |
|
10602 * port YUI 2 apps. |
|
10603 * @property signature |
|
10604 * @type int |
|
10605 * @default 9 |
|
10606 */ |
|
10607 signature : YUI3_SIGNATURE, |
|
10608 |
|
10609 /** |
|
10610 * The context the the event will fire from by default. Defaults to the YUI |
|
10611 * instance. |
|
10612 * @property context |
|
10613 * @type object |
|
10614 */ |
|
10615 context : Y, |
|
10616 |
|
10617 /** |
|
10618 * Specifies whether or not this event's default function |
|
10619 * can be cancelled by a subscriber by executing preventDefault() |
|
10620 * on the event facade |
|
10621 * @property preventable |
|
10622 * @type boolean |
|
10623 * @default true |
|
10624 */ |
|
10625 preventable : true, |
|
10626 |
|
10627 /** |
|
10628 * Specifies whether or not a subscriber can stop the event propagation |
|
10629 * via stopPropagation(), stopImmediatePropagation(), or halt() |
|
10630 * |
|
10631 * Events can only bubble if emitFacade is true. |
|
10632 * |
|
10633 * @property bubbles |
|
10634 * @type boolean |
|
10635 * @default true |
|
10636 */ |
|
10637 bubbles : true, |
|
10638 |
|
10639 /** |
|
10640 * Returns the number of subscribers for this event as the sum of the on() |
|
10641 * subscribers and after() subscribers. |
|
10642 * |
|
10643 * @method hasSubs |
|
10644 * @return Number |
|
10645 */ |
|
10646 hasSubs: function(when) { |
|
10647 var s = 0, |
|
10648 a = 0, |
|
10649 subs = this._subscribers, |
|
10650 afters = this._afters, |
|
10651 sib = this.sibling; |
|
10652 |
|
10653 if (subs) { |
|
10654 s = subs.length; |
|
10655 } |
|
10656 |
|
10657 if (afters) { |
|
10658 a = afters.length; |
|
10659 } |
|
10660 |
|
10661 if (sib) { |
|
10662 subs = sib._subscribers; |
|
10663 afters = sib._afters; |
|
10664 |
|
10665 if (subs) { |
|
10666 s += subs.length; |
|
10667 } |
|
10668 |
|
10669 if (afters) { |
|
10670 a += afters.length; |
|
10671 } |
|
10672 } |
|
10673 |
|
10674 if (when) { |
|
10675 return (when === 'after') ? a : s; |
|
10676 } |
|
10677 |
|
10678 return (s + a); |
|
10679 }, |
|
10680 |
|
10681 /** |
|
10682 * Monitor the event state for the subscribed event. The first parameter |
|
10683 * is what should be monitored, the rest are the normal parameters when |
|
10684 * subscribing to an event. |
|
10685 * @method monitor |
|
10686 * @param what {string} what to monitor ('detach', 'attach', 'publish'). |
|
10687 * @return {EventHandle} return value from the monitor event subscription. |
|
10688 */ |
|
10689 monitor: function(what) { |
|
10690 this.monitored = true; |
|
10691 var type = this.id + '|' + this.type + '_' + what, |
|
10692 args = nativeSlice.call(arguments, 0); |
|
10693 args[0] = type; |
|
10694 return this.host.on.apply(this.host, args); |
|
10695 }, |
|
10696 |
|
10697 /** |
|
10698 * Get all of the subscribers to this event and any sibling event |
|
10699 * @method getSubs |
|
10700 * @return {Array} first item is the on subscribers, second the after. |
|
10701 */ |
|
10702 getSubs: function() { |
|
10703 |
|
10704 var sibling = this.sibling, |
|
10705 subs = this._subscribers, |
|
10706 afters = this._afters, |
|
10707 siblingSubs, |
|
10708 siblingAfters; |
|
10709 |
|
10710 if (sibling) { |
|
10711 siblingSubs = sibling._subscribers; |
|
10712 siblingAfters = sibling._afters; |
|
10713 } |
|
10714 |
|
10715 if (siblingSubs) { |
|
10716 if (subs) { |
|
10717 subs = subs.concat(siblingSubs); |
|
10718 } else { |
|
10719 subs = siblingSubs.concat(); |
|
10720 } |
|
10721 } else { |
|
10722 if (subs) { |
|
10723 subs = subs.concat(); |
|
10724 } else { |
|
10725 subs = []; |
|
10726 } |
|
10727 } |
|
10728 |
|
10729 if (siblingAfters) { |
|
10730 if (afters) { |
|
10731 afters = afters.concat(siblingAfters); |
|
10732 } else { |
|
10733 afters = siblingAfters.concat(); |
|
10734 } |
|
10735 } else { |
|
10736 if (afters) { |
|
10737 afters = afters.concat(); |
|
10738 } else { |
|
10739 afters = []; |
|
10740 } |
|
10741 } |
|
10742 |
|
10743 return [subs, afters]; |
|
10744 }, |
|
10745 |
|
10746 /** |
|
10747 * Apply configuration properties. Only applies the CONFIG whitelist |
|
10748 * @method applyConfig |
|
10749 * @param o hash of properties to apply. |
|
10750 * @param force {boolean} if true, properties that exist on the event |
|
10751 * will be overwritten. |
|
10752 */ |
|
10753 applyConfig: function(o, force) { |
|
10754 mixConfigs(this, o, force); |
|
10755 }, |
|
10756 |
|
10757 /** |
|
10758 * Create the Subscription for subscribing function, context, and bound |
|
10759 * arguments. If this is a fireOnce event, the subscriber is immediately |
|
10760 * notified. |
|
10761 * |
|
10762 * @method _on |
|
10763 * @param fn {Function} Subscription callback |
|
10764 * @param [context] {Object} Override `this` in the callback |
|
10765 * @param [args] {Array} bound arguments that will be passed to the callback after the arguments generated by fire() |
|
10766 * @param [when] {String} "after" to slot into after subscribers |
|
10767 * @return {EventHandle} |
|
10768 * @protected |
|
10769 */ |
|
10770 _on: function(fn, context, args, when) { |
|
10771 |
|
10772 if (!fn) { this.log('Invalid callback for CE: ' + this.type); } |
|
10773 |
|
10774 var s = new Y.Subscriber(fn, context, args, when); |
|
10775 |
|
10776 if (this.fireOnce && this.fired) { |
|
10777 if (this.async) { |
|
10778 setTimeout(Y.bind(this._notify, this, s, this.firedWith), 0); |
|
10779 } else { |
|
10780 this._notify(s, this.firedWith); |
|
10781 } |
|
10782 } |
|
10783 |
|
10784 if (when === AFTER) { |
|
10785 if (!this._afters) { |
|
10786 this._afters = []; |
|
10787 this._hasAfters = true; |
|
10788 } |
|
10789 this._afters.push(s); |
|
10790 } else { |
|
10791 if (!this._subscribers) { |
|
10792 this._subscribers = []; |
|
10793 this._hasSubs = true; |
|
10794 } |
|
10795 this._subscribers.push(s); |
|
10796 } |
|
10797 |
|
10798 if (this._kds) { |
|
10799 if (when === AFTER) { |
|
10800 this.afters[s.id] = s; |
|
10801 } else { |
|
10802 this.subscribers[s.id] = s; |
|
10803 } |
|
10804 } |
|
10805 |
|
10806 return new Y.EventHandle(this, s); |
|
10807 }, |
|
10808 |
|
10809 /** |
|
10810 * Listen for this event |
|
10811 * @method subscribe |
|
10812 * @param {Function} fn The function to execute. |
|
10813 * @return {EventHandle} Unsubscribe handle. |
|
10814 * @deprecated use on. |
|
10815 */ |
|
10816 subscribe: function(fn, context) { |
|
10817 Y.log('ce.subscribe deprecated, use "on"', 'warn', 'deprecated'); |
|
10818 var a = (arguments.length > 2) ? nativeSlice.call(arguments, 2) : null; |
|
10819 return this._on(fn, context, a, true); |
|
10820 }, |
|
10821 |
|
10822 /** |
|
10823 * Listen for this event |
|
10824 * @method on |
|
10825 * @param {Function} fn The function to execute. |
|
10826 * @param {object} context optional execution context. |
|
10827 * @param {mixed} arg* 0..n additional arguments to supply to the subscriber |
|
10828 * when the event fires. |
|
10829 * @return {EventHandle} An object with a detach method to detch the handler(s). |
|
10830 */ |
|
10831 on: function(fn, context) { |
|
10832 var a = (arguments.length > 2) ? nativeSlice.call(arguments, 2) : null; |
|
10833 |
|
10834 if (this.monitored && this.host) { |
|
10835 this.host._monitor('attach', this, { |
|
10836 args: arguments |
|
10837 }); |
|
10838 } |
|
10839 return this._on(fn, context, a, true); |
|
10840 }, |
|
10841 |
|
10842 /** |
|
10843 * Listen for this event after the normal subscribers have been notified and |
|
10844 * the default behavior has been applied. If a normal subscriber prevents the |
|
10845 * default behavior, it also prevents after listeners from firing. |
|
10846 * @method after |
|
10847 * @param {Function} fn The function to execute. |
|
10848 * @param {object} context optional execution context. |
|
10849 * @param {mixed} arg* 0..n additional arguments to supply to the subscriber |
|
10850 * when the event fires. |
|
10851 * @return {EventHandle} handle Unsubscribe handle. |
|
10852 */ |
|
10853 after: function(fn, context) { |
|
10854 var a = (arguments.length > 2) ? nativeSlice.call(arguments, 2) : null; |
|
10855 return this._on(fn, context, a, AFTER); |
|
10856 }, |
|
10857 |
|
10858 /** |
|
10859 * Detach listeners. |
|
10860 * @method detach |
|
10861 * @param {Function} fn The subscribed function to remove, if not supplied |
|
10862 * all will be removed. |
|
10863 * @param {Object} context The context object passed to subscribe. |
|
10864 * @return {int} returns the number of subscribers unsubscribed. |
|
10865 */ |
|
10866 detach: function(fn, context) { |
|
10867 // unsubscribe handle |
|
10868 if (fn && fn.detach) { |
|
10869 return fn.detach(); |
|
10870 } |
|
10871 |
|
10872 var i, s, |
|
10873 found = 0, |
|
10874 subs = this._subscribers, |
|
10875 afters = this._afters; |
|
10876 |
|
10877 if (subs) { |
|
10878 for (i = subs.length; i >= 0; i--) { |
|
10879 s = subs[i]; |
|
10880 if (s && (!fn || fn === s.fn)) { |
|
10881 this._delete(s, subs, i); |
|
10882 found++; |
|
10883 } |
|
10884 } |
|
10885 } |
|
10886 |
|
10887 if (afters) { |
|
10888 for (i = afters.length; i >= 0; i--) { |
|
10889 s = afters[i]; |
|
10890 if (s && (!fn || fn === s.fn)) { |
|
10891 this._delete(s, afters, i); |
|
10892 found++; |
|
10893 } |
|
10894 } |
|
10895 } |
|
10896 |
|
10897 return found; |
|
10898 }, |
|
10899 |
|
10900 /** |
|
10901 * Detach listeners. |
|
10902 * @method unsubscribe |
|
10903 * @param {Function} fn The subscribed function to remove, if not supplied |
|
10904 * all will be removed. |
|
10905 * @param {Object} context The context object passed to subscribe. |
|
10906 * @return {int|undefined} returns the number of subscribers unsubscribed. |
|
10907 * @deprecated use detach. |
|
10908 */ |
|
10909 unsubscribe: function() { |
|
10910 return this.detach.apply(this, arguments); |
|
10911 }, |
|
10912 |
|
10913 /** |
|
10914 * Notify a single subscriber |
|
10915 * @method _notify |
|
10916 * @param {Subscriber} s the subscriber. |
|
10917 * @param {Array} args the arguments array to apply to the listener. |
|
10918 * @protected |
|
10919 */ |
|
10920 _notify: function(s, args, ef) { |
|
10921 |
|
10922 this.log(this.type + '->' + 'sub: ' + s.id); |
|
10923 |
|
10924 var ret; |
|
10925 |
|
10926 ret = s.notify(args, this); |
|
10927 |
|
10928 if (false === ret || this.stopped > 1) { |
|
10929 this.log(this.type + ' cancelled by subscriber'); |
|
10930 return false; |
|
10931 } |
|
10932 |
|
10933 return true; |
|
10934 }, |
|
10935 |
|
10936 /** |
|
10937 * Logger abstraction to centralize the application of the silent flag |
|
10938 * @method log |
|
10939 * @param {string} msg message to log. |
|
10940 * @param {string} cat log category. |
|
10941 */ |
|
10942 log: function(msg, cat) { |
|
10943 if (!this.silent) { Y.log(this.id + ': ' + msg, cat || 'info', 'event'); } |
|
10944 }, |
|
10945 |
|
10946 /** |
|
10947 * Notifies the subscribers. The callback functions will be executed |
|
10948 * from the context specified when the event was created, and with the |
|
10949 * following parameters: |
|
10950 * <ul> |
|
10951 * <li>The type of event</li> |
|
10952 * <li>All of the arguments fire() was executed with as an array</li> |
|
10953 * <li>The custom object (if any) that was passed into the subscribe() |
|
10954 * method</li> |
|
10955 * </ul> |
|
10956 * @method fire |
|
10957 * @param {Object*} arguments an arbitrary set of parameters to pass to |
|
10958 * the handler. |
|
10959 * @return {boolean} false if one of the subscribers returned false, |
|
10960 * true otherwise. |
|
10961 * |
|
10962 */ |
|
10963 fire: function() { |
|
10964 |
|
10965 // push is the fastest way to go from arguments to arrays |
|
10966 // for most browsers currently |
|
10967 // http://jsperf.com/push-vs-concat-vs-slice/2 |
|
10968 |
|
10969 var args = []; |
|
10970 args.push.apply(args, arguments); |
|
10971 |
|
10972 return this._fire(args); |
|
10973 }, |
|
10974 |
|
10975 /** |
|
10976 * Private internal implementation for `fire`, which is can be used directly by |
|
10977 * `EventTarget` and other event module classes which have already converted from |
|
10978 * an `arguments` list to an array, to avoid the repeated overhead. |
|
10979 * |
|
10980 * @method _fire |
|
10981 * @private |
|
10982 * @param {Array} args The array of arguments passed to be passed to handlers. |
|
10983 * @return {boolean} false if one of the subscribers returned false, true otherwise. |
|
10984 */ |
|
10985 _fire: function(args) { |
|
10986 |
|
10987 if (this.fireOnce && this.fired) { |
|
10988 this.log('fireOnce event: ' + this.type + ' already fired'); |
|
10989 return true; |
|
10990 } else { |
|
10991 |
|
10992 // this doesn't happen if the event isn't published |
|
10993 // this.host._monitor('fire', this.type, args); |
|
10994 |
|
10995 this.fired = true; |
|
10996 |
|
10997 if (this.fireOnce) { |
|
10998 this.firedWith = args; |
|
10999 } |
|
11000 |
|
11001 if (this.emitFacade) { |
|
11002 return this.fireComplex(args); |
|
11003 } else { |
|
11004 return this.fireSimple(args); |
|
11005 } |
|
11006 } |
|
11007 }, |
|
11008 |
|
11009 /** |
|
11010 * Set up for notifying subscribers of non-emitFacade events. |
|
11011 * |
|
11012 * @method fireSimple |
|
11013 * @param args {Array} Arguments passed to fire() |
|
11014 * @return Boolean false if a subscriber returned false |
|
11015 * @protected |
|
11016 */ |
|
11017 fireSimple: function(args) { |
|
11018 this.stopped = 0; |
|
11019 this.prevented = 0; |
|
11020 if (this.hasSubs()) { |
|
11021 var subs = this.getSubs(); |
|
11022 this._procSubs(subs[0], args); |
|
11023 this._procSubs(subs[1], args); |
|
11024 } |
|
11025 if (this.broadcast) { |
|
11026 this._broadcast(args); |
|
11027 } |
|
11028 return this.stopped ? false : true; |
|
11029 }, |
|
11030 |
|
11031 // Requires the event-custom-complex module for full funcitonality. |
|
11032 fireComplex: function(args) { |
|
11033 this.log('Missing event-custom-complex needed to emit a facade for: ' + this.type); |
|
11034 args[0] = args[0] || {}; |
|
11035 return this.fireSimple(args); |
|
11036 }, |
|
11037 |
|
11038 /** |
|
11039 * Notifies a list of subscribers. |
|
11040 * |
|
11041 * @method _procSubs |
|
11042 * @param subs {Array} List of subscribers |
|
11043 * @param args {Array} Arguments passed to fire() |
|
11044 * @param ef {} |
|
11045 * @return Boolean false if a subscriber returns false or stops the event |
|
11046 * propagation via e.stopPropagation(), |
|
11047 * e.stopImmediatePropagation(), or e.halt() |
|
11048 * @private |
|
11049 */ |
|
11050 _procSubs: function(subs, args, ef) { |
|
11051 var s, i, l; |
|
11052 |
|
11053 for (i = 0, l = subs.length; i < l; i++) { |
|
11054 s = subs[i]; |
|
11055 if (s && s.fn) { |
|
11056 if (false === this._notify(s, args, ef)) { |
|
11057 this.stopped = 2; |
|
11058 } |
|
11059 if (this.stopped === 2) { |
|
11060 return false; |
|
11061 } |
|
11062 } |
|
11063 } |
|
11064 |
|
11065 return true; |
|
11066 }, |
|
11067 |
|
11068 /** |
|
11069 * Notifies the YUI instance if the event is configured with broadcast = 1, |
|
11070 * and both the YUI instance and Y.Global if configured with broadcast = 2. |
|
11071 * |
|
11072 * @method _broadcast |
|
11073 * @param args {Array} Arguments sent to fire() |
|
11074 * @private |
|
11075 */ |
|
11076 _broadcast: function(args) { |
|
11077 if (!this.stopped && this.broadcast) { |
|
11078 |
|
11079 var a = args.concat(); |
|
11080 a.unshift(this.type); |
|
11081 |
|
11082 if (this.host !== Y) { |
|
11083 Y.fire.apply(Y, a); |
|
11084 } |
|
11085 |
|
11086 if (this.broadcast === 2) { |
|
11087 Y.Global.fire.apply(Y.Global, a); |
|
11088 } |
|
11089 } |
|
11090 }, |
|
11091 |
|
11092 /** |
|
11093 * Removes all listeners |
|
11094 * @method unsubscribeAll |
|
11095 * @return {int} The number of listeners unsubscribed. |
|
11096 * @deprecated use detachAll. |
|
11097 */ |
|
11098 unsubscribeAll: function() { |
|
11099 return this.detachAll.apply(this, arguments); |
|
11100 }, |
|
11101 |
|
11102 /** |
|
11103 * Removes all listeners |
|
11104 * @method detachAll |
|
11105 * @return {int} The number of listeners unsubscribed. |
|
11106 */ |
|
11107 detachAll: function() { |
|
11108 return this.detach(); |
|
11109 }, |
|
11110 |
|
11111 /** |
|
11112 * Deletes the subscriber from the internal store of on() and after() |
|
11113 * subscribers. |
|
11114 * |
|
11115 * @method _delete |
|
11116 * @param s subscriber object. |
|
11117 * @param subs (optional) on or after subscriber array |
|
11118 * @param index (optional) The index found. |
|
11119 * @private |
|
11120 */ |
|
11121 _delete: function(s, subs, i) { |
|
11122 var when = s._when; |
|
11123 |
|
11124 if (!subs) { |
|
11125 subs = (when === AFTER) ? this._afters : this._subscribers; |
|
11126 } |
|
11127 |
|
11128 if (subs) { |
|
11129 i = YArray.indexOf(subs, s, 0); |
|
11130 |
|
11131 if (s && subs[i] === s) { |
|
11132 subs.splice(i, 1); |
|
11133 |
|
11134 if (subs.length === 0) { |
|
11135 if (when === AFTER) { |
|
11136 this._hasAfters = false; |
|
11137 } else { |
|
11138 this._hasSubs = false; |
|
11139 } |
|
11140 } |
|
11141 } |
|
11142 } |
|
11143 |
|
11144 if (this._kds) { |
|
11145 if (when === AFTER) { |
|
11146 delete this.afters[s.id]; |
|
11147 } else { |
|
11148 delete this.subscribers[s.id]; |
|
11149 } |
|
11150 } |
|
11151 |
|
11152 if (this.monitored && this.host) { |
|
11153 this.host._monitor('detach', this, { |
|
11154 ce: this, |
|
11155 sub: s |
|
11156 }); |
|
11157 } |
|
11158 |
|
11159 if (s) { |
|
11160 s.deleted = true; |
|
11161 } |
|
11162 } |
|
11163 }; |
|
11164 /** |
|
11165 * Stores the subscriber information to be used when the event fires. |
|
11166 * @param {Function} fn The wrapped function to execute. |
|
11167 * @param {Object} context The value of the keyword 'this' in the listener. |
|
11168 * @param {Array} args* 0..n additional arguments to supply the listener. |
|
11169 * |
|
11170 * @class Subscriber |
|
11171 * @constructor |
|
11172 */ |
|
11173 Y.Subscriber = function(fn, context, args, when) { |
|
11174 |
|
11175 /** |
|
11176 * The callback that will be execute when the event fires |
|
11177 * This is wrapped by Y.rbind if obj was supplied. |
|
11178 * @property fn |
|
11179 * @type Function |
|
11180 */ |
|
11181 this.fn = fn; |
|
11182 |
|
11183 /** |
|
11184 * Optional 'this' keyword for the listener |
|
11185 * @property context |
|
11186 * @type Object |
|
11187 */ |
|
11188 this.context = context; |
|
11189 |
|
11190 /** |
|
11191 * Unique subscriber id |
|
11192 * @property id |
|
11193 * @type String |
|
11194 */ |
|
11195 this.id = Y.guid(); |
|
11196 |
|
11197 /** |
|
11198 * Additional arguments to propagate to the subscriber |
|
11199 * @property args |
|
11200 * @type Array |
|
11201 */ |
|
11202 this.args = args; |
|
11203 |
|
11204 this._when = when; |
|
11205 |
|
11206 /** |
|
11207 * Custom events for a given fire transaction. |
|
11208 * @property events |
|
11209 * @type {EventTarget} |
|
11210 */ |
|
11211 // this.events = null; |
|
11212 |
|
11213 /** |
|
11214 * This listener only reacts to the event once |
|
11215 * @property once |
|
11216 */ |
|
11217 // this.once = false; |
|
11218 |
|
11219 }; |
|
11220 |
|
11221 Y.Subscriber.prototype = { |
|
11222 constructor: Y.Subscriber, |
|
11223 |
|
11224 _notify: function(c, args, ce) { |
|
11225 if (this.deleted && !this.postponed) { |
|
11226 if (this.postponed) { |
|
11227 delete this.fn; |
|
11228 delete this.context; |
|
11229 } else { |
|
11230 delete this.postponed; |
|
11231 return null; |
|
11232 } |
|
11233 } |
|
11234 var a = this.args, ret; |
|
11235 switch (ce.signature) { |
|
11236 case 0: |
|
11237 ret = this.fn.call(c, ce.type, args, c); |
|
11238 break; |
|
11239 case 1: |
|
11240 ret = this.fn.call(c, args[0] || null, c); |
|
11241 break; |
|
11242 default: |
|
11243 if (a || args) { |
|
11244 args = args || []; |
|
11245 a = (a) ? args.concat(a) : args; |
|
11246 ret = this.fn.apply(c, a); |
|
11247 } else { |
|
11248 ret = this.fn.call(c); |
|
11249 } |
|
11250 } |
|
11251 |
|
11252 if (this.once) { |
|
11253 ce._delete(this); |
|
11254 } |
|
11255 |
|
11256 return ret; |
|
11257 }, |
|
11258 |
|
11259 /** |
|
11260 * Executes the subscriber. |
|
11261 * @method notify |
|
11262 * @param args {Array} Arguments array for the subscriber. |
|
11263 * @param ce {CustomEvent} The custom event that sent the notification. |
|
11264 */ |
|
11265 notify: function(args, ce) { |
|
11266 var c = this.context, |
|
11267 ret = true; |
|
11268 |
|
11269 if (!c) { |
|
11270 c = (ce.contextFn) ? ce.contextFn() : ce.context; |
|
11271 } |
|
11272 |
|
11273 // only catch errors if we will not re-throw them. |
|
11274 if (Y.config && Y.config.throwFail) { |
|
11275 ret = this._notify(c, args, ce); |
|
11276 } else { |
|
11277 try { |
|
11278 ret = this._notify(c, args, ce); |
|
11279 } catch (e) { |
|
11280 Y.error(this + ' failed: ' + e.message, e); |
|
11281 } |
|
11282 } |
|
11283 |
|
11284 return ret; |
|
11285 }, |
|
11286 |
|
11287 /** |
|
11288 * Returns true if the fn and obj match this objects properties. |
|
11289 * Used by the unsubscribe method to match the right subscriber. |
|
11290 * |
|
11291 * @method contains |
|
11292 * @param {Function} fn the function to execute. |
|
11293 * @param {Object} context optional 'this' keyword for the listener. |
|
11294 * @return {boolean} true if the supplied arguments match this |
|
11295 * subscriber's signature. |
|
11296 */ |
|
11297 contains: function(fn, context) { |
|
11298 if (context) { |
|
11299 return ((this.fn === fn) && this.context === context); |
|
11300 } else { |
|
11301 return (this.fn === fn); |
|
11302 } |
|
11303 }, |
|
11304 |
|
11305 valueOf : function() { |
|
11306 return this.id; |
|
11307 } |
|
11308 |
|
11309 }; |
|
11310 /** |
|
11311 * Return value from all subscribe operations |
|
11312 * @class EventHandle |
|
11313 * @constructor |
|
11314 * @param {CustomEvent} evt the custom event. |
|
11315 * @param {Subscriber} sub the subscriber. |
|
11316 */ |
|
11317 Y.EventHandle = function(evt, sub) { |
|
11318 |
|
11319 /** |
|
11320 * The custom event |
|
11321 * |
|
11322 * @property evt |
|
11323 * @type CustomEvent |
|
11324 */ |
|
11325 this.evt = evt; |
|
11326 |
|
11327 /** |
|
11328 * The subscriber object |
|
11329 * |
|
11330 * @property sub |
|
11331 * @type Subscriber |
|
11332 */ |
|
11333 this.sub = sub; |
|
11334 }; |
|
11335 |
|
11336 Y.EventHandle.prototype = { |
|
11337 batch: function(f, c) { |
|
11338 f.call(c || this, this); |
|
11339 if (Y.Lang.isArray(this.evt)) { |
|
11340 Y.Array.each(this.evt, function(h) { |
|
11341 h.batch.call(c || h, f); |
|
11342 }); |
|
11343 } |
|
11344 }, |
|
11345 |
|
11346 /** |
|
11347 * Detaches this subscriber |
|
11348 * @method detach |
|
11349 * @return {int} the number of detached listeners |
|
11350 */ |
|
11351 detach: function() { |
|
11352 var evt = this.evt, detached = 0, i; |
|
11353 if (evt) { |
|
11354 // Y.log('EventHandle.detach: ' + this.sub, 'info', 'Event'); |
|
11355 if (Y.Lang.isArray(evt)) { |
|
11356 for (i = 0; i < evt.length; i++) { |
|
11357 detached += evt[i].detach(); |
|
11358 } |
|
11359 } else { |
|
11360 evt._delete(this.sub); |
|
11361 detached = 1; |
|
11362 } |
|
11363 |
|
11364 } |
|
11365 |
|
11366 return detached; |
|
11367 }, |
|
11368 |
|
11369 /** |
|
11370 * Monitor the event state for the subscribed event. The first parameter |
|
11371 * is what should be monitored, the rest are the normal parameters when |
|
11372 * subscribing to an event. |
|
11373 * @method monitor |
|
11374 * @param what {string} what to monitor ('attach', 'detach', 'publish'). |
|
11375 * @return {EventHandle} return value from the monitor event subscription. |
|
11376 */ |
|
11377 monitor: function(what) { |
|
11378 return this.evt.monitor.apply(this.evt, arguments); |
|
11379 } |
|
11380 }; |
|
11381 |
|
11382 /** |
|
11383 * Custom event engine, DOM event listener abstraction layer, synthetic DOM |
|
11384 * events. |
|
11385 * @module event-custom |
|
11386 * @submodule event-custom-base |
|
11387 */ |
|
11388 |
|
11389 /** |
|
11390 * EventTarget provides the implementation for any object to |
|
11391 * publish, subscribe and fire to custom events, and also |
|
11392 * alows other EventTargets to target the object with events |
|
11393 * sourced from the other object. |
|
11394 * EventTarget is designed to be used with Y.augment to wrap |
|
11395 * EventCustom in an interface that allows events to be listened to |
|
11396 * and fired by name. This makes it possible for implementing code to |
|
11397 * subscribe to an event that either has not been created yet, or will |
|
11398 * not be created at all. |
|
11399 * @class EventTarget |
|
11400 * @param opts a configuration object |
|
11401 * @config emitFacade {boolean} if true, all events will emit event |
|
11402 * facade payloads by default (default false) |
|
11403 * @config prefix {String} the prefix to apply to non-prefixed event names |
|
11404 */ |
|
11405 |
|
11406 var L = Y.Lang, |
|
11407 PREFIX_DELIMITER = ':', |
|
11408 CATEGORY_DELIMITER = '|', |
|
11409 AFTER_PREFIX = '~AFTER~', |
|
11410 WILD_TYPE_RE = /(.*?)(:)(.*?)/, |
|
11411 |
|
11412 _wildType = Y.cached(function(type) { |
|
11413 return type.replace(WILD_TYPE_RE, "*$2$3"); |
|
11414 }), |
|
11415 |
|
11416 /** |
|
11417 * If the instance has a prefix attribute and the |
|
11418 * event type is not prefixed, the instance prefix is |
|
11419 * applied to the supplied type. |
|
11420 * @method _getType |
|
11421 * @private |
|
11422 */ |
|
11423 _getType = function(type, pre) { |
|
11424 |
|
11425 if (!pre || type.indexOf(PREFIX_DELIMITER) > -1) { |
|
11426 return type; |
|
11427 } |
|
11428 |
|
11429 return pre + PREFIX_DELIMITER + type; |
|
11430 }, |
|
11431 |
|
11432 /** |
|
11433 * Returns an array with the detach key (if provided), |
|
11434 * and the prefixed event name from _getType |
|
11435 * Y.on('detachcategory| menu:click', fn) |
|
11436 * @method _parseType |
|
11437 * @private |
|
11438 */ |
|
11439 _parseType = Y.cached(function(type, pre) { |
|
11440 |
|
11441 var t = type, detachcategory, after, i; |
|
11442 |
|
11443 if (!L.isString(t)) { |
|
11444 return t; |
|
11445 } |
|
11446 |
|
11447 i = t.indexOf(AFTER_PREFIX); |
|
11448 |
|
11449 if (i > -1) { |
|
11450 after = true; |
|
11451 t = t.substr(AFTER_PREFIX.length); |
|
11452 } |
|
11453 |
|
11454 i = t.indexOf(CATEGORY_DELIMITER); |
|
11455 |
|
11456 if (i > -1) { |
|
11457 detachcategory = t.substr(0, (i)); |
|
11458 t = t.substr(i+1); |
|
11459 if (t === '*') { |
|
11460 t = null; |
|
11461 } |
|
11462 } |
|
11463 |
|
11464 // detach category, full type with instance prefix, is this an after listener, short type |
|
11465 return [detachcategory, (pre) ? _getType(t, pre) : t, after, t]; |
|
11466 }), |
|
11467 |
|
11468 ET = function(opts) { |
|
11469 |
|
11470 var etState = this._yuievt, |
|
11471 etConfig; |
|
11472 |
|
11473 if (!etState) { |
|
11474 etState = this._yuievt = { |
|
11475 events: {}, // PERF: Not much point instantiating lazily. We're bound to have events |
|
11476 targets: null, // PERF: Instantiate lazily, if user actually adds target, |
|
11477 config: { |
|
11478 host: this, |
|
11479 context: this |
|
11480 }, |
|
11481 chain: Y.config.chain |
|
11482 }; |
|
11483 } |
|
11484 |
|
11485 etConfig = etState.config; |
|
11486 |
|
11487 if (opts) { |
|
11488 mixConfigs(etConfig, opts, true); |
|
11489 |
|
11490 if (opts.chain !== undefined) { |
|
11491 etState.chain = opts.chain; |
|
11492 } |
|
11493 |
|
11494 if (opts.prefix) { |
|
11495 etConfig.prefix = opts.prefix; |
|
11496 } |
|
11497 } |
|
11498 }; |
|
11499 |
|
11500 ET.prototype = { |
|
11501 |
|
11502 constructor: ET, |
|
11503 |
|
11504 /** |
|
11505 * Listen to a custom event hosted by this object one time. |
|
11506 * This is the equivalent to <code>on</code> except the |
|
11507 * listener is immediatelly detached when it is executed. |
|
11508 * @method once |
|
11509 * @param {String} type The name of the event |
|
11510 * @param {Function} fn The callback to execute in response to the event |
|
11511 * @param {Object} [context] Override `this` object in callback |
|
11512 * @param {Any} [arg*] 0..n additional arguments to supply to the subscriber |
|
11513 * @return {EventHandle} A subscription handle capable of detaching the |
|
11514 * subscription |
|
11515 */ |
|
11516 once: function() { |
|
11517 var handle = this.on.apply(this, arguments); |
|
11518 handle.batch(function(hand) { |
|
11519 if (hand.sub) { |
|
11520 hand.sub.once = true; |
|
11521 } |
|
11522 }); |
|
11523 return handle; |
|
11524 }, |
|
11525 |
|
11526 /** |
|
11527 * Listen to a custom event hosted by this object one time. |
|
11528 * This is the equivalent to <code>after</code> except the |
|
11529 * listener is immediatelly detached when it is executed. |
|
11530 * @method onceAfter |
|
11531 * @param {String} type The name of the event |
|
11532 * @param {Function} fn The callback to execute in response to the event |
|
11533 * @param {Object} [context] Override `this` object in callback |
|
11534 * @param {Any} [arg*] 0..n additional arguments to supply to the subscriber |
|
11535 * @return {EventHandle} A subscription handle capable of detaching that |
|
11536 * subscription |
|
11537 */ |
|
11538 onceAfter: function() { |
|
11539 var handle = this.after.apply(this, arguments); |
|
11540 handle.batch(function(hand) { |
|
11541 if (hand.sub) { |
|
11542 hand.sub.once = true; |
|
11543 } |
|
11544 }); |
|
11545 return handle; |
|
11546 }, |
|
11547 |
|
11548 /** |
|
11549 * Takes the type parameter passed to 'on' and parses out the |
|
11550 * various pieces that could be included in the type. If the |
|
11551 * event type is passed without a prefix, it will be expanded |
|
11552 * to include the prefix one is supplied or the event target |
|
11553 * is configured with a default prefix. |
|
11554 * @method parseType |
|
11555 * @param {String} type the type |
|
11556 * @param {String} [pre=this._yuievt.config.prefix] the prefix |
|
11557 * @since 3.3.0 |
|
11558 * @return {Array} an array containing: |
|
11559 * * the detach category, if supplied, |
|
11560 * * the prefixed event type, |
|
11561 * * whether or not this is an after listener, |
|
11562 * * the supplied event type |
|
11563 */ |
|
11564 parseType: function(type, pre) { |
|
11565 return _parseType(type, pre || this._yuievt.config.prefix); |
|
11566 }, |
|
11567 |
|
11568 /** |
|
11569 * Subscribe a callback function to a custom event fired by this object or |
|
11570 * from an object that bubbles its events to this object. |
|
11571 * |
|
11572 * Callback functions for events published with `emitFacade = true` will |
|
11573 * receive an `EventFacade` as the first argument (typically named "e"). |
|
11574 * These callbacks can then call `e.preventDefault()` to disable the |
|
11575 * behavior published to that event's `defaultFn`. See the `EventFacade` |
|
11576 * API for all available properties and methods. Subscribers to |
|
11577 * non-`emitFacade` events will receive the arguments passed to `fire()` |
|
11578 * after the event name. |
|
11579 * |
|
11580 * To subscribe to multiple events at once, pass an object as the first |
|
11581 * argument, where the key:value pairs correspond to the eventName:callback, |
|
11582 * or pass an array of event names as the first argument to subscribe to |
|
11583 * all listed events with the same callback. |
|
11584 * |
|
11585 * Returning `false` from a callback is supported as an alternative to |
|
11586 * calling `e.preventDefault(); e.stopPropagation();`. However, it is |
|
11587 * recommended to use the event methods whenever possible. |
|
11588 * |
|
11589 * @method on |
|
11590 * @param {String} type The name of the event |
|
11591 * @param {Function} fn The callback to execute in response to the event |
|
11592 * @param {Object} [context] Override `this` object in callback |
|
11593 * @param {Any} [arg*] 0..n additional arguments to supply to the subscriber |
|
11594 * @return {EventHandle} A subscription handle capable of detaching that |
|
11595 * subscription |
|
11596 */ |
|
11597 on: function(type, fn, context) { |
|
11598 |
|
11599 var yuievt = this._yuievt, |
|
11600 parts = _parseType(type, yuievt.config.prefix), f, c, args, ret, ce, |
|
11601 detachcategory, handle, store = Y.Env.evt.handles, after, adapt, shorttype, |
|
11602 Node = Y.Node, n, domevent, isArr; |
|
11603 |
|
11604 // full name, args, detachcategory, after |
|
11605 this._monitor('attach', parts[1], { |
|
11606 args: arguments, |
|
11607 category: parts[0], |
|
11608 after: parts[2] |
|
11609 }); |
|
11610 |
|
11611 if (L.isObject(type)) { |
|
11612 |
|
11613 if (L.isFunction(type)) { |
|
11614 return Y.Do.before.apply(Y.Do, arguments); |
|
11615 } |
|
11616 |
|
11617 f = fn; |
|
11618 c = context; |
|
11619 args = nativeSlice.call(arguments, 0); |
|
11620 ret = []; |
|
11621 |
|
11622 if (L.isArray(type)) { |
|
11623 isArr = true; |
|
11624 } |
|
11625 |
|
11626 after = type._after; |
|
11627 delete type._after; |
|
11628 |
|
11629 Y.each(type, function(v, k) { |
|
11630 |
|
11631 if (L.isObject(v)) { |
|
11632 f = v.fn || ((L.isFunction(v)) ? v : f); |
|
11633 c = v.context || c; |
|
11634 } |
|
11635 |
|
11636 var nv = (after) ? AFTER_PREFIX : ''; |
|
11637 |
|
11638 args[0] = nv + ((isArr) ? v : k); |
|
11639 args[1] = f; |
|
11640 args[2] = c; |
|
11641 |
|
11642 ret.push(this.on.apply(this, args)); |
|
11643 |
|
11644 }, this); |
|
11645 |
|
11646 return (yuievt.chain) ? this : new Y.EventHandle(ret); |
|
11647 } |
|
11648 |
|
11649 detachcategory = parts[0]; |
|
11650 after = parts[2]; |
|
11651 shorttype = parts[3]; |
|
11652 |
|
11653 // extra redirection so we catch adaptor events too. take a look at this. |
|
11654 if (Node && Y.instanceOf(this, Node) && (shorttype in Node.DOM_EVENTS)) { |
|
11655 args = nativeSlice.call(arguments, 0); |
|
11656 args.splice(2, 0, Node.getDOMNode(this)); |
|
11657 // Y.log("Node detected, redirecting with these args: " + args); |
|
11658 return Y.on.apply(Y, args); |
|
11659 } |
|
11660 |
|
11661 type = parts[1]; |
|
11662 |
|
11663 if (Y.instanceOf(this, YUI)) { |
|
11664 |
|
11665 adapt = Y.Env.evt.plugins[type]; |
|
11666 args = nativeSlice.call(arguments, 0); |
|
11667 args[0] = shorttype; |
|
11668 |
|
11669 if (Node) { |
|
11670 n = args[2]; |
|
11671 |
|
11672 if (Y.instanceOf(n, Y.NodeList)) { |
|
11673 n = Y.NodeList.getDOMNodes(n); |
|
11674 } else if (Y.instanceOf(n, Node)) { |
|
11675 n = Node.getDOMNode(n); |
|
11676 } |
|
11677 |
|
11678 domevent = (shorttype in Node.DOM_EVENTS); |
|
11679 |
|
11680 // Captures both DOM events and event plugins. |
|
11681 if (domevent) { |
|
11682 args[2] = n; |
|
11683 } |
|
11684 } |
|
11685 |
|
11686 // check for the existance of an event adaptor |
|
11687 if (adapt) { |
|
11688 Y.log('Using adaptor for ' + shorttype + ', ' + n, 'info', 'event'); |
|
11689 handle = adapt.on.apply(Y, args); |
|
11690 } else if ((!type) || domevent) { |
|
11691 handle = Y.Event._attach(args); |
|
11692 } |
|
11693 |
|
11694 } |
|
11695 |
|
11696 if (!handle) { |
|
11697 ce = yuievt.events[type] || this.publish(type); |
|
11698 handle = ce._on(fn, context, (arguments.length > 3) ? nativeSlice.call(arguments, 3) : null, (after) ? 'after' : true); |
|
11699 |
|
11700 // TODO: More robust regex, accounting for category |
|
11701 if (type.indexOf("*:") !== -1) { |
|
11702 this._hasSiblings = true; |
|
11703 } |
|
11704 } |
|
11705 |
|
11706 if (detachcategory) { |
|
11707 store[detachcategory] = store[detachcategory] || {}; |
|
11708 store[detachcategory][type] = store[detachcategory][type] || []; |
|
11709 store[detachcategory][type].push(handle); |
|
11710 } |
|
11711 |
|
11712 return (yuievt.chain) ? this : handle; |
|
11713 |
|
11714 }, |
|
11715 |
|
11716 /** |
|
11717 * subscribe to an event |
|
11718 * @method subscribe |
|
11719 * @deprecated use on |
|
11720 */ |
|
11721 subscribe: function() { |
|
11722 Y.log('EventTarget subscribe() is deprecated, use on()', 'warn', 'deprecated'); |
|
11723 return this.on.apply(this, arguments); |
|
11724 }, |
|
11725 |
|
11726 /** |
|
11727 * Detach one or more listeners the from the specified event |
|
11728 * @method detach |
|
11729 * @param type {string|Object} Either the handle to the subscriber or the |
|
11730 * type of event. If the type |
|
11731 * is not specified, it will attempt to remove |
|
11732 * the listener from all hosted events. |
|
11733 * @param fn {Function} The subscribed function to unsubscribe, if not |
|
11734 * supplied, all subscribers will be removed. |
|
11735 * @param context {Object} The custom object passed to subscribe. This is |
|
11736 * optional, but if supplied will be used to |
|
11737 * disambiguate multiple listeners that are the same |
|
11738 * (e.g., you subscribe many object using a function |
|
11739 * that lives on the prototype) |
|
11740 * @return {EventTarget} the host |
|
11741 */ |
|
11742 detach: function(type, fn, context) { |
|
11743 |
|
11744 var evts = this._yuievt.events, |
|
11745 i, |
|
11746 Node = Y.Node, |
|
11747 isNode = Node && (Y.instanceOf(this, Node)); |
|
11748 |
|
11749 // detachAll disabled on the Y instance. |
|
11750 if (!type && (this !== Y)) { |
|
11751 for (i in evts) { |
|
11752 if (evts.hasOwnProperty(i)) { |
|
11753 evts[i].detach(fn, context); |
|
11754 } |
|
11755 } |
|
11756 if (isNode) { |
|
11757 Y.Event.purgeElement(Node.getDOMNode(this)); |
|
11758 } |
|
11759 |
|
11760 return this; |
|
11761 } |
|
11762 |
|
11763 var parts = _parseType(type, this._yuievt.config.prefix), |
|
11764 detachcategory = L.isArray(parts) ? parts[0] : null, |
|
11765 shorttype = (parts) ? parts[3] : null, |
|
11766 adapt, store = Y.Env.evt.handles, detachhost, cat, args, |
|
11767 ce, |
|
11768 |
|
11769 keyDetacher = function(lcat, ltype, host) { |
|
11770 var handles = lcat[ltype], ce, i; |
|
11771 if (handles) { |
|
11772 for (i = handles.length - 1; i >= 0; --i) { |
|
11773 ce = handles[i].evt; |
|
11774 if (ce.host === host || ce.el === host) { |
|
11775 handles[i].detach(); |
|
11776 } |
|
11777 } |
|
11778 } |
|
11779 }; |
|
11780 |
|
11781 if (detachcategory) { |
|
11782 |
|
11783 cat = store[detachcategory]; |
|
11784 type = parts[1]; |
|
11785 detachhost = (isNode) ? Y.Node.getDOMNode(this) : this; |
|
11786 |
|
11787 if (cat) { |
|
11788 if (type) { |
|
11789 keyDetacher(cat, type, detachhost); |
|
11790 } else { |
|
11791 for (i in cat) { |
|
11792 if (cat.hasOwnProperty(i)) { |
|
11793 keyDetacher(cat, i, detachhost); |
|
11794 } |
|
11795 } |
|
11796 } |
|
11797 |
|
11798 return this; |
|
11799 } |
|
11800 |
|
11801 // If this is an event handle, use it to detach |
|
11802 } else if (L.isObject(type) && type.detach) { |
|
11803 type.detach(); |
|
11804 return this; |
|
11805 // extra redirection so we catch adaptor events too. take a look at this. |
|
11806 } else if (isNode && ((!shorttype) || (shorttype in Node.DOM_EVENTS))) { |
|
11807 args = nativeSlice.call(arguments, 0); |
|
11808 args[2] = Node.getDOMNode(this); |
|
11809 Y.detach.apply(Y, args); |
|
11810 return this; |
|
11811 } |
|
11812 |
|
11813 adapt = Y.Env.evt.plugins[shorttype]; |
|
11814 |
|
11815 // The YUI instance handles DOM events and adaptors |
|
11816 if (Y.instanceOf(this, YUI)) { |
|
11817 args = nativeSlice.call(arguments, 0); |
|
11818 // use the adaptor specific detach code if |
|
11819 if (adapt && adapt.detach) { |
|
11820 adapt.detach.apply(Y, args); |
|
11821 return this; |
|
11822 // DOM event fork |
|
11823 } else if (!type || (!adapt && Node && (type in Node.DOM_EVENTS))) { |
|
11824 args[0] = type; |
|
11825 Y.Event.detach.apply(Y.Event, args); |
|
11826 return this; |
|
11827 } |
|
11828 } |
|
11829 |
|
11830 // ce = evts[type]; |
|
11831 ce = evts[parts[1]]; |
|
11832 if (ce) { |
|
11833 ce.detach(fn, context); |
|
11834 } |
|
11835 |
|
11836 return this; |
|
11837 }, |
|
11838 |
|
11839 /** |
|
11840 * detach a listener |
|
11841 * @method unsubscribe |
|
11842 * @deprecated use detach |
|
11843 */ |
|
11844 unsubscribe: function() { |
|
11845 Y.log('EventTarget unsubscribe() is deprecated, use detach()', 'warn', 'deprecated'); |
|
11846 return this.detach.apply(this, arguments); |
|
11847 }, |
|
11848 |
|
11849 /** |
|
11850 * Removes all listeners from the specified event. If the event type |
|
11851 * is not specified, all listeners from all hosted custom events will |
|
11852 * be removed. |
|
11853 * @method detachAll |
|
11854 * @param type {String} The type, or name of the event |
|
11855 */ |
|
11856 detachAll: function(type) { |
|
11857 return this.detach(type); |
|
11858 }, |
|
11859 |
|
11860 /** |
|
11861 * Removes all listeners from the specified event. If the event type |
|
11862 * is not specified, all listeners from all hosted custom events will |
|
11863 * be removed. |
|
11864 * @method unsubscribeAll |
|
11865 * @param type {String} The type, or name of the event |
|
11866 * @deprecated use detachAll |
|
11867 */ |
|
11868 unsubscribeAll: function() { |
|
11869 Y.log('EventTarget unsubscribeAll() is deprecated, use detachAll()', 'warn', 'deprecated'); |
|
11870 return this.detachAll.apply(this, arguments); |
|
11871 }, |
|
11872 |
|
11873 /** |
|
11874 * Creates a new custom event of the specified type. If a custom event |
|
11875 * by that name already exists, it will not be re-created. In either |
|
11876 * case the custom event is returned. |
|
11877 * |
|
11878 * @method publish |
|
11879 * |
|
11880 * @param type {String} the type, or name of the event |
|
11881 * @param opts {object} optional config params. Valid properties are: |
|
11882 * |
|
11883 * <ul> |
|
11884 * <li> |
|
11885 * 'broadcast': whether or not the YUI instance and YUI global are notified when the event is fired (false) |
|
11886 * </li> |
|
11887 * <li> |
|
11888 * 'bubbles': whether or not this event bubbles (true) |
|
11889 * Events can only bubble if emitFacade is true. |
|
11890 * </li> |
|
11891 * <li> |
|
11892 * 'context': the default execution context for the listeners (this) |
|
11893 * </li> |
|
11894 * <li> |
|
11895 * 'defaultFn': the default function to execute when this event fires if preventDefault was not called |
|
11896 * </li> |
|
11897 * <li> |
|
11898 * 'emitFacade': whether or not this event emits a facade (false) |
|
11899 * </li> |
|
11900 * <li> |
|
11901 * 'prefix': the prefix for this targets events, e.g., 'menu' in 'menu:click' |
|
11902 * </li> |
|
11903 * <li> |
|
11904 * 'fireOnce': if an event is configured to fire once, new subscribers after |
|
11905 * the fire will be notified immediately. |
|
11906 * </li> |
|
11907 * <li> |
|
11908 * 'async': fireOnce event listeners will fire synchronously if the event has already |
|
11909 * fired unless async is true. |
|
11910 * </li> |
|
11911 * <li> |
|
11912 * 'preventable': whether or not preventDefault() has an effect (true) |
|
11913 * </li> |
|
11914 * <li> |
|
11915 * 'preventedFn': a function that is executed when preventDefault is called |
|
11916 * </li> |
|
11917 * <li> |
|
11918 * 'queuable': whether or not this event can be queued during bubbling (false) |
|
11919 * </li> |
|
11920 * <li> |
|
11921 * 'silent': if silent is true, debug messages are not provided for this event. |
|
11922 * </li> |
|
11923 * <li> |
|
11924 * 'stoppedFn': a function that is executed when stopPropagation is called |
|
11925 * </li> |
|
11926 * |
|
11927 * <li> |
|
11928 * 'monitored': specifies whether or not this event should send notifications about |
|
11929 * when the event has been attached, detached, or published. |
|
11930 * </li> |
|
11931 * <li> |
|
11932 * 'type': the event type (valid option if not provided as the first parameter to publish) |
|
11933 * </li> |
|
11934 * </ul> |
|
11935 * |
|
11936 * @return {CustomEvent} the custom event |
|
11937 * |
|
11938 */ |
|
11939 publish: function(type, opts) { |
|
11940 |
|
11941 var ret, |
|
11942 etState = this._yuievt, |
|
11943 etConfig = etState.config, |
|
11944 pre = etConfig.prefix; |
|
11945 |
|
11946 if (typeof type === "string") { |
|
11947 if (pre) { |
|
11948 type = _getType(type, pre); |
|
11949 } |
|
11950 ret = this._publish(type, etConfig, opts); |
|
11951 } else { |
|
11952 ret = {}; |
|
11953 |
|
11954 Y.each(type, function(v, k) { |
|
11955 if (pre) { |
|
11956 k = _getType(k, pre); |
|
11957 } |
|
11958 ret[k] = this._publish(k, etConfig, v || opts); |
|
11959 }, this); |
|
11960 |
|
11961 } |
|
11962 |
|
11963 return ret; |
|
11964 }, |
|
11965 |
|
11966 /** |
|
11967 * Returns the fully qualified type, given a short type string. |
|
11968 * That is, returns "foo:bar" when given "bar" if "foo" is the configured prefix. |
|
11969 * |
|
11970 * NOTE: This method, unlike _getType, does no checking of the value passed in, and |
|
11971 * is designed to be used with the low level _publish() method, for critical path |
|
11972 * implementations which need to fast-track publish for performance reasons. |
|
11973 * |
|
11974 * @method _getFullType |
|
11975 * @private |
|
11976 * @param {String} type The short type to prefix |
|
11977 * @return {String} The prefixed type, if a prefix is set, otherwise the type passed in |
|
11978 */ |
|
11979 _getFullType : function(type) { |
|
11980 |
|
11981 var pre = this._yuievt.config.prefix; |
|
11982 |
|
11983 if (pre) { |
|
11984 return pre + PREFIX_DELIMITER + type; |
|
11985 } else { |
|
11986 return type; |
|
11987 } |
|
11988 }, |
|
11989 |
|
11990 /** |
|
11991 * The low level event publish implementation. It expects all the massaging to have been done |
|
11992 * outside of this method. e.g. the `type` to `fullType` conversion. It's designed to be a fast |
|
11993 * path publish, which can be used by critical code paths to improve performance. |
|
11994 * |
|
11995 * @method _publish |
|
11996 * @private |
|
11997 * @param {String} fullType The prefixed type of the event to publish. |
|
11998 * @param {Object} etOpts The EventTarget specific configuration to mix into the published event. |
|
11999 * @param {Object} ceOpts The publish specific configuration to mix into the published event. |
|
12000 * @return {CustomEvent} The published event. If called without `etOpts` or `ceOpts`, this will |
|
12001 * be the default `CustomEvent` instance, and can be configured independently. |
|
12002 */ |
|
12003 _publish : function(fullType, etOpts, ceOpts) { |
|
12004 |
|
12005 var ce, |
|
12006 etState = this._yuievt, |
|
12007 etConfig = etState.config, |
|
12008 host = etConfig.host, |
|
12009 context = etConfig.context, |
|
12010 events = etState.events; |
|
12011 |
|
12012 ce = events[fullType]; |
|
12013 |
|
12014 // PERF: Hate to pull the check out of monitor, but trying to keep critical path tight. |
|
12015 if ((etConfig.monitored && !ce) || (ce && ce.monitored)) { |
|
12016 this._monitor('publish', fullType, { |
|
12017 args: arguments |
|
12018 }); |
|
12019 } |
|
12020 |
|
12021 if (!ce) { |
|
12022 // Publish event |
|
12023 ce = events[fullType] = new Y.CustomEvent(fullType, etOpts); |
|
12024 |
|
12025 if (!etOpts) { |
|
12026 ce.host = host; |
|
12027 ce.context = context; |
|
12028 } |
|
12029 } |
|
12030 |
|
12031 if (ceOpts) { |
|
12032 mixConfigs(ce, ceOpts, true); |
|
12033 } |
|
12034 |
|
12035 return ce; |
|
12036 }, |
|
12037 |
|
12038 /** |
|
12039 * This is the entry point for the event monitoring system. |
|
12040 * You can monitor 'attach', 'detach', 'fire', and 'publish'. |
|
12041 * When configured, these events generate an event. click -> |
|
12042 * click_attach, click_detach, click_publish -- these can |
|
12043 * be subscribed to like other events to monitor the event |
|
12044 * system. Inividual published events can have monitoring |
|
12045 * turned on or off (publish can't be turned off before it |
|
12046 * it published) by setting the events 'monitor' config. |
|
12047 * |
|
12048 * @method _monitor |
|
12049 * @param what {String} 'attach', 'detach', 'fire', or 'publish' |
|
12050 * @param eventType {String|CustomEvent} The prefixed name of the event being monitored, or the CustomEvent object. |
|
12051 * @param o {Object} Information about the event interaction, such as |
|
12052 * fire() args, subscription category, publish config |
|
12053 * @private |
|
12054 */ |
|
12055 _monitor: function(what, eventType, o) { |
|
12056 var monitorevt, ce, type; |
|
12057 |
|
12058 if (eventType) { |
|
12059 if (typeof eventType === "string") { |
|
12060 type = eventType; |
|
12061 ce = this.getEvent(eventType, true); |
|
12062 } else { |
|
12063 ce = eventType; |
|
12064 type = eventType.type; |
|
12065 } |
|
12066 |
|
12067 if ((this._yuievt.config.monitored && (!ce || ce.monitored)) || (ce && ce.monitored)) { |
|
12068 monitorevt = type + '_' + what; |
|
12069 o.monitored = what; |
|
12070 this.fire.call(this, monitorevt, o); |
|
12071 } |
|
12072 } |
|
12073 }, |
|
12074 |
|
12075 /** |
|
12076 * Fire a custom event by name. The callback functions will be executed |
|
12077 * from the context specified when the event was created, and with the |
|
12078 * following parameters. |
|
12079 * |
|
12080 * If the custom event object hasn't been created, then the event hasn't |
|
12081 * been published and it has no subscribers. For performance sake, we |
|
12082 * immediate exit in this case. This means the event won't bubble, so |
|
12083 * if the intention is that a bubble target be notified, the event must |
|
12084 * be published on this object first. |
|
12085 * |
|
12086 * The first argument is the event type, and any additional arguments are |
|
12087 * passed to the listeners as parameters. If the first of these is an |
|
12088 * object literal, and the event is configured to emit an event facade, |
|
12089 * that object is mixed into the event facade and the facade is provided |
|
12090 * in place of the original object. |
|
12091 * |
|
12092 * @method fire |
|
12093 * @param type {String|Object} The type of the event, or an object that contains |
|
12094 * a 'type' property. |
|
12095 * @param arguments {Object*} an arbitrary set of parameters to pass to |
|
12096 * the handler. If the first of these is an object literal and the event is |
|
12097 * configured to emit an event facade, the event facade will replace that |
|
12098 * parameter after the properties the object literal contains are copied to |
|
12099 * the event facade. |
|
12100 * @return {EventTarget} the event host |
|
12101 */ |
|
12102 fire: function(type) { |
|
12103 |
|
12104 var typeIncluded = (typeof type === "string"), |
|
12105 argCount = arguments.length, |
|
12106 t = type, |
|
12107 yuievt = this._yuievt, |
|
12108 etConfig = yuievt.config, |
|
12109 pre = etConfig.prefix, |
|
12110 ret, |
|
12111 ce, |
|
12112 ce2, |
|
12113 args; |
|
12114 |
|
12115 if (typeIncluded && argCount <= 3) { |
|
12116 |
|
12117 // PERF: Try to avoid slice/iteration for the common signatures |
|
12118 |
|
12119 // Most common |
|
12120 if (argCount === 2) { |
|
12121 args = [arguments[1]]; // fire("foo", {}) |
|
12122 } else if (argCount === 3) { |
|
12123 args = [arguments[1], arguments[2]]; // fire("foo", {}, opts) |
|
12124 } else { |
|
12125 args = []; // fire("foo") |
|
12126 } |
|
12127 |
|
12128 } else { |
|
12129 args = nativeSlice.call(arguments, ((typeIncluded) ? 1 : 0)); |
|
12130 } |
|
12131 |
|
12132 if (!typeIncluded) { |
|
12133 t = (type && type.type); |
|
12134 } |
|
12135 |
|
12136 if (pre) { |
|
12137 t = _getType(t, pre); |
|
12138 } |
|
12139 |
|
12140 ce = yuievt.events[t]; |
|
12141 |
|
12142 if (this._hasSiblings) { |
|
12143 ce2 = this.getSibling(t, ce); |
|
12144 |
|
12145 if (ce2 && !ce) { |
|
12146 ce = this.publish(t); |
|
12147 } |
|
12148 } |
|
12149 |
|
12150 // PERF: trying to avoid function call, since this is a critical path |
|
12151 if ((etConfig.monitored && (!ce || ce.monitored)) || (ce && ce.monitored)) { |
|
12152 this._monitor('fire', (ce || t), { |
|
12153 args: args |
|
12154 }); |
|
12155 } |
|
12156 |
|
12157 // this event has not been published or subscribed to |
|
12158 if (!ce) { |
|
12159 if (yuievt.hasTargets) { |
|
12160 return this.bubble({ type: t }, args, this); |
|
12161 } |
|
12162 |
|
12163 // otherwise there is nothing to be done |
|
12164 ret = true; |
|
12165 } else { |
|
12166 |
|
12167 if (ce2) { |
|
12168 ce.sibling = ce2; |
|
12169 } |
|
12170 |
|
12171 ret = ce._fire(args); |
|
12172 } |
|
12173 |
|
12174 return (yuievt.chain) ? this : ret; |
|
12175 }, |
|
12176 |
|
12177 getSibling: function(type, ce) { |
|
12178 var ce2; |
|
12179 |
|
12180 // delegate to *:type events if there are subscribers |
|
12181 if (type.indexOf(PREFIX_DELIMITER) > -1) { |
|
12182 type = _wildType(type); |
|
12183 ce2 = this.getEvent(type, true); |
|
12184 if (ce2) { |
|
12185 ce2.applyConfig(ce); |
|
12186 ce2.bubbles = false; |
|
12187 ce2.broadcast = 0; |
|
12188 } |
|
12189 } |
|
12190 |
|
12191 return ce2; |
|
12192 }, |
|
12193 |
|
12194 /** |
|
12195 * Returns the custom event of the provided type has been created, a |
|
12196 * falsy value otherwise |
|
12197 * @method getEvent |
|
12198 * @param type {String} the type, or name of the event |
|
12199 * @param prefixed {String} if true, the type is prefixed already |
|
12200 * @return {CustomEvent} the custom event or null |
|
12201 */ |
|
12202 getEvent: function(type, prefixed) { |
|
12203 var pre, e; |
|
12204 |
|
12205 if (!prefixed) { |
|
12206 pre = this._yuievt.config.prefix; |
|
12207 type = (pre) ? _getType(type, pre) : type; |
|
12208 } |
|
12209 e = this._yuievt.events; |
|
12210 return e[type] || null; |
|
12211 }, |
|
12212 |
|
12213 /** |
|
12214 * Subscribe to a custom event hosted by this object. The |
|
12215 * supplied callback will execute after any listeners add |
|
12216 * via the subscribe method, and after the default function, |
|
12217 * if configured for the event, has executed. |
|
12218 * |
|
12219 * @method after |
|
12220 * @param {String} type The name of the event |
|
12221 * @param {Function} fn The callback to execute in response to the event |
|
12222 * @param {Object} [context] Override `this` object in callback |
|
12223 * @param {Any} [arg*] 0..n additional arguments to supply to the subscriber |
|
12224 * @return {EventHandle} A subscription handle capable of detaching the |
|
12225 * subscription |
|
12226 */ |
|
12227 after: function(type, fn) { |
|
12228 |
|
12229 var a = nativeSlice.call(arguments, 0); |
|
12230 |
|
12231 switch (L.type(type)) { |
|
12232 case 'function': |
|
12233 return Y.Do.after.apply(Y.Do, arguments); |
|
12234 case 'array': |
|
12235 // YArray.each(a[0], function(v) { |
|
12236 // v = AFTER_PREFIX + v; |
|
12237 // }); |
|
12238 // break; |
|
12239 case 'object': |
|
12240 a[0]._after = true; |
|
12241 break; |
|
12242 default: |
|
12243 a[0] = AFTER_PREFIX + type; |
|
12244 } |
|
12245 |
|
12246 return this.on.apply(this, a); |
|
12247 |
|
12248 }, |
|
12249 |
|
12250 /** |
|
12251 * Executes the callback before a DOM event, custom event |
|
12252 * or method. If the first argument is a function, it |
|
12253 * is assumed the target is a method. For DOM and custom |
|
12254 * events, this is an alias for Y.on. |
|
12255 * |
|
12256 * For DOM and custom events: |
|
12257 * type, callback, context, 0-n arguments |
|
12258 * |
|
12259 * For methods: |
|
12260 * callback, object (method host), methodName, context, 0-n arguments |
|
12261 * |
|
12262 * @method before |
|
12263 * @return detach handle |
|
12264 */ |
|
12265 before: function() { |
|
12266 return this.on.apply(this, arguments); |
|
12267 } |
|
12268 |
|
12269 }; |
|
12270 |
|
12271 Y.EventTarget = ET; |
|
12272 |
|
12273 // make Y an event target |
|
12274 Y.mix(Y, ET.prototype); |
|
12275 ET.call(Y, { bubbles: false }); |
|
12276 |
|
12277 YUI.Env.globalEvents = YUI.Env.globalEvents || new ET(); |
|
12278 |
|
12279 /** |
|
12280 * Hosts YUI page level events. This is where events bubble to |
|
12281 * when the broadcast config is set to 2. This property is |
|
12282 * only available if the custom event module is loaded. |
|
12283 * @property Global |
|
12284 * @type EventTarget |
|
12285 * @for YUI |
|
12286 */ |
|
12287 Y.Global = YUI.Env.globalEvents; |
|
12288 |
|
12289 // @TODO implement a global namespace function on Y.Global? |
|
12290 |
|
12291 /** |
|
12292 `Y.on()` can do many things: |
|
12293 |
|
12294 <ul> |
|
12295 <li>Subscribe to custom events `publish`ed and `fire`d from Y</li> |
|
12296 <li>Subscribe to custom events `publish`ed with `broadcast` 1 or 2 and |
|
12297 `fire`d from any object in the YUI instance sandbox</li> |
|
12298 <li>Subscribe to DOM events</li> |
|
12299 <li>Subscribe to the execution of a method on any object, effectively |
|
12300 treating that method as an event</li> |
|
12301 </ul> |
|
12302 |
|
12303 For custom event subscriptions, pass the custom event name as the first argument |
|
12304 and callback as the second. The `this` object in the callback will be `Y` unless |
|
12305 an override is passed as the third argument. |
|
12306 |
|
12307 Y.on('io:complete', function () { |
|
12308 Y.MyApp.updateStatus('Transaction complete'); |
|
12309 }); |
|
12310 |
|
12311 To subscribe to DOM events, pass the name of a DOM event as the first argument |
|
12312 and a CSS selector string as the third argument after the callback function. |
|
12313 Alternately, the third argument can be a `Node`, `NodeList`, `HTMLElement`, |
|
12314 array, or simply omitted (the default is the `window` object). |
|
12315 |
|
12316 Y.on('click', function (e) { |
|
12317 e.preventDefault(); |
|
12318 |
|
12319 // proceed with ajax form submission |
|
12320 var url = this.get('action'); |
|
12321 ... |
|
12322 }, '#my-form'); |
|
12323 |
|
12324 The `this` object in DOM event callbacks will be the `Node` targeted by the CSS |
|
12325 selector or other identifier. |
|
12326 |
|
12327 `on()` subscribers for DOM events or custom events `publish`ed with a |
|
12328 `defaultFn` can prevent the default behavior with `e.preventDefault()` from the |
|
12329 event object passed as the first parameter to the subscription callback. |
|
12330 |
|
12331 To subscribe to the execution of an object method, pass arguments corresponding to the call signature for |
|
12332 <a href="../classes/Do.html#methods_before">`Y.Do.before(...)`</a>. |
|
12333 |
|
12334 NOTE: The formal parameter list below is for events, not for function |
|
12335 injection. See `Y.Do.before` for that signature. |
|
12336 |
|
12337 @method on |
|
12338 @param {String} type DOM or custom event name |
|
12339 @param {Function} fn The callback to execute in response to the event |
|
12340 @param {Object} [context] Override `this` object in callback |
|
12341 @param {Any} [arg*] 0..n additional arguments to supply to the subscriber |
|
12342 @return {EventHandle} A subscription handle capable of detaching the |
|
12343 subscription |
|
12344 @see Do.before |
|
12345 @for YUI |
|
12346 **/ |
|
12347 |
|
12348 /** |
|
12349 Listen for an event one time. Equivalent to `on()`, except that |
|
12350 the listener is immediately detached when executed. |
|
12351 |
|
12352 See the <a href="#methods_on">`on()` method</a> for additional subscription |
|
12353 options. |
|
12354 |
|
12355 @see on |
|
12356 @method once |
|
12357 @param {String} type DOM or custom event name |
|
12358 @param {Function} fn The callback to execute in response to the event |
|
12359 @param {Object} [context] Override `this` object in callback |
|
12360 @param {Any} [arg*] 0..n additional arguments to supply to the subscriber |
|
12361 @return {EventHandle} A subscription handle capable of detaching the |
|
12362 subscription |
|
12363 @for YUI |
|
12364 **/ |
|
12365 |
|
12366 /** |
|
12367 Listen for an event one time. Equivalent to `once()`, except, like `after()`, |
|
12368 the subscription callback executes after all `on()` subscribers and the event's |
|
12369 `defaultFn` (if configured) have executed. Like `after()` if any `on()` phase |
|
12370 subscriber calls `e.preventDefault()`, neither the `defaultFn` nor the `after()` |
|
12371 subscribers will execute. |
|
12372 |
|
12373 The listener is immediately detached when executed. |
|
12374 |
|
12375 See the <a href="#methods_on">`on()` method</a> for additional subscription |
|
12376 options. |
|
12377 |
|
12378 @see once |
|
12379 @method onceAfter |
|
12380 @param {String} type The custom event name |
|
12381 @param {Function} fn The callback to execute in response to the event |
|
12382 @param {Object} [context] Override `this` object in callback |
|
12383 @param {Any} [arg*] 0..n additional arguments to supply to the subscriber |
|
12384 @return {EventHandle} A subscription handle capable of detaching the |
|
12385 subscription |
|
12386 @for YUI |
|
12387 **/ |
|
12388 |
|
12389 /** |
|
12390 Like `on()`, this method creates a subscription to a custom event or to the |
|
12391 execution of a method on an object. |
|
12392 |
|
12393 For events, `after()` subscribers are executed after the event's |
|
12394 `defaultFn` unless `e.preventDefault()` was called from an `on()` subscriber. |
|
12395 |
|
12396 See the <a href="#methods_on">`on()` method</a> for additional subscription |
|
12397 options. |
|
12398 |
|
12399 NOTE: The subscription signature shown is for events, not for function |
|
12400 injection. See <a href="../classes/Do.html#methods_after">`Y.Do.after`</a> |
|
12401 for that signature. |
|
12402 |
|
12403 @see on |
|
12404 @see Do.after |
|
12405 @method after |
|
12406 @param {String} type The custom event name |
|
12407 @param {Function} fn The callback to execute in response to the event |
|
12408 @param {Object} [context] Override `this` object in callback |
|
12409 @param {Any} [args*] 0..n additional arguments to supply to the subscriber |
|
12410 @return {EventHandle} A subscription handle capable of detaching the |
|
12411 subscription |
|
12412 @for YUI |
|
12413 **/ |
|
12414 |
|
12415 |
|
12416 }, '@VERSION@', {"requires": ["oop"]}); |
|
12417 YUI.add('event-custom-complex', function (Y, NAME) { |
|
12418 |
|
12419 |
|
12420 /** |
|
12421 * Adds event facades, preventable default behavior, and bubbling. |
|
12422 * events. |
|
12423 * @module event-custom |
|
12424 * @submodule event-custom-complex |
|
12425 */ |
|
12426 |
|
12427 var FACADE, |
|
12428 FACADE_KEYS, |
|
12429 YObject = Y.Object, |
|
12430 key, |
|
12431 EMPTY = {}, |
|
12432 CEProto = Y.CustomEvent.prototype, |
|
12433 ETProto = Y.EventTarget.prototype, |
|
12434 |
|
12435 mixFacadeProps = function(facade, payload) { |
|
12436 var p; |
|
12437 |
|
12438 for (p in payload) { |
|
12439 if (!(FACADE_KEYS.hasOwnProperty(p))) { |
|
12440 facade[p] = payload[p]; |
|
12441 } |
|
12442 } |
|
12443 }; |
|
12444 |
|
12445 /** |
|
12446 * Wraps and protects a custom event for use when emitFacade is set to true. |
|
12447 * Requires the event-custom-complex module |
|
12448 * @class EventFacade |
|
12449 * @param e {Event} the custom event |
|
12450 * @param currentTarget {HTMLElement} the element the listener was attached to |
|
12451 */ |
|
12452 |
|
12453 Y.EventFacade = function(e, currentTarget) { |
|
12454 |
|
12455 if (!e) { |
|
12456 e = EMPTY; |
|
12457 } |
|
12458 |
|
12459 this._event = e; |
|
12460 |
|
12461 /** |
|
12462 * The arguments passed to fire |
|
12463 * @property details |
|
12464 * @type Array |
|
12465 */ |
|
12466 this.details = e.details; |
|
12467 |
|
12468 /** |
|
12469 * The event type, this can be overridden by the fire() payload |
|
12470 * @property type |
|
12471 * @type string |
|
12472 */ |
|
12473 this.type = e.type; |
|
12474 |
|
12475 /** |
|
12476 * The real event type |
|
12477 * @property _type |
|
12478 * @type string |
|
12479 * @private |
|
12480 */ |
|
12481 this._type = e.type; |
|
12482 |
|
12483 ////////////////////////////////////////////////////// |
|
12484 |
|
12485 /** |
|
12486 * Node reference for the targeted eventtarget |
|
12487 * @property target |
|
12488 * @type Node |
|
12489 */ |
|
12490 this.target = e.target; |
|
12491 |
|
12492 /** |
|
12493 * Node reference for the element that the listener was attached to. |
|
12494 * @property currentTarget |
|
12495 * @type Node |
|
12496 */ |
|
12497 this.currentTarget = currentTarget; |
|
12498 |
|
12499 /** |
|
12500 * Node reference to the relatedTarget |
|
12501 * @property relatedTarget |
|
12502 * @type Node |
|
12503 */ |
|
12504 this.relatedTarget = e.relatedTarget; |
|
12505 |
|
12506 }; |
|
12507 |
|
12508 Y.mix(Y.EventFacade.prototype, { |
|
12509 |
|
12510 /** |
|
12511 * Stops the propagation to the next bubble target |
|
12512 * @method stopPropagation |
|
12513 */ |
|
12514 stopPropagation: function() { |
|
12515 this._event.stopPropagation(); |
|
12516 this.stopped = 1; |
|
12517 }, |
|
12518 |
|
12519 /** |
|
12520 * Stops the propagation to the next bubble target and |
|
12521 * prevents any additional listeners from being exectued |
|
12522 * on the current target. |
|
12523 * @method stopImmediatePropagation |
|
12524 */ |
|
12525 stopImmediatePropagation: function() { |
|
12526 this._event.stopImmediatePropagation(); |
|
12527 this.stopped = 2; |
|
12528 }, |
|
12529 |
|
12530 /** |
|
12531 * Prevents the event's default behavior |
|
12532 * @method preventDefault |
|
12533 */ |
|
12534 preventDefault: function() { |
|
12535 this._event.preventDefault(); |
|
12536 this.prevented = 1; |
|
12537 }, |
|
12538 |
|
12539 /** |
|
12540 * Stops the event propagation and prevents the default |
|
12541 * event behavior. |
|
12542 * @method halt |
|
12543 * @param immediate {boolean} if true additional listeners |
|
12544 * on the current target will not be executed |
|
12545 */ |
|
12546 halt: function(immediate) { |
|
12547 this._event.halt(immediate); |
|
12548 this.prevented = 1; |
|
12549 this.stopped = (immediate) ? 2 : 1; |
|
12550 } |
|
12551 |
|
12552 }); |
|
12553 |
|
12554 CEProto.fireComplex = function(args) { |
|
12555 |
|
12556 var es, |
|
12557 ef, |
|
12558 q, |
|
12559 queue, |
|
12560 ce, |
|
12561 ret = true, |
|
12562 events, |
|
12563 subs, |
|
12564 ons, |
|
12565 afters, |
|
12566 afterQueue, |
|
12567 postponed, |
|
12568 prevented, |
|
12569 preventedFn, |
|
12570 defaultFn, |
|
12571 self = this, |
|
12572 host = self.host || self, |
|
12573 next, |
|
12574 oldbubble, |
|
12575 stack, |
|
12576 yuievt = host._yuievt, |
|
12577 hasPotentialSubscribers; |
|
12578 |
|
12579 stack = self.stack; |
|
12580 |
|
12581 if (stack) { |
|
12582 |
|
12583 // queue this event if the current item in the queue bubbles |
|
12584 if (self.queuable && self.type !== stack.next.type) { |
|
12585 self.log('queue ' + self.type); |
|
12586 |
|
12587 if (!stack.queue) { |
|
12588 stack.queue = []; |
|
12589 } |
|
12590 stack.queue.push([self, args]); |
|
12591 |
|
12592 return true; |
|
12593 } |
|
12594 } |
|
12595 |
|
12596 hasPotentialSubscribers = self.hasSubs() || yuievt.hasTargets || self.broadcast; |
|
12597 |
|
12598 self.target = self.target || host; |
|
12599 self.currentTarget = host; |
|
12600 |
|
12601 self.details = args.concat(); |
|
12602 |
|
12603 if (hasPotentialSubscribers) { |
|
12604 |
|
12605 es = stack || { |
|
12606 |
|
12607 id: self.id, // id of the first event in the stack |
|
12608 next: self, |
|
12609 silent: self.silent, |
|
12610 stopped: 0, |
|
12611 prevented: 0, |
|
12612 bubbling: null, |
|
12613 type: self.type, |
|
12614 // defaultFnQueue: new Y.Queue(), |
|
12615 defaultTargetOnly: self.defaultTargetOnly |
|
12616 |
|
12617 }; |
|
12618 |
|
12619 subs = self.getSubs(); |
|
12620 ons = subs[0]; |
|
12621 afters = subs[1]; |
|
12622 |
|
12623 self.stopped = (self.type !== es.type) ? 0 : es.stopped; |
|
12624 self.prevented = (self.type !== es.type) ? 0 : es.prevented; |
|
12625 |
|
12626 if (self.stoppedFn) { |
|
12627 // PERF TODO: Can we replace with callback, like preventedFn. Look into history |
|
12628 events = new Y.EventTarget({ |
|
12629 fireOnce: true, |
|
12630 context: host |
|
12631 }); |
|
12632 self.events = events; |
|
12633 events.on('stopped', self.stoppedFn); |
|
12634 } |
|
12635 |
|
12636 // self.log("Firing " + self + ", " + "args: " + args); |
|
12637 self.log("Firing " + self.type); |
|
12638 |
|
12639 self._facade = null; // kill facade to eliminate stale properties |
|
12640 |
|
12641 ef = self._getFacade(args); |
|
12642 |
|
12643 if (ons) { |
|
12644 self._procSubs(ons, args, ef); |
|
12645 } |
|
12646 |
|
12647 // bubble if this is hosted in an event target and propagation has not been stopped |
|
12648 if (self.bubbles && host.bubble && !self.stopped) { |
|
12649 oldbubble = es.bubbling; |
|
12650 |
|
12651 es.bubbling = self.type; |
|
12652 |
|
12653 if (es.type !== self.type) { |
|
12654 es.stopped = 0; |
|
12655 es.prevented = 0; |
|
12656 } |
|
12657 |
|
12658 ret = host.bubble(self, args, null, es); |
|
12659 |
|
12660 self.stopped = Math.max(self.stopped, es.stopped); |
|
12661 self.prevented = Math.max(self.prevented, es.prevented); |
|
12662 |
|
12663 es.bubbling = oldbubble; |
|
12664 } |
|
12665 |
|
12666 prevented = self.prevented; |
|
12667 |
|
12668 if (prevented) { |
|
12669 preventedFn = self.preventedFn; |
|
12670 if (preventedFn) { |
|
12671 preventedFn.apply(host, args); |
|
12672 } |
|
12673 } else { |
|
12674 defaultFn = self.defaultFn; |
|
12675 |
|
12676 if (defaultFn && ((!self.defaultTargetOnly && !es.defaultTargetOnly) || host === ef.target)) { |
|
12677 defaultFn.apply(host, args); |
|
12678 } |
|
12679 } |
|
12680 |
|
12681 // broadcast listeners are fired as discreet events on the |
|
12682 // YUI instance and potentially the YUI global. |
|
12683 if (self.broadcast) { |
|
12684 self._broadcast(args); |
|
12685 } |
|
12686 |
|
12687 if (afters && !self.prevented && self.stopped < 2) { |
|
12688 |
|
12689 // Queue the after |
|
12690 afterQueue = es.afterQueue; |
|
12691 |
|
12692 if (es.id === self.id || self.type !== yuievt.bubbling) { |
|
12693 |
|
12694 self._procSubs(afters, args, ef); |
|
12695 |
|
12696 if (afterQueue) { |
|
12697 while ((next = afterQueue.last())) { |
|
12698 next(); |
|
12699 } |
|
12700 } |
|
12701 } else { |
|
12702 postponed = afters; |
|
12703 |
|
12704 if (es.execDefaultCnt) { |
|
12705 postponed = Y.merge(postponed); |
|
12706 |
|
12707 Y.each(postponed, function(s) { |
|
12708 s.postponed = true; |
|
12709 }); |
|
12710 } |
|
12711 |
|
12712 if (!afterQueue) { |
|
12713 es.afterQueue = new Y.Queue(); |
|
12714 } |
|
12715 |
|
12716 es.afterQueue.add(function() { |
|
12717 self._procSubs(postponed, args, ef); |
|
12718 }); |
|
12719 } |
|
12720 |
|
12721 } |
|
12722 |
|
12723 self.target = null; |
|
12724 |
|
12725 if (es.id === self.id) { |
|
12726 |
|
12727 queue = es.queue; |
|
12728 |
|
12729 if (queue) { |
|
12730 while (queue.length) { |
|
12731 q = queue.pop(); |
|
12732 ce = q[0]; |
|
12733 // set up stack to allow the next item to be processed |
|
12734 es.next = ce; |
|
12735 ce._fire(q[1]); |
|
12736 } |
|
12737 } |
|
12738 |
|
12739 self.stack = null; |
|
12740 } |
|
12741 |
|
12742 ret = !(self.stopped); |
|
12743 |
|
12744 if (self.type !== yuievt.bubbling) { |
|
12745 es.stopped = 0; |
|
12746 es.prevented = 0; |
|
12747 self.stopped = 0; |
|
12748 self.prevented = 0; |
|
12749 } |
|
12750 |
|
12751 } else { |
|
12752 defaultFn = self.defaultFn; |
|
12753 |
|
12754 if(defaultFn) { |
|
12755 ef = self._getFacade(args); |
|
12756 |
|
12757 if ((!self.defaultTargetOnly) || (host === ef.target)) { |
|
12758 defaultFn.apply(host, args); |
|
12759 } |
|
12760 } |
|
12761 } |
|
12762 |
|
12763 // Kill the cached facade to free up memory. |
|
12764 // Otherwise we have the facade from the last fire, sitting around forever. |
|
12765 self._facade = null; |
|
12766 |
|
12767 return ret; |
|
12768 }; |
|
12769 |
|
12770 CEProto._getFacade = function(fireArgs) { |
|
12771 |
|
12772 var userArgs = this.details, |
|
12773 firstArg = userArgs && userArgs[0], |
|
12774 firstArgIsObj = (firstArg && (typeof firstArg === "object")), |
|
12775 ef = this._facade; |
|
12776 |
|
12777 if (!ef) { |
|
12778 ef = new Y.EventFacade(this, this.currentTarget); |
|
12779 } |
|
12780 |
|
12781 if (firstArgIsObj) { |
|
12782 // protect the event facade properties |
|
12783 mixFacadeProps(ef, firstArg); |
|
12784 |
|
12785 // Allow the event type to be faked http://yuilibrary.com/projects/yui3/ticket/2528376 |
|
12786 if (firstArg.type) { |
|
12787 ef.type = firstArg.type; |
|
12788 } |
|
12789 |
|
12790 if (fireArgs) { |
|
12791 fireArgs[0] = ef; |
|
12792 } |
|
12793 } else { |
|
12794 if (fireArgs) { |
|
12795 fireArgs.unshift(ef); |
|
12796 } |
|
12797 } |
|
12798 |
|
12799 // update the details field with the arguments |
|
12800 ef.details = this.details; |
|
12801 |
|
12802 // use the original target when the event bubbled to this target |
|
12803 ef.target = this.originalTarget || this.target; |
|
12804 |
|
12805 ef.currentTarget = this.currentTarget; |
|
12806 ef.stopped = 0; |
|
12807 ef.prevented = 0; |
|
12808 |
|
12809 this._facade = ef; |
|
12810 |
|
12811 return this._facade; |
|
12812 }; |
|
12813 |
|
12814 /** |
|
12815 * Stop propagation to bubble targets |
|
12816 * @for CustomEvent |
|
12817 * @method stopPropagation |
|
12818 */ |
|
12819 CEProto.stopPropagation = function() { |
|
12820 this.stopped = 1; |
|
12821 if (this.stack) { |
|
12822 this.stack.stopped = 1; |
|
12823 } |
|
12824 if (this.events) { |
|
12825 this.events.fire('stopped', this); |
|
12826 } |
|
12827 }; |
|
12828 |
|
12829 /** |
|
12830 * Stops propagation to bubble targets, and prevents any remaining |
|
12831 * subscribers on the current target from executing. |
|
12832 * @method stopImmediatePropagation |
|
12833 */ |
|
12834 CEProto.stopImmediatePropagation = function() { |
|
12835 this.stopped = 2; |
|
12836 if (this.stack) { |
|
12837 this.stack.stopped = 2; |
|
12838 } |
|
12839 if (this.events) { |
|
12840 this.events.fire('stopped', this); |
|
12841 } |
|
12842 }; |
|
12843 |
|
12844 /** |
|
12845 * Prevents the execution of this event's defaultFn |
|
12846 * @method preventDefault |
|
12847 */ |
|
12848 CEProto.preventDefault = function() { |
|
12849 if (this.preventable) { |
|
12850 this.prevented = 1; |
|
12851 if (this.stack) { |
|
12852 this.stack.prevented = 1; |
|
12853 } |
|
12854 } |
|
12855 }; |
|
12856 |
|
12857 /** |
|
12858 * Stops the event propagation and prevents the default |
|
12859 * event behavior. |
|
12860 * @method halt |
|
12861 * @param immediate {boolean} if true additional listeners |
|
12862 * on the current target will not be executed |
|
12863 */ |
|
12864 CEProto.halt = function(immediate) { |
|
12865 if (immediate) { |
|
12866 this.stopImmediatePropagation(); |
|
12867 } else { |
|
12868 this.stopPropagation(); |
|
12869 } |
|
12870 this.preventDefault(); |
|
12871 }; |
|
12872 |
|
12873 /** |
|
12874 * Registers another EventTarget as a bubble target. Bubble order |
|
12875 * is determined by the order registered. Multiple targets can |
|
12876 * be specified. |
|
12877 * |
|
12878 * Events can only bubble if emitFacade is true. |
|
12879 * |
|
12880 * Included in the event-custom-complex submodule. |
|
12881 * |
|
12882 * @method addTarget |
|
12883 * @param o {EventTarget} the target to add |
|
12884 * @for EventTarget |
|
12885 */ |
|
12886 ETProto.addTarget = function(o) { |
|
12887 var etState = this._yuievt; |
|
12888 |
|
12889 if (!etState.targets) { |
|
12890 etState.targets = {}; |
|
12891 } |
|
12892 |
|
12893 etState.targets[Y.stamp(o)] = o; |
|
12894 etState.hasTargets = true; |
|
12895 }; |
|
12896 |
|
12897 /** |
|
12898 * Returns an array of bubble targets for this object. |
|
12899 * @method getTargets |
|
12900 * @return EventTarget[] |
|
12901 */ |
|
12902 ETProto.getTargets = function() { |
|
12903 var targets = this._yuievt.targets; |
|
12904 return targets ? YObject.values(targets) : []; |
|
12905 }; |
|
12906 |
|
12907 /** |
|
12908 * Removes a bubble target |
|
12909 * @method removeTarget |
|
12910 * @param o {EventTarget} the target to remove |
|
12911 * @for EventTarget |
|
12912 */ |
|
12913 ETProto.removeTarget = function(o) { |
|
12914 var targets = this._yuievt.targets; |
|
12915 |
|
12916 if (targets) { |
|
12917 delete targets[Y.stamp(o, true)]; |
|
12918 |
|
12919 if (YObject.size(targets) === 0) { |
|
12920 this._yuievt.hasTargets = false; |
|
12921 } |
|
12922 } |
|
12923 }; |
|
12924 |
|
12925 /** |
|
12926 * Propagate an event. Requires the event-custom-complex module. |
|
12927 * @method bubble |
|
12928 * @param evt {CustomEvent} the custom event to propagate |
|
12929 * @return {boolean} the aggregated return value from Event.Custom.fire |
|
12930 * @for EventTarget |
|
12931 */ |
|
12932 ETProto.bubble = function(evt, args, target, es) { |
|
12933 |
|
12934 var targs = this._yuievt.targets, |
|
12935 ret = true, |
|
12936 t, |
|
12937 ce, |
|
12938 i, |
|
12939 bc, |
|
12940 ce2, |
|
12941 type = evt && evt.type, |
|
12942 originalTarget = target || (evt && evt.target) || this, |
|
12943 oldbubble; |
|
12944 |
|
12945 if (!evt || ((!evt.stopped) && targs)) { |
|
12946 |
|
12947 for (i in targs) { |
|
12948 if (targs.hasOwnProperty(i)) { |
|
12949 |
|
12950 t = targs[i]; |
|
12951 |
|
12952 ce = t._yuievt.events[type]; |
|
12953 |
|
12954 if (t._hasSiblings) { |
|
12955 ce2 = t.getSibling(type, ce); |
|
12956 } |
|
12957 |
|
12958 if (ce2 && !ce) { |
|
12959 ce = t.publish(type); |
|
12960 } |
|
12961 |
|
12962 oldbubble = t._yuievt.bubbling; |
|
12963 t._yuievt.bubbling = type; |
|
12964 |
|
12965 // if this event was not published on the bubble target, |
|
12966 // continue propagating the event. |
|
12967 if (!ce) { |
|
12968 if (t._yuievt.hasTargets) { |
|
12969 t.bubble(evt, args, originalTarget, es); |
|
12970 } |
|
12971 } else { |
|
12972 |
|
12973 if (ce2) { |
|
12974 ce.sibling = ce2; |
|
12975 } |
|
12976 |
|
12977 // set the original target to that the target payload on the facade is correct. |
|
12978 ce.target = originalTarget; |
|
12979 ce.originalTarget = originalTarget; |
|
12980 ce.currentTarget = t; |
|
12981 bc = ce.broadcast; |
|
12982 ce.broadcast = false; |
|
12983 |
|
12984 // default publish may not have emitFacade true -- that |
|
12985 // shouldn't be what the implementer meant to do |
|
12986 ce.emitFacade = true; |
|
12987 |
|
12988 ce.stack = es; |
|
12989 |
|
12990 // TODO: See what's getting in the way of changing this to use |
|
12991 // the more performant ce._fire(args || evt.details || []). |
|
12992 |
|
12993 // Something in Widget Parent/Child tests is not happy if we |
|
12994 // change it - maybe evt.details related? |
|
12995 ret = ret && ce.fire.apply(ce, args || evt.details || []); |
|
12996 |
|
12997 ce.broadcast = bc; |
|
12998 ce.originalTarget = null; |
|
12999 |
|
13000 // stopPropagation() was called |
|
13001 if (ce.stopped) { |
|
13002 break; |
|
13003 } |
|
13004 } |
|
13005 |
|
13006 t._yuievt.bubbling = oldbubble; |
|
13007 } |
|
13008 } |
|
13009 } |
|
13010 |
|
13011 return ret; |
|
13012 }; |
|
13013 |
|
13014 FACADE = new Y.EventFacade(); |
|
13015 FACADE_KEYS = {}; |
|
13016 |
|
13017 // Flatten whitelist |
|
13018 for (key in FACADE) { |
|
13019 FACADE_KEYS[key] = true; |
|
13020 } |
|
13021 |
|
13022 |
|
13023 }, '@VERSION@', {"requires": ["event-custom-base"]}); |
|
13024 YUI.add('node-core', function (Y, NAME) { |
|
13025 |
|
13026 /** |
|
13027 * The Node Utility provides a DOM-like interface for interacting with DOM nodes. |
|
13028 * @module node |
|
13029 * @main node |
|
13030 * @submodule node-core |
|
13031 */ |
|
13032 |
|
13033 /** |
|
13034 * The Node class provides a wrapper for manipulating DOM Nodes. |
|
13035 * Node properties can be accessed via the set/get methods. |
|
13036 * Use `Y.one()` to retrieve Node instances. |
|
13037 * |
|
13038 * <strong>NOTE:</strong> Node properties are accessed using |
|
13039 * the <code>set</code> and <code>get</code> methods. |
|
13040 * |
|
13041 * @class Node |
|
13042 * @constructor |
|
13043 * @param {DOMNode} node the DOM node to be mapped to the Node instance. |
|
13044 * @uses EventTarget |
|
13045 */ |
|
13046 |
|
13047 // "globals" |
|
13048 var DOT = '.', |
|
13049 NODE_NAME = 'nodeName', |
|
13050 NODE_TYPE = 'nodeType', |
|
13051 OWNER_DOCUMENT = 'ownerDocument', |
|
13052 TAG_NAME = 'tagName', |
|
13053 UID = '_yuid', |
|
13054 EMPTY_OBJ = {}, |
|
13055 |
|
13056 _slice = Array.prototype.slice, |
|
13057 |
|
13058 Y_DOM = Y.DOM, |
|
13059 |
|
13060 Y_Node = function(node) { |
|
13061 if (!this.getDOMNode) { // support optional "new" |
|
13062 return new Y_Node(node); |
|
13063 } |
|
13064 |
|
13065 if (typeof node == 'string') { |
|
13066 node = Y_Node._fromString(node); |
|
13067 if (!node) { |
|
13068 return null; // NOTE: return |
|
13069 } |
|
13070 } |
|
13071 |
|
13072 var uid = (node.nodeType !== 9) ? node.uniqueID : node[UID]; |
|
13073 |
|
13074 if (uid && Y_Node._instances[uid] && Y_Node._instances[uid]._node !== node) { |
|
13075 node[UID] = null; // unset existing uid to prevent collision (via clone or hack) |
|
13076 } |
|
13077 |
|
13078 uid = uid || Y.stamp(node); |
|
13079 if (!uid) { // stamp failed; likely IE non-HTMLElement |
|
13080 uid = Y.guid(); |
|
13081 } |
|
13082 |
|
13083 this[UID] = uid; |
|
13084 |
|
13085 /** |
|
13086 * The underlying DOM node bound to the Y.Node instance |
|
13087 * @property _node |
|
13088 * @type DOMNode |
|
13089 * @private |
|
13090 */ |
|
13091 this._node = node; |
|
13092 |
|
13093 this._stateProxy = node; // when augmented with Attribute |
|
13094 |
|
13095 if (this._initPlugins) { // when augmented with Plugin.Host |
|
13096 this._initPlugins(); |
|
13097 } |
|
13098 }, |
|
13099 |
|
13100 // used with previous/next/ancestor tests |
|
13101 _wrapFn = function(fn) { |
|
13102 var ret = null; |
|
13103 if (fn) { |
|
13104 ret = (typeof fn == 'string') ? |
|
13105 function(n) { |
|
13106 return Y.Selector.test(n, fn); |
|
13107 } : |
|
13108 function(n) { |
|
13109 return fn(Y.one(n)); |
|
13110 }; |
|
13111 } |
|
13112 |
|
13113 return ret; |
|
13114 }; |
|
13115 // end "globals" |
|
13116 |
|
13117 Y_Node.ATTRS = {}; |
|
13118 Y_Node.DOM_EVENTS = {}; |
|
13119 |
|
13120 Y_Node._fromString = function(node) { |
|
13121 if (node) { |
|
13122 if (node.indexOf('doc') === 0) { // doc OR document |
|
13123 node = Y.config.doc; |
|
13124 } else if (node.indexOf('win') === 0) { // win OR window |
|
13125 node = Y.config.win; |
|
13126 } else { |
|
13127 node = Y.Selector.query(node, null, true); |
|
13128 } |
|
13129 } |
|
13130 |
|
13131 return node || null; |
|
13132 }; |
|
13133 |
|
13134 /** |
|
13135 * The name of the component |
|
13136 * @static |
|
13137 * @type String |
|
13138 * @property NAME |
|
13139 */ |
|
13140 Y_Node.NAME = 'node'; |
|
13141 |
|
13142 /* |
|
13143 * The pattern used to identify ARIA attributes |
|
13144 */ |
|
13145 Y_Node.re_aria = /^(?:role$|aria-)/; |
|
13146 |
|
13147 Y_Node.SHOW_TRANSITION = 'fadeIn'; |
|
13148 Y_Node.HIDE_TRANSITION = 'fadeOut'; |
|
13149 |
|
13150 /** |
|
13151 * A list of Node instances that have been created |
|
13152 * @private |
|
13153 * @type Object |
|
13154 * @property _instances |
|
13155 * @static |
|
13156 * |
|
13157 */ |
|
13158 Y_Node._instances = {}; |
|
13159 |
|
13160 /** |
|
13161 * Retrieves the DOM node bound to a Node instance |
|
13162 * @method getDOMNode |
|
13163 * @static |
|
13164 * |
|
13165 * @param {Node | HTMLNode} node The Node instance or an HTMLNode |
|
13166 * @return {HTMLNode} The DOM node bound to the Node instance. If a DOM node is passed |
|
13167 * as the node argument, it is simply returned. |
|
13168 */ |
|
13169 Y_Node.getDOMNode = function(node) { |
|
13170 if (node) { |
|
13171 return (node.nodeType) ? node : node._node || null; |
|
13172 } |
|
13173 return null; |
|
13174 }; |
|
13175 |
|
13176 /** |
|
13177 * Checks Node return values and wraps DOM Nodes as Y.Node instances |
|
13178 * and DOM Collections / Arrays as Y.NodeList instances. |
|
13179 * Other return values just pass thru. If undefined is returned (e.g. no return) |
|
13180 * then the Node instance is returned for chainability. |
|
13181 * @method scrubVal |
|
13182 * @static |
|
13183 * |
|
13184 * @param {any} node The Node instance or an HTMLNode |
|
13185 * @return {Node | NodeList | Any} Depends on what is returned from the DOM node. |
|
13186 */ |
|
13187 Y_Node.scrubVal = function(val, node) { |
|
13188 if (val) { // only truthy values are risky |
|
13189 if (typeof val == 'object' || typeof val == 'function') { // safari nodeList === function |
|
13190 if (NODE_TYPE in val || Y_DOM.isWindow(val)) {// node || window |
|
13191 val = Y.one(val); |
|
13192 } else if ((val.item && !val._nodes) || // dom collection or Node instance |
|
13193 (val[0] && val[0][NODE_TYPE])) { // array of DOM Nodes |
|
13194 val = Y.all(val); |
|
13195 } |
|
13196 } |
|
13197 } else if (typeof val === 'undefined') { |
|
13198 val = node; // for chaining |
|
13199 } else if (val === null) { |
|
13200 val = null; // IE: DOM null not the same as null |
|
13201 } |
|
13202 |
|
13203 return val; |
|
13204 }; |
|
13205 |
|
13206 /** |
|
13207 * Adds methods to the Y.Node prototype, routing through scrubVal. |
|
13208 * @method addMethod |
|
13209 * @static |
|
13210 * |
|
13211 * @param {String} name The name of the method to add |
|
13212 * @param {Function} fn The function that becomes the method |
|
13213 * @param {Object} context An optional context to call the method with |
|
13214 * (defaults to the Node instance) |
|
13215 * @return {any} Depends on what is returned from the DOM node. |
|
13216 */ |
|
13217 Y_Node.addMethod = function(name, fn, context) { |
|
13218 if (name && fn && typeof fn == 'function') { |
|
13219 Y_Node.prototype[name] = function() { |
|
13220 var args = _slice.call(arguments), |
|
13221 node = this, |
|
13222 ret; |
|
13223 |
|
13224 if (args[0] && args[0]._node) { |
|
13225 args[0] = args[0]._node; |
|
13226 } |
|
13227 |
|
13228 if (args[1] && args[1]._node) { |
|
13229 args[1] = args[1]._node; |
|
13230 } |
|
13231 args.unshift(node._node); |
|
13232 |
|
13233 ret = fn.apply(node, args); |
|
13234 |
|
13235 if (ret) { // scrub truthy |
|
13236 ret = Y_Node.scrubVal(ret, node); |
|
13237 } |
|
13238 |
|
13239 (typeof ret != 'undefined') || (ret = node); |
|
13240 return ret; |
|
13241 }; |
|
13242 } else { |
|
13243 Y.log('unable to add method: ' + name, 'warn', 'Node'); |
|
13244 } |
|
13245 }; |
|
13246 |
|
13247 /** |
|
13248 * Imports utility methods to be added as Y.Node methods. |
|
13249 * @method importMethod |
|
13250 * @static |
|
13251 * |
|
13252 * @param {Object} host The object that contains the method to import. |
|
13253 * @param {String} name The name of the method to import |
|
13254 * @param {String} altName An optional name to use in place of the host name |
|
13255 * @param {Object} context An optional context to call the method with |
|
13256 */ |
|
13257 Y_Node.importMethod = function(host, name, altName) { |
|
13258 if (typeof name == 'string') { |
|
13259 altName = altName || name; |
|
13260 Y_Node.addMethod(altName, host[name], host); |
|
13261 } else { |
|
13262 Y.Array.each(name, function(n) { |
|
13263 Y_Node.importMethod(host, n); |
|
13264 }); |
|
13265 } |
|
13266 }; |
|
13267 |
|
13268 /** |
|
13269 * Retrieves a NodeList based on the given CSS selector. |
|
13270 * @method all |
|
13271 * |
|
13272 * @param {string} selector The CSS selector to test against. |
|
13273 * @return {NodeList} A NodeList instance for the matching HTMLCollection/Array. |
|
13274 * @for YUI |
|
13275 */ |
|
13276 |
|
13277 /** |
|
13278 * Returns a single Node instance bound to the node or the |
|
13279 * first element matching the given selector. Returns null if no match found. |
|
13280 * <strong>Note:</strong> For chaining purposes you may want to |
|
13281 * use <code>Y.all</code>, which returns a NodeList when no match is found. |
|
13282 * @method one |
|
13283 * @param {String | HTMLElement} node a node or Selector |
|
13284 * @return {Node | null} a Node instance or null if no match found. |
|
13285 * @for YUI |
|
13286 */ |
|
13287 |
|
13288 /** |
|
13289 * Returns a single Node instance bound to the node or the |
|
13290 * first element matching the given selector. Returns null if no match found. |
|
13291 * <strong>Note:</strong> For chaining purposes you may want to |
|
13292 * use <code>Y.all</code>, which returns a NodeList when no match is found. |
|
13293 * @method one |
|
13294 * @static |
|
13295 * @param {String | HTMLElement} node a node or Selector |
|
13296 * @return {Node | null} a Node instance or null if no match found. |
|
13297 * @for Node |
|
13298 */ |
|
13299 Y_Node.one = function(node) { |
|
13300 var instance = null, |
|
13301 cachedNode, |
|
13302 uid; |
|
13303 |
|
13304 if (node) { |
|
13305 if (typeof node == 'string') { |
|
13306 node = Y_Node._fromString(node); |
|
13307 if (!node) { |
|
13308 return null; // NOTE: return |
|
13309 } |
|
13310 } else if (node.getDOMNode) { |
|
13311 return node; // NOTE: return |
|
13312 } |
|
13313 |
|
13314 if (node.nodeType || Y.DOM.isWindow(node)) { // avoid bad input (numbers, boolean, etc) |
|
13315 uid = (node.uniqueID && node.nodeType !== 9) ? node.uniqueID : node._yuid; |
|
13316 instance = Y_Node._instances[uid]; // reuse exising instances |
|
13317 cachedNode = instance ? instance._node : null; |
|
13318 if (!instance || (cachedNode && node !== cachedNode)) { // new Node when nodes don't match |
|
13319 instance = new Y_Node(node); |
|
13320 if (node.nodeType != 11) { // dont cache document fragment |
|
13321 Y_Node._instances[instance[UID]] = instance; // cache node |
|
13322 } |
|
13323 } |
|
13324 } |
|
13325 } |
|
13326 |
|
13327 return instance; |
|
13328 }; |
|
13329 |
|
13330 /** |
|
13331 * The default setter for DOM properties |
|
13332 * Called with instance context (this === the Node instance) |
|
13333 * @method DEFAULT_SETTER |
|
13334 * @static |
|
13335 * @param {String} name The attribute/property being set |
|
13336 * @param {any} val The value to be set |
|
13337 * @return {any} The value |
|
13338 */ |
|
13339 Y_Node.DEFAULT_SETTER = function(name, val) { |
|
13340 var node = this._stateProxy, |
|
13341 strPath; |
|
13342 |
|
13343 if (name.indexOf(DOT) > -1) { |
|
13344 strPath = name; |
|
13345 name = name.split(DOT); |
|
13346 // only allow when defined on node |
|
13347 Y.Object.setValue(node, name, val); |
|
13348 } else if (typeof node[name] != 'undefined') { // pass thru DOM properties |
|
13349 node[name] = val; |
|
13350 } |
|
13351 |
|
13352 return val; |
|
13353 }; |
|
13354 |
|
13355 /** |
|
13356 * The default getter for DOM properties |
|
13357 * Called with instance context (this === the Node instance) |
|
13358 * @method DEFAULT_GETTER |
|
13359 * @static |
|
13360 * @param {String} name The attribute/property to look up |
|
13361 * @return {any} The current value |
|
13362 */ |
|
13363 Y_Node.DEFAULT_GETTER = function(name) { |
|
13364 var node = this._stateProxy, |
|
13365 val; |
|
13366 |
|
13367 if (name.indexOf && name.indexOf(DOT) > -1) { |
|
13368 val = Y.Object.getValue(node, name.split(DOT)); |
|
13369 } else if (typeof node[name] != 'undefined') { // pass thru from DOM |
|
13370 val = node[name]; |
|
13371 } |
|
13372 |
|
13373 return val; |
|
13374 }; |
|
13375 |
|
13376 Y.mix(Y_Node.prototype, { |
|
13377 DATA_PREFIX: 'data-', |
|
13378 |
|
13379 /** |
|
13380 * The method called when outputting Node instances as strings |
|
13381 * @method toString |
|
13382 * @return {String} A string representation of the Node instance |
|
13383 */ |
|
13384 toString: function() { |
|
13385 var str = this[UID] + ': not bound to a node', |
|
13386 node = this._node, |
|
13387 attrs, id, className; |
|
13388 |
|
13389 if (node) { |
|
13390 attrs = node.attributes; |
|
13391 id = (attrs && attrs.id) ? node.getAttribute('id') : null; |
|
13392 className = (attrs && attrs.className) ? node.getAttribute('className') : null; |
|
13393 str = node[NODE_NAME]; |
|
13394 |
|
13395 if (id) { |
|
13396 str += '#' + id; |
|
13397 } |
|
13398 |
|
13399 if (className) { |
|
13400 str += '.' + className.replace(' ', '.'); |
|
13401 } |
|
13402 |
|
13403 // TODO: add yuid? |
|
13404 str += ' ' + this[UID]; |
|
13405 } |
|
13406 return str; |
|
13407 }, |
|
13408 |
|
13409 /** |
|
13410 * Returns an attribute value on the Node instance. |
|
13411 * Unless pre-configured (via `Node.ATTRS`), get hands |
|
13412 * off to the underlying DOM node. Only valid |
|
13413 * attributes/properties for the node will be queried. |
|
13414 * @method get |
|
13415 * @param {String} attr The attribute |
|
13416 * @return {any} The current value of the attribute |
|
13417 */ |
|
13418 get: function(attr) { |
|
13419 var val; |
|
13420 |
|
13421 if (this._getAttr) { // use Attribute imple |
|
13422 val = this._getAttr(attr); |
|
13423 } else { |
|
13424 val = this._get(attr); |
|
13425 } |
|
13426 |
|
13427 if (val) { |
|
13428 val = Y_Node.scrubVal(val, this); |
|
13429 } else if (val === null) { |
|
13430 val = null; // IE: DOM null is not true null (even though they ===) |
|
13431 } |
|
13432 return val; |
|
13433 }, |
|
13434 |
|
13435 /** |
|
13436 * Helper method for get. |
|
13437 * @method _get |
|
13438 * @private |
|
13439 * @param {String} attr The attribute |
|
13440 * @return {any} The current value of the attribute |
|
13441 */ |
|
13442 _get: function(attr) { |
|
13443 var attrConfig = Y_Node.ATTRS[attr], |
|
13444 val; |
|
13445 |
|
13446 if (attrConfig && attrConfig.getter) { |
|
13447 val = attrConfig.getter.call(this); |
|
13448 } else if (Y_Node.re_aria.test(attr)) { |
|
13449 val = this._node.getAttribute(attr, 2); |
|
13450 } else { |
|
13451 val = Y_Node.DEFAULT_GETTER.apply(this, arguments); |
|
13452 } |
|
13453 |
|
13454 return val; |
|
13455 }, |
|
13456 |
|
13457 /** |
|
13458 * Sets an attribute on the Node instance. |
|
13459 * Unless pre-configured (via Node.ATTRS), set hands |
|
13460 * off to the underlying DOM node. Only valid |
|
13461 * attributes/properties for the node will be set. |
|
13462 * To set custom attributes use setAttribute. |
|
13463 * @method set |
|
13464 * @param {String} attr The attribute to be set. |
|
13465 * @param {any} val The value to set the attribute to. |
|
13466 * @chainable |
|
13467 */ |
|
13468 set: function(attr, val) { |
|
13469 var attrConfig = Y_Node.ATTRS[attr]; |
|
13470 |
|
13471 if (this._setAttr) { // use Attribute imple |
|
13472 this._setAttr.apply(this, arguments); |
|
13473 } else { // use setters inline |
|
13474 if (attrConfig && attrConfig.setter) { |
|
13475 attrConfig.setter.call(this, val, attr); |
|
13476 } else if (Y_Node.re_aria.test(attr)) { // special case Aria |
|
13477 this._node.setAttribute(attr, val); |
|
13478 } else { |
|
13479 Y_Node.DEFAULT_SETTER.apply(this, arguments); |
|
13480 } |
|
13481 } |
|
13482 |
|
13483 return this; |
|
13484 }, |
|
13485 |
|
13486 /** |
|
13487 * Sets multiple attributes. |
|
13488 * @method setAttrs |
|
13489 * @param {Object} attrMap an object of name/value pairs to set |
|
13490 * @chainable |
|
13491 */ |
|
13492 setAttrs: function(attrMap) { |
|
13493 if (this._setAttrs) { // use Attribute imple |
|
13494 this._setAttrs(attrMap); |
|
13495 } else { // use setters inline |
|
13496 Y.Object.each(attrMap, function(v, n) { |
|
13497 this.set(n, v); |
|
13498 }, this); |
|
13499 } |
|
13500 |
|
13501 return this; |
|
13502 }, |
|
13503 |
|
13504 /** |
|
13505 * Returns an object containing the values for the requested attributes. |
|
13506 * @method getAttrs |
|
13507 * @param {Array} attrs an array of attributes to get values |
|
13508 * @return {Object} An object with attribute name/value pairs. |
|
13509 */ |
|
13510 getAttrs: function(attrs) { |
|
13511 var ret = {}; |
|
13512 if (this._getAttrs) { // use Attribute imple |
|
13513 this._getAttrs(attrs); |
|
13514 } else { // use setters inline |
|
13515 Y.Array.each(attrs, function(v, n) { |
|
13516 ret[v] = this.get(v); |
|
13517 }, this); |
|
13518 } |
|
13519 |
|
13520 return ret; |
|
13521 }, |
|
13522 |
|
13523 /** |
|
13524 * Compares nodes to determine if they match. |
|
13525 * Node instances can be compared to each other and/or HTMLElements. |
|
13526 * @method compareTo |
|
13527 * @param {HTMLElement | Node} refNode The reference node to compare to the node. |
|
13528 * @return {Boolean} True if the nodes match, false if they do not. |
|
13529 */ |
|
13530 compareTo: function(refNode) { |
|
13531 var node = this._node; |
|
13532 |
|
13533 if (refNode && refNode._node) { |
|
13534 refNode = refNode._node; |
|
13535 } |
|
13536 return node === refNode; |
|
13537 }, |
|
13538 |
|
13539 /** |
|
13540 * Determines whether the node is appended to the document. |
|
13541 * @method inDoc |
|
13542 * @param {Node|HTMLElement} doc optional An optional document to check against. |
|
13543 * Defaults to current document. |
|
13544 * @return {Boolean} Whether or not this node is appended to the document. |
|
13545 */ |
|
13546 inDoc: function(doc) { |
|
13547 var node = this._node; |
|
13548 doc = (doc) ? doc._node || doc : node[OWNER_DOCUMENT]; |
|
13549 if (doc.documentElement) { |
|
13550 return Y_DOM.contains(doc.documentElement, node); |
|
13551 } |
|
13552 }, |
|
13553 |
|
13554 getById: function(id) { |
|
13555 var node = this._node, |
|
13556 ret = Y_DOM.byId(id, node[OWNER_DOCUMENT]); |
|
13557 if (ret && Y_DOM.contains(node, ret)) { |
|
13558 ret = Y.one(ret); |
|
13559 } else { |
|
13560 ret = null; |
|
13561 } |
|
13562 return ret; |
|
13563 }, |
|
13564 |
|
13565 /** |
|
13566 * Returns the nearest ancestor that passes the test applied by supplied boolean method. |
|
13567 * @method ancestor |
|
13568 * @param {String | Function} fn A selector string or boolean method for testing elements. |
|
13569 * If a function is used, it receives the current node being tested as the only argument. |
|
13570 * If fn is not passed as an argument, the parent node will be returned. |
|
13571 * @param {Boolean} testSelf optional Whether or not to include the element in the scan |
|
13572 * @param {String | Function} stopFn optional A selector string or boolean |
|
13573 * method to indicate when the search should stop. The search bails when the function |
|
13574 * returns true or the selector matches. |
|
13575 * If a function is used, it receives the current node being tested as the only argument. |
|
13576 * @return {Node} The matching Node instance or null if not found |
|
13577 */ |
|
13578 ancestor: function(fn, testSelf, stopFn) { |
|
13579 // testSelf is optional, check for stopFn as 2nd arg |
|
13580 if (arguments.length === 2 && |
|
13581 (typeof testSelf == 'string' || typeof testSelf == 'function')) { |
|
13582 stopFn = testSelf; |
|
13583 } |
|
13584 |
|
13585 return Y.one(Y_DOM.ancestor(this._node, _wrapFn(fn), testSelf, _wrapFn(stopFn))); |
|
13586 }, |
|
13587 |
|
13588 /** |
|
13589 * Returns the ancestors that pass the test applied by supplied boolean method. |
|
13590 * @method ancestors |
|
13591 * @param {String | Function} fn A selector string or boolean method for testing elements. |
|
13592 * @param {Boolean} testSelf optional Whether or not to include the element in the scan |
|
13593 * If a function is used, it receives the current node being tested as the only argument. |
|
13594 * @return {NodeList} A NodeList instance containing the matching elements |
|
13595 */ |
|
13596 ancestors: function(fn, testSelf, stopFn) { |
|
13597 if (arguments.length === 2 && |
|
13598 (typeof testSelf == 'string' || typeof testSelf == 'function')) { |
|
13599 stopFn = testSelf; |
|
13600 } |
|
13601 return Y.all(Y_DOM.ancestors(this._node, _wrapFn(fn), testSelf, _wrapFn(stopFn))); |
|
13602 }, |
|
13603 |
|
13604 /** |
|
13605 * Returns the previous matching sibling. |
|
13606 * Returns the nearest element node sibling if no method provided. |
|
13607 * @method previous |
|
13608 * @param {String | Function} fn A selector or boolean method for testing elements. |
|
13609 * If a function is used, it receives the current node being tested as the only argument. |
|
13610 * @return {Node} Node instance or null if not found |
|
13611 */ |
|
13612 previous: function(fn, all) { |
|
13613 return Y.one(Y_DOM.elementByAxis(this._node, 'previousSibling', _wrapFn(fn), all)); |
|
13614 }, |
|
13615 |
|
13616 /** |
|
13617 * Returns the next matching sibling. |
|
13618 * Returns the nearest element node sibling if no method provided. |
|
13619 * @method next |
|
13620 * @param {String | Function} fn A selector or boolean method for testing elements. |
|
13621 * If a function is used, it receives the current node being tested as the only argument. |
|
13622 * @return {Node} Node instance or null if not found |
|
13623 */ |
|
13624 next: function(fn, all) { |
|
13625 return Y.one(Y_DOM.elementByAxis(this._node, 'nextSibling', _wrapFn(fn), all)); |
|
13626 }, |
|
13627 |
|
13628 /** |
|
13629 * Returns all matching siblings. |
|
13630 * Returns all siblings if no method provided. |
|
13631 * @method siblings |
|
13632 * @param {String | Function} fn A selector or boolean method for testing elements. |
|
13633 * If a function is used, it receives the current node being tested as the only argument. |
|
13634 * @return {NodeList} NodeList instance bound to found siblings |
|
13635 */ |
|
13636 siblings: function(fn) { |
|
13637 return Y.all(Y_DOM.siblings(this._node, _wrapFn(fn))); |
|
13638 }, |
|
13639 |
|
13640 /** |
|
13641 * Retrieves a single Node instance, the first element matching the given |
|
13642 * CSS selector. |
|
13643 * Returns null if no match found. |
|
13644 * @method one |
|
13645 * |
|
13646 * @param {string} selector The CSS selector to test against. |
|
13647 * @return {Node | null} A Node instance for the matching HTMLElement or null |
|
13648 * if no match found. |
|
13649 */ |
|
13650 one: function(selector) { |
|
13651 return Y.one(Y.Selector.query(selector, this._node, true)); |
|
13652 }, |
|
13653 |
|
13654 /** |
|
13655 * Retrieves a NodeList based on the given CSS selector. |
|
13656 * @method all |
|
13657 * |
|
13658 * @param {string} selector The CSS selector to test against. |
|
13659 * @return {NodeList} A NodeList instance for the matching HTMLCollection/Array. |
|
13660 */ |
|
13661 all: function(selector) { |
|
13662 var nodelist; |
|
13663 |
|
13664 if (this._node) { |
|
13665 nodelist = Y.all(Y.Selector.query(selector, this._node)); |
|
13666 nodelist._query = selector; |
|
13667 nodelist._queryRoot = this._node; |
|
13668 } |
|
13669 |
|
13670 return nodelist || Y.all([]); |
|
13671 }, |
|
13672 |
|
13673 // TODO: allow fn test |
|
13674 /** |
|
13675 * Test if the supplied node matches the supplied selector. |
|
13676 * @method test |
|
13677 * |
|
13678 * @param {string} selector The CSS selector to test against. |
|
13679 * @return {boolean} Whether or not the node matches the selector. |
|
13680 */ |
|
13681 test: function(selector) { |
|
13682 return Y.Selector.test(this._node, selector); |
|
13683 }, |
|
13684 |
|
13685 /** |
|
13686 * Removes the node from its parent. |
|
13687 * Shortcut for myNode.get('parentNode').removeChild(myNode); |
|
13688 * @method remove |
|
13689 * @param {Boolean} destroy whether or not to call destroy() on the node |
|
13690 * after removal. |
|
13691 * @chainable |
|
13692 * |
|
13693 */ |
|
13694 remove: function(destroy) { |
|
13695 var node = this._node; |
|
13696 |
|
13697 if (node && node.parentNode) { |
|
13698 node.parentNode.removeChild(node); |
|
13699 } |
|
13700 |
|
13701 if (destroy) { |
|
13702 this.destroy(); |
|
13703 } |
|
13704 |
|
13705 return this; |
|
13706 }, |
|
13707 |
|
13708 /** |
|
13709 * Replace the node with the other node. This is a DOM update only |
|
13710 * and does not change the node bound to the Node instance. |
|
13711 * Shortcut for myNode.get('parentNode').replaceChild(newNode, myNode); |
|
13712 * @method replace |
|
13713 * @param {Node | HTMLNode} newNode Node to be inserted |
|
13714 * @chainable |
|
13715 * |
|
13716 */ |
|
13717 replace: function(newNode) { |
|
13718 var node = this._node; |
|
13719 if (typeof newNode == 'string') { |
|
13720 newNode = Y_Node.create(newNode); |
|
13721 } |
|
13722 node.parentNode.replaceChild(Y_Node.getDOMNode(newNode), node); |
|
13723 return this; |
|
13724 }, |
|
13725 |
|
13726 /** |
|
13727 * @method replaceChild |
|
13728 * @for Node |
|
13729 * @param {String | HTMLElement | Node} node Node to be inserted |
|
13730 * @param {HTMLElement | Node} refNode Node to be replaced |
|
13731 * @return {Node} The replaced node |
|
13732 */ |
|
13733 replaceChild: function(node, refNode) { |
|
13734 if (typeof node == 'string') { |
|
13735 node = Y_DOM.create(node); |
|
13736 } |
|
13737 |
|
13738 return Y.one(this._node.replaceChild(Y_Node.getDOMNode(node), Y_Node.getDOMNode(refNode))); |
|
13739 }, |
|
13740 |
|
13741 /** |
|
13742 * Nulls internal node references, removes any plugins and event listeners. |
|
13743 * Note that destroy() will not remove the node from its parent or from the DOM. For that |
|
13744 * functionality, call remove(true). |
|
13745 * @method destroy |
|
13746 * @param {Boolean} recursivePurge (optional) Whether or not to remove listeners from the |
|
13747 * node's subtree (default is false) |
|
13748 * |
|
13749 */ |
|
13750 destroy: function(recursive) { |
|
13751 var UID = Y.config.doc.uniqueID ? 'uniqueID' : '_yuid', |
|
13752 instance; |
|
13753 |
|
13754 this.purge(); // TODO: only remove events add via this Node |
|
13755 |
|
13756 if (this.unplug) { // may not be a PluginHost |
|
13757 this.unplug(); |
|
13758 } |
|
13759 |
|
13760 this.clearData(); |
|
13761 |
|
13762 if (recursive) { |
|
13763 Y.NodeList.each(this.all('*'), function(node) { |
|
13764 instance = Y_Node._instances[node[UID]]; |
|
13765 if (instance) { |
|
13766 instance.destroy(); |
|
13767 } else { // purge in case added by other means |
|
13768 Y.Event.purgeElement(node); |
|
13769 } |
|
13770 }); |
|
13771 } |
|
13772 |
|
13773 this._node = null; |
|
13774 this._stateProxy = null; |
|
13775 |
|
13776 delete Y_Node._instances[this._yuid]; |
|
13777 }, |
|
13778 |
|
13779 /** |
|
13780 * Invokes a method on the Node instance |
|
13781 * @method invoke |
|
13782 * @param {String} method The name of the method to invoke |
|
13783 * @param {Any} a, b, c, etc. Arguments to invoke the method with. |
|
13784 * @return Whatever the underly method returns. |
|
13785 * DOM Nodes and Collections return values |
|
13786 * are converted to Node/NodeList instances. |
|
13787 * |
|
13788 */ |
|
13789 invoke: function(method, a, b, c, d, e) { |
|
13790 var node = this._node, |
|
13791 ret; |
|
13792 |
|
13793 if (a && a._node) { |
|
13794 a = a._node; |
|
13795 } |
|
13796 |
|
13797 if (b && b._node) { |
|
13798 b = b._node; |
|
13799 } |
|
13800 |
|
13801 ret = node[method](a, b, c, d, e); |
|
13802 return Y_Node.scrubVal(ret, this); |
|
13803 }, |
|
13804 |
|
13805 /** |
|
13806 * @method swap |
|
13807 * @description Swap DOM locations with the given node. |
|
13808 * This does not change which DOM node each Node instance refers to. |
|
13809 * @param {Node} otherNode The node to swap with |
|
13810 * @chainable |
|
13811 */ |
|
13812 swap: Y.config.doc.documentElement.swapNode ? |
|
13813 function(otherNode) { |
|
13814 this._node.swapNode(Y_Node.getDOMNode(otherNode)); |
|
13815 } : |
|
13816 function(otherNode) { |
|
13817 otherNode = Y_Node.getDOMNode(otherNode); |
|
13818 var node = this._node, |
|
13819 parent = otherNode.parentNode, |
|
13820 nextSibling = otherNode.nextSibling; |
|
13821 |
|
13822 if (nextSibling === node) { |
|
13823 parent.insertBefore(node, otherNode); |
|
13824 } else if (otherNode === node.nextSibling) { |
|
13825 parent.insertBefore(otherNode, node); |
|
13826 } else { |
|
13827 node.parentNode.replaceChild(otherNode, node); |
|
13828 Y_DOM.addHTML(parent, node, nextSibling); |
|
13829 } |
|
13830 return this; |
|
13831 }, |
|
13832 |
|
13833 |
|
13834 hasMethod: function(method) { |
|
13835 var node = this._node; |
|
13836 return !!(node && method in node && |
|
13837 typeof node[method] != 'unknown' && |
|
13838 (typeof node[method] == 'function' || |
|
13839 String(node[method]).indexOf('function') === 1)); // IE reports as object, prepends space |
|
13840 }, |
|
13841 |
|
13842 isFragment: function() { |
|
13843 return (this.get('nodeType') === 11); |
|
13844 }, |
|
13845 |
|
13846 /** |
|
13847 * Removes and destroys all of the nodes within the node. |
|
13848 * @method empty |
|
13849 * @chainable |
|
13850 */ |
|
13851 empty: function() { |
|
13852 this.get('childNodes').remove().destroy(true); |
|
13853 return this; |
|
13854 }, |
|
13855 |
|
13856 /** |
|
13857 * Returns the DOM node bound to the Node instance |
|
13858 * @method getDOMNode |
|
13859 * @return {DOMNode} |
|
13860 */ |
|
13861 getDOMNode: function() { |
|
13862 return this._node; |
|
13863 } |
|
13864 }, true); |
|
13865 |
|
13866 Y.Node = Y_Node; |
|
13867 Y.one = Y_Node.one; |
|
13868 /** |
|
13869 * The NodeList module provides support for managing collections of Nodes. |
|
13870 * @module node |
|
13871 * @submodule node-core |
|
13872 */ |
|
13873 |
|
13874 /** |
|
13875 * The NodeList class provides a wrapper for manipulating DOM NodeLists. |
|
13876 * NodeList properties can be accessed via the set/get methods. |
|
13877 * Use Y.all() to retrieve NodeList instances. |
|
13878 * |
|
13879 * @class NodeList |
|
13880 * @constructor |
|
13881 * @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. |
|
13882 */ |
|
13883 |
|
13884 var NodeList = function(nodes) { |
|
13885 var tmp = []; |
|
13886 |
|
13887 if (nodes) { |
|
13888 if (typeof nodes === 'string') { // selector query |
|
13889 this._query = nodes; |
|
13890 nodes = Y.Selector.query(nodes); |
|
13891 } else if (nodes.nodeType || Y_DOM.isWindow(nodes)) { // domNode || window |
|
13892 nodes = [nodes]; |
|
13893 } else if (nodes._node) { // Y.Node |
|
13894 nodes = [nodes._node]; |
|
13895 } else if (nodes[0] && nodes[0]._node) { // allow array of Y.Nodes |
|
13896 Y.Array.each(nodes, function(node) { |
|
13897 if (node._node) { |
|
13898 tmp.push(node._node); |
|
13899 } |
|
13900 }); |
|
13901 nodes = tmp; |
|
13902 } else { // array of domNodes or domNodeList (no mixed array of Y.Node/domNodes) |
|
13903 nodes = Y.Array(nodes, 0, true); |
|
13904 } |
|
13905 } |
|
13906 |
|
13907 /** |
|
13908 * The underlying array of DOM nodes bound to the Y.NodeList instance |
|
13909 * @property _nodes |
|
13910 * @private |
|
13911 */ |
|
13912 this._nodes = nodes || []; |
|
13913 }; |
|
13914 |
|
13915 NodeList.NAME = 'NodeList'; |
|
13916 |
|
13917 /** |
|
13918 * Retrieves the DOM nodes bound to a NodeList instance |
|
13919 * @method getDOMNodes |
|
13920 * @static |
|
13921 * |
|
13922 * @param {NodeList} nodelist The NodeList instance |
|
13923 * @return {Array} The array of DOM nodes bound to the NodeList |
|
13924 */ |
|
13925 NodeList.getDOMNodes = function(nodelist) { |
|
13926 return (nodelist && nodelist._nodes) ? nodelist._nodes : nodelist; |
|
13927 }; |
|
13928 |
|
13929 NodeList.each = function(instance, fn, context) { |
|
13930 var nodes = instance._nodes; |
|
13931 if (nodes && nodes.length) { |
|
13932 Y.Array.each(nodes, fn, context || instance); |
|
13933 } else { |
|
13934 Y.log('no nodes bound to ' + this, 'warn', 'NodeList'); |
|
13935 } |
|
13936 }; |
|
13937 |
|
13938 NodeList.addMethod = function(name, fn, context) { |
|
13939 if (name && fn) { |
|
13940 NodeList.prototype[name] = function() { |
|
13941 var ret = [], |
|
13942 args = arguments; |
|
13943 |
|
13944 Y.Array.each(this._nodes, function(node) { |
|
13945 var UID = (node.uniqueID && node.nodeType !== 9 ) ? 'uniqueID' : '_yuid', |
|
13946 instance = Y.Node._instances[node[UID]], |
|
13947 ctx, |
|
13948 result; |
|
13949 |
|
13950 if (!instance) { |
|
13951 instance = NodeList._getTempNode(node); |
|
13952 } |
|
13953 ctx = context || instance; |
|
13954 result = fn.apply(ctx, args); |
|
13955 if (result !== undefined && result !== instance) { |
|
13956 ret[ret.length] = result; |
|
13957 } |
|
13958 }); |
|
13959 |
|
13960 // TODO: remove tmp pointer |
|
13961 return ret.length ? ret : this; |
|
13962 }; |
|
13963 } else { |
|
13964 Y.log('unable to add method: ' + name + ' to NodeList', 'warn', 'node'); |
|
13965 } |
|
13966 }; |
|
13967 |
|
13968 NodeList.importMethod = function(host, name, altName) { |
|
13969 if (typeof name === 'string') { |
|
13970 altName = altName || name; |
|
13971 NodeList.addMethod(name, host[name]); |
|
13972 } else { |
|
13973 Y.Array.each(name, function(n) { |
|
13974 NodeList.importMethod(host, n); |
|
13975 }); |
|
13976 } |
|
13977 }; |
|
13978 |
|
13979 NodeList._getTempNode = function(node) { |
|
13980 var tmp = NodeList._tempNode; |
|
13981 if (!tmp) { |
|
13982 tmp = Y.Node.create('<div></div>'); |
|
13983 NodeList._tempNode = tmp; |
|
13984 } |
|
13985 |
|
13986 tmp._node = node; |
|
13987 tmp._stateProxy = node; |
|
13988 return tmp; |
|
13989 }; |
|
13990 |
|
13991 Y.mix(NodeList.prototype, { |
|
13992 _invoke: function(method, args, getter) { |
|
13993 var ret = (getter) ? [] : this; |
|
13994 |
|
13995 this.each(function(node) { |
|
13996 var val = node[method].apply(node, args); |
|
13997 if (getter) { |
|
13998 ret.push(val); |
|
13999 } |
|
14000 }); |
|
14001 |
|
14002 return ret; |
|
14003 }, |
|
14004 |
|
14005 /** |
|
14006 * Retrieves the Node instance at the given index. |
|
14007 * @method item |
|
14008 * |
|
14009 * @param {Number} index The index of the target Node. |
|
14010 * @return {Node} The Node instance at the given index. |
|
14011 */ |
|
14012 item: function(index) { |
|
14013 return Y.one((this._nodes || [])[index]); |
|
14014 }, |
|
14015 |
|
14016 /** |
|
14017 * Applies the given function to each Node in the NodeList. |
|
14018 * @method each |
|
14019 * @param {Function} fn The function to apply. It receives 3 arguments: |
|
14020 * the current node instance, the node's index, and the NodeList instance |
|
14021 * @param {Object} context optional An optional context to apply the function with |
|
14022 * Default context is the current Node instance |
|
14023 * @chainable |
|
14024 */ |
|
14025 each: function(fn, context) { |
|
14026 var instance = this; |
|
14027 Y.Array.each(this._nodes, function(node, index) { |
|
14028 node = Y.one(node); |
|
14029 return fn.call(context || node, node, index, instance); |
|
14030 }); |
|
14031 return instance; |
|
14032 }, |
|
14033 |
|
14034 batch: function(fn, context) { |
|
14035 var nodelist = this; |
|
14036 |
|
14037 Y.Array.each(this._nodes, function(node, index) { |
|
14038 var instance = Y.Node._instances[node[UID]]; |
|
14039 if (!instance) { |
|
14040 instance = NodeList._getTempNode(node); |
|
14041 } |
|
14042 |
|
14043 return fn.call(context || instance, instance, index, nodelist); |
|
14044 }); |
|
14045 return nodelist; |
|
14046 }, |
|
14047 |
|
14048 /** |
|
14049 * Executes the function once for each node until a true value is returned. |
|
14050 * @method some |
|
14051 * @param {Function} fn The function to apply. It receives 3 arguments: |
|
14052 * the current node instance, the node's index, and the NodeList instance |
|
14053 * @param {Object} context optional An optional context to execute the function from. |
|
14054 * Default context is the current Node instance |
|
14055 * @return {Boolean} Whether or not the function returned true for any node. |
|
14056 */ |
|
14057 some: function(fn, context) { |
|
14058 var instance = this; |
|
14059 return Y.Array.some(this._nodes, function(node, index) { |
|
14060 node = Y.one(node); |
|
14061 context = context || node; |
|
14062 return fn.call(context, node, index, instance); |
|
14063 }); |
|
14064 }, |
|
14065 |
|
14066 /** |
|
14067 * Creates a documenFragment from the nodes bound to the NodeList instance |
|
14068 * @method toFrag |
|
14069 * @return {Node} a Node instance bound to the documentFragment |
|
14070 */ |
|
14071 toFrag: function() { |
|
14072 return Y.one(Y.DOM._nl2frag(this._nodes)); |
|
14073 }, |
|
14074 |
|
14075 /** |
|
14076 * Returns the index of the node in the NodeList instance |
|
14077 * or -1 if the node isn't found. |
|
14078 * @method indexOf |
|
14079 * @param {Node | DOMNode} node the node to search for |
|
14080 * @return {Int} the index of the node value or -1 if not found |
|
14081 */ |
|
14082 indexOf: function(node) { |
|
14083 return Y.Array.indexOf(this._nodes, Y.Node.getDOMNode(node)); |
|
14084 }, |
|
14085 |
|
14086 /** |
|
14087 * Filters the NodeList instance down to only nodes matching the given selector. |
|
14088 * @method filter |
|
14089 * @param {String} selector The selector to filter against |
|
14090 * @return {NodeList} NodeList containing the updated collection |
|
14091 * @see Selector |
|
14092 */ |
|
14093 filter: function(selector) { |
|
14094 return Y.all(Y.Selector.filter(this._nodes, selector)); |
|
14095 }, |
|
14096 |
|
14097 |
|
14098 /** |
|
14099 * Creates a new NodeList containing all nodes at every n indices, where |
|
14100 * remainder n % index equals r. |
|
14101 * (zero-based index). |
|
14102 * @method modulus |
|
14103 * @param {Int} n The offset to use (return every nth node) |
|
14104 * @param {Int} r An optional remainder to use with the modulus operation (defaults to zero) |
|
14105 * @return {NodeList} NodeList containing the updated collection |
|
14106 */ |
|
14107 modulus: function(n, r) { |
|
14108 r = r || 0; |
|
14109 var nodes = []; |
|
14110 NodeList.each(this, function(node, i) { |
|
14111 if (i % n === r) { |
|
14112 nodes.push(node); |
|
14113 } |
|
14114 }); |
|
14115 |
|
14116 return Y.all(nodes); |
|
14117 }, |
|
14118 |
|
14119 /** |
|
14120 * Creates a new NodeList containing all nodes at odd indices |
|
14121 * (zero-based index). |
|
14122 * @method odd |
|
14123 * @return {NodeList} NodeList containing the updated collection |
|
14124 */ |
|
14125 odd: function() { |
|
14126 return this.modulus(2, 1); |
|
14127 }, |
|
14128 |
|
14129 /** |
|
14130 * Creates a new NodeList containing all nodes at even indices |
|
14131 * (zero-based index), including zero. |
|
14132 * @method even |
|
14133 * @return {NodeList} NodeList containing the updated collection |
|
14134 */ |
|
14135 even: function() { |
|
14136 return this.modulus(2); |
|
14137 }, |
|
14138 |
|
14139 destructor: function() { |
|
14140 }, |
|
14141 |
|
14142 /** |
|
14143 * Reruns the initial query, when created using a selector query |
|
14144 * @method refresh |
|
14145 * @chainable |
|
14146 */ |
|
14147 refresh: function() { |
|
14148 var doc, |
|
14149 nodes = this._nodes, |
|
14150 query = this._query, |
|
14151 root = this._queryRoot; |
|
14152 |
|
14153 if (query) { |
|
14154 if (!root) { |
|
14155 if (nodes && nodes[0] && nodes[0].ownerDocument) { |
|
14156 root = nodes[0].ownerDocument; |
|
14157 } |
|
14158 } |
|
14159 |
|
14160 this._nodes = Y.Selector.query(query, root); |
|
14161 } |
|
14162 |
|
14163 return this; |
|
14164 }, |
|
14165 |
|
14166 /** |
|
14167 * Returns the current number of items in the NodeList. |
|
14168 * @method size |
|
14169 * @return {Int} The number of items in the NodeList. |
|
14170 */ |
|
14171 size: function() { |
|
14172 return this._nodes.length; |
|
14173 }, |
|
14174 |
|
14175 /** |
|
14176 * Determines if the instance is bound to any nodes |
|
14177 * @method isEmpty |
|
14178 * @return {Boolean} Whether or not the NodeList is bound to any nodes |
|
14179 */ |
|
14180 isEmpty: function() { |
|
14181 return this._nodes.length < 1; |
|
14182 }, |
|
14183 |
|
14184 toString: function() { |
|
14185 var str = '', |
|
14186 errorMsg = this[UID] + ': not bound to any nodes', |
|
14187 nodes = this._nodes, |
|
14188 node; |
|
14189 |
|
14190 if (nodes && nodes[0]) { |
|
14191 node = nodes[0]; |
|
14192 str += node[NODE_NAME]; |
|
14193 if (node.id) { |
|
14194 str += '#' + node.id; |
|
14195 } |
|
14196 |
|
14197 if (node.className) { |
|
14198 str += '.' + node.className.replace(' ', '.'); |
|
14199 } |
|
14200 |
|
14201 if (nodes.length > 1) { |
|
14202 str += '...[' + nodes.length + ' items]'; |
|
14203 } |
|
14204 } |
|
14205 return str || errorMsg; |
|
14206 }, |
|
14207 |
|
14208 /** |
|
14209 * Returns the DOM node bound to the Node instance |
|
14210 * @method getDOMNodes |
|
14211 * @return {Array} |
|
14212 */ |
|
14213 getDOMNodes: function() { |
|
14214 return this._nodes; |
|
14215 } |
|
14216 }, true); |
|
14217 |
|
14218 NodeList.importMethod(Y.Node.prototype, [ |
|
14219 /** |
|
14220 * Called on each Node instance. Nulls internal node references, |
|
14221 * removes any plugins and event listeners |
|
14222 * @method destroy |
|
14223 * @param {Boolean} recursivePurge (optional) Whether or not to |
|
14224 * remove listeners from the node's subtree (default is false) |
|
14225 * @see Node.destroy |
|
14226 */ |
|
14227 'destroy', |
|
14228 |
|
14229 /** |
|
14230 * Called on each Node instance. Removes and destroys all of the nodes |
|
14231 * within the node |
|
14232 * @method empty |
|
14233 * @chainable |
|
14234 * @see Node.empty |
|
14235 */ |
|
14236 'empty', |
|
14237 |
|
14238 /** |
|
14239 * Called on each Node instance. Removes the node from its parent. |
|
14240 * Shortcut for myNode.get('parentNode').removeChild(myNode); |
|
14241 * @method remove |
|
14242 * @param {Boolean} destroy whether or not to call destroy() on the node |
|
14243 * after removal. |
|
14244 * @chainable |
|
14245 * @see Node.remove |
|
14246 */ |
|
14247 'remove', |
|
14248 |
|
14249 /** |
|
14250 * Called on each Node instance. Sets an attribute on the Node instance. |
|
14251 * Unless pre-configured (via Node.ATTRS), set hands |
|
14252 * off to the underlying DOM node. Only valid |
|
14253 * attributes/properties for the node will be set. |
|
14254 * To set custom attributes use setAttribute. |
|
14255 * @method set |
|
14256 * @param {String} attr The attribute to be set. |
|
14257 * @param {any} val The value to set the attribute to. |
|
14258 * @chainable |
|
14259 * @see Node.set |
|
14260 */ |
|
14261 'set' |
|
14262 ]); |
|
14263 |
|
14264 // one-off implementation to convert array of Nodes to NodeList |
|
14265 // e.g. Y.all('input').get('parentNode'); |
|
14266 |
|
14267 /** Called on each Node instance |
|
14268 * @method get |
|
14269 * @see Node |
|
14270 */ |
|
14271 NodeList.prototype.get = function(attr) { |
|
14272 var ret = [], |
|
14273 nodes = this._nodes, |
|
14274 isNodeList = false, |
|
14275 getTemp = NodeList._getTempNode, |
|
14276 instance, |
|
14277 val; |
|
14278 |
|
14279 if (nodes[0]) { |
|
14280 instance = Y.Node._instances[nodes[0]._yuid] || getTemp(nodes[0]); |
|
14281 val = instance._get(attr); |
|
14282 if (val && val.nodeType) { |
|
14283 isNodeList = true; |
|
14284 } |
|
14285 } |
|
14286 |
|
14287 Y.Array.each(nodes, function(node) { |
|
14288 instance = Y.Node._instances[node._yuid]; |
|
14289 |
|
14290 if (!instance) { |
|
14291 instance = getTemp(node); |
|
14292 } |
|
14293 |
|
14294 val = instance._get(attr); |
|
14295 if (!isNodeList) { // convert array of Nodes to NodeList |
|
14296 val = Y.Node.scrubVal(val, instance); |
|
14297 } |
|
14298 |
|
14299 ret.push(val); |
|
14300 }); |
|
14301 |
|
14302 return (isNodeList) ? Y.all(ret) : ret; |
|
14303 }; |
|
14304 |
|
14305 Y.NodeList = NodeList; |
|
14306 |
|
14307 Y.all = function(nodes) { |
|
14308 return new NodeList(nodes); |
|
14309 }; |
|
14310 |
|
14311 Y.Node.all = Y.all; |
|
14312 /** |
|
14313 * @module node |
|
14314 * @submodule node-core |
|
14315 */ |
|
14316 |
|
14317 var Y_NodeList = Y.NodeList, |
|
14318 ArrayProto = Array.prototype, |
|
14319 ArrayMethods = { |
|
14320 /** Returns a new NodeList combining the given NodeList(s) |
|
14321 * @for NodeList |
|
14322 * @method concat |
|
14323 * @param {NodeList | Array} valueN Arrays/NodeLists and/or values to |
|
14324 * concatenate to the resulting NodeList |
|
14325 * @return {NodeList} A new NodeList comprised of this NodeList joined with the input. |
|
14326 */ |
|
14327 'concat': 1, |
|
14328 /** Removes the last from the NodeList and returns it. |
|
14329 * @for NodeList |
|
14330 * @method pop |
|
14331 * @return {Node | null} The last item in the NodeList, or null if the list is empty. |
|
14332 */ |
|
14333 'pop': 0, |
|
14334 /** Adds the given Node(s) to the end of the NodeList. |
|
14335 * @for NodeList |
|
14336 * @method push |
|
14337 * @param {Node | DOMNode} nodes One or more nodes to add to the end of the NodeList. |
|
14338 */ |
|
14339 'push': 0, |
|
14340 /** Removes the first item from the NodeList and returns it. |
|
14341 * @for NodeList |
|
14342 * @method shift |
|
14343 * @return {Node | null} The first item in the NodeList, or null if the NodeList is empty. |
|
14344 */ |
|
14345 'shift': 0, |
|
14346 /** Returns a new NodeList comprising the Nodes in the given range. |
|
14347 * @for NodeList |
|
14348 * @method slice |
|
14349 * @param {Number} begin Zero-based index at which to begin extraction. |
|
14350 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. |
|
14351 * @param {Number} end Zero-based index at which to end extraction. slice extracts up to but not including end. |
|
14352 slice(1,4) extracts the second element through the fourth element (elements indexed 1, 2, and 3). |
|
14353 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. |
|
14354 If end is omitted, slice extracts to the end of the sequence. |
|
14355 * @return {NodeList} A new NodeList comprised of this NodeList joined with the input. |
|
14356 */ |
|
14357 'slice': 1, |
|
14358 /** Changes the content of the NodeList, adding new elements while removing old elements. |
|
14359 * @for NodeList |
|
14360 * @method splice |
|
14361 * @param {Number} index Index at which to start changing the array. If negative, will begin that many elements from the end. |
|
14362 * @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. |
|
14363 * {Node | DOMNode| element1, ..., elementN |
|
14364 The elements to add to the array. If you don't specify any elements, splice simply removes elements from the array. |
|
14365 * @return {NodeList} The element(s) removed. |
|
14366 */ |
|
14367 'splice': 1, |
|
14368 /** Adds the given Node(s) to the beginning of the NodeList. |
|
14369 * @for NodeList |
|
14370 * @method unshift |
|
14371 * @param {Node | DOMNode} nodes One or more nodes to add to the NodeList. |
|
14372 */ |
|
14373 'unshift': 0 |
|
14374 }; |
|
14375 |
|
14376 |
|
14377 Y.Object.each(ArrayMethods, function(returnNodeList, name) { |
|
14378 Y_NodeList.prototype[name] = function() { |
|
14379 var args = [], |
|
14380 i = 0, |
|
14381 arg, |
|
14382 ret; |
|
14383 |
|
14384 while (typeof (arg = arguments[i++]) != 'undefined') { // use DOM nodes/nodeLists |
|
14385 args.push(arg._node || arg._nodes || arg); |
|
14386 } |
|
14387 |
|
14388 ret = ArrayProto[name].apply(this._nodes, args); |
|
14389 |
|
14390 if (returnNodeList) { |
|
14391 ret = Y.all(ret); |
|
14392 } else { |
|
14393 ret = Y.Node.scrubVal(ret); |
|
14394 } |
|
14395 |
|
14396 return ret; |
|
14397 }; |
|
14398 }); |
|
14399 /** |
|
14400 * @module node |
|
14401 * @submodule node-core |
|
14402 */ |
|
14403 |
|
14404 Y.Array.each([ |
|
14405 /** |
|
14406 * Passes through to DOM method. |
|
14407 * @for Node |
|
14408 * @method removeChild |
|
14409 * @param {HTMLElement | Node} node Node to be removed |
|
14410 * @return {Node} The removed node |
|
14411 */ |
|
14412 'removeChild', |
|
14413 |
|
14414 /** |
|
14415 * Passes through to DOM method. |
|
14416 * @method hasChildNodes |
|
14417 * @return {Boolean} Whether or not the node has any childNodes |
|
14418 */ |
|
14419 'hasChildNodes', |
|
14420 |
|
14421 /** |
|
14422 * Passes through to DOM method. |
|
14423 * @method cloneNode |
|
14424 * @param {Boolean} deep Whether or not to perform a deep clone, which includes |
|
14425 * subtree and attributes |
|
14426 * @return {Node} The clone |
|
14427 */ |
|
14428 'cloneNode', |
|
14429 |
|
14430 /** |
|
14431 * Passes through to DOM method. |
|
14432 * @method hasAttribute |
|
14433 * @param {String} attribute The attribute to test for |
|
14434 * @return {Boolean} Whether or not the attribute is present |
|
14435 */ |
|
14436 'hasAttribute', |
|
14437 |
|
14438 /** |
|
14439 * Passes through to DOM method. |
|
14440 * @method scrollIntoView |
|
14441 * @chainable |
|
14442 */ |
|
14443 'scrollIntoView', |
|
14444 |
|
14445 /** |
|
14446 * Passes through to DOM method. |
|
14447 * @method getElementsByTagName |
|
14448 * @param {String} tagName The tagName to collect |
|
14449 * @return {NodeList} A NodeList representing the HTMLCollection |
|
14450 */ |
|
14451 'getElementsByTagName', |
|
14452 |
|
14453 /** |
|
14454 * Passes through to DOM method. |
|
14455 * @method focus |
|
14456 * @chainable |
|
14457 */ |
|
14458 'focus', |
|
14459 |
|
14460 /** |
|
14461 * Passes through to DOM method. |
|
14462 * @method blur |
|
14463 * @chainable |
|
14464 */ |
|
14465 'blur', |
|
14466 |
|
14467 /** |
|
14468 * Passes through to DOM method. |
|
14469 * Only valid on FORM elements |
|
14470 * @method submit |
|
14471 * @chainable |
|
14472 */ |
|
14473 'submit', |
|
14474 |
|
14475 /** |
|
14476 * Passes through to DOM method. |
|
14477 * Only valid on FORM elements |
|
14478 * @method reset |
|
14479 * @chainable |
|
14480 */ |
|
14481 'reset', |
|
14482 |
|
14483 /** |
|
14484 * Passes through to DOM method. |
|
14485 * @method select |
|
14486 * @chainable |
|
14487 */ |
|
14488 'select', |
|
14489 |
|
14490 /** |
|
14491 * Passes through to DOM method. |
|
14492 * Only valid on TABLE elements |
|
14493 * @method createCaption |
|
14494 * @chainable |
|
14495 */ |
|
14496 'createCaption' |
|
14497 |
|
14498 ], function(method) { |
|
14499 Y.log('adding: ' + method, 'info', 'node'); |
|
14500 Y.Node.prototype[method] = function(arg1, arg2, arg3) { |
|
14501 var ret = this.invoke(method, arg1, arg2, arg3); |
|
14502 return ret; |
|
14503 }; |
|
14504 }); |
|
14505 |
|
14506 /** |
|
14507 * Passes through to DOM method. |
|
14508 * @method removeAttribute |
|
14509 * @param {String} attribute The attribute to be removed |
|
14510 * @chainable |
|
14511 */ |
|
14512 // one-off implementation due to IE returning boolean, breaking chaining |
|
14513 Y.Node.prototype.removeAttribute = function(attr) { |
|
14514 var node = this._node; |
|
14515 if (node) { |
|
14516 node.removeAttribute(attr, 0); // comma zero for IE < 8 to force case-insensitive |
|
14517 } |
|
14518 |
|
14519 return this; |
|
14520 }; |
|
14521 |
|
14522 Y.Node.importMethod(Y.DOM, [ |
|
14523 /** |
|
14524 * Determines whether the node is an ancestor of another HTML element in the DOM hierarchy. |
|
14525 * @method contains |
|
14526 * @param {Node | HTMLElement} needle The possible node or descendent |
|
14527 * @return {Boolean} Whether or not this node is the needle its ancestor |
|
14528 */ |
|
14529 'contains', |
|
14530 /** |
|
14531 * Allows setting attributes on DOM nodes, normalizing in some cases. |
|
14532 * This passes through to the DOM node, allowing for custom attributes. |
|
14533 * @method setAttribute |
|
14534 * @for Node |
|
14535 * @chainable |
|
14536 * @param {string} name The attribute name |
|
14537 * @param {string} value The value to set |
|
14538 */ |
|
14539 'setAttribute', |
|
14540 /** |
|
14541 * Allows getting attributes on DOM nodes, normalizing in some cases. |
|
14542 * This passes through to the DOM node, allowing for custom attributes. |
|
14543 * @method getAttribute |
|
14544 * @for Node |
|
14545 * @param {string} name The attribute name |
|
14546 * @return {string} The attribute value |
|
14547 */ |
|
14548 'getAttribute', |
|
14549 |
|
14550 /** |
|
14551 * Wraps the given HTML around the node. |
|
14552 * @method wrap |
|
14553 * @param {String} html The markup to wrap around the node. |
|
14554 * @chainable |
|
14555 * @for Node |
|
14556 */ |
|
14557 'wrap', |
|
14558 |
|
14559 /** |
|
14560 * Removes the node's parent node. |
|
14561 * @method unwrap |
|
14562 * @chainable |
|
14563 */ |
|
14564 'unwrap', |
|
14565 |
|
14566 /** |
|
14567 * Applies a unique ID to the node if none exists |
|
14568 * @method generateID |
|
14569 * @return {String} The existing or generated ID |
|
14570 */ |
|
14571 'generateID' |
|
14572 ]); |
|
14573 |
|
14574 Y.NodeList.importMethod(Y.Node.prototype, [ |
|
14575 /** |
|
14576 * Allows getting attributes on DOM nodes, normalizing in some cases. |
|
14577 * This passes through to the DOM node, allowing for custom attributes. |
|
14578 * @method getAttribute |
|
14579 * @see Node |
|
14580 * @for NodeList |
|
14581 * @param {string} name The attribute name |
|
14582 * @return {string} The attribute value |
|
14583 */ |
|
14584 |
|
14585 'getAttribute', |
|
14586 /** |
|
14587 * Allows setting attributes on DOM nodes, normalizing in some cases. |
|
14588 * This passes through to the DOM node, allowing for custom attributes. |
|
14589 * @method setAttribute |
|
14590 * @see Node |
|
14591 * @for NodeList |
|
14592 * @chainable |
|
14593 * @param {string} name The attribute name |
|
14594 * @param {string} value The value to set |
|
14595 */ |
|
14596 'setAttribute', |
|
14597 |
|
14598 /** |
|
14599 * Allows for removing attributes on DOM nodes. |
|
14600 * This passes through to the DOM node, allowing for custom attributes. |
|
14601 * @method removeAttribute |
|
14602 * @see Node |
|
14603 * @for NodeList |
|
14604 * @param {string} name The attribute to remove |
|
14605 */ |
|
14606 'removeAttribute', |
|
14607 /** |
|
14608 * Removes the parent node from node in the list. |
|
14609 * @method unwrap |
|
14610 * @chainable |
|
14611 */ |
|
14612 'unwrap', |
|
14613 /** |
|
14614 * Wraps the given HTML around each node. |
|
14615 * @method wrap |
|
14616 * @param {String} html The markup to wrap around the node. |
|
14617 * @chainable |
|
14618 */ |
|
14619 'wrap', |
|
14620 |
|
14621 /** |
|
14622 * Applies a unique ID to each node if none exists |
|
14623 * @method generateID |
|
14624 * @return {String} The existing or generated ID |
|
14625 */ |
|
14626 'generateID' |
|
14627 ]); |
|
14628 |
|
14629 |
|
14630 }, '@VERSION@', {"requires": ["dom-core", "selector"]}); |
|
14631 YUI.add('node-base', function (Y, NAME) { |
|
14632 |
|
14633 /** |
|
14634 * @module node |
|
14635 * @submodule node-base |
|
14636 */ |
|
14637 |
|
14638 var methods = [ |
|
14639 /** |
|
14640 * Determines whether each node has the given className. |
|
14641 * @method hasClass |
|
14642 * @for Node |
|
14643 * @param {String} className the class name to search for |
|
14644 * @return {Boolean} Whether or not the element has the specified class |
|
14645 */ |
|
14646 'hasClass', |
|
14647 |
|
14648 /** |
|
14649 * Adds a class name to each node. |
|
14650 * @method addClass |
|
14651 * @param {String} className the class name to add to the node's class attribute |
|
14652 * @chainable |
|
14653 */ |
|
14654 'addClass', |
|
14655 |
|
14656 /** |
|
14657 * Removes a class name from each node. |
|
14658 * @method removeClass |
|
14659 * @param {String} className the class name to remove from the node's class attribute |
|
14660 * @chainable |
|
14661 */ |
|
14662 'removeClass', |
|
14663 |
|
14664 /** |
|
14665 * Replace a class with another class for each node. |
|
14666 * If no oldClassName is present, the newClassName is simply added. |
|
14667 * @method replaceClass |
|
14668 * @param {String} oldClassName the class name to be replaced |
|
14669 * @param {String} newClassName the class name that will be replacing the old class name |
|
14670 * @chainable |
|
14671 */ |
|
14672 'replaceClass', |
|
14673 |
|
14674 /** |
|
14675 * If the className exists on the node it is removed, if it doesn't exist it is added. |
|
14676 * @method toggleClass |
|
14677 * @param {String} className the class name to be toggled |
|
14678 * @param {Boolean} force Option to force adding or removing the class. |
|
14679 * @chainable |
|
14680 */ |
|
14681 'toggleClass' |
|
14682 ]; |
|
14683 |
|
14684 Y.Node.importMethod(Y.DOM, methods); |
|
14685 /** |
|
14686 * Determines whether each node has the given className. |
|
14687 * @method hasClass |
|
14688 * @see Node.hasClass |
|
14689 * @for NodeList |
|
14690 * @param {String} className the class name to search for |
|
14691 * @return {Array} An array of booleans for each node bound to the NodeList. |
|
14692 */ |
|
14693 |
|
14694 /** |
|
14695 * Adds a class name to each node. |
|
14696 * @method addClass |
|
14697 * @see Node.addClass |
|
14698 * @param {String} className the class name to add to the node's class attribute |
|
14699 * @chainable |
|
14700 */ |
|
14701 |
|
14702 /** |
|
14703 * Removes a class name from each node. |
|
14704 * @method removeClass |
|
14705 * @see Node.removeClass |
|
14706 * @param {String} className the class name to remove from the node's class attribute |
|
14707 * @chainable |
|
14708 */ |
|
14709 |
|
14710 /** |
|
14711 * Replace a class with another class for each node. |
|
14712 * If no oldClassName is present, the newClassName is simply added. |
|
14713 * @method replaceClass |
|
14714 * @see Node.replaceClass |
|
14715 * @param {String} oldClassName the class name to be replaced |
|
14716 * @param {String} newClassName the class name that will be replacing the old class name |
|
14717 * @chainable |
|
14718 */ |
|
14719 |
|
14720 /** |
|
14721 * If the className exists on the node it is removed, if it doesn't exist it is added. |
|
14722 * @method toggleClass |
|
14723 * @see Node.toggleClass |
|
14724 * @param {String} className the class name to be toggled |
|
14725 * @chainable |
|
14726 */ |
|
14727 Y.NodeList.importMethod(Y.Node.prototype, methods); |
|
14728 /** |
|
14729 * @module node |
|
14730 * @submodule node-base |
|
14731 */ |
|
14732 |
|
14733 var Y_Node = Y.Node, |
|
14734 Y_DOM = Y.DOM; |
|
14735 |
|
14736 /** |
|
14737 * Returns a new dom node using the provided markup string. |
|
14738 * @method create |
|
14739 * @static |
|
14740 * @param {String} html The markup used to create the element |
|
14741 * Use <a href="../classes/Escape.html#method_html">`Y.Escape.html()`</a> |
|
14742 * to escape html content. |
|
14743 * @param {HTMLDocument} doc An optional document context |
|
14744 * @return {Node} A Node instance bound to a DOM node or fragment |
|
14745 * @for Node |
|
14746 */ |
|
14747 Y_Node.create = function(html, doc) { |
|
14748 if (doc && doc._node) { |
|
14749 doc = doc._node; |
|
14750 } |
|
14751 return Y.one(Y_DOM.create(html, doc)); |
|
14752 }; |
|
14753 |
|
14754 Y.mix(Y_Node.prototype, { |
|
14755 /** |
|
14756 * Creates a new Node using the provided markup string. |
|
14757 * @method create |
|
14758 * @param {String} html The markup used to create the element. |
|
14759 * Use <a href="../classes/Escape.html#method_html">`Y.Escape.html()`</a> |
|
14760 * to escape html content. |
|
14761 * @param {HTMLDocument} doc An optional document context |
|
14762 * @return {Node} A Node instance bound to a DOM node or fragment |
|
14763 */ |
|
14764 create: Y_Node.create, |
|
14765 |
|
14766 /** |
|
14767 * Inserts the content before the reference node. |
|
14768 * @method insert |
|
14769 * @param {String | Node | HTMLElement | NodeList | HTMLCollection} content The content to insert |
|
14770 * Use <a href="../classes/Escape.html#method_html">`Y.Escape.html()`</a> |
|
14771 * to escape html content. |
|
14772 * @param {Int | Node | HTMLElement | String} where The position to insert at. |
|
14773 * Possible "where" arguments |
|
14774 * <dl> |
|
14775 * <dt>Y.Node</dt> |
|
14776 * <dd>The Node to insert before</dd> |
|
14777 * <dt>HTMLElement</dt> |
|
14778 * <dd>The element to insert before</dd> |
|
14779 * <dt>Int</dt> |
|
14780 * <dd>The index of the child element to insert before</dd> |
|
14781 * <dt>"replace"</dt> |
|
14782 * <dd>Replaces the existing HTML</dd> |
|
14783 * <dt>"before"</dt> |
|
14784 * <dd>Inserts before the existing HTML</dd> |
|
14785 * <dt>"before"</dt> |
|
14786 * <dd>Inserts content before the node</dd> |
|
14787 * <dt>"after"</dt> |
|
14788 * <dd>Inserts content after the node</dd> |
|
14789 * </dl> |
|
14790 * @chainable |
|
14791 */ |
|
14792 insert: function(content, where) { |
|
14793 this._insert(content, where); |
|
14794 return this; |
|
14795 }, |
|
14796 |
|
14797 _insert: function(content, where) { |
|
14798 var node = this._node, |
|
14799 ret = null; |
|
14800 |
|
14801 if (typeof where == 'number') { // allow index |
|
14802 where = this._node.childNodes[where]; |
|
14803 } else if (where && where._node) { // Node |
|
14804 where = where._node; |
|
14805 } |
|
14806 |
|
14807 if (content && typeof content != 'string') { // allow Node or NodeList/Array instances |
|
14808 content = content._node || content._nodes || content; |
|
14809 } |
|
14810 ret = Y_DOM.addHTML(node, content, where); |
|
14811 |
|
14812 return ret; |
|
14813 }, |
|
14814 |
|
14815 /** |
|
14816 * Inserts the content as the firstChild of the node. |
|
14817 * @method prepend |
|
14818 * @param {String | Node | HTMLElement} content The content to insert |
|
14819 * Use <a href="../classes/Escape.html#method_html">`Y.Escape.html()`</a> |
|
14820 * to escape html content. |
|
14821 * @chainable |
|
14822 */ |
|
14823 prepend: function(content) { |
|
14824 return this.insert(content, 0); |
|
14825 }, |
|
14826 |
|
14827 /** |
|
14828 * Inserts the content as the lastChild of the node. |
|
14829 * @method append |
|
14830 * @param {String | Node | HTMLElement} content The content to insert |
|
14831 * Use <a href="../classes/Escape.html#method_html">`Y.Escape.html()`</a> |
|
14832 * to escape html content. |
|
14833 * @chainable |
|
14834 */ |
|
14835 append: function(content) { |
|
14836 return this.insert(content, null); |
|
14837 }, |
|
14838 |
|
14839 /** |
|
14840 * @method appendChild |
|
14841 * @param {String | HTMLElement | Node} node Node to be appended |
|
14842 * Use <a href="../classes/Escape.html#method_html">`Y.Escape.html()`</a> |
|
14843 * to escape html content. |
|
14844 * @return {Node} The appended node |
|
14845 */ |
|
14846 appendChild: function(node) { |
|
14847 return Y_Node.scrubVal(this._insert(node)); |
|
14848 }, |
|
14849 |
|
14850 /** |
|
14851 * @method insertBefore |
|
14852 * @param {String | HTMLElement | Node} newNode Node to be appended |
|
14853 * @param {HTMLElement | Node} refNode Node to be inserted before |
|
14854 * Use <a href="../classes/Escape.html#method_html">`Y.Escape.html()`</a> |
|
14855 * to escape html content. |
|
14856 * @return {Node} The inserted node |
|
14857 */ |
|
14858 insertBefore: function(newNode, refNode) { |
|
14859 return Y.Node.scrubVal(this._insert(newNode, refNode)); |
|
14860 }, |
|
14861 |
|
14862 /** |
|
14863 * Appends the node to the given node. |
|
14864 * @method appendTo |
|
14865 * @param {Node | HTMLElement} node The node to append to |
|
14866 * @chainable |
|
14867 */ |
|
14868 appendTo: function(node) { |
|
14869 Y.one(node).append(this); |
|
14870 return this; |
|
14871 }, |
|
14872 |
|
14873 /** |
|
14874 * Replaces the node's current content with the content. |
|
14875 * Note that this passes to innerHTML and is not escaped. |
|
14876 * Use <a href="../classes/Escape.html#method_html">`Y.Escape.html()`</a> |
|
14877 * to escape html content or `set('text')` to add as text. |
|
14878 * @method setContent |
|
14879 * @deprecated Use setHTML |
|
14880 * @param {String | Node | HTMLElement | NodeList | HTMLCollection} content The content to insert |
|
14881 * @chainable |
|
14882 */ |
|
14883 setContent: function(content) { |
|
14884 this._insert(content, 'replace'); |
|
14885 return this; |
|
14886 }, |
|
14887 |
|
14888 /** |
|
14889 * Returns the node's current content (e.g. innerHTML) |
|
14890 * @method getContent |
|
14891 * @deprecated Use getHTML |
|
14892 * @return {String} The current content |
|
14893 */ |
|
14894 getContent: function(content) { |
|
14895 return this.get('innerHTML'); |
|
14896 } |
|
14897 }); |
|
14898 |
|
14899 /** |
|
14900 * Replaces the node's current html content with the content provided. |
|
14901 * Note that this passes to innerHTML and is not escaped. |
|
14902 * Use `Y.Escape.html()` to escape HTML, or `set('text')` to add as text. |
|
14903 * @method setHTML |
|
14904 * @param {String | HTML | Node | HTMLElement | NodeList | HTMLCollection} content The content to insert |
|
14905 * @chainable |
|
14906 */ |
|
14907 Y.Node.prototype.setHTML = Y.Node.prototype.setContent; |
|
14908 |
|
14909 /** |
|
14910 * Returns the node's current html content (e.g. innerHTML) |
|
14911 * @method getHTML |
|
14912 * @return {String} The html content |
|
14913 */ |
|
14914 Y.Node.prototype.getHTML = Y.Node.prototype.getContent; |
|
14915 |
|
14916 Y.NodeList.importMethod(Y.Node.prototype, [ |
|
14917 /** |
|
14918 * Called on each Node instance |
|
14919 * @for NodeList |
|
14920 * @method append |
|
14921 * @see Node.append |
|
14922 */ |
|
14923 'append', |
|
14924 |
|
14925 /** |
|
14926 * Called on each Node instance |
|
14927 * @for NodeList |
|
14928 * @method insert |
|
14929 * @see Node.insert |
|
14930 */ |
|
14931 'insert', |
|
14932 |
|
14933 /** |
|
14934 * Called on each Node instance |
|
14935 * @for NodeList |
|
14936 * @method appendChild |
|
14937 * @see Node.appendChild |
|
14938 */ |
|
14939 'appendChild', |
|
14940 |
|
14941 /** |
|
14942 * Called on each Node instance |
|
14943 * @for NodeList |
|
14944 * @method insertBefore |
|
14945 * @see Node.insertBefore |
|
14946 */ |
|
14947 'insertBefore', |
|
14948 |
|
14949 /** |
|
14950 * Called on each Node instance |
|
14951 * @for NodeList |
|
14952 * @method prepend |
|
14953 * @see Node.prepend |
|
14954 */ |
|
14955 'prepend', |
|
14956 |
|
14957 /** |
|
14958 * Called on each Node instance |
|
14959 * Note that this passes to innerHTML and is not escaped. |
|
14960 * Use `Y.Escape.html()` to escape HTML, or `set('text')` to add as text. |
|
14961 * @for NodeList |
|
14962 * @method setContent |
|
14963 * @deprecated Use setHTML |
|
14964 */ |
|
14965 'setContent', |
|
14966 |
|
14967 /** |
|
14968 * Called on each Node instance |
|
14969 * @for NodeList |
|
14970 * @method getContent |
|
14971 * @deprecated Use getHTML |
|
14972 */ |
|
14973 'getContent', |
|
14974 |
|
14975 /** |
|
14976 * Called on each Node instance |
|
14977 * Note that this passes to innerHTML and is not escaped. |
|
14978 * Use `Y.Escape.html()` to escape HTML, or `set('text')` to add as text. |
|
14979 * @for NodeList |
|
14980 * @method setHTML |
|
14981 * @see Node.setHTML |
|
14982 */ |
|
14983 'setHTML', |
|
14984 |
|
14985 /** |
|
14986 * Called on each Node instance |
|
14987 * @for NodeList |
|
14988 * @method getHTML |
|
14989 * @see Node.getHTML |
|
14990 */ |
|
14991 'getHTML' |
|
14992 ]); |
|
14993 /** |
|
14994 * @module node |
|
14995 * @submodule node-base |
|
14996 */ |
|
14997 |
|
14998 var Y_Node = Y.Node, |
|
14999 Y_DOM = Y.DOM; |
|
15000 |
|
15001 /** |
|
15002 * Static collection of configuration attributes for special handling |
|
15003 * @property ATTRS |
|
15004 * @static |
|
15005 * @type object |
|
15006 */ |
|
15007 Y_Node.ATTRS = { |
|
15008 /** |
|
15009 * Allows for getting and setting the text of an element. |
|
15010 * Formatting is preserved and special characters are treated literally. |
|
15011 * @config text |
|
15012 * @type String |
|
15013 */ |
|
15014 text: { |
|
15015 getter: function() { |
|
15016 return Y_DOM.getText(this._node); |
|
15017 }, |
|
15018 |
|
15019 setter: function(content) { |
|
15020 Y_DOM.setText(this._node, content); |
|
15021 return content; |
|
15022 } |
|
15023 }, |
|
15024 |
|
15025 /** |
|
15026 * Allows for getting and setting the text of an element. |
|
15027 * Formatting is preserved and special characters are treated literally. |
|
15028 * @config for |
|
15029 * @type String |
|
15030 */ |
|
15031 'for': { |
|
15032 getter: function() { |
|
15033 return Y_DOM.getAttribute(this._node, 'for'); |
|
15034 }, |
|
15035 |
|
15036 setter: function(val) { |
|
15037 Y_DOM.setAttribute(this._node, 'for', val); |
|
15038 return val; |
|
15039 } |
|
15040 }, |
|
15041 |
|
15042 'options': { |
|
15043 getter: function() { |
|
15044 return this._node.getElementsByTagName('option'); |
|
15045 } |
|
15046 }, |
|
15047 |
|
15048 /** |
|
15049 * Returns a NodeList instance of all HTMLElement children. |
|
15050 * @readOnly |
|
15051 * @config children |
|
15052 * @type NodeList |
|
15053 */ |
|
15054 'children': { |
|
15055 getter: function() { |
|
15056 var node = this._node, |
|
15057 children = node.children, |
|
15058 childNodes, i, len; |
|
15059 |
|
15060 if (!children) { |
|
15061 childNodes = node.childNodes; |
|
15062 children = []; |
|
15063 |
|
15064 for (i = 0, len = childNodes.length; i < len; ++i) { |
|
15065 if (childNodes[i].tagName) { |
|
15066 children[children.length] = childNodes[i]; |
|
15067 } |
|
15068 } |
|
15069 } |
|
15070 return Y.all(children); |
|
15071 } |
|
15072 }, |
|
15073 |
|
15074 value: { |
|
15075 getter: function() { |
|
15076 return Y_DOM.getValue(this._node); |
|
15077 }, |
|
15078 |
|
15079 setter: function(val) { |
|
15080 Y_DOM.setValue(this._node, val); |
|
15081 return val; |
|
15082 } |
|
15083 } |
|
15084 }; |
|
15085 |
|
15086 Y.Node.importMethod(Y.DOM, [ |
|
15087 /** |
|
15088 * Allows setting attributes on DOM nodes, normalizing in some cases. |
|
15089 * This passes through to the DOM node, allowing for custom attributes. |
|
15090 * @method setAttribute |
|
15091 * @for Node |
|
15092 * @for NodeList |
|
15093 * @chainable |
|
15094 * @param {string} name The attribute name |
|
15095 * @param {string} value The value to set |
|
15096 */ |
|
15097 'setAttribute', |
|
15098 /** |
|
15099 * Allows getting attributes on DOM nodes, normalizing in some cases. |
|
15100 * This passes through to the DOM node, allowing for custom attributes. |
|
15101 * @method getAttribute |
|
15102 * @for Node |
|
15103 * @for NodeList |
|
15104 * @param {string} name The attribute name |
|
15105 * @return {string} The attribute value |
|
15106 */ |
|
15107 'getAttribute' |
|
15108 |
|
15109 ]); |
|
15110 /** |
|
15111 * @module node |
|
15112 * @submodule node-base |
|
15113 */ |
|
15114 |
|
15115 var Y_Node = Y.Node; |
|
15116 var Y_NodeList = Y.NodeList; |
|
15117 /** |
|
15118 * List of events that route to DOM events |
|
15119 * @static |
|
15120 * @property DOM_EVENTS |
|
15121 * @for Node |
|
15122 */ |
|
15123 |
|
15124 Y_Node.DOM_EVENTS = { |
|
15125 abort: 1, |
|
15126 beforeunload: 1, |
|
15127 blur: 1, |
|
15128 change: 1, |
|
15129 click: 1, |
|
15130 close: 1, |
|
15131 command: 1, |
|
15132 contextmenu: 1, |
|
15133 dblclick: 1, |
|
15134 DOMMouseScroll: 1, |
|
15135 drag: 1, |
|
15136 dragstart: 1, |
|
15137 dragenter: 1, |
|
15138 dragover: 1, |
|
15139 dragleave: 1, |
|
15140 dragend: 1, |
|
15141 drop: 1, |
|
15142 error: 1, |
|
15143 focus: 1, |
|
15144 key: 1, |
|
15145 keydown: 1, |
|
15146 keypress: 1, |
|
15147 keyup: 1, |
|
15148 load: 1, |
|
15149 message: 1, |
|
15150 mousedown: 1, |
|
15151 mouseenter: 1, |
|
15152 mouseleave: 1, |
|
15153 mousemove: 1, |
|
15154 mousemultiwheel: 1, |
|
15155 mouseout: 1, |
|
15156 mouseover: 1, |
|
15157 mouseup: 1, |
|
15158 mousewheel: 1, |
|
15159 orientationchange: 1, |
|
15160 reset: 1, |
|
15161 resize: 1, |
|
15162 select: 1, |
|
15163 selectstart: 1, |
|
15164 submit: 1, |
|
15165 scroll: 1, |
|
15166 textInput: 1, |
|
15167 unload: 1 |
|
15168 }; |
|
15169 |
|
15170 // Add custom event adaptors to this list. This will make it so |
|
15171 // that delegate, key, available, contentready, etc all will |
|
15172 // be available through Node.on |
|
15173 Y.mix(Y_Node.DOM_EVENTS, Y.Env.evt.plugins); |
|
15174 |
|
15175 Y.augment(Y_Node, Y.EventTarget); |
|
15176 |
|
15177 Y.mix(Y_Node.prototype, { |
|
15178 /** |
|
15179 * Removes event listeners from the node and (optionally) its subtree |
|
15180 * @method purge |
|
15181 * @param {Boolean} recurse (optional) Whether or not to remove listeners from the |
|
15182 * node's subtree |
|
15183 * @param {String} type (optional) Only remove listeners of the specified type |
|
15184 * @chainable |
|
15185 * |
|
15186 */ |
|
15187 purge: function(recurse, type) { |
|
15188 Y.Event.purgeElement(this._node, recurse, type); |
|
15189 return this; |
|
15190 } |
|
15191 |
|
15192 }); |
|
15193 |
|
15194 Y.mix(Y.NodeList.prototype, { |
|
15195 _prepEvtArgs: function(type, fn, context) { |
|
15196 // map to Y.on/after signature (type, fn, nodes, context, arg1, arg2, etc) |
|
15197 var args = Y.Array(arguments, 0, true); |
|
15198 |
|
15199 if (args.length < 2) { // type only (event hash) just add nodes |
|
15200 args[2] = this._nodes; |
|
15201 } else { |
|
15202 args.splice(2, 0, this._nodes); |
|
15203 } |
|
15204 |
|
15205 args[3] = context || this; // default to NodeList instance as context |
|
15206 |
|
15207 return args; |
|
15208 }, |
|
15209 |
|
15210 /** |
|
15211 Subscribe a callback function for each `Node` in the collection to execute |
|
15212 in response to a DOM event. |
|
15213 |
|
15214 NOTE: Generally, the `on()` method should be avoided on `NodeLists`, in |
|
15215 favor of using event delegation from a parent Node. See the Event user |
|
15216 guide for details. |
|
15217 |
|
15218 Most DOM events are associated with a preventable default behavior, such as |
|
15219 link clicks navigating to a new page. Callbacks are passed a |
|
15220 `DOMEventFacade` object as their first argument (usually called `e`) that |
|
15221 can be used to prevent this default behavior with `e.preventDefault()`. See |
|
15222 the `DOMEventFacade` API for all available properties and methods on the |
|
15223 object. |
|
15224 |
|
15225 By default, the `this` object will be the `NodeList` that the subscription |
|
15226 came from, <em>not the `Node` that received the event</em>. Use |
|
15227 `e.currentTarget` to refer to the `Node`. |
|
15228 |
|
15229 Returning `false` from a callback is supported as an alternative to calling |
|
15230 `e.preventDefault(); e.stopPropagation();`. However, it is recommended to |
|
15231 use the event methods. |
|
15232 |
|
15233 @example |
|
15234 |
|
15235 Y.all(".sku").on("keydown", function (e) { |
|
15236 if (e.keyCode === 13) { |
|
15237 e.preventDefault(); |
|
15238 |
|
15239 // Use e.currentTarget to refer to the individual Node |
|
15240 var item = Y.MyApp.searchInventory( e.currentTarget.get('value') ); |
|
15241 // etc ... |
|
15242 } |
|
15243 }); |
|
15244 |
|
15245 @method on |
|
15246 @param {String} type The name of the event |
|
15247 @param {Function} fn The callback to execute in response to the event |
|
15248 @param {Object} [context] Override `this` object in callback |
|
15249 @param {Any} [arg*] 0..n additional arguments to supply to the subscriber |
|
15250 @return {EventHandle} A subscription handle capable of detaching that |
|
15251 subscription |
|
15252 @for NodeList |
|
15253 **/ |
|
15254 on: function(type, fn, context) { |
|
15255 return Y.on.apply(Y, this._prepEvtArgs.apply(this, arguments)); |
|
15256 }, |
|
15257 |
|
15258 /** |
|
15259 * Applies an one-time event listener to each Node bound to the NodeList. |
|
15260 * @method once |
|
15261 * @param {String} type The event being listened for |
|
15262 * @param {Function} fn The handler to call when the event fires |
|
15263 * @param {Object} context The context to call the handler with. |
|
15264 * Default is the NodeList instance. |
|
15265 * @return {EventHandle} A subscription handle capable of detaching that |
|
15266 * subscription |
|
15267 * @for NodeList |
|
15268 */ |
|
15269 once: function(type, fn, context) { |
|
15270 return Y.once.apply(Y, this._prepEvtArgs.apply(this, arguments)); |
|
15271 }, |
|
15272 |
|
15273 /** |
|
15274 * Applies an event listener to each Node bound to the NodeList. |
|
15275 * The handler is called only after all on() handlers are called |
|
15276 * and the event is not prevented. |
|
15277 * @method after |
|
15278 * @param {String} type The event being listened for |
|
15279 * @param {Function} fn The handler to call when the event fires |
|
15280 * @param {Object} context The context to call the handler with. |
|
15281 * Default is the NodeList instance. |
|
15282 * @return {EventHandle} A subscription handle capable of detaching that |
|
15283 * subscription |
|
15284 * @for NodeList |
|
15285 */ |
|
15286 after: function(type, fn, context) { |
|
15287 return Y.after.apply(Y, this._prepEvtArgs.apply(this, arguments)); |
|
15288 }, |
|
15289 |
|
15290 /** |
|
15291 * Applies an one-time event listener to each Node bound to the NodeList |
|
15292 * that will be called only after all on() handlers are called and the |
|
15293 * event is not prevented. |
|
15294 * |
|
15295 * @method onceAfter |
|
15296 * @param {String} type The event being listened for |
|
15297 * @param {Function} fn The handler to call when the event fires |
|
15298 * @param {Object} context The context to call the handler with. |
|
15299 * Default is the NodeList instance. |
|
15300 * @return {EventHandle} A subscription handle capable of detaching that |
|
15301 * subscription |
|
15302 * @for NodeList |
|
15303 */ |
|
15304 onceAfter: function(type, fn, context) { |
|
15305 return Y.onceAfter.apply(Y, this._prepEvtArgs.apply(this, arguments)); |
|
15306 } |
|
15307 }); |
|
15308 |
|
15309 Y_NodeList.importMethod(Y.Node.prototype, [ |
|
15310 /** |
|
15311 * Called on each Node instance |
|
15312 * @method detach |
|
15313 * @see Node.detach |
|
15314 * @for NodeList |
|
15315 */ |
|
15316 'detach', |
|
15317 |
|
15318 /** Called on each Node instance |
|
15319 * @method detachAll |
|
15320 * @see Node.detachAll |
|
15321 * @for NodeList |
|
15322 */ |
|
15323 'detachAll' |
|
15324 ]); |
|
15325 |
|
15326 /** |
|
15327 Subscribe a callback function to execute in response to a DOM event or custom |
|
15328 event. |
|
15329 |
|
15330 Most DOM events are associated with a preventable default behavior such as |
|
15331 link clicks navigating to a new page. Callbacks are passed a `DOMEventFacade` |
|
15332 object as their first argument (usually called `e`) that can be used to |
|
15333 prevent this default behavior with `e.preventDefault()`. See the |
|
15334 `DOMEventFacade` API for all available properties and methods on the object. |
|
15335 |
|
15336 If the event name passed as the first parameter is not a whitelisted DOM event, |
|
15337 it will be treated as a custom event subscriptions, allowing |
|
15338 `node.fire('customEventName')` later in the code. Refer to the Event user guide |
|
15339 for the full DOM event whitelist. |
|
15340 |
|
15341 By default, the `this` object in the callback will refer to the subscribed |
|
15342 `Node`. |
|
15343 |
|
15344 Returning `false` from a callback is supported as an alternative to calling |
|
15345 `e.preventDefault(); e.stopPropagation();`. However, it is recommended to use |
|
15346 the event methods. |
|
15347 |
|
15348 @example |
|
15349 |
|
15350 Y.one("#my-form").on("submit", function (e) { |
|
15351 e.preventDefault(); |
|
15352 |
|
15353 // proceed with ajax form submission instead... |
|
15354 }); |
|
15355 |
|
15356 @method on |
|
15357 @param {String} type The name of the event |
|
15358 @param {Function} fn The callback to execute in response to the event |
|
15359 @param {Object} [context] Override `this` object in callback |
|
15360 @param {Any} [arg*] 0..n additional arguments to supply to the subscriber |
|
15361 @return {EventHandle} A subscription handle capable of detaching that |
|
15362 subscription |
|
15363 @for Node |
|
15364 **/ |
|
15365 |
|
15366 Y.mix(Y.Node.ATTRS, { |
|
15367 offsetHeight: { |
|
15368 setter: function(h) { |
|
15369 Y.DOM.setHeight(this._node, h); |
|
15370 return h; |
|
15371 }, |
|
15372 |
|
15373 getter: function() { |
|
15374 return this._node.offsetHeight; |
|
15375 } |
|
15376 }, |
|
15377 |
|
15378 offsetWidth: { |
|
15379 setter: function(w) { |
|
15380 Y.DOM.setWidth(this._node, w); |
|
15381 return w; |
|
15382 }, |
|
15383 |
|
15384 getter: function() { |
|
15385 return this._node.offsetWidth; |
|
15386 } |
|
15387 } |
|
15388 }); |
|
15389 |
|
15390 Y.mix(Y.Node.prototype, { |
|
15391 sizeTo: function(w, h) { |
|
15392 var node; |
|
15393 if (arguments.length < 2) { |
|
15394 node = Y.one(w); |
|
15395 w = node.get('offsetWidth'); |
|
15396 h = node.get('offsetHeight'); |
|
15397 } |
|
15398 |
|
15399 this.setAttrs({ |
|
15400 offsetWidth: w, |
|
15401 offsetHeight: h |
|
15402 }); |
|
15403 } |
|
15404 }); |
|
15405 /** |
|
15406 * @module node |
|
15407 * @submodule node-base |
|
15408 */ |
|
15409 |
|
15410 var Y_Node = Y.Node; |
|
15411 |
|
15412 Y.mix(Y_Node.prototype, { |
|
15413 /** |
|
15414 * Makes the node visible. |
|
15415 * If the "transition" module is loaded, show optionally |
|
15416 * animates the showing of the node using either the default |
|
15417 * transition effect ('fadeIn'), or the given named effect. |
|
15418 * @method show |
|
15419 * @for Node |
|
15420 * @param {String} name A named Transition effect to use as the show effect. |
|
15421 * @param {Object} config Options to use with the transition. |
|
15422 * @param {Function} callback An optional function to run after the transition completes. |
|
15423 * @chainable |
|
15424 */ |
|
15425 show: function(callback) { |
|
15426 callback = arguments[arguments.length - 1]; |
|
15427 this.toggleView(true, callback); |
|
15428 return this; |
|
15429 }, |
|
15430 |
|
15431 /** |
|
15432 * The implementation for showing nodes. |
|
15433 * Default is to remove the hidden attribute and reset the CSS style.display property. |
|
15434 * @method _show |
|
15435 * @protected |
|
15436 * @chainable |
|
15437 */ |
|
15438 _show: function() { |
|
15439 this.removeAttribute('hidden'); |
|
15440 |
|
15441 // For back-compat we need to leave this in for browsers that |
|
15442 // do not visually hide a node via the hidden attribute |
|
15443 // and for users that check visibility based on style display. |
|
15444 this.setStyle('display', ''); |
|
15445 |
|
15446 }, |
|
15447 |
|
15448 _isHidden: function() { |
|
15449 return Y.DOM.getAttribute(this._node, 'hidden') === 'true'; |
|
15450 }, |
|
15451 |
|
15452 /** |
|
15453 * Displays or hides the node. |
|
15454 * If the "transition" module is loaded, toggleView optionally |
|
15455 * animates the toggling of the node using given named effect. |
|
15456 * @method toggleView |
|
15457 * @for Node |
|
15458 * @param {String} [name] An optional string value to use as transition effect. |
|
15459 * @param {Boolean} [on] An optional boolean value to force the node to be shown or hidden |
|
15460 * @param {Function} [callback] An optional function to run after the transition completes. |
|
15461 * @chainable |
|
15462 */ |
|
15463 toggleView: function(on, callback) { |
|
15464 this._toggleView.apply(this, arguments); |
|
15465 return this; |
|
15466 }, |
|
15467 |
|
15468 _toggleView: function(on, callback) { |
|
15469 callback = arguments[arguments.length - 1]; |
|
15470 |
|
15471 // base on current state if not forcing |
|
15472 if (typeof on != 'boolean') { |
|
15473 on = (this._isHidden()) ? 1 : 0; |
|
15474 } |
|
15475 |
|
15476 if (on) { |
|
15477 this._show(); |
|
15478 } else { |
|
15479 this._hide(); |
|
15480 } |
|
15481 |
|
15482 if (typeof callback == 'function') { |
|
15483 callback.call(this); |
|
15484 } |
|
15485 |
|
15486 return this; |
|
15487 }, |
|
15488 |
|
15489 /** |
|
15490 * Hides the node. |
|
15491 * If the "transition" module is loaded, hide optionally |
|
15492 * animates the hiding of the node using either the default |
|
15493 * transition effect ('fadeOut'), or the given named effect. |
|
15494 * @method hide |
|
15495 * @param {String} name A named Transition effect to use as the show effect. |
|
15496 * @param {Object} config Options to use with the transition. |
|
15497 * @param {Function} callback An optional function to run after the transition completes. |
|
15498 * @chainable |
|
15499 */ |
|
15500 hide: function(callback) { |
|
15501 callback = arguments[arguments.length - 1]; |
|
15502 this.toggleView(false, callback); |
|
15503 return this; |
|
15504 }, |
|
15505 |
|
15506 /** |
|
15507 * The implementation for hiding nodes. |
|
15508 * Default is to set the hidden attribute to true and set the CSS style.display to 'none'. |
|
15509 * @method _hide |
|
15510 * @protected |
|
15511 * @chainable |
|
15512 */ |
|
15513 _hide: function() { |
|
15514 this.setAttribute('hidden', true); |
|
15515 |
|
15516 // For back-compat we need to leave this in for browsers that |
|
15517 // do not visually hide a node via the hidden attribute |
|
15518 // and for users that check visibility based on style display. |
|
15519 this.setStyle('display', 'none'); |
|
15520 } |
|
15521 }); |
|
15522 |
|
15523 Y.NodeList.importMethod(Y.Node.prototype, [ |
|
15524 /** |
|
15525 * Makes each node visible. |
|
15526 * If the "transition" module is loaded, show optionally |
|
15527 * animates the showing of the node using either the default |
|
15528 * transition effect ('fadeIn'), or the given named effect. |
|
15529 * @method show |
|
15530 * @param {String} name A named Transition effect to use as the show effect. |
|
15531 * @param {Object} config Options to use with the transition. |
|
15532 * @param {Function} callback An optional function to run after the transition completes. |
|
15533 * @for NodeList |
|
15534 * @chainable |
|
15535 */ |
|
15536 'show', |
|
15537 |
|
15538 /** |
|
15539 * Hides each node. |
|
15540 * If the "transition" module is loaded, hide optionally |
|
15541 * animates the hiding of the node using either the default |
|
15542 * transition effect ('fadeOut'), or the given named effect. |
|
15543 * @method hide |
|
15544 * @param {String} name A named Transition effect to use as the show effect. |
|
15545 * @param {Object} config Options to use with the transition. |
|
15546 * @param {Function} callback An optional function to run after the transition completes. |
|
15547 * @chainable |
|
15548 */ |
|
15549 'hide', |
|
15550 |
|
15551 /** |
|
15552 * Displays or hides each node. |
|
15553 * If the "transition" module is loaded, toggleView optionally |
|
15554 * animates the toggling of the nodes using given named effect. |
|
15555 * @method toggleView |
|
15556 * @param {String} [name] An optional string value to use as transition effect. |
|
15557 * @param {Boolean} [on] An optional boolean value to force the nodes to be shown or hidden |
|
15558 * @param {Function} [callback] An optional function to run after the transition completes. |
|
15559 * @chainable |
|
15560 */ |
|
15561 'toggleView' |
|
15562 ]); |
|
15563 |
|
15564 if (!Y.config.doc.documentElement.hasAttribute) { // IE < 8 |
|
15565 Y.Node.prototype.hasAttribute = function(attr) { |
|
15566 if (attr === 'value') { |
|
15567 if (this.get('value') !== "") { // IE < 8 fails to populate specified when set in HTML |
|
15568 return true; |
|
15569 } |
|
15570 } |
|
15571 return !!(this._node.attributes[attr] && |
|
15572 this._node.attributes[attr].specified); |
|
15573 }; |
|
15574 } |
|
15575 |
|
15576 // IE throws an error when calling focus() on an element that's invisible, not |
|
15577 // displayed, or disabled. |
|
15578 Y.Node.prototype.focus = function () { |
|
15579 try { |
|
15580 this._node.focus(); |
|
15581 } catch (e) { |
|
15582 Y.log('error focusing node: ' + e.toString(), 'error', 'node'); |
|
15583 } |
|
15584 |
|
15585 return this; |
|
15586 }; |
|
15587 |
|
15588 // IE throws error when setting input.type = 'hidden', |
|
15589 // input.setAttribute('type', 'hidden') and input.attributes.type.value = 'hidden' |
|
15590 Y.Node.ATTRS.type = { |
|
15591 setter: function(val) { |
|
15592 if (val === 'hidden') { |
|
15593 try { |
|
15594 this._node.type = 'hidden'; |
|
15595 } catch(e) { |
|
15596 this.setStyle('display', 'none'); |
|
15597 this._inputType = 'hidden'; |
|
15598 } |
|
15599 } else { |
|
15600 try { // IE errors when changing the type from "hidden' |
|
15601 this._node.type = val; |
|
15602 } catch (e) { |
|
15603 Y.log('error setting type: ' + val, 'info', 'node'); |
|
15604 } |
|
15605 } |
|
15606 return val; |
|
15607 }, |
|
15608 |
|
15609 getter: function() { |
|
15610 return this._inputType || this._node.type; |
|
15611 }, |
|
15612 |
|
15613 _bypassProxy: true // don't update DOM when using with Attribute |
|
15614 }; |
|
15615 |
|
15616 if (Y.config.doc.createElement('form').elements.nodeType) { |
|
15617 // IE: elements collection is also FORM node which trips up scrubVal. |
|
15618 Y.Node.ATTRS.elements = { |
|
15619 getter: function() { |
|
15620 return this.all('input, textarea, button, select'); |
|
15621 } |
|
15622 }; |
|
15623 } |
|
15624 |
|
15625 /** |
|
15626 * Provides methods for managing custom Node data. |
|
15627 * |
|
15628 * @module node |
|
15629 * @main node |
|
15630 * @submodule node-data |
|
15631 */ |
|
15632 |
|
15633 Y.mix(Y.Node.prototype, { |
|
15634 _initData: function() { |
|
15635 if (! ('_data' in this)) { |
|
15636 this._data = {}; |
|
15637 } |
|
15638 }, |
|
15639 |
|
15640 /** |
|
15641 * @method getData |
|
15642 * @for Node |
|
15643 * @description Retrieves arbitrary data stored on a Node instance. |
|
15644 * If no data is associated with the Node, it will attempt to retrieve |
|
15645 * a value from the corresponding HTML data attribute. (e.g. node.getData('foo') |
|
15646 * will check node.getAttribute('data-foo')). |
|
15647 * @param {string} name Optional name of the data field to retrieve. |
|
15648 * If no name is given, all data is returned. |
|
15649 * @return {any | Object} Whatever is stored at the given field, |
|
15650 * or an object hash of all fields. |
|
15651 */ |
|
15652 getData: function(name) { |
|
15653 this._initData(); |
|
15654 var data = this._data, |
|
15655 ret = data; |
|
15656 |
|
15657 if (arguments.length) { // single field |
|
15658 if (name in data) { |
|
15659 ret = data[name]; |
|
15660 } else { // initialize from HTML attribute |
|
15661 ret = this._getDataAttribute(name); |
|
15662 } |
|
15663 } else if (typeof data == 'object' && data !== null) { // all fields |
|
15664 ret = {}; |
|
15665 Y.Object.each(data, function(v, n) { |
|
15666 ret[n] = v; |
|
15667 }); |
|
15668 |
|
15669 ret = this._getDataAttributes(ret); |
|
15670 } |
|
15671 |
|
15672 return ret; |
|
15673 |
|
15674 }, |
|
15675 |
|
15676 _getDataAttributes: function(ret) { |
|
15677 ret = ret || {}; |
|
15678 var i = 0, |
|
15679 attrs = this._node.attributes, |
|
15680 len = attrs.length, |
|
15681 prefix = this.DATA_PREFIX, |
|
15682 prefixLength = prefix.length, |
|
15683 name; |
|
15684 |
|
15685 while (i < len) { |
|
15686 name = attrs[i].name; |
|
15687 if (name.indexOf(prefix) === 0) { |
|
15688 name = name.substr(prefixLength); |
|
15689 if (!(name in ret)) { // only merge if not already stored |
|
15690 ret[name] = this._getDataAttribute(name); |
|
15691 } |
|
15692 } |
|
15693 |
|
15694 i += 1; |
|
15695 } |
|
15696 |
|
15697 return ret; |
|
15698 }, |
|
15699 |
|
15700 _getDataAttribute: function(name) { |
|
15701 name = this.DATA_PREFIX + name; |
|
15702 |
|
15703 var node = this._node, |
|
15704 attrs = node.attributes, |
|
15705 data = attrs && attrs[name] && attrs[name].value; |
|
15706 |
|
15707 return data; |
|
15708 }, |
|
15709 |
|
15710 /** |
|
15711 * @method setData |
|
15712 * @for Node |
|
15713 * @description Stores arbitrary data on a Node instance. |
|
15714 * This is not stored with the DOM node. |
|
15715 * @param {string} name The name of the field to set. If no val |
|
15716 * is given, name is treated as the data and overrides any existing data. |
|
15717 * @param {any} val The value to be assigned to the field. |
|
15718 * @chainable |
|
15719 */ |
|
15720 setData: function(name, val) { |
|
15721 this._initData(); |
|
15722 if (arguments.length > 1) { |
|
15723 this._data[name] = val; |
|
15724 } else { |
|
15725 this._data = name; |
|
15726 } |
|
15727 |
|
15728 return this; |
|
15729 }, |
|
15730 |
|
15731 /** |
|
15732 * @method clearData |
|
15733 * @for Node |
|
15734 * @description Clears internally stored data. |
|
15735 * @param {string} name The name of the field to clear. If no name |
|
15736 * is given, all data is cleared. |
|
15737 * @chainable |
|
15738 */ |
|
15739 clearData: function(name) { |
|
15740 if ('_data' in this) { |
|
15741 if (typeof name != 'undefined') { |
|
15742 delete this._data[name]; |
|
15743 } else { |
|
15744 delete this._data; |
|
15745 } |
|
15746 } |
|
15747 |
|
15748 return this; |
|
15749 } |
|
15750 }); |
|
15751 |
|
15752 Y.mix(Y.NodeList.prototype, { |
|
15753 /** |
|
15754 * @method getData |
|
15755 * @for NodeList |
|
15756 * @description Retrieves arbitrary data stored on each Node instance |
|
15757 * bound to the NodeList. |
|
15758 * @see Node |
|
15759 * @param {string} name Optional name of the data field to retrieve. |
|
15760 * If no name is given, all data is returned. |
|
15761 * @return {Array} An array containing all of the data for each Node instance. |
|
15762 * or an object hash of all fields. |
|
15763 */ |
|
15764 getData: function(name) { |
|
15765 var args = (arguments.length) ? [name] : []; |
|
15766 return this._invoke('getData', args, true); |
|
15767 }, |
|
15768 |
|
15769 /** |
|
15770 * @method setData |
|
15771 * @for NodeList |
|
15772 * @description Stores arbitrary data on each Node instance bound to the |
|
15773 * NodeList. This is not stored with the DOM node. |
|
15774 * @param {string} name The name of the field to set. If no name |
|
15775 * is given, name is treated as the data and overrides any existing data. |
|
15776 * @param {any} val The value to be assigned to the field. |
|
15777 * @chainable |
|
15778 */ |
|
15779 setData: function(name, val) { |
|
15780 var args = (arguments.length > 1) ? [name, val] : [name]; |
|
15781 return this._invoke('setData', args); |
|
15782 }, |
|
15783 |
|
15784 /** |
|
15785 * @method clearData |
|
15786 * @for NodeList |
|
15787 * @description Clears data on all Node instances bound to the NodeList. |
|
15788 * @param {string} name The name of the field to clear. If no name |
|
15789 * is given, all data is cleared. |
|
15790 * @chainable |
|
15791 */ |
|
15792 clearData: function(name) { |
|
15793 var args = (arguments.length) ? [name] : []; |
|
15794 return this._invoke('clearData', [name]); |
|
15795 } |
|
15796 }); |
|
15797 |
|
15798 |
|
15799 }, '@VERSION@', {"requires": ["event-base", "node-core", "dom-base"]}); |
|
15800 (function () { |
|
15801 var GLOBAL_ENV = YUI.Env; |
|
15802 |
|
15803 if (!GLOBAL_ENV._ready) { |
|
15804 GLOBAL_ENV._ready = function() { |
|
15805 GLOBAL_ENV.DOMReady = true; |
|
15806 GLOBAL_ENV.remove(YUI.config.doc, 'DOMContentLoaded', GLOBAL_ENV._ready); |
|
15807 }; |
|
15808 |
|
15809 GLOBAL_ENV.add(YUI.config.doc, 'DOMContentLoaded', GLOBAL_ENV._ready); |
|
15810 } |
|
15811 })(); |
|
15812 YUI.add('event-base', function (Y, NAME) { |
|
15813 |
|
15814 /* |
|
15815 * DOM event listener abstraction layer |
|
15816 * @module event |
|
15817 * @submodule event-base |
|
15818 */ |
|
15819 |
|
15820 /** |
|
15821 * The domready event fires at the moment the browser's DOM is |
|
15822 * usable. In most cases, this is before images are fully |
|
15823 * downloaded, allowing you to provide a more responsive user |
|
15824 * interface. |
|
15825 * |
|
15826 * In YUI 3, domready subscribers will be notified immediately if |
|
15827 * that moment has already passed when the subscription is created. |
|
15828 * |
|
15829 * One exception is if the yui.js file is dynamically injected into |
|
15830 * the page. If this is done, you must tell the YUI instance that |
|
15831 * you did this in order for DOMReady (and window load events) to |
|
15832 * fire normally. That configuration option is 'injected' -- set |
|
15833 * it to true if the yui.js script is not included inline. |
|
15834 * |
|
15835 * This method is part of the 'event-ready' module, which is a |
|
15836 * submodule of 'event'. |
|
15837 * |
|
15838 * @event domready |
|
15839 * @for YUI |
|
15840 */ |
|
15841 Y.publish('domready', { |
|
15842 fireOnce: true, |
|
15843 async: true |
|
15844 }); |
|
15845 |
|
15846 if (YUI.Env.DOMReady) { |
|
15847 Y.fire('domready'); |
|
15848 } else { |
|
15849 Y.Do.before(function() { Y.fire('domready'); }, YUI.Env, '_ready'); |
|
15850 } |
|
15851 |
|
15852 /** |
|
15853 * Custom event engine, DOM event listener abstraction layer, synthetic DOM |
|
15854 * events. |
|
15855 * @module event |
|
15856 * @submodule event-base |
|
15857 */ |
|
15858 |
|
15859 /** |
|
15860 * Wraps a DOM event, properties requiring browser abstraction are |
|
15861 * fixed here. Provids a security layer when required. |
|
15862 * @class DOMEventFacade |
|
15863 * @param ev {Event} the DOM event |
|
15864 * @param currentTarget {HTMLElement} the element the listener was attached to |
|
15865 * @param wrapper {Event.Custom} the custom event wrapper for this DOM event |
|
15866 */ |
|
15867 |
|
15868 var ua = Y.UA, |
|
15869 |
|
15870 EMPTY = {}, |
|
15871 |
|
15872 /** |
|
15873 * webkit key remapping required for Safari < 3.1 |
|
15874 * @property webkitKeymap |
|
15875 * @private |
|
15876 */ |
|
15877 webkitKeymap = { |
|
15878 63232: 38, // up |
|
15879 63233: 40, // down |
|
15880 63234: 37, // left |
|
15881 63235: 39, // right |
|
15882 63276: 33, // page up |
|
15883 63277: 34, // page down |
|
15884 25: 9, // SHIFT-TAB (Safari provides a different key code in |
|
15885 // this case, even though the shiftKey modifier is set) |
|
15886 63272: 46, // delete |
|
15887 63273: 36, // home |
|
15888 63275: 35 // end |
|
15889 }, |
|
15890 |
|
15891 /** |
|
15892 * Returns a wrapped node. Intended to be used on event targets, |
|
15893 * so it will return the node's parent if the target is a text |
|
15894 * node. |
|
15895 * |
|
15896 * If accessing a property of the node throws an error, this is |
|
15897 * probably the anonymous div wrapper Gecko adds inside text |
|
15898 * nodes. This likely will only occur when attempting to access |
|
15899 * the relatedTarget. In this case, we now return null because |
|
15900 * the anonymous div is completely useless and we do not know |
|
15901 * what the related target was because we can't even get to |
|
15902 * the element's parent node. |
|
15903 * |
|
15904 * @method resolve |
|
15905 * @private |
|
15906 */ |
|
15907 resolve = function(n) { |
|
15908 if (!n) { |
|
15909 return n; |
|
15910 } |
|
15911 try { |
|
15912 if (n && 3 == n.nodeType) { |
|
15913 n = n.parentNode; |
|
15914 } |
|
15915 } catch(e) { |
|
15916 return null; |
|
15917 } |
|
15918 |
|
15919 return Y.one(n); |
|
15920 }, |
|
15921 |
|
15922 DOMEventFacade = function(ev, currentTarget, wrapper) { |
|
15923 this._event = ev; |
|
15924 this._currentTarget = currentTarget; |
|
15925 this._wrapper = wrapper || EMPTY; |
|
15926 |
|
15927 // if not lazy init |
|
15928 this.init(); |
|
15929 }; |
|
15930 |
|
15931 Y.extend(DOMEventFacade, Object, { |
|
15932 |
|
15933 init: function() { |
|
15934 |
|
15935 var e = this._event, |
|
15936 overrides = this._wrapper.overrides, |
|
15937 x = e.pageX, |
|
15938 y = e.pageY, |
|
15939 c, |
|
15940 currentTarget = this._currentTarget; |
|
15941 |
|
15942 this.altKey = e.altKey; |
|
15943 this.ctrlKey = e.ctrlKey; |
|
15944 this.metaKey = e.metaKey; |
|
15945 this.shiftKey = e.shiftKey; |
|
15946 this.type = (overrides && overrides.type) || e.type; |
|
15947 this.clientX = e.clientX; |
|
15948 this.clientY = e.clientY; |
|
15949 |
|
15950 this.pageX = x; |
|
15951 this.pageY = y; |
|
15952 |
|
15953 // charCode is unknown in keyup, keydown. keyCode is unknown in keypress. |
|
15954 // FF 3.6 - 8+? pass 0 for keyCode in keypress events. |
|
15955 // Webkit, FF 3.6-8+?, and IE9+? pass 0 for charCode in keydown, keyup. |
|
15956 // Webkit and IE9+? duplicate charCode in keyCode. |
|
15957 // Opera never sets charCode, always keyCode (though with the charCode). |
|
15958 // IE6-8 don't set charCode or which. |
|
15959 // All browsers other than IE6-8 set which=keyCode in keydown, keyup, and |
|
15960 // which=charCode in keypress. |
|
15961 // |
|
15962 // Moral of the story: (e.which || e.keyCode) will always return the |
|
15963 // known code for that key event phase. e.keyCode is often different in |
|
15964 // keypress from keydown and keyup. |
|
15965 c = e.keyCode || e.charCode; |
|
15966 |
|
15967 if (ua.webkit && (c in webkitKeymap)) { |
|
15968 c = webkitKeymap[c]; |
|
15969 } |
|
15970 |
|
15971 this.keyCode = c; |
|
15972 this.charCode = c; |
|
15973 // Fill in e.which for IE - implementers should always use this over |
|
15974 // e.keyCode or e.charCode. |
|
15975 this.which = e.which || e.charCode || c; |
|
15976 // this.button = e.button; |
|
15977 this.button = this.which; |
|
15978 |
|
15979 this.target = resolve(e.target); |
|
15980 this.currentTarget = resolve(currentTarget); |
|
15981 this.relatedTarget = resolve(e.relatedTarget); |
|
15982 |
|
15983 if (e.type == "mousewheel" || e.type == "DOMMouseScroll") { |
|
15984 this.wheelDelta = (e.detail) ? (e.detail * -1) : Math.round(e.wheelDelta / 80) || ((e.wheelDelta < 0) ? -1 : 1); |
|
15985 } |
|
15986 |
|
15987 if (this._touch) { |
|
15988 this._touch(e, currentTarget, this._wrapper); |
|
15989 } |
|
15990 }, |
|
15991 |
|
15992 stopPropagation: function() { |
|
15993 this._event.stopPropagation(); |
|
15994 this._wrapper.stopped = 1; |
|
15995 this.stopped = 1; |
|
15996 }, |
|
15997 |
|
15998 stopImmediatePropagation: function() { |
|
15999 var e = this._event; |
|
16000 if (e.stopImmediatePropagation) { |
|
16001 e.stopImmediatePropagation(); |
|
16002 } else { |
|
16003 this.stopPropagation(); |
|
16004 } |
|
16005 this._wrapper.stopped = 2; |
|
16006 this.stopped = 2; |
|
16007 }, |
|
16008 |
|
16009 preventDefault: function(returnValue) { |
|
16010 var e = this._event; |
|
16011 e.preventDefault(); |
|
16012 e.returnValue = returnValue || false; |
|
16013 this._wrapper.prevented = 1; |
|
16014 this.prevented = 1; |
|
16015 }, |
|
16016 |
|
16017 halt: function(immediate) { |
|
16018 if (immediate) { |
|
16019 this.stopImmediatePropagation(); |
|
16020 } else { |
|
16021 this.stopPropagation(); |
|
16022 } |
|
16023 |
|
16024 this.preventDefault(); |
|
16025 } |
|
16026 |
|
16027 }); |
|
16028 |
|
16029 DOMEventFacade.resolve = resolve; |
|
16030 Y.DOM2EventFacade = DOMEventFacade; |
|
16031 Y.DOMEventFacade = DOMEventFacade; |
|
16032 |
|
16033 /** |
|
16034 * The native event |
|
16035 * @property _event |
|
16036 * @type {Native DOM Event} |
|
16037 * @private |
|
16038 */ |
|
16039 |
|
16040 /** |
|
16041 The name of the event (e.g. "click") |
|
16042 |
|
16043 @property type |
|
16044 @type {String} |
|
16045 **/ |
|
16046 |
|
16047 /** |
|
16048 `true` if the "alt" or "option" key is pressed. |
|
16049 |
|
16050 @property altKey |
|
16051 @type {Boolean} |
|
16052 **/ |
|
16053 |
|
16054 /** |
|
16055 `true` if the shift key is pressed. |
|
16056 |
|
16057 @property shiftKey |
|
16058 @type {Boolean} |
|
16059 **/ |
|
16060 |
|
16061 /** |
|
16062 `true` if the "Windows" key on a Windows keyboard, "command" key on an |
|
16063 Apple keyboard, or "meta" key on other keyboards is pressed. |
|
16064 |
|
16065 @property metaKey |
|
16066 @type {Boolean} |
|
16067 **/ |
|
16068 |
|
16069 /** |
|
16070 `true` if the "Ctrl" or "control" key is pressed. |
|
16071 |
|
16072 @property ctrlKey |
|
16073 @type {Boolean} |
|
16074 **/ |
|
16075 |
|
16076 /** |
|
16077 * The X location of the event on the page (including scroll) |
|
16078 * @property pageX |
|
16079 * @type {Number} |
|
16080 */ |
|
16081 |
|
16082 /** |
|
16083 * The Y location of the event on the page (including scroll) |
|
16084 * @property pageY |
|
16085 * @type {Number} |
|
16086 */ |
|
16087 |
|
16088 /** |
|
16089 * The X location of the event in the viewport |
|
16090 * @property clientX |
|
16091 * @type {Number} |
|
16092 */ |
|
16093 |
|
16094 /** |
|
16095 * The Y location of the event in the viewport |
|
16096 * @property clientY |
|
16097 * @type {Number} |
|
16098 */ |
|
16099 |
|
16100 /** |
|
16101 * The keyCode for key events. Uses charCode if keyCode is not available |
|
16102 * @property keyCode |
|
16103 * @type {Number} |
|
16104 */ |
|
16105 |
|
16106 /** |
|
16107 * The charCode for key events. Same as keyCode |
|
16108 * @property charCode |
|
16109 * @type {Number} |
|
16110 */ |
|
16111 |
|
16112 /** |
|
16113 * The button that was pushed. 1 for left click, 2 for middle click, 3 for |
|
16114 * right click. This is only reliably populated on `mouseup` events. |
|
16115 * @property button |
|
16116 * @type {Number} |
|
16117 */ |
|
16118 |
|
16119 /** |
|
16120 * The button that was pushed. Same as button. |
|
16121 * @property which |
|
16122 * @type {Number} |
|
16123 */ |
|
16124 |
|
16125 /** |
|
16126 * Node reference for the targeted element |
|
16127 * @property target |
|
16128 * @type {Node} |
|
16129 */ |
|
16130 |
|
16131 /** |
|
16132 * Node reference for the element that the listener was attached to. |
|
16133 * @property currentTarget |
|
16134 * @type {Node} |
|
16135 */ |
|
16136 |
|
16137 /** |
|
16138 * Node reference to the relatedTarget |
|
16139 * @property relatedTarget |
|
16140 * @type {Node} |
|
16141 */ |
|
16142 |
|
16143 /** |
|
16144 * Number representing the direction and velocity of the movement of the mousewheel. |
|
16145 * Negative is down, the higher the number, the faster. Applies to the mousewheel event. |
|
16146 * @property wheelDelta |
|
16147 * @type {Number} |
|
16148 */ |
|
16149 |
|
16150 /** |
|
16151 * Stops the propagation to the next bubble target |
|
16152 * @method stopPropagation |
|
16153 */ |
|
16154 |
|
16155 /** |
|
16156 * Stops the propagation to the next bubble target and |
|
16157 * prevents any additional listeners from being exectued |
|
16158 * on the current target. |
|
16159 * @method stopImmediatePropagation |
|
16160 */ |
|
16161 |
|
16162 /** |
|
16163 * Prevents the event's default behavior |
|
16164 * @method preventDefault |
|
16165 * @param returnValue {string} sets the returnValue of the event to this value |
|
16166 * (rather than the default false value). This can be used to add a customized |
|
16167 * confirmation query to the beforeunload event). |
|
16168 */ |
|
16169 |
|
16170 /** |
|
16171 * Stops the event propagation and prevents the default |
|
16172 * event behavior. |
|
16173 * @method halt |
|
16174 * @param immediate {boolean} if true additional listeners |
|
16175 * on the current target will not be executed |
|
16176 */ |
|
16177 (function() { |
|
16178 |
|
16179 /** |
|
16180 * The event utility provides functions to add and remove event listeners, |
|
16181 * event cleansing. It also tries to automatically remove listeners it |
|
16182 * registers during the unload event. |
|
16183 * @module event |
|
16184 * @main event |
|
16185 * @submodule event-base |
|
16186 */ |
|
16187 |
|
16188 /** |
|
16189 * The event utility provides functions to add and remove event listeners, |
|
16190 * event cleansing. It also tries to automatically remove listeners it |
|
16191 * registers during the unload event. |
|
16192 * |
|
16193 * @class Event |
|
16194 * @static |
|
16195 */ |
|
16196 |
|
16197 Y.Env.evt.dom_wrappers = {}; |
|
16198 Y.Env.evt.dom_map = {}; |
|
16199 |
|
16200 var _eventenv = Y.Env.evt, |
|
16201 config = Y.config, |
|
16202 win = config.win, |
|
16203 add = YUI.Env.add, |
|
16204 remove = YUI.Env.remove, |
|
16205 |
|
16206 onLoad = function() { |
|
16207 YUI.Env.windowLoaded = true; |
|
16208 Y.Event._load(); |
|
16209 remove(win, "load", onLoad); |
|
16210 }, |
|
16211 |
|
16212 onUnload = function() { |
|
16213 Y.Event._unload(); |
|
16214 }, |
|
16215 |
|
16216 EVENT_READY = 'domready', |
|
16217 |
|
16218 COMPAT_ARG = '~yui|2|compat~', |
|
16219 |
|
16220 shouldIterate = function(o) { |
|
16221 try { |
|
16222 // TODO: See if there's a more performant way to return true early on this, for the common case |
|
16223 return (o && typeof o !== "string" && Y.Lang.isNumber(o.length) && !o.tagName && !Y.DOM.isWindow(o)); |
|
16224 } catch(ex) { |
|
16225 Y.log("collection check failure", "warn", "event"); |
|
16226 return false; |
|
16227 } |
|
16228 }, |
|
16229 |
|
16230 // aliases to support DOM event subscription clean up when the last |
|
16231 // subscriber is detached. deleteAndClean overrides the DOM event's wrapper |
|
16232 // CustomEvent _delete method. |
|
16233 _ceProtoDelete = Y.CustomEvent.prototype._delete, |
|
16234 _deleteAndClean = function(s) { |
|
16235 var ret = _ceProtoDelete.apply(this, arguments); |
|
16236 |
|
16237 if (!this.hasSubs()) { |
|
16238 Y.Event._clean(this); |
|
16239 } |
|
16240 |
|
16241 return ret; |
|
16242 }, |
|
16243 |
|
16244 Event = function() { |
|
16245 |
|
16246 /** |
|
16247 * True after the onload event has fired |
|
16248 * @property _loadComplete |
|
16249 * @type boolean |
|
16250 * @static |
|
16251 * @private |
|
16252 */ |
|
16253 var _loadComplete = false, |
|
16254 |
|
16255 /** |
|
16256 * The number of times to poll after window.onload. This number is |
|
16257 * increased if additional late-bound handlers are requested after |
|
16258 * the page load. |
|
16259 * @property _retryCount |
|
16260 * @static |
|
16261 * @private |
|
16262 */ |
|
16263 _retryCount = 0, |
|
16264 |
|
16265 /** |
|
16266 * onAvailable listeners |
|
16267 * @property _avail |
|
16268 * @static |
|
16269 * @private |
|
16270 */ |
|
16271 _avail = [], |
|
16272 |
|
16273 /** |
|
16274 * Custom event wrappers for DOM events. Key is |
|
16275 * 'event:' + Element uid stamp + event type |
|
16276 * @property _wrappers |
|
16277 * @type Y.Event.Custom |
|
16278 * @static |
|
16279 * @private |
|
16280 */ |
|
16281 _wrappers = _eventenv.dom_wrappers, |
|
16282 |
|
16283 _windowLoadKey = null, |
|
16284 |
|
16285 /** |
|
16286 * Custom event wrapper map DOM events. Key is |
|
16287 * Element uid stamp. Each item is a hash of custom event |
|
16288 * wrappers as provided in the _wrappers collection. This |
|
16289 * provides the infrastructure for getListeners. |
|
16290 * @property _el_events |
|
16291 * @static |
|
16292 * @private |
|
16293 */ |
|
16294 _el_events = _eventenv.dom_map; |
|
16295 |
|
16296 return { |
|
16297 |
|
16298 /** |
|
16299 * The number of times we should look for elements that are not |
|
16300 * in the DOM at the time the event is requested after the document |
|
16301 * has been loaded. The default is 1000@amp;40 ms, so it will poll |
|
16302 * for 40 seconds or until all outstanding handlers are bound |
|
16303 * (whichever comes first). |
|
16304 * @property POLL_RETRYS |
|
16305 * @type int |
|
16306 * @static |
|
16307 * @final |
|
16308 */ |
|
16309 POLL_RETRYS: 1000, |
|
16310 |
|
16311 /** |
|
16312 * The poll interval in milliseconds |
|
16313 * @property POLL_INTERVAL |
|
16314 * @type int |
|
16315 * @static |
|
16316 * @final |
|
16317 */ |
|
16318 POLL_INTERVAL: 40, |
|
16319 |
|
16320 /** |
|
16321 * addListener/removeListener can throw errors in unexpected scenarios. |
|
16322 * These errors are suppressed, the method returns false, and this property |
|
16323 * is set |
|
16324 * @property lastError |
|
16325 * @static |
|
16326 * @type Error |
|
16327 */ |
|
16328 lastError: null, |
|
16329 |
|
16330 |
|
16331 /** |
|
16332 * poll handle |
|
16333 * @property _interval |
|
16334 * @static |
|
16335 * @private |
|
16336 */ |
|
16337 _interval: null, |
|
16338 |
|
16339 /** |
|
16340 * document readystate poll handle |
|
16341 * @property _dri |
|
16342 * @static |
|
16343 * @private |
|
16344 */ |
|
16345 _dri: null, |
|
16346 |
|
16347 /** |
|
16348 * True when the document is initially usable |
|
16349 * @property DOMReady |
|
16350 * @type boolean |
|
16351 * @static |
|
16352 */ |
|
16353 DOMReady: false, |
|
16354 |
|
16355 /** |
|
16356 * @method startInterval |
|
16357 * @static |
|
16358 * @private |
|
16359 */ |
|
16360 startInterval: function() { |
|
16361 if (!Event._interval) { |
|
16362 Event._interval = setInterval(Event._poll, Event.POLL_INTERVAL); |
|
16363 } |
|
16364 }, |
|
16365 |
|
16366 /** |
|
16367 * Executes the supplied callback when the item with the supplied |
|
16368 * id is found. This is meant to be used to execute behavior as |
|
16369 * soon as possible as the page loads. If you use this after the |
|
16370 * initial page load it will poll for a fixed time for the element. |
|
16371 * The number of times it will poll and the frequency are |
|
16372 * configurable. By default it will poll for 10 seconds. |
|
16373 * |
|
16374 * <p>The callback is executed with a single parameter: |
|
16375 * the custom object parameter, if provided.</p> |
|
16376 * |
|
16377 * @method onAvailable |
|
16378 * |
|
16379 * @param {string||string[]} id the id of the element, or an array |
|
16380 * of ids to look for. |
|
16381 * @param {function} fn what to execute when the element is found. |
|
16382 * @param {object} p_obj an optional object to be passed back as |
|
16383 * a parameter to fn. |
|
16384 * @param {boolean|object} p_override If set to true, fn will execute |
|
16385 * in the context of p_obj, if set to an object it |
|
16386 * will execute in the context of that object |
|
16387 * @param checkContent {boolean} check child node readiness (onContentReady) |
|
16388 * @static |
|
16389 * @deprecated Use Y.on("available") |
|
16390 */ |
|
16391 // @TODO fix arguments |
|
16392 onAvailable: function(id, fn, p_obj, p_override, checkContent, compat) { |
|
16393 |
|
16394 var a = Y.Array(id), i, availHandle; |
|
16395 |
|
16396 // Y.log('onAvailable registered for: ' + id); |
|
16397 |
|
16398 for (i=0; i<a.length; i=i+1) { |
|
16399 _avail.push({ |
|
16400 id: a[i], |
|
16401 fn: fn, |
|
16402 obj: p_obj, |
|
16403 override: p_override, |
|
16404 checkReady: checkContent, |
|
16405 compat: compat |
|
16406 }); |
|
16407 } |
|
16408 _retryCount = this.POLL_RETRYS; |
|
16409 |
|
16410 // We want the first test to be immediate, but async |
|
16411 setTimeout(Event._poll, 0); |
|
16412 |
|
16413 availHandle = new Y.EventHandle({ |
|
16414 |
|
16415 _delete: function() { |
|
16416 // set by the event system for lazy DOM listeners |
|
16417 if (availHandle.handle) { |
|
16418 availHandle.handle.detach(); |
|
16419 return; |
|
16420 } |
|
16421 |
|
16422 var i, j; |
|
16423 |
|
16424 // otherwise try to remove the onAvailable listener(s) |
|
16425 for (i = 0; i < a.length; i++) { |
|
16426 for (j = 0; j < _avail.length; j++) { |
|
16427 if (a[i] === _avail[j].id) { |
|
16428 _avail.splice(j, 1); |
|
16429 } |
|
16430 } |
|
16431 } |
|
16432 } |
|
16433 |
|
16434 }); |
|
16435 |
|
16436 return availHandle; |
|
16437 }, |
|
16438 |
|
16439 /** |
|
16440 * Works the same way as onAvailable, but additionally checks the |
|
16441 * state of sibling elements to determine if the content of the |
|
16442 * available element is safe to modify. |
|
16443 * |
|
16444 * <p>The callback is executed with a single parameter: |
|
16445 * the custom object parameter, if provided.</p> |
|
16446 * |
|
16447 * @method onContentReady |
|
16448 * |
|
16449 * @param {string} id the id of the element to look for. |
|
16450 * @param {function} fn what to execute when the element is ready. |
|
16451 * @param {object} obj an optional object to be passed back as |
|
16452 * a parameter to fn. |
|
16453 * @param {boolean|object} override If set to true, fn will execute |
|
16454 * in the context of p_obj. If an object, fn will |
|
16455 * exectute in the context of that object |
|
16456 * |
|
16457 * @static |
|
16458 * @deprecated Use Y.on("contentready") |
|
16459 */ |
|
16460 // @TODO fix arguments |
|
16461 onContentReady: function(id, fn, obj, override, compat) { |
|
16462 return Event.onAvailable(id, fn, obj, override, true, compat); |
|
16463 }, |
|
16464 |
|
16465 /** |
|
16466 * Adds an event listener |
|
16467 * |
|
16468 * @method attach |
|
16469 * |
|
16470 * @param {String} type The type of event to append |
|
16471 * @param {Function} fn The method the event invokes |
|
16472 * @param {String|HTMLElement|Array|NodeList} el An id, an element |
|
16473 * reference, or a collection of ids and/or elements to assign the |
|
16474 * listener to. |
|
16475 * @param {Object} context optional context object |
|
16476 * @param {Boolean|object} args 0..n arguments to pass to the callback |
|
16477 * @return {EventHandle} an object to that can be used to detach the listener |
|
16478 * |
|
16479 * @static |
|
16480 */ |
|
16481 |
|
16482 attach: function(type, fn, el, context) { |
|
16483 return Event._attach(Y.Array(arguments, 0, true)); |
|
16484 }, |
|
16485 |
|
16486 _createWrapper: function (el, type, capture, compat, facade) { |
|
16487 |
|
16488 var cewrapper, |
|
16489 ek = Y.stamp(el), |
|
16490 key = 'event:' + ek + type; |
|
16491 |
|
16492 if (false === facade) { |
|
16493 key += 'native'; |
|
16494 } |
|
16495 if (capture) { |
|
16496 key += 'capture'; |
|
16497 } |
|
16498 |
|
16499 |
|
16500 cewrapper = _wrappers[key]; |
|
16501 |
|
16502 |
|
16503 if (!cewrapper) { |
|
16504 // create CE wrapper |
|
16505 cewrapper = Y.publish(key, { |
|
16506 silent: true, |
|
16507 bubbles: false, |
|
16508 emitFacade:false, |
|
16509 contextFn: function() { |
|
16510 if (compat) { |
|
16511 return cewrapper.el; |
|
16512 } else { |
|
16513 cewrapper.nodeRef = cewrapper.nodeRef || Y.one(cewrapper.el); |
|
16514 return cewrapper.nodeRef; |
|
16515 } |
|
16516 } |
|
16517 }); |
|
16518 |
|
16519 cewrapper.overrides = {}; |
|
16520 |
|
16521 // for later removeListener calls |
|
16522 cewrapper.el = el; |
|
16523 cewrapper.key = key; |
|
16524 cewrapper.domkey = ek; |
|
16525 cewrapper.type = type; |
|
16526 cewrapper.fn = function(e) { |
|
16527 cewrapper.fire(Event.getEvent(e, el, (compat || (false === facade)))); |
|
16528 }; |
|
16529 cewrapper.capture = capture; |
|
16530 |
|
16531 if (el == win && type == "load") { |
|
16532 // window load happens once |
|
16533 cewrapper.fireOnce = true; |
|
16534 _windowLoadKey = key; |
|
16535 } |
|
16536 cewrapper._delete = _deleteAndClean; |
|
16537 |
|
16538 _wrappers[key] = cewrapper; |
|
16539 _el_events[ek] = _el_events[ek] || {}; |
|
16540 _el_events[ek][key] = cewrapper; |
|
16541 |
|
16542 add(el, type, cewrapper.fn, capture); |
|
16543 } |
|
16544 |
|
16545 return cewrapper; |
|
16546 |
|
16547 }, |
|
16548 |
|
16549 _attach: function(args, conf) { |
|
16550 |
|
16551 var compat, |
|
16552 handles, oEl, cewrapper, context, |
|
16553 fireNow = false, ret, |
|
16554 type = args[0], |
|
16555 fn = args[1], |
|
16556 el = args[2] || win, |
|
16557 facade = conf && conf.facade, |
|
16558 capture = conf && conf.capture, |
|
16559 overrides = conf && conf.overrides; |
|
16560 |
|
16561 if (args[args.length-1] === COMPAT_ARG) { |
|
16562 compat = true; |
|
16563 } |
|
16564 |
|
16565 if (!fn || !fn.call) { |
|
16566 // throw new TypeError(type + " attach call failed, callback undefined"); |
|
16567 Y.log(type + " attach call failed, invalid callback", "error", "event"); |
|
16568 return false; |
|
16569 } |
|
16570 |
|
16571 // The el argument can be an array of elements or element ids. |
|
16572 if (shouldIterate(el)) { |
|
16573 |
|
16574 handles=[]; |
|
16575 |
|
16576 Y.each(el, function(v, k) { |
|
16577 args[2] = v; |
|
16578 handles.push(Event._attach(args.slice(), conf)); |
|
16579 }); |
|
16580 |
|
16581 // return (handles.length === 1) ? handles[0] : handles; |
|
16582 return new Y.EventHandle(handles); |
|
16583 |
|
16584 // If the el argument is a string, we assume it is |
|
16585 // actually the id of the element. If the page is loaded |
|
16586 // we convert el to the actual element, otherwise we |
|
16587 // defer attaching the event until the element is |
|
16588 // ready |
|
16589 } else if (Y.Lang.isString(el)) { |
|
16590 |
|
16591 // oEl = (compat) ? Y.DOM.byId(el) : Y.Selector.query(el); |
|
16592 |
|
16593 if (compat) { |
|
16594 oEl = Y.DOM.byId(el); |
|
16595 } else { |
|
16596 |
|
16597 oEl = Y.Selector.query(el); |
|
16598 |
|
16599 switch (oEl.length) { |
|
16600 case 0: |
|
16601 oEl = null; |
|
16602 break; |
|
16603 case 1: |
|
16604 oEl = oEl[0]; |
|
16605 break; |
|
16606 default: |
|
16607 args[2] = oEl; |
|
16608 return Event._attach(args, conf); |
|
16609 } |
|
16610 } |
|
16611 |
|
16612 if (oEl) { |
|
16613 |
|
16614 el = oEl; |
|
16615 |
|
16616 // Not found = defer adding the event until the element is available |
|
16617 } else { |
|
16618 |
|
16619 // Y.log(el + ' not found'); |
|
16620 ret = Event.onAvailable(el, function() { |
|
16621 // Y.log('lazy attach: ' + args); |
|
16622 |
|
16623 ret.handle = Event._attach(args, conf); |
|
16624 |
|
16625 }, Event, true, false, compat); |
|
16626 |
|
16627 return ret; |
|
16628 |
|
16629 } |
|
16630 } |
|
16631 |
|
16632 // Element should be an html element or node |
|
16633 if (!el) { |
|
16634 Y.log("unable to attach event " + type, "warn", "event"); |
|
16635 return false; |
|
16636 } |
|
16637 |
|
16638 if (Y.Node && Y.instanceOf(el, Y.Node)) { |
|
16639 el = Y.Node.getDOMNode(el); |
|
16640 } |
|
16641 |
|
16642 cewrapper = Event._createWrapper(el, type, capture, compat, facade); |
|
16643 if (overrides) { |
|
16644 Y.mix(cewrapper.overrides, overrides); |
|
16645 } |
|
16646 |
|
16647 if (el == win && type == "load") { |
|
16648 |
|
16649 // if the load is complete, fire immediately. |
|
16650 // all subscribers, including the current one |
|
16651 // will be notified. |
|
16652 if (YUI.Env.windowLoaded) { |
|
16653 fireNow = true; |
|
16654 } |
|
16655 } |
|
16656 |
|
16657 if (compat) { |
|
16658 args.pop(); |
|
16659 } |
|
16660 |
|
16661 context = args[3]; |
|
16662 |
|
16663 // set context to the Node if not specified |
|
16664 // ret = cewrapper.on.apply(cewrapper, trimmedArgs); |
|
16665 ret = cewrapper._on(fn, context, (args.length > 4) ? args.slice(4) : null); |
|
16666 |
|
16667 if (fireNow) { |
|
16668 cewrapper.fire(); |
|
16669 } |
|
16670 |
|
16671 return ret; |
|
16672 |
|
16673 }, |
|
16674 |
|
16675 /** |
|
16676 * Removes an event listener. Supports the signature the event was bound |
|
16677 * with, but the preferred way to remove listeners is using the handle |
|
16678 * that is returned when using Y.on |
|
16679 * |
|
16680 * @method detach |
|
16681 * |
|
16682 * @param {String} type the type of event to remove. |
|
16683 * @param {Function} fn the method the event invokes. If fn is |
|
16684 * undefined, then all event handlers for the type of event are |
|
16685 * removed. |
|
16686 * @param {String|HTMLElement|Array|NodeList|EventHandle} el An |
|
16687 * event handle, an id, an element reference, or a collection |
|
16688 * of ids and/or elements to remove the listener from. |
|
16689 * @return {boolean} true if the unbind was successful, false otherwise. |
|
16690 * @static |
|
16691 */ |
|
16692 detach: function(type, fn, el, obj) { |
|
16693 |
|
16694 var args=Y.Array(arguments, 0, true), compat, l, ok, i, |
|
16695 id, ce; |
|
16696 |
|
16697 if (args[args.length-1] === COMPAT_ARG) { |
|
16698 compat = true; |
|
16699 // args.pop(); |
|
16700 } |
|
16701 |
|
16702 if (type && type.detach) { |
|
16703 return type.detach(); |
|
16704 } |
|
16705 |
|
16706 // The el argument can be a string |
|
16707 if (typeof el == "string") { |
|
16708 |
|
16709 // el = (compat) ? Y.DOM.byId(el) : Y.all(el); |
|
16710 if (compat) { |
|
16711 el = Y.DOM.byId(el); |
|
16712 } else { |
|
16713 el = Y.Selector.query(el); |
|
16714 l = el.length; |
|
16715 if (l < 1) { |
|
16716 el = null; |
|
16717 } else if (l == 1) { |
|
16718 el = el[0]; |
|
16719 } |
|
16720 } |
|
16721 // return Event.detach.apply(Event, args); |
|
16722 } |
|
16723 |
|
16724 if (!el) { |
|
16725 return false; |
|
16726 } |
|
16727 |
|
16728 if (el.detach) { |
|
16729 args.splice(2, 1); |
|
16730 return el.detach.apply(el, args); |
|
16731 // The el argument can be an array of elements or element ids. |
|
16732 } else if (shouldIterate(el)) { |
|
16733 ok = true; |
|
16734 for (i=0, l=el.length; i<l; ++i) { |
|
16735 args[2] = el[i]; |
|
16736 ok = ( Y.Event.detach.apply(Y.Event, args) && ok ); |
|
16737 } |
|
16738 |
|
16739 return ok; |
|
16740 } |
|
16741 |
|
16742 if (!type || !fn || !fn.call) { |
|
16743 return Event.purgeElement(el, false, type); |
|
16744 } |
|
16745 |
|
16746 id = 'event:' + Y.stamp(el) + type; |
|
16747 ce = _wrappers[id]; |
|
16748 |
|
16749 if (ce) { |
|
16750 return ce.detach(fn); |
|
16751 } else { |
|
16752 return false; |
|
16753 } |
|
16754 |
|
16755 }, |
|
16756 |
|
16757 /** |
|
16758 * Finds the event in the window object, the caller's arguments, or |
|
16759 * in the arguments of another method in the callstack. This is |
|
16760 * executed automatically for events registered through the event |
|
16761 * manager, so the implementer should not normally need to execute |
|
16762 * this function at all. |
|
16763 * @method getEvent |
|
16764 * @param {Event} e the event parameter from the handler |
|
16765 * @param {HTMLElement} el the element the listener was attached to |
|
16766 * @return {Event} the event |
|
16767 * @static |
|
16768 */ |
|
16769 getEvent: function(e, el, noFacade) { |
|
16770 var ev = e || win.event; |
|
16771 |
|
16772 return (noFacade) ? ev : |
|
16773 new Y.DOMEventFacade(ev, el, _wrappers['event:' + Y.stamp(el) + e.type]); |
|
16774 }, |
|
16775 |
|
16776 /** |
|
16777 * Generates an unique ID for the element if it does not already |
|
16778 * have one. |
|
16779 * @method generateId |
|
16780 * @param el the element to create the id for |
|
16781 * @return {string} the resulting id of the element |
|
16782 * @static |
|
16783 */ |
|
16784 generateId: function(el) { |
|
16785 return Y.DOM.generateID(el); |
|
16786 }, |
|
16787 |
|
16788 /** |
|
16789 * We want to be able to use getElementsByTagName as a collection |
|
16790 * to attach a group of events to. Unfortunately, different |
|
16791 * browsers return different types of collections. This function |
|
16792 * tests to determine if the object is array-like. It will also |
|
16793 * fail if the object is an array, but is empty. |
|
16794 * @method _isValidCollection |
|
16795 * @param o the object to test |
|
16796 * @return {boolean} true if the object is array-like and populated |
|
16797 * @deprecated was not meant to be used directly |
|
16798 * @static |
|
16799 * @private |
|
16800 */ |
|
16801 _isValidCollection: shouldIterate, |
|
16802 |
|
16803 /** |
|
16804 * hook up any deferred listeners |
|
16805 * @method _load |
|
16806 * @static |
|
16807 * @private |
|
16808 */ |
|
16809 _load: function(e) { |
|
16810 if (!_loadComplete) { |
|
16811 // Y.log('Load Complete', 'info', 'event'); |
|
16812 _loadComplete = true; |
|
16813 |
|
16814 // Just in case DOMReady did not go off for some reason |
|
16815 // E._ready(); |
|
16816 if (Y.fire) { |
|
16817 Y.fire(EVENT_READY); |
|
16818 } |
|
16819 |
|
16820 // Available elements may not have been detected before the |
|
16821 // window load event fires. Try to find them now so that the |
|
16822 // the user is more likely to get the onAvailable notifications |
|
16823 // before the window load notification |
|
16824 Event._poll(); |
|
16825 } |
|
16826 }, |
|
16827 |
|
16828 /** |
|
16829 * Polling function that runs before the onload event fires, |
|
16830 * attempting to attach to DOM Nodes as soon as they are |
|
16831 * available |
|
16832 * @method _poll |
|
16833 * @static |
|
16834 * @private |
|
16835 */ |
|
16836 _poll: function() { |
|
16837 if (Event.locked) { |
|
16838 return; |
|
16839 } |
|
16840 |
|
16841 if (Y.UA.ie && !YUI.Env.DOMReady) { |
|
16842 // Hold off if DOMReady has not fired and check current |
|
16843 // readyState to protect against the IE operation aborted |
|
16844 // issue. |
|
16845 Event.startInterval(); |
|
16846 return; |
|
16847 } |
|
16848 |
|
16849 Event.locked = true; |
|
16850 |
|
16851 // Y.log.debug("poll"); |
|
16852 // keep trying until after the page is loaded. We need to |
|
16853 // check the page load state prior to trying to bind the |
|
16854 // elements so that we can be certain all elements have been |
|
16855 // tested appropriately |
|
16856 var i, len, item, el, notAvail, executeItem, |
|
16857 tryAgain = !_loadComplete; |
|
16858 |
|
16859 if (!tryAgain) { |
|
16860 tryAgain = (_retryCount > 0); |
|
16861 } |
|
16862 |
|
16863 // onAvailable |
|
16864 notAvail = []; |
|
16865 |
|
16866 executeItem = function (el, item) { |
|
16867 var context, ov = item.override; |
|
16868 try { |
|
16869 if (item.compat) { |
|
16870 if (item.override) { |
|
16871 if (ov === true) { |
|
16872 context = item.obj; |
|
16873 } else { |
|
16874 context = ov; |
|
16875 } |
|
16876 } else { |
|
16877 context = el; |
|
16878 } |
|
16879 item.fn.call(context, item.obj); |
|
16880 } else { |
|
16881 context = item.obj || Y.one(el); |
|
16882 item.fn.apply(context, (Y.Lang.isArray(ov)) ? ov : []); |
|
16883 } |
|
16884 } catch (e) { |
|
16885 Y.log("Error in available or contentReady callback", 'error', 'event'); |
|
16886 } |
|
16887 }; |
|
16888 |
|
16889 // onAvailable |
|
16890 for (i=0,len=_avail.length; i<len; ++i) { |
|
16891 item = _avail[i]; |
|
16892 if (item && !item.checkReady) { |
|
16893 |
|
16894 // el = (item.compat) ? Y.DOM.byId(item.id) : Y.one(item.id); |
|
16895 el = (item.compat) ? Y.DOM.byId(item.id) : Y.Selector.query(item.id, null, true); |
|
16896 |
|
16897 if (el) { |
|
16898 // Y.log('avail: ' + el); |
|
16899 executeItem(el, item); |
|
16900 _avail[i] = null; |
|
16901 } else { |
|
16902 // Y.log('NOT avail: ' + el); |
|
16903 notAvail.push(item); |
|
16904 } |
|
16905 } |
|
16906 } |
|
16907 |
|
16908 // onContentReady |
|
16909 for (i=0,len=_avail.length; i<len; ++i) { |
|
16910 item = _avail[i]; |
|
16911 if (item && item.checkReady) { |
|
16912 |
|
16913 // el = (item.compat) ? Y.DOM.byId(item.id) : Y.one(item.id); |
|
16914 el = (item.compat) ? Y.DOM.byId(item.id) : Y.Selector.query(item.id, null, true); |
|
16915 |
|
16916 if (el) { |
|
16917 // The element is available, but not necessarily ready |
|
16918 // @todo should we test parentNode.nextSibling? |
|
16919 if (_loadComplete || (el.get && el.get('nextSibling')) || el.nextSibling) { |
|
16920 executeItem(el, item); |
|
16921 _avail[i] = null; |
|
16922 } |
|
16923 } else { |
|
16924 notAvail.push(item); |
|
16925 } |
|
16926 } |
|
16927 } |
|
16928 |
|
16929 _retryCount = (notAvail.length === 0) ? 0 : _retryCount - 1; |
|
16930 |
|
16931 if (tryAgain) { |
|
16932 // we may need to strip the nulled out items here |
|
16933 Event.startInterval(); |
|
16934 } else { |
|
16935 clearInterval(Event._interval); |
|
16936 Event._interval = null; |
|
16937 } |
|
16938 |
|
16939 Event.locked = false; |
|
16940 |
|
16941 return; |
|
16942 |
|
16943 }, |
|
16944 |
|
16945 /** |
|
16946 * Removes all listeners attached to the given element via addListener. |
|
16947 * Optionally, the node's children can also be purged. |
|
16948 * Optionally, you can specify a specific type of event to remove. |
|
16949 * @method purgeElement |
|
16950 * @param {HTMLElement} el the element to purge |
|
16951 * @param {boolean} recurse recursively purge this element's children |
|
16952 * as well. Use with caution. |
|
16953 * @param {string} type optional type of listener to purge. If |
|
16954 * left out, all listeners will be removed |
|
16955 * @static |
|
16956 */ |
|
16957 purgeElement: function(el, recurse, type) { |
|
16958 // var oEl = (Y.Lang.isString(el)) ? Y.one(el) : el, |
|
16959 var oEl = (Y.Lang.isString(el)) ? Y.Selector.query(el, null, true) : el, |
|
16960 lis = Event.getListeners(oEl, type), i, len, children, child; |
|
16961 |
|
16962 if (recurse && oEl) { |
|
16963 lis = lis || []; |
|
16964 children = Y.Selector.query('*', oEl); |
|
16965 len = children.length; |
|
16966 for (i = 0; i < len; ++i) { |
|
16967 child = Event.getListeners(children[i], type); |
|
16968 if (child) { |
|
16969 lis = lis.concat(child); |
|
16970 } |
|
16971 } |
|
16972 } |
|
16973 |
|
16974 if (lis) { |
|
16975 for (i = 0, len = lis.length; i < len; ++i) { |
|
16976 lis[i].detachAll(); |
|
16977 } |
|
16978 } |
|
16979 |
|
16980 }, |
|
16981 |
|
16982 /** |
|
16983 * Removes all object references and the DOM proxy subscription for |
|
16984 * a given event for a DOM node. |
|
16985 * |
|
16986 * @method _clean |
|
16987 * @param wrapper {CustomEvent} Custom event proxy for the DOM |
|
16988 * subscription |
|
16989 * @private |
|
16990 * @static |
|
16991 * @since 3.4.0 |
|
16992 */ |
|
16993 _clean: function (wrapper) { |
|
16994 var key = wrapper.key, |
|
16995 domkey = wrapper.domkey; |
|
16996 |
|
16997 remove(wrapper.el, wrapper.type, wrapper.fn, wrapper.capture); |
|
16998 delete _wrappers[key]; |
|
16999 delete Y._yuievt.events[key]; |
|
17000 if (_el_events[domkey]) { |
|
17001 delete _el_events[domkey][key]; |
|
17002 if (!Y.Object.size(_el_events[domkey])) { |
|
17003 delete _el_events[domkey]; |
|
17004 } |
|
17005 } |
|
17006 }, |
|
17007 |
|
17008 /** |
|
17009 * Returns all listeners attached to the given element via addListener. |
|
17010 * Optionally, you can specify a specific type of event to return. |
|
17011 * @method getListeners |
|
17012 * @param el {HTMLElement|string} the element or element id to inspect |
|
17013 * @param type {string} optional type of listener to return. If |
|
17014 * left out, all listeners will be returned |
|
17015 * @return {CustomEvent} the custom event wrapper for the DOM event(s) |
|
17016 * @static |
|
17017 */ |
|
17018 getListeners: function(el, type) { |
|
17019 var ek = Y.stamp(el, true), evts = _el_events[ek], |
|
17020 results=[] , key = (type) ? 'event:' + ek + type : null, |
|
17021 adapters = _eventenv.plugins; |
|
17022 |
|
17023 if (!evts) { |
|
17024 return null; |
|
17025 } |
|
17026 |
|
17027 if (key) { |
|
17028 // look for synthetic events |
|
17029 if (adapters[type] && adapters[type].eventDef) { |
|
17030 key += '_synth'; |
|
17031 } |
|
17032 |
|
17033 if (evts[key]) { |
|
17034 results.push(evts[key]); |
|
17035 } |
|
17036 |
|
17037 // get native events as well |
|
17038 key += 'native'; |
|
17039 if (evts[key]) { |
|
17040 results.push(evts[key]); |
|
17041 } |
|
17042 |
|
17043 } else { |
|
17044 Y.each(evts, function(v, k) { |
|
17045 results.push(v); |
|
17046 }); |
|
17047 } |
|
17048 |
|
17049 return (results.length) ? results : null; |
|
17050 }, |
|
17051 |
|
17052 /** |
|
17053 * Removes all listeners registered by pe.event. Called |
|
17054 * automatically during the unload event. |
|
17055 * @method _unload |
|
17056 * @static |
|
17057 * @private |
|
17058 */ |
|
17059 _unload: function(e) { |
|
17060 Y.each(_wrappers, function(v, k) { |
|
17061 if (v.type == 'unload') { |
|
17062 v.fire(e); |
|
17063 } |
|
17064 v.detachAll(); |
|
17065 }); |
|
17066 remove(win, "unload", onUnload); |
|
17067 }, |
|
17068 |
|
17069 /** |
|
17070 * Adds a DOM event directly without the caching, cleanup, context adj, etc |
|
17071 * |
|
17072 * @method nativeAdd |
|
17073 * @param {HTMLElement} el the element to bind the handler to |
|
17074 * @param {string} type the type of event handler |
|
17075 * @param {function} fn the callback to invoke |
|
17076 * @param {boolen} capture capture or bubble phase |
|
17077 * @static |
|
17078 * @private |
|
17079 */ |
|
17080 nativeAdd: add, |
|
17081 |
|
17082 /** |
|
17083 * Basic remove listener |
|
17084 * |
|
17085 * @method nativeRemove |
|
17086 * @param {HTMLElement} el the element to bind the handler to |
|
17087 * @param {string} type the type of event handler |
|
17088 * @param {function} fn the callback to invoke |
|
17089 * @param {boolen} capture capture or bubble phase |
|
17090 * @static |
|
17091 * @private |
|
17092 */ |
|
17093 nativeRemove: remove |
|
17094 }; |
|
17095 |
|
17096 }(); |
|
17097 |
|
17098 Y.Event = Event; |
|
17099 |
|
17100 if (config.injected || YUI.Env.windowLoaded) { |
|
17101 onLoad(); |
|
17102 } else { |
|
17103 add(win, "load", onLoad); |
|
17104 } |
|
17105 |
|
17106 // Process onAvailable/onContentReady items when when the DOM is ready in IE |
|
17107 if (Y.UA.ie) { |
|
17108 Y.on(EVENT_READY, Event._poll); |
|
17109 } |
|
17110 |
|
17111 try { |
|
17112 add(win, "unload", onUnload); |
|
17113 } catch(e) { |
|
17114 /*jshint maxlen:300*/ |
|
17115 Y.log("Registering unload listener failed. This is known to happen in Chrome Packaged Apps and Extensions, which don't support unload, and don't provide a way to test for support", "warn", "event-base"); |
|
17116 } |
|
17117 |
|
17118 Event.Custom = Y.CustomEvent; |
|
17119 Event.Subscriber = Y.Subscriber; |
|
17120 Event.Target = Y.EventTarget; |
|
17121 Event.Handle = Y.EventHandle; |
|
17122 Event.Facade = Y.EventFacade; |
|
17123 |
|
17124 Event._poll(); |
|
17125 |
|
17126 }()); |
|
17127 |
|
17128 /** |
|
17129 * DOM event listener abstraction layer |
|
17130 * @module event |
|
17131 * @submodule event-base |
|
17132 */ |
|
17133 |
|
17134 /** |
|
17135 * Executes the callback as soon as the specified element |
|
17136 * is detected in the DOM. This function expects a selector |
|
17137 * string for the element(s) to detect. If you already have |
|
17138 * an element reference, you don't need this event. |
|
17139 * @event available |
|
17140 * @param type {string} 'available' |
|
17141 * @param fn {function} the callback function to execute. |
|
17142 * @param el {string} an selector for the element(s) to attach |
|
17143 * @param context optional argument that specifies what 'this' refers to. |
|
17144 * @param args* 0..n additional arguments to pass on to the callback function. |
|
17145 * These arguments will be added after the event object. |
|
17146 * @return {EventHandle} the detach handle |
|
17147 * @for YUI |
|
17148 */ |
|
17149 Y.Env.evt.plugins.available = { |
|
17150 on: function(type, fn, id, o) { |
|
17151 var a = arguments.length > 4 ? Y.Array(arguments, 4, true) : null; |
|
17152 return Y.Event.onAvailable.call(Y.Event, id, fn, o, a); |
|
17153 } |
|
17154 }; |
|
17155 |
|
17156 /** |
|
17157 * Executes the callback as soon as the specified element |
|
17158 * is detected in the DOM with a nextSibling property |
|
17159 * (indicating that the element's children are available). |
|
17160 * This function expects a selector |
|
17161 * string for the element(s) to detect. If you already have |
|
17162 * an element reference, you don't need this event. |
|
17163 * @event contentready |
|
17164 * @param type {string} 'contentready' |
|
17165 * @param fn {function} the callback function to execute. |
|
17166 * @param el {string} an selector for the element(s) to attach. |
|
17167 * @param context optional argument that specifies what 'this' refers to. |
|
17168 * @param args* 0..n additional arguments to pass on to the callback function. |
|
17169 * These arguments will be added after the event object. |
|
17170 * @return {EventHandle} the detach handle |
|
17171 * @for YUI |
|
17172 */ |
|
17173 Y.Env.evt.plugins.contentready = { |
|
17174 on: function(type, fn, id, o) { |
|
17175 var a = arguments.length > 4 ? Y.Array(arguments, 4, true) : null; |
|
17176 return Y.Event.onContentReady.call(Y.Event, id, fn, o, a); |
|
17177 } |
|
17178 }; |
|
17179 |
|
17180 |
|
17181 }, '@VERSION@', {"requires": ["event-custom-base"]}); |
|
17182 (function() { |
|
17183 |
|
17184 var stateChangeListener, |
|
17185 GLOBAL_ENV = YUI.Env, |
|
17186 config = YUI.config, |
|
17187 doc = config.doc, |
|
17188 docElement = doc && doc.documentElement, |
|
17189 EVENT_NAME = 'onreadystatechange', |
|
17190 pollInterval = config.pollInterval || 40; |
|
17191 |
|
17192 if (docElement.doScroll && !GLOBAL_ENV._ieready) { |
|
17193 GLOBAL_ENV._ieready = function() { |
|
17194 GLOBAL_ENV._ready(); |
|
17195 }; |
|
17196 |
|
17197 /*! DOMReady: based on work by: Dean Edwards/John Resig/Matthias Miller/Diego Perini */ |
|
17198 // Internet Explorer: use the doScroll() method on the root element. |
|
17199 // This isolates what appears to be a safe moment to manipulate the |
|
17200 // DOM prior to when the document's readyState suggests it is safe to do so. |
|
17201 if (self !== self.top) { |
|
17202 stateChangeListener = function() { |
|
17203 if (doc.readyState == 'complete') { |
|
17204 GLOBAL_ENV.remove(doc, EVENT_NAME, stateChangeListener); |
|
17205 GLOBAL_ENV.ieready(); |
|
17206 } |
|
17207 }; |
|
17208 GLOBAL_ENV.add(doc, EVENT_NAME, stateChangeListener); |
|
17209 } else { |
|
17210 GLOBAL_ENV._dri = setInterval(function() { |
|
17211 try { |
|
17212 docElement.doScroll('left'); |
|
17213 clearInterval(GLOBAL_ENV._dri); |
|
17214 GLOBAL_ENV._dri = null; |
|
17215 GLOBAL_ENV._ieready(); |
|
17216 } catch (domNotReady) { } |
|
17217 }, pollInterval); |
|
17218 } |
|
17219 } |
|
17220 |
|
17221 })(); |
|
17222 YUI.add('event-base-ie', function (Y, NAME) { |
|
17223 |
|
17224 /* |
|
17225 * Custom event engine, DOM event listener abstraction layer, synthetic DOM |
|
17226 * events. |
|
17227 * @module event |
|
17228 * @submodule event-base |
|
17229 */ |
|
17230 |
|
17231 function IEEventFacade() { |
|
17232 // IEEventFacade.superclass.constructor.apply(this, arguments); |
|
17233 Y.DOM2EventFacade.apply(this, arguments); |
|
17234 } |
|
17235 |
|
17236 /* |
|
17237 * (intentially left out of API docs) |
|
17238 * Alternate Facade implementation that is based on Object.defineProperty, which |
|
17239 * is partially supported in IE8. Properties that involve setup work are |
|
17240 * deferred to temporary getters using the static _define method. |
|
17241 */ |
|
17242 function IELazyFacade(e) { |
|
17243 var proxy = Y.config.doc.createEventObject(e), |
|
17244 proto = IELazyFacade.prototype; |
|
17245 |
|
17246 // TODO: necessary? |
|
17247 proxy.hasOwnProperty = function () { return true; }; |
|
17248 |
|
17249 proxy.init = proto.init; |
|
17250 proxy.halt = proto.halt; |
|
17251 proxy.preventDefault = proto.preventDefault; |
|
17252 proxy.stopPropagation = proto.stopPropagation; |
|
17253 proxy.stopImmediatePropagation = proto.stopImmediatePropagation; |
|
17254 |
|
17255 Y.DOM2EventFacade.apply(proxy, arguments); |
|
17256 |
|
17257 return proxy; |
|
17258 } |
|
17259 |
|
17260 |
|
17261 var imp = Y.config.doc && Y.config.doc.implementation, |
|
17262 useLazyFacade = Y.config.lazyEventFacade, |
|
17263 |
|
17264 buttonMap = { |
|
17265 0: 1, // left click |
|
17266 4: 2, // middle click |
|
17267 2: 3 // right click |
|
17268 }, |
|
17269 relatedTargetMap = { |
|
17270 mouseout: 'toElement', |
|
17271 mouseover: 'fromElement' |
|
17272 }, |
|
17273 |
|
17274 resolve = Y.DOM2EventFacade.resolve, |
|
17275 |
|
17276 proto = { |
|
17277 init: function() { |
|
17278 |
|
17279 IEEventFacade.superclass.init.apply(this, arguments); |
|
17280 |
|
17281 var e = this._event, |
|
17282 x, y, d, b, de, t; |
|
17283 |
|
17284 this.target = resolve(e.srcElement); |
|
17285 |
|
17286 if (('clientX' in e) && (!x) && (0 !== x)) { |
|
17287 x = e.clientX; |
|
17288 y = e.clientY; |
|
17289 |
|
17290 d = Y.config.doc; |
|
17291 b = d.body; |
|
17292 de = d.documentElement; |
|
17293 |
|
17294 x += (de.scrollLeft || (b && b.scrollLeft) || 0); |
|
17295 y += (de.scrollTop || (b && b.scrollTop) || 0); |
|
17296 |
|
17297 this.pageX = x; |
|
17298 this.pageY = y; |
|
17299 } |
|
17300 |
|
17301 if (e.type == "mouseout") { |
|
17302 t = e.toElement; |
|
17303 } else if (e.type == "mouseover") { |
|
17304 t = e.fromElement; |
|
17305 } |
|
17306 |
|
17307 // fallback to t.relatedTarget to support simulated events. |
|
17308 // IE doesn't support setting toElement or fromElement on generic |
|
17309 // events, so Y.Event.simulate sets relatedTarget instead. |
|
17310 this.relatedTarget = resolve(t || e.relatedTarget); |
|
17311 |
|
17312 // which should contain the unicode key code if this is a key event. |
|
17313 // For click events, which is normalized for which mouse button was |
|
17314 // clicked. |
|
17315 this.which = // chained assignment |
|
17316 this.button = e.keyCode || buttonMap[e.button] || e.button; |
|
17317 }, |
|
17318 |
|
17319 stopPropagation: function() { |
|
17320 this._event.cancelBubble = true; |
|
17321 this._wrapper.stopped = 1; |
|
17322 this.stopped = 1; |
|
17323 }, |
|
17324 |
|
17325 stopImmediatePropagation: function() { |
|
17326 this.stopPropagation(); |
|
17327 this._wrapper.stopped = 2; |
|
17328 this.stopped = 2; |
|
17329 }, |
|
17330 |
|
17331 preventDefault: function(returnValue) { |
|
17332 this._event.returnValue = returnValue || false; |
|
17333 this._wrapper.prevented = 1; |
|
17334 this.prevented = 1; |
|
17335 } |
|
17336 }; |
|
17337 |
|
17338 Y.extend(IEEventFacade, Y.DOM2EventFacade, proto); |
|
17339 |
|
17340 Y.extend(IELazyFacade, Y.DOM2EventFacade, proto); |
|
17341 IELazyFacade.prototype.init = function () { |
|
17342 var e = this._event, |
|
17343 overrides = this._wrapper.overrides, |
|
17344 define = IELazyFacade._define, |
|
17345 lazyProperties = IELazyFacade._lazyProperties, |
|
17346 prop; |
|
17347 |
|
17348 this.altKey = e.altKey; |
|
17349 this.ctrlKey = e.ctrlKey; |
|
17350 this.metaKey = e.metaKey; |
|
17351 this.shiftKey = e.shiftKey; |
|
17352 this.type = (overrides && overrides.type) || e.type; |
|
17353 this.clientX = e.clientX; |
|
17354 this.clientY = e.clientY; |
|
17355 this.keyCode = // chained assignment |
|
17356 this.charCode = e.keyCode; |
|
17357 this.which = // chained assignment |
|
17358 this.button = e.keyCode || buttonMap[e.button] || e.button; |
|
17359 |
|
17360 for (prop in lazyProperties) { |
|
17361 if (lazyProperties.hasOwnProperty(prop)) { |
|
17362 define(this, prop, lazyProperties[prop]); |
|
17363 } |
|
17364 } |
|
17365 |
|
17366 if (this._touch) { |
|
17367 this._touch(e, this._currentTarget, this._wrapper); |
|
17368 } |
|
17369 }; |
|
17370 |
|
17371 IELazyFacade._lazyProperties = { |
|
17372 target: function () { |
|
17373 return resolve(this._event.srcElement); |
|
17374 }, |
|
17375 relatedTarget: function () { |
|
17376 var e = this._event, |
|
17377 targetProp = relatedTargetMap[e.type] || 'relatedTarget'; |
|
17378 |
|
17379 // fallback to t.relatedTarget to support simulated events. |
|
17380 // IE doesn't support setting toElement or fromElement on generic |
|
17381 // events, so Y.Event.simulate sets relatedTarget instead. |
|
17382 return resolve(e[targetProp] || e.relatedTarget); |
|
17383 }, |
|
17384 currentTarget: function () { |
|
17385 return resolve(this._currentTarget); |
|
17386 }, |
|
17387 |
|
17388 wheelDelta: function () { |
|
17389 var e = this._event; |
|
17390 |
|
17391 if (e.type === "mousewheel" || e.type === "DOMMouseScroll") { |
|
17392 return (e.detail) ? |
|
17393 (e.detail * -1) : |
|
17394 // wheelDelta between -80 and 80 result in -1 or 1 |
|
17395 Math.round(e.wheelDelta / 80) || ((e.wheelDelta < 0) ? -1 : 1); |
|
17396 } |
|
17397 }, |
|
17398 |
|
17399 pageX: function () { |
|
17400 var e = this._event, |
|
17401 val = e.pageX, |
|
17402 doc, bodyScroll, docScroll; |
|
17403 |
|
17404 if (val === undefined) { |
|
17405 doc = Y.config.doc; |
|
17406 bodyScroll = doc.body && doc.body.scrollLeft; |
|
17407 docScroll = doc.documentElement.scrollLeft; |
|
17408 |
|
17409 val = e.clientX + (docScroll || bodyScroll || 0); |
|
17410 } |
|
17411 |
|
17412 return val; |
|
17413 }, |
|
17414 pageY: function () { |
|
17415 var e = this._event, |
|
17416 val = e.pageY, |
|
17417 doc, bodyScroll, docScroll; |
|
17418 |
|
17419 if (val === undefined) { |
|
17420 doc = Y.config.doc; |
|
17421 bodyScroll = doc.body && doc.body.scrollTop; |
|
17422 docScroll = doc.documentElement.scrollTop; |
|
17423 |
|
17424 val = e.clientY + (docScroll || bodyScroll || 0); |
|
17425 } |
|
17426 |
|
17427 return val; |
|
17428 } |
|
17429 }; |
|
17430 |
|
17431 |
|
17432 /** |
|
17433 * Wrapper function for Object.defineProperty that creates a property whose |
|
17434 * value will be calulated only when asked for. After calculating the value, |
|
17435 * the getter wll be removed, so it will behave as a normal property beyond that |
|
17436 * point. A setter is also assigned so assigning to the property will clear |
|
17437 * the getter, so foo.prop = 'a'; foo.prop; won't trigger the getter, |
|
17438 * overwriting value 'a'. |
|
17439 * |
|
17440 * Used only by the DOMEventFacades used by IE8 when the YUI configuration |
|
17441 * <code>lazyEventFacade</code> is set to true. |
|
17442 * |
|
17443 * @method _define |
|
17444 * @param o {DOMObject} A DOM object to add the property to |
|
17445 * @param prop {String} The name of the new property |
|
17446 * @param valueFn {Function} The function that will return the initial, default |
|
17447 * value for the property. |
|
17448 * @static |
|
17449 * @private |
|
17450 */ |
|
17451 IELazyFacade._define = function (o, prop, valueFn) { |
|
17452 function val(v) { |
|
17453 var ret = (arguments.length) ? v : valueFn.call(this); |
|
17454 |
|
17455 delete o[prop]; |
|
17456 Object.defineProperty(o, prop, { |
|
17457 value: ret, |
|
17458 configurable: true, |
|
17459 writable: true |
|
17460 }); |
|
17461 return ret; |
|
17462 } |
|
17463 Object.defineProperty(o, prop, { |
|
17464 get: val, |
|
17465 set: val, |
|
17466 configurable: true |
|
17467 }); |
|
17468 }; |
|
17469 |
|
17470 if (imp && (!imp.hasFeature('Events', '2.0'))) { |
|
17471 if (useLazyFacade) { |
|
17472 // Make sure we can use the lazy facade logic |
|
17473 try { |
|
17474 Object.defineProperty(Y.config.doc.createEventObject(), 'z', {}); |
|
17475 } catch (e) { |
|
17476 useLazyFacade = false; |
|
17477 } |
|
17478 } |
|
17479 |
|
17480 Y.DOMEventFacade = (useLazyFacade) ? IELazyFacade : IEEventFacade; |
|
17481 } |
|
17482 |
|
17483 |
|
17484 }, '@VERSION@', {"requires": ["node-base"]}); |
|
17485 YUI.add('pluginhost-base', function (Y, NAME) { |
|
17486 |
|
17487 /** |
|
17488 * Provides the augmentable PluginHost interface, which can be added to any class. |
|
17489 * @module pluginhost |
|
17490 */ |
|
17491 |
|
17492 /** |
|
17493 * Provides the augmentable PluginHost interface, which can be added to any class. |
|
17494 * @module pluginhost-base |
|
17495 */ |
|
17496 |
|
17497 /** |
|
17498 * <p> |
|
17499 * An augmentable class, which provides the augmented class with the ability to host plugins. |
|
17500 * It adds <a href="#method_plug">plug</a> and <a href="#method_unplug">unplug</a> methods to the augmented class, which can |
|
17501 * be used to add or remove plugins from instances of the class. |
|
17502 * </p> |
|
17503 * |
|
17504 * <p>Plugins can also be added through the constructor configuration object passed to the host class' constructor using |
|
17505 * the "plugins" property. Supported values for the "plugins" property are those defined by the <a href="#method_plug">plug</a> method. |
|
17506 * |
|
17507 * For example the following code would add the AnimPlugin and IOPlugin to Overlay (the plugin host): |
|
17508 * <xmp> |
|
17509 * var o = new Overlay({plugins: [ AnimPlugin, {fn:IOPlugin, cfg:{section:"header"}}]}); |
|
17510 * </xmp> |
|
17511 * </p> |
|
17512 * <p> |
|
17513 * Plug.Host's protected <a href="#method_initPlugins">_initPlugins</a> and <a href="#method_destroyPlugins">_destroyPlugins</a> |
|
17514 * methods should be invoked by the host class at the appropriate point in the host's lifecyle. |
|
17515 * </p> |
|
17516 * |
|
17517 * @class Plugin.Host |
|
17518 */ |
|
17519 |
|
17520 var L = Y.Lang; |
|
17521 |
|
17522 function PluginHost() { |
|
17523 this._plugins = {}; |
|
17524 } |
|
17525 |
|
17526 PluginHost.prototype = { |
|
17527 |
|
17528 /** |
|
17529 * Adds a plugin to the host object. This will instantiate the |
|
17530 * plugin and attach it to the configured namespace on the host object. |
|
17531 * |
|
17532 * @method plug |
|
17533 * @chainable |
|
17534 * @param P {Function | Object |Array} Accepts the plugin class, or an |
|
17535 * object with a "fn" property specifying the plugin class and |
|
17536 * a "cfg" property specifying the configuration for the Plugin. |
|
17537 * <p> |
|
17538 * Additionally an Array can also be passed in, with the above function or |
|
17539 * object values, allowing the user to add multiple plugins in a single call. |
|
17540 * </p> |
|
17541 * @param config (Optional) If the first argument is the plugin class, the second argument |
|
17542 * can be the configuration for the plugin. |
|
17543 * @return {Base} A reference to the host object |
|
17544 */ |
|
17545 plug: function(Plugin, config) { |
|
17546 var i, ln, ns; |
|
17547 |
|
17548 if (L.isArray(Plugin)) { |
|
17549 for (i = 0, ln = Plugin.length; i < ln; i++) { |
|
17550 this.plug(Plugin[i]); |
|
17551 } |
|
17552 } else { |
|
17553 if (Plugin && !L.isFunction(Plugin)) { |
|
17554 config = Plugin.cfg; |
|
17555 Plugin = Plugin.fn; |
|
17556 } |
|
17557 |
|
17558 // Plugin should be fn by now |
|
17559 if (Plugin && Plugin.NS) { |
|
17560 ns = Plugin.NS; |
|
17561 |
|
17562 config = config || {}; |
|
17563 config.host = this; |
|
17564 |
|
17565 if (this.hasPlugin(ns)) { |
|
17566 // Update config |
|
17567 if (this[ns].setAttrs) { |
|
17568 this[ns].setAttrs(config); |
|
17569 } |
|
17570 else { Y.log("Attempt to replug an already attached plugin, and we can't setAttrs, because it's not Attribute based: " + ns); } |
|
17571 } else { |
|
17572 // Create new instance |
|
17573 this[ns] = new Plugin(config); |
|
17574 this._plugins[ns] = Plugin; |
|
17575 } |
|
17576 } |
|
17577 else { Y.log("Attempt to plug in an invalid plugin. Host:" + this + ", Plugin:" + Plugin); } |
|
17578 } |
|
17579 return this; |
|
17580 }, |
|
17581 |
|
17582 /** |
|
17583 * Removes a plugin from the host object. This will destroy the |
|
17584 * plugin instance and delete the namespace from the host object. |
|
17585 * |
|
17586 * @method unplug |
|
17587 * @param {String | Function} plugin The namespace of the plugin, or the plugin class with the static NS namespace property defined. If not provided, |
|
17588 * all registered plugins are unplugged. |
|
17589 * @return {Base} A reference to the host object |
|
17590 * @chainable |
|
17591 */ |
|
17592 unplug: function(plugin) { |
|
17593 var ns = plugin, |
|
17594 plugins = this._plugins; |
|
17595 |
|
17596 if (plugin) { |
|
17597 if (L.isFunction(plugin)) { |
|
17598 ns = plugin.NS; |
|
17599 if (ns && (!plugins[ns] || plugins[ns] !== plugin)) { |
|
17600 ns = null; |
|
17601 } |
|
17602 } |
|
17603 |
|
17604 if (ns) { |
|
17605 if (this[ns]) { |
|
17606 if (this[ns].destroy) { |
|
17607 this[ns].destroy(); |
|
17608 } |
|
17609 delete this[ns]; |
|
17610 } |
|
17611 if (plugins[ns]) { |
|
17612 delete plugins[ns]; |
|
17613 } |
|
17614 } |
|
17615 } else { |
|
17616 for (ns in this._plugins) { |
|
17617 if (this._plugins.hasOwnProperty(ns)) { |
|
17618 this.unplug(ns); |
|
17619 } |
|
17620 } |
|
17621 } |
|
17622 return this; |
|
17623 }, |
|
17624 |
|
17625 /** |
|
17626 * Determines if a plugin has plugged into this host. |
|
17627 * |
|
17628 * @method hasPlugin |
|
17629 * @param {String} ns The plugin's namespace |
|
17630 * @return {Plugin} Returns a truthy value (the plugin instance) if present, or undefined if not. |
|
17631 */ |
|
17632 hasPlugin : function(ns) { |
|
17633 return (this._plugins[ns] && this[ns]); |
|
17634 }, |
|
17635 |
|
17636 /** |
|
17637 * Initializes static plugins registered on the host (using the |
|
17638 * Base.plug static method) and any plugins passed to the |
|
17639 * instance through the "plugins" configuration property. |
|
17640 * |
|
17641 * @method _initPlugins |
|
17642 * @param {Config} config The configuration object with property name/value pairs. |
|
17643 * @private |
|
17644 */ |
|
17645 |
|
17646 _initPlugins: function(config) { |
|
17647 this._plugins = this._plugins || {}; |
|
17648 |
|
17649 if (this._initConfigPlugins) { |
|
17650 this._initConfigPlugins(config); |
|
17651 } |
|
17652 }, |
|
17653 |
|
17654 /** |
|
17655 * Unplugs and destroys all plugins on the host |
|
17656 * @method _destroyPlugins |
|
17657 * @private |
|
17658 */ |
|
17659 _destroyPlugins: function() { |
|
17660 this.unplug(); |
|
17661 } |
|
17662 }; |
|
17663 |
|
17664 Y.namespace("Plugin").Host = PluginHost; |
|
17665 |
|
17666 |
|
17667 }, '@VERSION@', {"requires": ["yui-base"]}); |
|
17668 YUI.add('pluginhost-config', function (Y, NAME) { |
|
17669 |
|
17670 /** |
|
17671 * Adds pluginhost constructor configuration and static configuration support |
|
17672 * @submodule pluginhost-config |
|
17673 */ |
|
17674 |
|
17675 var PluginHost = Y.Plugin.Host, |
|
17676 L = Y.Lang; |
|
17677 |
|
17678 /** |
|
17679 * A protected initialization method, used by the host class to initialize |
|
17680 * plugin configurations passed the constructor, through the config object. |
|
17681 * |
|
17682 * Host objects should invoke this method at the appropriate time in their |
|
17683 * construction lifecycle. |
|
17684 * |
|
17685 * @method _initConfigPlugins |
|
17686 * @param {Object} config The configuration object passed to the constructor |
|
17687 * @protected |
|
17688 * @for Plugin.Host |
|
17689 */ |
|
17690 PluginHost.prototype._initConfigPlugins = function(config) { |
|
17691 |
|
17692 // Class Configuration |
|
17693 var classes = (this._getClasses) ? this._getClasses() : [this.constructor], |
|
17694 plug = [], |
|
17695 unplug = {}, |
|
17696 constructor, i, classPlug, classUnplug, pluginClassName; |
|
17697 |
|
17698 // TODO: Room for optimization. Can we apply statically/unplug in same pass? |
|
17699 for (i = classes.length - 1; i >= 0; i--) { |
|
17700 constructor = classes[i]; |
|
17701 |
|
17702 classUnplug = constructor._UNPLUG; |
|
17703 if (classUnplug) { |
|
17704 // subclasses over-write |
|
17705 Y.mix(unplug, classUnplug, true); |
|
17706 } |
|
17707 |
|
17708 classPlug = constructor._PLUG; |
|
17709 if (classPlug) { |
|
17710 // subclasses over-write |
|
17711 Y.mix(plug, classPlug, true); |
|
17712 } |
|
17713 } |
|
17714 |
|
17715 for (pluginClassName in plug) { |
|
17716 if (plug.hasOwnProperty(pluginClassName)) { |
|
17717 if (!unplug[pluginClassName]) { |
|
17718 this.plug(plug[pluginClassName]); |
|
17719 } |
|
17720 } |
|
17721 } |
|
17722 |
|
17723 // User Configuration |
|
17724 if (config && config.plugins) { |
|
17725 this.plug(config.plugins); |
|
17726 } |
|
17727 }; |
|
17728 |
|
17729 /** |
|
17730 * Registers plugins to be instantiated at the class level (plugins |
|
17731 * which should be plugged into every instance of the class by default). |
|
17732 * |
|
17733 * @method plug |
|
17734 * @static |
|
17735 * |
|
17736 * @param {Function} hostClass The host class on which to register the plugins |
|
17737 * @param {Function | Array} plugin Either the plugin class, an array of plugin classes or an array of objects (with fn and cfg properties defined) |
|
17738 * @param {Object} config (Optional) If plugin is the plugin class, the configuration for the plugin |
|
17739 * @for Plugin.Host |
|
17740 */ |
|
17741 PluginHost.plug = function(hostClass, plugin, config) { |
|
17742 // Cannot plug into Base, since Plugins derive from Base [ will cause infinite recurrsion ] |
|
17743 var p, i, l, name; |
|
17744 |
|
17745 if (hostClass !== Y.Base) { |
|
17746 hostClass._PLUG = hostClass._PLUG || {}; |
|
17747 |
|
17748 if (!L.isArray(plugin)) { |
|
17749 if (config) { |
|
17750 plugin = {fn:plugin, cfg:config}; |
|
17751 } |
|
17752 plugin = [plugin]; |
|
17753 } |
|
17754 |
|
17755 for (i = 0, l = plugin.length; i < l;i++) { |
|
17756 p = plugin[i]; |
|
17757 name = p.NAME || p.fn.NAME; |
|
17758 hostClass._PLUG[name] = p; |
|
17759 } |
|
17760 } |
|
17761 }; |
|
17762 |
|
17763 /** |
|
17764 * Unregisters any class level plugins which have been registered by the host class, or any |
|
17765 * other class in the hierarchy. |
|
17766 * |
|
17767 * @method unplug |
|
17768 * @static |
|
17769 * |
|
17770 * @param {Function} hostClass The host class from which to unregister the plugins |
|
17771 * @param {Function | Array} plugin The plugin class, or an array of plugin classes |
|
17772 * @for Plugin.Host |
|
17773 */ |
|
17774 PluginHost.unplug = function(hostClass, plugin) { |
|
17775 var p, i, l, name; |
|
17776 |
|
17777 if (hostClass !== Y.Base) { |
|
17778 hostClass._UNPLUG = hostClass._UNPLUG || {}; |
|
17779 |
|
17780 if (!L.isArray(plugin)) { |
|
17781 plugin = [plugin]; |
|
17782 } |
|
17783 |
|
17784 for (i = 0, l = plugin.length; i < l; i++) { |
|
17785 p = plugin[i]; |
|
17786 name = p.NAME; |
|
17787 if (!hostClass._PLUG[name]) { |
|
17788 hostClass._UNPLUG[name] = p; |
|
17789 } else { |
|
17790 delete hostClass._PLUG[name]; |
|
17791 } |
|
17792 } |
|
17793 } |
|
17794 }; |
|
17795 |
|
17796 |
|
17797 }, '@VERSION@', {"requires": ["pluginhost-base"]}); |
|
17798 YUI.add('event-delegate', function (Y, NAME) { |
|
17799 |
|
17800 /** |
|
17801 * Adds event delegation support to the library. |
|
17802 * |
|
17803 * @module event |
|
17804 * @submodule event-delegate |
|
17805 */ |
|
17806 |
|
17807 var toArray = Y.Array, |
|
17808 YLang = Y.Lang, |
|
17809 isString = YLang.isString, |
|
17810 isObject = YLang.isObject, |
|
17811 isArray = YLang.isArray, |
|
17812 selectorTest = Y.Selector.test, |
|
17813 detachCategories = Y.Env.evt.handles; |
|
17814 |
|
17815 /** |
|
17816 * <p>Sets up event delegation on a container element. The delegated event |
|
17817 * will use a supplied selector or filtering function to test if the event |
|
17818 * references at least one node that should trigger the subscription |
|
17819 * callback.</p> |
|
17820 * |
|
17821 * <p>Selector string filters will trigger the callback if the event originated |
|
17822 * from a node that matches it or is contained in a node that matches it. |
|
17823 * Function filters are called for each Node up the parent axis to the |
|
17824 * subscribing container node, and receive at each level the Node and the event |
|
17825 * object. The function should return true (or a truthy value) if that Node |
|
17826 * should trigger the subscription callback. Note, it is possible for filters |
|
17827 * to match multiple Nodes for a single event. In this case, the delegate |
|
17828 * callback will be executed for each matching Node.</p> |
|
17829 * |
|
17830 * <p>For each matching Node, the callback will be executed with its 'this' |
|
17831 * object set to the Node matched by the filter (unless a specific context was |
|
17832 * provided during subscription), and the provided event's |
|
17833 * <code>currentTarget</code> will also be set to the matching Node. The |
|
17834 * containing Node from which the subscription was originally made can be |
|
17835 * referenced as <code>e.container</code>. |
|
17836 * |
|
17837 * @method delegate |
|
17838 * @param type {String} the event type to delegate |
|
17839 * @param fn {Function} the callback function to execute. This function |
|
17840 * will be provided the event object for the delegated event. |
|
17841 * @param el {String|node} the element that is the delegation container |
|
17842 * @param filter {string|Function} a selector that must match the target of the |
|
17843 * event or a function to test target and its parents for a match |
|
17844 * @param context optional argument that specifies what 'this' refers to. |
|
17845 * @param args* 0..n additional arguments to pass on to the callback function. |
|
17846 * These arguments will be added after the event object. |
|
17847 * @return {EventHandle} the detach handle |
|
17848 * @static |
|
17849 * @for Event |
|
17850 */ |
|
17851 function delegate(type, fn, el, filter) { |
|
17852 var args = toArray(arguments, 0, true), |
|
17853 query = isString(el) ? el : null, |
|
17854 typeBits, synth, container, categories, cat, i, len, handles, handle; |
|
17855 |
|
17856 // Support Y.delegate({ click: fnA, key: fnB }, el, filter, ...); |
|
17857 // and Y.delegate(['click', 'key'], fn, el, filter, ...); |
|
17858 if (isObject(type)) { |
|
17859 handles = []; |
|
17860 |
|
17861 if (isArray(type)) { |
|
17862 for (i = 0, len = type.length; i < len; ++i) { |
|
17863 args[0] = type[i]; |
|
17864 handles.push(Y.delegate.apply(Y, args)); |
|
17865 } |
|
17866 } else { |
|
17867 // Y.delegate({'click', fn}, el, filter) => |
|
17868 // Y.delegate('click', fn, el, filter) |
|
17869 args.unshift(null); // one arg becomes two; need to make space |
|
17870 |
|
17871 for (i in type) { |
|
17872 if (type.hasOwnProperty(i)) { |
|
17873 args[0] = i; |
|
17874 args[1] = type[i]; |
|
17875 handles.push(Y.delegate.apply(Y, args)); |
|
17876 } |
|
17877 } |
|
17878 } |
|
17879 |
|
17880 return new Y.EventHandle(handles); |
|
17881 } |
|
17882 |
|
17883 typeBits = type.split(/\|/); |
|
17884 |
|
17885 if (typeBits.length > 1) { |
|
17886 cat = typeBits.shift(); |
|
17887 args[0] = type = typeBits.shift(); |
|
17888 } |
|
17889 |
|
17890 synth = Y.Node.DOM_EVENTS[type]; |
|
17891 |
|
17892 if (isObject(synth) && synth.delegate) { |
|
17893 handle = synth.delegate.apply(synth, arguments); |
|
17894 } |
|
17895 |
|
17896 if (!handle) { |
|
17897 if (!type || !fn || !el || !filter) { |
|
17898 Y.log("delegate requires type, callback, parent, & filter", "warn"); |
|
17899 return; |
|
17900 } |
|
17901 |
|
17902 container = (query) ? Y.Selector.query(query, null, true) : el; |
|
17903 |
|
17904 if (!container && isString(el)) { |
|
17905 handle = Y.on('available', function () { |
|
17906 Y.mix(handle, Y.delegate.apply(Y, args), true); |
|
17907 }, el); |
|
17908 } |
|
17909 |
|
17910 if (!handle && container) { |
|
17911 args.splice(2, 2, container); // remove the filter |
|
17912 |
|
17913 handle = Y.Event._attach(args, { facade: false }); |
|
17914 handle.sub.filter = filter; |
|
17915 handle.sub._notify = delegate.notifySub; |
|
17916 } |
|
17917 } |
|
17918 |
|
17919 if (handle && cat) { |
|
17920 categories = detachCategories[cat] || (detachCategories[cat] = {}); |
|
17921 categories = categories[type] || (categories[type] = []); |
|
17922 categories.push(handle); |
|
17923 } |
|
17924 |
|
17925 return handle; |
|
17926 } |
|
17927 |
|
17928 /** |
|
17929 Overrides the <code>_notify</code> method on the normal DOM subscription to |
|
17930 inject the filtering logic and only proceed in the case of a match. |
|
17931 |
|
17932 This method is hosted as a private property of the `delegate` method |
|
17933 (e.g. `Y.delegate.notifySub`) |
|
17934 |
|
17935 @method notifySub |
|
17936 @param thisObj {Object} default 'this' object for the callback |
|
17937 @param args {Array} arguments passed to the event's <code>fire()</code> |
|
17938 @param ce {CustomEvent} the custom event managing the DOM subscriptions for |
|
17939 the subscribed event on the subscribing node. |
|
17940 @return {Boolean} false if the event was stopped |
|
17941 @private |
|
17942 @static |
|
17943 @since 3.2.0 |
|
17944 **/ |
|
17945 delegate.notifySub = function (thisObj, args, ce) { |
|
17946 // Preserve args for other subscribers |
|
17947 args = args.slice(); |
|
17948 if (this.args) { |
|
17949 args.push.apply(args, this.args); |
|
17950 } |
|
17951 |
|
17952 // Only notify subs if the event occurred on a targeted element |
|
17953 var currentTarget = delegate._applyFilter(this.filter, args, ce), |
|
17954 //container = e.currentTarget, |
|
17955 e, i, len, ret; |
|
17956 |
|
17957 if (currentTarget) { |
|
17958 // Support multiple matches up the the container subtree |
|
17959 currentTarget = toArray(currentTarget); |
|
17960 |
|
17961 // The second arg is the currentTarget, but we'll be reusing this |
|
17962 // facade, replacing the currentTarget for each use, so it doesn't |
|
17963 // matter what element we seed it with. |
|
17964 e = args[0] = new Y.DOMEventFacade(args[0], ce.el, ce); |
|
17965 |
|
17966 e.container = Y.one(ce.el); |
|
17967 |
|
17968 for (i = 0, len = currentTarget.length; i < len && !e.stopped; ++i) { |
|
17969 e.currentTarget = Y.one(currentTarget[i]); |
|
17970 |
|
17971 ret = this.fn.apply(this.context || e.currentTarget, args); |
|
17972 |
|
17973 if (ret === false) { // stop further notifications |
|
17974 break; |
|
17975 } |
|
17976 } |
|
17977 |
|
17978 return ret; |
|
17979 } |
|
17980 }; |
|
17981 |
|
17982 /** |
|
17983 Compiles a selector string into a filter function to identify whether |
|
17984 Nodes along the parent axis of an event's target should trigger event |
|
17985 notification. |
|
17986 |
|
17987 This function is memoized, so previously compiled filter functions are |
|
17988 returned if the same selector string is provided. |
|
17989 |
|
17990 This function may be useful when defining synthetic events for delegate |
|
17991 handling. |
|
17992 |
|
17993 Hosted as a property of the `delegate` method (e.g. `Y.delegate.compileFilter`). |
|
17994 |
|
17995 @method compileFilter |
|
17996 @param selector {String} the selector string to base the filtration on |
|
17997 @return {Function} |
|
17998 @since 3.2.0 |
|
17999 @static |
|
18000 **/ |
|
18001 delegate.compileFilter = Y.cached(function (selector) { |
|
18002 return function (target, e) { |
|
18003 return selectorTest(target._node, selector, |
|
18004 (e.currentTarget === e.target) ? null : e.currentTarget._node); |
|
18005 }; |
|
18006 }); |
|
18007 |
|
18008 /** |
|
18009 Regex to test for disabled elements during filtering. This is only relevant to |
|
18010 IE to normalize behavior with other browsers, which swallow events that occur |
|
18011 to disabled elements. IE fires the event from the parent element instead of the |
|
18012 original target, though it does preserve `event.srcElement` as the disabled |
|
18013 element. IE also supports disabled on `<a>`, but the event still bubbles, so it |
|
18014 acts more like `e.preventDefault()` plus styling. That issue is not handled here |
|
18015 because other browsers fire the event on the `<a>`, so delegate is supported in |
|
18016 both cases. |
|
18017 |
|
18018 @property _disabledRE |
|
18019 @type {RegExp} |
|
18020 @protected |
|
18021 @since 3.8.1 |
|
18022 **/ |
|
18023 delegate._disabledRE = /^(?:button|input|select|textarea)$/i; |
|
18024 |
|
18025 /** |
|
18026 Walks up the parent axis of an event's target, and tests each element |
|
18027 against a supplied filter function. If any Nodes, including the container, |
|
18028 satisfy the filter, the delegated callback will be triggered for each. |
|
18029 |
|
18030 Hosted as a protected property of the `delegate` method (e.g. |
|
18031 `Y.delegate._applyFilter`). |
|
18032 |
|
18033 @method _applyFilter |
|
18034 @param filter {Function} boolean function to test for inclusion in event |
|
18035 notification |
|
18036 @param args {Array} the arguments that would be passed to subscribers |
|
18037 @param ce {CustomEvent} the DOM event wrapper |
|
18038 @return {Node|Node[]|undefined} The Node or Nodes that satisfy the filter |
|
18039 @protected |
|
18040 **/ |
|
18041 delegate._applyFilter = function (filter, args, ce) { |
|
18042 var e = args[0], |
|
18043 container = ce.el, // facadeless events in IE, have no e.currentTarget |
|
18044 target = e.target || e.srcElement, |
|
18045 match = [], |
|
18046 isContainer = false; |
|
18047 |
|
18048 // Resolve text nodes to their containing element |
|
18049 if (target.nodeType === 3) { |
|
18050 target = target.parentNode; |
|
18051 } |
|
18052 |
|
18053 // For IE. IE propagates events from the parent element of disabled |
|
18054 // elements, where other browsers swallow the event entirely. To normalize |
|
18055 // this in IE, filtering for matching elements should abort if the target |
|
18056 // is a disabled form control. |
|
18057 if (target.disabled && delegate._disabledRE.test(target.nodeName)) { |
|
18058 return match; |
|
18059 } |
|
18060 |
|
18061 // passing target as the first arg rather than leaving well enough alone |
|
18062 // making 'this' in the filter function refer to the target. This is to |
|
18063 // support bound filter functions. |
|
18064 args.unshift(target); |
|
18065 |
|
18066 if (isString(filter)) { |
|
18067 while (target) { |
|
18068 isContainer = (target === container); |
|
18069 if (selectorTest(target, filter, (isContainer ? null: container))) { |
|
18070 match.push(target); |
|
18071 } |
|
18072 |
|
18073 if (isContainer) { |
|
18074 break; |
|
18075 } |
|
18076 |
|
18077 target = target.parentNode; |
|
18078 } |
|
18079 } else { |
|
18080 // filter functions are implementer code and should receive wrappers |
|
18081 args[0] = Y.one(target); |
|
18082 args[1] = new Y.DOMEventFacade(e, container, ce); |
|
18083 |
|
18084 while (target) { |
|
18085 // filter(target, e, extra args...) - this === target |
|
18086 if (filter.apply(args[0], args)) { |
|
18087 match.push(target); |
|
18088 } |
|
18089 |
|
18090 if (target === container) { |
|
18091 break; |
|
18092 } |
|
18093 |
|
18094 target = target.parentNode; |
|
18095 args[0] = Y.one(target); |
|
18096 } |
|
18097 args[1] = e; // restore the raw DOM event |
|
18098 } |
|
18099 |
|
18100 if (match.length <= 1) { |
|
18101 match = match[0]; // single match or undefined |
|
18102 } |
|
18103 |
|
18104 // remove the target |
|
18105 args.shift(); |
|
18106 |
|
18107 return match; |
|
18108 }; |
|
18109 |
|
18110 /** |
|
18111 * Sets up event delegation on a container element. The delegated event |
|
18112 * will use a supplied filter to test if the callback should be executed. |
|
18113 * This filter can be either a selector string or a function that returns |
|
18114 * a Node to use as the currentTarget for the event. |
|
18115 * |
|
18116 * The event object for the delegated event is supplied to the callback |
|
18117 * function. It is modified slightly in order to support all properties |
|
18118 * that may be needed for event delegation. 'currentTarget' is set to |
|
18119 * the element that matched the selector string filter or the Node returned |
|
18120 * from the filter function. 'container' is set to the element that the |
|
18121 * listener is delegated from (this normally would be the 'currentTarget'). |
|
18122 * |
|
18123 * Filter functions will be called with the arguments that would be passed to |
|
18124 * the callback function, including the event object as the first parameter. |
|
18125 * The function should return false (or a falsey value) if the success criteria |
|
18126 * aren't met, and the Node to use as the event's currentTarget and 'this' |
|
18127 * object if they are. |
|
18128 * |
|
18129 * @method delegate |
|
18130 * @param type {string} the event type to delegate |
|
18131 * @param fn {function} the callback function to execute. This function |
|
18132 * will be provided the event object for the delegated event. |
|
18133 * @param el {string|node} the element that is the delegation container |
|
18134 * @param filter {string|function} a selector that must match the target of the |
|
18135 * event or a function that returns a Node or false. |
|
18136 * @param context optional argument that specifies what 'this' refers to. |
|
18137 * @param args* 0..n additional arguments to pass on to the callback function. |
|
18138 * These arguments will be added after the event object. |
|
18139 * @return {EventHandle} the detach handle |
|
18140 * @for YUI |
|
18141 */ |
|
18142 Y.delegate = Y.Event.delegate = delegate; |
|
18143 |
|
18144 |
|
18145 }, '@VERSION@', {"requires": ["node-base"]}); |
|
18146 YUI.add('node-event-delegate', function (Y, NAME) { |
|
18147 |
|
18148 /** |
|
18149 * Functionality to make the node a delegated event container |
|
18150 * @module node |
|
18151 * @submodule node-event-delegate |
|
18152 */ |
|
18153 |
|
18154 /** |
|
18155 * <p>Sets up a delegation listener for an event occurring inside the Node. |
|
18156 * The delegated event will be verified against a supplied selector or |
|
18157 * filtering function to test if the event references at least one node that |
|
18158 * should trigger the subscription callback.</p> |
|
18159 * |
|
18160 * <p>Selector string filters will trigger the callback if the event originated |
|
18161 * from a node that matches it or is contained in a node that matches it. |
|
18162 * Function filters are called for each Node up the parent axis to the |
|
18163 * subscribing container node, and receive at each level the Node and the event |
|
18164 * object. The function should return true (or a truthy value) if that Node |
|
18165 * should trigger the subscription callback. Note, it is possible for filters |
|
18166 * to match multiple Nodes for a single event. In this case, the delegate |
|
18167 * callback will be executed for each matching Node.</p> |
|
18168 * |
|
18169 * <p>For each matching Node, the callback will be executed with its 'this' |
|
18170 * object set to the Node matched by the filter (unless a specific context was |
|
18171 * provided during subscription), and the provided event's |
|
18172 * <code>currentTarget</code> will also be set to the matching Node. The |
|
18173 * containing Node from which the subscription was originally made can be |
|
18174 * referenced as <code>e.container</code>. |
|
18175 * |
|
18176 * @method delegate |
|
18177 * @param type {String} the event type to delegate |
|
18178 * @param fn {Function} the callback function to execute. This function |
|
18179 * will be provided the event object for the delegated event. |
|
18180 * @param spec {String|Function} a selector that must match the target of the |
|
18181 * event or a function to test target and its parents for a match |
|
18182 * @param context {Object} optional argument that specifies what 'this' refers to. |
|
18183 * @param args* {any} 0..n additional arguments to pass on to the callback function. |
|
18184 * These arguments will be added after the event object. |
|
18185 * @return {EventHandle} the detach handle |
|
18186 * @for Node |
|
18187 */ |
|
18188 Y.Node.prototype.delegate = function(type) { |
|
18189 |
|
18190 var args = Y.Array(arguments, 0, true), |
|
18191 index = (Y.Lang.isObject(type) && !Y.Lang.isArray(type)) ? 1 : 2; |
|
18192 |
|
18193 args.splice(index, 0, this._node); |
|
18194 |
|
18195 return Y.delegate.apply(Y, args); |
|
18196 }; |
|
18197 |
|
18198 |
|
18199 }, '@VERSION@', {"requires": ["node-base", "event-delegate"]}); |
|
18200 YUI.add('node-pluginhost', function (Y, NAME) { |
|
18201 |
|
18202 /** |
|
18203 * @module node |
|
18204 * @submodule node-pluginhost |
|
18205 */ |
|
18206 |
|
18207 /** |
|
18208 * Registers plugins to be instantiated at the class level (plugins |
|
18209 * which should be plugged into every instance of Node by default). |
|
18210 * |
|
18211 * @method plug |
|
18212 * @static |
|
18213 * @for Node |
|
18214 * @param {Function | Array} plugin Either the plugin class, an array of plugin classes or an array of objects (with fn and cfg properties defined) |
|
18215 * @param {Object} config (Optional) If plugin is the plugin class, the configuration for the plugin |
|
18216 */ |
|
18217 Y.Node.plug = function() { |
|
18218 var args = Y.Array(arguments); |
|
18219 args.unshift(Y.Node); |
|
18220 Y.Plugin.Host.plug.apply(Y.Base, args); |
|
18221 return Y.Node; |
|
18222 }; |
|
18223 |
|
18224 /** |
|
18225 * Unregisters any class level plugins which have been registered by the Node |
|
18226 * |
|
18227 * @method unplug |
|
18228 * @static |
|
18229 * |
|
18230 * @param {Function | Array} plugin The plugin class, or an array of plugin classes |
|
18231 */ |
|
18232 Y.Node.unplug = function() { |
|
18233 var args = Y.Array(arguments); |
|
18234 args.unshift(Y.Node); |
|
18235 Y.Plugin.Host.unplug.apply(Y.Base, args); |
|
18236 return Y.Node; |
|
18237 }; |
|
18238 |
|
18239 Y.mix(Y.Node, Y.Plugin.Host, false, null, 1); |
|
18240 |
|
18241 // allow batching of plug/unplug via NodeList |
|
18242 // doesn't use NodeList.importMethod because we need real Nodes (not tmpNode) |
|
18243 /** |
|
18244 * Adds a plugin to each node in the NodeList. |
|
18245 * This will instantiate the plugin and attach it to the configured namespace on each node |
|
18246 * @method plug |
|
18247 * @for NodeList |
|
18248 * @param P {Function | Object |Array} Accepts the plugin class, or an |
|
18249 * object with a "fn" property specifying the plugin class and |
|
18250 * a "cfg" property specifying the configuration for the Plugin. |
|
18251 * <p> |
|
18252 * Additionally an Array can also be passed in, with the above function or |
|
18253 * object values, allowing the user to add multiple plugins in a single call. |
|
18254 * </p> |
|
18255 * @param config (Optional) If the first argument is the plugin class, the second argument |
|
18256 * can be the configuration for the plugin. |
|
18257 * @chainable |
|
18258 */ |
|
18259 Y.NodeList.prototype.plug = function() { |
|
18260 var args = arguments; |
|
18261 Y.NodeList.each(this, function(node) { |
|
18262 Y.Node.prototype.plug.apply(Y.one(node), args); |
|
18263 }); |
|
18264 return this; |
|
18265 }; |
|
18266 |
|
18267 /** |
|
18268 * Removes a plugin from all nodes in the NodeList. This will destroy the |
|
18269 * plugin instance and delete the namespace each node. |
|
18270 * @method unplug |
|
18271 * @for NodeList |
|
18272 * @param {String | Function} plugin The namespace of the plugin, or the plugin class with the static NS namespace property defined. If not provided, |
|
18273 * all registered plugins are unplugged. |
|
18274 * @chainable |
|
18275 */ |
|
18276 Y.NodeList.prototype.unplug = function() { |
|
18277 var args = arguments; |
|
18278 Y.NodeList.each(this, function(node) { |
|
18279 Y.Node.prototype.unplug.apply(Y.one(node), args); |
|
18280 }); |
|
18281 return this; |
|
18282 }; |
|
18283 |
|
18284 |
|
18285 }, '@VERSION@', {"requires": ["node-base", "pluginhost"]}); |
|
18286 YUI.add('node-screen', function (Y, NAME) { |
|
18287 |
|
18288 /** |
|
18289 * Extended Node interface for managing regions and screen positioning. |
|
18290 * Adds support for positioning elements and normalizes window size and scroll detection. |
|
18291 * @module node |
|
18292 * @submodule node-screen |
|
18293 */ |
|
18294 |
|
18295 // these are all "safe" returns, no wrapping required |
|
18296 Y.each([ |
|
18297 /** |
|
18298 * Returns the inner width of the viewport (exludes scrollbar). |
|
18299 * @config winWidth |
|
18300 * @for Node |
|
18301 * @type {Int} |
|
18302 */ |
|
18303 'winWidth', |
|
18304 |
|
18305 /** |
|
18306 * Returns the inner height of the viewport (exludes scrollbar). |
|
18307 * @config winHeight |
|
18308 * @type {Int} |
|
18309 */ |
|
18310 'winHeight', |
|
18311 |
|
18312 /** |
|
18313 * Document width |
|
18314 * @config docWidth |
|
18315 * @type {Int} |
|
18316 */ |
|
18317 'docWidth', |
|
18318 |
|
18319 /** |
|
18320 * Document height |
|
18321 * @config docHeight |
|
18322 * @type {Int} |
|
18323 */ |
|
18324 'docHeight', |
|
18325 |
|
18326 /** |
|
18327 * Pixel distance the page has been scrolled horizontally |
|
18328 * @config docScrollX |
|
18329 * @type {Int} |
|
18330 */ |
|
18331 'docScrollX', |
|
18332 |
|
18333 /** |
|
18334 * Pixel distance the page has been scrolled vertically |
|
18335 * @config docScrollY |
|
18336 * @type {Int} |
|
18337 */ |
|
18338 'docScrollY' |
|
18339 ], |
|
18340 function(name) { |
|
18341 Y.Node.ATTRS[name] = { |
|
18342 getter: function() { |
|
18343 var args = Array.prototype.slice.call(arguments); |
|
18344 args.unshift(Y.Node.getDOMNode(this)); |
|
18345 |
|
18346 return Y.DOM[name].apply(this, args); |
|
18347 } |
|
18348 }; |
|
18349 } |
|
18350 ); |
|
18351 |
|
18352 Y.Node.ATTRS.scrollLeft = { |
|
18353 getter: function() { |
|
18354 var node = Y.Node.getDOMNode(this); |
|
18355 return ('scrollLeft' in node) ? node.scrollLeft : Y.DOM.docScrollX(node); |
|
18356 }, |
|
18357 |
|
18358 setter: function(val) { |
|
18359 var node = Y.Node.getDOMNode(this); |
|
18360 if (node) { |
|
18361 if ('scrollLeft' in node) { |
|
18362 node.scrollLeft = val; |
|
18363 } else if (node.document || node.nodeType === 9) { |
|
18364 Y.DOM._getWin(node).scrollTo(val, Y.DOM.docScrollY(node)); // scroll window if win or doc |
|
18365 } |
|
18366 } else { |
|
18367 Y.log('unable to set scrollLeft for ' + node, 'error', 'Node'); |
|
18368 } |
|
18369 } |
|
18370 }; |
|
18371 |
|
18372 Y.Node.ATTRS.scrollTop = { |
|
18373 getter: function() { |
|
18374 var node = Y.Node.getDOMNode(this); |
|
18375 return ('scrollTop' in node) ? node.scrollTop : Y.DOM.docScrollY(node); |
|
18376 }, |
|
18377 |
|
18378 setter: function(val) { |
|
18379 var node = Y.Node.getDOMNode(this); |
|
18380 if (node) { |
|
18381 if ('scrollTop' in node) { |
|
18382 node.scrollTop = val; |
|
18383 } else if (node.document || node.nodeType === 9) { |
|
18384 Y.DOM._getWin(node).scrollTo(Y.DOM.docScrollX(node), val); // scroll window if win or doc |
|
18385 } |
|
18386 } else { |
|
18387 Y.log('unable to set scrollTop for ' + node, 'error', 'Node'); |
|
18388 } |
|
18389 } |
|
18390 }; |
|
18391 |
|
18392 Y.Node.importMethod(Y.DOM, [ |
|
18393 /** |
|
18394 * Gets the current position of the node in page coordinates. |
|
18395 * @method getXY |
|
18396 * @for Node |
|
18397 * @return {Array} The XY position of the node |
|
18398 */ |
|
18399 'getXY', |
|
18400 |
|
18401 /** |
|
18402 * Set the position of the node in page coordinates, regardless of how the node is positioned. |
|
18403 * @method setXY |
|
18404 * @param {Array} xy Contains X & Y values for new position (coordinates are page-based) |
|
18405 * @chainable |
|
18406 */ |
|
18407 'setXY', |
|
18408 |
|
18409 /** |
|
18410 * Gets the current position of the node in page coordinates. |
|
18411 * @method getX |
|
18412 * @return {Int} The X position of the node |
|
18413 */ |
|
18414 'getX', |
|
18415 |
|
18416 /** |
|
18417 * Set the position of the node in page coordinates, regardless of how the node is positioned. |
|
18418 * @method setX |
|
18419 * @param {Int} x X value for new position (coordinates are page-based) |
|
18420 * @chainable |
|
18421 */ |
|
18422 'setX', |
|
18423 |
|
18424 /** |
|
18425 * Gets the current position of the node in page coordinates. |
|
18426 * @method getY |
|
18427 * @return {Int} The Y position of the node |
|
18428 */ |
|
18429 'getY', |
|
18430 |
|
18431 /** |
|
18432 * Set the position of the node in page coordinates, regardless of how the node is positioned. |
|
18433 * @method setY |
|
18434 * @param {Int} y Y value for new position (coordinates are page-based) |
|
18435 * @chainable |
|
18436 */ |
|
18437 'setY', |
|
18438 |
|
18439 /** |
|
18440 * Swaps the XY position of this node with another node. |
|
18441 * @method swapXY |
|
18442 * @param {Node | HTMLElement} otherNode The node to swap with. |
|
18443 * @chainable |
|
18444 */ |
|
18445 'swapXY' |
|
18446 ]); |
|
18447 |
|
18448 /** |
|
18449 * @module node |
|
18450 * @submodule node-screen |
|
18451 */ |
|
18452 |
|
18453 /** |
|
18454 * Returns a region object for the node |
|
18455 * @config region |
|
18456 * @for Node |
|
18457 * @type Node |
|
18458 */ |
|
18459 Y.Node.ATTRS.region = { |
|
18460 getter: function() { |
|
18461 var node = this.getDOMNode(), |
|
18462 region; |
|
18463 |
|
18464 if (node && !node.tagName) { |
|
18465 if (node.nodeType === 9) { // document |
|
18466 node = node.documentElement; |
|
18467 } |
|
18468 } |
|
18469 if (Y.DOM.isWindow(node)) { |
|
18470 region = Y.DOM.viewportRegion(node); |
|
18471 } else { |
|
18472 region = Y.DOM.region(node); |
|
18473 } |
|
18474 return region; |
|
18475 } |
|
18476 }; |
|
18477 |
|
18478 /** |
|
18479 * Returns a region object for the node's viewport |
|
18480 * @config viewportRegion |
|
18481 * @type Node |
|
18482 */ |
|
18483 Y.Node.ATTRS.viewportRegion = { |
|
18484 getter: function() { |
|
18485 return Y.DOM.viewportRegion(Y.Node.getDOMNode(this)); |
|
18486 } |
|
18487 }; |
|
18488 |
|
18489 Y.Node.importMethod(Y.DOM, 'inViewportRegion'); |
|
18490 |
|
18491 // these need special treatment to extract 2nd node arg |
|
18492 /** |
|
18493 * Compares the intersection of the node with another node or region |
|
18494 * @method intersect |
|
18495 * @for Node |
|
18496 * @param {Node|Object} node2 The node or region to compare with. |
|
18497 * @param {Object} altRegion An alternate region to use (rather than this node's). |
|
18498 * @return {Object} An object representing the intersection of the regions. |
|
18499 */ |
|
18500 Y.Node.prototype.intersect = function(node2, altRegion) { |
|
18501 var node1 = Y.Node.getDOMNode(this); |
|
18502 if (Y.instanceOf(node2, Y.Node)) { // might be a region object |
|
18503 node2 = Y.Node.getDOMNode(node2); |
|
18504 } |
|
18505 return Y.DOM.intersect(node1, node2, altRegion); |
|
18506 }; |
|
18507 |
|
18508 /** |
|
18509 * Determines whether or not the node is within the giving region. |
|
18510 * @method inRegion |
|
18511 * @param {Node|Object} node2 The node or region to compare with. |
|
18512 * @param {Boolean} all Whether or not all of the node must be in the region. |
|
18513 * @param {Object} altRegion An alternate region to use (rather than this node's). |
|
18514 * @return {Boolean} True if in region, false if not. |
|
18515 */ |
|
18516 Y.Node.prototype.inRegion = function(node2, all, altRegion) { |
|
18517 var node1 = Y.Node.getDOMNode(this); |
|
18518 if (Y.instanceOf(node2, Y.Node)) { // might be a region object |
|
18519 node2 = Y.Node.getDOMNode(node2); |
|
18520 } |
|
18521 return Y.DOM.inRegion(node1, node2, all, altRegion); |
|
18522 }; |
|
18523 |
|
18524 |
|
18525 }, '@VERSION@', {"requires": ["dom-screen", "node-base"]}); |
|
18526 YUI.add('node-style', function (Y, NAME) { |
|
18527 |
|
18528 (function(Y) { |
|
18529 /** |
|
18530 * Extended Node interface for managing node styles. |
|
18531 * @module node |
|
18532 * @submodule node-style |
|
18533 */ |
|
18534 |
|
18535 Y.mix(Y.Node.prototype, { |
|
18536 /** |
|
18537 * Sets a style property of the node. |
|
18538 * Use camelCase (e.g. 'backgroundColor') for multi-word properties. |
|
18539 * @method setStyle |
|
18540 * @param {String} attr The style attribute to set. |
|
18541 * @param {String|Number} val The value. |
|
18542 * @chainable |
|
18543 */ |
|
18544 setStyle: function(attr, val) { |
|
18545 Y.DOM.setStyle(this._node, attr, val); |
|
18546 return this; |
|
18547 }, |
|
18548 |
|
18549 /** |
|
18550 * Sets multiple style properties on the node. |
|
18551 * Use camelCase (e.g. 'backgroundColor') for multi-word properties. |
|
18552 * @method setStyles |
|
18553 * @param {Object} hash An object literal of property:value pairs. |
|
18554 * @chainable |
|
18555 */ |
|
18556 setStyles: function(hash) { |
|
18557 Y.DOM.setStyles(this._node, hash); |
|
18558 return this; |
|
18559 }, |
|
18560 |
|
18561 /** |
|
18562 * Returns the style's current value. |
|
18563 * Use camelCase (e.g. 'backgroundColor') for multi-word properties. |
|
18564 * @method getStyle |
|
18565 * @for Node |
|
18566 * @param {String} attr The style attribute to retrieve. |
|
18567 * @return {String} The current value of the style property for the element. |
|
18568 */ |
|
18569 |
|
18570 getStyle: function(attr) { |
|
18571 return Y.DOM.getStyle(this._node, attr); |
|
18572 }, |
|
18573 |
|
18574 /** |
|
18575 * Returns the computed value for the given style property. |
|
18576 * Use camelCase (e.g. 'backgroundColor') for multi-word properties. |
|
18577 * @method getComputedStyle |
|
18578 * @param {String} attr The style attribute to retrieve. |
|
18579 * @return {String} The computed value of the style property for the element. |
|
18580 */ |
|
18581 getComputedStyle: function(attr) { |
|
18582 return Y.DOM.getComputedStyle(this._node, attr); |
|
18583 } |
|
18584 }); |
|
18585 |
|
18586 /** |
|
18587 * Returns an array of values for each node. |
|
18588 * Use camelCase (e.g. 'backgroundColor') for multi-word properties. |
|
18589 * @method getStyle |
|
18590 * @for NodeList |
|
18591 * @see Node.getStyle |
|
18592 * @param {String} attr The style attribute to retrieve. |
|
18593 * @return {Array} The current values of the style property for the element. |
|
18594 */ |
|
18595 |
|
18596 /** |
|
18597 * Returns an array of the computed value for each node. |
|
18598 * Use camelCase (e.g. 'backgroundColor') for multi-word properties. |
|
18599 * @method getComputedStyle |
|
18600 * @see Node.getComputedStyle |
|
18601 * @param {String} attr The style attribute to retrieve. |
|
18602 * @return {Array} The computed values for each node. |
|
18603 */ |
|
18604 |
|
18605 /** |
|
18606 * Sets a style property on each node. |
|
18607 * Use camelCase (e.g. 'backgroundColor') for multi-word properties. |
|
18608 * @method setStyle |
|
18609 * @see Node.setStyle |
|
18610 * @param {String} attr The style attribute to set. |
|
18611 * @param {String|Number} val The value. |
|
18612 * @chainable |
|
18613 */ |
|
18614 |
|
18615 /** |
|
18616 * Sets multiple style properties on each node. |
|
18617 * Use camelCase (e.g. 'backgroundColor') for multi-word properties. |
|
18618 * @method setStyles |
|
18619 * @see Node.setStyles |
|
18620 * @param {Object} hash An object literal of property:value pairs. |
|
18621 * @chainable |
|
18622 */ |
|
18623 |
|
18624 // These are broken out to handle undefined return (avoid false positive for |
|
18625 // chainable) |
|
18626 |
|
18627 Y.NodeList.importMethod(Y.Node.prototype, ['getStyle', 'getComputedStyle', 'setStyle', 'setStyles']); |
|
18628 })(Y); |
|
18629 |
|
18630 |
|
18631 }, '@VERSION@', {"requires": ["dom-style", "node-base"]}); |
|
18632 YUI.add('querystring-stringify-simple', function (Y, NAME) { |
|
18633 |
|
18634 /*global Y */ |
|
18635 /** |
|
18636 * <p>Provides Y.QueryString.stringify method for converting objects to Query Strings. |
|
18637 * This is a subset implementation of the full querystring-stringify.</p> |
|
18638 * <p>This module provides the bare minimum functionality (encoding a hash of simple values), |
|
18639 * without the additional support for nested data structures. Every key-value pair is |
|
18640 * encoded by encodeURIComponent.</p> |
|
18641 * <p>This module provides a minimalistic way for io to handle single-level objects |
|
18642 * as transaction data.</p> |
|
18643 * |
|
18644 * @module querystring |
|
18645 * @submodule querystring-stringify-simple |
|
18646 */ |
|
18647 |
|
18648 var QueryString = Y.namespace("QueryString"), |
|
18649 EUC = encodeURIComponent; |
|
18650 |
|
18651 |
|
18652 QueryString.stringify = function (obj, c) { |
|
18653 var qs = [], |
|
18654 // Default behavior is false; standard key notation. |
|
18655 s = c && c.arrayKey ? true : false, |
|
18656 key, i, l; |
|
18657 |
|
18658 for (key in obj) { |
|
18659 if (obj.hasOwnProperty(key)) { |
|
18660 if (Y.Lang.isArray(obj[key])) { |
|
18661 for (i = 0, l = obj[key].length; i < l; i++) { |
|
18662 qs.push(EUC(s ? key + '[]' : key) + '=' + EUC(obj[key][i])); |
|
18663 } |
|
18664 } |
|
18665 else { |
|
18666 qs.push(EUC(key) + '=' + EUC(obj[key])); |
|
18667 } |
|
18668 } |
|
18669 } |
|
18670 |
|
18671 return qs.join('&'); |
|
18672 }; |
|
18673 |
|
18674 |
|
18675 }, '@VERSION@', {"requires": ["yui-base"]}); |
|
18676 YUI.add('io-base', function (Y, NAME) { |
|
18677 |
|
18678 /** |
|
18679 Base IO functionality. Provides basic XHR transport support. |
|
18680 |
|
18681 @module io |
|
18682 @submodule io-base |
|
18683 @for IO |
|
18684 **/ |
|
18685 |
|
18686 var // List of events that comprise the IO event lifecycle. |
|
18687 EVENTS = ['start', 'complete', 'end', 'success', 'failure', 'progress'], |
|
18688 |
|
18689 // Whitelist of used XHR response object properties. |
|
18690 XHR_PROPS = ['status', 'statusText', 'responseText', 'responseXML'], |
|
18691 |
|
18692 win = Y.config.win, |
|
18693 uid = 0; |
|
18694 |
|
18695 /** |
|
18696 The IO class is a utility that brokers HTTP requests through a simplified |
|
18697 interface. Specifically, it allows JavaScript to make HTTP requests to |
|
18698 a resource without a page reload. The underlying transport for making |
|
18699 same-domain requests is the XMLHttpRequest object. IO can also use |
|
18700 Flash, if specified as a transport, for cross-domain requests. |
|
18701 |
|
18702 @class IO |
|
18703 @constructor |
|
18704 @param {Object} config Object of EventTarget's publish method configurations |
|
18705 used to configure IO's events. |
|
18706 **/ |
|
18707 function IO (config) { |
|
18708 var io = this; |
|
18709 |
|
18710 io._uid = 'io:' + uid++; |
|
18711 io._init(config); |
|
18712 Y.io._map[io._uid] = io; |
|
18713 } |
|
18714 |
|
18715 IO.prototype = { |
|
18716 //-------------------------------------- |
|
18717 // Properties |
|
18718 //-------------------------------------- |
|
18719 |
|
18720 /** |
|
18721 * A counter that increments for each transaction. |
|
18722 * |
|
18723 * @property _id |
|
18724 * @private |
|
18725 * @type {Number} |
|
18726 */ |
|
18727 _id: 0, |
|
18728 |
|
18729 /** |
|
18730 * Object of IO HTTP headers sent with each transaction. |
|
18731 * |
|
18732 * @property _headers |
|
18733 * @private |
|
18734 * @type {Object} |
|
18735 */ |
|
18736 _headers: { |
|
18737 'X-Requested-With' : 'XMLHttpRequest' |
|
18738 }, |
|
18739 |
|
18740 /** |
|
18741 * Object that stores timeout values for any transaction with a defined |
|
18742 * "timeout" configuration property. |
|
18743 * |
|
18744 * @property _timeout |
|
18745 * @private |
|
18746 * @type {Object} |
|
18747 */ |
|
18748 _timeout: {}, |
|
18749 |
|
18750 //-------------------------------------- |
|
18751 // Methods |
|
18752 //-------------------------------------- |
|
18753 |
|
18754 _init: function(config) { |
|
18755 var io = this, i, len; |
|
18756 |
|
18757 io.cfg = config || {}; |
|
18758 |
|
18759 Y.augment(io, Y.EventTarget); |
|
18760 for (i = 0, len = EVENTS.length; i < len; ++i) { |
|
18761 // Publish IO global events with configurations, if any. |
|
18762 // IO global events are set to broadcast by default. |
|
18763 // These events use the "io:" namespace. |
|
18764 io.publish('io:' + EVENTS[i], Y.merge({ broadcast: 1 }, config)); |
|
18765 // Publish IO transaction events with configurations, if |
|
18766 // any. These events use the "io-trn:" namespace. |
|
18767 io.publish('io-trn:' + EVENTS[i], config); |
|
18768 } |
|
18769 }, |
|
18770 |
|
18771 /** |
|
18772 * Method that creates a unique transaction object for each request. |
|
18773 * |
|
18774 * @method _create |
|
18775 * @private |
|
18776 * @param {Object} cfg Configuration object subset to determine if |
|
18777 * the transaction is an XDR or file upload, |
|
18778 * requiring an alternate transport. |
|
18779 * @param {Number} id Transaction id |
|
18780 * @return {Object} The transaction object |
|
18781 */ |
|
18782 _create: function(config, id) { |
|
18783 var io = this, |
|
18784 transaction = { |
|
18785 id : Y.Lang.isNumber(id) ? id : io._id++, |
|
18786 uid: io._uid |
|
18787 }, |
|
18788 alt = config.xdr ? config.xdr.use : null, |
|
18789 form = config.form && config.form.upload ? 'iframe' : null, |
|
18790 use; |
|
18791 |
|
18792 if (alt === 'native') { |
|
18793 // Non-IE and IE >= 10 can use XHR level 2 and not rely on an |
|
18794 // external transport. |
|
18795 alt = Y.UA.ie && !SUPPORTS_CORS ? 'xdr' : null; |
|
18796 |
|
18797 // Prevent "pre-flight" OPTIONS request by removing the |
|
18798 // `X-Requested-With` HTTP header from CORS requests. This header |
|
18799 // can be added back on a per-request basis, if desired. |
|
18800 io.setHeader('X-Requested-With'); |
|
18801 } |
|
18802 |
|
18803 use = alt || form; |
|
18804 transaction = use ? Y.merge(Y.IO.customTransport(use), transaction) : |
|
18805 Y.merge(Y.IO.defaultTransport(), transaction); |
|
18806 |
|
18807 if (transaction.notify) { |
|
18808 config.notify = function (e, t, c) { io.notify(e, t, c); }; |
|
18809 } |
|
18810 |
|
18811 if (!use) { |
|
18812 if (win && win.FormData && config.data instanceof win.FormData) { |
|
18813 transaction.c.upload.onprogress = function (e) { |
|
18814 io.progress(transaction, e, config); |
|
18815 }; |
|
18816 transaction.c.onload = function (e) { |
|
18817 io.load(transaction, e, config); |
|
18818 }; |
|
18819 transaction.c.onerror = function (e) { |
|
18820 io.error(transaction, e, config); |
|
18821 }; |
|
18822 transaction.upload = true; |
|
18823 } |
|
18824 } |
|
18825 |
|
18826 return transaction; |
|
18827 }, |
|
18828 |
|
18829 _destroy: function(transaction) { |
|
18830 if (win && !transaction.notify && !transaction.xdr) { |
|
18831 if (XHR && !transaction.upload) { |
|
18832 transaction.c.onreadystatechange = null; |
|
18833 } else if (transaction.upload) { |
|
18834 transaction.c.upload.onprogress = null; |
|
18835 transaction.c.onload = null; |
|
18836 transaction.c.onerror = null; |
|
18837 } else if (Y.UA.ie && !transaction.e) { |
|
18838 // IE, when using XMLHttpRequest as an ActiveX Object, will throw |
|
18839 // a "Type Mismatch" error if the event handler is set to "null". |
|
18840 transaction.c.abort(); |
|
18841 } |
|
18842 } |
|
18843 |
|
18844 transaction = transaction.c = null; |
|
18845 }, |
|
18846 |
|
18847 /** |
|
18848 * Method for creating and firing events. |
|
18849 * |
|
18850 * @method _evt |
|
18851 * @private |
|
18852 * @param {String} eventName Event to be published. |
|
18853 * @param {Object} transaction Transaction object. |
|
18854 * @param {Object} config Configuration data subset for event subscription. |
|
18855 */ |
|
18856 _evt: function(eventName, transaction, config) { |
|
18857 var io = this, params, |
|
18858 args = config['arguments'], |
|
18859 emitFacade = io.cfg.emitFacade, |
|
18860 globalEvent = "io:" + eventName, |
|
18861 trnEvent = "io-trn:" + eventName; |
|
18862 |
|
18863 // Workaround for #2532107 |
|
18864 this.detach(trnEvent); |
|
18865 |
|
18866 if (transaction.e) { |
|
18867 transaction.c = { status: 0, statusText: transaction.e }; |
|
18868 } |
|
18869 |
|
18870 // Fire event with parameters or an Event Facade. |
|
18871 params = [ emitFacade ? |
|
18872 { |
|
18873 id: transaction.id, |
|
18874 data: transaction.c, |
|
18875 cfg: config, |
|
18876 'arguments': args |
|
18877 } : |
|
18878 transaction.id |
|
18879 ]; |
|
18880 |
|
18881 if (!emitFacade) { |
|
18882 if (eventName === EVENTS[0] || eventName === EVENTS[2]) { |
|
18883 if (args) { |
|
18884 params.push(args); |
|
18885 } |
|
18886 } else { |
|
18887 if (transaction.evt) { |
|
18888 params.push(transaction.evt); |
|
18889 } else { |
|
18890 params.push(transaction.c); |
|
18891 } |
|
18892 if (args) { |
|
18893 params.push(args); |
|
18894 } |
|
18895 } |
|
18896 } |
|
18897 |
|
18898 params.unshift(globalEvent); |
|
18899 // Fire global events. |
|
18900 io.fire.apply(io, params); |
|
18901 // Fire transaction events, if receivers are defined. |
|
18902 if (config.on) { |
|
18903 params[0] = trnEvent; |
|
18904 io.once(trnEvent, config.on[eventName], config.context || Y); |
|
18905 io.fire.apply(io, params); |
|
18906 } |
|
18907 }, |
|
18908 |
|
18909 /** |
|
18910 * Fires event "io:start" and creates, fires a transaction-specific |
|
18911 * start event, if `config.on.start` is defined. |
|
18912 * |
|
18913 * @method start |
|
18914 * @param {Object} transaction Transaction object. |
|
18915 * @param {Object} config Configuration object for the transaction. |
|
18916 */ |
|
18917 start: function(transaction, config) { |
|
18918 /** |
|
18919 * Signals the start of an IO request. |
|
18920 * @event io:start |
|
18921 */ |
|
18922 this._evt(EVENTS[0], transaction, config); |
|
18923 }, |
|
18924 |
|
18925 /** |
|
18926 * Fires event "io:complete" and creates, fires a |
|
18927 * transaction-specific "complete" event, if config.on.complete is |
|
18928 * defined. |
|
18929 * |
|
18930 * @method complete |
|
18931 * @param {Object} transaction Transaction object. |
|
18932 * @param {Object} config Configuration object for the transaction. |
|
18933 */ |
|
18934 complete: function(transaction, config) { |
|
18935 /** |
|
18936 * Signals the completion of the request-response phase of a |
|
18937 * transaction. Response status and data are accessible, if |
|
18938 * available, in this event. |
|
18939 * @event io:complete |
|
18940 */ |
|
18941 this._evt(EVENTS[1], transaction, config); |
|
18942 }, |
|
18943 |
|
18944 /** |
|
18945 * Fires event "io:end" and creates, fires a transaction-specific "end" |
|
18946 * event, if config.on.end is defined. |
|
18947 * |
|
18948 * @method end |
|
18949 * @param {Object} transaction Transaction object. |
|
18950 * @param {Object} config Configuration object for the transaction. |
|
18951 */ |
|
18952 end: function(transaction, config) { |
|
18953 /** |
|
18954 * Signals the end of the transaction lifecycle. |
|
18955 * @event io:end |
|
18956 */ |
|
18957 this._evt(EVENTS[2], transaction, config); |
|
18958 this._destroy(transaction); |
|
18959 }, |
|
18960 |
|
18961 /** |
|
18962 * Fires event "io:success" and creates, fires a transaction-specific |
|
18963 * "success" event, if config.on.success is defined. |
|
18964 * |
|
18965 * @method success |
|
18966 * @param {Object} transaction Transaction object. |
|
18967 * @param {Object} config Configuration object for the transaction. |
|
18968 */ |
|
18969 success: function(transaction, config) { |
|
18970 /** |
|
18971 * Signals an HTTP response with status in the 2xx range. |
|
18972 * Fires after io:complete. |
|
18973 * @event io:success |
|
18974 */ |
|
18975 this._evt(EVENTS[3], transaction, config); |
|
18976 this.end(transaction, config); |
|
18977 }, |
|
18978 |
|
18979 /** |
|
18980 * Fires event "io:failure" and creates, fires a transaction-specific |
|
18981 * "failure" event, if config.on.failure is defined. |
|
18982 * |
|
18983 * @method failure |
|
18984 * @param {Object} transaction Transaction object. |
|
18985 * @param {Object} config Configuration object for the transaction. |
|
18986 */ |
|
18987 failure: function(transaction, config) { |
|
18988 /** |
|
18989 * Signals an HTTP response with status outside of the 2xx range. |
|
18990 * Fires after io:complete. |
|
18991 * @event io:failure |
|
18992 */ |
|
18993 this._evt(EVENTS[4], transaction, config); |
|
18994 this.end(transaction, config); |
|
18995 }, |
|
18996 |
|
18997 /** |
|
18998 * Fires event "io:progress" and creates, fires a transaction-specific |
|
18999 * "progress" event -- for XMLHttpRequest file upload -- if |
|
19000 * config.on.progress is defined. |
|
19001 * |
|
19002 * @method progress |
|
19003 * @param {Object} transaction Transaction object. |
|
19004 * @param {Object} progress event. |
|
19005 * @param {Object} config Configuration object for the transaction. |
|
19006 */ |
|
19007 progress: function(transaction, e, config) { |
|
19008 /** |
|
19009 * Signals the interactive state during a file upload transaction. |
|
19010 * This event fires after io:start and before io:complete. |
|
19011 * @event io:progress |
|
19012 */ |
|
19013 transaction.evt = e; |
|
19014 this._evt(EVENTS[5], transaction, config); |
|
19015 }, |
|
19016 |
|
19017 /** |
|
19018 * Fires event "io:complete" and creates, fires a transaction-specific |
|
19019 * "complete" event -- for XMLHttpRequest file upload -- if |
|
19020 * config.on.complete is defined. |
|
19021 * |
|
19022 * @method load |
|
19023 * @param {Object} transaction Transaction object. |
|
19024 * @param {Object} load event. |
|
19025 * @param {Object} config Configuration object for the transaction. |
|
19026 */ |
|
19027 load: function (transaction, e, config) { |
|
19028 transaction.evt = e.target; |
|
19029 this._evt(EVENTS[1], transaction, config); |
|
19030 }, |
|
19031 |
|
19032 /** |
|
19033 * Fires event "io:failure" and creates, fires a transaction-specific |
|
19034 * "failure" event -- for XMLHttpRequest file upload -- if |
|
19035 * config.on.failure is defined. |
|
19036 * |
|
19037 * @method error |
|
19038 * @param {Object} transaction Transaction object. |
|
19039 * @param {Object} error event. |
|
19040 * @param {Object} config Configuration object for the transaction. |
|
19041 */ |
|
19042 error: function (transaction, e, config) { |
|
19043 transaction.evt = e; |
|
19044 this._evt(EVENTS[4], transaction, config); |
|
19045 }, |
|
19046 |
|
19047 /** |
|
19048 * Retry an XDR transaction, using the Flash tranport, if the native |
|
19049 * transport fails. |
|
19050 * |
|
19051 * @method _retry |
|
19052 * @private |
|
19053 * @param {Object} transaction Transaction object. |
|
19054 * @param {String} uri Qualified path to transaction resource. |
|
19055 * @param {Object} config Configuration object for the transaction. |
|
19056 */ |
|
19057 _retry: function(transaction, uri, config) { |
|
19058 this._destroy(transaction); |
|
19059 config.xdr.use = 'flash'; |
|
19060 return this.send(uri, config, transaction.id); |
|
19061 }, |
|
19062 |
|
19063 /** |
|
19064 * Method that concatenates string data for HTTP GET transactions. |
|
19065 * |
|
19066 * @method _concat |
|
19067 * @private |
|
19068 * @param {String} uri URI or root data. |
|
19069 * @param {String} data Data to be concatenated onto URI. |
|
19070 * @return {String} |
|
19071 */ |
|
19072 _concat: function(uri, data) { |
|
19073 uri += (uri.indexOf('?') === -1 ? '?' : '&') + data; |
|
19074 return uri; |
|
19075 }, |
|
19076 |
|
19077 /** |
|
19078 * Stores default client headers for all transactions. If a label is |
|
19079 * passed with no value argument, the header will be deleted. |
|
19080 * |
|
19081 * @method setHeader |
|
19082 * @param {String} name HTTP header |
|
19083 * @param {String} value HTTP header value |
|
19084 */ |
|
19085 setHeader: function(name, value) { |
|
19086 if (value) { |
|
19087 this._headers[name] = value; |
|
19088 } else { |
|
19089 delete this._headers[name]; |
|
19090 } |
|
19091 }, |
|
19092 |
|
19093 /** |
|
19094 * Method that sets all HTTP headers to be sent in a transaction. |
|
19095 * |
|
19096 * @method _setHeaders |
|
19097 * @private |
|
19098 * @param {Object} transaction - XHR instance for the specific transaction. |
|
19099 * @param {Object} headers - HTTP headers for the specific transaction, as |
|
19100 * defined in the configuration object passed to YUI.io(). |
|
19101 */ |
|
19102 _setHeaders: function(transaction, headers) { |
|
19103 headers = Y.merge(this._headers, headers); |
|
19104 Y.Object.each(headers, function(value, name) { |
|
19105 if (value !== 'disable') { |
|
19106 transaction.setRequestHeader(name, headers[name]); |
|
19107 } |
|
19108 }); |
|
19109 }, |
|
19110 |
|
19111 /** |
|
19112 * Starts timeout count if the configuration object has a defined |
|
19113 * timeout property. |
|
19114 * |
|
19115 * @method _startTimeout |
|
19116 * @private |
|
19117 * @param {Object} transaction Transaction object generated by _create(). |
|
19118 * @param {Object} timeout Timeout in milliseconds. |
|
19119 */ |
|
19120 _startTimeout: function(transaction, timeout) { |
|
19121 var io = this; |
|
19122 |
|
19123 io._timeout[transaction.id] = setTimeout(function() { |
|
19124 io._abort(transaction, 'timeout'); |
|
19125 }, timeout); |
|
19126 }, |
|
19127 |
|
19128 /** |
|
19129 * Clears the timeout interval started by _startTimeout(). |
|
19130 * |
|
19131 * @method _clearTimeout |
|
19132 * @private |
|
19133 * @param {Number} id - Transaction id. |
|
19134 */ |
|
19135 _clearTimeout: function(id) { |
|
19136 clearTimeout(this._timeout[id]); |
|
19137 delete this._timeout[id]; |
|
19138 }, |
|
19139 |
|
19140 /** |
|
19141 * Method that determines if a transaction response qualifies as success |
|
19142 * or failure, based on the response HTTP status code, and fires the |
|
19143 * appropriate success or failure events. |
|
19144 * |
|
19145 * @method _result |
|
19146 * @private |
|
19147 * @static |
|
19148 * @param {Object} transaction Transaction object generated by _create(). |
|
19149 * @param {Object} config Configuration object passed to io(). |
|
19150 */ |
|
19151 _result: function(transaction, config) { |
|
19152 var status; |
|
19153 // Firefox will throw an exception if attempting to access |
|
19154 // an XHR object's status property, after a request is aborted. |
|
19155 try { |
|
19156 status = transaction.c.status; |
|
19157 } catch(e) { |
|
19158 status = 0; |
|
19159 } |
|
19160 |
|
19161 // IE reports HTTP 204 as HTTP 1223. |
|
19162 if (status >= 200 && status < 300 || status === 304 || status === 1223) { |
|
19163 this.success(transaction, config); |
|
19164 } else { |
|
19165 this.failure(transaction, config); |
|
19166 } |
|
19167 }, |
|
19168 |
|
19169 /** |
|
19170 * Event handler bound to onreadystatechange. |
|
19171 * |
|
19172 * @method _rS |
|
19173 * @private |
|
19174 * @param {Object} transaction Transaction object generated by _create(). |
|
19175 * @param {Object} config Configuration object passed to YUI.io(). |
|
19176 */ |
|
19177 _rS: function(transaction, config) { |
|
19178 var io = this; |
|
19179 |
|
19180 if (transaction.c.readyState === 4) { |
|
19181 if (config.timeout) { |
|
19182 io._clearTimeout(transaction.id); |
|
19183 } |
|
19184 |
|
19185 // Yield in the event of request timeout or abort. |
|
19186 setTimeout(function() { |
|
19187 io.complete(transaction, config); |
|
19188 io._result(transaction, config); |
|
19189 }, 0); |
|
19190 } |
|
19191 }, |
|
19192 |
|
19193 /** |
|
19194 * Terminates a transaction due to an explicit abort or timeout. |
|
19195 * |
|
19196 * @method _abort |
|
19197 * @private |
|
19198 * @param {Object} transaction Transaction object generated by _create(). |
|
19199 * @param {String} type Identifies timed out or aborted transaction. |
|
19200 */ |
|
19201 _abort: function(transaction, type) { |
|
19202 if (transaction && transaction.c) { |
|
19203 transaction.e = type; |
|
19204 transaction.c.abort(); |
|
19205 } |
|
19206 }, |
|
19207 |
|
19208 /** |
|
19209 * Requests a transaction. `send()` is implemented as `Y.io()`. Each |
|
19210 * transaction may include a configuration object. Its properties are: |
|
19211 * |
|
19212 * <dl> |
|
19213 * <dt>method</dt> |
|
19214 * <dd>HTTP method verb (e.g., GET or POST). If this property is not |
|
19215 * not defined, the default value will be GET.</dd> |
|
19216 * |
|
19217 * <dt>data</dt> |
|
19218 * <dd>This is the name-value string that will be sent as the |
|
19219 * transaction data. If the request is HTTP GET, the data become |
|
19220 * part of querystring. If HTTP POST, the data are sent in the |
|
19221 * message body.</dd> |
|
19222 * |
|
19223 * <dt>xdr</dt> |
|
19224 * <dd>Defines the transport to be used for cross-domain requests. |
|
19225 * By setting this property, the transaction will use the specified |
|
19226 * transport instead of XMLHttpRequest. The properties of the |
|
19227 * transport object are: |
|
19228 * <dl> |
|
19229 * <dt>use</dt> |
|
19230 * <dd>The transport to be used: 'flash' or 'native'</dd> |
|
19231 * <dt>dataType</dt> |
|
19232 * <dd>Set the value to 'XML' if that is the expected response |
|
19233 * content type.</dd> |
|
19234 * <dt>credentials</dt> |
|
19235 * <dd>Set the value to 'true' to set XHR.withCredentials property to true.</dd> |
|
19236 * </dl></dd> |
|
19237 * |
|
19238 * <dt>form</dt> |
|
19239 * <dd>Form serialization configuration object. Its properties are: |
|
19240 * <dl> |
|
19241 * <dt>id</dt> |
|
19242 * <dd>Node object or id of HTML form</dd> |
|
19243 * <dt>useDisabled</dt> |
|
19244 * <dd>`true` to also serialize disabled form field values |
|
19245 * (defaults to `false`)</dd> |
|
19246 * </dl></dd> |
|
19247 * |
|
19248 * <dt>on</dt> |
|
19249 * <dd>Assigns transaction event subscriptions. Available events are: |
|
19250 * <dl> |
|
19251 * <dt>start</dt> |
|
19252 * <dd>Fires when a request is sent to a resource.</dd> |
|
19253 * <dt>complete</dt> |
|
19254 * <dd>Fires when the transaction is complete.</dd> |
|
19255 * <dt>success</dt> |
|
19256 * <dd>Fires when the HTTP response status is within the 2xx |
|
19257 * range.</dd> |
|
19258 * <dt>failure</dt> |
|
19259 * <dd>Fires when the HTTP response status is outside the 2xx |
|
19260 * range, if an exception occurs, if the transation is aborted, |
|
19261 * or if the transaction exceeds a configured `timeout`.</dd> |
|
19262 * <dt>end</dt> |
|
19263 * <dd>Fires at the conclusion of the transaction |
|
19264 * lifecycle, after `success` or `failure`.</dd> |
|
19265 * </dl> |
|
19266 * |
|
19267 * <p>Callback functions for `start` and `end` receive the id of the |
|
19268 * transaction as a first argument. For `complete`, `success`, and |
|
19269 * `failure`, callbacks receive the id and the response object |
|
19270 * (usually the XMLHttpRequest instance). If the `arguments` |
|
19271 * property was included in the configuration object passed to |
|
19272 * `Y.io()`, the configured data will be passed to all callbacks as |
|
19273 * the last argument.</p> |
|
19274 * </dd> |
|
19275 * |
|
19276 * <dt>sync</dt> |
|
19277 * <dd>Pass `true` to make a same-domain transaction synchronous. |
|
19278 * <strong>CAVEAT</strong>: This will negatively impact the user |
|
19279 * experience. Have a <em>very</em> good reason if you intend to use |
|
19280 * this.</dd> |
|
19281 * |
|
19282 * <dt>context</dt> |
|
19283 * <dd>The "`this'" object for all configured event handlers. If a |
|
19284 * specific context is needed for individual callbacks, bind the |
|
19285 * callback to a context using `Y.bind()`.</dd> |
|
19286 * |
|
19287 * <dt>headers</dt> |
|
19288 * <dd>Object map of transaction headers to send to the server. The |
|
19289 * object keys are the header names and the values are the header |
|
19290 * values.</dd> |
|
19291 * |
|
19292 * <dt>timeout</dt> |
|
19293 * <dd>Millisecond threshold for the transaction before being |
|
19294 * automatically aborted.</dd> |
|
19295 * |
|
19296 * <dt>arguments</dt> |
|
19297 * <dd>User-defined data passed to all registered event handlers. |
|
19298 * This value is available as the second argument in the "start" and |
|
19299 * "end" event handlers. It is the third argument in the "complete", |
|
19300 * "success", and "failure" event handlers. <strong>Be sure to quote |
|
19301 * this property name in the transaction configuration as |
|
19302 * "arguments" is a reserved word in JavaScript</strong> (e.g. |
|
19303 * `Y.io({ ..., "arguments": stuff })`).</dd> |
|
19304 * </dl> |
|
19305 * |
|
19306 * @method send |
|
19307 * @public |
|
19308 * @param {String} uri Qualified path to transaction resource. |
|
19309 * @param {Object} config Configuration object for the transaction. |
|
19310 * @param {Number} id Transaction id, if already set. |
|
19311 * @return {Object} |
|
19312 */ |
|
19313 send: function(uri, config, id) { |
|
19314 var transaction, method, i, len, sync, data, |
|
19315 io = this, |
|
19316 u = uri, |
|
19317 response = {}; |
|
19318 |
|
19319 config = config ? Y.Object(config) : {}; |
|
19320 transaction = io._create(config, id); |
|
19321 method = config.method ? config.method.toUpperCase() : 'GET'; |
|
19322 sync = config.sync; |
|
19323 data = config.data; |
|
19324 |
|
19325 // Serialize a map object into a key-value string using |
|
19326 // querystring-stringify-simple. |
|
19327 if ((Y.Lang.isObject(data) && !data.nodeType) && !transaction.upload) { |
|
19328 if (Y.QueryString && Y.QueryString.stringify) { |
|
19329 Y.log('Stringifying config.data for request', 'info', 'io'); |
|
19330 config.data = data = Y.QueryString.stringify(data); |
|
19331 } else { |
|
19332 Y.log('Failed to stringify config.data object, likely because `querystring-stringify-simple` is missing.', 'warn', 'io'); |
|
19333 } |
|
19334 } |
|
19335 |
|
19336 if (config.form) { |
|
19337 if (config.form.upload) { |
|
19338 // This is a file upload transaction, calling |
|
19339 // upload() in io-upload-iframe. |
|
19340 return io.upload(transaction, uri, config); |
|
19341 } else { |
|
19342 // Serialize HTML form data into a key-value string. |
|
19343 data = io._serialize(config.form, data); |
|
19344 } |
|
19345 } |
|
19346 |
|
19347 // Convert falsy values to an empty string. This way IE can't be |
|
19348 // rediculous and translate `undefined` to "undefined". |
|
19349 data || (data = ''); |
|
19350 |
|
19351 if (data) { |
|
19352 switch (method) { |
|
19353 case 'GET': |
|
19354 case 'HEAD': |
|
19355 case 'DELETE': |
|
19356 u = io._concat(u, data); |
|
19357 data = ''; |
|
19358 Y.log('HTTP' + method + ' with data. The querystring is: ' + u, 'info', 'io'); |
|
19359 break; |
|
19360 case 'POST': |
|
19361 case 'PUT': |
|
19362 // If Content-Type is defined in the configuration object, or |
|
19363 // or as a default header, it will be used instead of |
|
19364 // 'application/x-www-form-urlencoded; charset=UTF-8' |
|
19365 config.headers = Y.merge({ |
|
19366 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' |
|
19367 }, config.headers); |
|
19368 break; |
|
19369 } |
|
19370 } |
|
19371 |
|
19372 if (transaction.xdr) { |
|
19373 // Route data to io-xdr module for flash and XDomainRequest. |
|
19374 return io.xdr(u, transaction, config); |
|
19375 } |
|
19376 else if (transaction.notify) { |
|
19377 // Route data to custom transport |
|
19378 return transaction.c.send(transaction, uri, config); |
|
19379 } |
|
19380 |
|
19381 if (!sync && !transaction.upload) { |
|
19382 transaction.c.onreadystatechange = function() { |
|
19383 io._rS(transaction, config); |
|
19384 }; |
|
19385 } |
|
19386 |
|
19387 try { |
|
19388 // Determine if request is to be set as |
|
19389 // synchronous or asynchronous. |
|
19390 transaction.c.open(method, u, !sync, config.username || null, config.password || null); |
|
19391 io._setHeaders(transaction.c, config.headers || {}); |
|
19392 io.start(transaction, config); |
|
19393 |
|
19394 // Will work only in browsers that implement the |
|
19395 // Cross-Origin Resource Sharing draft. |
|
19396 if (config.xdr && config.xdr.credentials && SUPPORTS_CORS) { |
|
19397 transaction.c.withCredentials = true; |
|
19398 } |
|
19399 |
|
19400 // Using "null" with HTTP POST will result in a request |
|
19401 // with no Content-Length header defined. |
|
19402 transaction.c.send(data); |
|
19403 |
|
19404 if (sync) { |
|
19405 // Create a response object for synchronous transactions, |
|
19406 // mixing id and arguments properties with the xhr |
|
19407 // properties whitelist. |
|
19408 for (i = 0, len = XHR_PROPS.length; i < len; ++i) { |
|
19409 response[XHR_PROPS[i]] = transaction.c[XHR_PROPS[i]]; |
|
19410 } |
|
19411 |
|
19412 response.getAllResponseHeaders = function() { |
|
19413 return transaction.c.getAllResponseHeaders(); |
|
19414 }; |
|
19415 |
|
19416 response.getResponseHeader = function(name) { |
|
19417 return transaction.c.getResponseHeader(name); |
|
19418 }; |
|
19419 |
|
19420 io.complete(transaction, config); |
|
19421 io._result(transaction, config); |
|
19422 |
|
19423 return response; |
|
19424 } |
|
19425 } catch(e) { |
|
19426 if (transaction.xdr) { |
|
19427 // This exception is usually thrown by browsers |
|
19428 // that do not support XMLHttpRequest Level 2. |
|
19429 // Retry the request with the XDR transport set |
|
19430 // to 'flash'. If the Flash transport is not |
|
19431 // initialized or available, the transaction |
|
19432 // will resolve to a transport error. |
|
19433 return io._retry(transaction, uri, config); |
|
19434 } else { |
|
19435 io.complete(transaction, config); |
|
19436 io._result(transaction, config); |
|
19437 } |
|
19438 } |
|
19439 |
|
19440 // If config.timeout is defined, and the request is standard XHR, |
|
19441 // initialize timeout polling. |
|
19442 if (config.timeout) { |
|
19443 io._startTimeout(transaction, config.timeout); |
|
19444 Y.log('Configuration timeout set to: ' + config.timeout, 'info', 'io'); |
|
19445 } |
|
19446 |
|
19447 return { |
|
19448 id: transaction.id, |
|
19449 abort: function() { |
|
19450 return transaction.c ? io._abort(transaction, 'abort') : false; |
|
19451 }, |
|
19452 isInProgress: function() { |
|
19453 return transaction.c ? (transaction.c.readyState % 4) : false; |
|
19454 }, |
|
19455 io: io |
|
19456 }; |
|
19457 } |
|
19458 }; |
|
19459 |
|
19460 /** |
|
19461 Method for initiating an ajax call. The first argument is the url end |
|
19462 point for the call. The second argument is an object to configure the |
|
19463 transaction and attach event subscriptions. The configuration object |
|
19464 supports the following properties: |
|
19465 |
|
19466 <dl> |
|
19467 <dt>method</dt> |
|
19468 <dd>HTTP method verb (e.g., GET or POST). If this property is not |
|
19469 not defined, the default value will be GET.</dd> |
|
19470 |
|
19471 <dt>data</dt> |
|
19472 <dd>This is the name-value string that will be sent as the |
|
19473 transaction data. If the request is HTTP GET, the data become |
|
19474 part of querystring. If HTTP POST, the data are sent in the |
|
19475 message body.</dd> |
|
19476 |
|
19477 <dt>xdr</dt> |
|
19478 <dd>Defines the transport to be used for cross-domain requests. |
|
19479 By setting this property, the transaction will use the specified |
|
19480 transport instead of XMLHttpRequest. The properties of the |
|
19481 transport object are: |
|
19482 <dl> |
|
19483 <dt>use</dt> |
|
19484 <dd>The transport to be used: 'flash' or 'native'</dd> |
|
19485 <dt>dataType</dt> |
|
19486 <dd>Set the value to 'XML' if that is the expected response |
|
19487 content type.</dd> |
|
19488 </dl></dd> |
|
19489 |
|
19490 <dt>form</dt> |
|
19491 <dd>Form serialization configuration object. Its properties are: |
|
19492 <dl> |
|
19493 <dt>id</dt> |
|
19494 <dd>Node object or id of HTML form</dd> |
|
19495 <dt>useDisabled</dt> |
|
19496 <dd>`true` to also serialize disabled form field values |
|
19497 (defaults to `false`)</dd> |
|
19498 </dl></dd> |
|
19499 |
|
19500 <dt>on</dt> |
|
19501 <dd>Assigns transaction event subscriptions. Available events are: |
|
19502 <dl> |
|
19503 <dt>start</dt> |
|
19504 <dd>Fires when a request is sent to a resource.</dd> |
|
19505 <dt>complete</dt> |
|
19506 <dd>Fires when the transaction is complete.</dd> |
|
19507 <dt>success</dt> |
|
19508 <dd>Fires when the HTTP response status is within the 2xx |
|
19509 range.</dd> |
|
19510 <dt>failure</dt> |
|
19511 <dd>Fires when the HTTP response status is outside the 2xx |
|
19512 range, if an exception occurs, if the transation is aborted, |
|
19513 or if the transaction exceeds a configured `timeout`.</dd> |
|
19514 <dt>end</dt> |
|
19515 <dd>Fires at the conclusion of the transaction |
|
19516 lifecycle, after `success` or `failure`.</dd> |
|
19517 </dl> |
|
19518 |
|
19519 <p>Callback functions for `start` and `end` receive the id of the |
|
19520 transaction as a first argument. For `complete`, `success`, and |
|
19521 `failure`, callbacks receive the id and the response object |
|
19522 (usually the XMLHttpRequest instance). If the `arguments` |
|
19523 property was included in the configuration object passed to |
|
19524 `Y.io()`, the configured data will be passed to all callbacks as |
|
19525 the last argument.</p> |
|
19526 </dd> |
|
19527 |
|
19528 <dt>sync</dt> |
|
19529 <dd>Pass `true` to make a same-domain transaction synchronous. |
|
19530 <strong>CAVEAT</strong>: This will negatively impact the user |
|
19531 experience. Have a <em>very</em> good reason if you intend to use |
|
19532 this.</dd> |
|
19533 |
|
19534 <dt>context</dt> |
|
19535 <dd>The "`this'" object for all configured event handlers. If a |
|
19536 specific context is needed for individual callbacks, bind the |
|
19537 callback to a context using `Y.bind()`.</dd> |
|
19538 |
|
19539 <dt>headers</dt> |
|
19540 <dd>Object map of transaction headers to send to the server. The |
|
19541 object keys are the header names and the values are the header |
|
19542 values.</dd> |
|
19543 |
|
19544 <dt>timeout</dt> |
|
19545 <dd>Millisecond threshold for the transaction before being |
|
19546 automatically aborted.</dd> |
|
19547 |
|
19548 <dt>arguments</dt> |
|
19549 <dd>User-defined data passed to all registered event handlers. |
|
19550 This value is available as the second argument in the "start" and |
|
19551 "end" event handlers. It is the third argument in the "complete", |
|
19552 "success", and "failure" event handlers. <strong>Be sure to quote |
|
19553 this property name in the transaction configuration as |
|
19554 "arguments" is a reserved word in JavaScript</strong> (e.g. |
|
19555 `Y.io({ ..., "arguments": stuff })`).</dd> |
|
19556 </dl> |
|
19557 |
|
19558 @method io |
|
19559 @static |
|
19560 @param {String} url qualified path to transaction resource. |
|
19561 @param {Object} config configuration object for the transaction. |
|
19562 @return {Object} |
|
19563 @for YUI |
|
19564 **/ |
|
19565 Y.io = function(url, config) { |
|
19566 // Calling IO through the static interface will use and reuse |
|
19567 // an instance of IO. |
|
19568 var transaction = Y.io._map['io:0'] || new IO(); |
|
19569 return transaction.send.apply(transaction, [url, config]); |
|
19570 }; |
|
19571 |
|
19572 /** |
|
19573 Method for setting and deleting IO HTTP headers to be sent with every |
|
19574 request. |
|
19575 |
|
19576 Hosted as a property on the `io` function (e.g. `Y.io.header`). |
|
19577 |
|
19578 @method header |
|
19579 @param {String} name HTTP header |
|
19580 @param {String} value HTTP header value |
|
19581 @static |
|
19582 **/ |
|
19583 Y.io.header = function(name, value) { |
|
19584 // Calling IO through the static interface will use and reuse |
|
19585 // an instance of IO. |
|
19586 var transaction = Y.io._map['io:0'] || new IO(); |
|
19587 transaction.setHeader(name, value); |
|
19588 }; |
|
19589 |
|
19590 Y.IO = IO; |
|
19591 // Map of all IO instances created. |
|
19592 Y.io._map = {}; |
|
19593 var XHR = win && win.XMLHttpRequest, |
|
19594 XDR = win && win.XDomainRequest, |
|
19595 AX = win && win.ActiveXObject, |
|
19596 |
|
19597 // Checks for the presence of the `withCredentials` in an XHR instance |
|
19598 // object, which will be present if the environment supports CORS. |
|
19599 SUPPORTS_CORS = XHR && 'withCredentials' in (new XMLHttpRequest()); |
|
19600 |
|
19601 |
|
19602 Y.mix(Y.IO, { |
|
19603 /** |
|
19604 * The ID of the default IO transport, defaults to `xhr` |
|
19605 * @property _default |
|
19606 * @type {String} |
|
19607 * @static |
|
19608 */ |
|
19609 _default: 'xhr', |
|
19610 /** |
|
19611 * |
|
19612 * @method defaultTransport |
|
19613 * @static |
|
19614 * @param {String} [id] The transport to set as the default, if empty a new transport is created. |
|
19615 * @return {Object} The transport object with a `send` method |
|
19616 */ |
|
19617 defaultTransport: function(id) { |
|
19618 if (id) { |
|
19619 Y.log('Setting default IO to: ' + id, 'info', 'io'); |
|
19620 Y.IO._default = id; |
|
19621 } else { |
|
19622 var o = { |
|
19623 c: Y.IO.transports[Y.IO._default](), |
|
19624 notify: Y.IO._default === 'xhr' ? false : true |
|
19625 }; |
|
19626 Y.log('Creating default transport: ' + Y.IO._default, 'info', 'io'); |
|
19627 return o; |
|
19628 } |
|
19629 }, |
|
19630 /** |
|
19631 * An object hash of custom transports available to IO |
|
19632 * @property transports |
|
19633 * @type {Object} |
|
19634 * @static |
|
19635 */ |
|
19636 transports: { |
|
19637 xhr: function () { |
|
19638 return XHR ? new XMLHttpRequest() : |
|
19639 AX ? new ActiveXObject('Microsoft.XMLHTTP') : null; |
|
19640 }, |
|
19641 xdr: function () { |
|
19642 return XDR ? new XDomainRequest() : null; |
|
19643 }, |
|
19644 iframe: function () { return {}; }, |
|
19645 flash: null, |
|
19646 nodejs: null |
|
19647 }, |
|
19648 /** |
|
19649 * Create a custom transport of type and return it's object |
|
19650 * @method customTransport |
|
19651 * @param {String} id The id of the transport to create. |
|
19652 * @static |
|
19653 */ |
|
19654 customTransport: function(id) { |
|
19655 var o = { c: Y.IO.transports[id]() }; |
|
19656 |
|
19657 o[(id === 'xdr' || id === 'flash') ? 'xdr' : 'notify'] = true; |
|
19658 return o; |
|
19659 } |
|
19660 }); |
|
19661 |
|
19662 Y.mix(Y.IO.prototype, { |
|
19663 /** |
|
19664 * Fired from the notify method of the transport which in turn fires |
|
19665 * the event on the IO object. |
|
19666 * @method notify |
|
19667 * @param {String} event The name of the event |
|
19668 * @param {Object} transaction The transaction object |
|
19669 * @param {Object} config The configuration object for this transaction |
|
19670 */ |
|
19671 notify: function(event, transaction, config) { |
|
19672 var io = this; |
|
19673 |
|
19674 switch (event) { |
|
19675 case 'timeout': |
|
19676 case 'abort': |
|
19677 case 'transport error': |
|
19678 transaction.c = { status: 0, statusText: event }; |
|
19679 event = 'failure'; |
|
19680 default: |
|
19681 io[event].apply(io, [transaction, config]); |
|
19682 } |
|
19683 } |
|
19684 }); |
|
19685 |
|
19686 |
|
19687 |
|
19688 |
|
19689 }, '@VERSION@', {"requires": ["event-custom-base", "querystring-stringify-simple"]}); |
|
19690 YUI.add('json-parse', function (Y, NAME) { |
|
19691 |
|
19692 var _JSON = Y.config.global.JSON; |
|
19693 |
|
19694 Y.namespace('JSON').parse = function (obj, reviver, space) { |
|
19695 return _JSON.parse((typeof obj === 'string' ? obj : obj + ''), reviver, space); |
|
19696 }; |
|
19697 |
|
19698 |
|
19699 }, '@VERSION@', {"requires": ["yui-base"]}); |
|
19700 YUI.add('transition', function (Y, NAME) { |
|
19701 |
|
19702 /** |
|
19703 * Provides the transition method for Node. |
|
19704 * Transition has no API of its own, but adds the transition method to Node. |
|
19705 * |
|
19706 * @module transition |
|
19707 * @requires node-style |
|
19708 */ |
|
19709 |
|
19710 var CAMEL_VENDOR_PREFIX = '', |
|
19711 VENDOR_PREFIX = '', |
|
19712 DOCUMENT = Y.config.doc, |
|
19713 DOCUMENT_ELEMENT = 'documentElement', |
|
19714 DOCUMENT_STYLE = DOCUMENT[DOCUMENT_ELEMENT].style, |
|
19715 TRANSITION_CAMEL = 'transition', |
|
19716 TRANSITION_PROPERTY_CAMEL = 'transitionProperty', |
|
19717 TRANSITION_PROPERTY, |
|
19718 TRANSITION_DURATION, |
|
19719 TRANSITION_TIMING_FUNCTION, |
|
19720 TRANSITION_DELAY, |
|
19721 TRANSITION_END, |
|
19722 ON_TRANSITION_END, |
|
19723 |
|
19724 EMPTY_OBJ = {}, |
|
19725 |
|
19726 VENDORS = [ |
|
19727 'Webkit', |
|
19728 'Moz' |
|
19729 ], |
|
19730 |
|
19731 VENDOR_TRANSITION_END = { |
|
19732 Webkit: 'webkitTransitionEnd' |
|
19733 }, |
|
19734 |
|
19735 /** |
|
19736 * A class for constructing transition instances. |
|
19737 * Adds the "transition" method to Node. |
|
19738 * @class Transition |
|
19739 * @constructor |
|
19740 */ |
|
19741 |
|
19742 Transition = function() { |
|
19743 this.init.apply(this, arguments); |
|
19744 }; |
|
19745 |
|
19746 // One off handling of transform-prefixing. |
|
19747 Transition._TRANSFORM = 'transform'; |
|
19748 |
|
19749 Transition._toCamel = function(property) { |
|
19750 property = property.replace(/-([a-z])/gi, function(m0, m1) { |
|
19751 return m1.toUpperCase(); |
|
19752 }); |
|
19753 |
|
19754 return property; |
|
19755 }; |
|
19756 |
|
19757 Transition._toHyphen = function(property) { |
|
19758 property = property.replace(/([A-Z]?)([a-z]+)([A-Z]?)/g, function(m0, m1, m2, m3) { |
|
19759 var str = ((m1) ? '-' + m1.toLowerCase() : '') + m2; |
|
19760 |
|
19761 if (m3) { |
|
19762 str += '-' + m3.toLowerCase(); |
|
19763 } |
|
19764 |
|
19765 return str; |
|
19766 }); |
|
19767 |
|
19768 return property; |
|
19769 }; |
|
19770 |
|
19771 Transition.SHOW_TRANSITION = 'fadeIn'; |
|
19772 Transition.HIDE_TRANSITION = 'fadeOut'; |
|
19773 |
|
19774 Transition.useNative = false; |
|
19775 |
|
19776 // Map transition properties to vendor-specific versions. |
|
19777 if ('transition' in DOCUMENT_STYLE |
|
19778 && 'transitionProperty' in DOCUMENT_STYLE |
|
19779 && 'transitionDuration' in DOCUMENT_STYLE |
|
19780 && 'transitionTimingFunction' in DOCUMENT_STYLE |
|
19781 && 'transitionDelay' in DOCUMENT_STYLE) { |
|
19782 Transition.useNative = true; |
|
19783 Transition.supported = true; // TODO: remove |
|
19784 } else { |
|
19785 Y.Array.each(VENDORS, function(val) { // then vendor specific |
|
19786 var property = val + 'Transition'; |
|
19787 if (property in DOCUMENT[DOCUMENT_ELEMENT].style) { |
|
19788 CAMEL_VENDOR_PREFIX = val; |
|
19789 VENDOR_PREFIX = Transition._toHyphen(val) + '-'; |
|
19790 |
|
19791 Transition.useNative = true; |
|
19792 Transition.supported = true; // TODO: remove |
|
19793 Transition._VENDOR_PREFIX = val; |
|
19794 } |
|
19795 }); |
|
19796 } |
|
19797 |
|
19798 // Map transform property to vendor-specific versions. |
|
19799 // One-off required for cssText injection. |
|
19800 if (typeof DOCUMENT_STYLE.transform === 'undefined') { |
|
19801 Y.Array.each(VENDORS, function(val) { // then vendor specific |
|
19802 var property = val + 'Transform'; |
|
19803 if (typeof DOCUMENT_STYLE[property] !== 'undefined') { |
|
19804 Transition._TRANSFORM = property; |
|
19805 } |
|
19806 }); |
|
19807 } |
|
19808 |
|
19809 if (CAMEL_VENDOR_PREFIX) { |
|
19810 TRANSITION_CAMEL = CAMEL_VENDOR_PREFIX + 'Transition'; |
|
19811 TRANSITION_PROPERTY_CAMEL = CAMEL_VENDOR_PREFIX + 'TransitionProperty'; |
|
19812 } |
|
19813 |
|
19814 TRANSITION_PROPERTY = VENDOR_PREFIX + 'transition-property'; |
|
19815 TRANSITION_DURATION = VENDOR_PREFIX + 'transition-duration'; |
|
19816 TRANSITION_TIMING_FUNCTION = VENDOR_PREFIX + 'transition-timing-function'; |
|
19817 TRANSITION_DELAY = VENDOR_PREFIX + 'transition-delay'; |
|
19818 |
|
19819 TRANSITION_END = 'transitionend'; |
|
19820 ON_TRANSITION_END = 'on' + CAMEL_VENDOR_PREFIX.toLowerCase() + 'transitionend'; |
|
19821 TRANSITION_END = VENDOR_TRANSITION_END[CAMEL_VENDOR_PREFIX] || TRANSITION_END; |
|
19822 |
|
19823 Transition.fx = {}; |
|
19824 Transition.toggles = {}; |
|
19825 |
|
19826 Transition._hasEnd = {}; |
|
19827 |
|
19828 Transition._reKeywords = /^(?:node|duration|iterations|easing|delay|on|onstart|onend)$/i; |
|
19829 |
|
19830 Y.Node.DOM_EVENTS[TRANSITION_END] = 1; |
|
19831 |
|
19832 Transition.NAME = 'transition'; |
|
19833 |
|
19834 Transition.DEFAULT_EASING = 'ease'; |
|
19835 Transition.DEFAULT_DURATION = 0.5; |
|
19836 Transition.DEFAULT_DELAY = 0; |
|
19837 |
|
19838 Transition._nodeAttrs = {}; |
|
19839 |
|
19840 Transition.prototype = { |
|
19841 constructor: Transition, |
|
19842 init: function(node, config) { |
|
19843 var anim = this; |
|
19844 anim._node = node; |
|
19845 if (!anim._running && config) { |
|
19846 anim._config = config; |
|
19847 node._transition = anim; // cache for reuse |
|
19848 |
|
19849 anim._duration = ('duration' in config) ? |
|
19850 config.duration: anim.constructor.DEFAULT_DURATION; |
|
19851 |
|
19852 anim._delay = ('delay' in config) ? |
|
19853 config.delay: anim.constructor.DEFAULT_DELAY; |
|
19854 |
|
19855 anim._easing = config.easing || anim.constructor.DEFAULT_EASING; |
|
19856 anim._count = 0; // track number of animated properties |
|
19857 anim._running = false; |
|
19858 |
|
19859 } |
|
19860 |
|
19861 return anim; |
|
19862 }, |
|
19863 |
|
19864 addProperty: function(prop, config) { |
|
19865 var anim = this, |
|
19866 node = this._node, |
|
19867 uid = Y.stamp(node), |
|
19868 nodeInstance = Y.one(node), |
|
19869 attrs = Transition._nodeAttrs[uid], |
|
19870 computed, |
|
19871 compareVal, |
|
19872 dur, |
|
19873 attr, |
|
19874 val; |
|
19875 |
|
19876 if (!attrs) { |
|
19877 attrs = Transition._nodeAttrs[uid] = {}; |
|
19878 } |
|
19879 |
|
19880 attr = attrs[prop]; |
|
19881 |
|
19882 // might just be a value |
|
19883 if (config && config.value !== undefined) { |
|
19884 val = config.value; |
|
19885 } else if (config !== undefined) { |
|
19886 val = config; |
|
19887 config = EMPTY_OBJ; |
|
19888 } |
|
19889 |
|
19890 if (typeof val === 'function') { |
|
19891 val = val.call(nodeInstance, nodeInstance); |
|
19892 } |
|
19893 |
|
19894 if (attr && attr.transition) { |
|
19895 // take control if another transition owns this property |
|
19896 if (attr.transition !== anim) { |
|
19897 attr.transition._count--; // remapping attr to this transition |
|
19898 } |
|
19899 } |
|
19900 |
|
19901 anim._count++; // properties per transition |
|
19902 |
|
19903 // make 0 async and fire events |
|
19904 dur = ((typeof config.duration !== 'undefined') ? config.duration : |
|
19905 anim._duration) || 0.0001; |
|
19906 |
|
19907 attrs[prop] = { |
|
19908 value: val, |
|
19909 duration: dur, |
|
19910 delay: (typeof config.delay !== 'undefined') ? config.delay : |
|
19911 anim._delay, |
|
19912 |
|
19913 easing: config.easing || anim._easing, |
|
19914 |
|
19915 transition: anim |
|
19916 }; |
|
19917 |
|
19918 // native end event doesnt fire when setting to same value |
|
19919 // supplementing with timer |
|
19920 // val may be a string or number (height: 0, etc), but computedStyle is always string |
|
19921 computed = Y.DOM.getComputedStyle(node, prop); |
|
19922 compareVal = (typeof val === 'string') ? computed : parseFloat(computed); |
|
19923 |
|
19924 if (Transition.useNative && compareVal === val) { |
|
19925 setTimeout(function() { |
|
19926 anim._onNativeEnd.call(node, { |
|
19927 propertyName: prop, |
|
19928 elapsedTime: dur |
|
19929 }); |
|
19930 }, dur * 1000); |
|
19931 } |
|
19932 }, |
|
19933 |
|
19934 removeProperty: function(prop) { |
|
19935 var anim = this, |
|
19936 attrs = Transition._nodeAttrs[Y.stamp(anim._node)]; |
|
19937 |
|
19938 if (attrs && attrs[prop]) { |
|
19939 delete attrs[prop]; |
|
19940 anim._count--; |
|
19941 } |
|
19942 |
|
19943 }, |
|
19944 |
|
19945 initAttrs: function(config) { |
|
19946 var attr, |
|
19947 node = this._node; |
|
19948 |
|
19949 if (config.transform && !config[Transition._TRANSFORM]) { |
|
19950 config[Transition._TRANSFORM] = config.transform; |
|
19951 delete config.transform; // TODO: copy |
|
19952 } |
|
19953 |
|
19954 for (attr in config) { |
|
19955 if (config.hasOwnProperty(attr) && !Transition._reKeywords.test(attr)) { |
|
19956 this.addProperty(attr, config[attr]); |
|
19957 |
|
19958 // when size is auto or % webkit starts from zero instead of computed |
|
19959 // (https://bugs.webkit.org/show_bug.cgi?id=16020) |
|
19960 // TODO: selective set |
|
19961 if (node.style[attr] === '') { |
|
19962 Y.DOM.setStyle(node, attr, Y.DOM.getComputedStyle(node, attr)); |
|
19963 } |
|
19964 } |
|
19965 } |
|
19966 }, |
|
19967 |
|
19968 /** |
|
19969 * Starts or an animation. |
|
19970 * @method run |
|
19971 * @chainable |
|
19972 * @private |
|
19973 */ |
|
19974 run: function(callback) { |
|
19975 var anim = this, |
|
19976 node = anim._node, |
|
19977 config = anim._config, |
|
19978 data = { |
|
19979 type: 'transition:start', |
|
19980 config: config |
|
19981 }; |
|
19982 |
|
19983 |
|
19984 if (!anim._running) { |
|
19985 anim._running = true; |
|
19986 |
|
19987 if (config.on && config.on.start) { |
|
19988 config.on.start.call(Y.one(node), data); |
|
19989 } |
|
19990 |
|
19991 anim.initAttrs(anim._config); |
|
19992 |
|
19993 anim._callback = callback; |
|
19994 anim._start(); |
|
19995 } |
|
19996 |
|
19997 |
|
19998 return anim; |
|
19999 }, |
|
20000 |
|
20001 _start: function() { |
|
20002 this._runNative(); |
|
20003 }, |
|
20004 |
|
20005 _prepDur: function(dur) { |
|
20006 dur = parseFloat(dur) * 1000; |
|
20007 |
|
20008 return dur + 'ms'; |
|
20009 }, |
|
20010 |
|
20011 _runNative: function() { |
|
20012 var anim = this, |
|
20013 node = anim._node, |
|
20014 uid = Y.stamp(node), |
|
20015 style = node.style, |
|
20016 computed = node.ownerDocument.defaultView.getComputedStyle(node), |
|
20017 attrs = Transition._nodeAttrs[uid], |
|
20018 cssText = '', |
|
20019 cssTransition = computed[Transition._toCamel(TRANSITION_PROPERTY)], |
|
20020 |
|
20021 transitionText = TRANSITION_PROPERTY + ': ', |
|
20022 duration = TRANSITION_DURATION + ': ', |
|
20023 easing = TRANSITION_TIMING_FUNCTION + ': ', |
|
20024 delay = TRANSITION_DELAY + ': ', |
|
20025 hyphy, |
|
20026 attr, |
|
20027 name; |
|
20028 |
|
20029 // preserve existing transitions |
|
20030 if (cssTransition !== 'all') { |
|
20031 transitionText += cssTransition + ','; |
|
20032 duration += computed[Transition._toCamel(TRANSITION_DURATION)] + ','; |
|
20033 easing += computed[Transition._toCamel(TRANSITION_TIMING_FUNCTION)] + ','; |
|
20034 delay += computed[Transition._toCamel(TRANSITION_DELAY)] + ','; |
|
20035 |
|
20036 } |
|
20037 |
|
20038 // run transitions mapped to this instance |
|
20039 for (name in attrs) { |
|
20040 hyphy = Transition._toHyphen(name); |
|
20041 attr = attrs[name]; |
|
20042 if ((attr = attrs[name]) && attr.transition === anim) { |
|
20043 if (name in node.style) { // only native styles allowed |
|
20044 duration += anim._prepDur(attr.duration) + ','; |
|
20045 delay += anim._prepDur(attr.delay) + ','; |
|
20046 easing += (attr.easing) + ','; |
|
20047 |
|
20048 transitionText += hyphy + ','; |
|
20049 cssText += hyphy + ': ' + attr.value + '; '; |
|
20050 } else { |
|
20051 this.removeProperty(name); |
|
20052 } |
|
20053 } |
|
20054 } |
|
20055 |
|
20056 transitionText = transitionText.replace(/,$/, ';'); |
|
20057 duration = duration.replace(/,$/, ';'); |
|
20058 easing = easing.replace(/,$/, ';'); |
|
20059 delay = delay.replace(/,$/, ';'); |
|
20060 |
|
20061 // only one native end event per node |
|
20062 if (!Transition._hasEnd[uid]) { |
|
20063 node.addEventListener(TRANSITION_END, anim._onNativeEnd, ''); |
|
20064 Transition._hasEnd[uid] = true; |
|
20065 |
|
20066 } |
|
20067 |
|
20068 style.cssText += transitionText + duration + easing + delay + cssText; |
|
20069 |
|
20070 }, |
|
20071 |
|
20072 _end: function(elapsed) { |
|
20073 var anim = this, |
|
20074 node = anim._node, |
|
20075 callback = anim._callback, |
|
20076 config = anim._config, |
|
20077 data = { |
|
20078 type: 'transition:end', |
|
20079 config: config, |
|
20080 elapsedTime: elapsed |
|
20081 }, |
|
20082 |
|
20083 nodeInstance = Y.one(node); |
|
20084 |
|
20085 anim._running = false; |
|
20086 anim._callback = null; |
|
20087 |
|
20088 if (node) { |
|
20089 if (config.on && config.on.end) { |
|
20090 setTimeout(function() { // IE: allow previous update to finish |
|
20091 config.on.end.call(nodeInstance, data); |
|
20092 |
|
20093 // nested to ensure proper fire order |
|
20094 if (callback) { |
|
20095 callback.call(nodeInstance, data); |
|
20096 } |
|
20097 |
|
20098 }, 1); |
|
20099 } else if (callback) { |
|
20100 setTimeout(function() { // IE: allow previous update to finish |
|
20101 callback.call(nodeInstance, data); |
|
20102 }, 1); |
|
20103 } |
|
20104 } |
|
20105 |
|
20106 }, |
|
20107 |
|
20108 _endNative: function(name) { |
|
20109 var node = this._node, |
|
20110 value = node.ownerDocument.defaultView.getComputedStyle(node, '')[Transition._toCamel(TRANSITION_PROPERTY)]; |
|
20111 |
|
20112 name = Transition._toHyphen(name); |
|
20113 if (typeof value === 'string') { |
|
20114 value = value.replace(new RegExp('(?:^|,\\s)' + name + ',?'), ','); |
|
20115 value = value.replace(/^,|,$/, ''); |
|
20116 node.style[TRANSITION_CAMEL] = value; |
|
20117 } |
|
20118 }, |
|
20119 |
|
20120 _onNativeEnd: function(e) { |
|
20121 var node = this, |
|
20122 uid = Y.stamp(node), |
|
20123 event = e,//e._event, |
|
20124 name = Transition._toCamel(event.propertyName), |
|
20125 elapsed = event.elapsedTime, |
|
20126 attrs = Transition._nodeAttrs[uid], |
|
20127 attr = attrs[name], |
|
20128 anim = (attr) ? attr.transition : null, |
|
20129 data, |
|
20130 config; |
|
20131 |
|
20132 if (anim) { |
|
20133 anim.removeProperty(name); |
|
20134 anim._endNative(name); |
|
20135 config = anim._config[name]; |
|
20136 |
|
20137 data = { |
|
20138 type: 'propertyEnd', |
|
20139 propertyName: name, |
|
20140 elapsedTime: elapsed, |
|
20141 config: config |
|
20142 }; |
|
20143 |
|
20144 if (config && config.on && config.on.end) { |
|
20145 config.on.end.call(Y.one(node), data); |
|
20146 } |
|
20147 |
|
20148 if (anim._count <= 0) { // after propertyEnd fires |
|
20149 anim._end(elapsed); |
|
20150 node.style[TRANSITION_PROPERTY_CAMEL] = ''; // clean up style |
|
20151 } |
|
20152 } |
|
20153 }, |
|
20154 |
|
20155 destroy: function() { |
|
20156 var anim = this, |
|
20157 node = anim._node; |
|
20158 |
|
20159 if (node) { |
|
20160 node.removeEventListener(TRANSITION_END, anim._onNativeEnd, false); |
|
20161 anim._node = null; |
|
20162 } |
|
20163 } |
|
20164 }; |
|
20165 |
|
20166 Y.Transition = Transition; |
|
20167 Y.TransitionNative = Transition; // TODO: remove |
|
20168 |
|
20169 /** |
|
20170 * Animate one or more css properties to a given value. Requires the "transition" module. |
|
20171 * <pre>example usage: |
|
20172 * Y.one('#demo').transition({ |
|
20173 * duration: 1, // in seconds, default is 0.5 |
|
20174 * easing: 'ease-out', // default is 'ease' |
|
20175 * delay: '1', // delay start for 1 second, default is 0 |
|
20176 * |
|
20177 * height: '10px', |
|
20178 * width: '10px', |
|
20179 * |
|
20180 * opacity: { // per property |
|
20181 * value: 0, |
|
20182 * duration: 2, |
|
20183 * delay: 2, |
|
20184 * easing: 'ease-in' |
|
20185 * } |
|
20186 * }); |
|
20187 * </pre> |
|
20188 * @for Node |
|
20189 * @method transition |
|
20190 * @param {Object} config An object containing one or more style properties, a duration and an easing. |
|
20191 * @param {Function} callback A function to run after the transition has completed. |
|
20192 * @chainable |
|
20193 */ |
|
20194 Y.Node.prototype.transition = function(name, config, callback) { |
|
20195 var |
|
20196 transitionAttrs = Transition._nodeAttrs[Y.stamp(this._node)], |
|
20197 anim = (transitionAttrs) ? transitionAttrs.transition || null : null, |
|
20198 fxConfig, |
|
20199 prop; |
|
20200 |
|
20201 if (typeof name === 'string') { // named effect, pull config from registry |
|
20202 if (typeof config === 'function') { |
|
20203 callback = config; |
|
20204 config = null; |
|
20205 } |
|
20206 |
|
20207 fxConfig = Transition.fx[name]; |
|
20208 |
|
20209 if (config && typeof config !== 'boolean') { |
|
20210 config = Y.clone(config); |
|
20211 |
|
20212 for (prop in fxConfig) { |
|
20213 if (fxConfig.hasOwnProperty(prop)) { |
|
20214 if (! (prop in config)) { |
|
20215 config[prop] = fxConfig[prop]; |
|
20216 } |
|
20217 } |
|
20218 } |
|
20219 } else { |
|
20220 config = fxConfig; |
|
20221 } |
|
20222 |
|
20223 } else { // name is a config, config is a callback or undefined |
|
20224 callback = config; |
|
20225 config = name; |
|
20226 } |
|
20227 |
|
20228 if (anim && !anim._running) { |
|
20229 anim.init(this, config); |
|
20230 } else { |
|
20231 anim = new Transition(this._node, config); |
|
20232 } |
|
20233 |
|
20234 anim.run(callback); |
|
20235 return this; |
|
20236 }; |
|
20237 |
|
20238 Y.Node.prototype.show = function(name, config, callback) { |
|
20239 this._show(); // show prior to transition |
|
20240 if (name && Y.Transition) { |
|
20241 if (typeof name !== 'string' && !name.push) { // named effect or array of effects supercedes default |
|
20242 if (typeof config === 'function') { |
|
20243 callback = config; |
|
20244 config = name; |
|
20245 } |
|
20246 name = Transition.SHOW_TRANSITION; |
|
20247 } |
|
20248 this.transition(name, config, callback); |
|
20249 } |
|
20250 else if (name && !Y.Transition) { Y.log('unable to transition show; missing transition module', 'warn', 'node'); } |
|
20251 return this; |
|
20252 }; |
|
20253 |
|
20254 Y.NodeList.prototype.show = function(name, config, callback) { |
|
20255 var nodes = this._nodes, |
|
20256 i = 0, |
|
20257 node; |
|
20258 |
|
20259 while ((node = nodes[i++])) { |
|
20260 Y.one(node).show(name, config, callback); |
|
20261 } |
|
20262 |
|
20263 return this; |
|
20264 }; |
|
20265 |
|
20266 |
|
20267 |
|
20268 var _wrapCallBack = function(anim, fn, callback) { |
|
20269 return function() { |
|
20270 if (fn) { |
|
20271 fn.call(anim); |
|
20272 } |
|
20273 if (callback && typeof callback === 'function') { |
|
20274 callback.apply(anim._node, arguments); |
|
20275 } |
|
20276 }; |
|
20277 }; |
|
20278 |
|
20279 Y.Node.prototype.hide = function(name, config, callback) { |
|
20280 if (name && Y.Transition) { |
|
20281 if (typeof config === 'function') { |
|
20282 callback = config; |
|
20283 config = null; |
|
20284 } |
|
20285 |
|
20286 callback = _wrapCallBack(this, this._hide, callback); // wrap with existing callback |
|
20287 if (typeof name !== 'string' && !name.push) { // named effect or array of effects supercedes default |
|
20288 if (typeof config === 'function') { |
|
20289 callback = config; |
|
20290 config = name; |
|
20291 } |
|
20292 name = Transition.HIDE_TRANSITION; |
|
20293 } |
|
20294 this.transition(name, config, callback); |
|
20295 } else if (name && !Y.Transition) { Y.log('unable to transition hide; missing transition module', 'warn', 'node'); |
|
20296 } else { |
|
20297 this._hide(); |
|
20298 } |
|
20299 return this; |
|
20300 }; |
|
20301 |
|
20302 Y.NodeList.prototype.hide = function(name, config, callback) { |
|
20303 var nodes = this._nodes, |
|
20304 i = 0, |
|
20305 node; |
|
20306 |
|
20307 while ((node = nodes[i++])) { |
|
20308 Y.one(node).hide(name, config, callback); |
|
20309 } |
|
20310 |
|
20311 return this; |
|
20312 }; |
|
20313 |
|
20314 /** |
|
20315 * Animate one or more css properties to a given value. Requires the "transition" module. |
|
20316 * <pre>example usage: |
|
20317 * Y.all('.demo').transition({ |
|
20318 * duration: 1, // in seconds, default is 0.5 |
|
20319 * easing: 'ease-out', // default is 'ease' |
|
20320 * delay: '1', // delay start for 1 second, default is 0 |
|
20321 * |
|
20322 * height: '10px', |
|
20323 * width: '10px', |
|
20324 * |
|
20325 * opacity: { // per property |
|
20326 * value: 0, |
|
20327 * duration: 2, |
|
20328 * delay: 2, |
|
20329 * easing: 'ease-in' |
|
20330 * } |
|
20331 * }); |
|
20332 * </pre> |
|
20333 * @for NodeList |
|
20334 * @method transition |
|
20335 * @param {Object} config An object containing one or more style properties, a duration and an easing. |
|
20336 * @param {Function} callback A function to run after the transition has completed. The callback fires |
|
20337 * once per item in the NodeList. |
|
20338 * @chainable |
|
20339 */ |
|
20340 Y.NodeList.prototype.transition = function(config, callback) { |
|
20341 var nodes = this._nodes, |
|
20342 i = 0, |
|
20343 node; |
|
20344 |
|
20345 while ((node = nodes[i++])) { |
|
20346 Y.one(node).transition(config, callback); |
|
20347 } |
|
20348 |
|
20349 return this; |
|
20350 }; |
|
20351 |
|
20352 Y.Node.prototype.toggleView = function(name, on, callback) { |
|
20353 this._toggles = this._toggles || []; |
|
20354 callback = arguments[arguments.length - 1]; |
|
20355 |
|
20356 if (typeof name !== 'string') { // no transition, just toggle |
|
20357 on = name; |
|
20358 this._toggleView(on, callback); // call original _toggleView in Y.Node |
|
20359 return; |
|
20360 } |
|
20361 |
|
20362 if (typeof on === 'function') { // Ignore "on" if used for callback argument. |
|
20363 on = undefined; |
|
20364 } |
|
20365 |
|
20366 if (typeof on === 'undefined' && name in this._toggles) { // reverse current toggle |
|
20367 on = ! this._toggles[name]; |
|
20368 } |
|
20369 |
|
20370 on = (on) ? 1 : 0; |
|
20371 if (on) { |
|
20372 this._show(); |
|
20373 } else { |
|
20374 callback = _wrapCallBack(this, this._hide, callback); |
|
20375 } |
|
20376 |
|
20377 this._toggles[name] = on; |
|
20378 this.transition(Y.Transition.toggles[name][on], callback); |
|
20379 |
|
20380 return this; |
|
20381 }; |
|
20382 |
|
20383 Y.NodeList.prototype.toggleView = function(name, on, callback) { |
|
20384 var nodes = this._nodes, |
|
20385 i = 0, |
|
20386 node; |
|
20387 |
|
20388 while ((node = nodes[i++])) { |
|
20389 node = Y.one(node); |
|
20390 node.toggleView.apply(node, arguments); |
|
20391 } |
|
20392 |
|
20393 return this; |
|
20394 }; |
|
20395 |
|
20396 Y.mix(Transition.fx, { |
|
20397 fadeOut: { |
|
20398 opacity: 0, |
|
20399 duration: 0.5, |
|
20400 easing: 'ease-out' |
|
20401 }, |
|
20402 |
|
20403 fadeIn: { |
|
20404 opacity: 1, |
|
20405 duration: 0.5, |
|
20406 easing: 'ease-in' |
|
20407 }, |
|
20408 |
|
20409 sizeOut: { |
|
20410 height: 0, |
|
20411 width: 0, |
|
20412 duration: 0.75, |
|
20413 easing: 'ease-out' |
|
20414 }, |
|
20415 |
|
20416 sizeIn: { |
|
20417 height: function(node) { |
|
20418 return node.get('scrollHeight') + 'px'; |
|
20419 }, |
|
20420 width: function(node) { |
|
20421 return node.get('scrollWidth') + 'px'; |
|
20422 }, |
|
20423 duration: 0.5, |
|
20424 easing: 'ease-in', |
|
20425 |
|
20426 on: { |
|
20427 start: function() { |
|
20428 var overflow = this.getStyle('overflow'); |
|
20429 if (overflow !== 'hidden') { // enable scrollHeight/Width |
|
20430 this.setStyle('overflow', 'hidden'); |
|
20431 this._transitionOverflow = overflow; |
|
20432 } |
|
20433 }, |
|
20434 |
|
20435 end: function() { |
|
20436 if (this._transitionOverflow) { // revert overridden value |
|
20437 this.setStyle('overflow', this._transitionOverflow); |
|
20438 delete this._transitionOverflow; |
|
20439 } |
|
20440 } |
|
20441 } |
|
20442 } |
|
20443 }); |
|
20444 |
|
20445 Y.mix(Transition.toggles, { |
|
20446 size: ['sizeOut', 'sizeIn'], |
|
20447 fade: ['fadeOut', 'fadeIn'] |
|
20448 }); |
|
20449 |
|
20450 |
|
20451 }, '@VERSION@', {"requires": ["node-style"]}); |
|
20452 YUI.add('selector-css2', function (Y, NAME) { |
|
20453 |
|
20454 /** |
|
20455 * The selector module provides helper methods allowing CSS2 Selectors to be used with DOM elements. |
|
20456 * @module dom |
|
20457 * @submodule selector-css2 |
|
20458 * @for Selector |
|
20459 */ |
|
20460 |
|
20461 /* |
|
20462 * Provides helper methods for collecting and filtering DOM elements. |
|
20463 */ |
|
20464 |
|
20465 var PARENT_NODE = 'parentNode', |
|
20466 TAG_NAME = 'tagName', |
|
20467 ATTRIBUTES = 'attributes', |
|
20468 COMBINATOR = 'combinator', |
|
20469 PSEUDOS = 'pseudos', |
|
20470 |
|
20471 Selector = Y.Selector, |
|
20472 |
|
20473 SelectorCSS2 = { |
|
20474 _reRegExpTokens: /([\^\$\?\[\]\*\+\-\.\(\)\|\\])/, |
|
20475 SORT_RESULTS: true, |
|
20476 |
|
20477 // TODO: better detection, document specific |
|
20478 _isXML: (function() { |
|
20479 var isXML = (Y.config.doc.createElement('div').tagName !== 'DIV'); |
|
20480 return isXML; |
|
20481 }()), |
|
20482 |
|
20483 /** |
|
20484 * Mapping of shorthand tokens to corresponding attribute selector |
|
20485 * @property shorthand |
|
20486 * @type object |
|
20487 */ |
|
20488 shorthand: { |
|
20489 '\\#(-?[_a-z0-9]+[-\\w\\uE000]*)': '[id=$1]', |
|
20490 '\\.(-?[_a-z]+[-\\w\\uE000]*)': '[className~=$1]' |
|
20491 }, |
|
20492 |
|
20493 /** |
|
20494 * List of operators and corresponding boolean functions. |
|
20495 * These functions are passed the attribute and the current node's value of the attribute. |
|
20496 * @property operators |
|
20497 * @type object |
|
20498 */ |
|
20499 operators: { |
|
20500 '': function(node, attr) { return Y.DOM.getAttribute(node, attr) !== ''; }, // Just test for existence of attribute |
|
20501 '~=': '(?:^|\\s+){val}(?:\\s+|$)', // space-delimited |
|
20502 '|=': '^{val}-?' // optional hyphen-delimited |
|
20503 }, |
|
20504 |
|
20505 pseudos: { |
|
20506 'first-child': function(node) { |
|
20507 return Y.DOM._children(node[PARENT_NODE])[0] === node; |
|
20508 } |
|
20509 }, |
|
20510 |
|
20511 _bruteQuery: function(selector, root, firstOnly) { |
|
20512 var ret = [], |
|
20513 nodes = [], |
|
20514 tokens = Selector._tokenize(selector), |
|
20515 token = tokens[tokens.length - 1], |
|
20516 rootDoc = Y.DOM._getDoc(root), |
|
20517 child, |
|
20518 id, |
|
20519 className, |
|
20520 tagName; |
|
20521 |
|
20522 if (token) { |
|
20523 // prefilter nodes |
|
20524 id = token.id; |
|
20525 className = token.className; |
|
20526 tagName = token.tagName || '*'; |
|
20527 |
|
20528 if (root.getElementsByTagName) { // non-IE lacks DOM api on doc frags |
|
20529 // try ID first, unless no root.all && root not in document |
|
20530 // (root.all works off document, but not getElementById) |
|
20531 if (id && (root.all || (root.nodeType === 9 || Y.DOM.inDoc(root)))) { |
|
20532 nodes = Y.DOM.allById(id, root); |
|
20533 // try className |
|
20534 } else if (className) { |
|
20535 nodes = root.getElementsByClassName(className); |
|
20536 } else { // default to tagName |
|
20537 nodes = root.getElementsByTagName(tagName); |
|
20538 } |
|
20539 |
|
20540 } else { // brute getElementsByTagName() |
|
20541 child = root.firstChild; |
|
20542 while (child) { |
|
20543 // only collect HTMLElements |
|
20544 // match tag to supplement missing getElementsByTagName |
|
20545 if (child.tagName && (tagName === '*' || child.tagName === tagName)) { |
|
20546 nodes.push(child); |
|
20547 } |
|
20548 child = child.nextSibling || child.firstChild; |
|
20549 } |
|
20550 } |
|
20551 if (nodes.length) { |
|
20552 ret = Selector._filterNodes(nodes, tokens, firstOnly); |
|
20553 } |
|
20554 } |
|
20555 |
|
20556 return ret; |
|
20557 }, |
|
20558 |
|
20559 _filterNodes: function(nodes, tokens, firstOnly) { |
|
20560 var i = 0, |
|
20561 j, |
|
20562 len = tokens.length, |
|
20563 n = len - 1, |
|
20564 result = [], |
|
20565 node = nodes[0], |
|
20566 tmpNode = node, |
|
20567 getters = Y.Selector.getters, |
|
20568 operator, |
|
20569 combinator, |
|
20570 token, |
|
20571 path, |
|
20572 pass, |
|
20573 value, |
|
20574 tests, |
|
20575 test; |
|
20576 |
|
20577 for (i = 0; (tmpNode = node = nodes[i++]);) { |
|
20578 n = len - 1; |
|
20579 path = null; |
|
20580 |
|
20581 testLoop: |
|
20582 while (tmpNode && tmpNode.tagName) { |
|
20583 token = tokens[n]; |
|
20584 tests = token.tests; |
|
20585 j = tests.length; |
|
20586 if (j && !pass) { |
|
20587 while ((test = tests[--j])) { |
|
20588 operator = test[1]; |
|
20589 if (getters[test[0]]) { |
|
20590 value = getters[test[0]](tmpNode, test[0]); |
|
20591 } else { |
|
20592 value = tmpNode[test[0]]; |
|
20593 if (test[0] === 'tagName' && !Selector._isXML) { |
|
20594 value = value.toUpperCase(); |
|
20595 } |
|
20596 if (typeof value != 'string' && value !== undefined && value.toString) { |
|
20597 value = value.toString(); // coerce for comparison |
|
20598 } else if (value === undefined && tmpNode.getAttribute) { |
|
20599 // use getAttribute for non-standard attributes |
|
20600 value = tmpNode.getAttribute(test[0], 2); // 2 === force string for IE |
|
20601 } |
|
20602 } |
|
20603 |
|
20604 if ((operator === '=' && value !== test[2]) || // fast path for equality |
|
20605 (typeof operator !== 'string' && // protect against String.test monkey-patch (Moo) |
|
20606 operator.test && !operator.test(value)) || // regex test |
|
20607 (!operator.test && // protect against RegExp as function (webkit) |
|
20608 typeof operator === 'function' && !operator(tmpNode, test[0], test[2]))) { // function test |
|
20609 |
|
20610 // skip non element nodes or non-matching tags |
|
20611 if ((tmpNode = tmpNode[path])) { |
|
20612 while (tmpNode && |
|
20613 (!tmpNode.tagName || |
|
20614 (token.tagName && token.tagName !== tmpNode.tagName)) |
|
20615 ) { |
|
20616 tmpNode = tmpNode[path]; |
|
20617 } |
|
20618 } |
|
20619 continue testLoop; |
|
20620 } |
|
20621 } |
|
20622 } |
|
20623 |
|
20624 n--; // move to next token |
|
20625 // now that we've passed the test, move up the tree by combinator |
|
20626 if (!pass && (combinator = token.combinator)) { |
|
20627 path = combinator.axis; |
|
20628 tmpNode = tmpNode[path]; |
|
20629 |
|
20630 // skip non element nodes |
|
20631 while (tmpNode && !tmpNode.tagName) { |
|
20632 tmpNode = tmpNode[path]; |
|
20633 } |
|
20634 |
|
20635 if (combinator.direct) { // one pass only |
|
20636 path = null; |
|
20637 } |
|
20638 |
|
20639 } else { // success if we made it this far |
|
20640 result.push(node); |
|
20641 if (firstOnly) { |
|
20642 return result; |
|
20643 } |
|
20644 break; |
|
20645 } |
|
20646 } |
|
20647 } |
|
20648 node = tmpNode = null; |
|
20649 return result; |
|
20650 }, |
|
20651 |
|
20652 combinators: { |
|
20653 ' ': { |
|
20654 axis: 'parentNode' |
|
20655 }, |
|
20656 |
|
20657 '>': { |
|
20658 axis: 'parentNode', |
|
20659 direct: true |
|
20660 }, |
|
20661 |
|
20662 |
|
20663 '+': { |
|
20664 axis: 'previousSibling', |
|
20665 direct: true |
|
20666 } |
|
20667 }, |
|
20668 |
|
20669 _parsers: [ |
|
20670 { |
|
20671 name: ATTRIBUTES, |
|
20672 re: /^\uE003(-?[a-z]+[\w\-]*)+([~\|\^\$\*!=]=?)?['"]?([^\uE004'"]*)['"]?\uE004/i, |
|
20673 fn: function(match, token) { |
|
20674 var operator = match[2] || '', |
|
20675 operators = Selector.operators, |
|
20676 escVal = (match[3]) ? match[3].replace(/\\/g, '') : '', |
|
20677 test; |
|
20678 |
|
20679 // add prefiltering for ID and CLASS |
|
20680 if ((match[1] === 'id' && operator === '=') || |
|
20681 (match[1] === 'className' && |
|
20682 Y.config.doc.documentElement.getElementsByClassName && |
|
20683 (operator === '~=' || operator === '='))) { |
|
20684 token.prefilter = match[1]; |
|
20685 |
|
20686 |
|
20687 match[3] = escVal; |
|
20688 |
|
20689 // escape all but ID for prefilter, which may run through QSA (via Dom.allById) |
|
20690 token[match[1]] = (match[1] === 'id') ? match[3] : escVal; |
|
20691 |
|
20692 } |
|
20693 |
|
20694 // add tests |
|
20695 if (operator in operators) { |
|
20696 test = operators[operator]; |
|
20697 if (typeof test === 'string') { |
|
20698 match[3] = escVal.replace(Selector._reRegExpTokens, '\\$1'); |
|
20699 test = new RegExp(test.replace('{val}', match[3])); |
|
20700 } |
|
20701 match[2] = test; |
|
20702 } |
|
20703 if (!token.last || token.prefilter !== match[1]) { |
|
20704 return match.slice(1); |
|
20705 } |
|
20706 } |
|
20707 }, |
|
20708 { |
|
20709 name: TAG_NAME, |
|
20710 re: /^((?:-?[_a-z]+[\w-]*)|\*)/i, |
|
20711 fn: function(match, token) { |
|
20712 var tag = match[1]; |
|
20713 |
|
20714 if (!Selector._isXML) { |
|
20715 tag = tag.toUpperCase(); |
|
20716 } |
|
20717 |
|
20718 token.tagName = tag; |
|
20719 |
|
20720 if (tag !== '*' && (!token.last || token.prefilter)) { |
|
20721 return [TAG_NAME, '=', tag]; |
|
20722 } |
|
20723 if (!token.prefilter) { |
|
20724 token.prefilter = 'tagName'; |
|
20725 } |
|
20726 } |
|
20727 }, |
|
20728 { |
|
20729 name: COMBINATOR, |
|
20730 re: /^\s*([>+~]|\s)\s*/, |
|
20731 fn: function(match, token) { |
|
20732 } |
|
20733 }, |
|
20734 { |
|
20735 name: PSEUDOS, |
|
20736 re: /^:([\-\w]+)(?:\uE005['"]?([^\uE005]*)['"]?\uE006)*/i, |
|
20737 fn: function(match, token) { |
|
20738 var test = Selector[PSEUDOS][match[1]]; |
|
20739 if (test) { // reorder match array and unescape special chars for tests |
|
20740 if (match[2]) { |
|
20741 match[2] = match[2].replace(/\\/g, ''); |
|
20742 } |
|
20743 return [match[2], test]; |
|
20744 } else { // selector token not supported (possibly missing CSS3 module) |
|
20745 return false; |
|
20746 } |
|
20747 } |
|
20748 } |
|
20749 ], |
|
20750 |
|
20751 _getToken: function(token) { |
|
20752 return { |
|
20753 tagName: null, |
|
20754 id: null, |
|
20755 className: null, |
|
20756 attributes: {}, |
|
20757 combinator: null, |
|
20758 tests: [] |
|
20759 }; |
|
20760 }, |
|
20761 |
|
20762 /* |
|
20763 Break selector into token units per simple selector. |
|
20764 Combinator is attached to the previous token. |
|
20765 */ |
|
20766 _tokenize: function(selector) { |
|
20767 selector = selector || ''; |
|
20768 selector = Selector._parseSelector(Y.Lang.trim(selector)); |
|
20769 var token = Selector._getToken(), // one token per simple selector (left selector holds combinator) |
|
20770 query = selector, // original query for debug report |
|
20771 tokens = [], // array of tokens |
|
20772 found = false, // whether or not any matches were found this pass |
|
20773 match, // the regex match |
|
20774 test, |
|
20775 i, parser; |
|
20776 |
|
20777 /* |
|
20778 Search for selector patterns, store, and strip them from the selector string |
|
20779 until no patterns match (invalid selector) or we run out of chars. |
|
20780 |
|
20781 Multiple attributes and pseudos are allowed, in any order. |
|
20782 for example: |
|
20783 'form:first-child[type=button]:not(button)[lang|=en]' |
|
20784 */ |
|
20785 outer: |
|
20786 do { |
|
20787 found = false; // reset after full pass |
|
20788 for (i = 0; (parser = Selector._parsers[i++]);) { |
|
20789 if ( (match = parser.re.exec(selector)) ) { // note assignment |
|
20790 if (parser.name !== COMBINATOR ) { |
|
20791 token.selector = selector; |
|
20792 } |
|
20793 selector = selector.replace(match[0], ''); // strip current match from selector |
|
20794 if (!selector.length) { |
|
20795 token.last = true; |
|
20796 } |
|
20797 |
|
20798 if (Selector._attrFilters[match[1]]) { // convert class to className, etc. |
|
20799 match[1] = Selector._attrFilters[match[1]]; |
|
20800 } |
|
20801 |
|
20802 test = parser.fn(match, token); |
|
20803 if (test === false) { // selector not supported |
|
20804 found = false; |
|
20805 break outer; |
|
20806 } else if (test) { |
|
20807 token.tests.push(test); |
|
20808 } |
|
20809 |
|
20810 if (!selector.length || parser.name === COMBINATOR) { |
|
20811 tokens.push(token); |
|
20812 token = Selector._getToken(token); |
|
20813 if (parser.name === COMBINATOR) { |
|
20814 token.combinator = Y.Selector.combinators[match[1]]; |
|
20815 } |
|
20816 } |
|
20817 found = true; |
|
20818 } |
|
20819 } |
|
20820 } while (found && selector.length); |
|
20821 |
|
20822 if (!found || selector.length) { // not fully parsed |
|
20823 Y.log('query: ' + query + ' contains unsupported token in: ' + selector, 'warn', 'Selector'); |
|
20824 tokens = []; |
|
20825 } |
|
20826 return tokens; |
|
20827 }, |
|
20828 |
|
20829 _replaceMarkers: function(selector) { |
|
20830 selector = selector.replace(/\[/g, '\uE003'); |
|
20831 selector = selector.replace(/\]/g, '\uE004'); |
|
20832 |
|
20833 selector = selector.replace(/\(/g, '\uE005'); |
|
20834 selector = selector.replace(/\)/g, '\uE006'); |
|
20835 return selector; |
|
20836 }, |
|
20837 |
|
20838 _replaceShorthand: function(selector) { |
|
20839 var shorthand = Y.Selector.shorthand, |
|
20840 re; |
|
20841 |
|
20842 for (re in shorthand) { |
|
20843 if (shorthand.hasOwnProperty(re)) { |
|
20844 selector = selector.replace(new RegExp(re, 'gi'), shorthand[re]); |
|
20845 } |
|
20846 } |
|
20847 |
|
20848 return selector; |
|
20849 }, |
|
20850 |
|
20851 _parseSelector: function(selector) { |
|
20852 var replaced = Y.Selector._replaceSelector(selector), |
|
20853 selector = replaced.selector; |
|
20854 |
|
20855 // replace shorthand (".foo, #bar") after pseudos and attrs |
|
20856 // to avoid replacing unescaped chars |
|
20857 selector = Y.Selector._replaceShorthand(selector); |
|
20858 |
|
20859 selector = Y.Selector._restore('attr', selector, replaced.attrs); |
|
20860 selector = Y.Selector._restore('pseudo', selector, replaced.pseudos); |
|
20861 |
|
20862 // replace braces and parens before restoring escaped chars |
|
20863 // to avoid replacing ecaped markers |
|
20864 selector = Y.Selector._replaceMarkers(selector); |
|
20865 selector = Y.Selector._restore('esc', selector, replaced.esc); |
|
20866 |
|
20867 return selector; |
|
20868 }, |
|
20869 |
|
20870 _attrFilters: { |
|
20871 'class': 'className', |
|
20872 'for': 'htmlFor' |
|
20873 }, |
|
20874 |
|
20875 getters: { |
|
20876 href: function(node, attr) { |
|
20877 return Y.DOM.getAttribute(node, attr); |
|
20878 }, |
|
20879 |
|
20880 id: function(node, attr) { |
|
20881 return Y.DOM.getId(node); |
|
20882 } |
|
20883 } |
|
20884 }; |
|
20885 |
|
20886 Y.mix(Y.Selector, SelectorCSS2, true); |
|
20887 Y.Selector.getters.src = Y.Selector.getters.rel = Y.Selector.getters.href; |
|
20888 |
|
20889 // IE wants class with native queries |
|
20890 if (Y.Selector.useNative && Y.config.doc.querySelector) { |
|
20891 Y.Selector.shorthand['\\.(-?[_a-z]+[-\\w]*)'] = '[class~=$1]'; |
|
20892 } |
|
20893 |
|
20894 |
|
20895 |
|
20896 }, '@VERSION@', {"requires": ["selector-native"]}); |
|
20897 YUI.add('selector-css3', function (Y, NAME) { |
|
20898 |
|
20899 /** |
|
20900 * The selector css3 module provides support for css3 selectors. |
|
20901 * @module dom |
|
20902 * @submodule selector-css3 |
|
20903 * @for Selector |
|
20904 */ |
|
20905 |
|
20906 /* |
|
20907 an+b = get every _a_th node starting at the _b_th |
|
20908 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 |
|
20909 1n+b = get every element starting from b ("1" may may be omitted, e.g. "1n+0" or "n+0" or "n") |
|
20910 an+0 = get every _a_th element, "0" may be omitted |
|
20911 */ |
|
20912 |
|
20913 Y.Selector._reNth = /^(?:([\-]?\d*)(n){1}|(odd|even)$)*([\-+]?\d*)$/; |
|
20914 |
|
20915 Y.Selector._getNth = function(node, expr, tag, reverse) { |
|
20916 Y.Selector._reNth.test(expr); |
|
20917 var a = parseInt(RegExp.$1, 10), // include every _a_ elements (zero means no repeat, just first _a_) |
|
20918 n = RegExp.$2, // "n" |
|
20919 oddeven = RegExp.$3, // "odd" or "even" |
|
20920 b = parseInt(RegExp.$4, 10) || 0, // start scan from element _b_ |
|
20921 result = [], |
|
20922 siblings = Y.DOM._children(node.parentNode, tag), |
|
20923 op; |
|
20924 |
|
20925 if (oddeven) { |
|
20926 a = 2; // always every other |
|
20927 op = '+'; |
|
20928 n = 'n'; |
|
20929 b = (oddeven === 'odd') ? 1 : 0; |
|
20930 } else if ( isNaN(a) ) { |
|
20931 a = (n) ? 1 : 0; // start from the first or no repeat |
|
20932 } |
|
20933 |
|
20934 if (a === 0) { // just the first |
|
20935 if (reverse) { |
|
20936 b = siblings.length - b + 1; |
|
20937 } |
|
20938 |
|
20939 if (siblings[b - 1] === node) { |
|
20940 return true; |
|
20941 } else { |
|
20942 return false; |
|
20943 } |
|
20944 |
|
20945 } else if (a < 0) { |
|
20946 reverse = !!reverse; |
|
20947 a = Math.abs(a); |
|
20948 } |
|
20949 |
|
20950 if (!reverse) { |
|
20951 for (var i = b - 1, len = siblings.length; i < len; i += a) { |
|
20952 if ( i >= 0 && siblings[i] === node ) { |
|
20953 return true; |
|
20954 } |
|
20955 } |
|
20956 } else { |
|
20957 for (var i = siblings.length - b, len = siblings.length; i >= 0; i -= a) { |
|
20958 if ( i < len && siblings[i] === node ) { |
|
20959 return true; |
|
20960 } |
|
20961 } |
|
20962 } |
|
20963 return false; |
|
20964 }; |
|
20965 |
|
20966 Y.mix(Y.Selector.pseudos, { |
|
20967 'root': function(node) { |
|
20968 return node === node.ownerDocument.documentElement; |
|
20969 }, |
|
20970 |
|
20971 'nth-child': function(node, expr) { |
|
20972 return Y.Selector._getNth(node, expr); |
|
20973 }, |
|
20974 |
|
20975 'nth-last-child': function(node, expr) { |
|
20976 return Y.Selector._getNth(node, expr, null, true); |
|
20977 }, |
|
20978 |
|
20979 'nth-of-type': function(node, expr) { |
|
20980 return Y.Selector._getNth(node, expr, node.tagName); |
|
20981 }, |
|
20982 |
|
20983 'nth-last-of-type': function(node, expr) { |
|
20984 return Y.Selector._getNth(node, expr, node.tagName, true); |
|
20985 }, |
|
20986 |
|
20987 'last-child': function(node) { |
|
20988 var children = Y.DOM._children(node.parentNode); |
|
20989 return children[children.length - 1] === node; |
|
20990 }, |
|
20991 |
|
20992 'first-of-type': function(node) { |
|
20993 return Y.DOM._children(node.parentNode, node.tagName)[0] === node; |
|
20994 }, |
|
20995 |
|
20996 'last-of-type': function(node) { |
|
20997 var children = Y.DOM._children(node.parentNode, node.tagName); |
|
20998 return children[children.length - 1] === node; |
|
20999 }, |
|
21000 |
|
21001 'only-child': function(node) { |
|
21002 var children = Y.DOM._children(node.parentNode); |
|
21003 return children.length === 1 && children[0] === node; |
|
21004 }, |
|
21005 |
|
21006 'only-of-type': function(node) { |
|
21007 var children = Y.DOM._children(node.parentNode, node.tagName); |
|
21008 return children.length === 1 && children[0] === node; |
|
21009 }, |
|
21010 |
|
21011 'empty': function(node) { |
|
21012 return node.childNodes.length === 0; |
|
21013 }, |
|
21014 |
|
21015 'not': function(node, expr) { |
|
21016 return !Y.Selector.test(node, expr); |
|
21017 }, |
|
21018 |
|
21019 'contains': function(node, expr) { |
|
21020 var text = node.innerText || node.textContent || ''; |
|
21021 return text.indexOf(expr) > -1; |
|
21022 }, |
|
21023 |
|
21024 'checked': function(node) { |
|
21025 return (node.checked === true || node.selected === true); |
|
21026 }, |
|
21027 |
|
21028 enabled: function(node) { |
|
21029 return (node.disabled !== undefined && !node.disabled); |
|
21030 }, |
|
21031 |
|
21032 disabled: function(node) { |
|
21033 return (node.disabled); |
|
21034 } |
|
21035 }); |
|
21036 |
|
21037 Y.mix(Y.Selector.operators, { |
|
21038 '^=': '^{val}', // Match starts with value |
|
21039 '$=': '{val}$', // Match ends with value |
|
21040 '*=': '{val}' // Match contains value as substring |
|
21041 }); |
|
21042 |
|
21043 Y.Selector.combinators['~'] = { |
|
21044 axis: 'previousSibling' |
|
21045 }; |
|
21046 |
|
21047 |
|
21048 }, '@VERSION@', {"requires": ["selector-native", "selector-css2"]}); |
|
21049 YUI.add('yui-log', function (Y, NAME) { |
|
21050 |
|
21051 /** |
|
21052 * Provides console log capability and exposes a custom event for |
|
21053 * console implementations. This module is a `core` YUI module, |
|
21054 * <a href="../classes/YUI.html#method_log">it's documentation is located under the YUI class</a>. |
|
21055 * |
|
21056 * @module yui |
|
21057 * @submodule yui-log |
|
21058 */ |
|
21059 |
|
21060 var INSTANCE = Y, |
|
21061 LOGEVENT = 'yui:log', |
|
21062 UNDEFINED = 'undefined', |
|
21063 LEVELS = { debug: 1, |
|
21064 info: 2, |
|
21065 warn: 4, |
|
21066 error: 8 }; |
|
21067 |
|
21068 /** |
|
21069 * If the 'debug' config is true, a 'yui:log' event will be |
|
21070 * dispatched, which the Console widget and anything else |
|
21071 * can consume. If the 'useBrowserConsole' config is true, it will |
|
21072 * write to the browser console if available. YUI-specific log |
|
21073 * messages will only be present in the -debug versions of the |
|
21074 * JS files. The build system is supposed to remove log statements |
|
21075 * from the raw and minified versions of the files. |
|
21076 * |
|
21077 * @method log |
|
21078 * @for YUI |
|
21079 * @param {String} msg The message to log. |
|
21080 * @param {String} cat The log category for the message. Default |
|
21081 * categories are "info", "warn", "error", time". |
|
21082 * Custom categories can be used as well. (opt). |
|
21083 * @param {String} src The source of the the message (opt). |
|
21084 * @param {boolean} silent If true, the log event won't fire. |
|
21085 * @return {YUI} YUI instance. |
|
21086 */ |
|
21087 INSTANCE.log = function(msg, cat, src, silent) { |
|
21088 var bail, excl, incl, m, f, minlevel, |
|
21089 Y = INSTANCE, |
|
21090 c = Y.config, |
|
21091 publisher = (Y.fire) ? Y : YUI.Env.globalEvents; |
|
21092 // suppress log message if the config is off or the event stack |
|
21093 // or the event call stack contains a consumer of the yui:log event |
|
21094 if (c.debug) { |
|
21095 // apply source filters |
|
21096 src = src || ""; |
|
21097 if (typeof src !== "undefined") { |
|
21098 excl = c.logExclude; |
|
21099 incl = c.logInclude; |
|
21100 if (incl && !(src in incl)) { |
|
21101 bail = 1; |
|
21102 } else if (incl && (src in incl)) { |
|
21103 bail = !incl[src]; |
|
21104 } else if (excl && (src in excl)) { |
|
21105 bail = excl[src]; |
|
21106 } |
|
21107 |
|
21108 // Determine the current minlevel as defined in configuration |
|
21109 Y.config.logLevel = Y.config.logLevel || 'debug'; |
|
21110 minlevel = LEVELS[Y.config.logLevel.toLowerCase()]; |
|
21111 |
|
21112 if (cat in LEVELS && LEVELS[cat] < minlevel) { |
|
21113 // Skip this message if the we don't meet the defined minlevel |
|
21114 bail = 1; |
|
21115 } |
|
21116 } |
|
21117 if (!bail) { |
|
21118 if (c.useBrowserConsole) { |
|
21119 m = (src) ? src + ': ' + msg : msg; |
|
21120 if (Y.Lang.isFunction(c.logFn)) { |
|
21121 c.logFn.call(Y, msg, cat, src); |
|
21122 } else if (typeof console !== UNDEFINED && console.log) { |
|
21123 f = (cat && console[cat] && (cat in LEVELS)) ? cat : 'log'; |
|
21124 console[f](m); |
|
21125 } else if (typeof opera !== UNDEFINED) { |
|
21126 opera.postError(m); |
|
21127 } |
|
21128 } |
|
21129 |
|
21130 if (publisher && !silent) { |
|
21131 |
|
21132 if (publisher === Y && (!publisher.getEvent(LOGEVENT))) { |
|
21133 publisher.publish(LOGEVENT, { |
|
21134 broadcast: 2 |
|
21135 }); |
|
21136 } |
|
21137 |
|
21138 publisher.fire(LOGEVENT, { |
|
21139 msg: msg, |
|
21140 cat: cat, |
|
21141 src: src |
|
21142 }); |
|
21143 } |
|
21144 } |
|
21145 } |
|
21146 |
|
21147 return Y; |
|
21148 }; |
|
21149 |
|
21150 /** |
|
21151 * Write a system message. This message will be preserved in the |
|
21152 * minified and raw versions of the YUI files, unlike log statements. |
|
21153 * @method message |
|
21154 * @for YUI |
|
21155 * @param {String} msg The message to log. |
|
21156 * @param {String} cat The log category for the message. Default |
|
21157 * categories are "info", "warn", "error", time". |
|
21158 * Custom categories can be used as well. (opt). |
|
21159 * @param {String} src The source of the the message (opt). |
|
21160 * @param {boolean} silent If true, the log event won't fire. |
|
21161 * @return {YUI} YUI instance. |
|
21162 */ |
|
21163 INSTANCE.message = function() { |
|
21164 return INSTANCE.log.apply(INSTANCE, arguments); |
|
21165 }; |
|
21166 |
|
21167 |
|
21168 }, '@VERSION@', {"requires": ["yui-base"]}); |
|
21169 YUI.add('dump', function (Y, NAME) { |
|
21170 |
|
21171 /** |
|
21172 * Returns a simple string representation of the object or array. |
|
21173 * Other types of objects will be returned unprocessed. Arrays |
|
21174 * are expected to be indexed. Use object notation for |
|
21175 * associative arrays. |
|
21176 * |
|
21177 * If included, the dump method is added to the YUI instance. |
|
21178 * |
|
21179 * @module dump |
|
21180 */ |
|
21181 |
|
21182 var L = Y.Lang, |
|
21183 OBJ = '{...}', |
|
21184 FUN = 'f(){...}', |
|
21185 COMMA = ', ', |
|
21186 ARROW = ' => ', |
|
21187 |
|
21188 /** |
|
21189 * Returns a simple string representation of the object or array. |
|
21190 * Other types of objects will be returned unprocessed. Arrays |
|
21191 * are expected to be indexed. |
|
21192 * |
|
21193 * @method dump |
|
21194 * @param {Object} o The object to dump. |
|
21195 * @param {Number} d How deep to recurse child objects, default 3. |
|
21196 * @return {String} the dump result. |
|
21197 * @for YUI |
|
21198 */ |
|
21199 dump = function(o, d) { |
|
21200 var i, len, s = [], type = L.type(o); |
|
21201 |
|
21202 // Cast non-objects to string |
|
21203 // Skip dates because the std toString is what we want |
|
21204 // Skip HTMLElement-like objects because trying to dump |
|
21205 // an element will cause an unhandled exception in FF 2.x |
|
21206 if (!L.isObject(o)) { |
|
21207 return o + ''; |
|
21208 } else if (type == 'date') { |
|
21209 return o; |
|
21210 } else if (o.nodeType && o.tagName) { |
|
21211 return o.tagName + '#' + o.id; |
|
21212 } else if (o.document && o.navigator) { |
|
21213 return 'window'; |
|
21214 } else if (o.location && o.body) { |
|
21215 return 'document'; |
|
21216 } else if (type == 'function') { |
|
21217 return FUN; |
|
21218 } |
|
21219 |
|
21220 // dig into child objects the depth specifed. Default 3 |
|
21221 d = (L.isNumber(d)) ? d : 3; |
|
21222 |
|
21223 // arrays [1, 2, 3] |
|
21224 if (type == 'array') { |
|
21225 s.push('['); |
|
21226 for (i = 0, len = o.length; i < len; i = i + 1) { |
|
21227 if (L.isObject(o[i])) { |
|
21228 s.push((d > 0) ? L.dump(o[i], d - 1) : OBJ); |
|
21229 } else { |
|
21230 s.push(o[i]); |
|
21231 } |
|
21232 s.push(COMMA); |
|
21233 } |
|
21234 if (s.length > 1) { |
|
21235 s.pop(); |
|
21236 } |
|
21237 s.push(']'); |
|
21238 // regexp /foo/ |
|
21239 } else if (type == 'regexp') { |
|
21240 s.push(o.toString()); |
|
21241 // objects {k1 => v1, k2 => v2} |
|
21242 } else { |
|
21243 s.push('{'); |
|
21244 for (i in o) { |
|
21245 if (o.hasOwnProperty(i)) { |
|
21246 try { |
|
21247 s.push(i + ARROW); |
|
21248 if (L.isObject(o[i])) { |
|
21249 s.push((d > 0) ? L.dump(o[i], d - 1) : OBJ); |
|
21250 } else { |
|
21251 s.push(o[i]); |
|
21252 } |
|
21253 s.push(COMMA); |
|
21254 } catch (e) { |
|
21255 s.push('Error: ' + e.message); |
|
21256 } |
|
21257 } |
|
21258 } |
|
21259 if (s.length > 1) { |
|
21260 s.pop(); |
|
21261 } |
|
21262 s.push('}'); |
|
21263 } |
|
21264 |
|
21265 return s.join(''); |
|
21266 }; |
|
21267 |
|
21268 Y.dump = dump; |
|
21269 L.dump = dump; |
|
21270 |
|
21271 |
|
21272 |
|
21273 }, '@VERSION@', {"requires": ["yui-base"]}); |
|
21274 YUI.add('transition-timer', function (Y, NAME) { |
|
21275 |
|
21276 /** |
|
21277 * Provides the base Transition class, for animating numeric properties. |
|
21278 * |
|
21279 * @module transition |
|
21280 * @submodule transition-timer |
|
21281 */ |
|
21282 |
|
21283 |
|
21284 var Transition = Y.Transition; |
|
21285 |
|
21286 Y.mix(Transition.prototype, { |
|
21287 _start: function() { |
|
21288 if (Transition.useNative) { |
|
21289 this._runNative(); |
|
21290 } else { |
|
21291 this._runTimer(); |
|
21292 } |
|
21293 }, |
|
21294 |
|
21295 _runTimer: function() { |
|
21296 var anim = this; |
|
21297 anim._initAttrs(); |
|
21298 |
|
21299 Transition._running[Y.stamp(anim)] = anim; |
|
21300 anim._startTime = new Date(); |
|
21301 Transition._startTimer(); |
|
21302 }, |
|
21303 |
|
21304 _endTimer: function() { |
|
21305 var anim = this; |
|
21306 delete Transition._running[Y.stamp(anim)]; |
|
21307 anim._startTime = null; |
|
21308 }, |
|
21309 |
|
21310 _runFrame: function() { |
|
21311 var t = new Date() - this._startTime; |
|
21312 this._runAttrs(t); |
|
21313 }, |
|
21314 |
|
21315 _runAttrs: function(time) { |
|
21316 var anim = this, |
|
21317 node = anim._node, |
|
21318 config = anim._config, |
|
21319 uid = Y.stamp(node), |
|
21320 attrs = Transition._nodeAttrs[uid], |
|
21321 customAttr = Transition.behaviors, |
|
21322 done = false, |
|
21323 allDone = false, |
|
21324 data, |
|
21325 name, |
|
21326 attribute, |
|
21327 setter, |
|
21328 elapsed, |
|
21329 delay, |
|
21330 d, |
|
21331 t, |
|
21332 i; |
|
21333 |
|
21334 for (name in attrs) { |
|
21335 if ((attribute = attrs[name]) && attribute.transition === anim) { |
|
21336 d = attribute.duration; |
|
21337 delay = attribute.delay; |
|
21338 elapsed = (time - delay) / 1000; |
|
21339 t = time; |
|
21340 data = { |
|
21341 type: 'propertyEnd', |
|
21342 propertyName: name, |
|
21343 config: config, |
|
21344 elapsedTime: elapsed |
|
21345 }; |
|
21346 |
|
21347 setter = (i in customAttr && 'set' in customAttr[i]) ? |
|
21348 customAttr[i].set : Transition.DEFAULT_SETTER; |
|
21349 |
|
21350 done = (t >= d); |
|
21351 |
|
21352 if (t > d) { |
|
21353 t = d; |
|
21354 } |
|
21355 |
|
21356 if (!delay || time >= delay) { |
|
21357 setter(anim, name, attribute.from, attribute.to, t - delay, d - delay, |
|
21358 attribute.easing, attribute.unit); |
|
21359 |
|
21360 if (done) { |
|
21361 delete attrs[name]; |
|
21362 anim._count--; |
|
21363 |
|
21364 if (config[name] && config[name].on && config[name].on.end) { |
|
21365 config[name].on.end.call(Y.one(node), data); |
|
21366 } |
|
21367 |
|
21368 //node.fire('transition:propertyEnd', data); |
|
21369 |
|
21370 if (!allDone && anim._count <= 0) { |
|
21371 allDone = true; |
|
21372 anim._end(elapsed); |
|
21373 anim._endTimer(); |
|
21374 } |
|
21375 } |
|
21376 } |
|
21377 |
|
21378 } |
|
21379 } |
|
21380 }, |
|
21381 |
|
21382 _initAttrs: function() { |
|
21383 var anim = this, |
|
21384 customAttr = Transition.behaviors, |
|
21385 uid = Y.stamp(anim._node), |
|
21386 attrs = Transition._nodeAttrs[uid], |
|
21387 attribute, |
|
21388 duration, |
|
21389 delay, |
|
21390 easing, |
|
21391 val, |
|
21392 name, |
|
21393 mTo, |
|
21394 mFrom, |
|
21395 unit, begin, end; |
|
21396 |
|
21397 for (name in attrs) { |
|
21398 if ((attribute = attrs[name]) && attribute.transition === anim) { |
|
21399 duration = attribute.duration * 1000; |
|
21400 delay = attribute.delay * 1000; |
|
21401 easing = attribute.easing; |
|
21402 val = attribute.value; |
|
21403 |
|
21404 // only allow supported properties |
|
21405 if (name in anim._node.style || name in Y.DOM.CUSTOM_STYLES) { |
|
21406 begin = (name in customAttr && 'get' in customAttr[name]) ? |
|
21407 customAttr[name].get(anim, name) : Transition.DEFAULT_GETTER(anim, name); |
|
21408 |
|
21409 mFrom = Transition.RE_UNITS.exec(begin); |
|
21410 mTo = Transition.RE_UNITS.exec(val); |
|
21411 |
|
21412 begin = mFrom ? mFrom[1] : begin; |
|
21413 end = mTo ? mTo[1] : val; |
|
21414 unit = mTo ? mTo[2] : mFrom ? mFrom[2] : ''; // one might be zero TODO: mixed units |
|
21415 |
|
21416 if (!unit && Transition.RE_DEFAULT_UNIT.test(name)) { |
|
21417 unit = Transition.DEFAULT_UNIT; |
|
21418 } |
|
21419 |
|
21420 if (typeof easing === 'string') { |
|
21421 if (easing.indexOf('cubic-bezier') > -1) { |
|
21422 easing = easing.substring(13, easing.length - 1).split(','); |
|
21423 } else if (Transition.easings[easing]) { |
|
21424 easing = Transition.easings[easing]; |
|
21425 } |
|
21426 } |
|
21427 |
|
21428 attribute.from = Number(begin); |
|
21429 attribute.to = Number(end); |
|
21430 attribute.unit = unit; |
|
21431 attribute.easing = easing; |
|
21432 attribute.duration = duration + delay; |
|
21433 attribute.delay = delay; |
|
21434 } else { |
|
21435 delete attrs[name]; |
|
21436 anim._count--; |
|
21437 } |
|
21438 } |
|
21439 } |
|
21440 }, |
|
21441 |
|
21442 destroy: function() { |
|
21443 this.detachAll(); |
|
21444 this._node = null; |
|
21445 } |
|
21446 }, true); |
|
21447 |
|
21448 Y.mix(Y.Transition, { |
|
21449 _runtimeAttrs: {}, |
|
21450 /* |
|
21451 * Regex of properties that should use the default unit. |
|
21452 * |
|
21453 * @property RE_DEFAULT_UNIT |
|
21454 * @static |
|
21455 */ |
|
21456 RE_DEFAULT_UNIT: /^width|height|top|right|bottom|left|margin.*|padding.*|border.*$/i, |
|
21457 |
|
21458 /* |
|
21459 * The default unit to use with properties that pass the RE_DEFAULT_UNIT test. |
|
21460 * |
|
21461 * @property DEFAULT_UNIT |
|
21462 * @static |
|
21463 */ |
|
21464 DEFAULT_UNIT: 'px', |
|
21465 |
|
21466 /* |
|
21467 * Time in milliseconds passed to setInterval for frame processing |
|
21468 * |
|
21469 * @property intervalTime |
|
21470 * @default 20 |
|
21471 * @static |
|
21472 */ |
|
21473 intervalTime: 20, |
|
21474 |
|
21475 /* |
|
21476 * Bucket for custom getters and setters |
|
21477 * |
|
21478 * @property behaviors |
|
21479 * @static |
|
21480 */ |
|
21481 behaviors: { |
|
21482 left: { |
|
21483 get: function(anim, attr) { |
|
21484 return Y.DOM._getAttrOffset(anim._node, attr); |
|
21485 } |
|
21486 } |
|
21487 }, |
|
21488 |
|
21489 /* |
|
21490 * The default setter to use when setting object properties. |
|
21491 * |
|
21492 * @property DEFAULT_SETTER |
|
21493 * @static |
|
21494 */ |
|
21495 DEFAULT_SETTER: function(anim, att, from, to, elapsed, duration, fn, unit) { |
|
21496 from = Number(from); |
|
21497 to = Number(to); |
|
21498 |
|
21499 var node = anim._node, |
|
21500 val = Transition.cubicBezier(fn, elapsed / duration); |
|
21501 |
|
21502 val = from + val[0] * (to - from); |
|
21503 |
|
21504 if (node) { |
|
21505 if (att in node.style || att in Y.DOM.CUSTOM_STYLES) { |
|
21506 unit = unit || ''; |
|
21507 Y.DOM.setStyle(node, att, val + unit); |
|
21508 } |
|
21509 } else { |
|
21510 anim._end(); |
|
21511 } |
|
21512 }, |
|
21513 |
|
21514 /* |
|
21515 * The default getter to use when getting object properties. |
|
21516 * |
|
21517 * @property DEFAULT_GETTER |
|
21518 * @static |
|
21519 */ |
|
21520 DEFAULT_GETTER: function(anim, att) { |
|
21521 var node = anim._node, |
|
21522 val = ''; |
|
21523 |
|
21524 if (att in node.style || att in Y.DOM.CUSTOM_STYLES) { |
|
21525 val = Y.DOM.getComputedStyle(node, att); |
|
21526 } |
|
21527 |
|
21528 return val; |
|
21529 }, |
|
21530 |
|
21531 _startTimer: function() { |
|
21532 if (!Transition._timer) { |
|
21533 Transition._timer = setInterval(Transition._runFrame, Transition.intervalTime); |
|
21534 } |
|
21535 }, |
|
21536 |
|
21537 _stopTimer: function() { |
|
21538 clearInterval(Transition._timer); |
|
21539 Transition._timer = null; |
|
21540 }, |
|
21541 |
|
21542 /* |
|
21543 * Called per Interval to handle each animation frame. |
|
21544 * @method _runFrame |
|
21545 * @private |
|
21546 * @static |
|
21547 */ |
|
21548 _runFrame: function() { |
|
21549 var done = true, |
|
21550 anim; |
|
21551 for (anim in Transition._running) { |
|
21552 if (Transition._running[anim]._runFrame) { |
|
21553 done = false; |
|
21554 Transition._running[anim]._runFrame(); |
|
21555 } |
|
21556 } |
|
21557 |
|
21558 if (done) { |
|
21559 Transition._stopTimer(); |
|
21560 } |
|
21561 }, |
|
21562 |
|
21563 cubicBezier: function(p, t) { |
|
21564 var x0 = 0, |
|
21565 y0 = 0, |
|
21566 x1 = p[0], |
|
21567 y1 = p[1], |
|
21568 x2 = p[2], |
|
21569 y2 = p[3], |
|
21570 x3 = 1, |
|
21571 y3 = 0, |
|
21572 |
|
21573 A = x3 - 3 * x2 + 3 * x1 - x0, |
|
21574 B = 3 * x2 - 6 * x1 + 3 * x0, |
|
21575 C = 3 * x1 - 3 * x0, |
|
21576 D = x0, |
|
21577 E = y3 - 3 * y2 + 3 * y1 - y0, |
|
21578 F = 3 * y2 - 6 * y1 + 3 * y0, |
|
21579 G = 3 * y1 - 3 * y0, |
|
21580 H = y0, |
|
21581 |
|
21582 x = (((A*t) + B)*t + C)*t + D, |
|
21583 y = (((E*t) + F)*t + G)*t + H; |
|
21584 |
|
21585 return [x, y]; |
|
21586 }, |
|
21587 |
|
21588 easings: { |
|
21589 ease: [0.25, 0, 1, 0.25], |
|
21590 linear: [0, 0, 1, 1], |
|
21591 'ease-in': [0.42, 0, 1, 1], |
|
21592 'ease-out': [0, 0, 0.58, 1], |
|
21593 'ease-in-out': [0.42, 0, 0.58, 1] |
|
21594 }, |
|
21595 |
|
21596 _running: {}, |
|
21597 _timer: null, |
|
21598 |
|
21599 RE_UNITS: /^(-?\d*\.?\d*){1}(em|ex|px|in|cm|mm|pt|pc|%)*$/ |
|
21600 }, true); |
|
21601 |
|
21602 Transition.behaviors.top = Transition.behaviors.bottom = Transition.behaviors.right = Transition.behaviors.left; |
|
21603 |
|
21604 Y.Transition = Transition; |
|
21605 |
|
21606 |
|
21607 }, '@VERSION@', {"requires": ["transition"]}); |
|
21608 YUI.add('yui', function (Y, NAME) { |
|
21609 |
|
21610 // empty |
|
21611 |
|
21612 |
|
21613 |
|
21614 }, '@VERSION@', { |
|
21615 "use": [ |
|
21616 "yui", |
|
21617 "oop", |
|
21618 "dom", |
|
21619 "event-custom-base", |
|
21620 "event-base", |
|
21621 "pluginhost", |
|
21622 "node", |
|
21623 "event-delegate", |
|
21624 "io-base", |
|
21625 "json-parse", |
|
21626 "transition", |
|
21627 "selector-css3", |
|
21628 "dom-style-ie", |
|
21629 "querystring-stringify-simple" |
|
21630 ] |
|
21631 }); |
|
21632 var Y = YUI().use('*'); |