From b077e126e58d553609fe3ab369cf8fea18f373e0 Mon Sep 17 00:00:00 2001 From: Igor Zhukov Date: Wed, 22 Jan 2014 14:09:49 +0400 Subject: [PATCH] Migrate to angular 1.2.9, fixed slow images load --- app/index.html | 13 +- app/js/lib/mtproto.js | 5 +- app/vendor/angular/angular-animate.js | 367 +++- app/vendor/angular/angular-animate.min.js | 37 +- app/vendor/angular/angular-animate.min.js.map | 6 +- app/vendor/angular/angular-cookies.js | 2 +- app/vendor/angular/angular-cookies.min.js | 2 +- app/vendor/angular/angular-csp.css | 11 - app/vendor/angular/angular-loader.js | 4 +- app/vendor/angular/angular-loader.min.js | 4 +- app/vendor/angular/angular-resource.js | 60 +- app/vendor/angular/angular-resource.min.js | 15 +- .../angular/angular-resource.min.js.map | 6 +- app/vendor/angular/angular-route.js | 95 +- app/vendor/angular/angular-route.min.js | 18 +- app/vendor/angular/angular-route.min.js.map | 4 +- app/vendor/angular/angular-sanitize.js | 31 +- app/vendor/angular/angular-sanitize.min.js | 18 +- .../angular/angular-sanitize.min.js.map | 4 +- app/vendor/angular/angular-touch.js | 2 +- app/vendor/angular/angular-touch.min.js | 2 +- app/vendor/angular/angular.js | 1480 ++++++++++------- app/vendor/angular/angular.min.js | 393 ++--- app/vendor/angular/angular.min.js.map | 6 +- app/vendor/angular/errors.json | 2 +- app/vendor/angular/version.json | 2 +- app/vendor/angular/version.txt | 2 +- test/lib/angular/angular-mocks.js | 60 +- test/lib/angular/angular-scenario.js | 1415 +++++++++------- test/lib/angular/version.txt | 2 +- update-angular.sh | 14 +- 31 files changed, 2477 insertions(+), 1605 deletions(-) diff --git a/app/index.html b/app/index.html index 7a43a856..68d65584 100644 --- a/app/index.html +++ b/app/index.html @@ -14,17 +14,14 @@
- - - - - + + + + @@ -33,7 +30,7 @@ - + diff --git a/app/js/lib/mtproto.js b/app/js/lib/mtproto.js index f6694ff4..80f391fb 100644 --- a/app/js/lib/mtproto.js +++ b/app/js/lib/mtproto.js @@ -1624,13 +1624,14 @@ factory('MtpNetworkerFactory', function (MtpDcConfigurator, MtpMessageIdGenerato }; MtpNetworker.prototype.checkLongPoll = function(force) { + var isClean = this.cleanupSent(); // dLog('Check lp', this.longPollPending, (new Date().getTime())); if (this.longPollPending && (new Date().getTime()) < this.longPollPending) { return false; } var self = this; AppConfigManager.get('dc').then(function (baseDcID) { - if (baseDcID != self.dcID && self.cleanupSent()) { + if (baseDcID != self.dcID && isClean) { // console.warn('send long-poll for guest DC is delayed', self.dcID); return; } @@ -1798,7 +1799,7 @@ factory('MtpNetworkerFactory', function (MtpDcConfigurator, MtpMessageIdGenerato } }); - self.cleanupSent(); + self.checkLongPoll(); }); }); }; diff --git a/app/vendor/angular/angular-animate.js b/app/vendor/angular/angular-animate.js index ce9a2d47..863724bc 100644 --- a/app/vendor/angular/angular-animate.js +++ b/app/vendor/angular/angular-animate.js @@ -1,5 +1,5 @@ /** - * @license AngularJS v1.2.3 + * @license AngularJS v1.2.9 * (c) 2010-2014 Google, Inc. http://angularjs.org * License: MIT */ @@ -196,7 +196,7 @@ * *
  * //!annotate="YourApp" Your AngularJS Module|Replace this or ngModule with the module that you used to define your application.
- * var ngModule = angular.module('YourApp', []);
+ * var ngModule = angular.module('YourApp', ['ngAnimate']);
  * ngModule.animation('.my-crazy-animation', function() {
  *   return {
  *     enter: function(element, done) {
@@ -205,8 +205,8 @@
  *         //this (optional) function will be called when the animation
  *         //completes or when the animation is cancelled (the cancelled
  *         //flag will be set to true if cancelled).
- *       }
- *     }
+ *       };
+ *     },
  *     leave: function(element, done) { },
  *     move: function(element, done) { },
  *
@@ -221,7 +221,7 @@
  *
  *     //animation that can be triggered after the class is removed
  *     removeClass: function(element, className, done) { }
- *   }
+ *   };
  * });
  * 
