[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 /* 2 YUI 3.17.2 (build 9c3c78e) 3 Copyright 2014 Yahoo! Inc. All rights reserved. 4 Licensed under the BSD License. 5 http://yuilibrary.com/license/ 6 */ 7 8 YUI.add('event-custom-base', function (Y, NAME) { 9 10 /** 11 * Custom event engine, DOM event listener abstraction layer, synthetic DOM 12 * events. 13 * @module event-custom 14 */ 15 16 Y.Env.evt = { 17 handles: {}, 18 plugins: {} 19 }; 20 21 /** 22 * Custom event engine, DOM event listener abstraction layer, synthetic DOM 23 * events. 24 * @module event-custom 25 * @submodule event-custom-base 26 */ 27 28 /** 29 * Allows for the insertion of methods that are executed before or after 30 * a specified method 31 * @class Do 32 * @static 33 */ 34 35 var DO_BEFORE = 0, 36 DO_AFTER = 1, 37 38 DO = { 39 40 /** 41 * Cache of objects touched by the utility 42 * @property objs 43 * @static 44 * @deprecated Since 3.6.0. The `_yuiaop` property on the AOP'd object 45 * replaces the role of this property, but is considered to be private, and 46 * is only mentioned to provide a migration path. 47 * 48 * If you have a use case which warrants migration to the _yuiaop property, 49 * please file a ticket to let us know what it's used for and we can see if 50 * we need to expose hooks for that functionality more formally. 51 */ 52 objs: null, 53 54 /** 55 * <p>Execute the supplied method before the specified function. Wrapping 56 * function may optionally return an instance of the following classes to 57 * further alter runtime behavior:</p> 58 * <dl> 59 * <dt></code>Y.Do.Halt(message, returnValue)</code></dt> 60 * <dd>Immediatly stop execution and return 61 * <code>returnValue</code>. No other wrapping functions will be 62 * executed.</dd> 63 * <dt></code>Y.Do.AlterArgs(message, newArgArray)</code></dt> 64 * <dd>Replace the arguments that the original function will be 65 * called with.</dd> 66 * <dt></code>Y.Do.Prevent(message)</code></dt> 67 * <dd>Don't execute the wrapped function. Other before phase 68 * wrappers will be executed.</dd> 69 * </dl> 70 * 71 * @method before 72 * @param fn {Function} the function to execute 73 * @param obj the object hosting the method to displace 74 * @param sFn {string} the name of the method to displace 75 * @param c The execution context for fn 76 * @param arg* {mixed} 0..n additional arguments to supply to the subscriber 77 * when the event fires. 78 * @return {EventHandle} handle for the subscription 79 * @static 80 */ 81 before: function(fn, obj, sFn, c) { 82 var f = fn, a; 83 if (c) { 84 a = [fn, c].concat(Y.Array(arguments, 4, true)); 85 f = Y.rbind.apply(Y, a); 86 } 87 88 return this._inject(DO_BEFORE, f, obj, sFn); 89 }, 90 91 /** 92 * <p>Execute the supplied method after the specified function. Wrapping 93 * function may optionally return an instance of the following classes to 94 * further alter runtime behavior:</p> 95 * <dl> 96 * <dt></code>Y.Do.Halt(message, returnValue)</code></dt> 97 * <dd>Immediatly stop execution and return 98 * <code>returnValue</code>. No other wrapping functions will be 99 * executed.</dd> 100 * <dt></code>Y.Do.AlterReturn(message, returnValue)</code></dt> 101 * <dd>Return <code>returnValue</code> instead of the wrapped 102 * method's original return value. This can be further altered by 103 * other after phase wrappers.</dd> 104 * </dl> 105 * 106 * <p>The static properties <code>Y.Do.originalRetVal</code> and 107 * <code>Y.Do.currentRetVal</code> will be populated for reference.</p> 108 * 109 * @method after 110 * @param fn {Function} the function to execute 111 * @param obj the object hosting the method to displace 112 * @param sFn {string} the name of the method to displace 113 * @param c The execution context for fn 114 * @param arg* {mixed} 0..n additional arguments to supply to the subscriber 115 * @return {EventHandle} handle for the subscription 116 * @static 117 */ 118 after: function(fn, obj, sFn, c) { 119 var f = fn, a; 120 if (c) { 121 a = [fn, c].concat(Y.Array(arguments, 4, true)); 122 f = Y.rbind.apply(Y, a); 123 } 124 125 return this._inject(DO_AFTER, f, obj, sFn); 126 }, 127 128 /** 129 * Execute the supplied method before or after the specified function. 130 * Used by <code>before</code> and <code>after</code>. 131 * 132 * @method _inject 133 * @param when {string} before or after 134 * @param fn {Function} the function to execute 135 * @param obj the object hosting the method to displace 136 * @param sFn {string} the name of the method to displace 137 * @param c The execution context for fn 138 * @return {EventHandle} handle for the subscription 139 * @private 140 * @static 141 */ 142 _inject: function(when, fn, obj, sFn) { 143 // object id 144 var id = Y.stamp(obj), o, sid; 145 146 if (!obj._yuiaop) { 147 // create a map entry for the obj if it doesn't exist, to hold overridden methods 148 obj._yuiaop = {}; 149 } 150 151 o = obj._yuiaop; 152 153 if (!o[sFn]) { 154 // create a map entry for the method if it doesn't exist 155 o[sFn] = new Y.Do.Method(obj, sFn); 156 157 // re-route the method to our wrapper 158 obj[sFn] = function() { 159 return o[sFn].exec.apply(o[sFn], arguments); 160 }; 161 } 162 163 // subscriber id 164 sid = id + Y.stamp(fn) + sFn; 165 166 // register the callback 167 o[sFn].register(sid, fn, when); 168 169 return new Y.EventHandle(o[sFn], sid); 170 }, 171 172 /** 173 * Detach a before or after subscription. 174 * 175 * @method detach 176 * @param handle {EventHandle} the subscription handle 177 * @static 178 */ 179 detach: function(handle) { 180 if (handle.detach) { 181 handle.detach(); 182 } 183 } 184 }; 185 186 Y.Do = DO; 187 188 ////////////////////////////////////////////////////////////////////////// 189 190 /** 191 * Contains the return value from the wrapped method, accessible 192 * by 'after' event listeners. 193 * 194 * @property originalRetVal 195 * @static 196 * @since 3.2.0 197 */ 198 199 /** 200 * Contains the current state of the return value, consumable by 201 * 'after' event listeners, and updated if an after subscriber 202 * changes the return value generated by the wrapped function. 203 * 204 * @property currentRetVal 205 * @static 206 * @since 3.2.0 207 */ 208 209 ////////////////////////////////////////////////////////////////////////// 210 211 /** 212 * Wrapper for a displaced method with aop enabled 213 * @class Do.Method 214 * @constructor 215 * @param obj The object to operate on 216 * @param sFn The name of the method to displace 217 */ 218 DO.Method = function(obj, sFn) { 219 this.obj = obj; 220 this.methodName = sFn; 221 this.method = obj[sFn]; 222 this.before = {}; 223 this.after = {}; 224 }; 225 226 /** 227 * Register a aop subscriber 228 * @method register 229 * @param sid {string} the subscriber id 230 * @param fn {Function} the function to execute 231 * @param when {string} when to execute the function 232 */ 233 DO.Method.prototype.register = function (sid, fn, when) { 234 if (when) { 235 this.after[sid] = fn; 236 } else { 237 this.before[sid] = fn; 238 } 239 }; 240 241 /** 242 * Unregister a aop subscriber 243 * @method delete 244 * @param sid {string} the subscriber id 245 * @param fn {Function} the function to execute 246 * @param when {string} when to execute the function 247 */ 248 DO.Method.prototype._delete = function (sid) { 249 delete this.before[sid]; 250 delete this.after[sid]; 251 }; 252 253 /** 254 * <p>Execute the wrapped method. All arguments are passed into the wrapping 255 * functions. If any of the before wrappers return an instance of 256 * <code>Y.Do.Halt</code> or <code>Y.Do.Prevent</code>, neither the wrapped 257 * function nor any after phase subscribers will be executed.</p> 258 * 259 * <p>The return value will be the return value of the wrapped function or one 260 * provided by a wrapper function via an instance of <code>Y.Do.Halt</code> or 261 * <code>Y.Do.AlterReturn</code>. 262 * 263 * @method exec 264 * @param arg* {any} Arguments are passed to the wrapping and wrapped functions 265 * @return {any} Return value of wrapped function unless overwritten (see above) 266 */ 267 DO.Method.prototype.exec = function () { 268 269 var args = Y.Array(arguments, 0, true), 270 i, ret, newRet, 271 bf = this.before, 272 af = this.after, 273 prevented = false; 274 275 // execute before 276 for (i in bf) { 277 if (bf.hasOwnProperty(i)) { 278 ret = bf[i].apply(this.obj, args); 279 if (ret) { 280 switch (ret.constructor) { 281 case DO.Halt: 282 return ret.retVal; 283 case DO.AlterArgs: 284 args = ret.newArgs; 285 break; 286 case DO.Prevent: 287 prevented = true; 288 break; 289 default: 290 } 291 } 292 } 293 } 294 295 // execute method 296 if (!prevented) { 297 ret = this.method.apply(this.obj, args); 298 } 299 300 DO.originalRetVal = ret; 301 DO.currentRetVal = ret; 302 303 // execute after methods. 304 for (i in af) { 305 if (af.hasOwnProperty(i)) { 306 newRet = af[i].apply(this.obj, args); 307 // Stop processing if a Halt object is returned 308 if (newRet && newRet.constructor === DO.Halt) { 309 return newRet.retVal; 310 // Check for a new return value 311 } else if (newRet && newRet.constructor === DO.AlterReturn) { 312 ret = newRet.newRetVal; 313 // Update the static retval state 314 DO.currentRetVal = ret; 315 } 316 } 317 } 318 319 return ret; 320 }; 321 322 ////////////////////////////////////////////////////////////////////////// 323 324 /** 325 * Return an AlterArgs object when you want to change the arguments that 326 * were passed into the function. Useful for Do.before subscribers. An 327 * example would be a service that scrubs out illegal characters prior to 328 * executing the core business logic. 329 * @class Do.AlterArgs 330 * @constructor 331 * @param msg {String} (optional) Explanation of the altered return value 332 * @param newArgs {Array} Call parameters to be used for the original method 333 * instead of the arguments originally passed in. 334 */ 335 DO.AlterArgs = function(msg, newArgs) { 336 this.msg = msg; 337 this.newArgs = newArgs; 338 }; 339 340 /** 341 * Return an AlterReturn object when you want to change the result returned 342 * from the core method to the caller. Useful for Do.after subscribers. 343 * @class Do.AlterReturn 344 * @constructor 345 * @param msg {String} (optional) Explanation of the altered return value 346 * @param newRetVal {any} Return value passed to code that invoked the wrapped 347 * function. 348 */ 349 DO.AlterReturn = function(msg, newRetVal) { 350 this.msg = msg; 351 this.newRetVal = newRetVal; 352 }; 353 354 /** 355 * Return a Halt object when you want to terminate the execution 356 * of all subsequent subscribers as well as the wrapped method 357 * if it has not exectued yet. Useful for Do.before subscribers. 358 * @class Do.Halt 359 * @constructor 360 * @param msg {String} (optional) Explanation of why the termination was done 361 * @param retVal {any} Return value passed to code that invoked the wrapped 362 * function. 363 */ 364 DO.Halt = function(msg, retVal) { 365 this.msg = msg; 366 this.retVal = retVal; 367 }; 368 369 /** 370 * Return a Prevent object when you want to prevent the wrapped function 371 * from executing, but want the remaining listeners to execute. Useful 372 * for Do.before subscribers. 373 * @class Do.Prevent 374 * @constructor 375 * @param msg {String} (optional) Explanation of why the termination was done 376 */ 377 DO.Prevent = function(msg) { 378 this.msg = msg; 379 }; 380 381 /** 382 * Return an Error object when you want to terminate the execution 383 * of all subsequent method calls. 384 * @class Do.Error 385 * @constructor 386 * @param msg {String} (optional) Explanation of the altered return value 387 * @param retVal {any} Return value passed to code that invoked the wrapped 388 * function. 389 * @deprecated use Y.Do.Halt or Y.Do.Prevent 390 */ 391 DO.Error = DO.Halt; 392 393 394 ////////////////////////////////////////////////////////////////////////// 395 396 /** 397 * Custom event engine, DOM event listener abstraction layer, synthetic DOM 398 * events. 399 * @module event-custom 400 * @submodule event-custom-base 401 */ 402 403 404 // var onsubscribeType = "_event:onsub", 405 var YArray = Y.Array, 406 407 AFTER = 'after', 408 CONFIGS = [ 409 'broadcast', 410 'monitored', 411 'bubbles', 412 'context', 413 'contextFn', 414 'currentTarget', 415 'defaultFn', 416 'defaultTargetOnly', 417 'details', 418 'emitFacade', 419 'fireOnce', 420 'async', 421 'host', 422 'preventable', 423 'preventedFn', 424 'queuable', 425 'silent', 426 'stoppedFn', 427 'target', 428 'type' 429 ], 430 431 CONFIGS_HASH = YArray.hash(CONFIGS), 432 433 nativeSlice = Array.prototype.slice, 434 435 YUI3_SIGNATURE = 9, 436 YUI_LOG = 'yui:log', 437 438 mixConfigs = function(r, s, ov) { 439 var p; 440 441 for (p in s) { 442 if (CONFIGS_HASH[p] && (ov || !(p in r))) { 443 r[p] = s[p]; 444 } 445 } 446 447 return r; 448 }; 449 450 /** 451 * The CustomEvent class lets you define events for your application 452 * that can be subscribed to by one or more independent component. 453 * 454 * @param {String} type The type of event, which is passed to the callback 455 * when the event fires. 456 * @param {object} defaults configuration object. 457 * @class CustomEvent 458 * @constructor 459 */ 460 461 /** 462 * The type of event, returned to subscribers when the event fires 463 * @property type 464 * @type string 465 */ 466 467 /** 468 * By default all custom events are logged in the debug build, set silent 469 * to true to disable debug outpu for this event. 470 * @property silent 471 * @type boolean 472 */ 473 474 Y.CustomEvent = function(type, defaults) { 475 476 this._kds = Y.CustomEvent.keepDeprecatedSubs; 477 478 this.id = Y.guid(); 479 480 this.type = type; 481 this.silent = this.logSystem = (type === YUI_LOG); 482 483 if (this._kds) { 484 /** 485 * The subscribers to this event 486 * @property subscribers 487 * @type Subscriber {} 488 * @deprecated 489 */ 490 491 /** 492 * 'After' subscribers 493 * @property afters 494 * @type Subscriber {} 495 * @deprecated 496 */ 497 this.subscribers = {}; 498 this.afters = {}; 499 } 500 501 if (defaults) { 502 mixConfigs(this, defaults, true); 503 } 504 }; 505 506 /** 507 * Static flag to enable population of the <a href="#property_subscribers">`subscribers`</a> 508 * and <a href="#property_subscribers">`afters`</a> properties held on a `CustomEvent` instance. 509 * 510 * These properties were changed to private properties (`_subscribers` and `_afters`), and 511 * converted from objects to arrays for performance reasons. 512 * 513 * Setting this property to true will populate the deprecated `subscribers` and `afters` 514 * properties for people who may be using them (which is expected to be rare). There will 515 * be a performance hit, compared to the new array based implementation. 516 * 517 * If you are using these deprecated properties for a use case which the public API 518 * does not support, please file an enhancement request, and we can provide an alternate 519 * public implementation which doesn't have the performance cost required to maintiain the 520 * properties as objects. 521 * 522 * @property keepDeprecatedSubs 523 * @static 524 * @for CustomEvent 525 * @type boolean 526 * @default false 527 * @deprecated 528 */ 529 Y.CustomEvent.keepDeprecatedSubs = false; 530 531 Y.CustomEvent.mixConfigs = mixConfigs; 532 533 Y.CustomEvent.prototype = { 534 535 constructor: Y.CustomEvent, 536 537 /** 538 * Monitor when an event is attached or detached. 539 * 540 * @property monitored 541 * @type boolean 542 */ 543 544 /** 545 * If 0, this event does not broadcast. If 1, the YUI instance is notified 546 * every time this event fires. If 2, the YUI instance and the YUI global 547 * (if event is enabled on the global) are notified every time this event 548 * fires. 549 * @property broadcast 550 * @type int 551 */ 552 553 /** 554 * Specifies whether this event should be queued when the host is actively 555 * processing an event. This will effect exectution order of the callbacks 556 * for the various events. 557 * @property queuable 558 * @type boolean 559 * @default false 560 */ 561 562 /** 563 * This event has fired if true 564 * 565 * @property fired 566 * @type boolean 567 * @default false; 568 */ 569 570 /** 571 * An array containing the arguments the custom event 572 * was last fired with. 573 * @property firedWith 574 * @type Array 575 */ 576 577 /** 578 * This event should only fire one time if true, and if 579 * it has fired, any new subscribers should be notified 580 * immediately. 581 * 582 * @property fireOnce 583 * @type boolean 584 * @default false; 585 */ 586 587 /** 588 * fireOnce listeners will fire syncronously unless async 589 * is set to true 590 * @property async 591 * @type boolean 592 * @default false 593 */ 594 595 /** 596 * Flag for stopPropagation that is modified during fire() 597 * 1 means to stop propagation to bubble targets. 2 means 598 * to also stop additional subscribers on this target. 599 * @property stopped 600 * @type int 601 */ 602 603 /** 604 * Flag for preventDefault that is modified during fire(). 605 * if it is not 0, the default behavior for this event 606 * @property prevented 607 * @type int 608 */ 609 610 /** 611 * Specifies the host for this custom event. This is used 612 * to enable event bubbling 613 * @property host 614 * @type EventTarget 615 */ 616 617 /** 618 * The default function to execute after event listeners 619 * have fire, but only if the default action was not 620 * prevented. 621 * @property defaultFn 622 * @type Function 623 */ 624 625 /** 626 * Flag for the default function to execute only if the 627 * firing event is the current target. This happens only 628 * when using custom event delegation and setting the 629 * flag to `true` mimics the behavior of event delegation 630 * in the DOM. 631 * 632 * @property defaultTargetOnly 633 * @type Boolean 634 * @default false 635 */ 636 637 /** 638 * The function to execute if a subscriber calls 639 * stopPropagation or stopImmediatePropagation 640 * @property stoppedFn 641 * @type Function 642 */ 643 644 /** 645 * The function to execute if a subscriber calls 646 * preventDefault 647 * @property preventedFn 648 * @type Function 649 */ 650 651 /** 652 * The subscribers to this event 653 * @property _subscribers 654 * @type Subscriber [] 655 * @private 656 */ 657 658 /** 659 * 'After' subscribers 660 * @property _afters 661 * @type Subscriber [] 662 * @private 663 */ 664 665 /** 666 * If set to true, the custom event will deliver an EventFacade object 667 * that is similar to a DOM event object. 668 * @property emitFacade 669 * @type boolean 670 * @default false 671 */ 672 673 /** 674 * Supports multiple options for listener signatures in order to 675 * port YUI 2 apps. 676 * @property signature 677 * @type int 678 * @default 9 679 */ 680 signature : YUI3_SIGNATURE, 681 682 /** 683 * The context the the event will fire from by default. Defaults to the YUI 684 * instance. 685 * @property context 686 * @type object 687 */ 688 context : Y, 689 690 /** 691 * Specifies whether or not this event's default function 692 * can be cancelled by a subscriber by executing preventDefault() 693 * on the event facade 694 * @property preventable 695 * @type boolean 696 * @default true 697 */ 698 preventable : true, 699 700 /** 701 * Specifies whether or not a subscriber can stop the event propagation 702 * via stopPropagation(), stopImmediatePropagation(), or halt() 703 * 704 * Events can only bubble if emitFacade is true. 705 * 706 * @property bubbles 707 * @type boolean 708 * @default true 709 */ 710 bubbles : true, 711 712 /** 713 * Returns the number of subscribers for this event as the sum of the on() 714 * subscribers and after() subscribers. 715 * 716 * @method hasSubs 717 * @return Number 718 */ 719 hasSubs: function(when) { 720 var s = 0, 721 a = 0, 722 subs = this._subscribers, 723 afters = this._afters, 724 sib = this.sibling; 725 726 if (subs) { 727 s = subs.length; 728 } 729 730 if (afters) { 731 a = afters.length; 732 } 733 734 if (sib) { 735 subs = sib._subscribers; 736 afters = sib._afters; 737 738 if (subs) { 739 s += subs.length; 740 } 741 742 if (afters) { 743 a += afters.length; 744 } 745 } 746 747 if (when) { 748 return (when === 'after') ? a : s; 749 } 750 751 return (s + a); 752 }, 753 754 /** 755 * Monitor the event state for the subscribed event. The first parameter 756 * is what should be monitored, the rest are the normal parameters when 757 * subscribing to an event. 758 * @method monitor 759 * @param what {string} what to monitor ('detach', 'attach', 'publish'). 760 * @return {EventHandle} return value from the monitor event subscription. 761 */ 762 monitor: function(what) { 763 this.monitored = true; 764 var type = this.id + '|' + this.type + '_' + what, 765 args = nativeSlice.call(arguments, 0); 766 args[0] = type; 767 return this.host.on.apply(this.host, args); 768 }, 769 770 /** 771 * Get all of the subscribers to this event and any sibling event 772 * @method getSubs 773 * @return {Array} first item is the on subscribers, second the after. 774 */ 775 getSubs: function() { 776 777 var sibling = this.sibling, 778 subs = this._subscribers, 779 afters = this._afters, 780 siblingSubs, 781 siblingAfters; 782 783 if (sibling) { 784 siblingSubs = sibling._subscribers; 785 siblingAfters = sibling._afters; 786 } 787 788 if (siblingSubs) { 789 if (subs) { 790 subs = subs.concat(siblingSubs); 791 } else { 792 subs = siblingSubs.concat(); 793 } 794 } else { 795 if (subs) { 796 subs = subs.concat(); 797 } else { 798 subs = []; 799 } 800 } 801 802 if (siblingAfters) { 803 if (afters) { 804 afters = afters.concat(siblingAfters); 805 } else { 806 afters = siblingAfters.concat(); 807 } 808 } else { 809 if (afters) { 810 afters = afters.concat(); 811 } else { 812 afters = []; 813 } 814 } 815 816 return [subs, afters]; 817 }, 818 819 /** 820 * Apply configuration properties. Only applies the CONFIG whitelist 821 * @method applyConfig 822 * @param o hash of properties to apply. 823 * @param force {boolean} if true, properties that exist on the event 824 * will be overwritten. 825 */ 826 applyConfig: function(o, force) { 827 mixConfigs(this, o, force); 828 }, 829 830 /** 831 * Create the Subscription for subscribing function, context, and bound 832 * arguments. If this is a fireOnce event, the subscriber is immediately 833 * notified. 834 * 835 * @method _on 836 * @param fn {Function} Subscription callback 837 * @param [context] {Object} Override `this` in the callback 838 * @param [args] {Array} bound arguments that will be passed to the callback after the arguments generated by fire() 839 * @param [when] {String} "after" to slot into after subscribers 840 * @return {EventHandle} 841 * @protected 842 */ 843 _on: function(fn, context, args, when) { 844 845 846 var s = new Y.Subscriber(fn, context, args, when), 847 firedWith; 848 849 if (this.fireOnce && this.fired) { 850 851 firedWith = this.firedWith; 852 853 // It's a little ugly for this to know about facades, 854 // but given the current breakup, not much choice without 855 // moving a whole lot of stuff around. 856 if (this.emitFacade && this._addFacadeToArgs) { 857 this._addFacadeToArgs(firedWith); 858 } 859 860 if (this.async) { 861 setTimeout(Y.bind(this._notify, this, s, firedWith), 0); 862 } else { 863 this._notify(s, firedWith); 864 } 865 } 866 867 if (when === AFTER) { 868 if (!this._afters) { 869 this._afters = []; 870 } 871 this._afters.push(s); 872 } else { 873 if (!this._subscribers) { 874 this._subscribers = []; 875 } 876 this._subscribers.push(s); 877 } 878 879 if (this._kds) { 880 if (when === AFTER) { 881 this.afters[s.id] = s; 882 } else { 883 this.subscribers[s.id] = s; 884 } 885 } 886 887 return new Y.EventHandle(this, s); 888 }, 889 890 /** 891 * Listen for this event 892 * @method subscribe 893 * @param {Function} fn The function to execute. 894 * @return {EventHandle} Unsubscribe handle. 895 * @deprecated use on. 896 */ 897 subscribe: function(fn, context) { 898 var a = (arguments.length > 2) ? nativeSlice.call(arguments, 2) : null; 899 return this._on(fn, context, a, true); 900 }, 901 902 /** 903 * Listen for this event 904 * @method on 905 * @param {Function} fn The function to execute. 906 * @param {object} context optional execution context. 907 * @param {mixed} arg* 0..n additional arguments to supply to the subscriber 908 * when the event fires. 909 * @return {EventHandle} An object with a detach method to detch the handler(s). 910 */ 911 on: function(fn, context) { 912 var a = (arguments.length > 2) ? nativeSlice.call(arguments, 2) : null; 913 914 if (this.monitored && this.host) { 915 this.host._monitor('attach', this, { 916 args: arguments 917 }); 918 } 919 return this._on(fn, context, a, true); 920 }, 921 922 /** 923 * Listen for this event after the normal subscribers have been notified and 924 * the default behavior has been applied. If a normal subscriber prevents the 925 * default behavior, it also prevents after listeners from firing. 926 * @method after 927 * @param {Function} fn The function to execute. 928 * @param {object} context optional execution context. 929 * @param {mixed} arg* 0..n additional arguments to supply to the subscriber 930 * when the event fires. 931 * @return {EventHandle} handle Unsubscribe handle. 932 */ 933 after: function(fn, context) { 934 var a = (arguments.length > 2) ? nativeSlice.call(arguments, 2) : null; 935 return this._on(fn, context, a, AFTER); 936 }, 937 938 /** 939 * Detach listeners. 940 * @method detach 941 * @param {Function} fn The subscribed function to remove, if not supplied 942 * all will be removed. 943 * @param {Object} context The context object passed to subscribe. 944 * @return {Number} returns the number of subscribers unsubscribed. 945 */ 946 detach: function(fn, context) { 947 // unsubscribe handle 948 if (fn && fn.detach) { 949 return fn.detach(); 950 } 951 952 var i, s, 953 found = 0, 954 subs = this._subscribers, 955 afters = this._afters; 956 957 if (subs) { 958 for (i = subs.length; i >= 0; i--) { 959 s = subs[i]; 960 if (s && (!fn || fn === s.fn)) { 961 this._delete(s, subs, i); 962 found++; 963 } 964 } 965 } 966 967 if (afters) { 968 for (i = afters.length; i >= 0; i--) { 969 s = afters[i]; 970 if (s && (!fn || fn === s.fn)) { 971 this._delete(s, afters, i); 972 found++; 973 } 974 } 975 } 976 977 return found; 978 }, 979 980 /** 981 * Detach listeners. 982 * @method unsubscribe 983 * @param {Function} fn The subscribed function to remove, if not supplied 984 * all will be removed. 985 * @param {Object} context The context object passed to subscribe. 986 * @return {int|undefined} returns the number of subscribers unsubscribed. 987 * @deprecated use detach. 988 */ 989 unsubscribe: function() { 990 return this.detach.apply(this, arguments); 991 }, 992 993 /** 994 * Notify a single subscriber 995 * @method _notify 996 * @param {Subscriber} s the subscriber. 997 * @param {Array} args the arguments array to apply to the listener. 998 * @protected 999 */ 1000 _notify: function(s, args, ef) { 1001 1002 1003 var ret; 1004 1005 ret = s.notify(args, this); 1006 1007 if (false === ret || this.stopped > 1) { 1008 return false; 1009 } 1010 1011 return true; 1012 }, 1013 1014 /** 1015 * Logger abstraction to centralize the application of the silent flag 1016 * @method log 1017 * @param {string} msg message to log. 1018 * @param {string} cat log category. 1019 */ 1020 log: function(msg, cat) { 1021 }, 1022 1023 /** 1024 * Notifies the subscribers. The callback functions will be executed 1025 * from the context specified when the event was created, and with the 1026 * following parameters: 1027 * <ul> 1028 * <li>The type of event</li> 1029 * <li>All of the arguments fire() was executed with as an array</li> 1030 * <li>The custom object (if any) that was passed into the subscribe() 1031 * method</li> 1032 * </ul> 1033 * @method fire 1034 * @param {Object*} arguments an arbitrary set of parameters to pass to 1035 * the handler. 1036 * @return {boolean} false if one of the subscribers returned false, 1037 * true otherwise. 1038 * 1039 */ 1040 fire: function() { 1041 1042 // push is the fastest way to go from arguments to arrays 1043 // for most browsers currently 1044 // http://jsperf.com/push-vs-concat-vs-slice/2 1045 1046 var args = []; 1047 args.push.apply(args, arguments); 1048 1049 return this._fire(args); 1050 }, 1051 1052 /** 1053 * Private internal implementation for `fire`, which is can be used directly by 1054 * `EventTarget` and other event module classes which have already converted from 1055 * an `arguments` list to an array, to avoid the repeated overhead. 1056 * 1057 * @method _fire 1058 * @private 1059 * @param {Array} args The array of arguments passed to be passed to handlers. 1060 * @return {boolean} false if one of the subscribers returned false, true otherwise. 1061 */ 1062 _fire: function(args) { 1063 1064 if (this.fireOnce && this.fired) { 1065 return true; 1066 } else { 1067 1068 // this doesn't happen if the event isn't published 1069 // this.host._monitor('fire', this.type, args); 1070 1071 this.fired = true; 1072 1073 if (this.fireOnce) { 1074 this.firedWith = args; 1075 } 1076 1077 if (this.emitFacade) { 1078 return this.fireComplex(args); 1079 } else { 1080 return this.fireSimple(args); 1081 } 1082 } 1083 }, 1084 1085 /** 1086 * Set up for notifying subscribers of non-emitFacade events. 1087 * 1088 * @method fireSimple 1089 * @param args {Array} Arguments passed to fire() 1090 * @return Boolean false if a subscriber returned false 1091 * @protected 1092 */ 1093 fireSimple: function(args) { 1094 this.stopped = 0; 1095 this.prevented = 0; 1096 if (this.hasSubs()) { 1097 var subs = this.getSubs(); 1098 this._procSubs(subs[0], args); 1099 this._procSubs(subs[1], args); 1100 } 1101 if (this.broadcast) { 1102 this._broadcast(args); 1103 } 1104 return this.stopped ? false : true; 1105 }, 1106 1107 // Requires the event-custom-complex module for full funcitonality. 1108 fireComplex: function(args) { 1109 args[0] = args[0] || {}; 1110 return this.fireSimple(args); 1111 }, 1112 1113 /** 1114 * Notifies a list of subscribers. 1115 * 1116 * @method _procSubs 1117 * @param subs {Array} List of subscribers 1118 * @param args {Array} Arguments passed to fire() 1119 * @param ef {} 1120 * @return Boolean false if a subscriber returns false or stops the event 1121 * propagation via e.stopPropagation(), 1122 * e.stopImmediatePropagation(), or e.halt() 1123 * @private 1124 */ 1125 _procSubs: function(subs, args, ef) { 1126 var s, i, l; 1127 1128 for (i = 0, l = subs.length; i < l; i++) { 1129 s = subs[i]; 1130 if (s && s.fn) { 1131 if (false === this._notify(s, args, ef)) { 1132 this.stopped = 2; 1133 } 1134 if (this.stopped === 2) { 1135 return false; 1136 } 1137 } 1138 } 1139 1140 return true; 1141 }, 1142 1143 /** 1144 * Notifies the YUI instance if the event is configured with broadcast = 1, 1145 * and both the YUI instance and Y.Global if configured with broadcast = 2. 1146 * 1147 * @method _broadcast 1148 * @param args {Array} Arguments sent to fire() 1149 * @private 1150 */ 1151 _broadcast: function(args) { 1152 if (!this.stopped && this.broadcast) { 1153 1154 var a = args.concat(); 1155 a.unshift(this.type); 1156 1157 if (this.host !== Y) { 1158 Y.fire.apply(Y, a); 1159 } 1160 1161 if (this.broadcast === 2) { 1162 Y.Global.fire.apply(Y.Global, a); 1163 } 1164 } 1165 }, 1166 1167 /** 1168 * Removes all listeners 1169 * @method unsubscribeAll 1170 * @return {Number} The number of listeners unsubscribed. 1171 * @deprecated use detachAll. 1172 */ 1173 unsubscribeAll: function() { 1174 return this.detachAll.apply(this, arguments); 1175 }, 1176 1177 /** 1178 * Removes all listeners 1179 * @method detachAll 1180 * @return {Number} The number of listeners unsubscribed. 1181 */ 1182 detachAll: function() { 1183 return this.detach(); 1184 }, 1185 1186 /** 1187 * Deletes the subscriber from the internal store of on() and after() 1188 * subscribers. 1189 * 1190 * @method _delete 1191 * @param s subscriber object. 1192 * @param subs (optional) on or after subscriber array 1193 * @param index (optional) The index found. 1194 * @private 1195 */ 1196 _delete: function(s, subs, i) { 1197 var when = s._when; 1198 1199 if (!subs) { 1200 subs = (when === AFTER) ? this._afters : this._subscribers; 1201 } 1202 1203 if (subs) { 1204 i = YArray.indexOf(subs, s, 0); 1205 1206 if (s && subs[i] === s) { 1207 subs.splice(i, 1); 1208 } 1209 } 1210 1211 if (this._kds) { 1212 if (when === AFTER) { 1213 delete this.afters[s.id]; 1214 } else { 1215 delete this.subscribers[s.id]; 1216 } 1217 } 1218 1219 if (this.monitored && this.host) { 1220 this.host._monitor('detach', this, { 1221 ce: this, 1222 sub: s 1223 }); 1224 } 1225 1226 if (s) { 1227 s.deleted = true; 1228 } 1229 } 1230 }; 1231 /** 1232 * Stores the subscriber information to be used when the event fires. 1233 * @param {Function} fn The wrapped function to execute. 1234 * @param {Object} context The value of the keyword 'this' in the listener. 1235 * @param {Array} args* 0..n additional arguments to supply the listener. 1236 * 1237 * @class Subscriber 1238 * @constructor 1239 */ 1240 Y.Subscriber = function(fn, context, args, when) { 1241 1242 /** 1243 * The callback that will be execute when the event fires 1244 * This is wrapped by Y.rbind if obj was supplied. 1245 * @property fn 1246 * @type Function 1247 */ 1248 this.fn = fn; 1249 1250 /** 1251 * Optional 'this' keyword for the listener 1252 * @property context 1253 * @type Object 1254 */ 1255 this.context = context; 1256 1257 /** 1258 * Unique subscriber id 1259 * @property id 1260 * @type String 1261 */ 1262 this.id = Y.guid(); 1263 1264 /** 1265 * Additional arguments to propagate to the subscriber 1266 * @property args 1267 * @type Array 1268 */ 1269 this.args = args; 1270 1271 this._when = when; 1272 1273 /** 1274 * Custom events for a given fire transaction. 1275 * @property events 1276 * @type {EventTarget} 1277 */ 1278 // this.events = null; 1279 1280 /** 1281 * This listener only reacts to the event once 1282 * @property once 1283 */ 1284 // this.once = false; 1285 1286 }; 1287 1288 Y.Subscriber.prototype = { 1289 constructor: Y.Subscriber, 1290 1291 _notify: function(c, args, ce) { 1292 if (this.deleted && !this.postponed) { 1293 if (this.postponed) { 1294 delete this.fn; 1295 delete this.context; 1296 } else { 1297 delete this.postponed; 1298 return null; 1299 } 1300 } 1301 var a = this.args, ret; 1302 switch (ce.signature) { 1303 case 0: 1304 ret = this.fn.call(c, ce.type, args, c); 1305 break; 1306 case 1: 1307 ret = this.fn.call(c, args[0] || null, c); 1308 break; 1309 default: 1310 if (a || args) { 1311 args = args || []; 1312 a = (a) ? args.concat(a) : args; 1313 ret = this.fn.apply(c, a); 1314 } else { 1315 ret = this.fn.call(c); 1316 } 1317 } 1318 1319 if (this.once) { 1320 ce._delete(this); 1321 } 1322 1323 return ret; 1324 }, 1325 1326 /** 1327 * Executes the subscriber. 1328 * @method notify 1329 * @param args {Array} Arguments array for the subscriber. 1330 * @param ce {CustomEvent} The custom event that sent the notification. 1331 */ 1332 notify: function(args, ce) { 1333 var c = this.context, 1334 ret = true; 1335 1336 if (!c) { 1337 c = (ce.contextFn) ? ce.contextFn() : ce.context; 1338 } 1339 1340 // only catch errors if we will not re-throw them. 1341 if (Y.config && Y.config.throwFail) { 1342 ret = this._notify(c, args, ce); 1343 } else { 1344 try { 1345 ret = this._notify(c, args, ce); 1346 } catch (e) { 1347 Y.error(this + ' failed: ' + e.message, e); 1348 } 1349 } 1350 1351 return ret; 1352 }, 1353 1354 /** 1355 * Returns true if the fn and obj match this objects properties. 1356 * Used by the unsubscribe method to match the right subscriber. 1357 * 1358 * @method contains 1359 * @param {Function} fn the function to execute. 1360 * @param {Object} context optional 'this' keyword for the listener. 1361 * @return {boolean} true if the supplied arguments match this 1362 * subscriber's signature. 1363 */ 1364 contains: function(fn, context) { 1365 if (context) { 1366 return ((this.fn === fn) && this.context === context); 1367 } else { 1368 return (this.fn === fn); 1369 } 1370 }, 1371 1372 valueOf : function() { 1373 return this.id; 1374 } 1375 1376 }; 1377 /** 1378 * Return value from all subscribe operations 1379 * @class EventHandle 1380 * @constructor 1381 * @param {CustomEvent} evt the custom event. 1382 * @param {Subscriber} sub the subscriber. 1383 */ 1384 Y.EventHandle = function(evt, sub) { 1385 1386 /** 1387 * The custom event 1388 * 1389 * @property evt 1390 * @type CustomEvent 1391 */ 1392 this.evt = evt; 1393 1394 /** 1395 * The subscriber object 1396 * 1397 * @property sub 1398 * @type Subscriber 1399 */ 1400 this.sub = sub; 1401 }; 1402 1403 Y.EventHandle.prototype = { 1404 batch: function(f, c) { 1405 f.call(c || this, this); 1406 if (Y.Lang.isArray(this.evt)) { 1407 Y.Array.each(this.evt, function(h) { 1408 h.batch.call(c || h, f); 1409 }); 1410 } 1411 }, 1412 1413 /** 1414 * Detaches this subscriber 1415 * @method detach 1416 * @return {Number} the number of detached listeners 1417 */ 1418 detach: function() { 1419 var evt = this.evt, detached = 0, i; 1420 if (evt) { 1421 if (Y.Lang.isArray(evt)) { 1422 for (i = 0; i < evt.length; i++) { 1423 detached += evt[i].detach(); 1424 } 1425 } else { 1426 evt._delete(this.sub); 1427 detached = 1; 1428 } 1429 1430 } 1431 1432 return detached; 1433 }, 1434 1435 /** 1436 * Monitor the event state for the subscribed event. The first parameter 1437 * is what should be monitored, the rest are the normal parameters when 1438 * subscribing to an event. 1439 * @method monitor 1440 * @param what {string} what to monitor ('attach', 'detach', 'publish'). 1441 * @return {EventHandle} return value from the monitor event subscription. 1442 */ 1443 monitor: function(what) { 1444 return this.evt.monitor.apply(this.evt, arguments); 1445 } 1446 }; 1447 1448 /** 1449 * Custom event engine, DOM event listener abstraction layer, synthetic DOM 1450 * events. 1451 * @module event-custom 1452 * @submodule event-custom-base 1453 */ 1454 1455 /** 1456 * EventTarget provides the implementation for any object to 1457 * publish, subscribe and fire to custom events, and also 1458 * alows other EventTargets to target the object with events 1459 * sourced from the other object. 1460 * EventTarget is designed to be used with Y.augment to wrap 1461 * EventCustom in an interface that allows events to be listened to 1462 * and fired by name. This makes it possible for implementing code to 1463 * subscribe to an event that either has not been created yet, or will 1464 * not be created at all. 1465 * @class EventTarget 1466 * @param opts a configuration object 1467 * @config emitFacade {boolean} if true, all events will emit event 1468 * facade payloads by default (default false) 1469 * @config prefix {String} the prefix to apply to non-prefixed event names 1470 */ 1471 1472 var L = Y.Lang, 1473 PREFIX_DELIMITER = ':', 1474 CATEGORY_DELIMITER = '|', 1475 AFTER_PREFIX = '~AFTER~', 1476 WILD_TYPE_RE = /(.*?)(:)(.*?)/, 1477 1478 _wildType = Y.cached(function(type) { 1479 return type.replace(WILD_TYPE_RE, "*$2$3"); 1480 }), 1481 1482 /** 1483 * If the instance has a prefix attribute and the 1484 * event type is not prefixed, the instance prefix is 1485 * applied to the supplied type. 1486 * @method _getType 1487 * @private 1488 */ 1489 _getType = function(type, pre) { 1490 1491 if (!pre || !type || type.indexOf(PREFIX_DELIMITER) > -1) { 1492 return type; 1493 } 1494 1495 return pre + PREFIX_DELIMITER + type; 1496 }, 1497 1498 /** 1499 * Returns an array with the detach key (if provided), 1500 * and the prefixed event name from _getType 1501 * Y.on('detachcategory| menu:click', fn) 1502 * @method _parseType 1503 * @private 1504 */ 1505 _parseType = Y.cached(function(type, pre) { 1506 1507 var t = type, detachcategory, after, i; 1508 1509 if (!L.isString(t)) { 1510 return t; 1511 } 1512 1513 i = t.indexOf(AFTER_PREFIX); 1514 1515 if (i > -1) { 1516 after = true; 1517 t = t.substr(AFTER_PREFIX.length); 1518 } 1519 1520 i = t.indexOf(CATEGORY_DELIMITER); 1521 1522 if (i > -1) { 1523 detachcategory = t.substr(0, (i)); 1524 t = t.substr(i+1); 1525 if (t === '*') { 1526 t = null; 1527 } 1528 } 1529 1530 // detach category, full type with instance prefix, is this an after listener, short type 1531 return [detachcategory, (pre) ? _getType(t, pre) : t, after, t]; 1532 }), 1533 1534 ET = function(opts) { 1535 1536 var etState = this._yuievt, 1537 etConfig; 1538 1539 if (!etState) { 1540 etState = this._yuievt = { 1541 events: {}, // PERF: Not much point instantiating lazily. We're bound to have events 1542 targets: null, // PERF: Instantiate lazily, if user actually adds target, 1543 config: { 1544 host: this, 1545 context: this 1546 }, 1547 chain: Y.config.chain 1548 }; 1549 } 1550 1551 etConfig = etState.config; 1552 1553 if (opts) { 1554 mixConfigs(etConfig, opts, true); 1555 1556 if (opts.chain !== undefined) { 1557 etState.chain = opts.chain; 1558 } 1559 1560 if (opts.prefix) { 1561 etConfig.prefix = opts.prefix; 1562 } 1563 } 1564 }; 1565 1566 ET.prototype = { 1567 1568 constructor: ET, 1569 1570 /** 1571 * Listen to a custom event hosted by this object one time. 1572 * This is the equivalent to <code>on</code> except the 1573 * listener is immediatelly detached when it is executed. 1574 * @method once 1575 * @param {String} type The name of the event 1576 * @param {Function} fn The callback to execute in response to the event 1577 * @param {Object} [context] Override `this` object in callback 1578 * @param {Any} [arg*] 0..n additional arguments to supply to the subscriber 1579 * @return {EventHandle} A subscription handle capable of detaching the 1580 * subscription 1581 */ 1582 once: function() { 1583 var handle = this.on.apply(this, arguments); 1584 handle.batch(function(hand) { 1585 if (hand.sub) { 1586 hand.sub.once = true; 1587 } 1588 }); 1589 return handle; 1590 }, 1591 1592 /** 1593 * Listen to a custom event hosted by this object one time. 1594 * This is the equivalent to <code>after</code> except the 1595 * listener is immediatelly detached when it is executed. 1596 * @method onceAfter 1597 * @param {String} type The name of the event 1598 * @param {Function} fn The callback to execute in response to the event 1599 * @param {Object} [context] Override `this` object in callback 1600 * @param {Any} [arg*] 0..n additional arguments to supply to the subscriber 1601 * @return {EventHandle} A subscription handle capable of detaching that 1602 * subscription 1603 */ 1604 onceAfter: function() { 1605 var handle = this.after.apply(this, arguments); 1606 handle.batch(function(hand) { 1607 if (hand.sub) { 1608 hand.sub.once = true; 1609 } 1610 }); 1611 return handle; 1612 }, 1613 1614 /** 1615 * Takes the type parameter passed to 'on' and parses out the 1616 * various pieces that could be included in the type. If the 1617 * event type is passed without a prefix, it will be expanded 1618 * to include the prefix one is supplied or the event target 1619 * is configured with a default prefix. 1620 * @method parseType 1621 * @param {String} type the type 1622 * @param {String} [pre] The prefix. Defaults to this._yuievt.config.prefix 1623 * @since 3.3.0 1624 * @return {Array} an array containing: 1625 * * the detach category, if supplied, 1626 * * the prefixed event type, 1627 * * whether or not this is an after listener, 1628 * * the supplied event type 1629 */ 1630 parseType: function(type, pre) { 1631 return _parseType(type, pre || this._yuievt.config.prefix); 1632 }, 1633 1634 /** 1635 * Subscribe a callback function to a custom event fired by this object or 1636 * from an object that bubbles its events to this object. 1637 * 1638 * Callback functions for events published with `emitFacade = true` will 1639 * receive an `EventFacade` as the first argument (typically named "e"). 1640 * These callbacks can then call `e.preventDefault()` to disable the 1641 * behavior published to that event's `defaultFn`. See the `EventFacade` 1642 * API for all available properties and methods. Subscribers to 1643 * non-`emitFacade` events will receive the arguments passed to `fire()` 1644 * after the event name. 1645 * 1646 * To subscribe to multiple events at once, pass an object as the first 1647 * argument, where the key:value pairs correspond to the eventName:callback, 1648 * or pass an array of event names as the first argument to subscribe to 1649 * all listed events with the same callback. 1650 * 1651 * Returning `false` from a callback is supported as an alternative to 1652 * calling `e.preventDefault(); e.stopPropagation();`. However, it is 1653 * recommended to use the event methods whenever possible. 1654 * 1655 * @method on 1656 * @param {String} type The name of the event 1657 * @param {Function} fn The callback to execute in response to the event 1658 * @param {Object} [context] Override `this` object in callback 1659 * @param {Any} [arg*] 0..n additional arguments to supply to the subscriber 1660 * @return {EventHandle} A subscription handle capable of detaching that 1661 * subscription 1662 */ 1663 on: function(type, fn, context) { 1664 1665 var yuievt = this._yuievt, 1666 parts = _parseType(type, yuievt.config.prefix), f, c, args, ret, ce, 1667 detachcategory, handle, store = Y.Env.evt.handles, after, adapt, shorttype, 1668 Node = Y.Node, n, domevent, isArr; 1669 1670 // full name, args, detachcategory, after 1671 this._monitor('attach', parts[1], { 1672 args: arguments, 1673 category: parts[0], 1674 after: parts[2] 1675 }); 1676 1677 if (L.isObject(type)) { 1678 1679 if (L.isFunction(type)) { 1680 return Y.Do.before.apply(Y.Do, arguments); 1681 } 1682 1683 f = fn; 1684 c = context; 1685 args = nativeSlice.call(arguments, 0); 1686 ret = []; 1687 1688 if (L.isArray(type)) { 1689 isArr = true; 1690 } 1691 1692 after = type._after; 1693 delete type._after; 1694 1695 Y.each(type, function(v, k) { 1696 1697 if (L.isObject(v)) { 1698 f = v.fn || ((L.isFunction(v)) ? v : f); 1699 c = v.context || c; 1700 } 1701 1702 var nv = (after) ? AFTER_PREFIX : ''; 1703 1704 args[0] = nv + ((isArr) ? v : k); 1705 args[1] = f; 1706 args[2] = c; 1707 1708 ret.push(this.on.apply(this, args)); 1709 1710 }, this); 1711 1712 return (yuievt.chain) ? this : new Y.EventHandle(ret); 1713 } 1714 1715 detachcategory = parts[0]; 1716 after = parts[2]; 1717 shorttype = parts[3]; 1718 1719 // extra redirection so we catch adaptor events too. take a look at this. 1720 if (Node && Y.instanceOf(this, Node) && (shorttype in Node.DOM_EVENTS)) { 1721 args = nativeSlice.call(arguments, 0); 1722 args.splice(2, 0, Node.getDOMNode(this)); 1723 return Y.on.apply(Y, args); 1724 } 1725 1726 type = parts[1]; 1727 1728 if (Y.instanceOf(this, YUI)) { 1729 1730 adapt = Y.Env.evt.plugins[type]; 1731 args = nativeSlice.call(arguments, 0); 1732 args[0] = shorttype; 1733 1734 if (Node) { 1735 n = args[2]; 1736 1737 if (Y.instanceOf(n, Y.NodeList)) { 1738 n = Y.NodeList.getDOMNodes(n); 1739 } else if (Y.instanceOf(n, Node)) { 1740 n = Node.getDOMNode(n); 1741 } 1742 1743 domevent = (shorttype in Node.DOM_EVENTS); 1744 1745 // Captures both DOM events and event plugins. 1746 if (domevent) { 1747 args[2] = n; 1748 } 1749 } 1750 1751 // check for the existance of an event adaptor 1752 if (adapt) { 1753 handle = adapt.on.apply(Y, args); 1754 } else if ((!type) || domevent) { 1755 handle = Y.Event._attach(args); 1756 } 1757 1758 } 1759 1760 if (!handle) { 1761 ce = yuievt.events[type] || this.publish(type); 1762 handle = ce._on(fn, context, (arguments.length > 3) ? nativeSlice.call(arguments, 3) : null, (after) ? 'after' : true); 1763 1764 // TODO: More robust regex, accounting for category 1765 if (type.indexOf("*:") !== -1) { 1766 this._hasSiblings = true; 1767 } 1768 } 1769 1770 if (detachcategory) { 1771 store[detachcategory] = store[detachcategory] || {}; 1772 store[detachcategory][type] = store[detachcategory][type] || []; 1773 store[detachcategory][type].push(handle); 1774 } 1775 1776 return (yuievt.chain) ? this : handle; 1777 1778 }, 1779 1780 /** 1781 * subscribe to an event 1782 * @method subscribe 1783 * @deprecated use on 1784 */ 1785 subscribe: function() { 1786 return this.on.apply(this, arguments); 1787 }, 1788 1789 /** 1790 * Detach one or more listeners the from the specified event 1791 * @method detach 1792 * @param type {string|Object} Either the handle to the subscriber or the 1793 * type of event. If the type 1794 * is not specified, it will attempt to remove 1795 * the listener from all hosted events. 1796 * @param fn {Function} The subscribed function to unsubscribe, if not 1797 * supplied, all subscribers will be removed. 1798 * @param context {Object} The custom object passed to subscribe. This is 1799 * optional, but if supplied will be used to 1800 * disambiguate multiple listeners that are the same 1801 * (e.g., you subscribe many object using a function 1802 * that lives on the prototype) 1803 * @return {EventTarget} the host 1804 */ 1805 detach: function(type, fn, context) { 1806 1807 var evts = this._yuievt.events, 1808 i, 1809 Node = Y.Node, 1810 isNode = Node && (Y.instanceOf(this, Node)); 1811 1812 // detachAll disabled on the Y instance. 1813 if (!type && (this !== Y)) { 1814 for (i in evts) { 1815 if (evts.hasOwnProperty(i)) { 1816 evts[i].detach(fn, context); 1817 } 1818 } 1819 if (isNode) { 1820 Y.Event.purgeElement(Node.getDOMNode(this)); 1821 } 1822 1823 return this; 1824 } 1825 1826 var parts = _parseType(type, this._yuievt.config.prefix), 1827 detachcategory = L.isArray(parts) ? parts[0] : null, 1828 shorttype = (parts) ? parts[3] : null, 1829 adapt, store = Y.Env.evt.handles, detachhost, cat, args, 1830 ce, 1831 1832 keyDetacher = function(lcat, ltype, host) { 1833 var handles = lcat[ltype], ce, i; 1834 if (handles) { 1835 for (i = handles.length - 1; i >= 0; --i) { 1836 ce = handles[i].evt; 1837 if (ce.host === host || ce.el === host) { 1838 handles[i].detach(); 1839 } 1840 } 1841 } 1842 }; 1843 1844 if (detachcategory) { 1845 1846 cat = store[detachcategory]; 1847 type = parts[1]; 1848 detachhost = (isNode) ? Y.Node.getDOMNode(this) : this; 1849 1850 if (cat) { 1851 if (type) { 1852 keyDetacher(cat, type, detachhost); 1853 } else { 1854 for (i in cat) { 1855 if (cat.hasOwnProperty(i)) { 1856 keyDetacher(cat, i, detachhost); 1857 } 1858 } 1859 } 1860 1861 return this; 1862 } 1863 1864 // If this is an event handle, use it to detach 1865 } else if (L.isObject(type) && type.detach) { 1866 type.detach(); 1867 return this; 1868 // extra redirection so we catch adaptor events too. take a look at this. 1869 } else if (isNode && ((!shorttype) || (shorttype in Node.DOM_EVENTS))) { 1870 args = nativeSlice.call(arguments, 0); 1871 args[2] = Node.getDOMNode(this); 1872 Y.detach.apply(Y, args); 1873 return this; 1874 } 1875 1876 adapt = Y.Env.evt.plugins[shorttype]; 1877 1878 // The YUI instance handles DOM events and adaptors 1879 if (Y.instanceOf(this, YUI)) { 1880 args = nativeSlice.call(arguments, 0); 1881 // use the adaptor specific detach code if 1882 if (adapt && adapt.detach) { 1883 adapt.detach.apply(Y, args); 1884 return this; 1885 // DOM event fork 1886 } else if (!type || (!adapt && Node && (type in Node.DOM_EVENTS))) { 1887 args[0] = type; 1888 Y.Event.detach.apply(Y.Event, args); 1889 return this; 1890 } 1891 } 1892 1893 // ce = evts[type]; 1894 ce = evts[parts[1]]; 1895 if (ce) { 1896 ce.detach(fn, context); 1897 } 1898 1899 return this; 1900 }, 1901 1902 /** 1903 * detach a listener 1904 * @method unsubscribe 1905 * @deprecated use detach 1906 */ 1907 unsubscribe: function() { 1908 return this.detach.apply(this, arguments); 1909 }, 1910 1911 /** 1912 * Removes all listeners from the specified event. If the event type 1913 * is not specified, all listeners from all hosted custom events will 1914 * be removed. 1915 * @method detachAll 1916 * @param type {String} The type, or name of the event 1917 */ 1918 detachAll: function(type) { 1919 return this.detach(type); 1920 }, 1921 1922 /** 1923 * Removes all listeners from the specified event. If the event type 1924 * is not specified, all listeners from all hosted custom events will 1925 * be removed. 1926 * @method unsubscribeAll 1927 * @param type {String} The type, or name of the event 1928 * @deprecated use detachAll 1929 */ 1930 unsubscribeAll: function() { 1931 return this.detachAll.apply(this, arguments); 1932 }, 1933 1934 /** 1935 * Creates a new custom event of the specified type. If a custom event 1936 * by that name already exists, it will not be re-created. In either 1937 * case the custom event is returned. 1938 * 1939 * @method publish 1940 * 1941 * @param type {String} the type, or name of the event 1942 * @param opts {object} optional config params. Valid properties are: 1943 * 1944 * <ul> 1945 * <li> 1946 * 'broadcast': whether or not the YUI instance and YUI global are notified when the event is fired (false) 1947 * </li> 1948 * <li> 1949 * 'bubbles': whether or not this event bubbles (true) 1950 * Events can only bubble if emitFacade is true. 1951 * </li> 1952 * <li> 1953 * 'context': the default execution context for the listeners (this) 1954 * </li> 1955 * <li> 1956 * 'defaultFn': the default function to execute when this event fires if preventDefault was not called 1957 * </li> 1958 * <li> 1959 * 'emitFacade': whether or not this event emits a facade (false) 1960 * </li> 1961 * <li> 1962 * 'prefix': the prefix for this targets events, e.g., 'menu' in 'menu:click' 1963 * </li> 1964 * <li> 1965 * 'fireOnce': if an event is configured to fire once, new subscribers after 1966 * the fire will be notified immediately. 1967 * </li> 1968 * <li> 1969 * 'async': fireOnce event listeners will fire synchronously if the event has already 1970 * fired unless async is true. 1971 * </li> 1972 * <li> 1973 * 'preventable': whether or not preventDefault() has an effect (true) 1974 * </li> 1975 * <li> 1976 * 'preventedFn': a function that is executed when preventDefault is called 1977 * </li> 1978 * <li> 1979 * 'queuable': whether or not this event can be queued during bubbling (false) 1980 * </li> 1981 * <li> 1982 * 'silent': if silent is true, debug messages are not provided for this event. 1983 * </li> 1984 * <li> 1985 * 'stoppedFn': a function that is executed when stopPropagation is called 1986 * </li> 1987 * 1988 * <li> 1989 * 'monitored': specifies whether or not this event should send notifications about 1990 * when the event has been attached, detached, or published. 1991 * </li> 1992 * <li> 1993 * 'type': the event type (valid option if not provided as the first parameter to publish) 1994 * </li> 1995 * </ul> 1996 * 1997 * @return {CustomEvent} the custom event 1998 * 1999 */ 2000 publish: function(type, opts) { 2001 2002 var ret, 2003 etState = this._yuievt, 2004 etConfig = etState.config, 2005 pre = etConfig.prefix; 2006 2007 if (typeof type === "string") { 2008 if (pre) { 2009 type = _getType(type, pre); 2010 } 2011 ret = this._publish(type, etConfig, opts); 2012 } else { 2013 ret = {}; 2014 2015 Y.each(type, function(v, k) { 2016 if (pre) { 2017 k = _getType(k, pre); 2018 } 2019 ret[k] = this._publish(k, etConfig, v || opts); 2020 }, this); 2021 2022 } 2023 2024 return ret; 2025 }, 2026 2027 /** 2028 * Returns the fully qualified type, given a short type string. 2029 * That is, returns "foo:bar" when given "bar" if "foo" is the configured prefix. 2030 * 2031 * NOTE: This method, unlike _getType, does no checking of the value passed in, and 2032 * is designed to be used with the low level _publish() method, for critical path 2033 * implementations which need to fast-track publish for performance reasons. 2034 * 2035 * @method _getFullType 2036 * @private 2037 * @param {String} type The short type to prefix 2038 * @return {String} The prefixed type, if a prefix is set, otherwise the type passed in 2039 */ 2040 _getFullType : function(type) { 2041 2042 var pre = this._yuievt.config.prefix; 2043 2044 if (pre) { 2045 return pre + PREFIX_DELIMITER + type; 2046 } else { 2047 return type; 2048 } 2049 }, 2050 2051 /** 2052 * The low level event publish implementation. It expects all the massaging to have been done 2053 * outside of this method. e.g. the `type` to `fullType` conversion. It's designed to be a fast 2054 * path publish, which can be used by critical code paths to improve performance. 2055 * 2056 * @method _publish 2057 * @private 2058 * @param {String} fullType The prefixed type of the event to publish. 2059 * @param {Object} etOpts The EventTarget specific configuration to mix into the published event. 2060 * @param {Object} ceOpts The publish specific configuration to mix into the published event. 2061 * @return {CustomEvent} The published event. If called without `etOpts` or `ceOpts`, this will 2062 * be the default `CustomEvent` instance, and can be configured independently. 2063 */ 2064 _publish : function(fullType, etOpts, ceOpts) { 2065 2066 var ce, 2067 etState = this._yuievt, 2068 etConfig = etState.config, 2069 host = etConfig.host, 2070 context = etConfig.context, 2071 events = etState.events; 2072 2073 ce = events[fullType]; 2074 2075 // PERF: Hate to pull the check out of monitor, but trying to keep critical path tight. 2076 if ((etConfig.monitored && !ce) || (ce && ce.monitored)) { 2077 this._monitor('publish', fullType, { 2078 args: arguments 2079 }); 2080 } 2081 2082 if (!ce) { 2083 // Publish event 2084 ce = events[fullType] = new Y.CustomEvent(fullType, etOpts); 2085 2086 if (!etOpts) { 2087 ce.host = host; 2088 ce.context = context; 2089 } 2090 } 2091 2092 if (ceOpts) { 2093 mixConfigs(ce, ceOpts, true); 2094 } 2095 2096 return ce; 2097 }, 2098 2099 /** 2100 * This is the entry point for the event monitoring system. 2101 * You can monitor 'attach', 'detach', 'fire', and 'publish'. 2102 * When configured, these events generate an event. click -> 2103 * click_attach, click_detach, click_publish -- these can 2104 * be subscribed to like other events to monitor the event 2105 * system. Inividual published events can have monitoring 2106 * turned on or off (publish can't be turned off before it 2107 * it published) by setting the events 'monitor' config. 2108 * 2109 * @method _monitor 2110 * @param what {String} 'attach', 'detach', 'fire', or 'publish' 2111 * @param eventType {String|CustomEvent} The prefixed name of the event being monitored, or the CustomEvent object. 2112 * @param o {Object} Information about the event interaction, such as 2113 * fire() args, subscription category, publish config 2114 * @private 2115 */ 2116 _monitor: function(what, eventType, o) { 2117 var monitorevt, ce, type; 2118 2119 if (eventType) { 2120 if (typeof eventType === "string") { 2121 type = eventType; 2122 ce = this.getEvent(eventType, true); 2123 } else { 2124 ce = eventType; 2125 type = eventType.type; 2126 } 2127 2128 if ((this._yuievt.config.monitored && (!ce || ce.monitored)) || (ce && ce.monitored)) { 2129 monitorevt = type + '_' + what; 2130 o.monitored = what; 2131 this.fire.call(this, monitorevt, o); 2132 } 2133 } 2134 }, 2135 2136 /** 2137 * Fire a custom event by name. The callback functions will be executed 2138 * from the context specified when the event was created, and with the 2139 * following parameters. 2140 * 2141 * The first argument is the event type, and any additional arguments are 2142 * passed to the listeners as parameters. If the first of these is an 2143 * object literal, and the event is configured to emit an event facade, 2144 * that object is mixed into the event facade and the facade is provided 2145 * in place of the original object. 2146 * 2147 * If the custom event object hasn't been created, then the event hasn't 2148 * been published and it has no subscribers. For performance sake, we 2149 * immediate exit in this case. This means the event won't bubble, so 2150 * if the intention is that a bubble target be notified, the event must 2151 * be published on this object first. 2152 * 2153 * @method fire 2154 * @param type {String|Object} The type of the event, or an object that contains 2155 * a 'type' property. 2156 * @param arguments {Object*} an arbitrary set of parameters to pass to 2157 * the handler. If the first of these is an object literal and the event is 2158 * configured to emit an event facade, the event facade will replace that 2159 * parameter after the properties the object literal contains are copied to 2160 * the event facade. 2161 * @return {Boolean} True if the whole lifecycle of the event went through, 2162 * false if at any point the event propagation was halted. 2163 */ 2164 fire: function(type) { 2165 2166 var typeIncluded = (typeof type === "string"), 2167 argCount = arguments.length, 2168 t = type, 2169 yuievt = this._yuievt, 2170 etConfig = yuievt.config, 2171 pre = etConfig.prefix, 2172 ret, 2173 ce, 2174 ce2, 2175 args; 2176 2177 if (typeIncluded && argCount <= 3) { 2178 2179 // PERF: Try to avoid slice/iteration for the common signatures 2180 2181 // Most common 2182 if (argCount === 2) { 2183 args = [arguments[1]]; // fire("foo", {}) 2184 } else if (argCount === 3) { 2185 args = [arguments[1], arguments[2]]; // fire("foo", {}, opts) 2186 } else { 2187 args = []; // fire("foo") 2188 } 2189 2190 } else { 2191 args = nativeSlice.call(arguments, ((typeIncluded) ? 1 : 0)); 2192 } 2193 2194 if (!typeIncluded) { 2195 t = (type && type.type); 2196 } 2197 2198 if (pre) { 2199 t = _getType(t, pre); 2200 } 2201 2202 ce = yuievt.events[t]; 2203 2204 if (this._hasSiblings) { 2205 ce2 = this.getSibling(t, ce); 2206 2207 if (ce2 && !ce) { 2208 ce = this.publish(t); 2209 } 2210 } 2211 2212 // PERF: trying to avoid function call, since this is a critical path 2213 if ((etConfig.monitored && (!ce || ce.monitored)) || (ce && ce.monitored)) { 2214 this._monitor('fire', (ce || t), { 2215 args: args 2216 }); 2217 } 2218 2219 // this event has not been published or subscribed to 2220 if (!ce) { 2221 if (yuievt.hasTargets) { 2222 return this.bubble({ type: t }, args, this); 2223 } 2224 2225 // otherwise there is nothing to be done 2226 ret = true; 2227 } else { 2228 2229 if (ce2) { 2230 ce.sibling = ce2; 2231 } 2232 2233 ret = ce._fire(args); 2234 } 2235 2236 return (yuievt.chain) ? this : ret; 2237 }, 2238 2239 getSibling: function(type, ce) { 2240 var ce2; 2241 2242 // delegate to *:type events if there are subscribers 2243 if (type.indexOf(PREFIX_DELIMITER) > -1) { 2244 type = _wildType(type); 2245 ce2 = this.getEvent(type, true); 2246 if (ce2) { 2247 ce2.applyConfig(ce); 2248 ce2.bubbles = false; 2249 ce2.broadcast = 0; 2250 } 2251 } 2252 2253 return ce2; 2254 }, 2255 2256 /** 2257 * Returns the custom event of the provided type has been created, a 2258 * falsy value otherwise 2259 * @method getEvent 2260 * @param type {String} the type, or name of the event 2261 * @param prefixed {String} if true, the type is prefixed already 2262 * @return {CustomEvent} the custom event or null 2263 */ 2264 getEvent: function(type, prefixed) { 2265 var pre, e; 2266 2267 if (!prefixed) { 2268 pre = this._yuievt.config.prefix; 2269 type = (pre) ? _getType(type, pre) : type; 2270 } 2271 e = this._yuievt.events; 2272 return e[type] || null; 2273 }, 2274 2275 /** 2276 * Subscribe to a custom event hosted by this object. The 2277 * supplied callback will execute after any listeners add 2278 * via the subscribe method, and after the default function, 2279 * if configured for the event, has executed. 2280 * 2281 * @method after 2282 * @param {String} type The name of the event 2283 * @param {Function} fn The callback to execute in response to the event 2284 * @param {Object} [context] Override `this` object in callback 2285 * @param {Any} [arg*] 0..n additional arguments to supply to the subscriber 2286 * @return {EventHandle} A subscription handle capable of detaching the 2287 * subscription 2288 */ 2289 after: function(type, fn) { 2290 2291 var a = nativeSlice.call(arguments, 0); 2292 2293 switch (L.type(type)) { 2294 case 'function': 2295 return Y.Do.after.apply(Y.Do, arguments); 2296 case 'array': 2297 // YArray.each(a[0], function(v) { 2298 // v = AFTER_PREFIX + v; 2299 // }); 2300 // break; 2301 case 'object': 2302 a[0]._after = true; 2303 break; 2304 default: 2305 a[0] = AFTER_PREFIX + type; 2306 } 2307 2308 return this.on.apply(this, a); 2309 2310 }, 2311 2312 /** 2313 * Executes the callback before a DOM event, custom event 2314 * or method. If the first argument is a function, it 2315 * is assumed the target is a method. For DOM and custom 2316 * events, this is an alias for Y.on. 2317 * 2318 * For DOM and custom events: 2319 * type, callback, context, 0-n arguments 2320 * 2321 * For methods: 2322 * callback, object (method host), methodName, context, 0-n arguments 2323 * 2324 * @method before 2325 * @return detach handle 2326 */ 2327 before: function() { 2328 return this.on.apply(this, arguments); 2329 } 2330 2331 }; 2332 2333 Y.EventTarget = ET; 2334 2335 // make Y an event target 2336 Y.mix(Y, ET.prototype); 2337 ET.call(Y, { bubbles: false }); 2338 2339 YUI.Env.globalEvents = YUI.Env.globalEvents || new ET(); 2340 2341 /** 2342 * Hosts YUI page level events. This is where events bubble to 2343 * when the broadcast config is set to 2. This property is 2344 * only available if the custom event module is loaded. 2345 * @property Global 2346 * @type EventTarget 2347 * @for YUI 2348 */ 2349 Y.Global = YUI.Env.globalEvents; 2350 2351 // @TODO implement a global namespace function on Y.Global? 2352 2353 /** 2354 `Y.on()` can do many things: 2355 2356 <ul> 2357 <li>Subscribe to custom events `publish`ed and `fire`d from Y</li> 2358 <li>Subscribe to custom events `publish`ed with `broadcast` 1 or 2 and 2359 `fire`d from any object in the YUI instance sandbox</li> 2360 <li>Subscribe to DOM events</li> 2361 <li>Subscribe to the execution of a method on any object, effectively 2362 treating that method as an event</li> 2363 </ul> 2364 2365 For custom event subscriptions, pass the custom event name as the first argument 2366 and callback as the second. The `this` object in the callback will be `Y` unless 2367 an override is passed as the third argument. 2368 2369 Y.on('io:complete', function () { 2370 Y.MyApp.updateStatus('Transaction complete'); 2371 }); 2372 2373 To subscribe to DOM events, pass the name of a DOM event as the first argument 2374 and a CSS selector string as the third argument after the callback function. 2375 Alternately, the third argument can be a `Node`, `NodeList`, `HTMLElement`, 2376 array, or simply omitted (the default is the `window` object). 2377 2378 Y.on('click', function (e) { 2379 e.preventDefault(); 2380 2381 // proceed with ajax form submission 2382 var url = this.get('action'); 2383 ... 2384 }, '#my-form'); 2385 2386 The `this` object in DOM event callbacks will be the `Node` targeted by the CSS 2387 selector or other identifier. 2388 2389 `on()` subscribers for DOM events or custom events `publish`ed with a 2390 `defaultFn` can prevent the default behavior with `e.preventDefault()` from the 2391 event object passed as the first parameter to the subscription callback. 2392 2393 To subscribe to the execution of an object method, pass arguments corresponding to the call signature for 2394 <a href="../classes/Do.html#methods_before">`Y.Do.before(...)`</a>. 2395 2396 NOTE: The formal parameter list below is for events, not for function 2397 injection. See `Y.Do.before` for that signature. 2398 2399 @method on 2400 @param {String} type DOM or custom event name 2401 @param {Function} fn The callback to execute in response to the event 2402 @param {Object} [context] Override `this` object in callback 2403 @param {Any} [arg*] 0..n additional arguments to supply to the subscriber 2404 @return {EventHandle} A subscription handle capable of detaching the 2405 subscription 2406 @see Do.before 2407 @for YUI 2408 **/ 2409 2410 /** 2411 Listen for an event one time. Equivalent to `on()`, except that 2412 the listener is immediately detached when executed. 2413 2414 See the <a href="#methods_on">`on()` method</a> for additional subscription 2415 options. 2416 2417 @see on 2418 @method once 2419 @param {String} type DOM or custom event name 2420 @param {Function} fn The callback to execute in response to the event 2421 @param {Object} [context] Override `this` object in callback 2422 @param {Any} [arg*] 0..n additional arguments to supply to the subscriber 2423 @return {EventHandle} A subscription handle capable of detaching the 2424 subscription 2425 @for YUI 2426 **/ 2427 2428 /** 2429 Listen for an event one time. Equivalent to `once()`, except, like `after()`, 2430 the subscription callback executes after all `on()` subscribers and the event's 2431 `defaultFn` (if configured) have executed. Like `after()` if any `on()` phase 2432 subscriber calls `e.preventDefault()`, neither the `defaultFn` nor the `after()` 2433 subscribers will execute. 2434 2435 The listener is immediately detached when executed. 2436 2437 See the <a href="#methods_on">`on()` method</a> for additional subscription 2438 options. 2439 2440 @see once 2441 @method onceAfter 2442 @param {String} type The custom event name 2443 @param {Function} fn The callback to execute in response to the event 2444 @param {Object} [context] Override `this` object in callback 2445 @param {Any} [arg*] 0..n additional arguments to supply to the subscriber 2446 @return {EventHandle} A subscription handle capable of detaching the 2447 subscription 2448 @for YUI 2449 **/ 2450 2451 /** 2452 Like `on()`, this method creates a subscription to a custom event or to the 2453 execution of a method on an object. 2454 2455 For events, `after()` subscribers are executed after the event's 2456 `defaultFn` unless `e.preventDefault()` was called from an `on()` subscriber. 2457 2458 See the <a href="#methods_on">`on()` method</a> for additional subscription 2459 options. 2460 2461 NOTE: The subscription signature shown is for events, not for function 2462 injection. See <a href="../classes/Do.html#methods_after">`Y.Do.after`</a> 2463 for that signature. 2464 2465 @see on 2466 @see Do.after 2467 @method after 2468 @param {String} type The custom event name 2469 @param {Function} fn The callback to execute in response to the event 2470 @param {Object} [context] Override `this` object in callback 2471 @param {Any} [args*] 0..n additional arguments to supply to the subscriber 2472 @return {EventHandle} A subscription handle capable of detaching the 2473 subscription 2474 @for YUI 2475 **/ 2476 2477 2478 }, '3.17.2', {"requires": ["oop"]});
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |