| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900 | /* * angular-ui-bootstrap * http://angular-ui.github.io/bootstrap/ * Version: 0.12.1 - 2015-02-20 * License: MIT */angular.module("ui.bootstrap", ["ui.bootstrap.transition","ui.bootstrap.collapse","ui.bootstrap.accordion","ui.bootstrap.alert","ui.bootstrap.bindHtml","ui.bootstrap.buttons","ui.bootstrap.carousel","ui.bootstrap.dateparser","ui.bootstrap.position","ui.bootstrap.datepicker","ui.bootstrap.dropdown","ui.bootstrap.modal","ui.bootstrap.pagination","ui.bootstrap.tooltip","ui.bootstrap.popover","ui.bootstrap.progressbar","ui.bootstrap.rating","ui.bootstrap.tabs","ui.bootstrap.timepicker","ui.bootstrap.typeahead"]);angular.module('ui.bootstrap.transition', [])/** * $transition service provides a consistent interface to trigger CSS 3 transitions and to be informed when they complete. * @param  {DOMElement} element  The DOMElement that will be animated. * @param  {string|object|function} trigger  The thing that will cause the transition to start: *   - As a string, it represents the css class to be added to the element. *   - As an object, it represents a hash of style attributes to be applied to the element. *   - As a function, it represents a function to be called that will cause the transition to occur. * @return {Promise}  A promise that is resolved when the transition finishes. */.factory('$transition', ['$q', '$timeout', '$rootScope', function($q, $timeout, $rootScope) {  var $transition = function(element, trigger, options) {    options = options || {};    var deferred = $q.defer();    var endEventName = $transition[options.animation ? 'animationEndEventName' : 'transitionEndEventName'];    var transitionEndHandler = function(event) {      $rootScope.$apply(function() {        element.unbind(endEventName, transitionEndHandler);        deferred.resolve(element);      });    };    if (endEventName) {      element.bind(endEventName, transitionEndHandler);    }    // Wrap in a timeout to allow the browser time to update the DOM before the transition is to occur    $timeout(function() {      if ( angular.isString(trigger) ) {        element.addClass(trigger);      } else if ( angular.isFunction(trigger) ) {        trigger(element);      } else if ( angular.isObject(trigger) ) {        element.css(trigger);      }      //If browser does not support transitions, instantly resolve      if ( !endEventName ) {        deferred.resolve(element);      }    });    // Add our custom cancel function to the promise that is returned    // We can call this if we are about to run a new transition, which we know will prevent this transition from ending,    // i.e. it will therefore never raise a transitionEnd event for that transition    deferred.promise.cancel = function() {      if ( endEventName ) {        element.unbind(endEventName, transitionEndHandler);      }      deferred.reject('Transition cancelled');    };    return deferred.promise;  };  // Work out the name of the transitionEnd event  var transElement = document.createElement('trans');  var transitionEndEventNames = {    'WebkitTransition': 'webkitTransitionEnd',    'MozTransition': 'transitionend',    'OTransition': 'oTransitionEnd',    'transition': 'transitionend'  };  var animationEndEventNames = {    'WebkitTransition': 'webkitAnimationEnd',    'MozTransition': 'animationend',    'OTransition': 'oAnimationEnd',    'transition': 'animationend'  };  function findEndEventName(endEventNames) {    for (var name in endEventNames){      if (transElement.style[name] !== undefined) {        return endEventNames[name];      }    }  }  $transition.transitionEndEventName = findEndEventName(transitionEndEventNames);  $transition.animationEndEventName = findEndEventName(animationEndEventNames);  return $transition;}]);angular.module('ui.bootstrap.collapse', ['ui.bootstrap.transition'])  .directive('collapse', ['$transition', function ($transition) {    return {      link: function (scope, element, attrs) {        var initialAnimSkip = true;        var currentTransition;        function doTransition(change) {          var newTransition = $transition(element, change);          if (currentTransition) {            currentTransition.cancel();          }          currentTransition = newTransition;          newTransition.then(newTransitionDone, newTransitionDone);          return newTransition;          function newTransitionDone() {            // Make sure it's this transition, otherwise, leave it alone.            if (currentTransition === newTransition) {              currentTransition = undefined;            }          }        }        function expand() {          if (initialAnimSkip) {            initialAnimSkip = false;            expandDone();          } else {            element.removeClass('collapse').addClass('collapsing');            doTransition({ height: element[0].scrollHeight + 'px' }).then(expandDone);          }        }        function expandDone() {          element.removeClass('collapsing');          element.addClass('collapse in');          element.css({height: 'auto'});        }        function collapse() {          if (initialAnimSkip) {            initialAnimSkip = false;            collapseDone();            element.css({height: 0});          } else {            // CSS transitions don't work with height: auto, so we have to manually change the height to a specific value            element.css({ height: element[0].scrollHeight + 'px' });            //trigger reflow so a browser realizes that height was updated from auto to a specific value            var x = element[0].offsetWidth;            element.removeClass('collapse in').addClass('collapsing');            doTransition({ height: 0 }).then(collapseDone);          }        }        function collapseDone() {          element.removeClass('collapsing');          element.addClass('collapse');        }        scope.$watch(attrs.collapse, function (shouldCollapse) {          if (shouldCollapse) {            collapse();          } else {            expand();          }        });      }    };  }]);angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse']).constant('accordionConfig', {  closeOthers: true}).controller('AccordionController', ['$scope', '$attrs', 'accordionConfig', function ($scope, $attrs, accordionConfig) {  // This array keeps track of the accordion groups  this.groups = [];  // Ensure that all the groups in this accordion are closed, unless close-others explicitly says not to  this.closeOthers = function(openGroup) {    var closeOthers = angular.isDefined($attrs.closeOthers) ? $scope.$eval($attrs.closeOthers) : accordionConfig.closeOthers;    if ( closeOthers ) {      angular.forEach(this.groups, function (group) {        if ( group !== openGroup ) {          group.isOpen = false;        }      });    }  };  // This is called from the accordion-group directive to add itself to the accordion  this.addGroup = function(groupScope) {    var that = this;    this.groups.push(groupScope);    groupScope.$on('$destroy', function (event) {      that.removeGroup(groupScope);    });  };  // This is called from the accordion-group directive when to remove itself  this.removeGroup = function(group) {    var index = this.groups.indexOf(group);    if ( index !== -1 ) {      this.groups.splice(index, 1);    }  };}])// The accordion directive simply sets up the directive controller// and adds an accordion CSS class to itself element..directive('accordion', function () {  return {    restrict:'EA',    controller:'AccordionController',    transclude: true,    replace: false,    templateUrl: 'template/accordion/accordion.html'  };})// The accordion-group directive indicates a block of html that will expand and collapse in an accordion.directive('accordionGroup', function() {  return {    require:'^accordion',         // We need this directive to be inside an accordion    restrict:'EA',    transclude:true,              // It transcludes the contents of the directive into the template    replace: true,                // The element containing the directive will be replaced with the template    templateUrl:'template/accordion/accordion-group.html',    scope: {      heading: '@',               // Interpolate the heading attribute onto this scope      isOpen: '=?',      isDisabled: '=?'    },    controller: function() {      this.setHeading = function(element) {        this.heading = element;      };    },    link: function(scope, element, attrs, accordionCtrl) {      accordionCtrl.addGroup(scope);      scope.$watch('isOpen', function(value) {        if ( value ) {          accordionCtrl.closeOthers(scope);        }      });      scope.toggleOpen = function() {        if ( !scope.isDisabled ) {          scope.isOpen = !scope.isOpen;        }      };    }  };})// Use accordion-heading below an accordion-group to provide a heading containing HTML// <accordion-group>//   <accordion-heading>Heading containing HTML - <img src="..."></accordion-heading>// </accordion-group>.directive('accordionHeading', function() {  return {    restrict: 'EA',    transclude: true,   // Grab the contents to be used as the heading    template: '',       // In effect remove this element!    replace: true,    require: '^accordionGroup',    link: function(scope, element, attr, accordionGroupCtrl, transclude) {      // Pass the heading to the accordion-group controller      // so that it can be transcluded into the right place in the template      // [The second parameter to transclude causes the elements to be cloned so that they work in ng-repeat]      accordionGroupCtrl.setHeading(transclude(scope, function() {}));    }  };})// Use in the accordion-group template to indicate where you want the heading to be transcluded// You must provide the property on the accordion-group controller that will hold the transcluded element// <div class="accordion-group">//   <div class="accordion-heading" ><a ... accordion-transclude="heading">...</a></div>//   ...// </div>.directive('accordionTransclude', function() {  return {    require: '^accordionGroup',    link: function(scope, element, attr, controller) {      scope.$watch(function() { return controller[attr.accordionTransclude]; }, function(heading) {        if ( heading ) {          element.html('');          element.append(heading);        }      });    }  };});angular.module('ui.bootstrap.alert', []).controller('AlertController', ['$scope', '$attrs', function ($scope, $attrs) {  $scope.closeable = 'close' in $attrs;  this.close = $scope.close;}]).directive('alert', function () {  return {    restrict:'EA',    controller:'AlertController',    templateUrl:'template/alert/alert.html',    transclude:true,    replace:true,    scope: {      type: '@',      close: '&'    }  };}).directive('dismissOnTimeout', ['$timeout', function($timeout) {  return {    require: 'alert',    link: function(scope, element, attrs, alertCtrl) {      $timeout(function(){        alertCtrl.close();      }, parseInt(attrs.dismissOnTimeout, 10));    }  };}]);angular.module('ui.bootstrap.bindHtml', [])  .directive('bindHtmlUnsafe', function () {    return function (scope, element, attr) {      element.addClass('ng-binding').data('$binding', attr.bindHtmlUnsafe);      scope.$watch(attr.bindHtmlUnsafe, function bindHtmlUnsafeWatchAction(value) {        element.html(value || '');      });    };  });angular.module('ui.bootstrap.buttons', []).constant('buttonConfig', {  activeClass: 'active',  toggleEvent: 'click'}).controller('ButtonsController', ['buttonConfig', function(buttonConfig) {  this.activeClass = buttonConfig.activeClass || 'active';  this.toggleEvent = buttonConfig.toggleEvent || 'click';}]).directive('btnRadio', function () {  return {    require: ['btnRadio', 'ngModel'],    controller: 'ButtonsController',    link: function (scope, element, attrs, ctrls) {      var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1];      //model -> UI      ngModelCtrl.$render = function () {        element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, scope.$eval(attrs.btnRadio)));      };      //ui->model      element.bind(buttonsCtrl.toggleEvent, function () {        var isActive = element.hasClass(buttonsCtrl.activeClass);        if (!isActive || angular.isDefined(attrs.uncheckable)) {          scope.$apply(function () {            ngModelCtrl.$setViewValue(isActive ? null : scope.$eval(attrs.btnRadio));            ngModelCtrl.$render();          });        }      });    }  };}).directive('btnCheckbox', function () {  return {    require: ['btnCheckbox', 'ngModel'],    controller: 'ButtonsController',    link: function (scope, element, attrs, ctrls) {      var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1];      function getTrueValue() {        return getCheckboxValue(attrs.btnCheckboxTrue, true);      }      function getFalseValue() {        return getCheckboxValue(attrs.btnCheckboxFalse, false);      }      function getCheckboxValue(attributeValue, defaultValue) {        var val = scope.$eval(attributeValue);        return angular.isDefined(val) ? val : defaultValue;      }      //model -> UI      ngModelCtrl.$render = function () {        element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, getTrueValue()));      };      //ui->model      element.bind(buttonsCtrl.toggleEvent, function () {        scope.$apply(function () {          ngModelCtrl.$setViewValue(element.hasClass(buttonsCtrl.activeClass) ? getFalseValue() : getTrueValue());          ngModelCtrl.$render();        });      });    }  };});/*** @ngdoc overview* @name ui.bootstrap.carousel** @description* AngularJS version of an image carousel.**/angular.module('ui.bootstrap.carousel', ['ui.bootstrap.transition']).controller('CarouselController', ['$scope', '$timeout', '$interval', '$transition', function ($scope, $timeout, $interval, $transition) {  var self = this,    slides = self.slides = $scope.slides = [],    currentIndex = -1,    currentInterval, isPlaying;  self.currentSlide = null;  var destroyed = false;  /* direction: "prev" or "next" */  self.select = $scope.select = function(nextSlide, direction) {    var nextIndex = slides.indexOf(nextSlide);    //Decide direction if it's not given    if (direction === undefined) {      direction = nextIndex > currentIndex ? 'next' : 'prev';    }    if (nextSlide && nextSlide !== self.currentSlide) {      if ($scope.$currentTransition) {        $scope.$currentTransition.cancel();        //Timeout so ng-class in template has time to fix classes for finished slide        $timeout(goNext);      } else {        goNext();      }    }    function goNext() {      // Scope has been destroyed, stop here.      if (destroyed) { return; }      //If we have a slide to transition from and we have a transition type and we're allowed, go      if (self.currentSlide && angular.isString(direction) && !$scope.noTransition && nextSlide.$element) {        //We shouldn't do class manip in here, but it's the same weird thing bootstrap does. need to fix sometime        nextSlide.$element.addClass(direction);        var reflow = nextSlide.$element[0].offsetWidth; //force reflow        //Set all other slides to stop doing their stuff for the new transition        angular.forEach(slides, function(slide) {          angular.extend(slide, {direction: '', entering: false, leaving: false, active: false});        });        angular.extend(nextSlide, {direction: direction, active: true, entering: true});        angular.extend(self.currentSlide||{}, {direction: direction, leaving: true});        $scope.$currentTransition = $transition(nextSlide.$element, {});        //We have to create new pointers inside a closure since next & current will change        (function(next,current) {          $scope.$currentTransition.then(            function(){ transitionDone(next, current); },            function(){ transitionDone(next, current); }          );        }(nextSlide, self.currentSlide));      } else {        transitionDone(nextSlide, self.currentSlide);      }      self.currentSlide = nextSlide;      currentIndex = nextIndex;      //every time you change slides, reset the timer      restartTimer();    }    function transitionDone(next, current) {      angular.extend(next, {direction: '', active: true, leaving: false, entering: false});      angular.extend(current||{}, {direction: '', active: false, leaving: false, entering: false});      $scope.$currentTransition = null;    }  };  $scope.$on('$destroy', function () {    destroyed = true;  });  /* Allow outside people to call indexOf on slides array */  self.indexOfSlide = function(slide) {    return slides.indexOf(slide);  };  $scope.next = function() {    var newIndex = (currentIndex + 1) % slides.length;    //Prevent this user-triggered transition from occurring if there is already one in progress    if (!$scope.$currentTransition) {      return self.select(slides[newIndex], 'next');    }  };  $scope.prev = function() {    var newIndex = currentIndex - 1 < 0 ? slides.length - 1 : currentIndex - 1;    //Prevent this user-triggered transition from occurring if there is already one in progress    if (!$scope.$currentTransition) {      return self.select(slides[newIndex], 'prev');    }  };  $scope.isActive = function(slide) {     return self.currentSlide === slide;  };  $scope.$watch('interval', restartTimer);  $scope.$on('$destroy', resetTimer);  function restartTimer() {    resetTimer();    var interval = +$scope.interval;    if (!isNaN(interval) && interval > 0) {      currentInterval = $interval(timerFn, interval);    }  }  function resetTimer() {    if (currentInterval) {      $interval.cancel(currentInterval);      currentInterval = null;    }  }  function timerFn() {    var interval = +$scope.interval;    if (isPlaying && !isNaN(interval) && interval > 0) {      $scope.next();    } else {      $scope.pause();    }  }  $scope.play = function() {    if (!isPlaying) {      isPlaying = true;      restartTimer();    }  };  $scope.pause = function() {    if (!$scope.noPause) {      isPlaying = false;      resetTimer();    }  };  self.addSlide = function(slide, element) {    slide.$element = element;    slides.push(slide);    //if this is the first slide or the slide is set to active, select it    if(slides.length === 1 || slide.active) {      self.select(slides[slides.length-1]);      if (slides.length == 1) {        $scope.play();      }    } else {      slide.active = false;    }  };  self.removeSlide = function(slide) {    //get the index of the slide inside the carousel    var index = slides.indexOf(slide);    slides.splice(index, 1);    if (slides.length > 0 && slide.active) {      if (index >= slides.length) {        self.select(slides[index-1]);      } else {        self.select(slides[index]);      }    } else if (currentIndex > index) {      currentIndex--;    }  };}])/** * @ngdoc directive * @name ui.bootstrap.carousel.directive:carousel * @restrict EA * * @description * Carousel is the outer container for a set of image 'slides' to showcase. * * @param {number=} interval The time, in milliseconds, that it will take the carousel to go to the next slide. * @param {boolean=} noTransition Whether to disable transitions on the carousel. * @param {boolean=} noPause Whether to disable pausing on the carousel (by default, the carousel interval pauses on hover). * * @example<example module="ui.bootstrap">  <file name="index.html">    <carousel>      <slide>        <img src="http://placekitten.com/150/150" style="margin:auto;">        <div class="carousel-caption">          <p>Beautiful!</p>        </div>      </slide>      <slide>        <img src="http://placekitten.com/100/150" style="margin:auto;">        <div class="carousel-caption">          <p>D'aww!</p>        </div>      </slide>    </carousel>  </file>  <file name="demo.css">    .carousel-indicators {      top: auto;      bottom: 15px;    }  </file></example> */.directive('carousel', [function() {  return {    restrict: 'EA',    transclude: true,    replace: true,    controller: 'CarouselController',    require: 'carousel',    templateUrl: 'template/carousel/carousel.html',    scope: {      interval: '=',      noTransition: '=',      noPause: '='    }  };}])/** * @ngdoc directive * @name ui.bootstrap.carousel.directive:slide * @restrict EA * * @description * Creates a slide inside a {@link ui.bootstrap.carousel.directive:carousel carousel}.  Must be placed as a child of a carousel element. * * @param {boolean=} active Model binding, whether or not this slide is currently active. * * @example<example module="ui.bootstrap">  <file name="index.html"><div ng-controller="CarouselDemoCtrl">  <carousel>    <slide ng-repeat="slide in slides" active="slide.active">      <img ng-src="{{slide.image}}" style="margin:auto;">      <div class="carousel-caption">        <h4>Slide {{$index}}</h4>        <p>{{slide.text}}</p>      </div>    </slide>  </carousel>  Interval, in milliseconds: <input type="number" ng-model="myInterval">  <br />Enter a negative number to stop the interval.</div>  </file>  <file name="script.js">function CarouselDemoCtrl($scope) {  $scope.myInterval = 5000;}  </file>  <file name="demo.css">    .carousel-indicators {      top: auto;      bottom: 15px;    }  </file></example>*/.directive('slide', function() {  return {    require: '^carousel',    restrict: 'EA',    transclude: true,    replace: true,    templateUrl: 'template/carousel/slide.html',    scope: {      active: '=?'    },    link: function (scope, element, attrs, carouselCtrl) {      carouselCtrl.addSlide(scope, element);      //when the scope is destroyed then remove the slide from the current slides array      scope.$on('$destroy', function() {        carouselCtrl.removeSlide(scope);      });      scope.$watch('active', function(active) {        if (active) {          carouselCtrl.select(scope);        }      });    }  };});angular.module('ui.bootstrap.dateparser', []).service('dateParser', ['$locale', 'orderByFilter', function($locale, orderByFilter) {  this.parsers = {};  var formatCodeToRegex = {    'yyyy': {      regex: '\\d{4}',      apply: function(value) { this.year = +value; }    },    'yy': {      regex: '\\d{2}',      apply: function(value) { this.year = +value + 2000; }    },    'y': {      regex: '\\d{1,4}',      apply: function(value) { this.year = +value; }    },    'MMMM': {      regex: $locale.DATETIME_FORMATS.MONTH.join('|'),      apply: function(value) { this.month = $locale.DATETIME_FORMATS.MONTH.indexOf(value); }    },    'MMM': {      regex: $locale.DATETIME_FORMATS.SHORTMONTH.join('|'),      apply: function(value) { this.month = $locale.DATETIME_FORMATS.SHORTMONTH.indexOf(value); }    },    'MM': {      regex: '0[1-9]|1[0-2]',      apply: function(value) { this.month = value - 1; }    },    'M': {      regex: '[1-9]|1[0-2]',      apply: function(value) { this.month = value - 1; }    },    'dd': {      regex: '[0-2][0-9]{1}|3[0-1]{1}',      apply: function(value) { this.date = +value; }    },    'd': {      regex: '[1-2]?[0-9]{1}|3[0-1]{1}',      apply: function(value) { this.date = +value; }    },    'EEEE': {      regex: $locale.DATETIME_FORMATS.DAY.join('|')    },    'EEE': {      regex: $locale.DATETIME_FORMATS.SHORTDAY.join('|')    }  };  function createParser(format) {    var map = [], regex = format.split('');    angular.forEach(formatCodeToRegex, function(data, code) {      var index = format.indexOf(code);      if (index > -1) {        format = format.split('');        regex[index] = '(' + data.regex + ')';        format[index] = '$'; // Custom symbol to define consumed part of format        for (var i = index + 1, n = index + code.length; i < n; i++) {          regex[i] = '';          format[i] = '$';        }        format = format.join('');        map.push({ index: index, apply: data.apply });      }    });    return {      regex: new RegExp('^' + regex.join('') + '$'),      map: orderByFilter(map, 'index')    };  }  this.parse = function(input, format) {    if ( !angular.isString(input) || !format ) {      return input;    }    format = $locale.DATETIME_FORMATS[format] || format;    if ( !this.parsers[format] ) {      this.parsers[format] = createParser(format);    }    var parser = this.parsers[format],        regex = parser.regex,        map = parser.map,        results = input.match(regex);    if ( results && results.length ) {      var fields = { year: 1900, month: 0, date: 1, hours: 0 }, dt;      for( var i = 1, n = results.length; i < n; i++ ) {        var mapper = map[i-1];        if ( mapper.apply ) {          mapper.apply.call(fields, results[i]);        }      }      if ( isValid(fields.year, fields.month, fields.date) ) {        dt = new Date( fields.year, fields.month, fields.date, fields.hours);      }      return dt;    }  };  // Check if date is valid for specific month (and year for February).  // Month: 0 = Jan, 1 = Feb, etc  function isValid(year, month, date) {    if ( month === 1 && date > 28) {        return date === 29 && ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0);    }    if ( month === 3 || month === 5 || month === 8 || month === 10) {        return date < 31;    }    return true;  }}]);angular.module('ui.bootstrap.position', [])/** * A set of utility methods that can be use to retrieve position of DOM elements. * It is meant to be used where we need to absolute-position DOM elements in * relation to other, existing elements (this is the case for tooltips, popovers, * typeahead suggestions etc.). */  .factory('$position', ['$document', '$window', function ($document, $window) {    function getStyle(el, cssprop) {      if (el.currentStyle) { //IE        return el.currentStyle[cssprop];      } else if ($window.getComputedStyle) {        return $window.getComputedStyle(el)[cssprop];      }      // finally try and get inline style      return el.style[cssprop];    }    /**     * Checks if a given element is statically positioned     * @param element - raw DOM element     */    function isStaticPositioned(element) {      return (getStyle(element, 'position') || 'static' ) === 'static';    }    /**     * returns the closest, non-statically positioned parentOffset of a given element     * @param element     */    var parentOffsetEl = function (element) {      var docDomEl = $document[0];      var offsetParent = element.offsetParent || docDomEl;      while (offsetParent && offsetParent !== docDomEl && isStaticPositioned(offsetParent) ) {        offsetParent = offsetParent.offsetParent;      }      return offsetParent || docDomEl;    };    return {      /**       * Provides read-only equivalent of jQuery's position function:       * http://api.jquery.com/position/       */      position: function (element) {        var elBCR = this.offset(element);        var offsetParentBCR = { top: 0, left: 0 };        var offsetParentEl = parentOffsetEl(element[0]);        if (offsetParentEl != $document[0]) {          offsetParentBCR = this.offset(angular.element(offsetParentEl));          offsetParentBCR.top += offsetParentEl.clientTop - offsetParentEl.scrollTop;          offsetParentBCR.left += offsetParentEl.clientLeft - offsetParentEl.scrollLeft;        }        var boundingClientRect = element[0].getBoundingClientRect();        return {          width: boundingClientRect.width || element.prop('offsetWidth'),          height: boundingClientRect.height || element.prop('offsetHeight'),          top: elBCR.top - offsetParentBCR.top,          left: elBCR.left - offsetParentBCR.left        };      },      /**       * Provides read-only equivalent of jQuery's offset function:       * http://api.jquery.com/offset/       */      offset: function (element) {        var boundingClientRect = element[0].getBoundingClientRect();        return {          width: boundingClientRect.width || element.prop('offsetWidth'),          height: boundingClientRect.height || element.prop('offsetHeight'),          top: boundingClientRect.top + ($window.pageYOffset || $document[0].documentElement.scrollTop),          left: boundingClientRect.left + ($window.pageXOffset || $document[0].documentElement.scrollLeft)        };      },      /**       * Provides coordinates for the targetEl in relation to hostEl       */      positionElements: function (hostEl, targetEl, positionStr, appendToBody) {        var positionStrParts = positionStr.split('-');        var pos0 = positionStrParts[0], pos1 = positionStrParts[1] || 'center';        var hostElPos,          targetElWidth,          targetElHeight,          targetElPos;        hostElPos = appendToBody ? this.offset(hostEl) : this.position(hostEl);        targetElWidth = targetEl.prop('offsetWidth');        targetElHeight = targetEl.prop('offsetHeight');        var shiftWidth = {          center: function () {            return hostElPos.left + hostElPos.width / 2 - targetElWidth / 2;          },          left: function () {            return hostElPos.left;          },          right: function () {            return hostElPos.left + hostElPos.width;          }        };        var shiftHeight = {          center: function () {            return hostElPos.top + hostElPos.height / 2 - targetElHeight / 2;          },          top: function () {            return hostElPos.top;          },          bottom: function () {            return hostElPos.top + hostElPos.height;          }        };        switch (pos0) {          case 'right':            targetElPos = {              top: shiftHeight[pos1](),              left: shiftWidth[pos0]()            };            break;          case 'left':            targetElPos = {              top: shiftHeight[pos1](),              left: hostElPos.left - targetElWidth            };            break;          case 'bottom':            targetElPos = {              top: shiftHeight[pos0](),              left: shiftWidth[pos1]()            };            break;          default:            targetElPos = {              top: hostElPos.top - targetElHeight,              left: shiftWidth[pos1]()            };            break;        }        return targetElPos;      }    };  }]);angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootstrap.position']).constant('datepickerConfig', {  formatDay: 'dd',  formatMonth: 'MMMM',  formatYear: 'yyyy',  formatDayHeader: 'EEE',  formatDayTitle: 'MMMM yyyy',  formatMonthTitle: 'yyyy',  datepickerMode: 'day',  minMode: 'day',  maxMode: 'year',  showWeeks: true,  startingDay: 0,  yearRange: 20,  minDate: null,  maxDate: null}).controller('DatepickerController', ['$scope', '$attrs', '$parse', '$interpolate', '$timeout', '$log', 'dateFilter', 'datepickerConfig', function($scope, $attrs, $parse, $interpolate, $timeout, $log, dateFilter, datepickerConfig) {  var self = this,      ngModelCtrl = { $setViewValue: angular.noop }; // nullModelCtrl;  // Modes chain  this.modes = ['day', 'month', 'year'];  // Configuration attributes  angular.forEach(['formatDay', 'formatMonth', 'formatYear', 'formatDayHeader', 'formatDayTitle', 'formatMonthTitle',                   'minMode', 'maxMode', 'showWeeks', 'startingDay', 'yearRange'], function( key, index ) {    self[key] = angular.isDefined($attrs[key]) ? (index < 8 ? $interpolate($attrs[key])($scope.$parent) : $scope.$parent.$eval($attrs[key])) : datepickerConfig[key];  });  // Watchable date attributes  angular.forEach(['minDate', 'maxDate'], function( key ) {    if ( $attrs[key] ) {      $scope.$parent.$watch($parse($attrs[key]), function(value) {        self[key] = value ? new Date(value) : null;        self.refreshView();      });    } else {      self[key] = datepickerConfig[key] ? new Date(datepickerConfig[key]) : null;    }  });  $scope.datepickerMode = $scope.datepickerMode || datepickerConfig.datepickerMode;  $scope.uniqueId = 'datepicker-' + $scope.$id + '-' + Math.floor(Math.random() * 10000);  this.activeDate = angular.isDefined($attrs.initDate) ? $scope.$parent.$eval($attrs.initDate) : new Date();  $scope.isActive = function(dateObject) {    if (self.compare(dateObject.date, self.activeDate) === 0) {      $scope.activeDateId = dateObject.uid;      return true;    }    return false;  };  this.init = function( ngModelCtrl_ ) {    ngModelCtrl = ngModelCtrl_;    ngModelCtrl.$render = function() {      self.render();    };  };  this.render = function() {    if ( ngModelCtrl.$modelValue ) {      var date = new Date( ngModelCtrl.$modelValue ),          isValid = !isNaN(date);      if ( isValid ) {        this.activeDate = date;      } else {        $log.error('Datepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.');      }      ngModelCtrl.$setValidity('date', isValid);    }    this.refreshView();  };  this.refreshView = function() {    if ( this.element ) {      this._refreshView();      var date = ngModelCtrl.$modelValue ? new Date(ngModelCtrl.$modelValue) : null;      ngModelCtrl.$setValidity('date-disabled', !date || (this.element && !this.isDisabled(date)));    }  };  this.createDateObject = function(date, format) {    var model = ngModelCtrl.$modelValue ? new Date(ngModelCtrl.$modelValue) : null;    return {      date: date,      label: dateFilter(date, format),      selected: model && this.compare(date, model) === 0,      disabled: this.isDisabled(date),      current: this.compare(date, new Date()) === 0    };  };  this.isDisabled = function( date ) {    return ((this.minDate && this.compare(date, this.minDate) < 0) || (this.maxDate && this.compare(date, this.maxDate) > 0) || ($attrs.dateDisabled && $scope.dateDisabled({date: date, mode: $scope.datepickerMode})));  };  // Split array into smaller arrays  this.split = function(arr, size) {    var arrays = [];    while (arr.length > 0) {      arrays.push(arr.splice(0, size));    }    return arrays;  };  $scope.select = function( date ) {    if ( $scope.datepickerMode === self.minMode ) {      var dt = ngModelCtrl.$modelValue ? new Date( ngModelCtrl.$modelValue ) : new Date(0, 0, 0, 0, 0, 0, 0);      dt.setFullYear( date.getFullYear(), date.getMonth(), date.getDate() );      ngModelCtrl.$setViewValue( dt );      ngModelCtrl.$render();    } else {      self.activeDate = date;      $scope.datepickerMode = self.modes[ self.modes.indexOf( $scope.datepickerMode ) - 1 ];    }  };  $scope.move = function( direction ) {    var year = self.activeDate.getFullYear() + direction * (self.step.years || 0),        month = self.activeDate.getMonth() + direction * (self.step.months || 0);    self.activeDate.setFullYear(year, month, 1);    self.refreshView();  };  $scope.toggleMode = function( direction ) {    direction = direction || 1;    if (($scope.datepickerMode === self.maxMode && direction === 1) || ($scope.datepickerMode === self.minMode && direction === -1)) {      return;    }    $scope.datepickerMode = self.modes[ self.modes.indexOf( $scope.datepickerMode ) + direction ];  };  // Key event mapper  $scope.keys = { 13:'enter', 32:'space', 33:'pageup', 34:'pagedown', 35:'end', 36:'home', 37:'left', 38:'up', 39:'right', 40:'down' };  var focusElement = function() {    $timeout(function() {      self.element[0].focus();    }, 0 , false);  };  // Listen for focus requests from popup directive  $scope.$on('datepicker.focus', focusElement);  $scope.keydown = function( evt ) {    var key = $scope.keys[evt.which];    if ( !key || evt.shiftKey || evt.altKey ) {      return;    }    evt.preventDefault();    evt.stopPropagation();    if (key === 'enter' || key === 'space') {      if ( self.isDisabled(self.activeDate)) {        return; // do nothing      }      $scope.select(self.activeDate);      focusElement();    } else if (evt.ctrlKey && (key === 'up' || key === 'down')) {      $scope.toggleMode(key === 'up' ? 1 : -1);      focusElement();    } else {      self.handleKeyDown(key, evt);      self.refreshView();    }  };}]).directive( 'datepicker', function () {  return {    restrict: 'EA',    replace: true,    templateUrl: 'template/datepicker/datepicker.html',    scope: {      datepickerMode: '=?',      dateDisabled: '&'    },    require: ['datepicker', '?^ngModel'],    controller: 'DatepickerController',    link: function(scope, element, attrs, ctrls) {      var datepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1];      if ( ngModelCtrl ) {        datepickerCtrl.init( ngModelCtrl );      }    }  };}).directive('daypicker', ['dateFilter', function (dateFilter) {  return {    restrict: 'EA',    replace: true,    templateUrl: 'template/datepicker/day.html',    require: '^datepicker',    link: function(scope, element, attrs, ctrl) {      scope.showWeeks = ctrl.showWeeks;      ctrl.step = { months: 1 };      ctrl.element = element;      var DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];      function getDaysInMonth( year, month ) {        return ((month === 1) && (year % 4 === 0) && ((year % 100 !== 0) || (year % 400 === 0))) ? 29 : DAYS_IN_MONTH[month];      }      function getDates(startDate, n) {        var dates = new Array(n), current = new Date(startDate), i = 0;        current.setHours(12); // Prevent repeated dates because of timezone bug        while ( i < n ) {          dates[i++] = new Date(current);          current.setDate( current.getDate() + 1 );        }        return dates;      }      ctrl._refreshView = function() {        var year = ctrl.activeDate.getFullYear(),          month = ctrl.activeDate.getMonth(),          firstDayOfMonth = new Date(year, month, 1),          difference = ctrl.startingDay - firstDayOfMonth.getDay(),          numDisplayedFromPreviousMonth = (difference > 0) ? 7 - difference : - difference,          firstDate = new Date(firstDayOfMonth);        if ( numDisplayedFromPreviousMonth > 0 ) {          firstDate.setDate( - numDisplayedFromPreviousMonth + 1 );        }        // 42 is the number of days on a six-month calendar        var days = getDates(firstDate, 42);        for (var i = 0; i < 42; i ++) {          days[i] = angular.extend(ctrl.createDateObject(days[i], ctrl.formatDay), {            secondary: days[i].getMonth() !== month,            uid: scope.uniqueId + '-' + i          });        }        scope.labels = new Array(7);        for (var j = 0; j < 7; j++) {          scope.labels[j] = {            abbr: dateFilter(days[j].date, ctrl.formatDayHeader),            full: dateFilter(days[j].date, 'EEEE')          };        }        scope.title = dateFilter(ctrl.activeDate, ctrl.formatDayTitle);        scope.rows = ctrl.split(days, 7);        if ( scope.showWeeks ) {          scope.weekNumbers = [];          var weekNumber = getISO8601WeekNumber( scope.rows[0][0].date ),              numWeeks = scope.rows.length;          while( scope.weekNumbers.push(weekNumber++) < numWeeks ) {}        }      };      ctrl.compare = function(date1, date2) {        return (new Date( date1.getFullYear(), date1.getMonth(), date1.getDate() ) - new Date( date2.getFullYear(), date2.getMonth(), date2.getDate() ) );      };      function getISO8601WeekNumber(date) {        var checkDate = new Date(date);        checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7)); // Thursday        var time = checkDate.getTime();        checkDate.setMonth(0); // Compare with Jan 1        checkDate.setDate(1);        return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;      }      ctrl.handleKeyDown = function( key, evt ) {        var date = ctrl.activeDate.getDate();        if (key === 'left') {          date = date - 1;   // up        } else if (key === 'up') {          date = date - 7;   // down        } else if (key === 'right') {          date = date + 1;   // down        } else if (key === 'down') {          date = date + 7;        } else if (key === 'pageup' || key === 'pagedown') {          var month = ctrl.activeDate.getMonth() + (key === 'pageup' ? - 1 : 1);          ctrl.activeDate.setMonth(month, 1);          date = Math.min(getDaysInMonth(ctrl.activeDate.getFullYear(), ctrl.activeDate.getMonth()), date);        } else if (key === 'home') {          date = 1;        } else if (key === 'end') {          date = getDaysInMonth(ctrl.activeDate.getFullYear(), ctrl.activeDate.getMonth());        }        ctrl.activeDate.setDate(date);      };      ctrl.refreshView();    }  };}]).directive('monthpicker', ['dateFilter', function (dateFilter) {  return {    restrict: 'EA',    replace: true,    templateUrl: 'template/datepicker/month.html',    require: '^datepicker',    link: function(scope, element, attrs, ctrl) {      ctrl.step = { years: 1 };      ctrl.element = element;      ctrl._refreshView = function() {        var months = new Array(12),            year = ctrl.activeDate.getFullYear();        for ( var i = 0; i < 12; i++ ) {          months[i] = angular.extend(ctrl.createDateObject(new Date(year, i, 1), ctrl.formatMonth), {            uid: scope.uniqueId + '-' + i          });        }        scope.title = dateFilter(ctrl.activeDate, ctrl.formatMonthTitle);        scope.rows = ctrl.split(months, 3);      };      ctrl.compare = function(date1, date2) {        return new Date( date1.getFullYear(), date1.getMonth() ) - new Date( date2.getFullYear(), date2.getMonth() );      };      ctrl.handleKeyDown = function( key, evt ) {        var date = ctrl.activeDate.getMonth();        if (key === 'left') {          date = date - 1;   // up        } else if (key === 'up') {          date = date - 3;   // down        } else if (key === 'right') {          date = date + 1;   // down        } else if (key === 'down') {          date = date + 3;        } else if (key === 'pageup' || key === 'pagedown') {          var year = ctrl.activeDate.getFullYear() + (key === 'pageup' ? - 1 : 1);          ctrl.activeDate.setFullYear(year);        } else if (key === 'home') {          date = 0;        } else if (key === 'end') {          date = 11;        }        ctrl.activeDate.setMonth(date);      };      ctrl.refreshView();    }  };}]).directive('yearpicker', ['dateFilter', function (dateFilter) {  return {    restrict: 'EA',    replace: true,    templateUrl: 'template/datepicker/year.html',    require: '^datepicker',    link: function(scope, element, attrs, ctrl) {      var range = ctrl.yearRange;      ctrl.step = { years: range };      ctrl.element = element;      function getStartingYear( year ) {        return parseInt((year - 1) / range, 10) * range + 1;      }      ctrl._refreshView = function() {        var years = new Array(range);        for ( var i = 0, start = getStartingYear(ctrl.activeDate.getFullYear()); i < range; i++ ) {          years[i] = angular.extend(ctrl.createDateObject(new Date(start + i, 0, 1), ctrl.formatYear), {            uid: scope.uniqueId + '-' + i          });        }        scope.title = [years[0].label, years[range - 1].label].join(' - ');        scope.rows = ctrl.split(years, 5);      };      ctrl.compare = function(date1, date2) {        return date1.getFullYear() - date2.getFullYear();      };      ctrl.handleKeyDown = function( key, evt ) {        var date = ctrl.activeDate.getFullYear();        if (key === 'left') {          date = date - 1;   // up        } else if (key === 'up') {          date = date - 5;   // down        } else if (key === 'right') {          date = date + 1;   // down        } else if (key === 'down') {          date = date + 5;        } else if (key === 'pageup' || key === 'pagedown') {          date += (key === 'pageup' ? - 1 : 1) * ctrl.step.years;        } else if (key === 'home') {          date = getStartingYear( ctrl.activeDate.getFullYear() );        } else if (key === 'end') {          date = getStartingYear( ctrl.activeDate.getFullYear() ) + range - 1;        }        ctrl.activeDate.setFullYear(date);      };      ctrl.refreshView();    }  };}]).constant('datepickerPopupConfig', {  datepickerPopup: 'yyyy-MM-dd',  currentText: 'Today',  clearText: 'Clear',  closeText: 'Done',  closeOnDateSelection: true,  appendToBody: false,  showButtonBar: true}).directive('datepickerPopup', ['$compile', '$parse', '$document', '$position', 'dateFilter', 'dateParser', 'datepickerPopupConfig',function ($compile, $parse, $document, $position, dateFilter, dateParser, datepickerPopupConfig) {  return {    restrict: 'EA',    require: 'ngModel',    scope: {      isOpen: '=?',      currentText: '@',      clearText: '@',      closeText: '@',      dateDisabled: '&'    },    link: function(scope, element, attrs, ngModel) {      var dateFormat,          closeOnDateSelection = angular.isDefined(attrs.closeOnDateSelection) ? scope.$parent.$eval(attrs.closeOnDateSelection) : datepickerPopupConfig.closeOnDateSelection,          appendToBody = angular.isDefined(attrs.datepickerAppendToBody) ? scope.$parent.$eval(attrs.datepickerAppendToBody) : datepickerPopupConfig.appendToBody;      scope.showButtonBar = angular.isDefined(attrs.showButtonBar) ? scope.$parent.$eval(attrs.showButtonBar) : datepickerPopupConfig.showButtonBar;      scope.getText = function( key ) {        return scope[key + 'Text'] || datepickerPopupConfig[key + 'Text'];      };      attrs.$observe('datepickerPopup', function(value) {          dateFormat = value || datepickerPopupConfig.datepickerPopup;          ngModel.$render();      });      // popup element used to display calendar      var popupEl = angular.element('<div datepicker-popup-wrap><div datepicker></div></div>');      popupEl.attr({        'ng-model': 'date',        'ng-change': 'dateSelection()'      });      function cameltoDash( string ){        return string.replace(/([A-Z])/g, function($1) { return '-' + $1.toLowerCase(); });      }      // datepicker element      var datepickerEl = angular.element(popupEl.children()[0]);      if ( attrs.datepickerOptions ) {        angular.forEach(scope.$parent.$eval(attrs.datepickerOptions), function( value, option ) {          datepickerEl.attr( cameltoDash(option), value );        });      }      scope.watchData = {};      angular.forEach(['minDate', 'maxDate', 'datepickerMode'], function( key ) {        if ( attrs[key] ) {          var getAttribute = $parse(attrs[key]);          scope.$parent.$watch(getAttribute, function(value){            scope.watchData[key] = value;          });          datepickerEl.attr(cameltoDash(key), 'watchData.' + key);          // Propagate changes from datepicker to outside          if ( key === 'datepickerMode' ) {            var setAttribute = getAttribute.assign;            scope.$watch('watchData.' + key, function(value, oldvalue) {              if ( value !== oldvalue ) {                setAttribute(scope.$parent, value);              }            });          }        }      });      if (attrs.dateDisabled) {        datepickerEl.attr('date-disabled', 'dateDisabled({ date: date, mode: mode })');      }      function parseDate(viewValue) {        if (!viewValue) {          ngModel.$setValidity('date', true);          return null;        } else if (angular.isDate(viewValue) && !isNaN(viewValue)) {          ngModel.$setValidity('date', true);          return viewValue;        } else if (angular.isString(viewValue)) {          var date = dateParser.parse(viewValue, dateFormat) || new Date(viewValue);          if (isNaN(date)) {            ngModel.$setValidity('date', false);            return undefined;          } else {            ngModel.$setValidity('date', true);            return date;          }        } else {          ngModel.$setValidity('date', false);          return undefined;        }      }      ngModel.$parsers.unshift(parseDate);      // Inner change      scope.dateSelection = function(dt) {        if (angular.isDefined(dt)) {          scope.date = dt;        }        ngModel.$setViewValue(scope.date);        ngModel.$render();        if ( closeOnDateSelection ) {          scope.isOpen = false;          element[0].focus();        }      };      element.bind('input change keyup', function() {        scope.$apply(function() {          scope.date = ngModel.$modelValue;        });      });      // Outter change      ngModel.$render = function() {        var date = ngModel.$viewValue ? dateFilter(ngModel.$viewValue, dateFormat) : '';        element.val(date);        scope.date = parseDate( ngModel.$modelValue );      };      var documentClickBind = function(event) {        if (scope.isOpen && event.target !== element[0]) {          scope.$apply(function() {            scope.isOpen = false;          });        }      };      var keydown = function(evt, noApply) {        scope.keydown(evt);      };      element.bind('keydown', keydown);      scope.keydown = function(evt) {        if (evt.which === 27) {          evt.preventDefault();          evt.stopPropagation();          scope.close();        } else if (evt.which === 40 && !scope.isOpen) {          scope.isOpen = true;        }      };      scope.$watch('isOpen', function(value) {        if (value) {          scope.$broadcast('datepicker.focus');          scope.position = appendToBody ? $position.offset(element) : $position.position(element);          scope.position.top = scope.position.top + element.prop('offsetHeight');          $document.bind('click', documentClickBind);        } else {          $document.unbind('click', documentClickBind);        }      });      scope.select = function( date ) {        if (date === 'today') {          var today = new Date();          if (angular.isDate(ngModel.$modelValue)) {            date = new Date(ngModel.$modelValue);            date.setFullYear(today.getFullYear(), today.getMonth(), today.getDate());          } else {            date = new Date(today.setHours(0, 0, 0, 0));          }        }        scope.dateSelection( date );      };      scope.close = function() {        scope.isOpen = false;        element[0].focus();      };      var $popup = $compile(popupEl)(scope);      // Prevent jQuery cache memory leak (template is now redundant after linking)      popupEl.remove();      if ( appendToBody ) {        $document.find('body').append($popup);      } else {        element.after($popup);      }      scope.$on('$destroy', function() {        $popup.remove();        element.unbind('keydown', keydown);        $document.unbind('click', documentClickBind);      });    }  };}]).directive('datepickerPopupWrap', function() {  return {    restrict:'EA',    replace: true,    transclude: true,    templateUrl: 'template/datepicker/popup.html',    link:function (scope, element, attrs) {      element.bind('click', function(event) {        event.preventDefault();        event.stopPropagation();      });    }  };});angular.module('ui.bootstrap.dropdown', []).constant('dropdownConfig', {  openClass: 'open'}).service('dropdownService', ['$document', function($document) {  var openScope = null;  this.open = function( dropdownScope ) {    if ( !openScope ) {      $document.bind('click', closeDropdown);      $document.bind('keydown', escapeKeyBind);    }    if ( openScope && openScope !== dropdownScope ) {        openScope.isOpen = false;    }    openScope = dropdownScope;  };  this.close = function( dropdownScope ) {    if ( openScope === dropdownScope ) {      openScope = null;      $document.unbind('click', closeDropdown);      $document.unbind('keydown', escapeKeyBind);    }  };  var closeDropdown = function( evt ) {    // This method may still be called during the same mouse event that    // unbound this event handler. So check openScope before proceeding.    if (!openScope) { return; }    var toggleElement = openScope.getToggleElement();    if ( evt && toggleElement && toggleElement[0].contains(evt.target) ) {        return;    }    openScope.$apply(function() {      openScope.isOpen = false;    });  };  var escapeKeyBind = function( evt ) {    if ( evt.which === 27 ) {      openScope.focusToggleElement();      closeDropdown();    }  };}]).controller('DropdownController', ['$scope', '$attrs', '$parse', 'dropdownConfig', 'dropdownService', '$animate', function($scope, $attrs, $parse, dropdownConfig, dropdownService, $animate) {  var self = this,      scope = $scope.$new(), // create a child scope so we are not polluting original one      openClass = dropdownConfig.openClass,      getIsOpen,      setIsOpen = angular.noop,      toggleInvoker = $attrs.onToggle ? $parse($attrs.onToggle) : angular.noop;  this.init = function( element ) {    self.$element = element;    if ( $attrs.isOpen ) {      getIsOpen = $parse($attrs.isOpen);      setIsOpen = getIsOpen.assign;      $scope.$watch(getIsOpen, function(value) {        scope.isOpen = !!value;      });    }  };  this.toggle = function( open ) {    return scope.isOpen = arguments.length ? !!open : !scope.isOpen;  };  // Allow other directives to watch status  this.isOpen = function() {    return scope.isOpen;  };  scope.getToggleElement = function() {    return self.toggleElement;  };  scope.focusToggleElement = function() {    if ( self.toggleElement ) {      self.toggleElement[0].focus();    }  };  scope.$watch('isOpen', function( isOpen, wasOpen ) {    $animate[isOpen ? 'addClass' : 'removeClass'](self.$element, openClass);    if ( isOpen ) {      scope.focusToggleElement();      dropdownService.open( scope );    } else {      dropdownService.close( scope );    }    setIsOpen($scope, isOpen);    if (angular.isDefined(isOpen) && isOpen !== wasOpen) {      toggleInvoker($scope, { open: !!isOpen });    }  });  $scope.$on('$locationChangeSuccess', function() {    scope.isOpen = false;  });  $scope.$on('$destroy', function() {    scope.$destroy();  });}]).directive('dropdown', function() {  return {    controller: 'DropdownController',    link: function(scope, element, attrs, dropdownCtrl) {      dropdownCtrl.init( element );    }  };}).directive('dropdownToggle', function() {  return {    require: '?^dropdown',    link: function(scope, element, attrs, dropdownCtrl) {      if ( !dropdownCtrl ) {        return;      }      dropdownCtrl.toggleElement = element;      var toggleDropdown = function(event) {        event.preventDefault();        if ( !element.hasClass('disabled') && !attrs.disabled ) {          scope.$apply(function() {            dropdownCtrl.toggle();          });        }      };      element.bind('click', toggleDropdown);      // WAI-ARIA      element.attr({ 'aria-haspopup': true, 'aria-expanded': false });      scope.$watch(dropdownCtrl.isOpen, function( isOpen ) {        element.attr('aria-expanded', !!isOpen);      });      scope.$on('$destroy', function() {        element.unbind('click', toggleDropdown);      });    }  };});angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition'])/** * A helper, internal data structure that acts as a map but also allows getting / removing * elements in the LIFO order */  .factory('$$stackedMap', function () {    return {      createNew: function () {        var stack = [];        return {          add: function (key, value) {            stack.push({              key: key,              value: value            });          },          get: function (key) {            for (var i = 0; i < stack.length; i++) {              if (key == stack[i].key) {                return stack[i];              }            }          },          keys: function() {            var keys = [];            for (var i = 0; i < stack.length; i++) {              keys.push(stack[i].key);            }            return keys;          },          top: function () {            return stack[stack.length - 1];          },          remove: function (key) {            var idx = -1;            for (var i = 0; i < stack.length; i++) {              if (key == stack[i].key) {                idx = i;                break;              }            }            return stack.splice(idx, 1)[0];          },          removeTop: function () {            return stack.splice(stack.length - 1, 1)[0];          },          length: function () {            return stack.length;          }        };      }    };  })/** * A helper directive for the $modal service. It creates a backdrop element. */  .directive('modalBackdrop', ['$timeout', function ($timeout) {    return {      restrict: 'EA',      replace: true,      templateUrl: 'template/modal/backdrop.html',      link: function (scope, element, attrs) {        scope.backdropClass = attrs.backdropClass || '';        scope.animate = false;        //trigger CSS transitions        $timeout(function () {          scope.animate = true;        });      }    };  }])  .directive('modalWindow', ['$modalStack', '$timeout', function ($modalStack, $timeout) {    return {      restrict: 'EA',      scope: {        index: '@',        animate: '='      },      replace: true,      transclude: true,      templateUrl: function(tElement, tAttrs) {        return tAttrs.templateUrl || 'template/modal/window.html';      },      link: function (scope, element, attrs) {        element.addClass(attrs.windowClass || '');        scope.size = attrs.size;        $timeout(function () {          // trigger CSS transitions          scope.animate = true;          /**           * Auto-focusing of a freshly-opened modal element causes any child elements           * with the autofocus attribute to lose focus. This is an issue on touch           * based devices which will show and then hide the onscreen keyboard.           * Attempts to refocus the autofocus element via JavaScript will not reopen           * the onscreen keyboard. Fixed by updated the focusing logic to only autofocus           * the modal element if the modal does not contain an autofocus element.           */          if (!element[0].querySelectorAll('[autofocus]').length) {            element[0].focus();          }        });        scope.close = function (evt) {          var modal = $modalStack.getTop();          if (modal && modal.value.backdrop && modal.value.backdrop != 'static' && (evt.target === evt.currentTarget)) {            evt.preventDefault();            evt.stopPropagation();            $modalStack.dismiss(modal.key, 'backdrop click');          }        };      }    };  }])  .directive('modalTransclude', function () {    return {      link: function($scope, $element, $attrs, controller, $transclude) {        $transclude($scope.$parent, function(clone) {          $element.empty();          $element.append(clone);        });      }    };  })  .factory('$modalStack', ['$transition', '$timeout', '$document', '$compile', '$rootScope', '$$stackedMap',    function ($transition, $timeout, $document, $compile, $rootScope, $$stackedMap) {      var OPENED_MODAL_CLASS = 'modal-open';      var backdropDomEl, backdropScope;      var openedWindows = $$stackedMap.createNew();      var $modalStack = {};      function backdropIndex() {        var topBackdropIndex = -1;        var opened = openedWindows.keys();        for (var i = 0; i < opened.length; i++) {          if (openedWindows.get(opened[i]).value.backdrop) {            topBackdropIndex = i;          }        }        return topBackdropIndex;      }      $rootScope.$watch(backdropIndex, function(newBackdropIndex){        if (backdropScope) {          backdropScope.index = newBackdropIndex;        }      });      function removeModalWindow(modalInstance) {        var body = $document.find('body').eq(0);        var modalWindow = openedWindows.get(modalInstance).value;        //clean up the stack        openedWindows.remove(modalInstance);        //remove window DOM element        removeAfterAnimate(modalWindow.modalDomEl, modalWindow.modalScope, 300, function() {          modalWindow.modalScope.$destroy();          body.toggleClass(OPENED_MODAL_CLASS, openedWindows.length() > 0);          checkRemoveBackdrop();        });      }      function checkRemoveBackdrop() {          //remove backdrop if no longer needed          if (backdropDomEl && backdropIndex() == -1) {            var backdropScopeRef = backdropScope;            removeAfterAnimate(backdropDomEl, backdropScope, 150, function () {              backdropScopeRef.$destroy();              backdropScopeRef = null;            });            backdropDomEl = undefined;            backdropScope = undefined;          }      }      function removeAfterAnimate(domEl, scope, emulateTime, done) {        // Closing animation        scope.animate = false;        var transitionEndEventName = $transition.transitionEndEventName;        if (transitionEndEventName) {          // transition out          var timeout = $timeout(afterAnimating, emulateTime);          domEl.bind(transitionEndEventName, function () {            $timeout.cancel(timeout);            afterAnimating();            scope.$apply();          });        } else {          // Ensure this call is async          $timeout(afterAnimating);        }        function afterAnimating() {          if (afterAnimating.done) {            return;          }          afterAnimating.done = true;          domEl.remove();          if (done) {            done();          }        }      }      $document.bind('keydown', function (evt) {        var modal;        if (evt.which === 27) {          modal = openedWindows.top();          if (modal && modal.value.keyboard) {            evt.preventDefault();            $rootScope.$apply(function () {              $modalStack.dismiss(modal.key, 'escape key press');            });          }        }      });      $modalStack.open = function (modalInstance, modal) {        openedWindows.add(modalInstance, {          deferred: modal.deferred,          modalScope: modal.scope,          backdrop: modal.backdrop,          keyboard: modal.keyboard        });        var body = $document.find('body').eq(0),            currBackdropIndex = backdropIndex();        if (currBackdropIndex >= 0 && !backdropDomEl) {          backdropScope = $rootScope.$new(true);          backdropScope.index = currBackdropIndex;          var angularBackgroundDomEl = angular.element('<div modal-backdrop></div>');          angularBackgroundDomEl.attr('backdrop-class', modal.backdropClass);          backdropDomEl = $compile(angularBackgroundDomEl)(backdropScope);          body.append(backdropDomEl);        }        var angularDomEl = angular.element('<div modal-window></div>');        angularDomEl.attr({          'template-url': modal.windowTemplateUrl,          'window-class': modal.windowClass,          'size': modal.size,          'index': openedWindows.length() - 1,          'animate': 'animate'        }).html(modal.content);        var modalDomEl = $compile(angularDomEl)(modal.scope);        openedWindows.top().value.modalDomEl = modalDomEl;        body.append(modalDomEl);        body.addClass(OPENED_MODAL_CLASS);      };      $modalStack.close = function (modalInstance, result) {        var modalWindow = openedWindows.get(modalInstance);        if (modalWindow) {          modalWindow.value.deferred.resolve(result);          removeModalWindow(modalInstance);        }      };      $modalStack.dismiss = function (modalInstance, reason) {        var modalWindow = openedWindows.get(modalInstance);        if (modalWindow) {          modalWindow.value.deferred.reject(reason);          removeModalWindow(modalInstance);        }      };      $modalStack.dismissAll = function (reason) {        var topModal = this.getTop();        while (topModal) {          this.dismiss(topModal.key, reason);          topModal = this.getTop();        }      };      $modalStack.getTop = function () {        return openedWindows.top();      };      return $modalStack;    }])  .provider('$modal', function () {    var $modalProvider = {      options: {        backdrop: true, //can be also false or 'static'        keyboard: true      },      $get: ['$injector', '$rootScope', '$q', '$http', '$templateCache', '$controller', '$modalStack',        function ($injector, $rootScope, $q, $http, $templateCache, $controller, $modalStack) {          var $modal = {};          function getTemplatePromise(options) {            return options.template ? $q.when(options.template) :              $http.get(angular.isFunction(options.templateUrl) ? (options.templateUrl)() : options.templateUrl,                {cache: $templateCache}).then(function (result) {                  return result.data;              });          }          function getResolvePromises(resolves) {            var promisesArr = [];            angular.forEach(resolves, function (value) {              if (angular.isFunction(value) || angular.isArray(value)) {                promisesArr.push($q.when($injector.invoke(value)));              }            });            return promisesArr;          }          $modal.open = function (modalOptions) {            var modalResultDeferred = $q.defer();            var modalOpenedDeferred = $q.defer();            //prepare an instance of a modal to be injected into controllers and returned to a caller            var modalInstance = {              result: modalResultDeferred.promise,              opened: modalOpenedDeferred.promise,              close: function (result) {                $modalStack.close(modalInstance, result);              },              dismiss: function (reason) {                $modalStack.dismiss(modalInstance, reason);              }            };            //merge and clean up options            modalOptions = angular.extend({}, $modalProvider.options, modalOptions);            modalOptions.resolve = modalOptions.resolve || {};            //verify options            if (!modalOptions.template && !modalOptions.templateUrl) {              throw new Error('One of template or templateUrl options is required.');            }            var templateAndResolvePromise =              $q.all([getTemplatePromise(modalOptions)].concat(getResolvePromises(modalOptions.resolve)));            templateAndResolvePromise.then(function resolveSuccess(tplAndVars) {              var modalScope = (modalOptions.scope || $rootScope).$new();              modalScope.$close = modalInstance.close;              modalScope.$dismiss = modalInstance.dismiss;              var ctrlInstance, ctrlLocals = {};              var resolveIter = 1;              //controllers              if (modalOptions.controller) {                ctrlLocals.$scope = modalScope;                ctrlLocals.$modalInstance = modalInstance;                angular.forEach(modalOptions.resolve, function (value, key) {                  ctrlLocals[key] = tplAndVars[resolveIter++];                });                ctrlInstance = $controller(modalOptions.controller, ctrlLocals);                if (modalOptions.controllerAs) {                  modalScope[modalOptions.controllerAs] = ctrlInstance;                }              }              $modalStack.open(modalInstance, {                scope: modalScope,                deferred: modalResultDeferred,                content: tplAndVars[0],                backdrop: modalOptions.backdrop,                keyboard: modalOptions.keyboard,                backdropClass: modalOptions.backdropClass,                windowClass: modalOptions.windowClass,                windowTemplateUrl: modalOptions.windowTemplateUrl,                size: modalOptions.size              });            }, function resolveError(reason) {              modalResultDeferred.reject(reason);            });            templateAndResolvePromise.then(function () {              modalOpenedDeferred.resolve(true);            }, function () {              modalOpenedDeferred.reject(false);            });            return modalInstance;          };          return $modal;        }]    };    return $modalProvider;  });angular.module('ui.bootstrap.pagination', []).controller('PaginationController', ['$scope', '$attrs', '$parse', function ($scope, $attrs, $parse) {  var self = this,      ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl      setNumPages = $attrs.numPages ? $parse($attrs.numPages).assign : angular.noop;  this.init = function(ngModelCtrl_, config) {    ngModelCtrl = ngModelCtrl_;    this.config = config;    ngModelCtrl.$render = function() {      self.render();    };    if ($attrs.itemsPerPage) {      $scope.$parent.$watch($parse($attrs.itemsPerPage), function(value) {        self.itemsPerPage = parseInt(value, 10);        $scope.totalPages = self.calculateTotalPages();      });    } else {      this.itemsPerPage = config.itemsPerPage;    }  };  this.calculateTotalPages = function() {    var totalPages = this.itemsPerPage < 1 ? 1 : Math.ceil($scope.totalItems / this.itemsPerPage);    return Math.max(totalPages || 0, 1);  };  this.render = function() {    $scope.page = parseInt(ngModelCtrl.$viewValue, 10) || 1;  };  $scope.selectPage = function(page) {    if ( $scope.page !== page && page > 0 && page <= $scope.totalPages) {      ngModelCtrl.$setViewValue(page);      ngModelCtrl.$render();    }  };  $scope.getText = function( key ) {    return $scope[key + 'Text'] || self.config[key + 'Text'];  };  $scope.noPrevious = function() {    return $scope.page === 1;  };  $scope.noNext = function() {    return $scope.page === $scope.totalPages;  };  $scope.$watch('totalItems', function() {    $scope.totalPages = self.calculateTotalPages();  });  $scope.$watch('totalPages', function(value) {    setNumPages($scope.$parent, value); // Readonly variable    if ( $scope.page > value ) {      $scope.selectPage(value);    } else {      ngModelCtrl.$render();    }  });}]).constant('paginationConfig', {  itemsPerPage: 10,  boundaryLinks: false,  directionLinks: true,  firstText: 'First',  previousText: 'Previous',  nextText: 'Next',  lastText: 'Last',  rotate: true}).directive('pagination', ['$parse', 'paginationConfig', function($parse, paginationConfig) {  return {    restrict: 'EA',    scope: {      totalItems: '=',      firstText: '@',      previousText: '@',      nextText: '@',      lastText: '@'    },    require: ['pagination', '?ngModel'],    controller: 'PaginationController',    templateUrl: 'template/pagination/pagination.html',    replace: true,    link: function(scope, element, attrs, ctrls) {      var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1];      if (!ngModelCtrl) {         return; // do nothing if no ng-model      }      // Setup configuration parameters      var maxSize = angular.isDefined(attrs.maxSize) ? scope.$parent.$eval(attrs.maxSize) : paginationConfig.maxSize,          rotate = angular.isDefined(attrs.rotate) ? scope.$parent.$eval(attrs.rotate) : paginationConfig.rotate;      scope.boundaryLinks = angular.isDefined(attrs.boundaryLinks) ? scope.$parent.$eval(attrs.boundaryLinks) : paginationConfig.boundaryLinks;      scope.directionLinks = angular.isDefined(attrs.directionLinks) ? scope.$parent.$eval(attrs.directionLinks) : paginationConfig.directionLinks;      paginationCtrl.init(ngModelCtrl, paginationConfig);      if (attrs.maxSize) {        scope.$parent.$watch($parse(attrs.maxSize), function(value) {          maxSize = parseInt(value, 10);          paginationCtrl.render();        });      }      // Create page object used in template      function makePage(number, text, isActive) {        return {          number: number,          text: text,          active: isActive        };      }      function getPages(currentPage, totalPages) {        var pages = [];        // Default page limits        var startPage = 1, endPage = totalPages;        var isMaxSized = ( angular.isDefined(maxSize) && maxSize < totalPages );        // recompute if maxSize        if ( isMaxSized ) {          if ( rotate ) {            // Current page is displayed in the middle of the visible ones            startPage = Math.max(currentPage - Math.floor(maxSize/2), 1);            endPage   = startPage + maxSize - 1;            // Adjust if limit is exceeded            if (endPage > totalPages) {              endPage   = totalPages;              startPage = endPage - maxSize + 1;            }          } else {            // Visible pages are paginated with maxSize            startPage = ((Math.ceil(currentPage / maxSize) - 1) * maxSize) + 1;            // Adjust last page if limit is exceeded            endPage = Math.min(startPage + maxSize - 1, totalPages);          }        }        // Add page number links        for (var number = startPage; number <= endPage; number++) {          var page = makePage(number, number, number === currentPage);          pages.push(page);        }        // Add links to move between page sets        if ( isMaxSized && ! rotate ) {          if ( startPage > 1 ) {            var previousPageSet = makePage(startPage - 1, '...', false);            pages.unshift(previousPageSet);          }          if ( endPage < totalPages ) {            var nextPageSet = makePage(endPage + 1, '...', false);            pages.push(nextPageSet);          }        }        return pages;      }      var originalRender = paginationCtrl.render;      paginationCtrl.render = function() {        originalRender();        if (scope.page > 0 && scope.page <= scope.totalPages) {          scope.pages = getPages(scope.page, scope.totalPages);        }      };    }  };}]).constant('pagerConfig', {  itemsPerPage: 10,  previousText: '« Previous',  nextText: 'Next »',  align: true}).directive('pager', ['pagerConfig', function(pagerConfig) {  return {    restrict: 'EA',    scope: {      totalItems: '=',      previousText: '@',      nextText: '@'    },    require: ['pager', '?ngModel'],    controller: 'PaginationController',    templateUrl: 'template/pagination/pager.html',    replace: true,    link: function(scope, element, attrs, ctrls) {      var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1];      if (!ngModelCtrl) {         return; // do nothing if no ng-model      }      scope.align = angular.isDefined(attrs.align) ? scope.$parent.$eval(attrs.align) : pagerConfig.align;      paginationCtrl.init(ngModelCtrl, pagerConfig);    }  };}]);/** * The following features are still outstanding: animation as a * function, placement as a function, inside, support for more triggers than * just mouse enter/leave, html tooltips, and selector delegation. */angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap.bindHtml' ] )/** * The $tooltip service creates tooltip- and popover-like directives as well as * houses global options for them. */.provider( '$tooltip', function () {  // The default options tooltip and popover.  var defaultOptions = {    placement: 'top',    animation: true,    popupDelay: 0  };  // Default hide triggers for each show trigger  var triggerMap = {    'mouseenter': 'mouseleave',    'click': 'click',    'focus': 'blur'  };  // The options specified to the provider globally.  var globalOptions = {};  /**   * `options({})` allows global configuration of all tooltips in the   * application.   *   *   var app = angular.module( 'App', ['ui.bootstrap.tooltip'], function( $tooltipProvider ) {   *     // place tooltips left instead of top by default   *     $tooltipProvider.options( { placement: 'left' } );   *   });   */	this.options = function( value ) {		angular.extend( globalOptions, value );	};  /**   * This allows you to extend the set of trigger mappings available. E.g.:   *   *   $tooltipProvider.setTriggers( 'openTrigger': 'closeTrigger' );   */  this.setTriggers = function setTriggers ( triggers ) {    angular.extend( triggerMap, triggers );  };  /**   * This is a helper function for translating camel-case to snake-case.   */  function snake_case(name){    var regexp = /[A-Z]/g;    var separator = '-';    return name.replace(regexp, function(letter, pos) {      return (pos ? separator : '') + letter.toLowerCase();    });  }  /**   * Returns the actual instance of the $tooltip service.   * TODO support multiple triggers   */  this.$get = [ '$window', '$compile', '$timeout', '$document', '$position', '$interpolate', function ( $window, $compile, $timeout, $document, $position, $interpolate ) {    return function $tooltip ( type, prefix, defaultTriggerShow ) {      var options = angular.extend( {}, defaultOptions, globalOptions );      /**       * Returns an object of show and hide triggers.       *       * If a trigger is supplied,       * it is used to show the tooltip; otherwise, it will use the `trigger`       * option passed to the `$tooltipProvider.options` method; else it will       * default to the trigger supplied to this directive factory.       *       * The hide trigger is based on the show trigger. If the `trigger` option       * was passed to the `$tooltipProvider.options` method, it will use the       * mapped trigger from `triggerMap` or the passed trigger if the map is       * undefined; otherwise, it uses the `triggerMap` value of the show       * trigger; else it will just use the show trigger.       */      function getTriggers ( trigger ) {        var show = trigger || options.trigger || defaultTriggerShow;        var hide = triggerMap[show] || show;        return {          show: show,          hide: hide        };      }      var directiveName = snake_case( type );      var startSym = $interpolate.startSymbol();      var endSym = $interpolate.endSymbol();      var template =        '<div '+ directiveName +'-popup '+          'title="'+startSym+'title'+endSym+'" '+          'content="'+startSym+'content'+endSym+'" '+          'placement="'+startSym+'placement'+endSym+'" '+          'animation="animation" '+          'is-open="isOpen"'+          '>'+        '</div>';      return {        restrict: 'EA',        compile: function (tElem, tAttrs) {          var tooltipLinker = $compile( template );          return function link ( scope, element, attrs ) {            var tooltip;            var tooltipLinkedScope;            var transitionTimeout;            var popupTimeout;            var appendToBody = angular.isDefined( options.appendToBody ) ? options.appendToBody : false;            var triggers = getTriggers( undefined );            var hasEnableExp = angular.isDefined(attrs[prefix+'Enable']);            var ttScope = scope.$new(true);            var positionTooltip = function () {              var ttPosition = $position.positionElements(element, tooltip, ttScope.placement, appendToBody);              ttPosition.top += 'px';              ttPosition.left += 'px';              // Now set the calculated positioning.              tooltip.css( ttPosition );            };            // By default, the tooltip is not open.            // TODO add ability to start tooltip opened            ttScope.isOpen = false;            function toggleTooltipBind () {              if ( ! ttScope.isOpen ) {                showTooltipBind();              } else {                hideTooltipBind();              }            }            // Show the tooltip with delay if specified, otherwise show it immediately            function showTooltipBind() {              if(hasEnableExp && !scope.$eval(attrs[prefix+'Enable'])) {                return;              }              prepareTooltip();              if ( ttScope.popupDelay ) {                // Do nothing if the tooltip was already scheduled to pop-up.                // This happens if show is triggered multiple times before any hide is triggered.                if (!popupTimeout) {                  popupTimeout = $timeout( show, ttScope.popupDelay, false );                  popupTimeout.then(function(reposition){reposition();});                }              } else {                show()();              }            }            function hideTooltipBind () {              scope.$apply(function () {                hide();              });            }            // Show the tooltip popup element.            function show() {              popupTimeout = null;              // If there is a pending remove transition, we must cancel it, lest the              // tooltip be mysteriously removed.              if ( transitionTimeout ) {                $timeout.cancel( transitionTimeout );                transitionTimeout = null;              }              // Don't show empty tooltips.              if ( ! ttScope.content ) {                return angular.noop;              }              createTooltip();              // Set the initial positioning.              tooltip.css({ top: 0, left: 0, display: 'block' });              ttScope.$digest();              positionTooltip();              // And show the tooltip.              ttScope.isOpen = true;              ttScope.$digest(); // digest required as $apply is not called              // Return positioning function as promise callback for correct              // positioning after draw.              return positionTooltip;            }            // Hide the tooltip popup element.            function hide() {              // First things first: we don't show it anymore.              ttScope.isOpen = false;              //if tooltip is going to be shown after delay, we must cancel this              $timeout.cancel( popupTimeout );              popupTimeout = null;              // And now we remove it from the DOM. However, if we have animation, we              // need to wait for it to expire beforehand.              // FIXME: this is a placeholder for a port of the transitions library.              if ( ttScope.animation ) {                if (!transitionTimeout) {                  transitionTimeout = $timeout(removeTooltip, 500);                }              } else {                removeTooltip();              }            }            function createTooltip() {              // There can only be one tooltip element per directive shown at once.              if (tooltip) {                removeTooltip();              }              tooltipLinkedScope = ttScope.$new();              tooltip = tooltipLinker(tooltipLinkedScope, function (tooltip) {                if ( appendToBody ) {                  $document.find( 'body' ).append( tooltip );                } else {                  element.after( tooltip );                }              });            }            function removeTooltip() {              transitionTimeout = null;              if (tooltip) {                tooltip.remove();                tooltip = null;              }              if (tooltipLinkedScope) {                tooltipLinkedScope.$destroy();                tooltipLinkedScope = null;              }            }            function prepareTooltip() {              prepPlacement();              prepPopupDelay();            }            /**             * Observe the relevant attributes.             */            attrs.$observe( type, function ( val ) {              ttScope.content = val;              if (!val && ttScope.isOpen ) {                hide();              }            });            attrs.$observe( prefix+'Title', function ( val ) {              ttScope.title = val;            });            function prepPlacement() {              var val = attrs[ prefix + 'Placement' ];              ttScope.placement = angular.isDefined( val ) ? val : options.placement;            }            function prepPopupDelay() {              var val = attrs[ prefix + 'PopupDelay' ];              var delay = parseInt( val, 10 );              ttScope.popupDelay = ! isNaN(delay) ? delay : options.popupDelay;            }            var unregisterTriggers = function () {              element.unbind(triggers.show, showTooltipBind);              element.unbind(triggers.hide, hideTooltipBind);            };            function prepTriggers() {              var val = attrs[ prefix + 'Trigger' ];              unregisterTriggers();              triggers = getTriggers( val );              if ( triggers.show === triggers.hide ) {                element.bind( triggers.show, toggleTooltipBind );              } else {                element.bind( triggers.show, showTooltipBind );                element.bind( triggers.hide, hideTooltipBind );              }            }            prepTriggers();            var animation = scope.$eval(attrs[prefix + 'Animation']);            ttScope.animation = angular.isDefined(animation) ? !!animation : options.animation;            var appendToBodyVal = scope.$eval(attrs[prefix + 'AppendToBody']);            appendToBody = angular.isDefined(appendToBodyVal) ? appendToBodyVal : appendToBody;            // if a tooltip is attached to <body> we need to remove it on            // location change as its parent scope will probably not be destroyed            // by the change.            if ( appendToBody ) {              scope.$on('$locationChangeSuccess', function closeTooltipOnLocationChangeSuccess () {              if ( ttScope.isOpen ) {                hide();              }            });            }            // Make sure tooltip is destroyed and removed.            scope.$on('$destroy', function onDestroyTooltip() {              $timeout.cancel( transitionTimeout );              $timeout.cancel( popupTimeout );              unregisterTriggers();              removeTooltip();              ttScope = null;            });          };        }      };    };  }];}).directive( 'tooltipPopup', function () {  return {    restrict: 'EA',    replace: true,    scope: { content: '@', placement: '@', animation: '&', isOpen: '&' },    templateUrl: 'template/tooltip/tooltip-popup.html'  };}).directive( 'tooltip', [ '$tooltip', function ( $tooltip ) {  return $tooltip( 'tooltip', 'tooltip', 'mouseenter' );}]).directive( 'tooltipHtmlUnsafePopup', function () {  return {    restrict: 'EA',    replace: true,    scope: { content: '@', placement: '@', animation: '&', isOpen: '&' },    templateUrl: 'template/tooltip/tooltip-html-unsafe-popup.html'  };}).directive( 'tooltipHtmlUnsafe', [ '$tooltip', function ( $tooltip ) {  return $tooltip( 'tooltipHtmlUnsafe', 'tooltip', 'mouseenter' );}]);/** * The following features are still outstanding: popup delay, animation as a * function, placement as a function, inside, support for more triggers than * just mouse enter/leave, html popovers, and selector delegatation. */angular.module( 'ui.bootstrap.popover', [ 'ui.bootstrap.tooltip' ] ).directive( 'popoverPopup', function () {  return {    restrict: 'EA',    replace: true,    scope: { title: '@', content: '@', placement: '@', animation: '&', isOpen: '&' },    templateUrl: 'template/popover/popover.html'  };}).directive( 'popover', [ '$tooltip', function ( $tooltip ) {  return $tooltip( 'popover', 'popover', 'click' );}]);angular.module('ui.bootstrap.progressbar', []).constant('progressConfig', {  animate: true,  max: 100}).controller('ProgressController', ['$scope', '$attrs', 'progressConfig', function($scope, $attrs, progressConfig) {    var self = this,        animate = angular.isDefined($attrs.animate) ? $scope.$parent.$eval($attrs.animate) : progressConfig.animate;    this.bars = [];    $scope.max = angular.isDefined($attrs.max) ? $scope.$parent.$eval($attrs.max) : progressConfig.max;    this.addBar = function(bar, element) {        if ( !animate ) {            element.css({'transition': 'none'});        }        this.bars.push(bar);        bar.$watch('value', function( value ) {            bar.percent = +(100 * value / $scope.max).toFixed(2);        });        bar.$on('$destroy', function() {            element = null;            self.removeBar(bar);        });    };    this.removeBar = function(bar) {        this.bars.splice(this.bars.indexOf(bar), 1);    };}]).directive('progress', function() {    return {        restrict: 'EA',        replace: true,        transclude: true,        controller: 'ProgressController',        require: 'progress',        scope: {},        templateUrl: 'template/progressbar/progress.html'    };}).directive('bar', function() {    return {        restrict: 'EA',        replace: true,        transclude: true,        require: '^progress',        scope: {            value: '=',            type: '@'        },        templateUrl: 'template/progressbar/bar.html',        link: function(scope, element, attrs, progressCtrl) {            progressCtrl.addBar(scope, element);        }    };}).directive('progressbar', function() {    return {        restrict: 'EA',        replace: true,        transclude: true,        controller: 'ProgressController',        scope: {            value: '=',            type: '@'        },        templateUrl: 'template/progressbar/progressbar.html',        link: function(scope, element, attrs, progressCtrl) {            progressCtrl.addBar(scope, angular.element(element.children()[0]));        }    };});angular.module('ui.bootstrap.rating', []).constant('ratingConfig', {  max: 5,  stateOn: null,  stateOff: null}).controller('RatingController', ['$scope', '$attrs', 'ratingConfig', function($scope, $attrs, ratingConfig) {  var ngModelCtrl  = { $setViewValue: angular.noop };  this.init = function(ngModelCtrl_) {    ngModelCtrl = ngModelCtrl_;    ngModelCtrl.$render = this.render;    this.stateOn = angular.isDefined($attrs.stateOn) ? $scope.$parent.$eval($attrs.stateOn) : ratingConfig.stateOn;    this.stateOff = angular.isDefined($attrs.stateOff) ? $scope.$parent.$eval($attrs.stateOff) : ratingConfig.stateOff;    var ratingStates = angular.isDefined($attrs.ratingStates) ? $scope.$parent.$eval($attrs.ratingStates) :                        new Array( angular.isDefined($attrs.max) ? $scope.$parent.$eval($attrs.max) : ratingConfig.max );    $scope.range = this.buildTemplateObjects(ratingStates);  };  this.buildTemplateObjects = function(states) {    for (var i = 0, n = states.length; i < n; i++) {      states[i] = angular.extend({ index: i }, { stateOn: this.stateOn, stateOff: this.stateOff }, states[i]);    }    return states;  };  $scope.rate = function(value) {    if ( !$scope.readonly && value >= 0 && value <= $scope.range.length ) {      ngModelCtrl.$setViewValue(value);      ngModelCtrl.$render();    }  };  $scope.enter = function(value) {    if ( !$scope.readonly ) {      $scope.value = value;    }    $scope.onHover({value: value});  };  $scope.reset = function() {    $scope.value = ngModelCtrl.$viewValue;    $scope.onLeave();  };  $scope.onKeydown = function(evt) {    if (/(37|38|39|40)/.test(evt.which)) {      evt.preventDefault();      evt.stopPropagation();      $scope.rate( $scope.value + (evt.which === 38 || evt.which === 39 ? 1 : -1) );    }  };  this.render = function() {    $scope.value = ngModelCtrl.$viewValue;  };}]).directive('rating', function() {  return {    restrict: 'EA',    require: ['rating', 'ngModel'],    scope: {      readonly: '=?',      onHover: '&',      onLeave: '&'    },    controller: 'RatingController',    templateUrl: 'template/rating/rating.html',    replace: true,    link: function(scope, element, attrs, ctrls) {      var ratingCtrl = ctrls[0], ngModelCtrl = ctrls[1];      if ( ngModelCtrl ) {        ratingCtrl.init( ngModelCtrl );      }    }  };});/** * @ngdoc overview * @name ui.bootstrap.tabs * * @description * AngularJS version of the tabs directive. */angular.module('ui.bootstrap.tabs', []).controller('TabsetController', ['$scope', function TabsetCtrl($scope) {  var ctrl = this,      tabs = ctrl.tabs = $scope.tabs = [];  ctrl.select = function(selectedTab) {    angular.forEach(tabs, function(tab) {      if (tab.active && tab !== selectedTab) {        tab.active = false;        tab.onDeselect();      }    });    selectedTab.active = true;    selectedTab.onSelect();  };  ctrl.addTab = function addTab(tab) {    tabs.push(tab);    // we can't run the select function on the first tab    // since that would select it twice    if (tabs.length === 1) {      tab.active = true;    } else if (tab.active) {      ctrl.select(tab);    }  };  ctrl.removeTab = function removeTab(tab) {    var index = tabs.indexOf(tab);    //Select a new tab if the tab to be removed is selected and not destroyed    if (tab.active && tabs.length > 1 && !destroyed) {      //If this is the last tab, select the previous tab. else, the next tab.      var newActiveIndex = index == tabs.length - 1 ? index - 1 : index + 1;      ctrl.select(tabs[newActiveIndex]);    }    tabs.splice(index, 1);  };  var destroyed;  $scope.$on('$destroy', function() {    destroyed = true;  });}])/** * @ngdoc directive * @name ui.bootstrap.tabs.directive:tabset * @restrict EA * * @description * Tabset is the outer container for the tabs directive * * @param {boolean=} vertical Whether or not to use vertical styling for the tabs. * @param {boolean=} justified Whether or not to use justified styling for the tabs. * * @example<example module="ui.bootstrap">  <file name="index.html">    <tabset>      <tab heading="Tab 1"><b>First</b> Content!</tab>      <tab heading="Tab 2"><i>Second</i> Content!</tab>    </tabset>    <hr />    <tabset vertical="true">      <tab heading="Vertical Tab 1"><b>First</b> Vertical Content!</tab>      <tab heading="Vertical Tab 2"><i>Second</i> Vertical Content!</tab>    </tabset>    <tabset justified="true">      <tab heading="Justified Tab 1"><b>First</b> Justified Content!</tab>      <tab heading="Justified Tab 2"><i>Second</i> Justified Content!</tab>    </tabset>  </file></example> */.directive('tabset', function() {  return {    restrict: 'EA',    transclude: true,    replace: true,    scope: {      type: '@'    },    controller: 'TabsetController',    templateUrl: 'template/tabs/tabset.html',    link: function(scope, element, attrs) {      scope.vertical = angular.isDefined(attrs.vertical) ? scope.$parent.$eval(attrs.vertical) : false;      scope.justified = angular.isDefined(attrs.justified) ? scope.$parent.$eval(attrs.justified) : false;    }  };})/** * @ngdoc directive * @name ui.bootstrap.tabs.directive:tab * @restrict EA * * @param {string=} heading The visible heading, or title, of the tab. Set HTML headings with {@link ui.bootstrap.tabs.directive:tabHeading tabHeading}. * @param {string=} select An expression to evaluate when the tab is selected. * @param {boolean=} active A binding, telling whether or not this tab is selected. * @param {boolean=} disabled A binding, telling whether or not this tab is disabled. * * @description * Creates a tab with a heading and content. Must be placed within a {@link ui.bootstrap.tabs.directive:tabset tabset}. * * @example<example module="ui.bootstrap">  <file name="index.html">    <div ng-controller="TabsDemoCtrl">      <button class="btn btn-small" ng-click="items[0].active = true">        Select item 1, using active binding      </button>      <button class="btn btn-small" ng-click="items[1].disabled = !items[1].disabled">        Enable/disable item 2, using disabled binding      </button>      <br />      <tabset>        <tab heading="Tab 1">First Tab</tab>        <tab select="alertMe()">          <tab-heading><i class="icon-bell"></i> Alert me!</tab-heading>          Second Tab, with alert callback and html heading!        </tab>        <tab ng-repeat="item in items"          heading="{{item.title}}"          disabled="item.disabled"          active="item.active">          {{item.content}}        </tab>      </tabset>    </div>  </file>  <file name="script.js">    function TabsDemoCtrl($scope) {      $scope.items = [        { title:"Dynamic Title 1", content:"Dynamic Item 0" },        { title:"Dynamic Title 2", content:"Dynamic Item 1", disabled: true }      ];      $scope.alertMe = function() {        setTimeout(function() {          alert("You've selected the alert tab!");        });      };    };  </file></example> *//** * @ngdoc directive * @name ui.bootstrap.tabs.directive:tabHeading * @restrict EA * * @description * Creates an HTML heading for a {@link ui.bootstrap.tabs.directive:tab tab}. Must be placed as a child of a tab element. * * @example<example module="ui.bootstrap">  <file name="index.html">    <tabset>      <tab>        <tab-heading><b>HTML</b> in my titles?!</tab-heading>        And some content, too!      </tab>      <tab>        <tab-heading><i class="icon-heart"></i> Icon heading?!?</tab-heading>        That's right.      </tab>    </tabset>  </file></example> */.directive('tab', ['$parse', function($parse) {  return {    require: '^tabset',    restrict: 'EA',    replace: true,    templateUrl: 'template/tabs/tab.html',    transclude: true,    scope: {      active: '=?',      heading: '@',      onSelect: '&select', //This callback is called in contentHeadingTransclude                          //once it inserts the tab's content into the dom      onDeselect: '&deselect'    },    controller: function() {      //Empty controller so other directives can require being 'under' a tab    },    compile: function(elm, attrs, transclude) {      return function postLink(scope, elm, attrs, tabsetCtrl) {        scope.$watch('active', function(active) {          if (active) {            tabsetCtrl.select(scope);          }        });        scope.disabled = false;        if ( attrs.disabled ) {          scope.$parent.$watch($parse(attrs.disabled), function(value) {            scope.disabled = !! value;          });        }        scope.select = function() {          if ( !scope.disabled ) {            scope.active = true;          }        };        tabsetCtrl.addTab(scope);        scope.$on('$destroy', function() {          tabsetCtrl.removeTab(scope);        });        //We need to transclude later, once the content container is ready.        //when this link happens, we're inside a tab heading.        scope.$transcludeFn = transclude;      };    }  };}]).directive('tabHeadingTransclude', [function() {  return {    restrict: 'A',    require: '^tab',    link: function(scope, elm, attrs, tabCtrl) {      scope.$watch('headingElement', function updateHeadingElement(heading) {        if (heading) {          elm.html('');          elm.append(heading);        }      });    }  };}]).directive('tabContentTransclude', function() {  return {    restrict: 'A',    require: '^tabset',    link: function(scope, elm, attrs) {      var tab = scope.$eval(attrs.tabContentTransclude);      //Now our tab is ready to be transcluded: both the tab heading area      //and the tab content area are loaded.  Transclude 'em both.      tab.$transcludeFn(tab.$parent, function(contents) {        angular.forEach(contents, function(node) {          if (isTabHeading(node)) {            //Let tabHeadingTransclude know.            tab.headingElement = node;          } else {            elm.append(node);          }        });      });    }  };  function isTabHeading(node) {    return node.tagName &&  (      node.hasAttribute('tab-heading') ||      node.hasAttribute('data-tab-heading') ||      node.tagName.toLowerCase() === 'tab-heading' ||      node.tagName.toLowerCase() === 'data-tab-heading'    );  }});angular.module('ui.bootstrap.timepicker', []).constant('timepickerConfig', {  hourStep: 1,  minuteStep: 1,  showMeridian: true,  meridians: null,  readonlyInput: false,  mousewheel: true}).controller('TimepickerController', ['$scope', '$attrs', '$parse', '$log', '$locale', 'timepickerConfig', function($scope, $attrs, $parse, $log, $locale, timepickerConfig) {  var selected = new Date(),      ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl      meridians = angular.isDefined($attrs.meridians) ? $scope.$parent.$eval($attrs.meridians) : timepickerConfig.meridians || $locale.DATETIME_FORMATS.AMPMS;  this.init = function( ngModelCtrl_, inputs ) {    ngModelCtrl = ngModelCtrl_;    ngModelCtrl.$render = this.render;    var hoursInputEl = inputs.eq(0),        minutesInputEl = inputs.eq(1);    var mousewheel = angular.isDefined($attrs.mousewheel) ? $scope.$parent.$eval($attrs.mousewheel) : timepickerConfig.mousewheel;    if ( mousewheel ) {      this.setupMousewheelEvents( hoursInputEl, minutesInputEl );    }    $scope.readonlyInput = angular.isDefined($attrs.readonlyInput) ? $scope.$parent.$eval($attrs.readonlyInput) : timepickerConfig.readonlyInput;    this.setupInputEvents( hoursInputEl, minutesInputEl );  };  var hourStep = timepickerConfig.hourStep;  if ($attrs.hourStep) {    $scope.$parent.$watch($parse($attrs.hourStep), function(value) {      hourStep = parseInt(value, 10);    });  }  var minuteStep = timepickerConfig.minuteStep;  if ($attrs.minuteStep) {    $scope.$parent.$watch($parse($attrs.minuteStep), function(value) {      minuteStep = parseInt(value, 10);    });  }  // 12H / 24H mode  $scope.showMeridian = timepickerConfig.showMeridian;  if ($attrs.showMeridian) {    $scope.$parent.$watch($parse($attrs.showMeridian), function(value) {      $scope.showMeridian = !!value;      if ( ngModelCtrl.$error.time ) {        // Evaluate from template        var hours = getHoursFromTemplate(), minutes = getMinutesFromTemplate();        if (angular.isDefined( hours ) && angular.isDefined( minutes )) {          selected.setHours( hours );          refresh();        }      } else {        updateTemplate();      }    });  }  // Get $scope.hours in 24H mode if valid  function getHoursFromTemplate ( ) {    var hours = parseInt( $scope.hours, 10 );    var valid = ( $scope.showMeridian ) ? (hours > 0 && hours < 13) : (hours >= 0 && hours < 24);    if ( !valid ) {      return undefined;    }    if ( $scope.showMeridian ) {      if ( hours === 12 ) {        hours = 0;      }      if ( $scope.meridian === meridians[1] ) {        hours = hours + 12;      }    }    return hours;  }  function getMinutesFromTemplate() {    var minutes = parseInt($scope.minutes, 10);    return ( minutes >= 0 && minutes < 60 ) ? minutes : undefined;  }  function pad( value ) {    return ( angular.isDefined(value) && value.toString().length < 2 ) ? '0' + value : value;  }  // Respond on mousewheel spin  this.setupMousewheelEvents = function( hoursInputEl, minutesInputEl ) {    var isScrollingUp = function(e) {      if (e.originalEvent) {        e = e.originalEvent;      }      //pick correct delta variable depending on event      var delta = (e.wheelDelta) ? e.wheelDelta : -e.deltaY;      return (e.detail || delta > 0);    };    hoursInputEl.bind('mousewheel wheel', function(e) {      $scope.$apply( (isScrollingUp(e)) ? $scope.incrementHours() : $scope.decrementHours() );      e.preventDefault();    });    minutesInputEl.bind('mousewheel wheel', function(e) {      $scope.$apply( (isScrollingUp(e)) ? $scope.incrementMinutes() : $scope.decrementMinutes() );      e.preventDefault();    });  };  this.setupInputEvents = function( hoursInputEl, minutesInputEl ) {    if ( $scope.readonlyInput ) {      $scope.updateHours = angular.noop;      $scope.updateMinutes = angular.noop;      return;    }    var invalidate = function(invalidHours, invalidMinutes) {      ngModelCtrl.$setViewValue( null );      ngModelCtrl.$setValidity('time', false);      if (angular.isDefined(invalidHours)) {        $scope.invalidHours = invalidHours;      }      if (angular.isDefined(invalidMinutes)) {        $scope.invalidMinutes = invalidMinutes;      }    };    $scope.updateHours = function() {      var hours = getHoursFromTemplate();      if ( angular.isDefined(hours) ) {        selected.setHours( hours );        refresh( 'h' );      } else {        invalidate(true);      }    };    hoursInputEl.bind('blur', function(e) {      if ( !$scope.invalidHours && $scope.hours < 10) {        $scope.$apply( function() {          $scope.hours = pad( $scope.hours );        });      }    });    $scope.updateMinutes = function() {      var minutes = getMinutesFromTemplate();      if ( angular.isDefined(minutes) ) {        selected.setMinutes( minutes );        refresh( 'm' );      } else {        invalidate(undefined, true);      }    };    minutesInputEl.bind('blur', function(e) {      if ( !$scope.invalidMinutes && $scope.minutes < 10 ) {        $scope.$apply( function() {          $scope.minutes = pad( $scope.minutes );        });      }    });  };  this.render = function() {    var date = ngModelCtrl.$modelValue ? new Date( ngModelCtrl.$modelValue ) : null;    if ( isNaN(date) ) {      ngModelCtrl.$setValidity('time', false);      $log.error('Timepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.');    } else {      if ( date ) {        selected = date;      }      makeValid();      updateTemplate();    }  };  // Call internally when we know that model is valid.  function refresh( keyboardChange ) {    makeValid();    ngModelCtrl.$setViewValue( new Date(selected) );    updateTemplate( keyboardChange );  }  function makeValid() {    ngModelCtrl.$setValidity('time', true);    $scope.invalidHours = false;    $scope.invalidMinutes = false;  }  function updateTemplate( keyboardChange ) {    var hours = selected.getHours(), minutes = selected.getMinutes();    if ( $scope.showMeridian ) {      hours = ( hours === 0 || hours === 12 ) ? 12 : hours % 12; // Convert 24 to 12 hour system    }    $scope.hours = keyboardChange === 'h' ? hours : pad(hours);    $scope.minutes = keyboardChange === 'm' ? minutes : pad(minutes);    $scope.meridian = selected.getHours() < 12 ? meridians[0] : meridians[1];  }  function addMinutes( minutes ) {    var dt = new Date( selected.getTime() + minutes * 60000 );    selected.setHours( dt.getHours(), dt.getMinutes() );    refresh();  }  $scope.incrementHours = function() {    addMinutes( hourStep * 60 );  };  $scope.decrementHours = function() {    addMinutes( - hourStep * 60 );  };  $scope.incrementMinutes = function() {    addMinutes( minuteStep );  };  $scope.decrementMinutes = function() {    addMinutes( - minuteStep );  };  $scope.toggleMeridian = function() {    addMinutes( 12 * 60 * (( selected.getHours() < 12 ) ? 1 : -1) );  };}]).directive('timepicker', function () {  return {    restrict: 'EA',    require: ['timepicker', '?^ngModel'],    controller:'TimepickerController',    replace: true,    scope: {},    templateUrl: 'template/timepicker/timepicker.html',    link: function(scope, element, attrs, ctrls) {      var timepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1];      if ( ngModelCtrl ) {        timepickerCtrl.init( ngModelCtrl, element.find('input') );      }    }  };});angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap.bindHtml'])/** * A helper service that can parse typeahead's syntax (string provided by users) * Extracted to a separate service for ease of unit testing */  .factory('typeaheadParser', ['$parse', function ($parse) {  //                      00000111000000000000022200000000000000003333333333333330000000000044000  var TYPEAHEAD_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w\d]*))\s+in\s+([\s\S]+?)$/;  return {    parse:function (input) {      var match = input.match(TYPEAHEAD_REGEXP);      if (!match) {        throw new Error(          'Expected typeahead specification in form of "_modelValue_ (as _label_)? for _item_ in _collection_"' +            ' but got "' + input + '".');      }      return {        itemName:match[3],        source:$parse(match[4]),        viewMapper:$parse(match[2] || match[1]),        modelMapper:$parse(match[1])      };    }  };}])  .directive('typeahead', ['$compile', '$parse', '$q', '$timeout', '$document', '$position', 'typeaheadParser',    function ($compile, $parse, $q, $timeout, $document, $position, typeaheadParser) {  var HOT_KEYS = [9, 13, 27, 38, 40];  return {    require:'ngModel',    link:function (originalScope, element, attrs, modelCtrl) {      //SUPPORTED ATTRIBUTES (OPTIONS)      //minimal no of characters that needs to be entered before typeahead kicks-in      var minSearch = originalScope.$eval(attrs.typeaheadMinLength) || 1;      //minimal wait time after last character typed before typehead kicks-in      var waitTime = originalScope.$eval(attrs.typeaheadWaitMs) || 0;      //should it restrict model values to the ones selected from the popup only?      var isEditable = originalScope.$eval(attrs.typeaheadEditable) !== false;      //binding to a variable that indicates if matches are being retrieved asynchronously      var isLoadingSetter = $parse(attrs.typeaheadLoading).assign || angular.noop;      //a callback executed when a match is selected      var onSelectCallback = $parse(attrs.typeaheadOnSelect);      var inputFormatter = attrs.typeaheadInputFormatter ? $parse(attrs.typeaheadInputFormatter) : undefined;      var appendToBody =  attrs.typeaheadAppendToBody ? originalScope.$eval(attrs.typeaheadAppendToBody) : false;      var focusFirst = originalScope.$eval(attrs.typeaheadFocusFirst) !== false;      //INTERNAL VARIABLES      //model setter executed upon match selection      var $setModelValue = $parse(attrs.ngModel).assign;      //expressions used by typeahead      var parserResult = typeaheadParser.parse(attrs.typeahead);      var hasFocus;      //create a child scope for the typeahead directive so we are not polluting original scope      //with typeahead-specific data (matches, query etc.)      var scope = originalScope.$new();      originalScope.$on('$destroy', function(){        scope.$destroy();      });      // WAI-ARIA      var popupId = 'typeahead-' + scope.$id + '-' + Math.floor(Math.random() * 10000);      element.attr({        'aria-autocomplete': 'list',        'aria-expanded': false,        'aria-owns': popupId      });      //pop-up element used to display matches      var popUpEl = angular.element('<div typeahead-popup></div>');      popUpEl.attr({        id: popupId,        matches: 'matches',        active: 'activeIdx',        select: 'select(activeIdx)',        query: 'query',        position: 'position'      });      //custom item template      if (angular.isDefined(attrs.typeaheadTemplateUrl)) {        popUpEl.attr('template-url', attrs.typeaheadTemplateUrl);      }      var resetMatches = function() {        scope.matches = [];        scope.activeIdx = -1;        element.attr('aria-expanded', false);      };      var getMatchId = function(index) {        return popupId + '-option-' + index;      };      // Indicate that the specified match is the active (pre-selected) item in the list owned by this typeahead.      // This attribute is added or removed automatically when the `activeIdx` changes.      scope.$watch('activeIdx', function(index) {        if (index < 0) {          element.removeAttr('aria-activedescendant');        } else {          element.attr('aria-activedescendant', getMatchId(index));        }      });      var getMatchesAsync = function(inputValue) {        var locals = {$viewValue: inputValue};        isLoadingSetter(originalScope, true);        $q.when(parserResult.source(originalScope, locals)).then(function(matches) {          //it might happen that several async queries were in progress if a user were typing fast          //but we are interested only in responses that correspond to the current view value          var onCurrentRequest = (inputValue === modelCtrl.$viewValue);          if (onCurrentRequest && hasFocus) {            if (matches.length > 0) {              scope.activeIdx = focusFirst ? 0 : -1;              scope.matches.length = 0;              //transform labels              for(var i=0; i<matches.length; i++) {                locals[parserResult.itemName] = matches[i];                scope.matches.push({                  id: getMatchId(i),                  label: parserResult.viewMapper(scope, locals),                  model: matches[i]                });              }              scope.query = inputValue;              //position pop-up with matches - we need to re-calculate its position each time we are opening a window              //with matches as a pop-up might be absolute-positioned and position of an input might have changed on a page              //due to other elements being rendered              scope.position = appendToBody ? $position.offset(element) : $position.position(element);              scope.position.top = scope.position.top + element.prop('offsetHeight');              element.attr('aria-expanded', true);            } else {              resetMatches();            }          }          if (onCurrentRequest) {            isLoadingSetter(originalScope, false);          }        }, function(){          resetMatches();          isLoadingSetter(originalScope, false);        });      };      resetMatches();      //we need to propagate user's query so we can higlight matches      scope.query = undefined;      //Declare the timeout promise var outside the function scope so that stacked calls can be cancelled later       var timeoutPromise;      var scheduleSearchWithTimeout = function(inputValue) {        timeoutPromise = $timeout(function () {          getMatchesAsync(inputValue);        }, waitTime);      };      var cancelPreviousTimeout = function() {        if (timeoutPromise) {          $timeout.cancel(timeoutPromise);        }      };      //plug into $parsers pipeline to open a typeahead on view changes initiated from DOM      //$parsers kick-in on all the changes coming from the view as well as manually triggered by $setViewValue      modelCtrl.$parsers.unshift(function (inputValue) {        hasFocus = true;        if (inputValue && inputValue.length >= minSearch) {          if (waitTime > 0) {            cancelPreviousTimeout();            scheduleSearchWithTimeout(inputValue);          } else {            getMatchesAsync(inputValue);          }        } else {          isLoadingSetter(originalScope, false);          cancelPreviousTimeout();          resetMatches();        }        if (isEditable) {          return inputValue;        } else {          if (!inputValue) {            // Reset in case user had typed something previously.            modelCtrl.$setValidity('editable', true);            return inputValue;          } else {            modelCtrl.$setValidity('editable', false);            return undefined;          }        }      });      modelCtrl.$formatters.push(function (modelValue) {        var candidateViewValue, emptyViewValue;        var locals = {};        if (inputFormatter) {          locals.$model = modelValue;          return inputFormatter(originalScope, locals);        } else {          //it might happen that we don't have enough info to properly render input value          //we need to check for this situation and simply return model value if we can't apply custom formatting          locals[parserResult.itemName] = modelValue;          candidateViewValue = parserResult.viewMapper(originalScope, locals);          locals[parserResult.itemName] = undefined;          emptyViewValue = parserResult.viewMapper(originalScope, locals);          return candidateViewValue!== emptyViewValue ? candidateViewValue : modelValue;        }      });      scope.select = function (activeIdx) {        //called from within the $digest() cycle        var locals = {};        var model, item;        locals[parserResult.itemName] = item = scope.matches[activeIdx].model;        model = parserResult.modelMapper(originalScope, locals);        $setModelValue(originalScope, model);        modelCtrl.$setValidity('editable', true);        onSelectCallback(originalScope, {          $item: item,          $model: model,          $label: parserResult.viewMapper(originalScope, locals)        });        resetMatches();        //return focus to the input element if a match was selected via a mouse click event        // use timeout to avoid $rootScope:inprog error        $timeout(function() { element[0].focus(); }, 0, false);      };      //bind keyboard events: arrows up(38) / down(40), enter(13) and tab(9), esc(27)      element.bind('keydown', function (evt) {        //typeahead is open and an "interesting" key was pressed        if (scope.matches.length === 0 || HOT_KEYS.indexOf(evt.which) === -1) {          return;        }        // if there's nothing selected (i.e. focusFirst) and enter is hit, don't do anything        if (scope.activeIdx == -1 && (evt.which === 13 || evt.which === 9)) {          return;        }        evt.preventDefault();        if (evt.which === 40) {          scope.activeIdx = (scope.activeIdx + 1) % scope.matches.length;          scope.$digest();        } else if (evt.which === 38) {          scope.activeIdx = (scope.activeIdx > 0 ? scope.activeIdx : scope.matches.length) - 1;          scope.$digest();        } else if (evt.which === 13 || evt.which === 9) {          scope.$apply(function () {            scope.select(scope.activeIdx);          });        } else if (evt.which === 27) {          evt.stopPropagation();          resetMatches();          scope.$digest();        }      });      element.bind('blur', function (evt) {        hasFocus = false;      });      // Keep reference to click handler to unbind it.      var dismissClickHandler = function (evt) {        if (element[0] !== evt.target) {          resetMatches();          scope.$digest();        }      };      $document.bind('click', dismissClickHandler);      originalScope.$on('$destroy', function(){        $document.unbind('click', dismissClickHandler);        if (appendToBody) {          $popup.remove();        }      });      var $popup = $compile(popUpEl)(scope);      if (appendToBody) {        $document.find('body').append($popup);      } else {        element.after($popup);      }    }  };}])  .directive('typeaheadPopup', function () {    return {      restrict:'EA',      scope:{        matches:'=',        query:'=',        active:'=',        position:'=',        select:'&'      },      replace:true,      templateUrl:'template/typeahead/typeahead-popup.html',      link:function (scope, element, attrs) {        scope.templateUrl = attrs.templateUrl;        scope.isOpen = function () {          return scope.matches.length > 0;        };        scope.isActive = function (matchIdx) {          return scope.active == matchIdx;        };        scope.selectActive = function (matchIdx) {          scope.active = matchIdx;        };        scope.selectMatch = function (activeIdx) {          scope.select({activeIdx:activeIdx});        };      }    };  })  .directive('typeaheadMatch', ['$http', '$templateCache', '$compile', '$parse', function ($http, $templateCache, $compile, $parse) {    return {      restrict:'EA',      scope:{        index:'=',        match:'=',        query:'='      },      link:function (scope, element, attrs) {        var tplUrl = $parse(attrs.templateUrl)(scope.$parent) || 'template/typeahead/typeahead-match.html';        $http.get(tplUrl, {cache: $templateCache}).success(function(tplContent){           element.replaceWith($compile(tplContent.trim())(scope));        });      }    };  }])  .filter('typeaheadHighlight', function() {    function escapeRegexp(queryToEscape) {      return queryToEscape.replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1');    }    return function(matchItem, query) {      return query ? ('' + matchItem).replace(new RegExp(escapeRegexp(query), 'gi'), '<strong>$&</strong>') : matchItem;    };  });
 |