* @@ -254,6 +254,26 @@ angular.module('ngAnimate', ['ng']) * Please visit the {@link ngAnimate `ngAnimate`} module overview page learn more about how to use animations in your application. * */ + .factory('$$animateReflow', ['$window', '$timeout', function($window, $timeout) { + var requestAnimationFrame = $window.requestAnimationFrame || + $window.webkitRequestAnimationFrame || + function(fn) { + return $timeout(fn, 10, false); + }; + + var cancelAnimationFrame = $window.cancelAnimationFrame || + $window.webkitCancelAnimationFrame || + function(timer) { + return $timeout.cancel(timer); + }; + return function(fn) { + var id = requestAnimationFrame(fn); + return function() { + cancelAnimationFrame(id); + }; + }; + }]) + .config(['$provide', '$animateProvider', function($provide, $animateProvider) { var noop = angular.noop; var forEach = angular.forEach; @@ -264,6 +284,19 @@ angular.module('ngAnimate', ['ng']) var NG_ANIMATE_CLASS_NAME = 'ng-animate'; var rootAnimateState = {running: true}; + function extractElementNode(element) { + for(var i = 0; i < element.length; i++) { + var elm = element[i]; + if(elm.nodeType == ELEMENT_NODE) { + return elm; + } + } + } + + function isMatchingElement(elm1, elm2) { + return extractElementNode(elm1) == extractElementNode(elm2); + } + $provide.decorator('$animate', ['$delegate', '$injector', '$sniffer', '$rootElement', '$timeout', '$rootScope', '$document', function($delegate, $injector, $sniffer, $rootElement, $timeout, $rootScope, $document) { @@ -281,6 +314,17 @@ angular.module('ngAnimate', ['ng']) }); }); + var classNameFilter = $animateProvider.classNameFilter(); + var isAnimatableClassName = !classNameFilter + ? function() { return true; } + : function(className) { + return classNameFilter.test(className); + }; + + function async(fn) { + return $timeout(fn, 0, false); + } + function lookup(name) { if (name) { var matches = [], @@ -376,7 +420,7 @@ angular.module('ngAnimate', ['ng']) * Runs the leave animation operation and, upon completion, removes the element from the DOM. Once * the animation is started, the following CSS classes will be added for the duration of the animation: * - * Below is a breakdown of each step that occurs during enter animation: + * Below is a breakdown of each step that occurs during leave animation: * * | Animation Step | What the element class attribute looks like | * |----------------------------------------------------------------------------------------------|---------------------------------------------| @@ -562,8 +606,22 @@ angular.module('ngAnimate', ['ng']) and the onComplete callback will be fired once the animation is fully complete. */ function performAnimation(animationEvent, className, element, parentElement, afterElement, domOperation, doneCallback) { - var currentClassName = element.attr('class') || ''; - var classes = currentClassName + ' ' + className; + var currentClassName, classes, node = extractElementNode(element); + if(node) { + currentClassName = node.className; + classes = currentClassName + ' ' + className; + } + + //transcluded directives may sometimes fire an animation using only comment nodes + //best to catch this early on to prevent any animation operations from occurring + if(!node || !isAnimatableClassName(classes)) { + fireDOMOperation(); + fireBeforeCallbackAsync(); + fireAfterCallbackAsync(); + closeAnimation(); + return; + } + var animationLookup = (' ' + classes).replace(/\s+/g,'.'); if (!parentElement) { parentElement = afterElement ? afterElement.parent() : element.parent(); @@ -579,14 +637,21 @@ angular.module('ngAnimate', ['ng']) //NOTE: IE8 + IE9 should close properly (run closeAnimation()) in case a NO animation is not found. if (animationsDisabled(element, parentElement) || matches.length === 0) { fireDOMOperation(); + fireBeforeCallbackAsync(); + fireAfterCallbackAsync(); closeAnimation(); return; } var animations = []; + //only add animations if the currently running animation is not structural //or if there is no animation running at all - if(!ngAnimateState.running || !(isClassBased && ngAnimateState.structural)) { + var allowAnimations = isClassBased ? + !ngAnimateState.disabled && (!ngAnimateState.running || !ngAnimateState.structural) : + true; + + if(allowAnimations) { forEach(matches, function(animation) { //add the animation to the queue to if it is allowed to be cancelled if(!animation.allowCancel || animation.allowCancel(element, animationEvent, className)) { @@ -612,14 +677,17 @@ angular.module('ngAnimate', ['ng']) //animation do it's thing and close this one early if(animations.length === 0) { fireDOMOperation(); + fireBeforeCallbackAsync(); + fireAfterCallbackAsync(); fireDoneCallbackAsync(); return; } + var ONE_SPACE = ' '; //this value will be searched for class-based CSS className lookup. Therefore, //we prefix and suffix the current className value with spaces to avoid substring //lookups of className tokens - var futureClassName = ' ' + currentClassName + ' '; + var futureClassName = ONE_SPACE + currentClassName + ONE_SPACE; if(ngAnimateState.running) { //if an animation is currently running on the element then lets take the steps //to cancel that animation and fire any required callbacks @@ -627,12 +695,23 @@ angular.module('ngAnimate', ['ng']) cleanup(element); cancelAnimations(ngAnimateState.animations); + //in the event that the CSS is class is quickly added and removed back + //then we don't want to wait until after the reflow to add/remove the CSS + //class since both class animations may run into a race condition. + //The code below will check to see if that is occurring and will + //immediately remove the former class before the reflow so that the + //animation can snap back to the original animation smoothly + var isFullyClassBasedAnimation = isClassBased && !ngAnimateState.structural; + var isRevertingClassAnimation = isFullyClassBasedAnimation && + ngAnimateState.className == className && + animationEvent != ngAnimateState.event; + //if the class is removed during the reflow then it will revert the styles temporarily //back to the base class CSS styling causing a jump-like effect to occur. This check //here ensures that the domOperation is only performed after the reflow has commenced - if(ngAnimateState.beforeComplete) { + if(ngAnimateState.beforeComplete || isRevertingClassAnimation) { (ngAnimateState.done || noop)(true); - } else if(isClassBased && !ngAnimateState.structural) { + } else if(isFullyClassBasedAnimation) { //class-based animations will compare element className values after cancelling the //previous animation to see if the element properties already contain the final CSS //class and if so then the animation will be skipped. Since the domOperation will @@ -640,8 +719,8 @@ angular.module('ngAnimate', ['ng']) //will be invalid. Therefore the same string manipulation that would occur within the //DOM operation will be performed below so that the class comparison is valid... futureClassName = ngAnimateState.event == 'removeClass' ? - futureClassName.replace(ngAnimateState.className, '') : - futureClassName + ngAnimateState.className + ' '; + futureClassName.replace(ONE_SPACE + ngAnimateState.className + ONE_SPACE, ONE_SPACE) : + futureClassName + ngAnimateState.className + ONE_SPACE; } } @@ -649,10 +728,12 @@ angular.module('ngAnimate', ['ng']) //(on addClass) or doesn't contain (on removeClass) the className being animated. //The reason why this is being called after the previous animations are cancelled //is so that the CSS classes present on the element can be properly examined. - var classNameToken = ' ' + className + ' '; + var classNameToken = ONE_SPACE + className + ONE_SPACE; if((animationEvent == 'addClass' && futureClassName.indexOf(classNameToken) >= 0) || (animationEvent == 'removeClass' && futureClassName.indexOf(classNameToken) == -1)) { fireDOMOperation(); + fireBeforeCallbackAsync(); + fireAfterCallbackAsync(); fireDoneCallbackAsync(); return; } @@ -693,6 +774,10 @@ angular.module('ngAnimate', ['ng']) } function invokeRegisteredAnimationFns(animations, phase, allAnimationFnsComplete) { + phase == 'after' ? + fireAfterCallbackAsync() : + fireBeforeCallbackAsync(); + var endFnName = phase + 'End'; forEach(animations, function(animation, index) { var animationPhaseCompleted = function() { @@ -729,8 +814,27 @@ angular.module('ngAnimate', ['ng']) } } + function fireDOMCallback(animationPhase) { + element.triggerHandler('$animate:' + animationPhase, { + event : animationEvent, + className : className + }); + } + + function fireBeforeCallbackAsync() { + async(function() { + fireDOMCallback('before'); + }); + } + + function fireAfterCallbackAsync() { + async(function() { + fireDOMCallback('after'); + }); + } + function fireDoneCallbackAsync() { - doneCallback && $timeout(doneCallback, 0, false); + doneCallback && async(doneCallback); } //it is less complicated to use a flag than managing and cancelling @@ -754,9 +858,9 @@ angular.module('ngAnimate', ['ng']) if(isClassBased) { cleanup(element); } else { - data.closeAnimationTimeout = $timeout(function() { + data.closeAnimationTimeout = async(function() { cleanup(element); - }, 0, false); + }); element.data(NG_ANIMATE_STATE, data); } } @@ -766,11 +870,7 @@ angular.module('ngAnimate', ['ng']) } function cancelChildAnimations(element) { - var node = element[0]; - if(node.nodeType != ELEMENT_NODE) { - return; - } - + var node = extractElementNode(element); forEach(node.querySelectorAll('.' + NG_ANIMATE_CLASS_NAME), function(element) { element = angular.element(element); var data = element.data(NG_ANIMATE_STATE); @@ -784,17 +884,17 @@ angular.module('ngAnimate', ['ng']) function cancelAnimations(animations) { var isCancelledFlag = true; forEach(animations, function(animation) { - if(!animations.beforeComplete) { + if(!animation.beforeComplete) { (animation.beforeEnd || noop)(isCancelledFlag); } - if(!animations.afterComplete) { + if(!animation.afterComplete) { (animation.afterEnd || noop)(isCancelledFlag); } }); } function cleanup(element) { - if(element[0] == $rootElement[0]) { + if(isMatchingElement(element, $rootElement)) { if(!rootAnimateState.disabled) { rootAnimateState.running = false; rootAnimateState.structural = false; @@ -808,7 +908,7 @@ angular.module('ngAnimate', ['ng']) function animationsDisabled(element, parentElement) { if (rootAnimateState.disabled) return true; - if(element[0] == $rootElement[0]) { + if(isMatchingElement(element, $rootElement)) { return rootAnimateState.disabled || rootAnimateState.running; } @@ -818,7 +918,7 @@ angular.module('ngAnimate', ['ng']) //any animations on it if(parentElement.length === 0) break; - var isRoot = parentElement[0] == $rootElement[0]; + var isRoot = isMatchingElement(parentElement, $rootElement); var state = isRoot ? rootAnimateState : parentElement.data(NG_ANIMATE_STATE); var result = state && (!!state.disabled || !!state.running); if(isRoot || result) { @@ -833,7 +933,8 @@ angular.module('ngAnimate', ['ng']) } }]); - $animateProvider.register('', ['$window', '$sniffer', '$timeout', function($window, $sniffer, $timeout) { + $animateProvider.register('', ['$window', '$sniffer', '$timeout', '$$animateReflow', + function($window, $sniffer, $timeout, $$animateReflow) { // Detect proper transitionend/animationend event names. var CSS_PREFIX = '', TRANSITION_PROP, TRANSITIONEND_EVENT, ANIMATION_PROP, ANIMATIONEND_EVENT; @@ -869,24 +970,77 @@ angular.module('ngAnimate', ['ng']) var ANIMATION_ITERATION_COUNT_KEY = 'IterationCount'; var NG_ANIMATE_PARENT_KEY = '$$ngAnimateKey'; var NG_ANIMATE_CSS_DATA_KEY = '$$ngAnimateCSS3Data'; - var NG_ANIMATE_FALLBACK_CLASS_NAME = 'ng-animate-start'; - var NG_ANIMATE_FALLBACK_ACTIVE_CLASS_NAME = 'ng-animate-active'; + var ELAPSED_TIME_MAX_DECIMAL_PLACES = 3; + var CLOSING_TIME_BUFFER = 1.5; + var ONE_SECOND = 1000; + var animationCounter = 0; var lookupCache = {}; var parentCounter = 0; + var animationReflowQueue = []; + var animationElementQueue = []; + var cancelAnimationReflow; + var closingAnimationTime = 0; + var timeOut = false; + function afterReflow(element, callback) { + if(cancelAnimationReflow) { + cancelAnimationReflow(); + } - var animationReflowQueue = [], animationTimer, timeOut = false; - function afterReflow(callback) { animationReflowQueue.push(callback); - $timeout.cancel(animationTimer); - animationTimer = $timeout(function() { + + var node = extractElementNode(element); + element = angular.element(node); + animationElementQueue.push(element); + + var elementData = element.data(NG_ANIMATE_CSS_DATA_KEY); + + var stagger = elementData.stagger; + var staggerTime = elementData.itemIndex * (Math.max(stagger.animationDelay, stagger.transitionDelay) || 0); + + var animationTime = (elementData.maxDelay + elementData.maxDuration) * CLOSING_TIME_BUFFER; + closingAnimationTime = Math.max(closingAnimationTime, (staggerTime + animationTime) * ONE_SECOND); + + //by placing a counter we can avoid an accidental + //race condition which may close an animation when + //a follow-up animation is midway in its animation + elementData.animationCount = animationCounter; + + cancelAnimationReflow = $$animateReflow(function() { forEach(animationReflowQueue, function(fn) { fn(); }); + + //copy the list of elements so that successive + //animations won't conflict if they're added before + //the closing animation timeout has run + var elementQueueSnapshot = []; + var animationCounterSnapshot = animationCounter; + forEach(animationElementQueue, function(elm) { + elementQueueSnapshot.push(elm); + }); + + $timeout(function() { + closeAllAnimations(elementQueueSnapshot, animationCounterSnapshot); + elementQueueSnapshot = null; + }, closingAnimationTime, false); + animationReflowQueue = []; - animationTimer = null; + animationElementQueue = []; + cancelAnimationReflow = null; lookupCache = {}; - }, 10, false); + closingAnimationTime = 0; + animationCounter++; + }); + } + + function closeAllAnimations(elements, count) { + forEach(elements, function(element) { + var elementData = element.data(NG_ANIMATE_CSS_DATA_KEY); + if(elementData && elementData.animationCount == count) { + (elementData.closeAnimationFn || noop)(); + } + }); } function getElementAnimationDetails(element, cacheKey) { @@ -965,16 +1119,16 @@ angular.module('ngAnimate', ['ng']) parentElement.data(NG_ANIMATE_PARENT_KEY, ++parentCounter); parentID = parentCounter; } - return parentID + '-' + element[0].className; + return parentID + '-' + extractElementNode(element).className; } - function animateSetup(element, className) { + function animateSetup(element, className, calculationDecorator) { var cacheKey = getCacheKey(element); var eventCacheKey = cacheKey + ' ' + className; var stagger = {}; - var ii = lookupCache[eventCacheKey] ? ++lookupCache[eventCacheKey].total : 0; + var itemIndex = lookupCache[eventCacheKey] ? ++lookupCache[eventCacheKey].total : 0; - if(ii > 0) { + if(itemIndex > 0) { var staggerClassName = className + '-stagger'; var staggerCacheKey = cacheKey + ' ' + staggerClassName; var applyClasses = !lookupCache[staggerCacheKey]; @@ -986,31 +1140,34 @@ angular.module('ngAnimate', ['ng']) applyClasses && element.removeClass(staggerClassName); } + /* the animation itself may need to add/remove special CSS classes + * before calculating the anmation styles */ + calculationDecorator = calculationDecorator || + function(fn) { return fn(); }; + element.addClass(className); - var timings = getElementAnimationDetails(element, eventCacheKey); + var timings = calculationDecorator(function() { + return getElementAnimationDetails(element, eventCacheKey); + }); /* there is no point in performing a reflow if the animation timeout is empty (this would cause a flicker bug normally in the page. There is also no point in performing an animation that only has a delay and no duration */ + var maxDelay = Math.max(timings.transitionDelay, timings.animationDelay); var maxDuration = Math.max(timings.transitionDuration, timings.animationDuration); if(maxDuration === 0) { element.removeClass(className); return false; } - var node = element[0]; //temporarily disable the transition so that the enter styles //don't animate twice (this is here to avoid a bug in Chrome/FF). var activeClassName = ''; - if(timings.transitionDuration > 0) { - element.addClass(NG_ANIMATE_FALLBACK_CLASS_NAME); - activeClassName += NG_ANIMATE_FALLBACK_ACTIVE_CLASS_NAME + ' '; - blockTransitions(element); - } else { + timings.transitionDuration > 0 ? + blockTransitions(element) : blockKeyframeAnimations(element); - } forEach(className.split(' '), function(klass, i) { activeClassName += (i > 0 ? ' ' : '') + klass + '-active'; @@ -1020,110 +1177,121 @@ angular.module('ngAnimate', ['ng']) className : className, activeClassName : activeClassName, maxDuration : maxDuration, + maxDelay : maxDelay, classes : className + ' ' + activeClassName, timings : timings, stagger : stagger, - ii : ii + itemIndex : itemIndex }); return true; } function blockTransitions(element) { - element[0].style[TRANSITION_PROP + PROPERTY_KEY] = 'none'; + extractElementNode(element).style[TRANSITION_PROP + PROPERTY_KEY] = 'none'; } function blockKeyframeAnimations(element) { - element[0].style[ANIMATION_PROP] = 'none 0s'; + extractElementNode(element).style[ANIMATION_PROP] = 'none 0s'; } function unblockTransitions(element) { - var node = element[0], prop = TRANSITION_PROP + PROPERTY_KEY; + var prop = TRANSITION_PROP + PROPERTY_KEY; + var node = extractElementNode(element); if(node.style[prop] && node.style[prop].length > 0) { node.style[prop] = ''; } } function unblockKeyframeAnimations(element) { - var node = element[0], prop = ANIMATION_PROP; + var prop = ANIMATION_PROP; + var node = extractElementNode(element); if(node.style[prop] && node.style[prop].length > 0) { - element[0].style[prop] = ''; + node.style[prop] = ''; } } function animateRun(element, className, activeAnimationComplete) { - var data = element.data(NG_ANIMATE_CSS_DATA_KEY); - if(!element.hasClass(className) || !data) { + var elementData = element.data(NG_ANIMATE_CSS_DATA_KEY); + var node = extractElementNode(element); + if(node.className.indexOf(className) == -1 || !elementData) { activeAnimationComplete(); return; } - var node = element[0]; - var timings = data.timings; - var stagger = data.stagger; - var maxDuration = data.maxDuration; - var activeClassName = data.activeClassName; - var maxDelayTime = Math.max(timings.transitionDelay, timings.animationDelay) * 1000; + var timings = elementData.timings; + var stagger = elementData.stagger; + var maxDuration = elementData.maxDuration; + var activeClassName = elementData.activeClassName; + var maxDelayTime = Math.max(timings.transitionDelay, timings.animationDelay) * ONE_SECOND; var startTime = Date.now(); var css3AnimationEvents = ANIMATIONEND_EVENT + ' ' + TRANSITIONEND_EVENT; - var ii = data.ii; + var itemIndex = elementData.itemIndex; - var applyFallbackStyle, style = '', appliedStyles = []; + var style = '', appliedStyles = []; if(timings.transitionDuration > 0) { var propertyStyle = timings.transitionPropertyStyle; if(propertyStyle.indexOf('all') == -1) { - applyFallbackStyle = true; - var fallbackProperty = $sniffer.msie ? '-ms-zoom' : 'border-spacing'; - style += CSS_PREFIX + 'transition-property: ' + propertyStyle + ', ' + fallbackProperty + '; '; - style += CSS_PREFIX + 'transition-duration: ' + timings.transitionDurationStyle + ', ' + timings.transitionDuration + 's; '; + style += CSS_PREFIX + 'transition-property: ' + propertyStyle + ';'; + style += CSS_PREFIX + 'transition-duration: ' + timings.transitionDurationStyle + ';'; appliedStyles.push(CSS_PREFIX + 'transition-property'); appliedStyles.push(CSS_PREFIX + 'transition-duration'); } } - if(ii > 0) { + if(itemIndex > 0) { if(stagger.transitionDelay > 0 && stagger.transitionDuration === 0) { var delayStyle = timings.transitionDelayStyle; - if(applyFallbackStyle) { - delayStyle += ', ' + timings.transitionDelay + 's'; - } - style += CSS_PREFIX + 'transition-delay: ' + - prepareStaggerDelay(delayStyle, stagger.transitionDelay, ii) + '; '; + prepareStaggerDelay(delayStyle, stagger.transitionDelay, itemIndex) + '; '; appliedStyles.push(CSS_PREFIX + 'transition-delay'); } if(stagger.animationDelay > 0 && stagger.animationDuration === 0) { style += CSS_PREFIX + 'animation-delay: ' + - prepareStaggerDelay(timings.animationDelayStyle, stagger.animationDelay, ii) + '; '; + prepareStaggerDelay(timings.animationDelayStyle, stagger.animationDelay, itemIndex) + '; '; appliedStyles.push(CSS_PREFIX + 'animation-delay'); } } if(appliedStyles.length > 0) { + //the element being animated may sometimes contain comment nodes in + //the jqLite object, so we're safe to use a single variable to house + //the styles since there is always only one element being animated var oldStyle = node.getAttribute('style') || ''; node.setAttribute('style', oldStyle + ' ' + style); } element.on(css3AnimationEvents, onAnimationProgress); element.addClass(activeClassName); + elementData.closeAnimationFn = function() { + onEnd(); + activeAnimationComplete(); + }; + return onEnd; // This will automatically be called by $animate so // there is no need to attach this internally to the // timeout done method. - return function onEnd(cancelled) { + function onEnd(cancelled) { element.off(css3AnimationEvents, onAnimationProgress); element.removeClass(activeClassName); animateClose(element, className); + var node = extractElementNode(element); for (var i in appliedStyles) { node.style.removeProperty(appliedStyles[i]); } - }; + } function onAnimationProgress(event) { event.stopPropagation(); var ev = event.originalEvent || event; var timeStamp = ev.$manualTimeStamp || ev.timeStamp || Date.now(); + + /* Firefox (or possibly just Gecko) likes to not round values up + * when a ms measurement is used for the animation */ + var elapsedTime = parseFloat(ev.elapsedTime.toFixed(ELAPSED_TIME_MAX_DECIMAL_PLACES)); + /* $manualTimeStamp is a mocked timeStamp value which is set * within browserTrigger(). This is only here so that tests can * mock animations properly. Real events fallback to event.timeStamp, @@ -1131,7 +1299,7 @@ angular.module('ngAnimate', ['ng']) * We're checking to see if the timeStamp surpasses the expected delay, * but we're using elapsedTime instead of the timeStamp on the 2nd * pre-condition since animations sometimes close off early */ - if(Math.max(timeStamp - startTime, 0) >= maxDelayTime && ev.elapsedTime >= maxDuration) { + if(Math.max(timeStamp - startTime, 0) >= maxDelayTime && elapsedTime >= maxDuration) { activeAnimationComplete(); } } @@ -1146,8 +1314,8 @@ angular.module('ngAnimate', ['ng']) return style; } - function animateBefore(element, className) { - if(animateSetup(element, className)) { + function animateBefore(element, className, calculationDecorator) { + if(animateSetup(element, className, calculationDecorator)) { return function(cancelled) { cancelled && animateClose(element, className); }; @@ -1179,7 +1347,7 @@ angular.module('ngAnimate', ['ng']) //data from the element which will not make the 2nd animation //happen in the first place var cancel = preReflowCancellation; - afterReflow(function() { + afterReflow(element, function() { unblockTransitions(element); unblockKeyframeAnimations(element); //once the reflow is complete then we point cancel to @@ -1195,7 +1363,6 @@ angular.module('ngAnimate', ['ng']) function animateClose(element, className) { element.removeClass(className); - element.removeClass(NG_ANIMATE_FALLBACK_CLASS_NAME); element.removeData(NG_ANIMATE_CSS_DATA_KEY); } @@ -1209,12 +1376,12 @@ angular.module('ngAnimate', ['ng']) } var parentElement = element.parent(); - var clone = angular.element(element[0].cloneNode()); + var clone = angular.element(extractElementNode(element).cloneNode()); //make the element super hidden and override any CSS style values clone.attr('style','position:absolute; top:-9999px; left:-9999px'); clone.removeAttr('id'); - clone.html(''); + clone.empty(); forEach(oldClasses.split(' '), function(klass) { clone.removeClass(klass); @@ -1243,9 +1410,20 @@ angular.module('ngAnimate', ['ng']) }, beforeAddClass : function(element, className, animationCompleted) { - var cancellationMethod = animateBefore(element, suffixClasses(className, '-add')); + var cancellationMethod = animateBefore(element, suffixClasses(className, '-add'), function(fn) { + + /* when a CSS class is added to an element then the transition style that + * is applied is the transition defined on the element when the CSS class + * is added at the time of the animation. This is how CSS3 functions + * outside of ngAnimate. */ + element.addClass(className); + var timings = fn(); + element.removeClass(className); + return timings; + }); + if(cancellationMethod) { - afterReflow(function() { + afterReflow(element, function() { unblockTransitions(element); unblockKeyframeAnimations(element); animationCompleted(); @@ -1260,9 +1438,20 @@ angular.module('ngAnimate', ['ng']) }, beforeRemoveClass : function(element, className, animationCompleted) { - var cancellationMethod = animateBefore(element, suffixClasses(className, '-remove')); + var cancellationMethod = animateBefore(element, suffixClasses(className, '-remove'), function(fn) { + /* when classes are removed from an element then the transition style + * that is applied is the transition defined on the element without the + * CSS class being there. This is how CSS3 functions outside of ngAnimate. + * http://plnkr.co/edit/j8OzgTNxHTb4n3zLyjGW?p=preview */ + var klass = element.attr('class'); + element.removeClass(className); + var timings = fn(); + element.attr('class', klass); + return timings; + }); + if(cancellationMethod) { - afterReflow(function() { + afterReflow(element, function() { unblockTransitions(element); unblockKeyframeAnimations(element); animationCompleted(); diff --git a/app/vendor/angular/angular-animate.min.js b/app/vendor/angular/angular-animate.min.js index 796fbbb2..4d909639 100644 --- a/app/vendor/angular/angular-animate.min.js +++ b/app/vendor/angular/angular-animate.min.js @@ -1,22 +1,25 @@ /* - AngularJS v1.2.3 + AngularJS v1.2.9 (c) 2010-2014 Google, Inc. http://angularjs.org License: MIT */ -(function(C,k,F){'use strict';k.module("ngAnimate",["ng"]).config(["$provide","$animateProvider",function(M,G){var p=k.noop,r=k.forEach,N=G.$$selectors,T=1,h="$$ngAnimateState",H="ng-animate",l={running:!0};M.decorator("$animate",["$delegate","$injector","$sniffer","$rootElement","$timeout","$rootScope","$document",function(v,C,I,g,s,q,F){function O(a){if(a){var d=[],c={};a=a.substr(1).split(".");(I.transitions||I.animations)&&a.push("");for(var e=0;e=v&&a.elapsedTime>=q&&e()}var n=a.data(y);if(a.hasClass(d)&&n){var l=a[0],g=n.timings,m=n.stagger, -q=n.maxDuration,r=n.activeClassName,v=1E3*Math.max(g.transitionDelay,g.animationDelay),w=Date.now(),t=Q+" "+P,u=n.ii,x,n="",p=[];if(0=t&&d>=n&&y()}var m=a.data(q),h=c(a);if(-1!=h.className.indexOf(e)&&m){var k=m.timings,l=m.stagger,n=m.maxDuration,r=m.activeClassName,t=Math.max(k.transitionDelay,k.animationDelay)* +aa,w=Date.now(),v=U+" "+T,u=m.itemIndex,p="",s=[];if(0 * @@ -258,6 +277,35 @@ function lookupDottedPath(obj, path) { }); }); + + * # Creating a custom 'PUT' request + * In this example we create a custom method on our resource to make a PUT request + *
+ *		var app = angular.module('app', ['ngResource', 'ngRoute']);
+ *
+ *		// Some APIs expect a PUT request in the format URL/object/ID
+ *		// Here we are creating an 'update' method 
+ *		app.factory('Notes', ['$resource', function($resource) {
+ *    return $resource('/notes/:id', null,
+ *        {
+ *            'update': { method:'PUT' }
+ *        });
+ *		}]);
+ *
+ *		// In our controller we get the ID from the URL using ngRoute and $routeParams
+ *		// We pass in $routeParams and our Notes factory along with $scope
+ *		app.controller('NotesCtrl', ['$scope', '$routeParams', 'Notes',
+                                      function($scope, $routeParams, Notes) {
+ *    // First get a note object from the factory
+ *    var note = Notes.get({ id:$routeParams.id });
+ *    $id = note.id;
+ *
+ *    // Now call update passing in the ID first then the object you are updating
+ *    Notes.update({ id:$id }, note);
+ *
+ *    // This will PUT /notes/ID with the note object in the request payload
+ *		}]);
+ * 
*/ angular.module('ngResource', ['ng']). factory('$resource', ['$http', '$q', function($http, $q) { @@ -358,7 +406,7 @@ angular.module('ngResource', ['ng']). }); // strip trailing slashes and set the url - url = url.replace(/\/+$/, ''); + url = url.replace(/\/+$/, '') || '/'; // then replace collapse `/.` if found in the last URL path segment before the query // E.g. `http://url.com/id./format?q=x` becomes `http://url.com/id.format?q=x` url = url.replace(/\/\.(?=\w+($|\?))/, '.'); @@ -398,7 +446,7 @@ angular.module('ngResource', ['ng']). } function Resource(value){ - copy(value || {}, this); + shallowClearAndCopy(value || {}, this); } forEach(actions, function(action, name) { @@ -470,7 +518,7 @@ angular.module('ngResource', ['ng']). if (data) { // Need to convert action.isArray to boolean in case it is undefined // jshint -W018 - if ( angular.isArray(data) !== (!!action.isArray) ) { + if (angular.isArray(data) !== (!!action.isArray)) { throw $resourceMinErr('badcfg', 'Error in resource configuration. Expected ' + 'response to contain an {0} but got an {1}', action.isArray?'array':'object', angular.isArray(data)?'array':'object'); @@ -482,7 +530,7 @@ angular.module('ngResource', ['ng']). value.push(new Resource(item)); }); } else { - copy(data, value); + shallowClearAndCopy(data, value); value.$promise = promise; } } diff --git a/app/vendor/angular/angular-resource.min.js b/app/vendor/angular/angular-resource.min.js index 728aee8f..77414315 100644 --- a/app/vendor/angular/angular-resource.min.js +++ b/app/vendor/angular/angular-resource.min.js @@ -1,12 +1,13 @@ /* - AngularJS v1.2.3 + AngularJS v1.2.9 (c) 2010-2014 Google, Inc. http://angularjs.org License: MIT */ -(function(H,f,z){'use strict';var u=f.$$minErr("$resource"),A=/^(\.[a-zA-Z_$][0-9a-zA-Z_$]*)+$/;f.module("ngResource",["ng"]).factory("$resource",["$http","$q",function(D,E){function n(f,h){this.template=f;this.defaults=h||{};this.urlParams={}}function v(m,h,k){function r(d,c){var e={};c=w({},h,c);s(c,function(a,c){t(a)&&(a=a());var g;if(a&&a.charAt&&"@"==a.charAt(0)){g=d;var b=a.substr(1);if(null==b||""===b||"hasOwnProperty"===b||!A.test("."+b))throw u("badmember",b);for(var b=b.split("."),f=0,h= -b.length;f @@ -809,8 +820,8 @@ ngRouteModule.directive('ngView', ngViewFactory); * @description * Emitted every time the ngView content is reloaded. */ -ngViewFactory.$inject = ['$route', '$anchorScroll', '$compile', '$controller', '$animate']; -function ngViewFactory( $route, $anchorScroll, $compile, $controller, $animate) { +ngViewFactory.$inject = ['$route', '$anchorScroll', '$animate']; +function ngViewFactory( $route, $anchorScroll, $animate) { return { restrict: 'ECA', terminal: true, @@ -840,8 +851,9 @@ function ngViewFactory( $route, $anchorScroll, $compile, $controller, var locals = $route.current && $route.current.locals, template = locals && locals.$template; - if (template) { + if (angular.isDefined(template)) { var newScope = scope.$new(); + var current = $route.current; // Note: This will also link all children of ng-view that were contained in the original // html. If that content contains controllers, ... they could pollute/change the scope. @@ -849,34 +861,18 @@ function ngViewFactory( $route, $anchorScroll, $compile, $controller, // Note: We can't remove them in the cloneAttchFn of $transclude as that // function is called before linking the content, which would apply child // directives to non existing elements. - var clone = $transclude(newScope, angular.noop); - clone.html(template); - $animate.enter(clone, null, currentElement || $element, function onNgViewEnter () { - if (angular.isDefined(autoScrollExp) - && (!autoScrollExp || scope.$eval(autoScrollExp))) { - $anchorScroll(); - } + var clone = $transclude(newScope, function(clone) { + $animate.enter(clone, null, currentElement || $element, function onNgViewEnter () { + if (angular.isDefined(autoScrollExp) + && (!autoScrollExp || scope.$eval(autoScrollExp))) { + $anchorScroll(); + } + }); + cleanupLastView(); }); - cleanupLastView(); - - var link = $compile(clone.contents()), - current = $route.current; - - currentScope = current.scope = newScope; currentElement = clone; - - if (current.controller) { - locals.$scope = currentScope; - var controller = $controller(current.controller, locals); - if (current.controllerAs) { - currentScope[current.controllerAs] = controller; - } - clone.data('$ngControllerController', controller); - clone.children().data('$ngControllerController', controller); - } - - link(currentScope); + currentScope = current.scope = newScope; currentScope.$emit('$viewContentLoaded'); currentScope.$eval(onloadExp); } else { @@ -887,5 +883,38 @@ function ngViewFactory( $route, $anchorScroll, $compile, $controller, }; } +// This directive is called during the $transclude call of the first `ngView` directive. +// It will replace and compile the content of the element with the loaded template. +// We need this directive so that the element content is already filled when +// the link function of another directive on the same element as ngView +// is called. +ngViewFillContentFactory.$inject = ['$compile', '$controller', '$route']; +function ngViewFillContentFactory($compile, $controller, $route) { + return { + restrict: 'ECA', + priority: -400, + link: function(scope, $element) { + var current = $route.current, + locals = current.locals; + + $element.html(locals.$template); + + var link = $compile($element.contents()); + + if (current.controller) { + locals.$scope = scope; + var controller = $controller(current.controller, locals); + if (current.controllerAs) { + scope[current.controllerAs] = controller; + } + $element.data('$ngControllerController', controller); + $element.children().data('$ngControllerController', controller); + } + + link(scope); + } + }; +} + })(window, window.angular); diff --git a/app/vendor/angular/angular-route.min.js b/app/vendor/angular/angular-route.min.js index 5870a6a8..5b0b5ad5 100644 --- a/app/vendor/angular/angular-route.min.js +++ b/app/vendor/angular/angular-route.min.js @@ -1,14 +1,14 @@ /* - AngularJS v1.2.3 + AngularJS v1.2.9 (c) 2010-2014 Google, Inc. http://angularjs.org License: MIT */ -(function(t,c,A){'use strict';function x(r,m,d,b,h){return{restrict:"ECA",terminal:!0,priority:400,transclude:"element",link:function(l,z,k,B,w){function v(){g&&(g.$destroy(),g=null);q&&(h.leave(q),q=null)}function u(){var a=r.current&&r.current.locals,e=a&&a.$template;if(e){var y=l.$new(),s=w(y,c.noop);s.html(e);h.enter(s,null,q||z,function(){!c.isDefined(n)||n&&!l.$eval(n)||m()});v();var e=d(s.contents()),f=r.current;g=f.scope=y;q=s;f.controller&&(a.$scope=g,a=b(f.controller,a),f.controllerAs&& -(g[f.controllerAs]=a),s.data("$ngControllerController",a),s.children().data("$ngControllerController",a));e(g);g.$emit("$viewContentLoaded");g.$eval(p)}else v()}var g,q,n=k.autoscroll,p=k.onload||"";l.$on("$routeChangeSuccess",u);u()}}}t=c.module("ngRoute",["ng"]).provider("$route",function(){function r(b,h){return c.extend(new (c.extend(function(){},{prototype:b})),h)}function m(b,c){var l=c.caseInsensitiveMatch,d={originalPath:b,regexp:b},k=d.keys=[];b=b.replace(/([().])/g,"\\$1").replace(/(\/)?:(\w+)([\?|\*])?/g, -function(b,c,h,d){b="?"===d?d:null;d="*"===d?d:null;k.push({name:h,optional:!!b});c=c||"";return""+(b?"":c)+"(?:"+(b?c:"")+(d&&"(.+?)"||"([^/]+)")+(b||"")+")"+(b||"")}).replace(/([\/$\*])/g,"\\$1");d.regexp=RegExp("^"+b+"$",l?"i":"");return d}var d={};this.when=function(b,h){d[b]=c.extend({reloadOnSearch:!0},h,b&&m(b,h));if(b){var l="/"==b[b.length-1]?b.substr(0,b.length-1):b+"/";d[l]=c.extend({redirectTo:b},m(l,h))}return this};this.otherwise=function(b){this.when(null,b);return this};this.$get= -["$rootScope","$location","$routeParams","$q","$injector","$http","$templateCache","$sce",function(b,h,l,m,k,t,w,v){function u(){var a=g(),e=p.current;if(a&&e&&a.$$route===e.$$route&&c.equals(a.pathParams,e.pathParams)&&!a.reloadOnSearch&&!n)e.params=a.params,c.copy(e.params,l),b.$broadcast("$routeUpdate",e);else if(a||e)n=!1,b.$broadcast("$routeChangeStart",a,e),(p.current=a)&&a.redirectTo&&(c.isString(a.redirectTo)?h.path(q(a.redirectTo,a.params)).search(a.params).replace():h.url(a.redirectTo(a.pathParams, -h.path(),h.search())).replace()),m.when(a).then(function(){if(a){var b=c.extend({},a.resolve),e,f;c.forEach(b,function(a,e){b[e]=c.isString(a)?k.get(a):k.invoke(a)});c.isDefined(e=a.template)?c.isFunction(e)&&(e=e(a.params)):c.isDefined(f=a.templateUrl)&&(c.isFunction(f)&&(f=f(a.params)),f=v.getTrustedResourceUrl(f),c.isDefined(f)&&(a.loadedTemplateUrl=f,e=t.get(f,{cache:w}).then(function(a){return a.data})));c.isDefined(e)&&(b.$template=e);return m.all(b)}}).then(function(d){a==p.current&&(a&&(a.locals= -d,c.copy(a.params,l)),b.$broadcast("$routeChangeSuccess",a,e))},function(c){a==p.current&&b.$broadcast("$routeChangeError",a,e,c)})}function g(){var a,b;c.forEach(d,function(d,l){var f;if(f=!b){var g=h.path();f=d.keys;var m={};if(d.regexp)if(g=d.regexp.exec(g)){for(var k=1,q=g.length;k