1/*! 2AniJS - http://anijs.github.io 3Licensed under the MIT license 4 5Copyright (c) 2014 Dariel Noel <[email protected]> 6 7Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 8 9The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 11THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12*/ 13 14(function(root, factory) { 15 "use strict"; 16 if (typeof module == "object" && typeof module.exports == "object") { 17 module.exports = root.document ? 18 factory(root, true) : 19 function(w) { 20 if (!w.document) { 21 throw new Error("AniJS-RWWD"); 22 } 23 return factory(w); 24 }; 25 } else { 26 factory(root); 27 } 28 29})(typeof window !== "undefined" ? window : this, function(window, noGlobal) { 30 31 32 /** 33 * AniJS is library for write declarative animations in your static html documents 34 * @class AniJSit 35 * @constructor init 36 * @author @dariel_noel 37 */ 38 var AniJS = (function(AniJS) { 39 40 //Shorthands 41 var ANIJS_DATATAG_NAME = 'data-anijs', 42 DEFAULT = 'default', 43 BODY = 'body', 44 PARAMS_SEPARATOR = '|', 45 MULTIPLE_CLASS_SEPARATOR = '$', 46 EVENT_RESERVED_WORD = 'if', 47 EVENT_TARGET_RESERVED_WORD = 'on', 48 BEHAVIOR_RESERVED_WORD = ['do', 'after', 'before', 'to'], 49 BEHAVIOR_TARGET_RESERVED_WORD = 'to', 50 REGEX_BEGIN = '(\\s+|^)', 51 REGEX_END = '(\\s+|$)', 52 ANIMATION_END = 'animationend', 53 TRANSITION_END = 'transitionend', 54 TARGET = 'target'; 55
56 ///////////////////////////////////////////////////////// 57 // Public API 58 ///////////////////////////////////////////////////////// 59 60 AniJS = { 61 62 rootDOMTravelScope: {}, 63 64 notifierCollection: {}, 65 66 /** 67 * Initializer Function 68 * @method init 69 * @return 70 */ 71 init: function() { 72 73 //ATTRS inicialization 74 selfish._helperCollection = {}; 75 76 var defaultHelper = selfish._createDefaultHelper(); 77 78 //Registering an empty helper 79 AniJS.registerHelper(DEFAULT, defaultHelper); 80 81 //Default Helper Index 82 selfish._helperDefaultIndex = DEFAULT; 83 84 AniJS.rootDOMTravelScope = document; 85 86 //Initialize the Parser Object 87 AniJS.Parser = selfish.Parser; 88 89 //AnimationEnd Correct Prefix Setup 90 selfish._animationEndEvent = selfish._animationEndPrefix(); 91 92 //Add this class names when anim 93 selfish._classNamesWhenAnim = ''; 94 }, 95 96 /** 97 * You can use these to change the scope to run AniJS 98 * @method setDOMRootTravelScope 99 * @param {} selector 100 * @return 101 */ 102 setDOMRootTravelScope: function(selector) { 103 var rootDOMTravelScope, 104 domDocument = document; 105 try { 106 if (selector === 'document') { 107 rootDOMTravelScope = domDocument; 108 } else { 109 rootDOMTravelScope = domDocument.querySelector(selector); 110 if (!rootDOMTravelScope) { 111 rootDOMTravelScope = domDocument; 112 } 113 } 114 115 } catch (e) { 116 rootDOMTravelScope = domDocument; 117 } 118 AniJS.rootDOMTravelScope = rootDOMTravelScope; 119 }, 120 121 /** 122 * Parse Declarations and setup Anim in a founded elements 123 * @method run 124 * @return 125 */ 126 run: function() { 127 var aniJSNodeCollection = [], 128 aniJSParsedSentenceCollection = {}; 129 130 //Clear all node listener 131 AniJS.purgeAll(); 132 133 AniJS.notifierCollection = {}; 134 135 aniJSNodeCollection = selfish._findAniJSNodeCollection(AniJS.rootDOMTravelScope); 136 137 var size = aniJSNodeCollection.length, 138 i = 0, 139 item; 140 141 for (i; i < size; i++) { 142 item = aniJSNodeCollection[i]; 143 144 //IMPROVE: The datatag name migth come from configuration 145 aniJSParsedSentenceCollection = selfish._getParsedAniJSSentenceCollection(item.getAttribute(ANIJS_DATATAG_NAME)); 146 147 //Le seteo su animacion 148 selfish._setupElementAnim(item, aniJSParsedSentenceCollection); 149 } 150 151 //We can use this for supply the window load and DomContentLoaded in some context 152 var aniJSEventsNotifier = AniJS.getNotifier('AniJSNotifier'); 153 if(aniJSEventsNotifier){ 154 aniJSEventsNotifier.dispatchEvent('onRunFinished'); 155 } 156 }, 157 158 /** 159 * Create an animation from a aniJSParsedSentenceCollection 160 * @method createAnimation 161 * @param {} aniJSParsedSentenceCollection 162 * @param {} element 163 * @return 164 */ 165 createAnimation: function(aniJSParsedSentenceCollection, element) { 166 var nodeElement = element || ''; 167 168 //BEAUTIFY: The params order migth be the same 169 selfish._setupElementAnim(nodeElement, aniJSParsedSentenceCollection); 170 }, 171 172 /** 173 * Return a Helper by ID, you can use this to attach callback to the Helper 174 * @method getHelper 175 * @param {} helperID 176 * @return LogicalExpression 177 */ 178 getHelper: function(helperID) { 179 var helperCollection = selfish._helperCollection; 180 return helperCollection[helperID] || helperCollection[DEFAULT]; 181 }, 182 183 /** 184 * A helper it's a callback function container 185 * using this function you can register your custom Helper 186 * @method registerHelper 187 * @param {} helperName 188 * @param {} helperInstance 189 * @return 190 */ 191 registerHelper: function(helperName, helperInstance) { 192 selfish._helperCollection[helperName] = helperInstance; 193 }, 194 195 /** 196 * Purge a NodeList By Selector 197 * @method purge 198 * @param {} selector 199 * @return 200 */ 201 purge: function(selector) { 202 //TODO: Search a regular expression for test a valid CSS selector 203 if (selector && selector !== '' && selector !== ' ') { 204 var purgeNodeCollection = document.querySelectorAll(selector), 205 size = purgeNodeCollection.length, 206 i = 0; 207 208 for (i; i < size; i++) { 209 AniJS.EventSystem.purgeEventTarget(purgeNodeCollection[i]); 210 } 211 } 212 }, 213 214 /** 215 * Purge all register elements handle 216 * you can use this when you run AniJS again 217 * @method purgeAll 218 * @return 219 */ 220 purgeAll: function() { 221 AniJS.EventSystem.purgeAll(); 222 }, 223 224 /** 225 * Remove all listener from an element 226 * @method purgeEventTarget 227 * @param {} element 228 * @return 229 */ 230 purgeEventTarget: function(element) { 231 AniJS.EventSystem.purgeEventTarget(element); 232 }, 233 234 /** 235 * Add default class names while Anim 236 * @method setClassNamesWhenAnim 237 * @param {} defaultClasses 238 * @return 239 */ 240 setClassNamesWhenAnim: function(defaultClasses) { 241 selfish._classNamesWhenAnim = ' ' + defaultClasses; 242 }, 243 244 /** 245 * Create an EventTarget 246 * @method createNotifier 247 * @return EventTarget 248 */ 249 createNotifier: function() { 250 return AniJS.EventSystem.createEventTarget(); 251 }, 252 253 /** 254 * Put an event Notifier in the notifierCollection 255 * @method registerNotifier 256 * @param {} notifier 257 * @return Literal 258 */ 259 registerNotifier: function(notifier) { 260 var notifierCollection = AniJS.notifierCollection; 261 262 //TODO: Optimize lookups here 263 if (notifier.id && notifier.value && AniJS.EventSystem.isEventTarget(notifier.value)) { 264 notifierCollection[notifier.id] = notifier.value; 265 return 1; 266 } 267 268 return ''; 269 }, 270 271 /** 272 * Return an notifier instance 273 * @method getNotifier 274 * @param {} notifierID 275 * @return notifier 276 */ 277 getNotifier: function(notifierID) { 278 return AniJS.notifierCollection[notifierID]; 279 } 280 281 }; 282
283 ///////////////////////////////////////////////////////// 284 // Private Methods an Vars 285 ///////////////////////////////////////////////////////// 286 287 var selfish = { 288 289 }; 290 291 /** 292 * Description 293 * @method _createDefaultHelper 294 * @return defaultHelper 295 */ 296 selfish._createDefaultHelper = function() { 297 //TODO: Why default helper here, migth be directly in the public API 298 var defaultHelper = { 299 /** 300 * Remove the animation class added when animation is created 301 * @method removeAnim 302 * @param {} e 303 * @param {} animationContext 304 * @return 305 */ 306 removeAnim: function(e, animationContext) { 307 if(e.target && e.type){ 308 animationContext.nodeHelper.removeClass(e.target, animationContext.behavior); 309 } 310 }, 311 /** 312 * Holds the animation class added when animation is created 313 * @method holdAnimClass 314 * @param {} e 315 * @param {} animationContext 316 * @return 317 */ 318 holdAnimClass: function(e, animationContext) { 319 }, 320 321 fireOnce: function(e, animationContext) { 322 animationContext.eventSystem.removeEventListenerHelper(animationContext.eventTarget, animationContext.event.type, animationContext.listener); 323 }, 324 325 /** 326 * Fire custom event 327 * 328 * Examples: 329 * 330 * Fire dummyEvent event of customNotifier 331 * 332 * if: click, do: $addClass fadeIn animated, to: #container, after: emit customNotifier.dummyEvent 333 * if: dummyEvent, on: $customNotifier, do: $addClass hidden, to: $children #container | div 334 * 335 * 336 * @author Yolier Galan Tasse <[email protected]> 337 * @since 2014-09-20 338 * @param {object} e The event handler 339 * @param {object} ctx AniJS Animation Context Object 340 * @param {[string]} params [description] 341 */ 342 emit: function(e, ctx, params) { 343 var cevent = params[0] || null, 344 notifier = ""; 345 if(cevent !== null) { 346 cevent = cevent.split('.'); 347 if(cevent.length > 1) { 348 notifier = cevent[0]; 349 cevent = cevent[1]; 350 } else { 351 notifier = ""; 352 cevent = cevent[0]; 353 } 354 var customNotifier = AniJS.getNotifier(notifier) || null; 355 if(customNotifier !== null) 356 customNotifier.dispatchEvent(cevent); 357 } 358 //Run the animation 359 if(!ctx.hasRunned){ 360 ctx.run(); 361 } 362 } 363 }; 364 365 return defaultHelper; 366 }; 367 368 /** 369 * Create a Parser Instance 370 * @method _createParser 371 * @return Parser 372 */ 373 selfish._createParser = function() { 374 //TODO: The Parser could be an static class 375 return new Parser(); 376 }; 377 378 /** 379 * Setup the animation of the some element 380 * @method _setupElementAnim 381 * @param {} element 382 * @param {} aniJSParsedSentenceCollection 383 * @return 384 */ 385 selfish._setupElementAnim = function(element, aniJSParsedSentenceCollection) { 386 var size = aniJSParsedSentenceCollection.length, 387 i = 0, 388 item, 389 before, 390 after, 391 behavior; 392 393 for (i; i < size; i++) { 394 item = aniJSParsedSentenceCollection[i]; 395 after = item.after; 396 before = item.before; 397 behavior = item.behavior; 398 399 if(after){ 400 item.after = selfish.Parser.parseDoDefinition(after); 401 } 402 if(before){ 403 item.before = selfish.Parser.parseDoDefinition(before); 404 } 405 if(behavior){ 406 item.behavior = selfish.Parser.parseDoDefinition(behavior); 407 } 408 selfish._setupElementSentenceAnim(element, item); 409 } 410 }; 411 412 /** 413 * Setup the element animation from a AniJS Sentence 414 * @method _setupElementSentenceAnim 415 * @param {} element 416 * @param {} aniJSParsedSentence 417 * @return 418 */ 419 selfish._setupElementSentenceAnim = function(element, aniJSParsedSentence) { 420 //TODO: If the user use animationend or transitionend names to custom events the eventdispach will be not executed 421 var event = selfish._eventHelper(aniJSParsedSentence), 422 eventTargetList = selfish._eventTargetHelper(element, aniJSParsedSentence), 423 afterFunctionName; 424 425 if(aniJSParsedSentence.after && selfish.Util.beArray(aniJSParsedSentence.after)){ 426 afterFunctionName = aniJSParsedSentence.after[0]; 427 } 428 429 //Es obligatorio definir de eventTarget ATTR 430 if (event !== '') { 431 var size = eventTargetList.length, 432 i = 0, 433 eventTargetItem; 434 for (i; i < size; i++) { 435 eventTargetItem = eventTargetList[i]; 436 if (AniJS.EventSystem.isEventTarget(eventTargetItem)) { 437 var listener = function(event) { 438 439 //Si cambia algun parametro dinamicamente entonces hay que enterarse 440 var behaviorTargetList = selfish._behaviorTargetHelper(element, aniJSParsedSentence, event), 441 behavior = selfish._behaviorHelper(aniJSParsedSentence), 442 before = selfish._beforeHelper(element, aniJSParsedSentence), 443 after = selfish._afterHelper(element, aniJSParsedSentence); 444 if (selfish._classNamesWhenAnim !== '') { 445 if(!selfish.Util.beArray(behavior)) 446 behavior += selfish._classNamesWhenAnim; 447 } 448 //Creo un nuevo animation context 449 var animationContextConfig = { 450 behaviorTargetList: behaviorTargetList, 451 nodeHelper: selfish.NodeHelper, 452 animationEndEvent: selfish._animationEndEvent, 453 behavior: behavior, 454 after: after, 455 eventSystem: AniJS.EventSystem, 456 eventTarget: event.currentTarget, 457 afterFunctionName: afterFunctionName, 458 dataAniJSOwner: element, 459 listener: listener, 460 event: event, 461 before: before 462 //TODO: eventSystem should be called directly 463 }, 464 465 animationContextInstance = new AniJS.AnimationContext(animationContextConfig); 466 467 animationContextInstance.runAll(animationContextConfig); 468 }; 469 470 //TODO: Improve lookup here AniJS.EventSystem 471 AniJS.EventSystem.addEventListenerHelper(eventTargetItem, event, listener, false); 472 473 //Register event to feature handle 474 AniJS.EventSystem.registerEventHandle(eventTargetItem, event, listener); 475 } 476 } 477 } 478 }; 479 480 /** 481 * Helper to setup the Event that trigger the animation from declaration 482 * https://developer.mozilla.org/en-US/docs/Web/Reference/Events 483 * http://www.w3schools.com/tags/ref_eventattributes.asp 484 * @method _eventHelper 485 * @param {} element 486 * @param {} aniJSParsedSentence 487 * @return event 488 */ 489 selfish._eventHelper = function(aniJSParsedSentence) { 490 var defaultValue = '', 491 event = aniJSParsedSentence.event || defaultValue; 492 493 //TODO: Improve to reduce this ugly logic here 494 if (event === ANIMATION_END) { 495 event = selfish._animationEndPrefix(); 496 } else if (event === TRANSITION_END) { 497 event = selfish._transitionEndPrefix(); 498 } 499 500 return event; 501 }; 502 503 /** 504 * Helper to setup the Place from listen the trigger event of the animation 505 * If is not specified one place, se asume que es himself 506 * Take in account that where it's just a selector 507 * @method _eventTargetHelper 508 * @param {} element 509 * @param {} aniJSParsedSentence 510 * @return eventTargetList 511 */ 512 selfish._eventTargetHelper = function(element, aniJSParsedSentence) { 513 var defaultValue = element, 514 eventTargetList = [defaultValue], 515 rootDOMTravelScope = AniJS.rootDOMTravelScope, 516 notifierList; 517 518 //TODO: We could add other non direct DOM Objects 519 if (aniJSParsedSentence.eventTarget) { 520 521 notifierList = selfish._notifierHelper(aniJSParsedSentence.eventTarget); 522 523 if (notifierList.length > 0) { 524 eventTargetList = notifierList; 525 } else if (aniJSParsedSentence.eventTarget === 'document') { 526 eventTargetList = [document]; 527 } else if (aniJSParsedSentence.eventTarget === 'window') { 528 eventTargetList = [window]; 529 } else if (aniJSParsedSentence.eventTarget.split) { 530 try { 531 eventTargetList = rootDOMTravelScope.querySelectorAll(aniJSParsedSentence.eventTarget); 532 } catch (e) { 533 console.log('Ugly Selector Here'); 534 eventTargetList = []; 535 } 536 } 537 } 538 //It's not a nodeList any more 539 return eventTargetList; 540 }; 541 542 /** 543 * Helper to setup the Node can be animated 544 * @method _behaviorTargetHelper 545 * @param {} element 546 * @param {} aniJSParsedSentence 547 * @return behaviorTargetNodeList 548 */ 549 selfish._behaviorTargetHelper = function(element, aniJSParsedSentence, event) { 550 var defaultValue = element, 551 behaviorTargetNodeList = [defaultValue], 552 rootDOMTravelScope = AniJS.rootDOMTravelScope, 553 behaviorTarget = aniJSParsedSentence.behaviorTarget; 554 555 if (behaviorTarget) { 556 if(!selfish.Util.beArray(behaviorTarget)){ 557 if(behaviorTarget === TARGET && event.currentTarget){ 558 behaviorTargetNodeList = [event.currentTarget]; 559 } else{ 560 //Expression regular remplazar caracteres $ por comas 561 //TODO: Estudiar si este caracter no esta agarrado 562 behaviorTarget = behaviorTarget.split(MULTIPLE_CLASS_SEPARATOR).join(','); 563 try { 564 behaviorTargetNodeList = rootDOMTravelScope.querySelectorAll(behaviorTarget); 565 } catch (e) { 566 behaviorTargetNodeList = []; 567 } 568 } 569 } else{ 570 var behaviorObject = this._actionHelper(element, aniJSParsedSentence, behaviorTarget); 571 if(behaviorObject && selfish.Util.isFunction(behaviorObject[0])){ 572 behaviorTargetNodeList = behaviorObject[0] 573 (event,{dataAniJSOwner:element}, 574 selfish._paramsHelper(behaviorObject)); 575 } 576 } 577 } 578 return behaviorTargetNodeList; 579 }; 580 581 /** 582 * Helper to setup the Animation type 583 * @method _behaviorHelper 584 * @param {} element 585 * @param {} aniJSParsedSentence 586 * @return defaultValue 587 */ 588 selfish._behaviorHelper = function(aniJSParsedSentence) { 589 return this._actionHelper({}, aniJSParsedSentence, aniJSParsedSentence.behavior); 590 }; 591 592 /** 593 * Helper to setup the after callback function 594 * @method _afterHelper 595 * @param {} element 596 * @param {} aniJSParsedSentence 597 * @return defaultValue 598 */ 599 selfish._afterHelper = function(element, aniJSParsedSentence) { 600 var defaultValue = aniJSParsedSentence.after; 601 // return defaultValue; 602 if(!selfish.Util.beArray(defaultValue)) 603 return selfish._callbackHelper(element, aniJSParsedSentence, defaultValue); 604 return this._actionHelper(element, aniJSParsedSentence, defaultValue); 605 }; 606 607 /** 608 * Helper to setup the after callback function 609 * @method _afterHelper 610 * @param {} element 611 * @param {} aniJSParsedSentence 612 * @return defaultValue 613 */ 614 selfish._beforeHelper = function(element, aniJSParsedSentence) { 615 var defaultValue = aniJSParsedSentence.before; 616 if(!selfish.Util.beArray(defaultValue)) 617 return selfish._callbackHelper(element, aniJSParsedSentence, defaultValue); 618 return this._actionHelper(element, aniJSParsedSentence, defaultValue); 619 }; 620 /** 621 * The executed actions helper 622 * @author Dariel Noel <[email protected]>
623 * @since 2014-09-10 624 * @param {[type]} element [description] 625 * @param {[type]} aniJSParsedSentence [description] 626 * @param {[type]} action [description] 627 * @return {[type]} [description] 628 */ 629 selfish._actionHelper = function(element, aniJSParsedSentence, action) { 630 var defaultValue = action || '', 631 executeFunction; 632 if(selfish.Util.beArray(defaultValue)) { 633 executeFunction = selfish._callbackHelper(element, aniJSParsedSentence, defaultValue[0]); 634 if(executeFunction) { 635 defaultValue[0] = executeFunction; 636 } else { 637 defaultValue = defaultValue.join(' '); 638 } 639 640 } 641 return defaultValue; 642 }; 643 644 /** 645 * Helper for before and after helpers refactoring 646 * @method _callbackHelper 647 * @param {} element 648 * @param {} aniJSParsedSentence 649 * @param {} callbackFunction 650 * @return defaultValue 651 */ 652 selfish._callbackHelper = function(element, aniJSParsedSentence, callbackFunction) { 653 var defaultValue = callbackFunction || '', 654 helper = selfish._helperHelper(aniJSParsedSentence); 655 656 if (defaultValue) { 657 if (!selfish.Util.isFunction(defaultValue)) { 658 var helperCollection = selfish._helperCollection, 659 helperInstance = helperCollection[helper]; 660 661 if (helperInstance && selfish.Util.isFunction(helperInstance[defaultValue])) { 662 defaultValue = helperInstance[defaultValue]; 663 } else { 664 defaultValue = false; 665 } 666 } 667 } 668 669 return defaultValue; 670 }; 671 672 /** 673 * Helper to setup the helper of the animation 674 * @method _helperHelper 675 * @param {} element 676 * @param {} aniJSParsedSentence 677 * @return defaultValue 678 */ 679 selfish._helperHelper = function(aniJSParsedSentence) { 680 var defaultValue = aniJSParsedSentence.helper || selfish._helperDefaultIndex; 681 return defaultValue; 682 }; 683 684 /** 685 * Helper to setup the notifier 686 * @method _notifierHelper 687 * @param {} eventTargetDefinition 688 * @return defaultValue 689 */ 690 selfish._notifierHelper = function(eventTargetDefinition) { 691 var defaultValue = [], 692 notifierCollection = AniJS.notifierCollection; 693 694 if (eventTargetDefinition) { 695 //{id: notifierID, value:notifierObject} 696 if (eventTargetDefinition.id && AniJS.EventSystem.isEventTarget(eventTargetDefinition.value)) { 697 //TODO: In the near future could be an object list 698 defaultValue.push(eventTargetDefinition.value); 699 700 AniJS.registerNotifier(eventTargetDefinition); 701 } else if (eventTargetDefinition.split) { 702 //Picar por signo de peso y obtener la lista de id de events Notifiers 703 notifierIDList = eventTargetDefinition.split('$'); 704 var size = notifierIDList.length, 705 i = 1, 706 notifierID; 707 708 for (i; i < size; i++) { 709 notifierID = notifierIDList[i]; 710 if (notifierID && notifierID !== ' ') { 711 //limpiarle los espacios alante y atras (trim) 712 notifierID = notifierID.trim(); 713 714 //TODO: Big Refactoring here 715 var value = AniJS.getNotifier(notifierID); 716 if (!value) { 717 value = AniJS.EventSystem.createEventTarget(); 718 AniJS.registerNotifier({ 719 id: notifierID, 720 value: value 721 }); 722 } 723 defaultValue.push(value); 724 } 725 } 726 } 727 } 728 729 return defaultValue; 730 }; 731 732 selfish._paramsHelper = function(paramsArray){ 733 var arr = [], 734 i = paramsArray.length; 735 while(i-- > 1) arr[i - 1] = paramsArray[i]; 736 return arr; 737 }; 738 739 /** 740 * Parse an String Declaration 741 * @method _getParsedAniJSSentenceCollection 742 * @param {} stringDeclaration 743 * @return CallExpression 744 */ 745 selfish._getParsedAniJSSentenceCollection = function(stringDeclaration) { 746 return selfish.Parser.parse(stringDeclaration); 747 }; 748 749 /** 750 * Select all DOM nodes that have a AniJS declaration 751 * @method _findAniJSNodeCollection 752 * @param {} rootDOMTravelScope 753 * @return CallExpression 754 */ 755 selfish._findAniJSNodeCollection = function(rootDOMTravelScope) { 756 //IMPROVE: Might a configuration option 757 var aniJSDataTagName = '[' + ANIJS_DATATAG_NAME + ']'; 758 return rootDOMTravelScope.querySelectorAll(aniJSDataTagName); 759 }; 760 761 /** 762 * Return the correct AnimationEnd Prefix according to the current browser 763 * @method _animationEndPrefix 764 * @return 765 */ 766 selfish._animationEndPrefix = function() { 767 var endPrefixBrowserDetectionIndex = selfish._endPrefixBrowserDetectionIndex(), 768 animationEndBrowserPrefix = [ANIMATION_END, 'oAnimationEnd', ANIMATION_END, 'webkitAnimationEnd']; 769 770 return animationEndBrowserPrefix[endPrefixBrowserDetectionIndex]; 771 }; 772 773 /** 774 * Return the correct TransitionEnd Prefix according to the current browser 775 * @method _transitionEndPrefix 776 * @return 777 */ 778 selfish._transitionEndPrefix = function() { 779 var endPrefixBrowserDetectionIndex = selfish._endPrefixBrowserDetectionIndex(), 780 transitionEndBrowserPrefix = [TRANSITION_END, 'oTransitionEnd', TRANSITION_END, 'webkitTransitionEnd']; 781 782 return transitionEndBrowserPrefix[endPrefixBrowserDetectionIndex]; 783 }; 784 785 /** 786 * Return the correct Transition and Animation End Prefix helper according to the current browser 787 * @method _transitionEndPrefix 788 * @return index of the prefix acording to the browser 789 */ 790 selfish._endPrefixBrowserDetectionIndex = function() { 791 //TODO: Delete de element after create this 792 var el = document.createElement('fe'), 793 ANIM = 'Animation', 794 animationBrowserDetection = ['animation', 'O'+ANIM, 'Moz'+ANIM, 'webkit'+ANIM]; 795 796 for (var i = 0; i < animationBrowserDetection.length; i++) { 797 if (el.style[animationBrowserDetection[i]] !== undefined) { 798 return i; 799 } 800 } 801 }; 802
803 ///////////////////////////////////////////////////////// 804 // Private SubModules 805 ///////////////////////////////////////////////////////// 806 807 /** 808 * Encapsulate the animation Context 809 * @class animationContext 810 * @author @dariel_noel 811 */ 812 AniJS.AnimationContext = (function(config) { 813 814 //TODO: Module aproach here 815 var animationContextInstance = this; 816 817 /** 818 * Class constructor 819 * @method init 820 * @param {} config 821 * @return 822 */ 823 animationContextInstance.init = function(config) { 824 825 //TODO: Valorar la idea de usar prototype por performance reasons 826 //ATTRS 827 animationContextInstance.behaviorTargetList = config.behaviorTargetList || []; 828 829 animationContextInstance.nodeHelper = config.nodeHelper; 830 831 animationContextInstance.animationEndEvent = config.animationEndEvent; 832 833 animationContextInstance.behavior = config.behavior; 834 835 animationContextInstance.after = config.after; 836 837 animationContextInstance.eventSystem = config.eventSystem; 838 839 animationContextInstance.eventTarget = config.eventTarget; 840 841 animationContextInstance.afterFunctionName = config.afterFunctionName; 842 843 animationContextInstance.dataAniJSOwner = config.dataAniJSOwner; 844 845 animationContextInstance.listener = config.listener; 846 847 animationContextInstance.event = config.event; 848 animationContextInstance.before = config.before; 849 }; 850 851 /** 852 * Custom AniJS animation behavior 853 * @author Dariel Noel <[email protected]> 854 * @since 2014-09-03 855 * @param {[type]} target [description] 856 * @param {[type]} behavior [description] 857 * @return {[type]} [description] 858 */ 859 animationContextInstance.doDefaultAction = function(target, behavior){ 860 var instance = animationContextInstance, 861 nodeHelper = instance.nodeHelper, 862 animationEndEvent = instance.animationEndEvent, 863 after = instance.after, 864 afterFunctionName = instance.afterFunctionName, 865 lastBehavior; 866 867 //create event 868 instance.eventSystem.addEventListenerHelper(target, animationEndEvent, function(e) { 869 e.stopPropagation(); 870 //remove event 871 instance.eventSystem.removeEventListenerHelper(e.target, e.type, arguments.callee); 872 if(after){ 873 if(selfish.Util.isFunction(after)){ 874 after(e, animationContextInstance); 875 } else if(selfish.Util.beArray(after)) { 876 after[0](e, animationContextInstance, selfish._paramsHelper(after)); 877 } 878 } 879 }); 880 // Backguard compatibility 881 if (afterFunctionName !== "holdAnimClass" && afterFunctionName !== "$holdAnimClass") { 882 lastBehavior = target._ajLastBehavior; 883 if(lastBehavior){ 884 // removing the animation by default if there are not hold animClass 885 nodeHelper.removeClass(target, lastBehavior); 886 } 887 target._ajLastBehavior = behavior; 888 } 889 // Trigger a reflow in between removing and adding the class name. 890 // http://css-tricks.com/restart-css-animation/ 891 target.offsetWidth = target.offsetWidth; 892 nodeHelper.addClass(target, behavior); 893 }; 894 895 /** 896 * Allows to use a custom helpers function via do definitions 897 * @author Dariel Noel <[email protected]>
898 * @since 2014-09-03 899 * @param {[type]} target [description] 900 * @param {[type]} behavior [description] 901 * @return {[type]} [description] 902 */ 903 animationContextInstance.doFunctionAction = function(target, behavior){ 904 var instance = animationContextInstance, 905 after = instance.after, 906 e = {target:target}; 907 behavior[0](e, animationContextInstance, selfish._paramsHelper(behavior)); 908 if(selfish.Util.isFunction(after)){ 909 after(e, animationContextInstance); 910 } else { 911 if(selfish.Util.beArray(after)) { 912 after[0](e, animationContextInstance, selfish._paramsHelper(after)); 913 } 914 } 915 }; 916 917 /** 918 * Create an animation context instance for every behavior target list 919 * and run it 920 * @method run 921 * @return 922 */ 923 animationContextInstance.runAll = function() { 924 var instance = animationContextInstance, 925 behaviorTargetList = instance.behaviorTargetList, 926 behaviorTargetListSize = behaviorTargetList.length, 927 behavior = instance.behavior, 928 j = 0, 929 before = instance.before, 930 simpleAnimationContextInstance, 931 event = animationContextInstance.event, 932 animationContextConfig; 933 934 for (j; j < behaviorTargetListSize; j++) { 935 936 animationContextConfig = { 937 behaviorTargetList: [behaviorTargetList[j]], 938 nodeHelper: animationContextInstance.nodeHelper, 939 animationEndEvent: animationContextInstance.animationEndEvent, 940 behavior: animationContextInstance.behavior, 941 after: animationContextInstance.after, 942 eventSystem: animationContextInstance.eventSystem, 943 eventTarget: animationContextInstance.eventTarget, 944 afterFunctionName: animationContextInstance.afterFunctionName, 945 dataAniJSOwner: animationContextInstance.dataAniJSOwner, 946 listener: animationContextInstance.listener, 947 event: event 948 //TODO: eventSystem should be called directly 949 }; 950 951 simpleAnimationContextInstance = new AniJS.AnimationContext(animationContextConfig); 952 if (before) { 953 if(selfish.Util.isFunction(before)) { 954 before(event, simpleAnimationContextInstance); 955 } else if(selfish.Util.beArray(before)) { 956 before[0](event, simpleAnimationContextInstance, selfish._paramsHelper(before)); 957 } 958 } else { 959 simpleAnimationContextInstance.run(); 960 } 961 } 962 }; 963 /** 964 * Execute an animation context instance 965 * @method run 966 * @return 967 */ 968 animationContextInstance.run = function() { 969 var instance = animationContextInstance, 970 behavior = instance.behavior, 971 behaviorTargetListItem = instance.behaviorTargetList[0]; 972 973 animationContextInstance.hasRunned = 1; 974 if(selfish.Util.beArray(behavior)){ 975 instance 976 .doFunctionAction(behaviorTargetListItem, behavior); 977 } else{ 978 instance 979 .doDefaultAction(behaviorTargetListItem, behavior); 980 } 981 }; 982 983 animationContextInstance.init(config); 984 }); 985 986 /** 987 * Encapsulate the AnimJS sintax parser 988 * @class Parser 989 * @author @dariel_noel 990 */ 991 selfish.Parser = { 992 993 /** 994 * Parse a aniJSDeclaration 995 * @method parse 996 * @param {} aniJSDeclaration 997 * @return CallExpression 998 */ 999 parse: function(aniJSDeclaration) { 1000 return this.parseDeclaration(aniJSDeclaration); 1001 }, 1002 1003 /** 1004 * Declaration parse 1005 * Sintax: Declaration -> Sentence; | * 1006 * Example: SentenceA; SentenceB 1007 * @method _parseDeclaration 1008 * @param {} declaration 1009 * @return parsedDeclaration 1010 */ 1011 parseDeclaration: function(declaration) { 1012 var parsedDeclaration = [], 1013 sentenceCollection, 1014 parsedSentence; 1015 1016 sentenceCollection = declaration.split(';'); 1017 1018 var size = sentenceCollection.length, 1019 i = 0; 1020 1021 for (i; i < size; i++) { 1022 parsedSentence = this.parseSentence(sentenceCollection[i]); 1023 parsedDeclaration.push(parsedSentence); 1024 } 1025 1026 return parsedDeclaration; 1027 }, 1028 1029 /** 1030 * Sentence Parse 1031 * Sintax: Sentence -> if, on, do, to, after, helper 1032 * Example: "if: DOMContentLoaded, on: document, do:flip, to: .animatecss, after: testcallback" 1033 * note: The order it's not important 1034 * @method _parseSentence 1035 * @param {} sentence 1036 * @return parsedSentence 1037 */ 1038 parseSentence: function(sentence) { 1039 var parsedSentence = {}, 1040 definitionCollection, 1041 parsedDefinition; 1042 1043 definitionCollection = sentence.split(','); 1044 1045 var size = definitionCollection.length, 1046 i = 0; 1047 1048 for (i; i < size; i++) { 1049 parsedDefinition = this.parseDefinition(definitionCollection[i]); 1050 parsedSentence[parsedDefinition.key] = parsedDefinition.value; 1051 } 1052 return parsedSentence; 1053 }, 1054 1055 /** 1056 * Parse definition 1057 * Sintax: Definition -> if | on | do | to | after | helper 1058 * Example: "if: DOMContentLoaded, on: document, do:flip, to: .animatecss, after: testcallback" 1059 * @method _parseDefinition 1060 * @param {} definition 1061 * @return parsedDefinition 1062 */ 1063 parseDefinition: function(definition) { 1064 var parsedDefinition = {}, 1065 definitionBody, 1066 definitionKey, 1067 definitionValue, 1068 EVENT_KEY = 'event', 1069 EVENT_TARGET_KEY = 'eventTarget', 1070 BEHAVIOR_KEY = ['behavior', 'after', 'before', 'behaviorTarget']; 1071 1072 //Performance reasons 1073 definitionBody = definition.split(':'); 1074 if (definitionBody.length > 1) { 1075 definitionKey = definitionBody[0].trim(); 1076 //CSS3 selectors support 1077 if(definitionBody.length > 2){ 1078 definitionValue = definitionBody.slice(1); 1079 definitionValue = definitionValue.join(':'); 1080 definitionValue = definitionValue.trim(); 1081 } else { 1082 definitionValue = definitionBody[1].trim(); 1083 } 1084 parsedDefinition.value = definitionValue; 1085 //Change by reserved words 1086 if (definitionKey === EVENT_RESERVED_WORD) { 1087 definitionKey = EVENT_KEY; 1088 } else if (definitionKey === EVENT_TARGET_RESERVED_WORD) { 1089 definitionKey = EVENT_TARGET_KEY; 1090 } else { 1091 for (var i = BEHAVIOR_RESERVED_WORD.length - 1; i >= 0; i--) { 1092 if(definitionKey === BEHAVIOR_RESERVED_WORD[i]) { 1093 definitionKey = BEHAVIOR_KEY[i]; 1094 /** 1095 * TODO: This code is deprecated for next version 1096 */ 1097 if((definitionKey === 'after' || definitionKey === 'before') && definitionValue[0]!== '$') { 1098 definitionValue = '$' + definitionValue; 1099 } 1100 definitionValue = this.parseDoDefinition(definitionValue); 1101 } 1102 } 1103 } 1104 parsedDefinition.key = definitionKey; 1105 parsedDefinition.value = definitionValue; 1106 } 1107 1108 return parsedDefinition; 1109 }, 1110 1111 /** 1112 * Allow to parse do definitions 1113 * @author Dariel Noel <[email protected]>
1114 * @since 2014-09-03 1115 * @param {[type]} doDefinition [description] 1116 */ 1117 parseDoDefinition: function(doDefinition) { 1118 var reg = /^\$(\w+)\s*/g, 1119 regMatch = reg.exec(doDefinition), 1120 functionName = "", 1121 parametersArray = [], it = 1; 1122 1123 if(regMatch !== null) { 1124 functionName = regMatch[1]; 1125 doDefinitionArray = doDefinition.split(regMatch[0])[1]; 1126 doDefinitionArray = doDefinitionArray !== null ? doDefinitionArray.split(PARAMS_SEPARATOR) : []; 1127 doDefinition = []; 1128 doDefinition[0] = functionName; 1129 for (var i = 0; i < doDefinitionArray.length; i++) { 1130 if(doDefinitionArray[i] !== "") 1131 doDefinition[it++] = doDefinitionArray[i].trim(); 1132 } 1133 return doDefinition; 1134 } 1135 return doDefinition; 1136 } 1137 }; 1138 1139 /** 1140 * Helper to DOM manipulation 1141 * @class Parser 1142 * @author @dariel_noel 1143 */ 1144 selfish.NodeHelper = { 1145 1146 /** 1147 * Add some classes to a node 1148 * @method addClass 1149 * @param {} elem 1150 * @param {} string 1151 * @return 1152 */ 1153 addClass: function(elem, string) { 1154 if (!(string instanceof Array)) { 1155 string = string.split(' '); 1156 } 1157 for (var i = 0, len = string.length; i < len; ++i) { 1158 if (string[i] && !new RegExp(REGEX_BEGIN + string[i] + REGEX_END).test(elem.className)) { 1159 elem.className = (elem.className === "") ? string[i] : elem.className.trim() + ' ' + string[i]; 1160 } 1161 } 1162 }, 1163 1164 /** 1165 * Remove class of some DOM element 1166 * @method removeClass 1167 * @param {} elem 1168 * @param {} string 1169 * @return 1170 */ 1171 removeClass: function(elem, string) { 1172 if (!(string instanceof Array)) { 1173 string = string.split(' '); 1174 } 1175 for (var i = 0, len = string.length; i < len; ++i) { 1176 elem.className = elem.className.replace(new RegExp(REGEX_BEGIN + string[i] + REGEX_END), ' ').trim(); 1177 } 1178 }, 1179 1180 /** 1181 * Test if the nested element has the supply class 1182 * @method hasClass 1183 * @param {} elem 1184 * @param {} string 1185 * @return LogicalExpression 1186 */ 1187 hasClass: function(elem, string) { 1188 return string && new RegExp(REGEX_BEGIN + string + REGEX_END).test(elem.className); 1189 } 1190 }; 1191 1192 /** 1193 * A kind of util functions 1194 * @class Util 1195 * @author @dariel_noel 1196 */ 1197 selfish.Util = { 1198 1199 /** 1200 * Thanks a lot to underscore guys 1201 * @method isFunction 1202 * @param {} obj 1203 * @return UnaryExpression 1204 */ 1205 isFunction: function(obj) { 1206 return !!(obj && obj.constructor && obj.call && obj.apply); 1207 }, 1208 /** 1209 * Test if some object is an array 1210 * @author Dariel Noel <[email protected]> 1211 * @since 2014-09-09 1212 * @return {Boolean} [description] 1213 */ 1214 beArray:function(object){ 1215 return Array.isArray(object); 1216 } 1217 }; 1218
1219 ///////////////////////////////////////////////////////// 1220 // Public SubModules 1221 ///////////////////////////////////////////////////////// 1222 1223 /** 1224 * Event System Interface (AniJS Current Implementation) 1225 * @class EventSystem 1226 * @author @dariel_noel 1227 */ 1228 AniJS.EventSystem = { 1229 1230 //ATTRS 1231 eventCollection: {}, 1232 1233 eventIdCounter: 0, 1234 1235 /** 1236 * Return true if the element it's an event target object 1237 * @method isEventTarget 1238 * @param {} element 1239 * @return true or false 1240 */ 1241 isEventTarget: function(element) { 1242 return (element.addEventListener) ? 1 : 0; 1243 }, 1244 1245 /** 1246 * Create new EventTarget element 1247 * @method createEventTarget 1248 * @return AniJS.EventTarget 1249 */ 1250 createEventTarget: function() { 1251 return new AniJS.EventTarget(); 1252 }, 1253 1254 /** 1255 * Put a listener in the object 1256 * @method addEventListenerHelper 1257 * @param {} eventTargetItem 1258 * @param {} event 1259 * @param {} listener 1260 * @param {} other 1261 * @return 1262 */ 1263 addEventListenerHelper: function(eventTargetItem, event, listener, other) { 1264 eventTargetItem.addEventListener(event, listener, false); 1265 }, 1266 1267 /** 1268 * Put a listener of the object 1269 * @method removeEventListenerHelper 1270 * @param {} e 1271 * @param {} arguments 1272 * @return 1273 */ 1274 removeEventListenerHelper: function(element, type, listener) { 1275 if(element){ 1276 element.removeEventListener(type, listener); 1277 } 1278 }, 1279 1280 1281 /** 1282 * Purge all register elements handle 1283 * @method purgeAll 1284 * @return 1285 */ 1286 purgeAll: function() { 1287 var instance = this, 1288 eventCollection = instance.eventCollection, 1289 eventCollectionKeyList = Object.keys(eventCollection), 1290 size = eventCollectionKeyList.length, 1291 i = 0, 1292 key, 1293 eventObject; 1294 1295 for (i; i < size; i++) { 1296 key = eventCollectionKeyList[i]; 1297 eventObject = eventCollection[key]; 1298 1299 if (eventObject && eventObject.handleCollection && eventObject.handleCollection.length > 0) { 1300 instance.purgeEventTarget(eventObject.handleCollection[0].element); 1301 } 1302 1303 delete eventCollection[key]; 1304 } 1305 }, 1306 1307 /** 1308 * Detach all AniJS subscriptions to the all nodes bellow this 1309 * @method purgeAllNodes 1310 * @param {} element 1311 * @return 1312 */ 1313 purgeAllNodes: function(element){ 1314 //Dame todos los que tengan data-anijs 1315 //a cada uno purgue 1316 var nodes = element.querySelectorAll("*"); 1317 size = nodes.length; 1318 for (var i = size - 1; i >= 0; i--) { 1319 this.purgeEventTarget(nodes[i]); 1320 } 1321 }, 1322 1323 /** 1324 * Detach all AniJS subscriptions to this element 1325 * @method purgeEventTarget 1326 * @param {} element 1327 * @return 1328 */ 1329 purgeEventTarget: function(element) { 1330 var instance = this, 1331 aniJSEventID = element._aniJSEventID, 1332 elementHandleCollection; 1333 if (aniJSEventID) { 1334 1335 //Se le quitan todos los eventos a los que este suscrito 1336 elementHandleCollection = instance.eventCollection[aniJSEventID].handleCollection; 1337 1338 var size = elementHandleCollection.length, 1339 i = 0, 1340 item; 1341 1342 for (i; i < size; i++) { 1343 item = elementHandleCollection[i]; 1344 1345 //Para cada handle 1346 instance.removeEventListenerHelper(element, item.eventType, item.listener); 1347 1348 } 1349 instance.eventCollection[aniJSEventID] = element._aniJSEventID = null; 1350 delete instance.eventCollection[aniJSEventID]; 1351 delete element._aniJSEventID; 1352 } 1353 }, 1354 1355 /** 1356 * Create a handle to remove the listener when purge it 1357 * @method registerEventHandle 1358 * @param {} element 1359 * @param {} eventType 1360 * @param {} listener 1361 * @return 1362 */ 1363 registerEventHandle: function(element, eventType, listener) { 1364 var instance = this, 1365 aniJSEventID = element._aniJSEventID, 1366 eventCollection = instance.eventCollection, 1367 elementEventHandle = { 1368 eventType: eventType, 1369 listener: listener, 1370 element: element 1371 }; 1372 1373 if (aniJSEventID) { 1374 eventCollection[aniJSEventID].handleCollection.push(elementEventHandle); 1375 } else { 1376 var tempEventHandle = { 1377 handleCollection: [elementEventHandle] 1378 }; 1379 1380 eventCollection[++instance.eventIdCounter] = tempEventHandle; 1381 element._aniJSEventID = instance.eventIdCounter; 1382 } 1383 } 1384 1385 }; 1386 1387 1388 /** 1389 * Helper the custom EventTarget 1390 * Copyright (c) 2010 Nicholas C. Zakas. All rights reserved. 1391 * MIT License 1392 * http://www.nczonline.net/blog/2010/03/09/custom-events-in-javascript/ 1393 * @class EventTarget 1394 */ 1395 AniJS.EventTarget = function EventTarget() { 1396 this._listeners = {}; 1397 }; 1398 1399 AniJS.EventTarget.prototype = { 1400 1401 constructor: AniJS.EventTarget, 1402 1403 /** 1404 * Registers the specified listener on the EventTarget it's called on 1405 * Similar to the native implementation 1406 * @method addEventListener 1407 * @param {} type 1408 * @param {} listener 1409 * @param {} other 1410 * @return 1411 */ 1412 addEventListener: function(type, listener, other) { 1413 var instance = this; 1414 if (typeof instance._listeners[type] == "undefined") { 1415 instance._listeners[type] = []; 1416 } 1417 1418 instance._listeners[type].push(listener); 1419 }, 1420 1421 /**
1422 * Dispatches an Event at the specified EventTarget 1423 * Similar to the native implementation 1424 * @method dispatchEvent 1425 * @param {} event 1426 * @return 1427 */ 1428 dispatchEvent: function(event) { 1429 var instance = this; 1430 if (typeof event == "string") { 1431 event = { 1432 type: event 1433 }; 1434 } 1435 if (!event.target) { 1436 event.target = instance; 1437 } 1438 1439 if (!event.type) { //falsy 1440 throw new Error("Event object missing 'type' property."); 1441 } 1442 1443 if (this._listeners[event.type] instanceof Array) { 1444 var listeners = instance._listeners[event.type]; 1445 1446 for (var i = 0, len = listeners.length; i < len; i++) { 1447 listeners[i].call(instance, event); 1448 } 1449 } 1450 }, 1451 1452 /** 1453 * Removes the event listener previously registered with EventTarget.addEventListener. 1454 * Similar to the native implementation 1455 * @method removeEventListener 1456 * @param {} type 1457 * @param {} listener 1458 * @return 1459 */ 1460 removeEventListener: function(type, listener) { 1461 var instance = this; 1462 if (instance._listeners[type] instanceof Array) { 1463 var listeners = instance._listeners[type]; 1464 for (var i = 0, len = listeners.length; i < len; i++) { 1465 if (listeners[i] === listener) { 1466 listeners.splice(i, 1); 1467 break; 1468 } 1469 } 1470 } 1471 } 1472 }; 1473 1474 return AniJS; 1475 1476 }(AniJS || {})); 1477 1478 AniJS.init(); 1479 AniJS.run(); 1480 1481 // https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon 1482 // AMD Support 1483 if (typeof define === "function" && define.amd) { 1484 define("anijs", [], function() { 1485 return AniJS; 1486 }); 1487 } 1488 if (typeof noGlobal == typeof undefined) { 1489 window.AniJS = AniJS; 1490 } 1491 1492 return AniJS; 1493});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.