| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115 | /** * @license AngularJS v1.5.3 * (c) 2010-2016 Google, Inc. http://angularjs.org * License: MIT */(function(window, angular, undefined) {'use strict';/* jshint ignore:start */var noop        = angular.noop;var copy        = angular.copy;var extend      = angular.extend;var jqLite      = angular.element;var forEach     = angular.forEach;var isArray     = angular.isArray;var isString    = angular.isString;var isObject    = angular.isObject;var isUndefined = angular.isUndefined;var isDefined   = angular.isDefined;var isFunction  = angular.isFunction;var isElement   = angular.isElement;var ELEMENT_NODE = 1;var COMMENT_NODE = 8;var ADD_CLASS_SUFFIX = '-add';var REMOVE_CLASS_SUFFIX = '-remove';var EVENT_CLASS_PREFIX = 'ng-';var ACTIVE_CLASS_SUFFIX = '-active';var PREPARE_CLASS_SUFFIX = '-prepare';var NG_ANIMATE_CLASSNAME = 'ng-animate';var NG_ANIMATE_CHILDREN_DATA = '$$ngAnimateChildren';// Detect proper transitionend/animationend event names.var CSS_PREFIX = '', TRANSITION_PROP, TRANSITIONEND_EVENT, ANIMATION_PROP, ANIMATIONEND_EVENT;// If unprefixed events are not supported but webkit-prefixed are, use the latter.// Otherwise, just use W3C names, browsers not supporting them at all will just ignore them.// Note: Chrome implements `window.onwebkitanimationend` and doesn't implement `window.onanimationend`// but at the same time dispatches the `animationend` event and not `webkitAnimationEnd`.// Register both events in case `window.onanimationend` is not supported because of that,// do the same for `transitionend` as Safari is likely to exhibit similar behavior.// Also, the only modern browser that uses vendor prefixes for transitions/keyframes is webkit// therefore there is no reason to test anymore for other vendor prefixes:// http://caniuse.com/#search=transitionif (isUndefined(window.ontransitionend) && isDefined(window.onwebkittransitionend)) {  CSS_PREFIX = '-webkit-';  TRANSITION_PROP = 'WebkitTransition';  TRANSITIONEND_EVENT = 'webkitTransitionEnd transitionend';} else {  TRANSITION_PROP = 'transition';  TRANSITIONEND_EVENT = 'transitionend';}if (isUndefined(window.onanimationend) && isDefined(window.onwebkitanimationend)) {  CSS_PREFIX = '-webkit-';  ANIMATION_PROP = 'WebkitAnimation';  ANIMATIONEND_EVENT = 'webkitAnimationEnd animationend';} else {  ANIMATION_PROP = 'animation';  ANIMATIONEND_EVENT = 'animationend';}var DURATION_KEY = 'Duration';var PROPERTY_KEY = 'Property';var DELAY_KEY = 'Delay';var TIMING_KEY = 'TimingFunction';var ANIMATION_ITERATION_COUNT_KEY = 'IterationCount';var ANIMATION_PLAYSTATE_KEY = 'PlayState';var SAFE_FAST_FORWARD_DURATION_VALUE = 9999;var ANIMATION_DELAY_PROP = ANIMATION_PROP + DELAY_KEY;var ANIMATION_DURATION_PROP = ANIMATION_PROP + DURATION_KEY;var TRANSITION_DELAY_PROP = TRANSITION_PROP + DELAY_KEY;var TRANSITION_DURATION_PROP = TRANSITION_PROP + DURATION_KEY;var isPromiseLike = function(p) {  return p && p.then ? true : false;};var ngMinErr = angular.$$minErr('ng');function assertArg(arg, name, reason) {  if (!arg) {    throw ngMinErr('areq', "Argument '{0}' is {1}", (name || '?'), (reason || "required"));  }  return arg;}function mergeClasses(a,b) {  if (!a && !b) return '';  if (!a) return b;  if (!b) return a;  if (isArray(a)) a = a.join(' ');  if (isArray(b)) b = b.join(' ');  return a + ' ' + b;}function packageStyles(options) {  var styles = {};  if (options && (options.to || options.from)) {    styles.to = options.to;    styles.from = options.from;  }  return styles;}function pendClasses(classes, fix, isPrefix) {  var className = '';  classes = isArray(classes)      ? classes      : classes && isString(classes) && classes.length          ? classes.split(/\s+/)          : [];  forEach(classes, function(klass, i) {    if (klass && klass.length > 0) {      className += (i > 0) ? ' ' : '';      className += isPrefix ? fix + klass                            : klass + fix;    }  });  return className;}function removeFromArray(arr, val) {  var index = arr.indexOf(val);  if (val >= 0) {    arr.splice(index, 1);  }}function stripCommentsFromElement(element) {  if (element instanceof jqLite) {    switch (element.length) {      case 0:        return [];        break;      case 1:        // there is no point of stripping anything if the element        // is the only element within the jqLite wrapper.        // (it's important that we retain the element instance.)        if (element[0].nodeType === ELEMENT_NODE) {          return element;        }        break;      default:        return jqLite(extractElementNode(element));        break;    }  }  if (element.nodeType === ELEMENT_NODE) {    return jqLite(element);  }}function extractElementNode(element) {  if (!element[0]) return element;  for (var i = 0; i < element.length; i++) {    var elm = element[i];    if (elm.nodeType == ELEMENT_NODE) {      return elm;    }  }}function $$addClass($$jqLite, element, className) {  forEach(element, function(elm) {    $$jqLite.addClass(elm, className);  });}function $$removeClass($$jqLite, element, className) {  forEach(element, function(elm) {    $$jqLite.removeClass(elm, className);  });}function applyAnimationClassesFactory($$jqLite) {  return function(element, options) {    if (options.addClass) {      $$addClass($$jqLite, element, options.addClass);      options.addClass = null;    }    if (options.removeClass) {      $$removeClass($$jqLite, element, options.removeClass);      options.removeClass = null;    }  }}function prepareAnimationOptions(options) {  options = options || {};  if (!options.$$prepared) {    var domOperation = options.domOperation || noop;    options.domOperation = function() {      options.$$domOperationFired = true;      domOperation();      domOperation = noop;    };    options.$$prepared = true;  }  return options;}function applyAnimationStyles(element, options) {  applyAnimationFromStyles(element, options);  applyAnimationToStyles(element, options);}function applyAnimationFromStyles(element, options) {  if (options.from) {    element.css(options.from);    options.from = null;  }}function applyAnimationToStyles(element, options) {  if (options.to) {    element.css(options.to);    options.to = null;  }}function mergeAnimationDetails(element, oldAnimation, newAnimation) {  var target = oldAnimation.options || {};  var newOptions = newAnimation.options || {};  var toAdd = (target.addClass || '') + ' ' + (newOptions.addClass || '');  var toRemove = (target.removeClass || '') + ' ' + (newOptions.removeClass || '');  var classes = resolveElementClasses(element.attr('class'), toAdd, toRemove);  if (newOptions.preparationClasses) {    target.preparationClasses = concatWithSpace(newOptions.preparationClasses, target.preparationClasses);    delete newOptions.preparationClasses;  }  // noop is basically when there is no callback; otherwise something has been set  var realDomOperation = target.domOperation !== noop ? target.domOperation : null;  extend(target, newOptions);  // TODO(matsko or sreeramu): proper fix is to maintain all animation callback in array and call at last,but now only leave has the callback so no issue with this.  if (realDomOperation) {    target.domOperation = realDomOperation;  }  if (classes.addClass) {    target.addClass = classes.addClass;  } else {    target.addClass = null;  }  if (classes.removeClass) {    target.removeClass = classes.removeClass;  } else {    target.removeClass = null;  }  oldAnimation.addClass = target.addClass;  oldAnimation.removeClass = target.removeClass;  return target;}function resolveElementClasses(existing, toAdd, toRemove) {  var ADD_CLASS = 1;  var REMOVE_CLASS = -1;  var flags = {};  existing = splitClassesToLookup(existing);  toAdd = splitClassesToLookup(toAdd);  forEach(toAdd, function(value, key) {    flags[key] = ADD_CLASS;  });  toRemove = splitClassesToLookup(toRemove);  forEach(toRemove, function(value, key) {    flags[key] = flags[key] === ADD_CLASS ? null : REMOVE_CLASS;  });  var classes = {    addClass: '',    removeClass: ''  };  forEach(flags, function(val, klass) {    var prop, allow;    if (val === ADD_CLASS) {      prop = 'addClass';      allow = !existing[klass];    } else if (val === REMOVE_CLASS) {      prop = 'removeClass';      allow = existing[klass];    }    if (allow) {      if (classes[prop].length) {        classes[prop] += ' ';      }      classes[prop] += klass;    }  });  function splitClassesToLookup(classes) {    if (isString(classes)) {      classes = classes.split(' ');    }    var obj = {};    forEach(classes, function(klass) {      // sometimes the split leaves empty string values      // incase extra spaces were applied to the options      if (klass.length) {        obj[klass] = true;      }    });    return obj;  }  return classes;}function getDomNode(element) {  return (element instanceof angular.element) ? element[0] : element;}function applyGeneratedPreparationClasses(element, event, options) {  var classes = '';  if (event) {    classes = pendClasses(event, EVENT_CLASS_PREFIX, true);  }  if (options.addClass) {    classes = concatWithSpace(classes, pendClasses(options.addClass, ADD_CLASS_SUFFIX));  }  if (options.removeClass) {    classes = concatWithSpace(classes, pendClasses(options.removeClass, REMOVE_CLASS_SUFFIX));  }  if (classes.length) {    options.preparationClasses = classes;    element.addClass(classes);  }}function clearGeneratedClasses(element, options) {  if (options.preparationClasses) {    element.removeClass(options.preparationClasses);    options.preparationClasses = null;  }  if (options.activeClasses) {    element.removeClass(options.activeClasses);    options.activeClasses = null;  }}function blockTransitions(node, duration) {  // we use a negative delay value since it performs blocking  // yet it doesn't kill any existing transitions running on the  // same element which makes this safe for class-based animations  var value = duration ? '-' + duration + 's' : '';  applyInlineStyle(node, [TRANSITION_DELAY_PROP, value]);  return [TRANSITION_DELAY_PROP, value];}function blockKeyframeAnimations(node, applyBlock) {  var value = applyBlock ? 'paused' : '';  var key = ANIMATION_PROP + ANIMATION_PLAYSTATE_KEY;  applyInlineStyle(node, [key, value]);  return [key, value];}function applyInlineStyle(node, styleTuple) {  var prop = styleTuple[0];  var value = styleTuple[1];  node.style[prop] = value;}function concatWithSpace(a,b) {  if (!a) return b;  if (!b) return a;  return a + ' ' + b;}var $$rAFSchedulerFactory = ['$$rAF', function($$rAF) {  var queue, cancelFn;  function scheduler(tasks) {    // we make a copy since RAFScheduler mutates the state    // of the passed in array variable and this would be difficult    // to track down on the outside code    queue = queue.concat(tasks);    nextTick();  }  queue = scheduler.queue = [];  /* waitUntilQuiet does two things:   * 1. It will run the FINAL `fn` value only when an uncanceled RAF has passed through   * 2. It will delay the next wave of tasks from running until the quiet `fn` has run.   *   * The motivation here is that animation code can request more time from the scheduler   * before the next wave runs. This allows for certain DOM properties such as classes to   * be resolved in time for the next animation to run.   */  scheduler.waitUntilQuiet = function(fn) {    if (cancelFn) cancelFn();    cancelFn = $$rAF(function() {      cancelFn = null;      fn();      nextTick();    });  };  return scheduler;  function nextTick() {    if (!queue.length) return;    var items = queue.shift();    for (var i = 0; i < items.length; i++) {      items[i]();    }    if (!cancelFn) {      $$rAF(function() {        if (!cancelFn) nextTick();      });    }  }}];/** * @ngdoc directive * @name ngAnimateChildren * @restrict AE * @element ANY * * @description * * ngAnimateChildren allows you to specify that children of this element should animate even if any * of the children's parents are currently animating. By default, when an element has an active `enter`, `leave`, or `move` * (structural) animation, child elements that also have an active structural animation are not animated. * * Note that even if `ngAnimteChildren` is set, no child animations will run when the parent element is removed from the DOM (`leave` animation). * * * @param {string} ngAnimateChildren If the value is empty, `true` or `on`, *     then child animations are allowed. If the value is `false`, child animations are not allowed. * * @example * <example module="ngAnimateChildren" name="ngAnimateChildren" deps="angular-animate.js" animations="true">     <file name="index.html">       <div ng-controller="mainController as main">         <label>Show container? <input type="checkbox" ng-model="main.enterElement" /></label>         <label>Animate children? <input type="checkbox" ng-model="main.animateChildren" /></label>         <hr>         <div ng-animate-children="{{main.animateChildren}}">           <div ng-if="main.enterElement" class="container">             List of items:             <div ng-repeat="item in [0, 1, 2, 3]" class="item">Item {{item}}</div>           </div>         </div>       </div>     </file>     <file name="animations.css">      .container.ng-enter,      .container.ng-leave {        transition: all ease 1.5s;      }      .container.ng-enter,      .container.ng-leave-active {        opacity: 0;      }      .container.ng-leave,      .container.ng-enter-active {        opacity: 1;      }      .item {        background: firebrick;        color: #FFF;        margin-bottom: 10px;      }      .item.ng-enter,      .item.ng-leave {        transition: transform 1.5s ease;      }      .item.ng-enter {        transform: translateX(50px);      }      .item.ng-enter-active {        transform: translateX(0);      }    </file>    <file name="script.js">      angular.module('ngAnimateChildren', ['ngAnimate'])        .controller('mainController', function() {          this.animateChildren = false;          this.enterElement = false;        });    </file>  </example> */var $$AnimateChildrenDirective = ['$interpolate', function($interpolate) {  return {    link: function(scope, element, attrs) {      var val = attrs.ngAnimateChildren;      if (angular.isString(val) && val.length === 0) { //empty attribute        element.data(NG_ANIMATE_CHILDREN_DATA, true);      } else {        // Interpolate and set the value, so that it is available to        // animations that run right after compilation        setData($interpolate(val)(scope));        attrs.$observe('ngAnimateChildren', setData);      }      function setData(value) {        value = value === 'on' || value === 'true';        element.data(NG_ANIMATE_CHILDREN_DATA, value);      }    }  };}];var ANIMATE_TIMER_KEY = '$$animateCss';/** * @ngdoc service * @name $animateCss * @kind object * * @description * The `$animateCss` service is a useful utility to trigger customized CSS-based transitions/keyframes * from a JavaScript-based animation or directly from a directive. The purpose of `$animateCss` is NOT * to side-step how `$animate` and ngAnimate work, but the goal is to allow pre-existing animations or * directives to create more complex animations that can be purely driven using CSS code. * * Note that only browsers that support CSS transitions and/or keyframe animations are capable of * rendering animations triggered via `$animateCss` (bad news for IE9 and lower). * * ## Usage * Once again, `$animateCss` is designed to be used inside of a registered JavaScript animation that * is powered by ngAnimate. It is possible to use `$animateCss` directly inside of a directive, however, * any automatic control over cancelling animations and/or preventing animations from being run on * child elements will not be handled by Angular. For this to work as expected, please use `$animate` to * trigger the animation and then setup a JavaScript animation that injects `$animateCss` to trigger * the CSS animation. * * The example below shows how we can create a folding animation on an element using `ng-if`: * * ```html * <!-- notice the `fold-animation` CSS class --> * <div ng-if="onOff" class="fold-animation"> *   This element will go BOOM * </div> * <button ng-click="onOff=true">Fold In</button> * ``` * * Now we create the **JavaScript animation** that will trigger the CSS transition: * * ```js * ngModule.animation('.fold-animation', ['$animateCss', function($animateCss) { *   return { *     enter: function(element, doneFn) { *       var height = element[0].offsetHeight; *       return $animateCss(element, { *         from: { height:'0px' }, *         to: { height:height + 'px' }, *         duration: 1 // one second *       }); *     } *   } * }]); * ``` * * ## More Advanced Uses * * `$animateCss` is the underlying code that ngAnimate uses to power **CSS-based animations** behind the scenes. Therefore CSS hooks * like `.ng-EVENT`, `.ng-EVENT-active`, `.ng-EVENT-stagger` are all features that can be triggered using `$animateCss` via JavaScript code. * * This also means that just about any combination of adding classes, removing classes, setting styles, dynamically setting a keyframe animation, * applying a hardcoded duration or delay value, changing the animation easing or applying a stagger animation are all options that work with * `$animateCss`. The service itself is smart enough to figure out the combination of options and examine the element styling properties in order * to provide a working animation that will run in CSS. * * The example below showcases a more advanced version of the `.fold-animation` from the example above: * * ```js * ngModule.animation('.fold-animation', ['$animateCss', function($animateCss) { *   return { *     enter: function(element, doneFn) { *       var height = element[0].offsetHeight; *       return $animateCss(element, { *         addClass: 'red large-text pulse-twice', *         easing: 'ease-out', *         from: { height:'0px' }, *         to: { height:height + 'px' }, *         duration: 1 // one second *       }); *     } *   } * }]); * ``` * * Since we're adding/removing CSS classes then the CSS transition will also pick those up: * * ```css * /* since a hardcoded duration value of 1 was provided in the JavaScript animation code, * the CSS classes below will be transitioned despite them being defined as regular CSS classes */ * .red { background:red; } * .large-text { font-size:20px; } * * /* we can also use a keyframe animation and $animateCss will make it work alongside the transition */ * .pulse-twice { *   animation: 0.5s pulse linear 2; *   -webkit-animation: 0.5s pulse linear 2; * } * * @keyframes pulse { *   from { transform: scale(0.5); } *   to { transform: scale(1.5); } * } * * @-webkit-keyframes pulse { *   from { -webkit-transform: scale(0.5); } *   to { -webkit-transform: scale(1.5); } * } * ``` * * Given this complex combination of CSS classes, styles and options, `$animateCss` will figure everything out and make the animation happen. * * ## How the Options are handled * * `$animateCss` is very versatile and intelligent when it comes to figuring out what configurations to apply to the element to ensure the animation * works with the options provided. Say for example we were adding a class that contained a keyframe value and we wanted to also animate some inline * styles using the `from` and `to` properties. * * ```js * var animator = $animateCss(element, { *   from: { background:'red' }, *   to: { background:'blue' } * }); * animator.start(); * ``` * * ```css * .rotating-animation { *   animation:0.5s rotate linear; *   -webkit-animation:0.5s rotate linear; * } * * @keyframes rotate { *   from { transform: rotate(0deg); } *   to { transform: rotate(360deg); } * } * * @-webkit-keyframes rotate { *   from { -webkit-transform: rotate(0deg); } *   to { -webkit-transform: rotate(360deg); } * } * ``` * * The missing pieces here are that we do not have a transition set (within the CSS code nor within the `$animateCss` options) and the duration of the animation is * going to be detected from what the keyframe styles on the CSS class are. In this event, `$animateCss` will automatically create an inline transition * style matching the duration detected from the keyframe style (which is present in the CSS class that is being added) and then prepare both the transition * and keyframe animations to run in parallel on the element. Then when the animation is underway the provided `from` and `to` CSS styles will be applied * and spread across the transition and keyframe animation. * * ## What is returned * * `$animateCss` works in two stages: a preparation phase and an animation phase. Therefore when `$animateCss` is first called it will NOT actually * start the animation. All that is going on here is that the element is being prepared for the animation (which means that the generated CSS classes are * added and removed on the element). Once `$animateCss` is called it will return an object with the following properties: * * ```js * var animator = $animateCss(element, { ... }); * ``` * * Now what do the contents of our `animator` variable look like: * * ```js * { *   // starts the animation *   start: Function, * *   // ends (aborts) the animation *   end: Function * } * ``` * * To actually start the animation we need to run `animation.start()` which will then return a promise that we can hook into to detect when the animation ends. * If we choose not to run the animation then we MUST run `animation.end()` to perform a cleanup on the element (since some CSS classes and styles may have been * applied to the element during the preparation phase). Note that all other properties such as duration, delay, transitions and keyframes are just properties * and that changing them will not reconfigure the parameters of the animation. * * ### runner.done() vs runner.then() * It is documented that `animation.start()` will return a promise object and this is true, however, there is also an additional method available on the * runner called `.done(callbackFn)`. The done method works the same as `.finally(callbackFn)`, however, it does **not trigger a digest to occur**. * Therefore, for performance reasons, it's always best to use `runner.done(callback)` instead of `runner.then()`, `runner.catch()` or `runner.finally()` * unless you really need a digest to kick off afterwards. * * Keep in mind that, to make this easier, ngAnimate has tweaked the JS animations API to recognize when a runner instance is returned from $animateCss * (so there is no need to call `runner.done(doneFn)` inside of your JavaScript animation code). * Check the {@link ngAnimate.$animateCss#usage animation code above} to see how this works. * * @param {DOMElement} element the element that will be animated * @param {object} options the animation-related options that will be applied during the animation * * * `event` - The DOM event (e.g. enter, leave, move). When used, a generated CSS class of `ng-EVENT` and `ng-EVENT-active` will be applied * to the element during the animation. Multiple events can be provided when spaces are used as a separator. (Note that this will not perform any DOM operation.) * * `structural` - Indicates that the `ng-` prefix will be added to the event class. Setting to `false` or omitting will turn `ng-EVENT` and * `ng-EVENT-active` in `EVENT` and `EVENT-active`. Unused if `event` is omitted. * * `easing` - The CSS easing value that will be applied to the transition or keyframe animation (or both). * * `transitionStyle` - The raw CSS transition style that will be used (e.g. `1s linear all`). * * `keyframeStyle` - The raw CSS keyframe animation style that will be used (e.g. `1s my_animation linear`). * * `from` - The starting CSS styles (a key/value object) that will be applied at the start of the animation. * * `to` - The ending CSS styles (a key/value object) that will be applied across the animation via a CSS transition. * * `addClass` - A space separated list of CSS classes that will be added to the element and spread across the animation. * * `removeClass` - A space separated list of CSS classes that will be removed from the element and spread across the animation. * * `duration` - A number value representing the total duration of the transition and/or keyframe (note that a value of 1 is 1000ms). If a value of `0` * is provided then the animation will be skipped entirely. * * `delay` - A number value representing the total delay of the transition and/or keyframe (note that a value of 1 is 1000ms). If a value of `true` is * used then whatever delay value is detected from the CSS classes will be mirrored on the elements styles (e.g. by setting delay true then the style value * of the element will be `transition-delay: DETECTED_VALUE`). Using `true` is useful when you want the CSS classes and inline styles to all share the same * CSS delay value. * * `stagger` - A numeric time value representing the delay between successively animated elements * ({@link ngAnimate#css-staggering-animations Click here to learn how CSS-based staggering works in ngAnimate.}) * * `staggerIndex` - The numeric index representing the stagger item (e.g. a value of 5 is equal to the sixth item in the stagger; therefore when a *   `stagger` option value of `0.1` is used then there will be a stagger delay of `600ms`) * * `applyClassesEarly` - Whether or not the classes being added or removed will be used when detecting the animation. This is set by `$animate` when enter/leave/move animations are fired to ensure that the CSS classes are resolved in time. (Note that this will prevent any transitions from occurring on the classes being added and removed.) * * `cleanupStyles` - Whether or not the provided `from` and `to` styles will be removed once *    the animation is closed. This is useful for when the styles are used purely for the sake of *    the animation and do not have a lasting visual effect on the element (e.g. a collapse and open animation). *    By default this value is set to `false`. * * @return {object} an object with start and end methods and details about the animation. * * * `start` - The method to start the animation. This will return a `Promise` when called. * * `end` - This method will cancel the animation and remove all applied CSS classes and styles. */var ONE_SECOND = 1000;var BASE_TEN = 10;var ELAPSED_TIME_MAX_DECIMAL_PLACES = 3;var CLOSING_TIME_BUFFER = 1.5;var DETECT_CSS_PROPERTIES = {  transitionDuration:      TRANSITION_DURATION_PROP,  transitionDelay:         TRANSITION_DELAY_PROP,  transitionProperty:      TRANSITION_PROP + PROPERTY_KEY,  animationDuration:       ANIMATION_DURATION_PROP,  animationDelay:          ANIMATION_DELAY_PROP,  animationIterationCount: ANIMATION_PROP + ANIMATION_ITERATION_COUNT_KEY};var DETECT_STAGGER_CSS_PROPERTIES = {  transitionDuration:      TRANSITION_DURATION_PROP,  transitionDelay:         TRANSITION_DELAY_PROP,  animationDuration:       ANIMATION_DURATION_PROP,  animationDelay:          ANIMATION_DELAY_PROP};function getCssKeyframeDurationStyle(duration) {  return [ANIMATION_DURATION_PROP, duration + 's'];}function getCssDelayStyle(delay, isKeyframeAnimation) {  var prop = isKeyframeAnimation ? ANIMATION_DELAY_PROP : TRANSITION_DELAY_PROP;  return [prop, delay + 's'];}function computeCssStyles($window, element, properties) {  var styles = Object.create(null);  var detectedStyles = $window.getComputedStyle(element) || {};  forEach(properties, function(formalStyleName, actualStyleName) {    var val = detectedStyles[formalStyleName];    if (val) {      var c = val.charAt(0);      // only numerical-based values have a negative sign or digit as the first value      if (c === '-' || c === '+' || c >= 0) {        val = parseMaxTime(val);      }      // by setting this to null in the event that the delay is not set or is set directly as 0      // then we can still allow for negative values to be used later on and not mistake this      // value for being greater than any other negative value.      if (val === 0) {        val = null;      }      styles[actualStyleName] = val;    }  });  return styles;}function parseMaxTime(str) {  var maxValue = 0;  var values = str.split(/\s*,\s*/);  forEach(values, function(value) {    // it's always safe to consider only second values and omit `ms` values since    // getComputedStyle will always handle the conversion for us    if (value.charAt(value.length - 1) == 's') {      value = value.substring(0, value.length - 1);    }    value = parseFloat(value) || 0;    maxValue = maxValue ? Math.max(value, maxValue) : value;  });  return maxValue;}function truthyTimingValue(val) {  return val === 0 || val != null;}function getCssTransitionDurationStyle(duration, applyOnlyDuration) {  var style = TRANSITION_PROP;  var value = duration + 's';  if (applyOnlyDuration) {    style += DURATION_KEY;  } else {    value += ' linear all';  }  return [style, value];}function createLocalCacheLookup() {  var cache = Object.create(null);  return {    flush: function() {      cache = Object.create(null);    },    count: function(key) {      var entry = cache[key];      return entry ? entry.total : 0;    },    get: function(key) {      var entry = cache[key];      return entry && entry.value;    },    put: function(key, value) {      if (!cache[key]) {        cache[key] = { total: 1, value: value };      } else {        cache[key].total++;      }    }  };}// we do not reassign an already present style value since// if we detect the style property value again we may be// detecting styles that were added via the `from` styles.// We make use of `isDefined` here since an empty string// or null value (which is what getPropertyValue will return// for a non-existing style) will still be marked as a valid// value for the style (a falsy value implies that the style// is to be removed at the end of the animation). If we had a simple// "OR" statement then it would not be enough to catch that.function registerRestorableStyles(backup, node, properties) {  forEach(properties, function(prop) {    backup[prop] = isDefined(backup[prop])        ? backup[prop]        : node.style.getPropertyValue(prop);  });}var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {  var gcsLookup = createLocalCacheLookup();  var gcsStaggerLookup = createLocalCacheLookup();  this.$get = ['$window', '$$jqLite', '$$AnimateRunner', '$timeout',               '$$forceReflow', '$sniffer', '$$rAFScheduler', '$$animateQueue',       function($window,   $$jqLite,   $$AnimateRunner,   $timeout,                $$forceReflow,   $sniffer,   $$rAFScheduler, $$animateQueue) {    var applyAnimationClasses = applyAnimationClassesFactory($$jqLite);    var parentCounter = 0;    function gcsHashFn(node, extraClasses) {      var KEY = "$$ngAnimateParentKey";      var parentNode = node.parentNode;      var parentID = parentNode[KEY] || (parentNode[KEY] = ++parentCounter);      return parentID + '-' + node.getAttribute('class') + '-' + extraClasses;    }    function computeCachedCssStyles(node, className, cacheKey, properties) {      var timings = gcsLookup.get(cacheKey);      if (!timings) {        timings = computeCssStyles($window, node, properties);        if (timings.animationIterationCount === 'infinite') {          timings.animationIterationCount = 1;        }      }      // we keep putting this in multiple times even though the value and the cacheKey are the same      // because we're keeping an internal tally of how many duplicate animations are detected.      gcsLookup.put(cacheKey, timings);      return timings;    }    function computeCachedCssStaggerStyles(node, className, cacheKey, properties) {      var stagger;      // if we have one or more existing matches of matching elements      // containing the same parent + CSS styles (which is how cacheKey works)      // then staggering is possible      if (gcsLookup.count(cacheKey) > 0) {        stagger = gcsStaggerLookup.get(cacheKey);        if (!stagger) {          var staggerClassName = pendClasses(className, '-stagger');          $$jqLite.addClass(node, staggerClassName);          stagger = computeCssStyles($window, node, properties);          // force the conversion of a null value to zero incase not set          stagger.animationDuration = Math.max(stagger.animationDuration, 0);          stagger.transitionDuration = Math.max(stagger.transitionDuration, 0);          $$jqLite.removeClass(node, staggerClassName);          gcsStaggerLookup.put(cacheKey, stagger);        }      }      return stagger || {};    }    var cancelLastRAFRequest;    var rafWaitQueue = [];    function waitUntilQuiet(callback) {      rafWaitQueue.push(callback);      $$rAFScheduler.waitUntilQuiet(function() {        gcsLookup.flush();        gcsStaggerLookup.flush();        // DO NOT REMOVE THIS LINE OR REFACTOR OUT THE `pageWidth` variable.        // PLEASE EXAMINE THE `$$forceReflow` service to understand why.        var pageWidth = $$forceReflow();        // we use a for loop to ensure that if the queue is changed        // during this looping then it will consider new requests        for (var i = 0; i < rafWaitQueue.length; i++) {          rafWaitQueue[i](pageWidth);        }        rafWaitQueue.length = 0;      });    }    function computeTimings(node, className, cacheKey) {      var timings = computeCachedCssStyles(node, className, cacheKey, DETECT_CSS_PROPERTIES);      var aD = timings.animationDelay;      var tD = timings.transitionDelay;      timings.maxDelay = aD && tD          ? Math.max(aD, tD)          : (aD || tD);      timings.maxDuration = Math.max(          timings.animationDuration * timings.animationIterationCount,          timings.transitionDuration);      return timings;    }    return function init(element, initialOptions) {      // all of the animation functions should create      // a copy of the options data, however, if a      // parent service has already created a copy then      // we should stick to using that      var options = initialOptions || {};      if (!options.$$prepared) {        options = prepareAnimationOptions(copy(options));      }      var restoreStyles = {};      var node = getDomNode(element);      if (!node          || !node.parentNode          || !$$animateQueue.enabled()) {        return closeAndReturnNoopAnimator();      }      var temporaryStyles = [];      var classes = element.attr('class');      var styles = packageStyles(options);      var animationClosed;      var animationPaused;      var animationCompleted;      var runner;      var runnerHost;      var maxDelay;      var maxDelayTime;      var maxDuration;      var maxDurationTime;      var startTime;      var events = [];      if (options.duration === 0 || (!$sniffer.animations && !$sniffer.transitions)) {        return closeAndReturnNoopAnimator();      }      var method = options.event && isArray(options.event)            ? options.event.join(' ')            : options.event;      var isStructural = method && options.structural;      var structuralClassName = '';      var addRemoveClassName = '';      if (isStructural) {        structuralClassName = pendClasses(method, EVENT_CLASS_PREFIX, true);      } else if (method) {        structuralClassName = method;      }      if (options.addClass) {        addRemoveClassName += pendClasses(options.addClass, ADD_CLASS_SUFFIX);      }      if (options.removeClass) {        if (addRemoveClassName.length) {          addRemoveClassName += ' ';        }        addRemoveClassName += pendClasses(options.removeClass, REMOVE_CLASS_SUFFIX);      }      // there may be a situation where a structural animation is combined together      // with CSS classes that need to resolve before the animation is computed.      // However this means that there is no explicit CSS code to block the animation      // from happening (by setting 0s none in the class name). If this is the case      // we need to apply the classes before the first rAF so we know to continue if      // there actually is a detected transition or keyframe animation      if (options.applyClassesEarly && addRemoveClassName.length) {        applyAnimationClasses(element, options);      }      var preparationClasses = [structuralClassName, addRemoveClassName].join(' ').trim();      var fullClassName = classes + ' ' + preparationClasses;      var activeClasses = pendClasses(preparationClasses, ACTIVE_CLASS_SUFFIX);      var hasToStyles = styles.to && Object.keys(styles.to).length > 0;      var containsKeyframeAnimation = (options.keyframeStyle || '').length > 0;      // there is no way we can trigger an animation if no styles and      // no classes are being applied which would then trigger a transition,      // unless there a is raw keyframe value that is applied to the element.      if (!containsKeyframeAnimation           && !hasToStyles           && !preparationClasses) {        return closeAndReturnNoopAnimator();      }      var cacheKey, stagger;      if (options.stagger > 0) {        var staggerVal = parseFloat(options.stagger);        stagger = {          transitionDelay: staggerVal,          animationDelay: staggerVal,          transitionDuration: 0,          animationDuration: 0        };      } else {        cacheKey = gcsHashFn(node, fullClassName);        stagger = computeCachedCssStaggerStyles(node, preparationClasses, cacheKey, DETECT_STAGGER_CSS_PROPERTIES);      }      if (!options.$$skipPreparationClasses) {        $$jqLite.addClass(element, preparationClasses);      }      var applyOnlyDuration;      if (options.transitionStyle) {        var transitionStyle = [TRANSITION_PROP, options.transitionStyle];        applyInlineStyle(node, transitionStyle);        temporaryStyles.push(transitionStyle);      }      if (options.duration >= 0) {        applyOnlyDuration = node.style[TRANSITION_PROP].length > 0;        var durationStyle = getCssTransitionDurationStyle(options.duration, applyOnlyDuration);        // we set the duration so that it will be picked up by getComputedStyle later        applyInlineStyle(node, durationStyle);        temporaryStyles.push(durationStyle);      }      if (options.keyframeStyle) {        var keyframeStyle = [ANIMATION_PROP, options.keyframeStyle];        applyInlineStyle(node, keyframeStyle);        temporaryStyles.push(keyframeStyle);      }      var itemIndex = stagger          ? options.staggerIndex >= 0              ? options.staggerIndex              : gcsLookup.count(cacheKey)          : 0;      var isFirst = itemIndex === 0;      // this is a pre-emptive way of forcing the setup classes to be added and applied INSTANTLY      // without causing any combination of transitions to kick in. By adding a negative delay value      // it forces the setup class' transition to end immediately. We later then remove the negative      // transition delay to allow for the transition to naturally do it's thing. The beauty here is      // that if there is no transition defined then nothing will happen and this will also allow      // other transitions to be stacked on top of each other without any chopping them out.      if (isFirst && !options.skipBlocking) {        blockTransitions(node, SAFE_FAST_FORWARD_DURATION_VALUE);      }      var timings = computeTimings(node, fullClassName, cacheKey);      var relativeDelay = timings.maxDelay;      maxDelay = Math.max(relativeDelay, 0);      maxDuration = timings.maxDuration;      var flags = {};      flags.hasTransitions          = timings.transitionDuration > 0;      flags.hasAnimations           = timings.animationDuration > 0;      flags.hasTransitionAll        = flags.hasTransitions && timings.transitionProperty == 'all';      flags.applyTransitionDuration = hasToStyles && (                                        (flags.hasTransitions && !flags.hasTransitionAll)                                         || (flags.hasAnimations && !flags.hasTransitions));      flags.applyAnimationDuration  = options.duration && flags.hasAnimations;      flags.applyTransitionDelay    = truthyTimingValue(options.delay) && (flags.applyTransitionDuration || flags.hasTransitions);      flags.applyAnimationDelay     = truthyTimingValue(options.delay) && flags.hasAnimations;      flags.recalculateTimingStyles = addRemoveClassName.length > 0;      if (flags.applyTransitionDuration || flags.applyAnimationDuration) {        maxDuration = options.duration ? parseFloat(options.duration) : maxDuration;        if (flags.applyTransitionDuration) {          flags.hasTransitions = true;          timings.transitionDuration = maxDuration;          applyOnlyDuration = node.style[TRANSITION_PROP + PROPERTY_KEY].length > 0;          temporaryStyles.push(getCssTransitionDurationStyle(maxDuration, applyOnlyDuration));        }        if (flags.applyAnimationDuration) {          flags.hasAnimations = true;          timings.animationDuration = maxDuration;          temporaryStyles.push(getCssKeyframeDurationStyle(maxDuration));        }      }      if (maxDuration === 0 && !flags.recalculateTimingStyles) {        return closeAndReturnNoopAnimator();      }      if (options.delay != null) {        var delayStyle;        if (typeof options.delay !== "boolean") {          delayStyle = parseFloat(options.delay);          // number in options.delay means we have to recalculate the delay for the closing timeout          maxDelay = Math.max(delayStyle, 0);        }        if (flags.applyTransitionDelay) {          temporaryStyles.push(getCssDelayStyle(delayStyle));        }        if (flags.applyAnimationDelay) {          temporaryStyles.push(getCssDelayStyle(delayStyle, true));        }      }      // we need to recalculate the delay value since we used a pre-emptive negative      // delay value and the delay value is required for the final event checking. This      // property will ensure that this will happen after the RAF phase has passed.      if (options.duration == null && timings.transitionDuration > 0) {        flags.recalculateTimingStyles = flags.recalculateTimingStyles || isFirst;      }      maxDelayTime = maxDelay * ONE_SECOND;      maxDurationTime = maxDuration * ONE_SECOND;      if (!options.skipBlocking) {        flags.blockTransition = timings.transitionDuration > 0;        flags.blockKeyframeAnimation = timings.animationDuration > 0 &&                                       stagger.animationDelay > 0 &&                                       stagger.animationDuration === 0;      }      if (options.from) {        if (options.cleanupStyles) {          registerRestorableStyles(restoreStyles, node, Object.keys(options.from));        }        applyAnimationFromStyles(element, options);      }      if (flags.blockTransition || flags.blockKeyframeAnimation) {        applyBlocking(maxDuration);      } else if (!options.skipBlocking) {        blockTransitions(node, false);      }      // TODO(matsko): for 1.5 change this code to have an animator object for better debugging      return {        $$willAnimate: true,        end: endFn,        start: function() {          if (animationClosed) return;          runnerHost = {            end: endFn,            cancel: cancelFn,            resume: null, //this will be set during the start() phase            pause: null          };          runner = new $$AnimateRunner(runnerHost);          waitUntilQuiet(start);          // we don't have access to pause/resume the animation          // since it hasn't run yet. AnimateRunner will therefore          // set noop functions for resume and pause and they will          // later be overridden once the animation is triggered          return runner;        }      };      function endFn() {        close();      }      function cancelFn() {        close(true);      }      function close(rejected) { // jshint ignore:line        // if the promise has been called already then we shouldn't close        // the animation again        if (animationClosed || (animationCompleted && animationPaused)) return;        animationClosed = true;        animationPaused = false;        if (!options.$$skipPreparationClasses) {          $$jqLite.removeClass(element, preparationClasses);        }        $$jqLite.removeClass(element, activeClasses);        blockKeyframeAnimations(node, false);        blockTransitions(node, false);        forEach(temporaryStyles, function(entry) {          // There is only one way to remove inline style properties entirely from elements.          // By using `removeProperty` this works, but we need to convert camel-cased CSS          // styles down to hyphenated values.          node.style[entry[0]] = '';        });        applyAnimationClasses(element, options);        applyAnimationStyles(element, options);        if (Object.keys(restoreStyles).length) {          forEach(restoreStyles, function(value, prop) {            value ? node.style.setProperty(prop, value)                  : node.style.removeProperty(prop);          });        }        // the reason why we have this option is to allow a synchronous closing callback        // that is fired as SOON as the animation ends (when the CSS is removed) or if        // the animation never takes off at all. A good example is a leave animation since        // the element must be removed just after the animation is over or else the element        // will appear on screen for one animation frame causing an overbearing flicker.        if (options.onDone) {          options.onDone();        }        if (events && events.length) {          // Remove the transitionend / animationend listener(s)          element.off(events.join(' '), onAnimationProgress);        }        //Cancel the fallback closing timeout and remove the timer data        var animationTimerData = element.data(ANIMATE_TIMER_KEY);        if (animationTimerData) {          $timeout.cancel(animationTimerData[0].timer);          element.removeData(ANIMATE_TIMER_KEY);        }        // if the preparation function fails then the promise is not setup        if (runner) {          runner.complete(!rejected);        }      }      function applyBlocking(duration) {        if (flags.blockTransition) {          blockTransitions(node, duration);        }        if (flags.blockKeyframeAnimation) {          blockKeyframeAnimations(node, !!duration);        }      }      function closeAndReturnNoopAnimator() {        runner = new $$AnimateRunner({          end: endFn,          cancel: cancelFn        });        // should flush the cache animation        waitUntilQuiet(noop);        close();        return {          $$willAnimate: false,          start: function() {            return runner;          },          end: endFn        };      }      function onAnimationProgress(event) {        event.stopPropagation();        var ev = event.originalEvent || event;        // we now always use `Date.now()` due to the recent changes with        // event.timeStamp in Firefox, Webkit and Chrome (see #13494 for more info)        var timeStamp = ev.$manualTimeStamp || 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,         * or, if they don't, then a timeStamp is automatically created for them.         * 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 animationPauseds sometimes close off early */        if (Math.max(timeStamp - startTime, 0) >= maxDelayTime && elapsedTime >= maxDuration) {          // we set this flag to ensure that if the transition is paused then, when resumed,          // the animation will automatically close itself since transitions cannot be paused.          animationCompleted = true;          close();        }      }      function start() {        if (animationClosed) return;        if (!node.parentNode) {          close();          return;        }        // even though we only pause keyframe animations here the pause flag        // will still happen when transitions are used. Only the transition will        // not be paused since that is not possible. If the animation ends when        // paused then it will not complete until unpaused or cancelled.        var playPause = function(playAnimation) {          if (!animationCompleted) {            animationPaused = !playAnimation;            if (timings.animationDuration) {              var value = blockKeyframeAnimations(node, animationPaused);              animationPaused                  ? temporaryStyles.push(value)                  : removeFromArray(temporaryStyles, value);            }          } else if (animationPaused && playAnimation) {            animationPaused = false;            close();          }        };        // checking the stagger duration prevents an accidentally cascade of the CSS delay style        // being inherited from the parent. If the transition duration is zero then we can safely        // rely that the delay value is an intentional stagger delay style.        var maxStagger = itemIndex > 0                         && ((timings.transitionDuration && stagger.transitionDuration === 0) ||                            (timings.animationDuration && stagger.animationDuration === 0))                         && Math.max(stagger.animationDelay, stagger.transitionDelay);        if (maxStagger) {          $timeout(triggerAnimationStart,                   Math.floor(maxStagger * itemIndex * ONE_SECOND),                   false);        } else {          triggerAnimationStart();        }        // this will decorate the existing promise runner with pause/resume methods        runnerHost.resume = function() {          playPause(true);        };        runnerHost.pause = function() {          playPause(false);        };        function triggerAnimationStart() {          // just incase a stagger animation kicks in when the animation          // itself was cancelled entirely          if (animationClosed) return;          applyBlocking(false);          forEach(temporaryStyles, function(entry) {            var key = entry[0];            var value = entry[1];            node.style[key] = value;          });          applyAnimationClasses(element, options);          $$jqLite.addClass(element, activeClasses);          if (flags.recalculateTimingStyles) {            fullClassName = node.className + ' ' + preparationClasses;            cacheKey = gcsHashFn(node, fullClassName);            timings = computeTimings(node, fullClassName, cacheKey);            relativeDelay = timings.maxDelay;            maxDelay = Math.max(relativeDelay, 0);            maxDuration = timings.maxDuration;            if (maxDuration === 0) {              close();              return;            }            flags.hasTransitions = timings.transitionDuration > 0;            flags.hasAnimations = timings.animationDuration > 0;          }          if (flags.applyAnimationDelay) {            relativeDelay = typeof options.delay !== "boolean" && truthyTimingValue(options.delay)                  ? parseFloat(options.delay)                  : relativeDelay;            maxDelay = Math.max(relativeDelay, 0);            timings.animationDelay = relativeDelay;            delayStyle = getCssDelayStyle(relativeDelay, true);            temporaryStyles.push(delayStyle);            node.style[delayStyle[0]] = delayStyle[1];          }          maxDelayTime = maxDelay * ONE_SECOND;          maxDurationTime = maxDuration * ONE_SECOND;          if (options.easing) {            var easeProp, easeVal = options.easing;            if (flags.hasTransitions) {              easeProp = TRANSITION_PROP + TIMING_KEY;              temporaryStyles.push([easeProp, easeVal]);              node.style[easeProp] = easeVal;            }            if (flags.hasAnimations) {              easeProp = ANIMATION_PROP + TIMING_KEY;              temporaryStyles.push([easeProp, easeVal]);              node.style[easeProp] = easeVal;            }          }          if (timings.transitionDuration) {            events.push(TRANSITIONEND_EVENT);          }          if (timings.animationDuration) {            events.push(ANIMATIONEND_EVENT);          }          startTime = Date.now();          var timerTime = maxDelayTime + CLOSING_TIME_BUFFER * maxDurationTime;          var endTime = startTime + timerTime;          var animationsData = element.data(ANIMATE_TIMER_KEY) || [];          var setupFallbackTimer = true;          if (animationsData.length) {            var currentTimerData = animationsData[0];            setupFallbackTimer = endTime > currentTimerData.expectedEndTime;            if (setupFallbackTimer) {              $timeout.cancel(currentTimerData.timer);            } else {              animationsData.push(close);            }          }          if (setupFallbackTimer) {            var timer = $timeout(onAnimationExpired, timerTime, false);            animationsData[0] = {              timer: timer,              expectedEndTime: endTime            };            animationsData.push(close);            element.data(ANIMATE_TIMER_KEY, animationsData);          }          if (events.length) {            element.on(events.join(' '), onAnimationProgress);          }          if (options.to) {            if (options.cleanupStyles) {              registerRestorableStyles(restoreStyles, node, Object.keys(options.to));            }            applyAnimationToStyles(element, options);          }        }        function onAnimationExpired() {          var animationsData = element.data(ANIMATE_TIMER_KEY);          // this will be false in the event that the element was          // removed from the DOM (via a leave animation or something          // similar)          if (animationsData) {            for (var i = 1; i < animationsData.length; i++) {              animationsData[i]();            }            element.removeData(ANIMATE_TIMER_KEY);          }        }      }    };  }];}];var $$AnimateCssDriverProvider = ['$$animationProvider', function($$animationProvider) {  $$animationProvider.drivers.push('$$animateCssDriver');  var NG_ANIMATE_SHIM_CLASS_NAME = 'ng-animate-shim';  var NG_ANIMATE_ANCHOR_CLASS_NAME = 'ng-anchor';  var NG_OUT_ANCHOR_CLASS_NAME = 'ng-anchor-out';  var NG_IN_ANCHOR_CLASS_NAME = 'ng-anchor-in';  function isDocumentFragment(node) {    return node.parentNode && node.parentNode.nodeType === 11;  }  this.$get = ['$animateCss', '$rootScope', '$$AnimateRunner', '$rootElement', '$sniffer', '$$jqLite', '$document',       function($animateCss,   $rootScope,   $$AnimateRunner,   $rootElement,   $sniffer,   $$jqLite,   $document) {    // only browsers that support these properties can render animations    if (!$sniffer.animations && !$sniffer.transitions) return noop;    var bodyNode = $document[0].body;    var rootNode = getDomNode($rootElement);    var rootBodyElement = jqLite(      // this is to avoid using something that exists outside of the body      // we also special case the doc fragment case because our unit test code      // appends the $rootElement to the body after the app has been bootstrapped      isDocumentFragment(rootNode) || bodyNode.contains(rootNode) ? rootNode : bodyNode    );    var applyAnimationClasses = applyAnimationClassesFactory($$jqLite);    return function initDriverFn(animationDetails) {      return animationDetails.from && animationDetails.to          ? prepareFromToAnchorAnimation(animationDetails.from,                                         animationDetails.to,                                         animationDetails.classes,                                         animationDetails.anchors)          : prepareRegularAnimation(animationDetails);    };    function filterCssClasses(classes) {      //remove all the `ng-` stuff      return classes.replace(/\bng-\S+\b/g, '');    }    function getUniqueValues(a, b) {      if (isString(a)) a = a.split(' ');      if (isString(b)) b = b.split(' ');      return a.filter(function(val) {        return b.indexOf(val) === -1;      }).join(' ');    }    function prepareAnchoredAnimation(classes, outAnchor, inAnchor) {      var clone = jqLite(getDomNode(outAnchor).cloneNode(true));      var startingClasses = filterCssClasses(getClassVal(clone));      outAnchor.addClass(NG_ANIMATE_SHIM_CLASS_NAME);      inAnchor.addClass(NG_ANIMATE_SHIM_CLASS_NAME);      clone.addClass(NG_ANIMATE_ANCHOR_CLASS_NAME);      rootBodyElement.append(clone);      var animatorIn, animatorOut = prepareOutAnimation();      // the user may not end up using the `out` animation and      // only making use of the `in` animation or vice-versa.      // In either case we should allow this and not assume the      // animation is over unless both animations are not used.      if (!animatorOut) {        animatorIn = prepareInAnimation();        if (!animatorIn) {          return end();        }      }      var startingAnimator = animatorOut || animatorIn;      return {        start: function() {          var runner;          var currentAnimation = startingAnimator.start();          currentAnimation.done(function() {            currentAnimation = null;            if (!animatorIn) {              animatorIn = prepareInAnimation();              if (animatorIn) {                currentAnimation = animatorIn.start();                currentAnimation.done(function() {                  currentAnimation = null;                  end();                  runner.complete();                });                return currentAnimation;              }            }            // in the event that there is no `in` animation            end();            runner.complete();          });          runner = new $$AnimateRunner({            end: endFn,            cancel: endFn          });          return runner;          function endFn() {            if (currentAnimation) {              currentAnimation.end();            }          }        }      };      function calculateAnchorStyles(anchor) {        var styles = {};        var coords = getDomNode(anchor).getBoundingClientRect();        // we iterate directly since safari messes up and doesn't return        // all the keys for the coords object when iterated        forEach(['width','height','top','left'], function(key) {          var value = coords[key];          switch (key) {            case 'top':              value += bodyNode.scrollTop;              break;            case 'left':              value += bodyNode.scrollLeft;              break;          }          styles[key] = Math.floor(value) + 'px';        });        return styles;      }      function prepareOutAnimation() {        var animator = $animateCss(clone, {          addClass: NG_OUT_ANCHOR_CLASS_NAME,          delay: true,          from: calculateAnchorStyles(outAnchor)        });        // read the comment within `prepareRegularAnimation` to understand        // why this check is necessary        return animator.$$willAnimate ? animator : null;      }      function getClassVal(element) {        return element.attr('class') || '';      }      function prepareInAnimation() {        var endingClasses = filterCssClasses(getClassVal(inAnchor));        var toAdd = getUniqueValues(endingClasses, startingClasses);        var toRemove = getUniqueValues(startingClasses, endingClasses);        var animator = $animateCss(clone, {          to: calculateAnchorStyles(inAnchor),          addClass: NG_IN_ANCHOR_CLASS_NAME + ' ' + toAdd,          removeClass: NG_OUT_ANCHOR_CLASS_NAME + ' ' + toRemove,          delay: true        });        // read the comment within `prepareRegularAnimation` to understand        // why this check is necessary        return animator.$$willAnimate ? animator : null;      }      function end() {        clone.remove();        outAnchor.removeClass(NG_ANIMATE_SHIM_CLASS_NAME);        inAnchor.removeClass(NG_ANIMATE_SHIM_CLASS_NAME);      }    }    function prepareFromToAnchorAnimation(from, to, classes, anchors) {      var fromAnimation = prepareRegularAnimation(from, noop);      var toAnimation = prepareRegularAnimation(to, noop);      var anchorAnimations = [];      forEach(anchors, function(anchor) {        var outElement = anchor['out'];        var inElement = anchor['in'];        var animator = prepareAnchoredAnimation(classes, outElement, inElement);        if (animator) {          anchorAnimations.push(animator);        }      });      // no point in doing anything when there are no elements to animate      if (!fromAnimation && !toAnimation && anchorAnimations.length === 0) return;      return {        start: function() {          var animationRunners = [];          if (fromAnimation) {            animationRunners.push(fromAnimation.start());          }          if (toAnimation) {            animationRunners.push(toAnimation.start());          }          forEach(anchorAnimations, function(animation) {            animationRunners.push(animation.start());          });          var runner = new $$AnimateRunner({            end: endFn,            cancel: endFn // CSS-driven animations cannot be cancelled, only ended          });          $$AnimateRunner.all(animationRunners, function(status) {            runner.complete(status);          });          return runner;          function endFn() {            forEach(animationRunners, function(runner) {              runner.end();            });          }        }      };    }    function prepareRegularAnimation(animationDetails) {      var element = animationDetails.element;      var options = animationDetails.options || {};      if (animationDetails.structural) {        options.event = animationDetails.event;        options.structural = true;        options.applyClassesEarly = true;        // we special case the leave animation since we want to ensure that        // the element is removed as soon as the animation is over. Otherwise        // a flicker might appear or the element may not be removed at all        if (animationDetails.event === 'leave') {          options.onDone = options.domOperation;        }      }      // We assign the preparationClasses as the actual animation event since      // the internals of $animateCss will just suffix the event token values      // with `-active` to trigger the animation.      if (options.preparationClasses) {        options.event = concatWithSpace(options.event, options.preparationClasses);      }      var animator = $animateCss(element, options);      // the driver lookup code inside of $$animation attempts to spawn a      // driver one by one until a driver returns a.$$willAnimate animator object.      // $animateCss will always return an object, however, it will pass in      // a flag as a hint as to whether an animation was detected or not      return animator.$$willAnimate ? animator : null;    }  }];}];// TODO(matsko): use caching here to speed things up for detection// TODO(matsko): add documentation//  by the time...var $$AnimateJsProvider = ['$animateProvider', function($animateProvider) {  this.$get = ['$injector', '$$AnimateRunner', '$$jqLite',       function($injector,   $$AnimateRunner,   $$jqLite) {    var applyAnimationClasses = applyAnimationClassesFactory($$jqLite);         // $animateJs(element, 'enter');    return function(element, event, classes, options) {      var animationClosed = false;      // the `classes` argument is optional and if it is not used      // then the classes will be resolved from the element's className      // property as well as options.addClass/options.removeClass.      if (arguments.length === 3 && isObject(classes)) {        options = classes;        classes = null;      }      options = prepareAnimationOptions(options);      if (!classes) {        classes = element.attr('class') || '';        if (options.addClass) {          classes += ' ' + options.addClass;        }        if (options.removeClass) {          classes += ' ' + options.removeClass;        }      }      var classesToAdd = options.addClass;      var classesToRemove = options.removeClass;      // the lookupAnimations function returns a series of animation objects that are      // matched up with one or more of the CSS classes. These animation objects are      // defined via the module.animation factory function. If nothing is detected then      // we don't return anything which then makes $animation query the next driver.      var animations = lookupAnimations(classes);      var before, after;      if (animations.length) {        var afterFn, beforeFn;        if (event == 'leave') {          beforeFn = 'leave';          afterFn = 'afterLeave'; // TODO(matsko): get rid of this        } else {          beforeFn = 'before' + event.charAt(0).toUpperCase() + event.substr(1);          afterFn = event;        }        if (event !== 'enter' && event !== 'move') {          before = packageAnimations(element, event, options, animations, beforeFn);        }        after  = packageAnimations(element, event, options, animations, afterFn);      }      // no matching animations      if (!before && !after) return;      function applyOptions() {        options.domOperation();        applyAnimationClasses(element, options);      }      function close() {        animationClosed = true;        applyOptions();        applyAnimationStyles(element, options);      }      var runner;      return {        $$willAnimate: true,        end: function() {          if (runner) {            runner.end();          } else {            close();            runner = new $$AnimateRunner();            runner.complete(true);          }          return runner;        },        start: function() {          if (runner) {            return runner;          }          runner = new $$AnimateRunner();          var closeActiveAnimations;          var chain = [];          if (before) {            chain.push(function(fn) {              closeActiveAnimations = before(fn);            });          }          if (chain.length) {            chain.push(function(fn) {              applyOptions();              fn(true);            });          } else {            applyOptions();          }          if (after) {            chain.push(function(fn) {              closeActiveAnimations = after(fn);            });          }          runner.setHost({            end: function() {              endAnimations();            },            cancel: function() {              endAnimations(true);            }          });          $$AnimateRunner.chain(chain, onComplete);          return runner;          function onComplete(success) {            close(success);            runner.complete(success);          }          function endAnimations(cancelled) {            if (!animationClosed) {              (closeActiveAnimations || noop)(cancelled);              onComplete(cancelled);            }          }        }      };      function executeAnimationFn(fn, element, event, options, onDone) {        var args;        switch (event) {          case 'animate':            args = [element, options.from, options.to, onDone];            break;          case 'setClass':            args = [element, classesToAdd, classesToRemove, onDone];            break;          case 'addClass':            args = [element, classesToAdd, onDone];            break;          case 'removeClass':            args = [element, classesToRemove, onDone];            break;          default:            args = [element, onDone];            break;        }        args.push(options);        var value = fn.apply(fn, args);        if (value) {          if (isFunction(value.start)) {            value = value.start();          }          if (value instanceof $$AnimateRunner) {            value.done(onDone);          } else if (isFunction(value)) {            // optional onEnd / onCancel callback            return value;          }        }        return noop;      }      function groupEventedAnimations(element, event, options, animations, fnName) {        var operations = [];        forEach(animations, function(ani) {          var animation = ani[fnName];          if (!animation) return;          // note that all of these animations will run in parallel          operations.push(function() {            var runner;            var endProgressCb;            var resolved = false;            var onAnimationComplete = function(rejected) {              if (!resolved) {                resolved = true;                (endProgressCb || noop)(rejected);                runner.complete(!rejected);              }            };            runner = new $$AnimateRunner({              end: function() {                onAnimationComplete();              },              cancel: function() {                onAnimationComplete(true);              }            });            endProgressCb = executeAnimationFn(animation, element, event, options, function(result) {              var cancelled = result === false;              onAnimationComplete(cancelled);            });            return runner;          });        });        return operations;      }      function packageAnimations(element, event, options, animations, fnName) {        var operations = groupEventedAnimations(element, event, options, animations, fnName);        if (operations.length === 0) {          var a,b;          if (fnName === 'beforeSetClass') {            a = groupEventedAnimations(element, 'removeClass', options, animations, 'beforeRemoveClass');            b = groupEventedAnimations(element, 'addClass', options, animations, 'beforeAddClass');          } else if (fnName === 'setClass') {            a = groupEventedAnimations(element, 'removeClass', options, animations, 'removeClass');            b = groupEventedAnimations(element, 'addClass', options, animations, 'addClass');          }          if (a) {            operations = operations.concat(a);          }          if (b) {            operations = operations.concat(b);          }        }        if (operations.length === 0) return;        // TODO(matsko): add documentation        return function startAnimation(callback) {          var runners = [];          if (operations.length) {            forEach(operations, function(animateFn) {              runners.push(animateFn());            });          }          runners.length ? $$AnimateRunner.all(runners, callback) : callback();          return function endFn(reject) {            forEach(runners, function(runner) {              reject ? runner.cancel() : runner.end();            });          };        };      }    };    function lookupAnimations(classes) {      classes = isArray(classes) ? classes : classes.split(' ');      var matches = [], flagMap = {};      for (var i=0; i < classes.length; i++) {        var klass = classes[i],            animationFactory = $animateProvider.$$registeredAnimations[klass];        if (animationFactory && !flagMap[klass]) {          matches.push($injector.get(animationFactory));          flagMap[klass] = true;        }      }      return matches;    }  }];}];var $$AnimateJsDriverProvider = ['$$animationProvider', function($$animationProvider) {  $$animationProvider.drivers.push('$$animateJsDriver');  this.$get = ['$$animateJs', '$$AnimateRunner', function($$animateJs, $$AnimateRunner) {    return function initDriverFn(animationDetails) {      if (animationDetails.from && animationDetails.to) {        var fromAnimation = prepareAnimation(animationDetails.from);        var toAnimation = prepareAnimation(animationDetails.to);        if (!fromAnimation && !toAnimation) return;        return {          start: function() {            var animationRunners = [];            if (fromAnimation) {              animationRunners.push(fromAnimation.start());            }            if (toAnimation) {              animationRunners.push(toAnimation.start());            }            $$AnimateRunner.all(animationRunners, done);            var runner = new $$AnimateRunner({              end: endFnFactory(),              cancel: endFnFactory()            });            return runner;            function endFnFactory() {              return function() {                forEach(animationRunners, function(runner) {                  // at this point we cannot cancel animations for groups just yet. 1.5+                  runner.end();                });              };            }            function done(status) {              runner.complete(status);            }          }        };      } else {        return prepareAnimation(animationDetails);      }    };    function prepareAnimation(animationDetails) {      // TODO(matsko): make sure to check for grouped animations and delegate down to normal animations      var element = animationDetails.element;      var event = animationDetails.event;      var options = animationDetails.options;      var classes = animationDetails.classes;      return $$animateJs(element, event, classes, options);    }  }];}];var NG_ANIMATE_ATTR_NAME = 'data-ng-animate';var NG_ANIMATE_PIN_DATA = '$ngAnimatePin';var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {  var PRE_DIGEST_STATE = 1;  var RUNNING_STATE = 2;  var ONE_SPACE = ' ';  var rules = this.rules = {    skip: [],    cancel: [],    join: []  };  function makeTruthyCssClassMap(classString) {    if (!classString) {      return null;    }    var keys = classString.split(ONE_SPACE);    var map = Object.create(null);    forEach(keys, function(key) {      map[key] = true;    });    return map;  }  function hasMatchingClasses(newClassString, currentClassString) {    if (newClassString && currentClassString) {      var currentClassMap = makeTruthyCssClassMap(currentClassString);      return newClassString.split(ONE_SPACE).some(function(className) {        return currentClassMap[className];      });    }  }  function isAllowed(ruleType, element, currentAnimation, previousAnimation) {    return rules[ruleType].some(function(fn) {      return fn(element, currentAnimation, previousAnimation);    });  }  function hasAnimationClasses(animation, and) {    var a = (animation.addClass || '').length > 0;    var b = (animation.removeClass || '').length > 0;    return and ? a && b : a || b;  }  rules.join.push(function(element, newAnimation, currentAnimation) {    // if the new animation is class-based then we can just tack that on    return !newAnimation.structural && hasAnimationClasses(newAnimation);  });  rules.skip.push(function(element, newAnimation, currentAnimation) {    // there is no need to animate anything if no classes are being added and    // there is no structural animation that will be triggered    return !newAnimation.structural && !hasAnimationClasses(newAnimation);  });  rules.skip.push(function(element, newAnimation, currentAnimation) {    // why should we trigger a new structural animation if the element will    // be removed from the DOM anyway?    return currentAnimation.event == 'leave' && newAnimation.structural;  });  rules.skip.push(function(element, newAnimation, currentAnimation) {    // if there is an ongoing current animation then don't even bother running the class-based animation    return currentAnimation.structural && currentAnimation.state === RUNNING_STATE && !newAnimation.structural;  });  rules.cancel.push(function(element, newAnimation, currentAnimation) {    // there can never be two structural animations running at the same time    return currentAnimation.structural && newAnimation.structural;  });  rules.cancel.push(function(element, newAnimation, currentAnimation) {    // if the previous animation is already running, but the new animation will    // be triggered, but the new animation is structural    return currentAnimation.state === RUNNING_STATE && newAnimation.structural;  });  rules.cancel.push(function(element, newAnimation, currentAnimation) {    // cancel the animation if classes added / removed in both animation cancel each other out,    // but only if the current animation isn't structural    if (currentAnimation.structural) return false;    var nA = newAnimation.addClass;    var nR = newAnimation.removeClass;    var cA = currentAnimation.addClass;    var cR = currentAnimation.removeClass;    // early detection to save the global CPU shortage :)    if ((isUndefined(nA) && isUndefined(nR)) || (isUndefined(cA) && isUndefined(cR))) {      return false;    }    return hasMatchingClasses(nA, cR) || hasMatchingClasses(nR, cA);  });  this.$get = ['$$rAF', '$rootScope', '$rootElement', '$document', '$$HashMap',               '$$animation', '$$AnimateRunner', '$templateRequest', '$$jqLite', '$$forceReflow',       function($$rAF,   $rootScope,   $rootElement,   $document,   $$HashMap,                $$animation,   $$AnimateRunner,   $templateRequest,   $$jqLite,   $$forceReflow) {    var activeAnimationsLookup = new $$HashMap();    var disabledElementsLookup = new $$HashMap();    var animationsEnabled = null;    function postDigestTaskFactory() {      var postDigestCalled = false;      return function(fn) {        // we only issue a call to postDigest before        // it has first passed. This prevents any callbacks        // from not firing once the animation has completed        // since it will be out of the digest cycle.        if (postDigestCalled) {          fn();        } else {          $rootScope.$$postDigest(function() {            postDigestCalled = true;            fn();          });        }      };    }    // Wait until all directive and route-related templates are downloaded and    // compiled. The $templateRequest.totalPendingRequests variable keeps track of    // all of the remote templates being currently downloaded. If there are no    // templates currently downloading then the watcher will still fire anyway.    var deregisterWatch = $rootScope.$watch(      function() { return $templateRequest.totalPendingRequests === 0; },      function(isEmpty) {        if (!isEmpty) return;        deregisterWatch();        // Now that all templates have been downloaded, $animate will wait until        // the post digest queue is empty before enabling animations. By having two        // calls to $postDigest calls we can ensure that the flag is enabled at the        // very end of the post digest queue. Since all of the animations in $animate        // use $postDigest, it's important that the code below executes at the end.        // This basically means that the page is fully downloaded and compiled before        // any animations are triggered.        $rootScope.$$postDigest(function() {          $rootScope.$$postDigest(function() {            // we check for null directly in the event that the application already called            // .enabled() with whatever arguments that it provided it with            if (animationsEnabled === null) {              animationsEnabled = true;            }          });        });      }    );    var callbackRegistry = {};    // remember that the classNameFilter is set during the provider/config    // stage therefore we can optimize here and setup a helper function    var classNameFilter = $animateProvider.classNameFilter();    var isAnimatableClassName = !classNameFilter              ? function() { return true; }              : function(className) {                return classNameFilter.test(className);              };    var applyAnimationClasses = applyAnimationClassesFactory($$jqLite);    function normalizeAnimationDetails(element, animation) {      return mergeAnimationDetails(element, animation, {});    }    // IE9-11 has no method "contains" in SVG element and in Node.prototype. Bug #10259.    var contains = Node.prototype.contains || function(arg) {      // jshint bitwise: false      return this === arg || !!(this.compareDocumentPosition(arg) & 16);      // jshint bitwise: true    };    function findCallbacks(parent, element, event) {      var targetNode = getDomNode(element);      var targetParentNode = getDomNode(parent);      var matches = [];      var entries = callbackRegistry[event];      if (entries) {        forEach(entries, function(entry) {          if (contains.call(entry.node, targetNode)) {            matches.push(entry.callback);          } else if (event === 'leave' && contains.call(entry.node, targetParentNode)) {            matches.push(entry.callback);          }        });      }      return matches;    }    var $animate = {      on: function(event, container, callback) {        var node = extractElementNode(container);        callbackRegistry[event] = callbackRegistry[event] || [];        callbackRegistry[event].push({          node: node,          callback: callback        });        // Remove the callback when the element is removed from the DOM        jqLite(container).on('$destroy', function() {          $animate.off(event, container, callback);        });      },      off: function(event, container, callback) {        var entries = callbackRegistry[event];        if (!entries) return;        callbackRegistry[event] = arguments.length === 1            ? null            : filterFromRegistry(entries, container, callback);        function filterFromRegistry(list, matchContainer, matchCallback) {          var containerNode = extractElementNode(matchContainer);          return list.filter(function(entry) {            var isMatch = entry.node === containerNode &&                            (!matchCallback || entry.callback === matchCallback);            return !isMatch;          });        }      },      pin: function(element, parentElement) {        assertArg(isElement(element), 'element', 'not an element');        assertArg(isElement(parentElement), 'parentElement', 'not an element');        element.data(NG_ANIMATE_PIN_DATA, parentElement);      },      push: function(element, event, options, domOperation) {        options = options || {};        options.domOperation = domOperation;        return queueAnimation(element, event, options);      },      // this method has four signatures:      //  () - global getter      //  (bool) - global setter      //  (element) - element getter      //  (element, bool) - element setter<F37>      enabled: function(element, bool) {        var argCount = arguments.length;        if (argCount === 0) {          // () - Global getter          bool = !!animationsEnabled;        } else {          var hasElement = isElement(element);          if (!hasElement) {            // (bool) - Global setter            bool = animationsEnabled = !!element;          } else {            var node = getDomNode(element);            var recordExists = disabledElementsLookup.get(node);            if (argCount === 1) {              // (element) - Element getter              bool = !recordExists;            } else {              // (element, bool) - Element setter              disabledElementsLookup.put(node, !bool);            }          }        }        return bool;      }    };    return $animate;    function queueAnimation(element, event, initialOptions) {      // we always make a copy of the options since      // there should never be any side effects on      // the input data when running `$animateCss`.      var options = copy(initialOptions);      var node, parent;      element = stripCommentsFromElement(element);      if (element) {        node = getDomNode(element);        parent = element.parent();      }      options = prepareAnimationOptions(options);      // we create a fake runner with a working promise.      // These methods will become available after the digest has passed      var runner = new $$AnimateRunner();      // this is used to trigger callbacks in postDigest mode      var runInNextPostDigestOrNow = postDigestTaskFactory();      if (isArray(options.addClass)) {        options.addClass = options.addClass.join(' ');      }      if (options.addClass && !isString(options.addClass)) {        options.addClass = null;      }      if (isArray(options.removeClass)) {        options.removeClass = options.removeClass.join(' ');      }      if (options.removeClass && !isString(options.removeClass)) {        options.removeClass = null;      }      if (options.from && !isObject(options.from)) {        options.from = null;      }      if (options.to && !isObject(options.to)) {        options.to = null;      }      // there are situations where a directive issues an animation for      // a jqLite wrapper that contains only comment nodes... If this      // happens then there is no way we can perform an animation      if (!node) {        close();        return runner;      }      var className = [node.className, options.addClass, options.removeClass].join(' ');      if (!isAnimatableClassName(className)) {        close();        return runner;      }      var isStructural = ['enter', 'move', 'leave'].indexOf(event) >= 0;      // this is a hard disable of all animations for the application or on      // the element itself, therefore  there is no need to continue further      // past this point if not enabled      // Animations are also disabled if the document is currently hidden (page is not visible      // to the user), because browsers slow down or do not flush calls to requestAnimationFrame      var skipAnimations = !animationsEnabled || $document[0].hidden || disabledElementsLookup.get(node);      var existingAnimation = (!skipAnimations && activeAnimationsLookup.get(node)) || {};      var hasExistingAnimation = !!existingAnimation.state;      // there is no point in traversing the same collection of parent ancestors if a followup      // animation will be run on the same element that already did all that checking work      if (!skipAnimations && (!hasExistingAnimation || existingAnimation.state != PRE_DIGEST_STATE)) {        skipAnimations = !areAnimationsAllowed(element, parent, event);      }      if (skipAnimations) {        close();        return runner;      }      if (isStructural) {        closeChildAnimations(element);      }      var newAnimation = {        structural: isStructural,        element: element,        event: event,        addClass: options.addClass,        removeClass: options.removeClass,        close: close,        options: options,        runner: runner      };      if (hasExistingAnimation) {        var skipAnimationFlag = isAllowed('skip', element, newAnimation, existingAnimation);        if (skipAnimationFlag) {          if (existingAnimation.state === RUNNING_STATE) {            close();            return runner;          } else {            mergeAnimationDetails(element, existingAnimation, newAnimation);            return existingAnimation.runner;          }        }        var cancelAnimationFlag = isAllowed('cancel', element, newAnimation, existingAnimation);        if (cancelAnimationFlag) {          if (existingAnimation.state === RUNNING_STATE) {            // this will end the animation right away and it is safe            // to do so since the animation is already running and the            // runner callback code will run in async            existingAnimation.runner.end();          } else if (existingAnimation.structural) {            // this means that the animation is queued into a digest, but            // hasn't started yet. Therefore it is safe to run the close            // method which will call the runner methods in async.            existingAnimation.close();          } else {            // this will merge the new animation options into existing animation options            mergeAnimationDetails(element, existingAnimation, newAnimation);            return existingAnimation.runner;          }        } else {          // a joined animation means that this animation will take over the existing one          // so an example would involve a leave animation taking over an enter. Then when          // the postDigest kicks in the enter will be ignored.          var joinAnimationFlag = isAllowed('join', element, newAnimation, existingAnimation);          if (joinAnimationFlag) {            if (existingAnimation.state === RUNNING_STATE) {              normalizeAnimationDetails(element, newAnimation);            } else {              applyGeneratedPreparationClasses(element, isStructural ? event : null, options);              event = newAnimation.event = existingAnimation.event;              options = mergeAnimationDetails(element, existingAnimation, newAnimation);              //we return the same runner since only the option values of this animation will              //be fed into the `existingAnimation`.              return existingAnimation.runner;            }          }        }      } else {        // normalization in this case means that it removes redundant CSS classes that        // already exist (addClass) or do not exist (removeClass) on the element        normalizeAnimationDetails(element, newAnimation);      }      // when the options are merged and cleaned up we may end up not having to do      // an animation at all, therefore we should check this before issuing a post      // digest callback. Structural animations will always run no matter what.      var isValidAnimation = newAnimation.structural;      if (!isValidAnimation) {        // animate (from/to) can be quickly checked first, otherwise we check if any classes are present        isValidAnimation = (newAnimation.event === 'animate' && Object.keys(newAnimation.options.to || {}).length > 0)                            || hasAnimationClasses(newAnimation);      }      if (!isValidAnimation) {        close();        clearElementAnimationState(element);        return runner;      }      // the counter keeps track of cancelled animations      var counter = (existingAnimation.counter || 0) + 1;      newAnimation.counter = counter;      markElementAnimationState(element, PRE_DIGEST_STATE, newAnimation);      $rootScope.$$postDigest(function() {        var animationDetails = activeAnimationsLookup.get(node);        var animationCancelled = !animationDetails;        animationDetails = animationDetails || {};        // if addClass/removeClass is called before something like enter then the        // registered parent element may not be present. The code below will ensure        // that a final value for parent element is obtained        var parentElement = element.parent() || [];        // animate/structural/class-based animations all have requirements. Otherwise there        // is no point in performing an animation. The parent node must also be set.        var isValidAnimation = parentElement.length > 0                                && (animationDetails.event === 'animate'                                    || animationDetails.structural                                    || hasAnimationClasses(animationDetails));        // this means that the previous animation was cancelled        // even if the follow-up animation is the same event        if (animationCancelled || animationDetails.counter !== counter || !isValidAnimation) {          // if another animation did not take over then we need          // to make sure that the domOperation and options are          // handled accordingly          if (animationCancelled) {            applyAnimationClasses(element, options);            applyAnimationStyles(element, options);          }          // if the event changed from something like enter to leave then we do          // it, otherwise if it's the same then the end result will be the same too          if (animationCancelled || (isStructural && animationDetails.event !== event)) {            options.domOperation();            runner.end();          }          // in the event that the element animation was not cancelled or a follow-up animation          // isn't allowed to animate from here then we need to clear the state of the element          // so that any future animations won't read the expired animation data.          if (!isValidAnimation) {            clearElementAnimationState(element);          }          return;        }        // this combined multiple class to addClass / removeClass into a setClass event        // so long as a structural event did not take over the animation        event = !animationDetails.structural && hasAnimationClasses(animationDetails, true)            ? 'setClass'            : animationDetails.event;        markElementAnimationState(element, RUNNING_STATE);        var realRunner = $$animation(element, event, animationDetails.options);        realRunner.done(function(status) {          close(!status);          var animationDetails = activeAnimationsLookup.get(node);          if (animationDetails && animationDetails.counter === counter) {            clearElementAnimationState(getDomNode(element));          }          notifyProgress(runner, event, 'close', {});        });        // this will update the runner's flow-control events based on        // the `realRunner` object.        runner.setHost(realRunner);        notifyProgress(runner, event, 'start', {});      });      return runner;      function notifyProgress(runner, event, phase, data) {        runInNextPostDigestOrNow(function() {          var callbacks = findCallbacks(parent, element, event);          if (callbacks.length) {            // do not optimize this call here to RAF because            // we don't know how heavy the callback code here will            // be and if this code is buffered then this can            // lead to a performance regression.            $$rAF(function() {              forEach(callbacks, function(callback) {                callback(element, phase, data);              });            });          }        });        runner.progress(event, phase, data);      }      function close(reject) { // jshint ignore:line        clearGeneratedClasses(element, options);        applyAnimationClasses(element, options);        applyAnimationStyles(element, options);        options.domOperation();        runner.complete(!reject);      }    }    function closeChildAnimations(element) {      var node = getDomNode(element);      var children = node.querySelectorAll('[' + NG_ANIMATE_ATTR_NAME + ']');      forEach(children, function(child) {        var state = parseInt(child.getAttribute(NG_ANIMATE_ATTR_NAME));        var animationDetails = activeAnimationsLookup.get(child);        if (animationDetails) {          switch (state) {            case RUNNING_STATE:              animationDetails.runner.end();              /* falls through */            case PRE_DIGEST_STATE:              activeAnimationsLookup.remove(child);              break;          }        }      });    }    function clearElementAnimationState(element) {      var node = getDomNode(element);      node.removeAttribute(NG_ANIMATE_ATTR_NAME);      activeAnimationsLookup.remove(node);    }    function isMatchingElement(nodeOrElmA, nodeOrElmB) {      return getDomNode(nodeOrElmA) === getDomNode(nodeOrElmB);    }    /**     * This fn returns false if any of the following is true:     * a) animations on any parent element are disabled, and animations on the element aren't explicitly allowed     * b) a parent element has an ongoing structural animation, and animateChildren is false     * c) the element is not a child of the body     * d) the element is not a child of the $rootElement     */    function areAnimationsAllowed(element, parentElement, event) {      var bodyElement = jqLite($document[0].body);      var bodyElementDetected = isMatchingElement(element, bodyElement) || element[0].nodeName === 'HTML';      var rootElementDetected = isMatchingElement(element, $rootElement);      var parentAnimationDetected = false;      var animateChildren;      var elementDisabled = disabledElementsLookup.get(getDomNode(element));      var parentHost = jqLite.data(element[0], NG_ANIMATE_PIN_DATA);      if (parentHost) {        parentElement = parentHost;      }      parentElement = getDomNode(parentElement);      while (parentElement) {        if (!rootElementDetected) {          // angular doesn't want to attempt to animate elements outside of the application          // therefore we need to ensure that the rootElement is an ancestor of the current element          rootElementDetected = isMatchingElement(parentElement, $rootElement);        }        if (parentElement.nodeType !== ELEMENT_NODE) {          // no point in inspecting the #document element          break;        }        var details = activeAnimationsLookup.get(parentElement) || {};        // either an enter, leave or move animation will commence        // therefore we can't allow any animations to take place        // but if a parent animation is class-based then that's ok        if (!parentAnimationDetected) {          var parentElementDisabled = disabledElementsLookup.get(parentElement);          if (parentElementDisabled === true && elementDisabled !== false) {            // disable animations if the user hasn't explicitly enabled animations on the            // current element            elementDisabled = true;            // element is disabled via parent element, no need to check anything else            break;          } else if (parentElementDisabled === false) {            elementDisabled = false;          }          parentAnimationDetected = details.structural;        }        if (isUndefined(animateChildren) || animateChildren === true) {          var value = jqLite.data(parentElement, NG_ANIMATE_CHILDREN_DATA);          if (isDefined(value)) {            animateChildren = value;          }        }        // there is no need to continue traversing at this point        if (parentAnimationDetected && animateChildren === false) break;        if (!bodyElementDetected) {          // we also need to ensure that the element is or will be a part of the body element          // otherwise it is pointless to even issue an animation to be rendered          bodyElementDetected = isMatchingElement(parentElement, bodyElement);        }        if (bodyElementDetected && rootElementDetected) {          // If both body and root have been found, any other checks are pointless,          // as no animation data should live outside the application          break;        }        if (!rootElementDetected) {          // If no rootElement is detected, check if the parentElement is pinned to another element          parentHost = jqLite.data(parentElement, NG_ANIMATE_PIN_DATA);          if (parentHost) {            // The pin target element becomes the next parent element            parentElement = getDomNode(parentHost);            continue;          }        }        parentElement = parentElement.parentNode;      }      var allowAnimation = (!parentAnimationDetected || animateChildren) && elementDisabled !== true;      return allowAnimation && rootElementDetected && bodyElementDetected;    }    function markElementAnimationState(element, state, details) {      details = details || {};      details.state = state;      var node = getDomNode(element);      node.setAttribute(NG_ANIMATE_ATTR_NAME, state);      var oldValue = activeAnimationsLookup.get(node);      var newValue = oldValue          ? extend(oldValue, details)          : details;      activeAnimationsLookup.put(node, newValue);    }  }];}];var $$AnimationProvider = ['$animateProvider', function($animateProvider) {  var NG_ANIMATE_REF_ATTR = 'ng-animate-ref';  var drivers = this.drivers = [];  var RUNNER_STORAGE_KEY = '$$animationRunner';  function setRunner(element, runner) {    element.data(RUNNER_STORAGE_KEY, runner);  }  function removeRunner(element) {    element.removeData(RUNNER_STORAGE_KEY);  }  function getRunner(element) {    return element.data(RUNNER_STORAGE_KEY);  }  this.$get = ['$$jqLite', '$rootScope', '$injector', '$$AnimateRunner', '$$HashMap', '$$rAFScheduler',       function($$jqLite,   $rootScope,   $injector,   $$AnimateRunner,   $$HashMap,   $$rAFScheduler) {    var animationQueue = [];    var applyAnimationClasses = applyAnimationClassesFactory($$jqLite);    function sortAnimations(animations) {      var tree = { children: [] };      var i, lookup = new $$HashMap();      // this is done first beforehand so that the hashmap      // is filled with a list of the elements that will be animated      for (i = 0; i < animations.length; i++) {        var animation = animations[i];        lookup.put(animation.domNode, animations[i] = {          domNode: animation.domNode,          fn: animation.fn,          children: []        });      }      for (i = 0; i < animations.length; i++) {        processNode(animations[i]);      }      return flatten(tree);      function processNode(entry) {        if (entry.processed) return entry;        entry.processed = true;        var elementNode = entry.domNode;        var parentNode = elementNode.parentNode;        lookup.put(elementNode, entry);        var parentEntry;        while (parentNode) {          parentEntry = lookup.get(parentNode);          if (parentEntry) {            if (!parentEntry.processed) {              parentEntry = processNode(parentEntry);            }            break;          }          parentNode = parentNode.parentNode;        }        (parentEntry || tree).children.push(entry);        return entry;      }      function flatten(tree) {        var result = [];        var queue = [];        var i;        for (i = 0; i < tree.children.length; i++) {          queue.push(tree.children[i]);        }        var remainingLevelEntries = queue.length;        var nextLevelEntries = 0;        var row = [];        for (i = 0; i < queue.length; i++) {          var entry = queue[i];          if (remainingLevelEntries <= 0) {            remainingLevelEntries = nextLevelEntries;            nextLevelEntries = 0;            result.push(row);            row = [];          }          row.push(entry.fn);          entry.children.forEach(function(childEntry) {            nextLevelEntries++;            queue.push(childEntry);          });          remainingLevelEntries--;        }        if (row.length) {          result.push(row);        }        return result;      }    }    // TODO(matsko): document the signature in a better way    return function(element, event, options) {      options = prepareAnimationOptions(options);      var isStructural = ['enter', 'move', 'leave'].indexOf(event) >= 0;      // there is no animation at the current moment, however      // these runner methods will get later updated with the      // methods leading into the driver's end/cancel methods      // for now they just stop the animation from starting      var runner = new $$AnimateRunner({        end: function() { close(); },        cancel: function() { close(true); }      });      if (!drivers.length) {        close();        return runner;      }      setRunner(element, runner);      var classes = mergeClasses(element.attr('class'), mergeClasses(options.addClass, options.removeClass));      var tempClasses = options.tempClasses;      if (tempClasses) {        classes += ' ' + tempClasses;        options.tempClasses = null;      }      var prepareClassName;      if (isStructural) {        prepareClassName = 'ng-' + event + PREPARE_CLASS_SUFFIX;        $$jqLite.addClass(element, prepareClassName);      }      animationQueue.push({        // this data is used by the postDigest code and passed into        // the driver step function        element: element,        classes: classes,        event: event,        structural: isStructural,        options: options,        beforeStart: beforeStart,        close: close      });      element.on('$destroy', handleDestroyedElement);      // we only want there to be one function called within the post digest      // block. This way we can group animations for all the animations that      // were apart of the same postDigest flush call.      if (animationQueue.length > 1) return runner;      $rootScope.$$postDigest(function() {        var animations = [];        forEach(animationQueue, function(entry) {          // the element was destroyed early on which removed the runner          // form its storage. This means we can't animate this element          // at all and it already has been closed due to destruction.          if (getRunner(entry.element)) {            animations.push(entry);          } else {            entry.close();          }        });        // now any future animations will be in another postDigest        animationQueue.length = 0;        var groupedAnimations = groupAnimations(animations);        var toBeSortedAnimations = [];        forEach(groupedAnimations, function(animationEntry) {          toBeSortedAnimations.push({            domNode: getDomNode(animationEntry.from ? animationEntry.from.element : animationEntry.element),            fn: function triggerAnimationStart() {              // it's important that we apply the `ng-animate` CSS class and the              // temporary classes before we do any driver invoking since these              // CSS classes may be required for proper CSS detection.              animationEntry.beforeStart();              var startAnimationFn, closeFn = animationEntry.close;              // in the event that the element was removed before the digest runs or              // during the RAF sequencing then we should not trigger the animation.              var targetElement = animationEntry.anchors                  ? (animationEntry.from.element || animationEntry.to.element)                  : animationEntry.element;              if (getRunner(targetElement)) {                var operation = invokeFirstDriver(animationEntry);                if (operation) {                  startAnimationFn = operation.start;                }              }              if (!startAnimationFn) {                closeFn();              } else {                var animationRunner = startAnimationFn();                animationRunner.done(function(status) {                  closeFn(!status);                });                updateAnimationRunners(animationEntry, animationRunner);              }            }          });        });        // we need to sort each of the animations in order of parent to child        // relationships. This ensures that the child classes are applied at the        // right time.        $$rAFScheduler(sortAnimations(toBeSortedAnimations));      });      return runner;      // TODO(matsko): change to reference nodes      function getAnchorNodes(node) {        var SELECTOR = '[' + NG_ANIMATE_REF_ATTR + ']';        var items = node.hasAttribute(NG_ANIMATE_REF_ATTR)              ? [node]              : node.querySelectorAll(SELECTOR);        var anchors = [];        forEach(items, function(node) {          var attr = node.getAttribute(NG_ANIMATE_REF_ATTR);          if (attr && attr.length) {            anchors.push(node);          }        });        return anchors;      }      function groupAnimations(animations) {        var preparedAnimations = [];        var refLookup = {};        forEach(animations, function(animation, index) {          var element = animation.element;          var node = getDomNode(element);          var event = animation.event;          var enterOrMove = ['enter', 'move'].indexOf(event) >= 0;          var anchorNodes = animation.structural ? getAnchorNodes(node) : [];          if (anchorNodes.length) {            var direction = enterOrMove ? 'to' : 'from';            forEach(anchorNodes, function(anchor) {              var key = anchor.getAttribute(NG_ANIMATE_REF_ATTR);              refLookup[key] = refLookup[key] || {};              refLookup[key][direction] = {                animationID: index,                element: jqLite(anchor)              };            });          } else {            preparedAnimations.push(animation);          }        });        var usedIndicesLookup = {};        var anchorGroups = {};        forEach(refLookup, function(operations, key) {          var from = operations.from;          var to = operations.to;          if (!from || !to) {            // only one of these is set therefore we can't have an            // anchor animation since all three pieces are required            var index = from ? from.animationID : to.animationID;            var indexKey = index.toString();            if (!usedIndicesLookup[indexKey]) {              usedIndicesLookup[indexKey] = true;              preparedAnimations.push(animations[index]);            }            return;          }          var fromAnimation = animations[from.animationID];          var toAnimation = animations[to.animationID];          var lookupKey = from.animationID.toString();          if (!anchorGroups[lookupKey]) {            var group = anchorGroups[lookupKey] = {              structural: true,              beforeStart: function() {                fromAnimation.beforeStart();                toAnimation.beforeStart();              },              close: function() {                fromAnimation.close();                toAnimation.close();              },              classes: cssClassesIntersection(fromAnimation.classes, toAnimation.classes),              from: fromAnimation,              to: toAnimation,              anchors: [] // TODO(matsko): change to reference nodes            };            // the anchor animations require that the from and to elements both have at least            // one shared CSS class which effectively marries the two elements together to use            // the same animation driver and to properly sequence the anchor animation.            if (group.classes.length) {              preparedAnimations.push(group);            } else {              preparedAnimations.push(fromAnimation);              preparedAnimations.push(toAnimation);            }          }          anchorGroups[lookupKey].anchors.push({            'out': from.element, 'in': to.element          });        });        return preparedAnimations;      }      function cssClassesIntersection(a,b) {        a = a.split(' ');        b = b.split(' ');        var matches = [];        for (var i = 0; i < a.length; i++) {          var aa = a[i];          if (aa.substring(0,3) === 'ng-') continue;          for (var j = 0; j < b.length; j++) {            if (aa === b[j]) {              matches.push(aa);              break;            }          }        }        return matches.join(' ');      }      function invokeFirstDriver(animationDetails) {        // we loop in reverse order since the more general drivers (like CSS and JS)        // may attempt more elements, but custom drivers are more particular        for (var i = drivers.length - 1; i >= 0; i--) {          var driverName = drivers[i];          if (!$injector.has(driverName)) continue; // TODO(matsko): remove this check          var factory = $injector.get(driverName);          var driver = factory(animationDetails);          if (driver) {            return driver;          }        }      }      function beforeStart() {        element.addClass(NG_ANIMATE_CLASSNAME);        if (tempClasses) {          $$jqLite.addClass(element, tempClasses);        }        if (prepareClassName) {          $$jqLite.removeClass(element, prepareClassName);          prepareClassName = null;        }      }      function updateAnimationRunners(animation, newRunner) {        if (animation.from && animation.to) {          update(animation.from.element);          update(animation.to.element);        } else {          update(animation.element);        }        function update(element) {          getRunner(element).setHost(newRunner);        }      }      function handleDestroyedElement() {        var runner = getRunner(element);        if (runner && (event !== 'leave' || !options.$$domOperationFired)) {          runner.end();        }      }      function close(rejected) { // jshint ignore:line        element.off('$destroy', handleDestroyedElement);        removeRunner(element);        applyAnimationClasses(element, options);        applyAnimationStyles(element, options);        options.domOperation();        if (tempClasses) {          $$jqLite.removeClass(element, tempClasses);        }        element.removeClass(NG_ANIMATE_CLASSNAME);        runner.complete(!rejected);      }    };  }];}];/** * @ngdoc directive * @name ngAnimateSwap * @restrict A * @scope * * @description * * ngAnimateSwap is a animation-oriented directive that allows for the container to * be removed and entered in whenever the associated expression changes. A * common usecase for this directive is a rotating banner or slider component which * contains one image being present at a time. When the active image changes * then the old image will perform a `leave` animation and the new element * will be inserted via an `enter` animation. * * @animations * | Animation                        | Occurs                               | * |----------------------------------|--------------------------------------| * | {@link ng.$animate#enter enter}  | when the new element is inserted to the DOM  | * | {@link ng.$animate#leave leave}  | when the old element is removed from the DOM | * * @example * <example name="ngAnimateSwap-directive" module="ngAnimateSwapExample" *          deps="angular-animate.js" *          animations="true" fixBase="true"> *   <file name="index.html"> *     <div class="container" ng-controller="AppCtrl"> *       <div ng-animate-swap="number" class="cell swap-animation" ng-class="colorClass(number)"> *         {{ number }} *       </div> *     </div> *   </file> *   <file name="script.js"> *     angular.module('ngAnimateSwapExample', ['ngAnimate']) *       .controller('AppCtrl', ['$scope', '$interval', function($scope, $interval) { *         $scope.number = 0; *         $interval(function() { *           $scope.number++; *         }, 1000); * *         var colors = ['red','blue','green','yellow','orange']; *         $scope.colorClass = function(number) { *           return colors[number % colors.length]; *         }; *       }]); *   </file> *  <file name="animations.css"> *  .container { *    height:250px; *    width:250px; *    position:relative; *    overflow:hidden; *    border:2px solid black; *  } *  .container .cell { *    font-size:150px; *    text-align:center; *    line-height:250px; *    position:absolute; *    top:0; *    left:0; *    right:0; *    border-bottom:2px solid black; *  } *  .swap-animation.ng-enter, .swap-animation.ng-leave { *    transition:0.5s linear all; *  } *  .swap-animation.ng-enter { *    top:-250px; *  } *  .swap-animation.ng-enter-active { *    top:0px; *  } *  .swap-animation.ng-leave { *    top:0px; *  } *  .swap-animation.ng-leave-active { *    top:250px; *  } *  .red { background:red; } *  .green { background:green; } *  .blue { background:blue; } *  .yellow { background:yellow; } *  .orange { background:orange; } *  </file> * </example> */var ngAnimateSwapDirective = ['$animate', '$rootScope', function($animate, $rootScope) {  return {    restrict: 'A',    transclude: 'element',    terminal: true,    priority: 600, // we use 600 here to ensure that the directive is caught before others    link: function(scope, $element, attrs, ctrl, $transclude) {      var previousElement, previousScope;      scope.$watchCollection(attrs.ngAnimateSwap || attrs['for'], function(value) {        if (previousElement) {          $animate.leave(previousElement);        }        if (previousScope) {          previousScope.$destroy();          previousScope = null;        }        if (value || value === 0) {          previousScope = scope.$new();          $transclude(previousScope, function(element) {            previousElement = element;            $animate.enter(element, null, $element);          });        }      });    }  };}];/* global angularAnimateModule: true,   ngAnimateSwapDirective,   $$AnimateAsyncRunFactory,   $$rAFSchedulerFactory,   $$AnimateChildrenDirective,   $$AnimateQueueProvider,   $$AnimationProvider,   $AnimateCssProvider,   $$AnimateCssDriverProvider,   $$AnimateJsProvider,   $$AnimateJsDriverProvider,*//** * @ngdoc module * @name ngAnimate * @description * * The `ngAnimate` module provides support for CSS-based animations (keyframes and transitions) as well as JavaScript-based animations via * callback hooks. Animations are not enabled by default, however, by including `ngAnimate` the animation hooks are enabled for an Angular app. * * <div doc-module-components="ngAnimate"></div> * * # Usage * Simply put, there are two ways to make use of animations when ngAnimate is used: by using **CSS** and **JavaScript**. The former works purely based * using CSS (by using matching CSS selectors/styles) and the latter triggers animations that are registered via `module.animation()`. For * both CSS and JS animations the sole requirement is to have a matching `CSS class` that exists both in the registered animation and within * the HTML element that the animation will be triggered on. * * ## Directive Support * The following directives are "animation aware": * * | Directive                                                                                                | Supported Animations                                                     | * |----------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------| * | {@link ng.directive:ngRepeat#animations ngRepeat}                                                        | enter, leave and move                                                    | * | {@link ngRoute.directive:ngView#animations ngView}                                                       | enter and leave                                                          | * | {@link ng.directive:ngInclude#animations ngInclude}                                                      | enter and leave                                                          | * | {@link ng.directive:ngSwitch#animations ngSwitch}                                                        | enter and leave                                                          | * | {@link ng.directive:ngIf#animations ngIf}                                                                | enter and leave                                                          | * | {@link ng.directive:ngClass#animations ngClass}                                                          | add and remove (the CSS class(es) present)                               | * | {@link ng.directive:ngShow#animations ngShow} & {@link ng.directive:ngHide#animations ngHide}            | add and remove (the ng-hide class value)                                 | * | {@link ng.directive:form#animation-hooks form} & {@link ng.directive:ngModel#animation-hooks ngModel}    | add and remove (dirty, pristine, valid, invalid & all other validations) | * | {@link module:ngMessages#animations ngMessages}                                                          | add and remove (ng-active & ng-inactive)                                 | * | {@link module:ngMessages#animations ngMessage}                                                           | enter and leave                                                          | * * (More information can be found by visiting each the documentation associated with each directive.) * * ## CSS-based Animations * * CSS-based animations with ngAnimate are unique since they require no JavaScript code at all. By using a CSS class that we reference between our HTML * and CSS code we can create an animation that will be picked up by Angular when an the underlying directive performs an operation. * * The example below shows how an `enter` animation can be made possible on an element using `ng-if`: * * ```html * <div ng-if="bool" class="fade"> *    Fade me in out * </div> * <button ng-click="bool=true">Fade In!</button> * <button ng-click="bool=false">Fade Out!</button> * ``` * * Notice the CSS class **fade**? We can now create the CSS transition code that references this class: * * ```css * /* The starting CSS styles for the enter animation */ * .fade.ng-enter { *   transition:0.5s linear all; *   opacity:0; * } * * /* The finishing CSS styles for the enter animation */ * .fade.ng-enter.ng-enter-active { *   opacity:1; * } * ``` * * The key thing to remember here is that, depending on the animation event (which each of the directives above trigger depending on what's going on) two * generated CSS classes will be applied to the element; in the example above we have `.ng-enter` and `.ng-enter-active`. For CSS transitions, the transition * code **must** be defined within the starting CSS class (in this case `.ng-enter`). The destination class is what the transition will animate towards. * * If for example we wanted to create animations for `leave` and `move` (ngRepeat triggers move) then we can do so using the same CSS naming conventions: * * ```css * /* now the element will fade out before it is removed from the DOM */ * .fade.ng-leave { *   transition:0.5s linear all; *   opacity:1; * } * .fade.ng-leave.ng-leave-active { *   opacity:0; * } * ``` * * We can also make use of **CSS Keyframes** by referencing the keyframe animation within the starting CSS class: * * ```css * /* there is no need to define anything inside of the destination * CSS class since the keyframe will take charge of the animation */ * .fade.ng-leave { *   animation: my_fade_animation 0.5s linear; *   -webkit-animation: my_fade_animation 0.5s linear; * } * * @keyframes my_fade_animation { *   from { opacity:1; } *   to { opacity:0; } * } * * @-webkit-keyframes my_fade_animation { *   from { opacity:1; } *   to { opacity:0; } * } * ``` * * Feel free also mix transitions and keyframes together as well as any other CSS classes on the same element. * * ### CSS Class-based Animations * * Class-based animations (animations that are triggered via `ngClass`, `ngShow`, `ngHide` and some other directives) have a slightly different * naming convention. Class-based animations are basic enough that a standard transition or keyframe can be referenced on the class being added * and removed. * * For example if we wanted to do a CSS animation for `ngHide` then we place an animation on the `.ng-hide` CSS class: * * ```html * <div ng-show="bool" class="fade"> *   Show and hide me * </div> * <button ng-click="bool=true">Toggle</button> * * <style> * .fade.ng-hide { *   transition:0.5s linear all; *   opacity:0; * } * </style> * ``` * * All that is going on here with ngShow/ngHide behind the scenes is the `.ng-hide` class is added/removed (when the hidden state is valid). Since * ngShow and ngHide are animation aware then we can match up a transition and ngAnimate handles the rest. * * In addition the addition and removal of the CSS class, ngAnimate also provides two helper methods that we can use to further decorate the animation * with CSS styles. * * ```html * <div ng-class="{on:onOff}" class="highlight"> *   Highlight this box * </div> * <button ng-click="onOff=!onOff">Toggle</button> * * <style> * .highlight { *   transition:0.5s linear all; * } * .highlight.on-add { *   background:white; * } * .highlight.on { *   background:yellow; * } * .highlight.on-remove { *   background:black; * } * </style> * ``` * * We can also make use of CSS keyframes by placing them within the CSS classes. * * * ### CSS Staggering Animations * A Staggering animation is a collection of animations that are issued with a slight delay in between each successive operation resulting in a * curtain-like effect. The ngAnimate module (versions >=1.2) supports staggering animations and the stagger effect can be * performed by creating a **ng-EVENT-stagger** CSS class and attaching that class to the base CSS class used for * the animation. The style property expected within the stagger class can either be a **transition-delay** or an * **animation-delay** property (or both if your animation contains both transitions and keyframe animations). * * ```css * .my-animation.ng-enter { *   /* standard transition code */ *   transition: 1s linear all; *   opacity:0; * } * .my-animation.ng-enter-stagger { *   /* this will have a 100ms delay between each successive leave animation */ *   transition-delay: 0.1s; * *   /* As of 1.4.4, this must always be set: it signals ngAnimate *     to not accidentally inherit a delay property from another CSS class */ *   transition-duration: 0s; * } * .my-animation.ng-enter.ng-enter-active { *   /* standard transition styles */ *   opacity:1; * } * ``` * * Staggering animations work by default in ngRepeat (so long as the CSS class is defined). Outside of ngRepeat, to use staggering animations * on your own, they can be triggered by firing multiple calls to the same event on $animate. However, the restrictions surrounding this * are that each of the elements must have the same CSS className value as well as the same parent element. A stagger operation * will also be reset if one or more animation frames have passed since the multiple calls to `$animate` were fired. * * The following code will issue the **ng-leave-stagger** event on the element provided: * * ```js * var kids = parent.children(); * * $animate.leave(kids[0]); //stagger index=0 * $animate.leave(kids[1]); //stagger index=1 * $animate.leave(kids[2]); //stagger index=2 * $animate.leave(kids[3]); //stagger index=3 * $animate.leave(kids[4]); //stagger index=4 * * window.requestAnimationFrame(function() { *   //stagger has reset itself *   $animate.leave(kids[5]); //stagger index=0 *   $animate.leave(kids[6]); //stagger index=1 * *   $scope.$digest(); * }); * ``` * * Stagger animations are currently only supported within CSS-defined animations. * * ### The `ng-animate` CSS class * * When ngAnimate is animating an element it will apply the `ng-animate` CSS class to the element for the duration of the animation. * This is a temporary CSS class and it will be removed once the animation is over (for both JavaScript and CSS-based animations). * * Therefore, animations can be applied to an element using this temporary class directly via CSS. * * ```css * .zipper.ng-animate { *   transition:0.5s linear all; * } * .zipper.ng-enter { *   opacity:0; * } * .zipper.ng-enter.ng-enter-active { *   opacity:1; * } * .zipper.ng-leave { *   opacity:1; * } * .zipper.ng-leave.ng-leave-active { *   opacity:0; * } * ``` * * (Note that the `ng-animate` CSS class is reserved and it cannot be applied on an element directly since ngAnimate will always remove * the CSS class once an animation has completed.) * * * ### The `ng-[event]-prepare` class * * This is a special class that can be used to prevent unwanted flickering / flash of content before * the actual animation starts. The class is added as soon as an animation is initialized, but removed * before the actual animation starts (after waiting for a $digest). * It is also only added for *structural* animations (`enter`, `move`, and `leave`). * * In practice, flickering can appear when nesting elements with structural animations such as `ngIf` * into elements that have class-based animations such as `ngClass`. * * ```html * <div ng-class="{red: myProp}"> *   <div ng-class="{blue: myProp}"> *     <div class="message" ng-if="myProp"></div> *   </div> * </div> * ``` * * It is possible that during the `enter` animation, the `.message` div will be briefly visible before it starts animating. * In that case, you can add styles to the CSS that make sure the element stays hidden before the animation starts: * * ```css * .message.ng-enter-prepare { *   opacity: 0; * } * * ``` * * ## JavaScript-based Animations * * ngAnimate also allows for animations to be consumed by JavaScript code. The approach is similar to CSS-based animations (where there is a shared * CSS class that is referenced in our HTML code) but in addition we need to register the JavaScript animation on the module. By making use of the * `module.animation()` module function we can register the animation. * * Let's see an example of a enter/leave animation using `ngRepeat`: * * ```html * <div ng-repeat="item in items" class="slide"> *   {{ item }} * </div> * ``` * * See the **slide** CSS class? Let's use that class to define an animation that we'll structure in our module code by using `module.animation`: * * ```js * myModule.animation('.slide', [function() { *   return { *     // make note that other events (like addClass/removeClass) *     // have different function input parameters *     enter: function(element, doneFn) { *       jQuery(element).fadeIn(1000, doneFn); * *       // remember to call doneFn so that angular *       // knows that the animation has concluded *     }, * *     move: function(element, doneFn) { *       jQuery(element).fadeIn(1000, doneFn); *     }, * *     leave: function(element, doneFn) { *       jQuery(element).fadeOut(1000, doneFn); *     } *   } * }]); * ``` * * The nice thing about JS-based animations is that we can inject other services and make use of advanced animation libraries such as * greensock.js and velocity.js. * * If our animation code class-based (meaning that something like `ngClass`, `ngHide` and `ngShow` triggers it) then we can still define * our animations inside of the same registered animation, however, the function input arguments are a bit different: * * ```html * <div ng-class="color" class="colorful"> *   this box is moody * </div> * <button ng-click="color='red'">Change to red</button> * <button ng-click="color='blue'">Change to blue</button> * <button ng-click="color='green'">Change to green</button> * ``` * * ```js * myModule.animation('.colorful', [function() { *   return { *     addClass: function(element, className, doneFn) { *       // do some cool animation and call the doneFn *     }, *     removeClass: function(element, className, doneFn) { *       // do some cool animation and call the doneFn *     }, *     setClass: function(element, addedClass, removedClass, doneFn) { *       // do some cool animation and call the doneFn *     } *   } * }]); * ``` * * ## CSS + JS Animations Together * * AngularJS 1.4 and higher has taken steps to make the amalgamation of CSS and JS animations more flexible. However, unlike earlier versions of Angular, * defining CSS and JS animations to work off of the same CSS class will not work anymore. Therefore the example below will only result in **JS animations taking * charge of the animation**: * * ```html * <div ng-if="bool" class="slide"> *   Slide in and out * </div> * ``` * * ```js * myModule.animation('.slide', [function() { *   return { *     enter: function(element, doneFn) { *       jQuery(element).slideIn(1000, doneFn); *     } *   } * }]); * ``` * * ```css * .slide.ng-enter { *   transition:0.5s linear all; *   transform:translateY(-100px); * } * .slide.ng-enter.ng-enter-active { *   transform:translateY(0); * } * ``` * * Does this mean that CSS and JS animations cannot be used together? Do JS-based animations always have higher priority? We can make up for the * lack of CSS animations by using the `$animateCss` service to trigger our own tweaked-out, CSS-based animations directly from * our own JS-based animation code: * * ```js * myModule.animation('.slide', ['$animateCss', function($animateCss) { *   return { *     enter: function(element) {*        // this will trigger `.slide.ng-enter` and `.slide.ng-enter-active`. *       return $animateCss(element, { *         event: 'enter', *         structural: true *       }); *     } *   } * }]); * ``` * * The nice thing here is that we can save bandwidth by sticking to our CSS-based animation code and we don't need to rely on a 3rd-party animation framework. * * The `$animateCss` service is very powerful since we can feed in all kinds of extra properties that will be evaluated and fed into a CSS transition or * keyframe animation. For example if we wanted to animate the height of an element while adding and removing classes then we can do so by providing that * data into `$animateCss` directly: * * ```js * myModule.animation('.slide', ['$animateCss', function($animateCss) { *   return { *     enter: function(element) { *       return $animateCss(element, { *         event: 'enter', *         structural: true, *         addClass: 'maroon-setting', *         from: { height:0 }, *         to: { height: 200 } *       }); *     } *   } * }]); * ``` * * Now we can fill in the rest via our transition CSS code: * * ```css * /* the transition tells ngAnimate to make the animation happen */ * .slide.ng-enter { transition:0.5s linear all; } * * /* this extra CSS class will be absorbed into the transition * since the $animateCss code is adding the class */ * .maroon-setting { background:red; } * ``` * * And `$animateCss` will figure out the rest. Just make sure to have the `done()` callback fire the `doneFn` function to signal when the animation is over. * * To learn more about what's possible be sure to visit the {@link ngAnimate.$animateCss $animateCss service}. * * ## Animation Anchoring (via `ng-animate-ref`) * * ngAnimate in AngularJS 1.4 comes packed with the ability to cross-animate elements between * structural areas of an application (like views) by pairing up elements using an attribute * called `ng-animate-ref`. * * Let's say for example we have two views that are managed by `ng-view` and we want to show * that there is a relationship between two components situated in within these views. By using the * `ng-animate-ref` attribute we can identify that the two components are paired together and we * can then attach an animation, which is triggered when the view changes. * * Say for example we have the following template code: * * ```html * <!-- index.html --> * <div ng-view class="view-animation"> * </div> * * <!-- home.html --> * <a href="#/banner-page"> *   <img src="./banner.jpg" class="banner" ng-animate-ref="banner"> * </a> * * <!-- banner-page.html --> * <img src="./banner.jpg" class="banner" ng-animate-ref="banner"> * ``` * * Now, when the view changes (once the link is clicked), ngAnimate will examine the * HTML contents to see if there is a match reference between any components in the view * that is leaving and the view that is entering. It will scan both the view which is being * removed (leave) and inserted (enter) to see if there are any paired DOM elements that * contain a matching ref value. * * The two images match since they share the same ref value. ngAnimate will now create a * transport element (which is a clone of the first image element) and it will then attempt * to animate to the position of the second image element in the next view. For the animation to * work a special CSS class called `ng-anchor` will be added to the transported element. * * We can now attach a transition onto the `.banner.ng-anchor` CSS class and then * ngAnimate will handle the entire transition for us as well as the addition and removal of * any changes of CSS classes between the elements: * * ```css * .banner.ng-anchor { *   /* this animation will last for 1 second since there are *          two phases to the animation (an `in` and an `out` phase) */ *   transition:0.5s linear all; * } * ``` * * We also **must** include animations for the views that are being entered and removed * (otherwise anchoring wouldn't be possible since the new view would be inserted right away). * * ```css * .view-animation.ng-enter, .view-animation.ng-leave { *   transition:0.5s linear all; *   position:fixed; *   left:0; *   top:0; *   width:100%; * } * .view-animation.ng-enter { *   transform:translateX(100%); * } * .view-animation.ng-leave, * .view-animation.ng-enter.ng-enter-active { *   transform:translateX(0%); * } * .view-animation.ng-leave.ng-leave-active { *   transform:translateX(-100%); * } * ``` * * Now we can jump back to the anchor animation. When the animation happens, there are two stages that occur: * an `out` and an `in` stage. The `out` stage happens first and that is when the element is animated away * from its origin. Once that animation is over then the `in` stage occurs which animates the * element to its destination. The reason why there are two animations is to give enough time * for the enter animation on the new element to be ready. * * The example above sets up a transition for both the in and out phases, but we can also target the out or * in phases directly via `ng-anchor-out` and `ng-anchor-in`. * * ```css * .banner.ng-anchor-out { *   transition: 0.5s linear all; * *   /* the scale will be applied during the out animation, *          but will be animated away when the in animation runs */ *   transform: scale(1.2); * } * * .banner.ng-anchor-in { *   transition: 1s linear all; * } * ``` * * * * * ### Anchoring Demo *  <example module="anchoringExample"           name="anchoringExample"           id="anchoringExample"           deps="angular-animate.js;angular-route.js"           animations="true">    <file name="index.html">      <a href="#/">Home</a>      <hr />      <div class="view-container">        <div ng-view class="view"></div>      </div>    </file>    <file name="script.js">      angular.module('anchoringExample', ['ngAnimate', 'ngRoute'])        .config(['$routeProvider', function($routeProvider) {          $routeProvider.when('/', {            templateUrl: 'home.html',            controller: 'HomeController as home'          });          $routeProvider.when('/profile/:id', {            templateUrl: 'profile.html',            controller: 'ProfileController as profile'          });        }])        .run(['$rootScope', function($rootScope) {          $rootScope.records = [            { id:1, title: "Miss Beulah Roob" },            { id:2, title: "Trent Morissette" },            { id:3, title: "Miss Ava Pouros" },            { id:4, title: "Rod Pouros" },            { id:5, title: "Abdul Rice" },            { id:6, title: "Laurie Rutherford Sr." },            { id:7, title: "Nakia McLaughlin" },            { id:8, title: "Jordon Blanda DVM" },            { id:9, title: "Rhoda Hand" },            { id:10, title: "Alexandrea Sauer" }          ];        }])        .controller('HomeController', [function() {          //empty        }])        .controller('ProfileController', ['$rootScope', '$routeParams', function($rootScope, $routeParams) {          var index = parseInt($routeParams.id, 10);          var record = $rootScope.records[index - 1];          this.title = record.title;          this.id = record.id;        }]);    </file>    <file name="home.html">      <h2>Welcome to the home page</h1>      <p>Please click on an element</p>      <a class="record"         ng-href="#/profile/{{ record.id }}"         ng-animate-ref="{{ record.id }}"         ng-repeat="record in records">        {{ record.title }}      </a>    </file>    <file name="profile.html">      <div class="profile record" ng-animate-ref="{{ profile.id }}">        {{ profile.title }}      </div>    </file>    <file name="animations.css">      .record {        display:block;        font-size:20px;      }      .profile {        background:black;        color:white;        font-size:100px;      }      .view-container {        position:relative;      }      .view-container > .view.ng-animate {        position:absolute;        top:0;        left:0;        width:100%;        min-height:500px;      }      .view.ng-enter, .view.ng-leave,      .record.ng-anchor {        transition:0.5s linear all;      }      .view.ng-enter {        transform:translateX(100%);      }      .view.ng-enter.ng-enter-active, .view.ng-leave {        transform:translateX(0%);      }      .view.ng-leave.ng-leave-active {        transform:translateX(-100%);      }      .record.ng-anchor-out {        background:red;      }    </file>  </example> * * ### How is the element transported? * * When an anchor animation occurs, ngAnimate will clone the starting element and position it exactly where the starting * element is located on screen via absolute positioning. The cloned element will be placed inside of the root element * of the application (where ng-app was defined) and all of the CSS classes of the starting element will be applied. The * element will then animate into the `out` and `in` animations and will eventually reach the coordinates and match * the dimensions of the destination element. During the entire animation a CSS class of `.ng-animate-shim` will be applied * to both the starting and destination elements in order to hide them from being visible (the CSS styling for the class * is: `visibility:hidden`). Once the anchor reaches its destination then it will be removed and the destination element * will become visible since the shim class will be removed. * * ### How is the morphing handled? * * CSS Anchoring relies on transitions and keyframes and the internal code is intelligent enough to figure out * what CSS classes differ between the starting element and the destination element. These different CSS classes * will be added/removed on the anchor element and a transition will be applied (the transition that is provided * in the anchor class). Long story short, ngAnimate will figure out what classes to add and remove which will * make the transition of the element as smooth and automatic as possible. Be sure to use simple CSS classes that * do not rely on DOM nesting structure so that the anchor element appears the same as the starting element (since * the cloned element is placed inside of root element which is likely close to the body element). * * Note that if the root element is on the `<html>` element then the cloned node will be placed inside of body. * * * ## Using $animate in your directive code * * So far we've explored how to feed in animations into an Angular application, but how do we trigger animations within our own directives in our application? * By injecting the `$animate` service into our directive code, we can trigger structural and class-based hooks which can then be consumed by animations. Let's * imagine we have a greeting box that shows and hides itself when the data changes * * ```html * <greeting-box active="onOrOff">Hi there</greeting-box> * ``` * * ```js * ngModule.directive('greetingBox', ['$animate', function($animate) { *   return function(scope, element, attrs) { *     attrs.$observe('active', function(value) { *       value ? $animate.addClass(element, 'on') : $animate.removeClass(element, 'on'); *     }); *   }); * }]); * ``` * * Now the `on` CSS class is added and removed on the greeting box component. Now if we add a CSS class on top of the greeting box element * in our HTML code then we can trigger a CSS or JS animation to happen. * * ```css * /* normally we would create a CSS class to reference on the element */ * greeting-box.on { transition:0.5s linear all; background:green; color:white; } * ``` * * The `$animate` service contains a variety of other methods like `enter`, `leave`, `animate` and `setClass`. To learn more about what's * possible be sure to visit the {@link ng.$animate $animate service API page}. * * * ## Callbacks and Promises * * When `$animate` is called it returns a promise that can be used to capture when the animation has ended. Therefore if we were to trigger * an animation (within our directive code) then we can continue performing directive and scope related activities after the animation has * ended by chaining onto the returned promise that animation method returns. * * ```js * // somewhere within the depths of the directive * $animate.enter(element, parent).then(function() { *   //the animation has completed * }); * ``` * * (Note that earlier versions of Angular prior to v1.4 required the promise code to be wrapped using `$scope.$apply(...)`. This is not the case * anymore.) * * In addition to the animation promise, we can also make use of animation-related callbacks within our directives and controller code by registering * an event listener using the `$animate` service. Let's say for example that an animation was triggered on our view * routing controller to hook into that: * * ```js * ngModule.controller('HomePageController', ['$animate', function($animate) { *   $animate.on('enter', ngViewElement, function(element) { *     // the animation for this route has completed *   }]); * }]) * ``` * * (Note that you will need to trigger a digest within the callback to get angular to notice any scope-related changes.) *//** * @ngdoc service * @name $animate * @kind object * * @description * The ngAnimate `$animate` service documentation is the same for the core `$animate` service. * * Click here {@link ng.$animate to learn more about animations with `$animate`}. */angular.module('ngAnimate', [])  .directive('ngAnimateSwap', ngAnimateSwapDirective)  .directive('ngAnimateChildren', $$AnimateChildrenDirective)  .factory('$$rAFScheduler', $$rAFSchedulerFactory)  .provider('$$animateQueue', $$AnimateQueueProvider)  .provider('$$animation', $$AnimationProvider)  .provider('$animateCss', $AnimateCssProvider)  .provider('$$animateCssDriver', $$AnimateCssDriverProvider)  .provider('$$animateJs', $$AnimateJsProvider)  .provider('$$animateJsDriver', $$AnimateJsDriverProvider);})(window, window.angular);
 |