angular-strap.js 179 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526
  1. /**
  2. * angular-strap
  3. * @version v2.3.12 - 2017-01-26
  4. * @link http://mgcrea.github.io/angular-strap
  5. * @author Olivier Louvignes <olivier@mg-crea.com> (https://github.com/mgcrea)
  6. * @license MIT License, http://www.opensource.org/licenses/MIT
  7. */
  8. (function(window, document, undefined) {
  9. 'use strict';
  10. bsCompilerService.$inject = [ '$q', '$http', '$injector', '$compile', '$controller', '$templateCache' ];
  11. angular.module('mgcrea.ngStrap.typeahead', [ 'mgcrea.ngStrap.tooltip', 'mgcrea.ngStrap.helpers.parseOptions' ]).provider('$typeahead', function() {
  12. var defaults = this.defaults = {
  13. animation: 'am-fade',
  14. prefixClass: 'typeahead',
  15. prefixEvent: '$typeahead',
  16. placement: 'bottom-left',
  17. templateUrl: 'typeahead/typeahead.tpl.html',
  18. trigger: 'focus',
  19. container: false,
  20. keyboard: true,
  21. html: false,
  22. delay: 0,
  23. minLength: 1,
  24. filter: 'bsAsyncFilter',
  25. limit: 6,
  26. autoSelect: false,
  27. comparator: '',
  28. trimValue: true
  29. };
  30. this.$get = [ '$window', '$rootScope', '$tooltip', '$$rAF', '$timeout', function($window, $rootScope, $tooltip, $$rAF, $timeout) {
  31. function TypeaheadFactory(element, controller, config) {
  32. var $typeahead = {};
  33. var options = angular.extend({}, defaults, config);
  34. $typeahead = $tooltip(element, options);
  35. var parentScope = config.scope;
  36. var scope = $typeahead.$scope;
  37. scope.$resetMatches = function() {
  38. scope.$matches = [];
  39. scope.$activeIndex = options.autoSelect ? 0 : -1;
  40. };
  41. scope.$resetMatches();
  42. scope.$activate = function(index) {
  43. scope.$$postDigest(function() {
  44. $typeahead.activate(index);
  45. });
  46. };
  47. scope.$select = function(index, evt) {
  48. scope.$$postDigest(function() {
  49. $typeahead.select(index);
  50. });
  51. };
  52. scope.$isVisible = function() {
  53. return $typeahead.$isVisible();
  54. };
  55. $typeahead.update = function(matches) {
  56. scope.$matches = matches;
  57. if (scope.$activeIndex >= matches.length) {
  58. scope.$activeIndex = options.autoSelect ? 0 : -1;
  59. }
  60. safeDigest(scope);
  61. $$rAF($typeahead.$applyPlacement);
  62. };
  63. $typeahead.activate = function(index) {
  64. scope.$activeIndex = index;
  65. };
  66. $typeahead.select = function(index) {
  67. if (index === -1) return;
  68. var value = scope.$matches[index].value;
  69. controller.$setViewValue(value);
  70. controller.$render();
  71. scope.$resetMatches();
  72. if (parentScope) parentScope.$digest();
  73. scope.$emit(options.prefixEvent + '.select', value, index, $typeahead);
  74. if (angular.isDefined(options.onSelect) && angular.isFunction(options.onSelect)) {
  75. options.onSelect(value, index, $typeahead);
  76. }
  77. };
  78. $typeahead.$isVisible = function() {
  79. if (!options.minLength || !controller) {
  80. return !!scope.$matches.length;
  81. }
  82. return scope.$matches.length && angular.isString(controller.$viewValue) && controller.$viewValue.length >= options.minLength;
  83. };
  84. $typeahead.$getIndex = function(value) {
  85. var index;
  86. for (index = scope.$matches.length; index--; ) {
  87. if (angular.equals(scope.$matches[index].value, value)) break;
  88. }
  89. return index;
  90. };
  91. $typeahead.$onMouseDown = function(evt) {
  92. evt.preventDefault();
  93. evt.stopPropagation();
  94. };
  95. $typeahead.$$updateScrollTop = function(container, index) {
  96. if (index > -1 && index < container.children.length) {
  97. var active = container.children[index];
  98. var clientTop = active.offsetTop;
  99. var clientBottom = active.offsetTop + active.clientHeight;
  100. var highWatermark = container.scrollTop;
  101. var lowWatermark = container.scrollTop + container.clientHeight;
  102. if (clientBottom >= highWatermark && clientTop < highWatermark) {
  103. container.scrollTop = Math.max(0, container.scrollTop - container.clientHeight);
  104. } else if (clientBottom > lowWatermark) {
  105. container.scrollTop = clientTop;
  106. }
  107. }
  108. };
  109. $typeahead.$onKeyDown = function(evt) {
  110. if (!/(38|40|13)/.test(evt.keyCode)) return;
  111. if ($typeahead.$isVisible() && !(evt.keyCode === 13 && scope.$activeIndex === -1)) {
  112. evt.preventDefault();
  113. evt.stopPropagation();
  114. }
  115. if (evt.keyCode === 13 && scope.$matches.length) {
  116. $typeahead.select(scope.$activeIndex);
  117. } else if (evt.keyCode === 38 && scope.$activeIndex > 0) {
  118. scope.$activeIndex--;
  119. } else if (evt.keyCode === 40 && scope.$activeIndex < scope.$matches.length - 1) {
  120. scope.$activeIndex++;
  121. } else if (angular.isUndefined(scope.$activeIndex)) {
  122. scope.$activeIndex = 0;
  123. }
  124. $typeahead.$$updateScrollTop($typeahead.$element[0], scope.$activeIndex);
  125. scope.$digest();
  126. };
  127. var show = $typeahead.show;
  128. $typeahead.show = function() {
  129. show();
  130. $timeout(function() {
  131. if ($typeahead.$element) {
  132. $typeahead.$element.on('mousedown', $typeahead.$onMouseDown);
  133. if (options.keyboard) {
  134. if (element) element.on('keydown', $typeahead.$onKeyDown);
  135. }
  136. }
  137. }, 0, false);
  138. };
  139. var hide = $typeahead.hide;
  140. $typeahead.hide = function() {
  141. if ($typeahead.$element) $typeahead.$element.off('mousedown', $typeahead.$onMouseDown);
  142. if (options.keyboard) {
  143. if (element) element.off('keydown', $typeahead.$onKeyDown);
  144. }
  145. if (!options.autoSelect) {
  146. $typeahead.activate(-1);
  147. }
  148. hide();
  149. };
  150. return $typeahead;
  151. }
  152. function safeDigest(scope) {
  153. scope.$$phase || scope.$root && scope.$root.$$phase || scope.$digest();
  154. }
  155. TypeaheadFactory.defaults = defaults;
  156. return TypeaheadFactory;
  157. } ];
  158. }).filter('bsAsyncFilter', [ '$filter', function($filter) {
  159. return function(array, expression, comparator) {
  160. if (array && angular.isFunction(array.then)) {
  161. return array.then(function(results) {
  162. return $filter('filter')(results, expression, comparator);
  163. });
  164. }
  165. return $filter('filter')(array, expression, comparator);
  166. };
  167. } ]).directive('bsTypeahead', [ '$window', '$parse', '$q', '$typeahead', '$parseOptions', function($window, $parse, $q, $typeahead, $parseOptions) {
  168. var defaults = $typeahead.defaults;
  169. return {
  170. restrict: 'EAC',
  171. require: 'ngModel',
  172. link: function postLink(scope, element, attr, controller) {
  173. element.off('change');
  174. var options = {
  175. scope: scope
  176. };
  177. angular.forEach([ 'template', 'templateUrl', 'controller', 'controllerAs', 'placement', 'container', 'delay', 'trigger', 'keyboard', 'html', 'animation', 'filter', 'limit', 'minLength', 'watchOptions', 'selectMode', 'autoSelect', 'comparator', 'id', 'prefixEvent', 'prefixClass' ], function(key) {
  178. if (angular.isDefined(attr[key])) options[key] = attr[key];
  179. });
  180. var falseValueRegExp = /^(false|0|)$/i;
  181. angular.forEach([ 'html', 'container', 'trimValue', 'filter' ], function(key) {
  182. if (angular.isDefined(attr[key]) && falseValueRegExp.test(attr[key])) options[key] = false;
  183. });
  184. angular.forEach([ 'onBeforeShow', 'onShow', 'onBeforeHide', 'onHide', 'onSelect' ], function(key) {
  185. var bsKey = 'bs' + key.charAt(0).toUpperCase() + key.slice(1);
  186. if (angular.isDefined(attr[bsKey])) {
  187. options[key] = scope.$eval(attr[bsKey]);
  188. }
  189. });
  190. if (!element.attr('autocomplete')) element.attr('autocomplete', 'off');
  191. var filter = angular.isDefined(options.filter) ? options.filter : defaults.filter;
  192. var limit = options.limit || defaults.limit;
  193. var comparator = options.comparator || defaults.comparator;
  194. var bsOptions = attr.bsOptions;
  195. if (filter) {
  196. bsOptions += ' | ' + filter + ':$viewValue';
  197. if (comparator) bsOptions += ':' + comparator;
  198. }
  199. if (limit) bsOptions += ' | limitTo:' + limit;
  200. var parsedOptions = $parseOptions(bsOptions);
  201. var typeahead = $typeahead(element, controller, options);
  202. if (options.watchOptions) {
  203. var watchedOptions = parsedOptions.$match[7].replace(/\|.+/, '').replace(/\(.*\)/g, '').trim();
  204. scope.$watchCollection(watchedOptions, function(newValue, oldValue) {
  205. parsedOptions.valuesFn(scope, controller).then(function(values) {
  206. typeahead.update(values);
  207. controller.$render();
  208. });
  209. });
  210. }
  211. scope.$watch(attr.ngModel, function(newValue, oldValue) {
  212. scope.$modelValue = newValue;
  213. parsedOptions.valuesFn(scope, controller).then(function(values) {
  214. if (options.selectMode && !values.length && newValue.length > 0) {
  215. controller.$setViewValue(controller.$viewValue.substring(0, controller.$viewValue.length - 1));
  216. return;
  217. }
  218. if (values.length > limit) values = values.slice(0, limit);
  219. typeahead.update(values);
  220. controller.$render();
  221. });
  222. });
  223. controller.$formatters.push(function(modelValue) {
  224. var displayValue = parsedOptions.displayValue(modelValue);
  225. if (displayValue) {
  226. return displayValue;
  227. }
  228. if (angular.isDefined(modelValue) && typeof modelValue !== 'object') {
  229. return modelValue;
  230. }
  231. return '';
  232. });
  233. controller.$render = function() {
  234. if (controller.$isEmpty(controller.$viewValue)) {
  235. return element.val('');
  236. }
  237. var index = typeahead.$getIndex(controller.$modelValue);
  238. var selected = index !== -1 ? typeahead.$scope.$matches[index].label : controller.$viewValue;
  239. selected = angular.isObject(selected) ? parsedOptions.displayValue(selected) : selected;
  240. var value = selected ? selected.toString().replace(/<(?:.|\n)*?>/gm, '') : '';
  241. var ss = element[0].selectionStart;
  242. var sd = element[0].selectionEnd;
  243. element.val(options.trimValue === false ? value : value.trim());
  244. element[0].setSelectionRange(ss, sd);
  245. };
  246. scope.$on('$destroy', function() {
  247. if (typeahead) typeahead.destroy();
  248. options = null;
  249. typeahead = null;
  250. });
  251. }
  252. };
  253. } ]);
  254. angular.module('mgcrea.ngStrap.tooltip', [ 'mgcrea.ngStrap.core', 'mgcrea.ngStrap.helpers.dimensions' ]).provider('$tooltip', function() {
  255. var defaults = this.defaults = {
  256. animation: 'am-fade',
  257. customClass: '',
  258. prefixClass: 'tooltip',
  259. prefixEvent: 'tooltip',
  260. container: false,
  261. target: false,
  262. placement: 'top',
  263. templateUrl: 'tooltip/tooltip.tpl.html',
  264. template: '',
  265. titleTemplate: false,
  266. trigger: 'hover focus',
  267. keyboard: false,
  268. html: false,
  269. show: false,
  270. title: '',
  271. type: '',
  272. delay: 0,
  273. autoClose: false,
  274. bsEnabled: true,
  275. mouseDownPreventDefault: true,
  276. mouseDownStopPropagation: true,
  277. viewport: {
  278. selector: 'body',
  279. padding: 0
  280. }
  281. };
  282. this.$get = [ '$window', '$rootScope', '$bsCompiler', '$q', '$templateCache', '$http', '$animate', '$sce', 'dimensions', '$$rAF', '$timeout', function($window, $rootScope, $bsCompiler, $q, $templateCache, $http, $animate, $sce, dimensions, $$rAF, $timeout) {
  283. var isNative = /(ip[ao]d|iphone|android)/gi.test($window.navigator.userAgent);
  284. var isTouch = 'createTouch' in $window.document && isNative;
  285. var $body = angular.element($window.document);
  286. function TooltipFactory(element, config) {
  287. var $tooltip = {};
  288. var options = $tooltip.$options = angular.extend({}, defaults, config);
  289. var promise = $tooltip.$promise = $bsCompiler.compile(options);
  290. var scope = $tooltip.$scope = options.scope && options.scope.$new() || $rootScope.$new();
  291. var nodeName = element[0].nodeName.toLowerCase();
  292. if (options.delay && angular.isString(options.delay)) {
  293. var split = options.delay.split(',').map(parseFloat);
  294. options.delay = split.length > 1 ? {
  295. show: split[0],
  296. hide: split[1]
  297. } : split[0];
  298. }
  299. $tooltip.$id = options.id || element.attr('id') || '';
  300. if (options.title) {
  301. scope.title = $sce.trustAsHtml(options.title);
  302. }
  303. scope.$setEnabled = function(isEnabled) {
  304. scope.$$postDigest(function() {
  305. $tooltip.setEnabled(isEnabled);
  306. });
  307. };
  308. scope.$hide = function() {
  309. scope.$$postDigest(function() {
  310. $tooltip.hide();
  311. });
  312. };
  313. scope.$show = function() {
  314. scope.$$postDigest(function() {
  315. $tooltip.show();
  316. });
  317. };
  318. scope.$toggle = function() {
  319. scope.$$postDigest(function() {
  320. $tooltip.toggle();
  321. });
  322. };
  323. $tooltip.$isShown = scope.$isShown = false;
  324. var timeout;
  325. var hoverState;
  326. var compileData;
  327. var tipElement;
  328. var tipContainer;
  329. var tipScope;
  330. promise.then(function(data) {
  331. compileData = data;
  332. $tooltip.init();
  333. });
  334. $tooltip.init = function() {
  335. if (options.delay && angular.isNumber(options.delay)) {
  336. options.delay = {
  337. show: options.delay,
  338. hide: options.delay
  339. };
  340. }
  341. if (options.container === 'self') {
  342. tipContainer = element;
  343. } else if (angular.isElement(options.container)) {
  344. tipContainer = options.container;
  345. } else if (options.container) {
  346. tipContainer = findElement(options.container);
  347. }
  348. bindTriggerEvents();
  349. if (options.target) {
  350. options.target = angular.isElement(options.target) ? options.target : findElement(options.target);
  351. }
  352. if (options.show) {
  353. scope.$$postDigest(function() {
  354. if (options.trigger === 'focus') {
  355. element[0].focus();
  356. } else {
  357. $tooltip.show();
  358. }
  359. });
  360. }
  361. };
  362. $tooltip.destroy = function() {
  363. unbindTriggerEvents();
  364. destroyTipElement();
  365. scope.$destroy();
  366. };
  367. $tooltip.enter = function() {
  368. clearTimeout(timeout);
  369. hoverState = 'in';
  370. if (!options.delay || !options.delay.show) {
  371. return $tooltip.show();
  372. }
  373. timeout = setTimeout(function() {
  374. if (hoverState === 'in') $tooltip.show();
  375. }, options.delay.show);
  376. };
  377. $tooltip.show = function() {
  378. if (!options.bsEnabled || $tooltip.$isShown) return;
  379. scope.$emit(options.prefixEvent + '.show.before', $tooltip);
  380. if (angular.isDefined(options.onBeforeShow) && angular.isFunction(options.onBeforeShow)) {
  381. options.onBeforeShow($tooltip);
  382. }
  383. var parent;
  384. var after;
  385. if (options.container) {
  386. parent = tipContainer;
  387. if (tipContainer[0].lastChild) {
  388. after = angular.element(tipContainer[0].lastChild);
  389. } else {
  390. after = null;
  391. }
  392. } else {
  393. parent = null;
  394. after = element;
  395. }
  396. if (tipElement) destroyTipElement();
  397. tipScope = $tooltip.$scope.$new();
  398. tipElement = $tooltip.$element = compileData.link(tipScope, function(clonedElement, scope) {});
  399. tipElement.css({
  400. top: '-9999px',
  401. left: '-9999px',
  402. right: 'auto',
  403. display: 'block',
  404. visibility: 'hidden'
  405. });
  406. if (options.animation) tipElement.addClass(options.animation);
  407. if (options.type) tipElement.addClass(options.prefixClass + '-' + options.type);
  408. if (options.customClass) tipElement.addClass(options.customClass);
  409. if (after) {
  410. after.after(tipElement);
  411. } else {
  412. parent.prepend(tipElement);
  413. }
  414. $tooltip.$isShown = scope.$isShown = true;
  415. safeDigest(scope);
  416. $tooltip.$applyPlacement();
  417. if (angular.version.minor <= 2) {
  418. $animate.enter(tipElement, parent, after, enterAnimateCallback);
  419. } else {
  420. $animate.enter(tipElement, parent, after).then(enterAnimateCallback);
  421. }
  422. safeDigest(scope);
  423. $$rAF(function() {
  424. if (tipElement) tipElement.css({
  425. visibility: 'visible'
  426. });
  427. if (options.keyboard) {
  428. if (options.trigger !== 'focus') {
  429. $tooltip.focus();
  430. }
  431. bindKeyboardEvents();
  432. }
  433. });
  434. if (options.autoClose) {
  435. bindAutoCloseEvents();
  436. }
  437. };
  438. function enterAnimateCallback() {
  439. scope.$emit(options.prefixEvent + '.show', $tooltip);
  440. if (angular.isDefined(options.onShow) && angular.isFunction(options.onShow)) {
  441. options.onShow($tooltip);
  442. }
  443. }
  444. $tooltip.leave = function() {
  445. clearTimeout(timeout);
  446. hoverState = 'out';
  447. if (!options.delay || !options.delay.hide) {
  448. return $tooltip.hide();
  449. }
  450. timeout = setTimeout(function() {
  451. if (hoverState === 'out') {
  452. $tooltip.hide();
  453. }
  454. }, options.delay.hide);
  455. };
  456. var _blur;
  457. var _tipToHide;
  458. $tooltip.hide = function(blur) {
  459. if (!$tooltip.$isShown) return;
  460. scope.$emit(options.prefixEvent + '.hide.before', $tooltip);
  461. if (angular.isDefined(options.onBeforeHide) && angular.isFunction(options.onBeforeHide)) {
  462. options.onBeforeHide($tooltip);
  463. }
  464. _blur = blur;
  465. _tipToHide = tipElement;
  466. if (tipElement !== null) {
  467. if (angular.version.minor <= 2) {
  468. $animate.leave(tipElement, leaveAnimateCallback);
  469. } else {
  470. $animate.leave(tipElement).then(leaveAnimateCallback);
  471. }
  472. }
  473. $tooltip.$isShown = scope.$isShown = false;
  474. safeDigest(scope);
  475. if (options.keyboard && tipElement !== null) {
  476. unbindKeyboardEvents();
  477. }
  478. if (options.autoClose && tipElement !== null) {
  479. unbindAutoCloseEvents();
  480. }
  481. };
  482. function leaveAnimateCallback() {
  483. scope.$emit(options.prefixEvent + '.hide', $tooltip);
  484. if (angular.isDefined(options.onHide) && angular.isFunction(options.onHide)) {
  485. options.onHide($tooltip);
  486. }
  487. if (tipElement === _tipToHide) {
  488. if (_blur && options.trigger === 'focus') {
  489. return element[0].blur();
  490. }
  491. destroyTipElement();
  492. }
  493. }
  494. $tooltip.toggle = function(evt) {
  495. if (evt) {
  496. evt.preventDefault();
  497. }
  498. if ($tooltip.$isShown) {
  499. $tooltip.leave();
  500. } else {
  501. $tooltip.enter();
  502. }
  503. };
  504. $tooltip.focus = function() {
  505. tipElement[0].focus();
  506. };
  507. $tooltip.setEnabled = function(isEnabled) {
  508. options.bsEnabled = isEnabled;
  509. };
  510. $tooltip.setViewport = function(viewport) {
  511. options.viewport = viewport;
  512. };
  513. $tooltip.$applyPlacement = function() {
  514. if (!tipElement) return;
  515. var placement = options.placement;
  516. var autoToken = /\s?auto?\s?/i;
  517. var autoPlace = autoToken.test(placement);
  518. if (autoPlace) {
  519. placement = placement.replace(autoToken, '') || defaults.placement;
  520. }
  521. tipElement.addClass(options.placement);
  522. var elementPosition = getPosition();
  523. var tipWidth = tipElement.prop('offsetWidth');
  524. var tipHeight = tipElement.prop('offsetHeight');
  525. $tooltip.$viewport = options.viewport && findElement(options.viewport.selector || options.viewport);
  526. if (autoPlace) {
  527. var originalPlacement = placement;
  528. var viewportPosition = getPosition($tooltip.$viewport);
  529. if (/bottom/.test(originalPlacement) && elementPosition.bottom + tipHeight > viewportPosition.bottom) {
  530. placement = originalPlacement.replace('bottom', 'top');
  531. } else if (/top/.test(originalPlacement) && elementPosition.top - tipHeight < viewportPosition.top) {
  532. placement = originalPlacement.replace('top', 'bottom');
  533. }
  534. if (/left/.test(originalPlacement) && elementPosition.left - tipWidth < viewportPosition.left) {
  535. placement = placement.replace('left', 'right');
  536. } else if (/right/.test(originalPlacement) && elementPosition.right + tipWidth > viewportPosition.width) {
  537. placement = placement.replace('right', 'left');
  538. }
  539. tipElement.removeClass(originalPlacement).addClass(placement);
  540. }
  541. var tipPosition = getCalculatedOffset(placement, elementPosition, tipWidth, tipHeight);
  542. applyPlacement(tipPosition, placement);
  543. };
  544. $tooltip.$onKeyUp = function(evt) {
  545. if (evt.which === 27 && $tooltip.$isShown) {
  546. $tooltip.hide();
  547. evt.stopPropagation();
  548. }
  549. };
  550. $tooltip.$onFocusKeyUp = function(evt) {
  551. if (evt.which === 27) {
  552. element[0].blur();
  553. evt.stopPropagation();
  554. }
  555. };
  556. $tooltip.$onFocusElementMouseDown = function(evt) {
  557. if (options.mouseDownPreventDefault) {
  558. evt.preventDefault();
  559. }
  560. if (options.mouseDownStopPropagation) {
  561. evt.stopPropagation();
  562. }
  563. if ($tooltip.$isShown) {
  564. element[0].blur();
  565. } else {
  566. element[0].focus();
  567. }
  568. };
  569. function bindTriggerEvents() {
  570. var triggers = options.trigger.split(' ');
  571. angular.forEach(triggers, function(trigger) {
  572. if (trigger === 'click' || trigger === 'contextmenu') {
  573. element.on(trigger, $tooltip.toggle);
  574. } else if (trigger !== 'manual') {
  575. element.on(trigger === 'hover' ? 'mouseenter' : 'focus', $tooltip.enter);
  576. element.on(trigger === 'hover' ? 'mouseleave' : 'blur', $tooltip.leave);
  577. if (nodeName === 'button' && trigger !== 'hover') {
  578. element.on(isTouch ? 'touchstart' : 'mousedown', $tooltip.$onFocusElementMouseDown);
  579. }
  580. }
  581. });
  582. }
  583. function unbindTriggerEvents() {
  584. var triggers = options.trigger.split(' ');
  585. for (var i = triggers.length; i--; ) {
  586. var trigger = triggers[i];
  587. if (trigger === 'click' || trigger === 'contextmenu') {
  588. element.off(trigger, $tooltip.toggle);
  589. } else if (trigger !== 'manual') {
  590. element.off(trigger === 'hover' ? 'mouseenter' : 'focus', $tooltip.enter);
  591. element.off(trigger === 'hover' ? 'mouseleave' : 'blur', $tooltip.leave);
  592. if (nodeName === 'button' && trigger !== 'hover') {
  593. element.off(isTouch ? 'touchstart' : 'mousedown', $tooltip.$onFocusElementMouseDown);
  594. }
  595. }
  596. }
  597. }
  598. function bindKeyboardEvents() {
  599. if (options.trigger !== 'focus') {
  600. tipElement.on('keyup', $tooltip.$onKeyUp);
  601. } else {
  602. element.on('keyup', $tooltip.$onFocusKeyUp);
  603. }
  604. }
  605. function unbindKeyboardEvents() {
  606. if (options.trigger !== 'focus') {
  607. tipElement.off('keyup', $tooltip.$onKeyUp);
  608. } else {
  609. element.off('keyup', $tooltip.$onFocusKeyUp);
  610. }
  611. }
  612. var _autoCloseEventsBinded = false;
  613. function bindAutoCloseEvents() {
  614. $timeout(function() {
  615. tipElement.on('click', stopEventPropagation);
  616. $body.on('click', $tooltip.hide);
  617. _autoCloseEventsBinded = true;
  618. }, 0, false);
  619. }
  620. function unbindAutoCloseEvents() {
  621. if (_autoCloseEventsBinded) {
  622. tipElement.off('click', stopEventPropagation);
  623. $body.off('click', $tooltip.hide);
  624. _autoCloseEventsBinded = false;
  625. }
  626. }
  627. function stopEventPropagation(event) {
  628. event.stopPropagation();
  629. }
  630. function getPosition($element) {
  631. $element = $element || (options.target || element);
  632. var el = $element[0];
  633. var isBody = el.tagName === 'BODY';
  634. var elRect = el.getBoundingClientRect();
  635. var rect = {};
  636. for (var p in elRect) {
  637. rect[p] = elRect[p];
  638. }
  639. if (rect.width === null) {
  640. rect = angular.extend({}, rect, {
  641. width: elRect.right - elRect.left,
  642. height: elRect.bottom - elRect.top
  643. });
  644. }
  645. var elOffset = isBody ? {
  646. top: 0,
  647. left: 0
  648. } : dimensions.offset(el);
  649. var scroll = {
  650. scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.prop('scrollTop') || 0
  651. };
  652. var outerDims = isBody ? {
  653. width: document.documentElement.clientWidth,
  654. height: $window.innerHeight
  655. } : null;
  656. return angular.extend({}, rect, scroll, outerDims, elOffset);
  657. }
  658. function getCalculatedOffset(placement, position, actualWidth, actualHeight) {
  659. var offset;
  660. var split = placement.split('-');
  661. switch (split[0]) {
  662. case 'right':
  663. offset = {
  664. top: position.top + position.height / 2 - actualHeight / 2,
  665. left: position.left + position.width
  666. };
  667. break;
  668. case 'bottom':
  669. offset = {
  670. top: position.top + position.height,
  671. left: position.left + position.width / 2 - actualWidth / 2
  672. };
  673. break;
  674. case 'left':
  675. offset = {
  676. top: position.top + position.height / 2 - actualHeight / 2,
  677. left: position.left - actualWidth
  678. };
  679. break;
  680. default:
  681. offset = {
  682. top: position.top - actualHeight,
  683. left: position.left + position.width / 2 - actualWidth / 2
  684. };
  685. break;
  686. }
  687. if (!split[1]) {
  688. return offset;
  689. }
  690. if (split[0] === 'top' || split[0] === 'bottom') {
  691. switch (split[1]) {
  692. case 'left':
  693. offset.left = position.left;
  694. break;
  695. case 'right':
  696. offset.left = position.left + position.width - actualWidth;
  697. break;
  698. default:
  699. break;
  700. }
  701. } else if (split[0] === 'left' || split[0] === 'right') {
  702. switch (split[1]) {
  703. case 'top':
  704. offset.top = position.top - actualHeight + position.height;
  705. break;
  706. case 'bottom':
  707. offset.top = position.top;
  708. break;
  709. default:
  710. break;
  711. }
  712. }
  713. return offset;
  714. }
  715. function applyPlacement(offset, placement) {
  716. var tip = tipElement[0];
  717. var width = tip.offsetWidth;
  718. var height = tip.offsetHeight;
  719. var marginTop = parseInt(dimensions.css(tip, 'margin-top'), 10);
  720. var marginLeft = parseInt(dimensions.css(tip, 'margin-left'), 10);
  721. if (isNaN(marginTop)) marginTop = 0;
  722. if (isNaN(marginLeft)) marginLeft = 0;
  723. offset.top = offset.top + marginTop;
  724. offset.left = offset.left + marginLeft;
  725. dimensions.setOffset(tip, angular.extend({
  726. using: function(props) {
  727. tipElement.css({
  728. top: Math.round(props.top) + 'px',
  729. left: Math.round(props.left) + 'px',
  730. right: ''
  731. });
  732. }
  733. }, offset), 0);
  734. var actualWidth = tip.offsetWidth;
  735. var actualHeight = tip.offsetHeight;
  736. if (placement === 'top' && actualHeight !== height) {
  737. offset.top = offset.top + height - actualHeight;
  738. }
  739. if (/top-left|top-right|bottom-left|bottom-right/.test(placement)) return;
  740. var delta = getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight);
  741. if (delta.left) {
  742. offset.left += delta.left;
  743. } else {
  744. offset.top += delta.top;
  745. }
  746. dimensions.setOffset(tip, offset);
  747. if (/top|right|bottom|left/.test(placement)) {
  748. var isVertical = /top|bottom/.test(placement);
  749. var arrowDelta = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight;
  750. var arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight';
  751. replaceArrow(arrowDelta, tip[arrowOffsetPosition], isVertical);
  752. }
  753. }
  754. function getViewportAdjustedDelta(placement, position, actualWidth, actualHeight) {
  755. var delta = {
  756. top: 0,
  757. left: 0
  758. };
  759. if (!$tooltip.$viewport) return delta;
  760. var viewportPadding = options.viewport && options.viewport.padding || 0;
  761. var viewportDimensions = getPosition($tooltip.$viewport);
  762. if (/right|left/.test(placement)) {
  763. var topEdgeOffset = position.top - viewportPadding - viewportDimensions.scroll;
  764. var bottomEdgeOffset = position.top + viewportPadding - viewportDimensions.scroll + actualHeight;
  765. if (topEdgeOffset < viewportDimensions.top) {
  766. delta.top = viewportDimensions.top - topEdgeOffset;
  767. } else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) {
  768. delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset;
  769. }
  770. } else {
  771. var leftEdgeOffset = position.left - viewportPadding;
  772. var rightEdgeOffset = position.left + viewportPadding + actualWidth;
  773. if (leftEdgeOffset < viewportDimensions.left) {
  774. delta.left = viewportDimensions.left - leftEdgeOffset;
  775. } else if (rightEdgeOffset > viewportDimensions.right) {
  776. delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset;
  777. }
  778. }
  779. return delta;
  780. }
  781. function replaceArrow(delta, dimension, isHorizontal) {
  782. var $arrow = findElement('.tooltip-arrow, .arrow', tipElement[0]);
  783. $arrow.css(isHorizontal ? 'left' : 'top', 50 * (1 - delta / dimension) + '%').css(isHorizontal ? 'top' : 'left', '');
  784. }
  785. function destroyTipElement() {
  786. clearTimeout(timeout);
  787. if ($tooltip.$isShown && tipElement !== null) {
  788. if (options.autoClose) {
  789. unbindAutoCloseEvents();
  790. }
  791. if (options.keyboard) {
  792. unbindKeyboardEvents();
  793. }
  794. }
  795. if (tipScope) {
  796. tipScope.$destroy();
  797. tipScope = null;
  798. }
  799. if (tipElement) {
  800. tipElement.remove();
  801. tipElement = $tooltip.$element = null;
  802. }
  803. }
  804. return $tooltip;
  805. }
  806. function safeDigest(scope) {
  807. scope.$$phase || scope.$root && scope.$root.$$phase || scope.$digest();
  808. }
  809. function findElement(query, element) {
  810. return angular.element((element || document).querySelectorAll(query));
  811. }
  812. return TooltipFactory;
  813. } ];
  814. }).directive('bsTooltip', [ '$window', '$location', '$sce', '$parse', '$tooltip', '$$rAF', function($window, $location, $sce, $parse, $tooltip, $$rAF) {
  815. return {
  816. restrict: 'EAC',
  817. scope: true,
  818. link: function postLink(scope, element, attr, transclusion) {
  819. var tooltip;
  820. var options = {
  821. scope: scope
  822. };
  823. angular.forEach([ 'template', 'templateUrl', 'controller', 'controllerAs', 'titleTemplate', 'placement', 'container', 'delay', 'trigger', 'html', 'animation', 'backdropAnimation', 'type', 'customClass', 'id' ], function(key) {
  824. if (angular.isDefined(attr[key])) options[key] = attr[key];
  825. });
  826. var falseValueRegExp = /^(false|0|)$/i;
  827. angular.forEach([ 'html', 'container' ], function(key) {
  828. if (angular.isDefined(attr[key]) && falseValueRegExp.test(attr[key])) {
  829. options[key] = false;
  830. }
  831. });
  832. angular.forEach([ 'onBeforeShow', 'onShow', 'onBeforeHide', 'onHide' ], function(key) {
  833. var bsKey = 'bs' + key.charAt(0).toUpperCase() + key.slice(1);
  834. if (angular.isDefined(attr[bsKey])) {
  835. options[key] = scope.$eval(attr[bsKey]);
  836. }
  837. });
  838. var dataTarget = element.attr('data-target');
  839. if (angular.isDefined(dataTarget)) {
  840. if (falseValueRegExp.test(dataTarget)) {
  841. options.target = false;
  842. } else {
  843. options.target = dataTarget;
  844. }
  845. }
  846. if (!scope.hasOwnProperty('title')) {
  847. scope.title = '';
  848. }
  849. attr.$observe('title', function(newValue) {
  850. if (angular.isDefined(newValue) || !scope.hasOwnProperty('title')) {
  851. var oldValue = scope.title;
  852. scope.title = $sce.trustAsHtml(newValue);
  853. if (angular.isDefined(oldValue)) {
  854. $$rAF(function() {
  855. if (tooltip) tooltip.$applyPlacement();
  856. });
  857. }
  858. }
  859. });
  860. attr.$observe('disabled', function(newValue) {
  861. if (newValue && tooltip.$isShown) {
  862. tooltip.hide();
  863. }
  864. });
  865. if (attr.bsTooltip) {
  866. scope.$watch(attr.bsTooltip, function(newValue, oldValue) {
  867. if (angular.isObject(newValue)) {
  868. angular.extend(scope, newValue);
  869. } else {
  870. scope.title = newValue;
  871. }
  872. if (angular.isDefined(oldValue)) {
  873. $$rAF(function() {
  874. if (tooltip) tooltip.$applyPlacement();
  875. });
  876. }
  877. }, true);
  878. }
  879. if (attr.bsShow) {
  880. scope.$watch(attr.bsShow, function(newValue, oldValue) {
  881. if (!tooltip || !angular.isDefined(newValue)) return;
  882. if (angular.isString(newValue)) newValue = !!newValue.match(/true|,?(tooltip),?/i);
  883. if (newValue === true) {
  884. tooltip.show();
  885. } else {
  886. tooltip.hide();
  887. }
  888. });
  889. }
  890. if (attr.bsEnabled) {
  891. scope.$watch(attr.bsEnabled, function(newValue, oldValue) {
  892. if (!tooltip || !angular.isDefined(newValue)) return;
  893. if (angular.isString(newValue)) newValue = !!newValue.match(/true|1|,?(tooltip),?/i);
  894. if (newValue === false) {
  895. tooltip.setEnabled(false);
  896. } else {
  897. tooltip.setEnabled(true);
  898. }
  899. });
  900. }
  901. if (attr.viewport) {
  902. scope.$watch(attr.viewport, function(newValue) {
  903. if (!tooltip || !angular.isDefined(newValue)) return;
  904. tooltip.setViewport(newValue);
  905. });
  906. }
  907. tooltip = $tooltip(element, options);
  908. scope.$on('$destroy', function() {
  909. if (tooltip) tooltip.destroy();
  910. options = null;
  911. tooltip = null;
  912. });
  913. }
  914. };
  915. } ]);
  916. angular.module('mgcrea.ngStrap.timepicker', [ 'mgcrea.ngStrap.helpers.dateParser', 'mgcrea.ngStrap.helpers.dateFormatter', 'mgcrea.ngStrap.tooltip' ]).provider('$timepicker', function() {
  917. var defaults = this.defaults = {
  918. animation: 'am-fade',
  919. defaultDate: 'auto',
  920. prefixClass: 'timepicker',
  921. placement: 'bottom-left',
  922. templateUrl: 'timepicker/timepicker.tpl.html',
  923. trigger: 'focus',
  924. container: false,
  925. keyboard: true,
  926. html: false,
  927. delay: 0,
  928. useNative: true,
  929. timeType: 'date',
  930. timeFormat: 'shortTime',
  931. timezone: null,
  932. modelTimeFormat: null,
  933. autoclose: false,
  934. minTime: -Infinity,
  935. maxTime: +Infinity,
  936. length: 5,
  937. hourStep: 1,
  938. minuteStep: 5,
  939. secondStep: 5,
  940. roundDisplay: false,
  941. iconUp: 'glyphicon glyphicon-chevron-up',
  942. iconDown: 'glyphicon glyphicon-chevron-down',
  943. arrowBehavior: 'pager'
  944. };
  945. this.$get = [ '$window', '$document', '$rootScope', '$sce', '$dateFormatter', '$tooltip', '$timeout', function($window, $document, $rootScope, $sce, $dateFormatter, $tooltip, $timeout) {
  946. var isNative = /(ip[ao]d|iphone|android)/gi.test($window.navigator.userAgent);
  947. var isTouch = 'createTouch' in $window.document && isNative;
  948. if (!defaults.lang) {
  949. defaults.lang = $dateFormatter.getDefaultLocale();
  950. }
  951. function timepickerFactory(element, controller, config) {
  952. var $timepicker = $tooltip(element, angular.extend({}, defaults, config));
  953. var parentScope = config.scope;
  954. var options = $timepicker.$options;
  955. var scope = $timepicker.$scope;
  956. var lang = options.lang;
  957. var formatDate = function(date, format, timezone) {
  958. return $dateFormatter.formatDate(date, format, lang, timezone);
  959. };
  960. function floorMinutes(time) {
  961. var coeff = 1e3 * 60 * options.minuteStep;
  962. return new Date(Math.floor(time.getTime() / coeff) * coeff);
  963. }
  964. var selectedIndex = 0;
  965. var defaultDate = options.roundDisplay ? floorMinutes(new Date()) : new Date();
  966. var startDate = controller.$dateValue || defaultDate;
  967. var viewDate = {
  968. hour: startDate.getHours(),
  969. meridian: startDate.getHours() < 12,
  970. minute: startDate.getMinutes(),
  971. second: startDate.getSeconds(),
  972. millisecond: startDate.getMilliseconds()
  973. };
  974. var format = $dateFormatter.getDatetimeFormat(options.timeFormat, lang);
  975. var hoursFormat = $dateFormatter.hoursFormat(format);
  976. var timeSeparator = $dateFormatter.timeSeparator(format);
  977. var minutesFormat = $dateFormatter.minutesFormat(format);
  978. var secondsFormat = $dateFormatter.secondsFormat(format);
  979. var showSeconds = $dateFormatter.showSeconds(format);
  980. var showAM = $dateFormatter.showAM(format);
  981. scope.$iconUp = options.iconUp;
  982. scope.$iconDown = options.iconDown;
  983. scope.$select = function(date, index) {
  984. $timepicker.select(date, index);
  985. };
  986. scope.$moveIndex = function(value, index) {
  987. $timepicker.$moveIndex(value, index);
  988. };
  989. scope.$switchMeridian = function(date) {
  990. $timepicker.switchMeridian(date);
  991. };
  992. $timepicker.update = function(date) {
  993. if (angular.isDate(date) && !isNaN(date.getTime())) {
  994. $timepicker.$date = date;
  995. angular.extend(viewDate, {
  996. hour: date.getHours(),
  997. minute: date.getMinutes(),
  998. second: date.getSeconds(),
  999. millisecond: date.getMilliseconds()
  1000. });
  1001. $timepicker.$build();
  1002. } else if (!$timepicker.$isBuilt) {
  1003. $timepicker.$build();
  1004. }
  1005. };
  1006. $timepicker.select = function(date, index, keep) {
  1007. if (!controller.$dateValue || isNaN(controller.$dateValue.getTime())) {
  1008. controller.$dateValue = options.defaultDate === 'today' ? new Date() : new Date(1970, 0, 1);
  1009. }
  1010. if (!angular.isDate(date)) date = new Date(date);
  1011. if (index === 0) controller.$dateValue.setHours(date.getHours()); else if (index === 1) controller.$dateValue.setMinutes(date.getMinutes()); else if (index === 2) controller.$dateValue.setSeconds(date.getSeconds());
  1012. controller.$setViewValue(angular.copy(controller.$dateValue));
  1013. controller.$render();
  1014. if (options.autoclose && !keep) {
  1015. $timeout(function() {
  1016. $timepicker.hide(true);
  1017. });
  1018. }
  1019. };
  1020. $timepicker.switchMeridian = function(date) {
  1021. if (!controller.$dateValue || isNaN(controller.$dateValue.getTime())) {
  1022. return;
  1023. }
  1024. var hours = (date || controller.$dateValue).getHours();
  1025. controller.$dateValue.setHours(hours < 12 ? hours + 12 : hours - 12);
  1026. controller.$setViewValue(angular.copy(controller.$dateValue));
  1027. controller.$render();
  1028. };
  1029. $timepicker.$build = function() {
  1030. var i;
  1031. var midIndex = scope.midIndex = parseInt(options.length / 2, 10);
  1032. var hours = [];
  1033. var hour;
  1034. for (i = 0; i < options.length; i++) {
  1035. hour = new Date(1970, 0, 1, viewDate.hour - (midIndex - i) * options.hourStep);
  1036. hours.push({
  1037. date: hour,
  1038. label: formatDate(hour, hoursFormat),
  1039. selected: $timepicker.$date && $timepicker.$isSelected(hour, 0),
  1040. disabled: $timepicker.$isDisabled(hour, 0)
  1041. });
  1042. }
  1043. var minutes = [];
  1044. var minute;
  1045. for (i = 0; i < options.length; i++) {
  1046. minute = new Date(1970, 0, 1, 0, viewDate.minute - (midIndex - i) * options.minuteStep);
  1047. minutes.push({
  1048. date: minute,
  1049. label: formatDate(minute, minutesFormat),
  1050. selected: $timepicker.$date && $timepicker.$isSelected(minute, 1),
  1051. disabled: $timepicker.$isDisabled(minute, 1)
  1052. });
  1053. }
  1054. var seconds = [];
  1055. var second;
  1056. for (i = 0; i < options.length; i++) {
  1057. second = new Date(1970, 0, 1, 0, 0, viewDate.second - (midIndex - i) * options.secondStep);
  1058. seconds.push({
  1059. date: second,
  1060. label: formatDate(second, secondsFormat),
  1061. selected: $timepicker.$date && $timepicker.$isSelected(second, 2),
  1062. disabled: $timepicker.$isDisabled(second, 2)
  1063. });
  1064. }
  1065. var rows = [];
  1066. for (i = 0; i < options.length; i++) {
  1067. if (showSeconds) {
  1068. rows.push([ hours[i], minutes[i], seconds[i] ]);
  1069. } else {
  1070. rows.push([ hours[i], minutes[i] ]);
  1071. }
  1072. }
  1073. scope.rows = rows;
  1074. scope.showSeconds = showSeconds;
  1075. scope.showAM = showAM;
  1076. scope.isAM = ($timepicker.$date || hours[midIndex].date).getHours() < 12;
  1077. scope.timeSeparator = timeSeparator;
  1078. $timepicker.$isBuilt = true;
  1079. };
  1080. $timepicker.$isSelected = function(date, index) {
  1081. if (!$timepicker.$date) return false; else if (index === 0) {
  1082. return date.getHours() === $timepicker.$date.getHours();
  1083. } else if (index === 1) {
  1084. return date.getMinutes() === $timepicker.$date.getMinutes();
  1085. } else if (index === 2) {
  1086. return date.getSeconds() === $timepicker.$date.getSeconds();
  1087. }
  1088. };
  1089. $timepicker.$isDisabled = function(date, index) {
  1090. var selectedTime;
  1091. if (index === 0) {
  1092. selectedTime = date.getTime() + viewDate.minute * 6e4 + viewDate.second * 1e3;
  1093. } else if (index === 1) {
  1094. selectedTime = date.getTime() + viewDate.hour * 36e5 + viewDate.second * 1e3;
  1095. } else if (index === 2) {
  1096. selectedTime = date.getTime() + viewDate.hour * 36e5 + viewDate.minute * 6e4;
  1097. }
  1098. return selectedTime < options.minTime * 1 || selectedTime > options.maxTime * 1;
  1099. };
  1100. scope.$arrowAction = function(value, index) {
  1101. if (options.arrowBehavior === 'picker') {
  1102. $timepicker.$setTimeByStep(value, index);
  1103. } else {
  1104. $timepicker.$moveIndex(value, index);
  1105. }
  1106. };
  1107. $timepicker.$setTimeByStep = function(value, index) {
  1108. var newDate = new Date($timepicker.$date || startDate);
  1109. var hours = newDate.getHours();
  1110. var minutes = newDate.getMinutes();
  1111. var seconds = newDate.getSeconds();
  1112. if (index === 0) {
  1113. newDate.setHours(hours - parseInt(options.hourStep, 10) * value);
  1114. } else if (index === 1) {
  1115. newDate.setMinutes(minutes - parseInt(options.minuteStep, 10) * value);
  1116. } else if (index === 2) {
  1117. newDate.setSeconds(seconds - parseInt(options.secondStep, 10) * value);
  1118. }
  1119. $timepicker.select(newDate, index, true);
  1120. };
  1121. $timepicker.$moveIndex = function(value, index) {
  1122. var targetDate;
  1123. if (index === 0) {
  1124. targetDate = new Date(1970, 0, 1, viewDate.hour + value * options.length, viewDate.minute, viewDate.second);
  1125. angular.extend(viewDate, {
  1126. hour: targetDate.getHours()
  1127. });
  1128. } else if (index === 1) {
  1129. targetDate = new Date(1970, 0, 1, viewDate.hour, viewDate.minute + value * options.length * options.minuteStep, viewDate.second);
  1130. angular.extend(viewDate, {
  1131. minute: targetDate.getMinutes()
  1132. });
  1133. } else if (index === 2) {
  1134. targetDate = new Date(1970, 0, 1, viewDate.hour, viewDate.minute, viewDate.second + value * options.length * options.secondStep);
  1135. angular.extend(viewDate, {
  1136. second: targetDate.getSeconds()
  1137. });
  1138. }
  1139. $timepicker.$build();
  1140. };
  1141. $timepicker.$onMouseDown = function(evt) {
  1142. if (evt.target.nodeName.toLowerCase() !== 'input') evt.preventDefault();
  1143. evt.stopPropagation();
  1144. if (isTouch) {
  1145. var targetEl = angular.element(evt.target);
  1146. if (targetEl[0].nodeName.toLowerCase() !== 'button') {
  1147. targetEl = targetEl.parent();
  1148. }
  1149. targetEl.triggerHandler('click');
  1150. }
  1151. };
  1152. $timepicker.$onKeyDown = function(evt) {
  1153. if (!/(38|37|39|40|13)/.test(evt.keyCode) || evt.shiftKey || evt.altKey) return;
  1154. evt.preventDefault();
  1155. evt.stopPropagation();
  1156. if (evt.keyCode === 13) {
  1157. $timepicker.hide(true);
  1158. return;
  1159. }
  1160. var newDate = new Date($timepicker.$date);
  1161. var hours = newDate.getHours();
  1162. var hoursLength = formatDate(newDate, hoursFormat).length;
  1163. var minutes = newDate.getMinutes();
  1164. var minutesLength = formatDate(newDate, minutesFormat).length;
  1165. var seconds = newDate.getSeconds();
  1166. var secondsLength = formatDate(newDate, secondsFormat).length;
  1167. var sepLength = 1;
  1168. var lateralMove = /(37|39)/.test(evt.keyCode);
  1169. var count = 2 + showSeconds * 1 + showAM * 1;
  1170. if (lateralMove) {
  1171. if (evt.keyCode === 37) selectedIndex = selectedIndex < 1 ? count - 1 : selectedIndex - 1; else if (evt.keyCode === 39) selectedIndex = selectedIndex < count - 1 ? selectedIndex + 1 : 0;
  1172. }
  1173. var selectRange = [ 0, hoursLength ];
  1174. var incr = 0;
  1175. if (evt.keyCode === 38) incr = -1;
  1176. if (evt.keyCode === 40) incr = +1;
  1177. var isSeconds = selectedIndex === 2 && showSeconds;
  1178. var isMeridian = selectedIndex === 2 && !showSeconds || selectedIndex === 3 && showSeconds;
  1179. if (selectedIndex === 0) {
  1180. newDate.setHours(hours + incr * parseInt(options.hourStep, 10));
  1181. hoursLength = formatDate(newDate, hoursFormat).length;
  1182. selectRange = [ 0, hoursLength ];
  1183. } else if (selectedIndex === 1) {
  1184. newDate.setMinutes(minutes + incr * parseInt(options.minuteStep, 10));
  1185. minutesLength = formatDate(newDate, minutesFormat).length;
  1186. selectRange = [ hoursLength + sepLength, minutesLength ];
  1187. } else if (isSeconds) {
  1188. newDate.setSeconds(seconds + incr * parseInt(options.secondStep, 10));
  1189. secondsLength = formatDate(newDate, secondsFormat).length;
  1190. selectRange = [ hoursLength + sepLength + minutesLength + sepLength, secondsLength ];
  1191. } else if (isMeridian) {
  1192. if (!lateralMove) $timepicker.switchMeridian();
  1193. selectRange = [ hoursLength + sepLength + minutesLength + sepLength + (secondsLength + sepLength) * showSeconds, 2 ];
  1194. }
  1195. $timepicker.select(newDate, selectedIndex, true);
  1196. createSelection(selectRange[0], selectRange[1]);
  1197. parentScope.$digest();
  1198. };
  1199. function createSelection(start, length) {
  1200. var end = start + length;
  1201. if (element[0].createTextRange) {
  1202. var selRange = element[0].createTextRange();
  1203. selRange.collapse(true);
  1204. selRange.moveStart('character', start);
  1205. selRange.moveEnd('character', end);
  1206. selRange.select();
  1207. } else if (element[0].setSelectionRange) {
  1208. element[0].setSelectionRange(start, end);
  1209. } else if (angular.isUndefined(element[0].selectionStart)) {
  1210. element[0].selectionStart = start;
  1211. element[0].selectionEnd = end;
  1212. }
  1213. }
  1214. function focusElement() {
  1215. element[0].focus();
  1216. }
  1217. var _init = $timepicker.init;
  1218. $timepicker.init = function() {
  1219. if (isNative && options.useNative) {
  1220. element.prop('type', 'time');
  1221. element.css('-webkit-appearance', 'textfield');
  1222. return;
  1223. } else if (isTouch) {
  1224. element.prop('type', 'text');
  1225. element.attr('readonly', 'true');
  1226. element.on('click', focusElement);
  1227. }
  1228. _init();
  1229. };
  1230. var _destroy = $timepicker.destroy;
  1231. $timepicker.destroy = function() {
  1232. if (isNative && options.useNative) {
  1233. element.off('click', focusElement);
  1234. }
  1235. _destroy();
  1236. };
  1237. var _show = $timepicker.show;
  1238. $timepicker.show = function() {
  1239. if (!isTouch && element.attr('readonly') || element.attr('disabled')) return;
  1240. _show();
  1241. $timeout(function() {
  1242. if ($timepicker.$element) $timepicker.$element.on(isTouch ? 'touchstart' : 'mousedown', $timepicker.$onMouseDown);
  1243. if (options.keyboard) {
  1244. if (element) element.on('keydown', $timepicker.$onKeyDown);
  1245. }
  1246. }, 0, false);
  1247. };
  1248. var _hide = $timepicker.hide;
  1249. $timepicker.hide = function(blur) {
  1250. if (!$timepicker.$isShown) return;
  1251. if ($timepicker.$element) $timepicker.$element.off(isTouch ? 'touchstart' : 'mousedown', $timepicker.$onMouseDown);
  1252. if (options.keyboard) {
  1253. if (element) element.off('keydown', $timepicker.$onKeyDown);
  1254. }
  1255. _hide(blur);
  1256. };
  1257. return $timepicker;
  1258. }
  1259. timepickerFactory.defaults = defaults;
  1260. return timepickerFactory;
  1261. } ];
  1262. }).directive('bsTimepicker', [ '$window', '$parse', '$q', '$dateFormatter', '$dateParser', '$timepicker', function($window, $parse, $q, $dateFormatter, $dateParser, $timepicker) {
  1263. var defaults = $timepicker.defaults;
  1264. var isNative = /(ip[ao]d|iphone|android)/gi.test($window.navigator.userAgent);
  1265. return {
  1266. restrict: 'EAC',
  1267. require: 'ngModel',
  1268. link: function postLink(scope, element, attr, controller) {
  1269. var options = {
  1270. scope: scope
  1271. };
  1272. angular.forEach([ 'template', 'templateUrl', 'controller', 'controllerAs', 'placement', 'container', 'delay', 'trigger', 'keyboard', 'html', 'animation', 'autoclose', 'timeType', 'timeFormat', 'timezone', 'modelTimeFormat', 'useNative', 'hourStep', 'minuteStep', 'secondStep', 'length', 'arrowBehavior', 'iconUp', 'iconDown', 'roundDisplay', 'id', 'prefixClass', 'prefixEvent', 'defaultDate' ], function(key) {
  1273. if (angular.isDefined(attr[key])) options[key] = attr[key];
  1274. });
  1275. var falseValueRegExp = /^(false|0|)$/i;
  1276. angular.forEach([ 'html', 'container', 'autoclose', 'useNative', 'roundDisplay' ], function(key) {
  1277. if (angular.isDefined(attr[key]) && falseValueRegExp.test(attr[key])) {
  1278. options[key] = false;
  1279. }
  1280. });
  1281. angular.forEach([ 'onBeforeShow', 'onShow', 'onBeforeHide', 'onHide' ], function(key) {
  1282. var bsKey = 'bs' + key.charAt(0).toUpperCase() + key.slice(1);
  1283. if (angular.isDefined(attr[bsKey])) {
  1284. options[key] = scope.$eval(attr[bsKey]);
  1285. }
  1286. });
  1287. if (isNative && (options.useNative || defaults.useNative)) options.timeFormat = 'HH:mm';
  1288. var timepicker = $timepicker(element, controller, options);
  1289. options = timepicker.$options;
  1290. var lang = options.lang;
  1291. var formatDate = function(date, format, timezone) {
  1292. return $dateFormatter.formatDate(date, format, lang, timezone);
  1293. };
  1294. if (attr.bsShow) {
  1295. scope.$watch(attr.bsShow, function(newValue, oldValue) {
  1296. if (!timepicker || !angular.isDefined(newValue)) return;
  1297. if (angular.isString(newValue)) newValue = !!newValue.match(/true|,?(timepicker),?/i);
  1298. if (newValue === true) {
  1299. timepicker.show();
  1300. } else {
  1301. timepicker.hide();
  1302. }
  1303. });
  1304. }
  1305. var dateParser = $dateParser({
  1306. format: options.timeFormat,
  1307. lang: lang
  1308. });
  1309. angular.forEach([ 'minTime', 'maxTime' ], function(key) {
  1310. if (angular.isDefined(attr[key])) {
  1311. attr.$observe(key, function(newValue) {
  1312. timepicker.$options[key] = dateParser.getTimeForAttribute(key, newValue);
  1313. if (!isNaN(timepicker.$options[key])) timepicker.$build();
  1314. validateAgainstMinMaxTime(controller.$dateValue);
  1315. });
  1316. }
  1317. });
  1318. scope.$watch(attr.ngModel, function(newValue, oldValue) {
  1319. timepicker.update(controller.$dateValue);
  1320. }, true);
  1321. function validateAgainstMinMaxTime(parsedTime) {
  1322. if (!angular.isDate(parsedTime)) return;
  1323. var isMinValid = isNaN(options.minTime) || new Date(parsedTime.getTime()).setFullYear(1970, 0, 1) >= options.minTime;
  1324. var isMaxValid = isNaN(options.maxTime) || new Date(parsedTime.getTime()).setFullYear(1970, 0, 1) <= options.maxTime;
  1325. var isValid = isMinValid && isMaxValid;
  1326. controller.$setValidity('date', isValid);
  1327. controller.$setValidity('min', isMinValid);
  1328. controller.$setValidity('max', isMaxValid);
  1329. if (!isValid) {
  1330. return;
  1331. }
  1332. controller.$dateValue = parsedTime;
  1333. }
  1334. controller.$parsers.unshift(function(viewValue) {
  1335. var date;
  1336. if (!viewValue) {
  1337. controller.$setValidity('date', true);
  1338. return null;
  1339. }
  1340. var parsedTime = angular.isDate(viewValue) ? viewValue : dateParser.parse(viewValue, controller.$dateValue);
  1341. if (!parsedTime || isNaN(parsedTime.getTime())) {
  1342. controller.$setValidity('date', false);
  1343. return undefined;
  1344. }
  1345. validateAgainstMinMaxTime(parsedTime);
  1346. if (options.timeType === 'string') {
  1347. date = dateParser.timezoneOffsetAdjust(parsedTime, options.timezone, true);
  1348. return formatDate(date, options.modelTimeFormat || options.timeFormat);
  1349. }
  1350. date = dateParser.timezoneOffsetAdjust(controller.$dateValue, options.timezone, true);
  1351. if (options.timeType === 'number') {
  1352. return date.getTime();
  1353. } else if (options.timeType === 'unix') {
  1354. return date.getTime() / 1e3;
  1355. } else if (options.timeType === 'iso') {
  1356. return date.toISOString();
  1357. }
  1358. return new Date(date);
  1359. });
  1360. controller.$formatters.push(function(modelValue) {
  1361. var date;
  1362. if (angular.isUndefined(modelValue) || modelValue === null) {
  1363. date = NaN;
  1364. } else if (angular.isDate(modelValue)) {
  1365. date = modelValue;
  1366. } else if (options.timeType === 'string') {
  1367. date = dateParser.parse(modelValue, null, options.modelTimeFormat);
  1368. } else if (options.timeType === 'unix') {
  1369. date = new Date(modelValue * 1e3);
  1370. } else {
  1371. date = new Date(modelValue);
  1372. }
  1373. controller.$dateValue = dateParser.timezoneOffsetAdjust(date, options.timezone);
  1374. return getTimeFormattedString();
  1375. });
  1376. controller.$render = function() {
  1377. element.val(getTimeFormattedString());
  1378. };
  1379. function getTimeFormattedString() {
  1380. return !controller.$dateValue || isNaN(controller.$dateValue.getTime()) ? '' : formatDate(controller.$dateValue, options.timeFormat);
  1381. }
  1382. scope.$on('$destroy', function() {
  1383. if (timepicker) timepicker.destroy();
  1384. options = null;
  1385. timepicker = null;
  1386. });
  1387. }
  1388. };
  1389. } ]);
  1390. angular.module('mgcrea.ngStrap.tab', []).provider('$tab', function() {
  1391. var defaults = this.defaults = {
  1392. animation: 'am-fade',
  1393. template: 'tab/tab.tpl.html',
  1394. navClass: 'nav-tabs',
  1395. activeClass: 'active'
  1396. };
  1397. var controller = this.controller = function($scope, $element, $attrs) {
  1398. var self = this;
  1399. self.$options = angular.copy(defaults);
  1400. angular.forEach([ 'animation', 'navClass', 'activeClass' ], function(key) {
  1401. if (angular.isDefined($attrs[key])) self.$options[key] = $attrs[key];
  1402. });
  1403. $scope.$navClass = self.$options.navClass;
  1404. $scope.$activeClass = self.$options.activeClass;
  1405. self.$panes = $scope.$panes = [];
  1406. self.$activePaneChangeListeners = self.$viewChangeListeners = [];
  1407. self.$push = function(pane) {
  1408. if (angular.isUndefined(self.$panes.$active)) {
  1409. $scope.$setActive(pane.name || 0);
  1410. }
  1411. self.$panes.push(pane);
  1412. };
  1413. self.$remove = function(pane) {
  1414. var index = self.$panes.indexOf(pane);
  1415. var active = self.$panes.$active;
  1416. var activeIndex;
  1417. if (angular.isString(active)) {
  1418. activeIndex = self.$panes.map(function(pane) {
  1419. return pane.name;
  1420. }).indexOf(active);
  1421. } else {
  1422. activeIndex = self.$panes.$active;
  1423. }
  1424. self.$panes.splice(index, 1);
  1425. if (index < activeIndex) {
  1426. activeIndex--;
  1427. } else if (index === activeIndex && activeIndex === self.$panes.length) {
  1428. activeIndex--;
  1429. }
  1430. if (activeIndex >= 0 && activeIndex < self.$panes.length) {
  1431. self.$setActive(self.$panes[activeIndex].name || activeIndex);
  1432. } else {
  1433. self.$setActive();
  1434. }
  1435. };
  1436. self.$setActive = $scope.$setActive = function(value) {
  1437. self.$panes.$active = value;
  1438. self.$activePaneChangeListeners.forEach(function(fn) {
  1439. fn();
  1440. });
  1441. };
  1442. self.$isActive = $scope.$isActive = function($pane, $index) {
  1443. return self.$panes.$active === $pane.name || self.$panes.$active === $index;
  1444. };
  1445. };
  1446. this.$get = function() {
  1447. var $tab = {};
  1448. $tab.defaults = defaults;
  1449. $tab.controller = controller;
  1450. return $tab;
  1451. };
  1452. }).directive('bsTabs', [ '$window', '$animate', '$tab', '$parse', function($window, $animate, $tab, $parse) {
  1453. var defaults = $tab.defaults;
  1454. return {
  1455. require: [ '?ngModel', 'bsTabs' ],
  1456. transclude: true,
  1457. scope: true,
  1458. controller: [ '$scope', '$element', '$attrs', $tab.controller ],
  1459. templateUrl: function(element, attr) {
  1460. return attr.template || defaults.template;
  1461. },
  1462. link: function postLink(scope, element, attrs, controllers) {
  1463. var ngModelCtrl = controllers[0];
  1464. var bsTabsCtrl = controllers[1];
  1465. if (ngModelCtrl) {
  1466. bsTabsCtrl.$activePaneChangeListeners.push(function() {
  1467. ngModelCtrl.$setViewValue(bsTabsCtrl.$panes.$active);
  1468. });
  1469. ngModelCtrl.$formatters.push(function(modelValue) {
  1470. bsTabsCtrl.$setActive(modelValue);
  1471. return modelValue;
  1472. });
  1473. }
  1474. if (attrs.bsActivePane) {
  1475. var parsedBsActivePane = $parse(attrs.bsActivePane);
  1476. bsTabsCtrl.$activePaneChangeListeners.push(function() {
  1477. parsedBsActivePane.assign(scope, bsTabsCtrl.$panes.$active);
  1478. });
  1479. scope.$watch(attrs.bsActivePane, function(newValue, oldValue) {
  1480. bsTabsCtrl.$setActive(newValue);
  1481. }, true);
  1482. }
  1483. }
  1484. };
  1485. } ]).directive('bsPane', [ '$window', '$animate', '$sce', function($window, $animate, $sce) {
  1486. return {
  1487. require: [ '^?ngModel', '^bsTabs' ],
  1488. scope: true,
  1489. link: function postLink(scope, element, attrs, controllers) {
  1490. var bsTabsCtrl = controllers[1];
  1491. element.addClass('tab-pane');
  1492. attrs.$observe('title', function(newValue, oldValue) {
  1493. scope.title = $sce.trustAsHtml(newValue);
  1494. });
  1495. scope.name = attrs.name;
  1496. if (bsTabsCtrl.$options.animation) {
  1497. element.addClass(bsTabsCtrl.$options.animation);
  1498. }
  1499. attrs.$observe('disabled', function(newValue, oldValue) {
  1500. scope.disabled = scope.$eval(newValue);
  1501. });
  1502. bsTabsCtrl.$push(scope);
  1503. scope.$on('$destroy', function() {
  1504. bsTabsCtrl.$remove(scope);
  1505. });
  1506. function render() {
  1507. var index = bsTabsCtrl.$panes.indexOf(scope);
  1508. $animate[bsTabsCtrl.$isActive(scope, index) ? 'addClass' : 'removeClass'](element, bsTabsCtrl.$options.activeClass);
  1509. }
  1510. bsTabsCtrl.$activePaneChangeListeners.push(function() {
  1511. render();
  1512. });
  1513. render();
  1514. }
  1515. };
  1516. } ]);
  1517. angular.module('mgcrea.ngStrap.select', [ 'mgcrea.ngStrap.tooltip', 'mgcrea.ngStrap.helpers.parseOptions' ]).provider('$select', function() {
  1518. var defaults = this.defaults = {
  1519. animation: 'am-fade',
  1520. prefixClass: 'select',
  1521. prefixEvent: '$select',
  1522. placement: 'bottom-left',
  1523. templateUrl: 'select/select.tpl.html',
  1524. trigger: 'focus',
  1525. container: false,
  1526. keyboard: true,
  1527. html: false,
  1528. delay: 0,
  1529. multiple: false,
  1530. allNoneButtons: false,
  1531. sort: true,
  1532. caretHtml: '&nbsp;<span class="caret"></span>',
  1533. placeholder: 'Choose among the following...',
  1534. allText: 'All',
  1535. noneText: 'None',
  1536. maxLength: 3,
  1537. maxLengthHtml: 'selected',
  1538. iconCheckmark: 'glyphicon glyphicon-ok',
  1539. toggle: false
  1540. };
  1541. this.$get = [ '$window', '$document', '$rootScope', '$tooltip', '$timeout', function($window, $document, $rootScope, $tooltip, $timeout) {
  1542. var isNative = /(ip[ao]d|iphone|android)/gi.test($window.navigator.userAgent);
  1543. var isTouch = 'createTouch' in $window.document && isNative;
  1544. function SelectFactory(element, controller, config) {
  1545. var $select = {};
  1546. var options = angular.extend({}, defaults, config);
  1547. $select = $tooltip(element, options);
  1548. var scope = $select.$scope;
  1549. scope.$matches = [];
  1550. if (options.multiple) {
  1551. scope.$activeIndex = [];
  1552. } else {
  1553. scope.$activeIndex = -1;
  1554. }
  1555. scope.$isMultiple = options.multiple;
  1556. scope.$showAllNoneButtons = options.allNoneButtons && options.multiple;
  1557. scope.$iconCheckmark = options.iconCheckmark;
  1558. scope.$allText = options.allText;
  1559. scope.$noneText = options.noneText;
  1560. scope.$activate = function(index) {
  1561. scope.$$postDigest(function() {
  1562. $select.activate(index);
  1563. });
  1564. };
  1565. scope.$select = function(index, evt) {
  1566. scope.$$postDigest(function() {
  1567. $select.select(index);
  1568. });
  1569. };
  1570. scope.$isVisible = function() {
  1571. return $select.$isVisible();
  1572. };
  1573. scope.$isActive = function(index) {
  1574. return $select.$isActive(index);
  1575. };
  1576. scope.$selectAll = function() {
  1577. for (var i = 0; i < scope.$matches.length; i++) {
  1578. if (!scope.$isActive(i)) {
  1579. scope.$select(i);
  1580. }
  1581. }
  1582. };
  1583. scope.$selectNone = function() {
  1584. for (var i = 0; i < scope.$matches.length; i++) {
  1585. if (scope.$isActive(i)) {
  1586. scope.$select(i);
  1587. }
  1588. }
  1589. };
  1590. $select.update = function(matches) {
  1591. scope.$matches = matches;
  1592. $select.$updateActiveIndex();
  1593. };
  1594. $select.activate = function(index) {
  1595. if (options.multiple) {
  1596. if ($select.$isActive(index)) {
  1597. scope.$activeIndex.splice(scope.$activeIndex.indexOf(index), 1);
  1598. } else {
  1599. scope.$activeIndex.push(index);
  1600. }
  1601. if (options.sort) scope.$activeIndex.sort(function(a, b) {
  1602. return a - b;
  1603. });
  1604. } else {
  1605. scope.$activeIndex = index;
  1606. }
  1607. return scope.$activeIndex;
  1608. };
  1609. $select.select = function(index) {
  1610. if (angular.isUndefined(index) || index < 0 || index >= scope.$matches.length) {
  1611. return;
  1612. }
  1613. var value = scope.$matches[index].value;
  1614. scope.$apply(function() {
  1615. $select.activate(index);
  1616. if (options.multiple) {
  1617. controller.$setViewValue(scope.$activeIndex.map(function(index) {
  1618. if (angular.isUndefined(scope.$matches[index])) {
  1619. return null;
  1620. }
  1621. return scope.$matches[index].value;
  1622. }));
  1623. } else {
  1624. if (options.toggle) {
  1625. controller.$setViewValue(value === controller.$modelValue ? undefined : value);
  1626. } else {
  1627. controller.$setViewValue(value);
  1628. }
  1629. $select.hide();
  1630. }
  1631. });
  1632. scope.$emit(options.prefixEvent + '.select', value, index, $select);
  1633. if (angular.isDefined(options.onSelect) && angular.isFunction(options.onSelect)) {
  1634. options.onSelect(value, index, $select);
  1635. }
  1636. };
  1637. $select.$updateActiveIndex = function() {
  1638. if (options.multiple) {
  1639. if (angular.isArray(controller.$modelValue)) {
  1640. scope.$activeIndex = controller.$modelValue.map(function(value) {
  1641. return $select.$getIndex(value);
  1642. });
  1643. } else {
  1644. scope.$activeIndex = [];
  1645. }
  1646. } else {
  1647. if (angular.isDefined(controller.$modelValue) && scope.$matches.length) {
  1648. scope.$activeIndex = $select.$getIndex(controller.$modelValue);
  1649. } else {
  1650. scope.$activeIndex = -1;
  1651. }
  1652. }
  1653. };
  1654. $select.$isVisible = function() {
  1655. if (!options.minLength || !controller) {
  1656. return scope.$matches.length;
  1657. }
  1658. return scope.$matches.length && controller.$viewValue.length >= options.minLength;
  1659. };
  1660. $select.$isActive = function(index) {
  1661. if (options.multiple) {
  1662. return scope.$activeIndex.indexOf(index) !== -1;
  1663. }
  1664. return scope.$activeIndex === index;
  1665. };
  1666. $select.$getIndex = function(value) {
  1667. var index;
  1668. for (index = scope.$matches.length; index--; ) {
  1669. if (angular.equals(scope.$matches[index].value, value)) break;
  1670. }
  1671. return index;
  1672. };
  1673. $select.$onMouseDown = function(evt) {
  1674. evt.preventDefault();
  1675. evt.stopPropagation();
  1676. if (isTouch) {
  1677. var targetEl = angular.element(evt.target);
  1678. var anchor;
  1679. if (evt.target.nodeName !== 'A') {
  1680. var anchorCandidate = targetEl.parent();
  1681. while (!anchor && anchorCandidate.length > 0) {
  1682. if (anchorCandidate[0].nodeName === 'A') {
  1683. anchor = anchorCandidate;
  1684. }
  1685. anchorCandidate = anchorCandidate.parent();
  1686. }
  1687. }
  1688. if (anchor) {
  1689. angular.element(anchor).triggerHandler('click');
  1690. } else {
  1691. targetEl.triggerHandler('click');
  1692. }
  1693. }
  1694. };
  1695. $select.$onKeyDown = function(evt) {
  1696. if (!/(9|13|38|40)/.test(evt.keyCode)) return;
  1697. if (evt.keyCode !== 9) {
  1698. evt.preventDefault();
  1699. evt.stopPropagation();
  1700. }
  1701. if (options.multiple && evt.keyCode === 9) {
  1702. return $select.hide();
  1703. }
  1704. if (!options.multiple && (evt.keyCode === 13 || evt.keyCode === 9)) {
  1705. return $select.select(scope.$activeIndex);
  1706. }
  1707. if (!options.multiple) {
  1708. if (evt.keyCode === 38 && scope.$activeIndex > 0) scope.$activeIndex--; else if (evt.keyCode === 38 && scope.$activeIndex < 0) scope.$activeIndex = scope.$matches.length - 1; else if (evt.keyCode === 40 && scope.$activeIndex < scope.$matches.length - 1) scope.$activeIndex++; else if (angular.isUndefined(scope.$activeIndex)) scope.$activeIndex = 0;
  1709. scope.$digest();
  1710. }
  1711. };
  1712. $select.$isIE = function() {
  1713. var ua = $window.navigator.userAgent;
  1714. return ua.indexOf('MSIE ') > 0 || ua.indexOf('Trident/') > 0 || ua.indexOf('Edge/') > 0;
  1715. };
  1716. $select.$selectScrollFix = function(e) {
  1717. if ($document[0].activeElement.tagName === 'UL') {
  1718. e.preventDefault();
  1719. e.stopImmediatePropagation();
  1720. e.target.focus();
  1721. }
  1722. };
  1723. var _show = $select.show;
  1724. $select.show = function() {
  1725. _show();
  1726. if (options.multiple) {
  1727. $select.$element.addClass('select-multiple');
  1728. }
  1729. $timeout(function() {
  1730. $select.$element.on(isTouch ? 'touchstart' : 'mousedown', $select.$onMouseDown);
  1731. if (options.keyboard) {
  1732. element.on('keydown', $select.$onKeyDown);
  1733. }
  1734. }, 0, false);
  1735. };
  1736. var _hide = $select.hide;
  1737. $select.hide = function() {
  1738. if (!options.multiple && angular.isUndefined(controller.$modelValue)) {
  1739. scope.$activeIndex = -1;
  1740. }
  1741. $select.$element.off(isTouch ? 'touchstart' : 'mousedown', $select.$onMouseDown);
  1742. if (options.keyboard) {
  1743. element.off('keydown', $select.$onKeyDown);
  1744. }
  1745. _hide(true);
  1746. };
  1747. return $select;
  1748. }
  1749. SelectFactory.defaults = defaults;
  1750. return SelectFactory;
  1751. } ];
  1752. }).directive('bsSelect', [ '$window', '$parse', '$q', '$select', '$parseOptions', function($window, $parse, $q, $select, $parseOptions) {
  1753. var defaults = $select.defaults;
  1754. return {
  1755. restrict: 'EAC',
  1756. require: 'ngModel',
  1757. link: function postLink(scope, element, attr, controller) {
  1758. var options = {
  1759. scope: scope,
  1760. placeholder: defaults.placeholder
  1761. };
  1762. angular.forEach([ 'template', 'templateUrl', 'controller', 'controllerAs', 'placement', 'container', 'delay', 'trigger', 'keyboard', 'html', 'animation', 'placeholder', 'allNoneButtons', 'maxLength', 'maxLengthHtml', 'allText', 'noneText', 'iconCheckmark', 'autoClose', 'id', 'sort', 'caretHtml', 'prefixClass', 'prefixEvent', 'toggle' ], function(key) {
  1763. if (angular.isDefined(attr[key])) options[key] = attr[key];
  1764. });
  1765. var falseValueRegExp = /^(false|0|)$/i;
  1766. angular.forEach([ 'html', 'container', 'allNoneButtons', 'sort' ], function(key) {
  1767. if (angular.isDefined(attr[key]) && falseValueRegExp.test(attr[key])) {
  1768. options[key] = false;
  1769. }
  1770. });
  1771. angular.forEach([ 'onBeforeShow', 'onShow', 'onBeforeHide', 'onHide', 'onSelect' ], function(key) {
  1772. var bsKey = 'bs' + key.charAt(0).toUpperCase() + key.slice(1);
  1773. if (angular.isDefined(attr[bsKey])) {
  1774. options[key] = scope.$eval(attr[bsKey]);
  1775. }
  1776. });
  1777. var dataMultiple = element.attr('data-multiple');
  1778. if (angular.isDefined(dataMultiple)) {
  1779. if (falseValueRegExp.test(dataMultiple)) {
  1780. options.multiple = false;
  1781. } else {
  1782. options.multiple = dataMultiple;
  1783. }
  1784. }
  1785. if (element[0].nodeName.toLowerCase() === 'select') {
  1786. var inputEl = element;
  1787. inputEl.css('display', 'none');
  1788. element = angular.element('<button type="button" class="btn btn-default"></button>');
  1789. inputEl.after(element);
  1790. }
  1791. var parsedOptions = $parseOptions(attr.bsOptions);
  1792. var select = $select(element, controller, options);
  1793. if (select.$isIE()) {
  1794. element[0].addEventListener('blur', select.$selectScrollFix);
  1795. }
  1796. var watchedOptions = parsedOptions.$match[7].replace(/\|.+/, '').trim();
  1797. scope.$watch(watchedOptions, function(newValue, oldValue) {
  1798. parsedOptions.valuesFn(scope, controller).then(function(values) {
  1799. select.update(values);
  1800. controller.$render();
  1801. });
  1802. }, true);
  1803. scope.$watch(attr.ngModel, function(newValue, oldValue) {
  1804. select.$updateActiveIndex();
  1805. controller.$render();
  1806. }, true);
  1807. controller.$render = function() {
  1808. var selected;
  1809. var index;
  1810. if (options.multiple && angular.isArray(controller.$modelValue)) {
  1811. selected = controller.$modelValue.map(function(value) {
  1812. index = select.$getIndex(value);
  1813. return index !== -1 ? select.$scope.$matches[index].label : false;
  1814. }).filter(angular.isDefined);
  1815. if (selected.length > (options.maxLength || defaults.maxLength)) {
  1816. selected = selected.length + ' ' + (options.maxLengthHtml || defaults.maxLengthHtml);
  1817. } else {
  1818. selected = selected.join(', ');
  1819. }
  1820. } else {
  1821. index = select.$getIndex(controller.$modelValue);
  1822. selected = index !== -1 ? select.$scope.$matches[index].label : false;
  1823. }
  1824. element.html((selected || options.placeholder) + (options.caretHtml || defaults.caretHtml));
  1825. };
  1826. if (options.multiple) {
  1827. controller.$isEmpty = function(value) {
  1828. return !value || value.length === 0;
  1829. };
  1830. }
  1831. scope.$on('$destroy', function() {
  1832. if (select) select.destroy();
  1833. options = null;
  1834. select = null;
  1835. });
  1836. }
  1837. };
  1838. } ]);
  1839. angular.module('mgcrea.ngStrap.scrollspy', [ 'mgcrea.ngStrap.helpers.debounce', 'mgcrea.ngStrap.helpers.dimensions' ]).provider('$scrollspy', function() {
  1840. var spies = this.$$spies = {};
  1841. var defaults = this.defaults = {
  1842. debounce: 150,
  1843. throttle: 100,
  1844. offset: 100
  1845. };
  1846. this.$get = [ '$window', '$document', '$rootScope', 'dimensions', 'debounce', 'throttle', function($window, $document, $rootScope, dimensions, debounce, throttle) {
  1847. var windowEl = angular.element($window);
  1848. var docEl = angular.element($document.prop('documentElement'));
  1849. var bodyEl = angular.element($window.document.body);
  1850. function nodeName(element, name) {
  1851. return element[0].nodeName && element[0].nodeName.toLowerCase() === name.toLowerCase();
  1852. }
  1853. function ScrollSpyFactory(config) {
  1854. var options = angular.extend({}, defaults, config);
  1855. if (!options.element) options.element = bodyEl;
  1856. var isWindowSpy = nodeName(options.element, 'body');
  1857. var scrollEl = isWindowSpy ? windowEl : options.element;
  1858. var scrollId = isWindowSpy ? 'window' : options.id;
  1859. if (spies[scrollId]) {
  1860. spies[scrollId].$$count++;
  1861. return spies[scrollId];
  1862. }
  1863. var $scrollspy = {};
  1864. var unbindViewContentLoaded;
  1865. var unbindIncludeContentLoaded;
  1866. var trackedElements = $scrollspy.$trackedElements = [];
  1867. var sortedElements = [];
  1868. var activeTarget;
  1869. var debouncedCheckPosition;
  1870. var throttledCheckPosition;
  1871. var debouncedCheckOffsets;
  1872. var viewportHeight;
  1873. var scrollTop;
  1874. $scrollspy.init = function() {
  1875. this.$$count = 1;
  1876. debouncedCheckPosition = debounce(this.checkPosition, options.debounce);
  1877. throttledCheckPosition = throttle(this.checkPosition, options.throttle);
  1878. scrollEl.on('click', this.checkPositionWithEventLoop);
  1879. windowEl.on('resize', debouncedCheckPosition);
  1880. scrollEl.on('scroll', throttledCheckPosition);
  1881. debouncedCheckOffsets = debounce(this.checkOffsets, options.debounce);
  1882. unbindViewContentLoaded = $rootScope.$on('$viewContentLoaded', debouncedCheckOffsets);
  1883. unbindIncludeContentLoaded = $rootScope.$on('$includeContentLoaded', debouncedCheckOffsets);
  1884. debouncedCheckOffsets();
  1885. if (scrollId) {
  1886. spies[scrollId] = $scrollspy;
  1887. }
  1888. };
  1889. $scrollspy.destroy = function() {
  1890. this.$$count--;
  1891. if (this.$$count > 0) {
  1892. return;
  1893. }
  1894. scrollEl.off('click', this.checkPositionWithEventLoop);
  1895. windowEl.off('resize', debouncedCheckPosition);
  1896. scrollEl.off('scroll', throttledCheckPosition);
  1897. unbindViewContentLoaded();
  1898. unbindIncludeContentLoaded();
  1899. if (scrollId) {
  1900. delete spies[scrollId];
  1901. }
  1902. };
  1903. $scrollspy.checkPosition = function() {
  1904. if (!sortedElements.length) return;
  1905. scrollTop = (isWindowSpy ? $window.pageYOffset : scrollEl.prop('scrollTop')) || 0;
  1906. viewportHeight = Math.max($window.innerHeight, docEl.prop('clientHeight'));
  1907. if (scrollTop < sortedElements[0].offsetTop && activeTarget !== sortedElements[0].target) {
  1908. return $scrollspy.$activateElement(sortedElements[0]);
  1909. }
  1910. for (var i = sortedElements.length; i--; ) {
  1911. if (angular.isUndefined(sortedElements[i].offsetTop) || sortedElements[i].offsetTop === null) continue;
  1912. if (activeTarget === sortedElements[i].target) continue;
  1913. if (scrollTop < sortedElements[i].offsetTop) continue;
  1914. if (sortedElements[i + 1] && scrollTop > sortedElements[i + 1].offsetTop) continue;
  1915. return $scrollspy.$activateElement(sortedElements[i]);
  1916. }
  1917. };
  1918. $scrollspy.checkPositionWithEventLoop = function() {
  1919. setTimeout($scrollspy.checkPosition, 1);
  1920. };
  1921. $scrollspy.$activateElement = function(element) {
  1922. if (activeTarget) {
  1923. var activeElement = $scrollspy.$getTrackedElement(activeTarget);
  1924. if (activeElement) {
  1925. activeElement.source.removeClass('active');
  1926. if (nodeName(activeElement.source, 'li') && nodeName(activeElement.source.parent().parent(), 'li')) {
  1927. activeElement.source.parent().parent().removeClass('active');
  1928. }
  1929. }
  1930. }
  1931. activeTarget = element.target;
  1932. element.source.addClass('active');
  1933. if (nodeName(element.source, 'li') && nodeName(element.source.parent().parent(), 'li')) {
  1934. element.source.parent().parent().addClass('active');
  1935. }
  1936. };
  1937. $scrollspy.$getTrackedElement = function(target) {
  1938. return trackedElements.filter(function(obj) {
  1939. return obj.target === target;
  1940. })[0];
  1941. };
  1942. $scrollspy.checkOffsets = function() {
  1943. angular.forEach(trackedElements, function(trackedElement) {
  1944. var targetElement = document.querySelector(trackedElement.target);
  1945. trackedElement.offsetTop = targetElement ? dimensions.offset(targetElement).top : null;
  1946. if (options.offset && trackedElement.offsetTop !== null) trackedElement.offsetTop -= options.offset * 1;
  1947. });
  1948. sortedElements = trackedElements.filter(function(el) {
  1949. return el.offsetTop !== null;
  1950. }).sort(function(a, b) {
  1951. return a.offsetTop - b.offsetTop;
  1952. });
  1953. debouncedCheckPosition();
  1954. };
  1955. $scrollspy.trackElement = function(target, source) {
  1956. trackedElements.push({
  1957. target: target,
  1958. source: source
  1959. });
  1960. };
  1961. $scrollspy.untrackElement = function(target, source) {
  1962. var toDelete;
  1963. for (var i = trackedElements.length; i--; ) {
  1964. if (trackedElements[i].target === target && trackedElements[i].source === source) {
  1965. toDelete = i;
  1966. break;
  1967. }
  1968. }
  1969. trackedElements.splice(toDelete, 1);
  1970. };
  1971. $scrollspy.activate = function(i) {
  1972. trackedElements[i].addClass('active');
  1973. };
  1974. $scrollspy.init();
  1975. return $scrollspy;
  1976. }
  1977. return ScrollSpyFactory;
  1978. } ];
  1979. }).directive('bsScrollspy', [ '$rootScope', 'debounce', 'dimensions', '$scrollspy', function($rootScope, debounce, dimensions, $scrollspy) {
  1980. return {
  1981. restrict: 'EAC',
  1982. link: function postLink(scope, element, attr) {
  1983. var options = {
  1984. scope: scope
  1985. };
  1986. angular.forEach([ 'offset', 'target' ], function(key) {
  1987. if (angular.isDefined(attr[key])) options[key] = attr[key];
  1988. });
  1989. var scrollspy = $scrollspy(options);
  1990. scrollspy.trackElement(options.target, element);
  1991. scope.$on('$destroy', function() {
  1992. if (scrollspy) {
  1993. scrollspy.untrackElement(options.target, element);
  1994. scrollspy.destroy();
  1995. }
  1996. options = null;
  1997. scrollspy = null;
  1998. });
  1999. }
  2000. };
  2001. } ]).directive('bsScrollspyList', [ '$rootScope', 'debounce', 'dimensions', '$scrollspy', function($rootScope, debounce, dimensions, $scrollspy) {
  2002. return {
  2003. restrict: 'A',
  2004. compile: function postLink(element, attr) {
  2005. var children = element[0].querySelectorAll('li > a[href]');
  2006. angular.forEach(children, function(child) {
  2007. var childEl = angular.element(child);
  2008. childEl.parent().attr('bs-scrollspy', '').attr('data-target', childEl.attr('href'));
  2009. });
  2010. }
  2011. };
  2012. } ]);
  2013. angular.module('mgcrea.ngStrap.popover', [ 'mgcrea.ngStrap.tooltip' ]).provider('$popover', function() {
  2014. var defaults = this.defaults = {
  2015. animation: 'am-fade',
  2016. customClass: '',
  2017. container: false,
  2018. target: false,
  2019. placement: 'right',
  2020. templateUrl: 'popover/popover.tpl.html',
  2021. contentTemplate: false,
  2022. trigger: 'click',
  2023. keyboard: true,
  2024. html: false,
  2025. title: '',
  2026. content: '',
  2027. delay: 0,
  2028. autoClose: false
  2029. };
  2030. this.$get = [ '$tooltip', function($tooltip) {
  2031. function PopoverFactory(element, config) {
  2032. var options = angular.extend({}, defaults, config);
  2033. var $popover = $tooltip(element, options);
  2034. if (options.content) {
  2035. $popover.$scope.content = options.content;
  2036. }
  2037. return $popover;
  2038. }
  2039. return PopoverFactory;
  2040. } ];
  2041. }).directive('bsPopover', [ '$window', '$sce', '$popover', function($window, $sce, $popover) {
  2042. var requestAnimationFrame = $window.requestAnimationFrame || $window.setTimeout;
  2043. return {
  2044. restrict: 'EAC',
  2045. scope: true,
  2046. link: function postLink(scope, element, attr) {
  2047. var popover;
  2048. var options = {
  2049. scope: scope
  2050. };
  2051. angular.forEach([ 'template', 'templateUrl', 'controller', 'controllerAs', 'contentTemplate', 'placement', 'container', 'delay', 'trigger', 'html', 'animation', 'customClass', 'autoClose', 'id', 'prefixClass', 'prefixEvent', 'bsEnabled' ], function(key) {
  2052. if (angular.isDefined(attr[key])) options[key] = attr[key];
  2053. });
  2054. var falseValueRegExp = /^(false|0|)$/i;
  2055. angular.forEach([ 'html', 'container', 'autoClose' ], function(key) {
  2056. if (angular.isDefined(attr[key]) && falseValueRegExp.test(attr[key])) options[key] = false;
  2057. });
  2058. angular.forEach([ 'onBeforeShow', 'onShow', 'onBeforeHide', 'onHide' ], function(key) {
  2059. var bsKey = 'bs' + key.charAt(0).toUpperCase() + key.slice(1);
  2060. if (angular.isDefined(attr[bsKey])) {
  2061. options[key] = scope.$eval(attr[bsKey]);
  2062. }
  2063. });
  2064. var dataTarget = element.attr('data-target');
  2065. if (angular.isDefined(dataTarget)) {
  2066. if (falseValueRegExp.test(dataTarget)) {
  2067. options.target = false;
  2068. } else {
  2069. options.target = dataTarget;
  2070. }
  2071. }
  2072. angular.forEach([ 'title', 'content' ], function(key) {
  2073. if (attr[key]) {
  2074. attr.$observe(key, function(newValue, oldValue) {
  2075. scope[key] = $sce.trustAsHtml(newValue);
  2076. if (angular.isDefined(oldValue)) {
  2077. requestAnimationFrame(function() {
  2078. if (popover) popover.$applyPlacement();
  2079. });
  2080. }
  2081. });
  2082. }
  2083. });
  2084. if (attr.bsPopover) {
  2085. scope.$watch(attr.bsPopover, function(newValue, oldValue) {
  2086. if (angular.isObject(newValue)) {
  2087. angular.extend(scope, newValue);
  2088. } else {
  2089. scope.content = newValue;
  2090. }
  2091. if (angular.isDefined(oldValue)) {
  2092. requestAnimationFrame(function() {
  2093. if (popover) popover.$applyPlacement();
  2094. });
  2095. }
  2096. }, true);
  2097. }
  2098. if (attr.bsShow) {
  2099. scope.$watch(attr.bsShow, function(newValue, oldValue) {
  2100. if (!popover || !angular.isDefined(newValue)) return;
  2101. if (angular.isString(newValue)) newValue = !!newValue.match(/true|,?(popover),?/i);
  2102. if (newValue === true) {
  2103. popover.show();
  2104. } else {
  2105. popover.hide();
  2106. }
  2107. });
  2108. }
  2109. if (attr.bsEnabled) {
  2110. scope.$watch(attr.bsEnabled, function(newValue) {
  2111. if (!popover || !angular.isDefined(newValue)) return;
  2112. if (angular.isString(newValue)) newValue = !!newValue.match(/true|1|,?(popover),?/i);
  2113. if (newValue === false) {
  2114. popover.setEnabled(false);
  2115. } else {
  2116. popover.setEnabled(true);
  2117. }
  2118. });
  2119. }
  2120. if (attr.viewport) {
  2121. scope.$watch(attr.viewport, function(newValue) {
  2122. if (!popover || !angular.isDefined(newValue)) return;
  2123. popover.setViewport(newValue);
  2124. });
  2125. }
  2126. popover = $popover(element, options);
  2127. scope.$on('$destroy', function() {
  2128. if (popover) popover.destroy();
  2129. options = null;
  2130. popover = null;
  2131. });
  2132. }
  2133. };
  2134. } ]);
  2135. angular.module('mgcrea.ngStrap.navbar', []).provider('$navbar', function() {
  2136. var defaults = this.defaults = {
  2137. activeClass: 'active',
  2138. routeAttr: 'data-match-route',
  2139. strict: false
  2140. };
  2141. this.$get = function() {
  2142. return {
  2143. defaults: defaults
  2144. };
  2145. };
  2146. }).directive('bsNavbar', [ '$window', '$location', '$navbar', function($window, $location, $navbar) {
  2147. var defaults = $navbar.defaults;
  2148. return {
  2149. restrict: 'A',
  2150. link: function postLink(scope, element, attr, controller) {
  2151. var options = angular.copy(defaults);
  2152. angular.forEach(Object.keys(defaults), function(key) {
  2153. if (angular.isDefined(attr[key])) options[key] = attr[key];
  2154. });
  2155. scope.$watch(function() {
  2156. return $location.path();
  2157. }, function(newValue, oldValue) {
  2158. var liElements = element[0].querySelectorAll('li[' + options.routeAttr + ']');
  2159. angular.forEach(liElements, function(li) {
  2160. var liElement = angular.element(li);
  2161. var pattern = liElement.attr(options.routeAttr).replace('/', '\\/');
  2162. if (options.strict) {
  2163. pattern = '^' + pattern + '$';
  2164. }
  2165. var regexp = new RegExp(pattern, 'i');
  2166. if (regexp.test(newValue)) {
  2167. liElement.addClass(options.activeClass);
  2168. } else {
  2169. liElement.removeClass(options.activeClass);
  2170. }
  2171. });
  2172. });
  2173. }
  2174. };
  2175. } ]);
  2176. angular.module('mgcrea.ngStrap.modal', [ 'mgcrea.ngStrap.core', 'mgcrea.ngStrap.helpers.dimensions' ]).provider('$modal', function() {
  2177. var defaults = this.defaults = {
  2178. animation: 'am-fade',
  2179. backdropAnimation: 'am-fade',
  2180. customClass: '',
  2181. prefixClass: 'modal',
  2182. prefixEvent: 'modal',
  2183. placement: 'top',
  2184. templateUrl: 'modal/modal.tpl.html',
  2185. template: '',
  2186. contentTemplate: false,
  2187. container: false,
  2188. element: null,
  2189. backdrop: true,
  2190. keyboard: true,
  2191. html: false,
  2192. show: true,
  2193. size: null,
  2194. zIndex: null
  2195. };
  2196. this.$get = [ '$window', '$rootScope', '$bsCompiler', '$animate', '$timeout', '$sce', 'dimensions', function($window, $rootScope, $bsCompiler, $animate, $timeout, $sce, dimensions) {
  2197. var forEach = angular.forEach;
  2198. var requestAnimationFrame = $window.requestAnimationFrame || $window.setTimeout;
  2199. var bodyElement = angular.element($window.document.body);
  2200. var backdropCount = 0;
  2201. var dialogBaseZindex = 1050;
  2202. var backdropBaseZindex = 1040;
  2203. var validSizes = {
  2204. lg: 'modal-lg',
  2205. sm: 'modal-sm'
  2206. };
  2207. function ModalFactory(config) {
  2208. var $modal = {};
  2209. var options = $modal.$options = angular.extend({}, defaults, config);
  2210. var promise = $modal.$promise = $bsCompiler.compile(options);
  2211. var scope = $modal.$scope = options.scope && options.scope.$new() || $rootScope.$new();
  2212. if (!options.element && !options.container) {
  2213. options.container = 'body';
  2214. }
  2215. if (options.zIndex) {
  2216. dialogBaseZindex = parseInt(options.zIndex, 10);
  2217. backdropBaseZindex = dialogBaseZindex - 10;
  2218. }
  2219. $modal.$id = options.id || options.element && options.element.attr('id') || '';
  2220. forEach([ 'title', 'content' ], function(key) {
  2221. if (options[key]) scope[key] = $sce.trustAsHtml(options[key]);
  2222. });
  2223. scope.$hide = function() {
  2224. scope.$$postDigest(function() {
  2225. $modal.hide();
  2226. });
  2227. };
  2228. scope.$show = function() {
  2229. scope.$$postDigest(function() {
  2230. $modal.show();
  2231. });
  2232. };
  2233. scope.$toggle = function() {
  2234. scope.$$postDigest(function() {
  2235. $modal.toggle();
  2236. });
  2237. };
  2238. $modal.$isShown = scope.$isShown = false;
  2239. var compileData;
  2240. var modalElement;
  2241. var modalScope;
  2242. var backdropElement = angular.element('<div class="' + options.prefixClass + '-backdrop"/>');
  2243. backdropElement.css({
  2244. position: 'fixed',
  2245. top: '0px',
  2246. left: '0px',
  2247. bottom: '0px',
  2248. right: '0px'
  2249. });
  2250. promise.then(function(data) {
  2251. compileData = data;
  2252. $modal.init();
  2253. });
  2254. $modal.init = function() {
  2255. if (options.show) {
  2256. scope.$$postDigest(function() {
  2257. $modal.show();
  2258. });
  2259. }
  2260. };
  2261. $modal.destroy = function() {
  2262. destroyModalElement();
  2263. if (backdropElement) {
  2264. backdropElement.remove();
  2265. backdropElement = null;
  2266. }
  2267. scope.$destroy();
  2268. };
  2269. $modal.show = function() {
  2270. if ($modal.$isShown) return;
  2271. var parent;
  2272. var after;
  2273. if (angular.isElement(options.container)) {
  2274. parent = options.container;
  2275. after = options.container[0].lastChild ? angular.element(options.container[0].lastChild) : null;
  2276. } else {
  2277. if (options.container) {
  2278. parent = findElement(options.container);
  2279. after = parent[0] && parent[0].lastChild ? angular.element(parent[0].lastChild) : null;
  2280. } else {
  2281. parent = null;
  2282. after = options.element;
  2283. }
  2284. }
  2285. if (modalElement) destroyModalElement();
  2286. modalScope = $modal.$scope.$new();
  2287. modalElement = $modal.$element = compileData.link(modalScope, function(clonedElement, scope) {});
  2288. if (options.backdrop) {
  2289. modalElement.css({
  2290. 'z-index': dialogBaseZindex + backdropCount * 20
  2291. });
  2292. backdropElement.css({
  2293. 'z-index': backdropBaseZindex + backdropCount * 20
  2294. });
  2295. backdropCount++;
  2296. }
  2297. if (scope.$emit(options.prefixEvent + '.show.before', $modal).defaultPrevented) {
  2298. return;
  2299. }
  2300. if (angular.isDefined(options.onBeforeShow) && angular.isFunction(options.onBeforeShow)) {
  2301. options.onBeforeShow($modal);
  2302. }
  2303. modalElement.css({
  2304. display: 'block'
  2305. }).addClass(options.placement);
  2306. if (options.customClass) {
  2307. modalElement.addClass(options.customClass);
  2308. }
  2309. if (options.size && validSizes[options.size]) {
  2310. angular.element(findElement('.modal-dialog', modalElement[0])).addClass(validSizes[options.size]);
  2311. }
  2312. if (options.animation) {
  2313. if (options.backdrop) {
  2314. backdropElement.addClass(options.backdropAnimation);
  2315. }
  2316. modalElement.addClass(options.animation);
  2317. }
  2318. if (options.backdrop) {
  2319. $animate.enter(backdropElement, bodyElement, null);
  2320. }
  2321. if (angular.version.minor <= 2) {
  2322. $animate.enter(modalElement, parent, after, enterAnimateCallback);
  2323. } else {
  2324. $animate.enter(modalElement, parent, after).then(enterAnimateCallback);
  2325. }
  2326. $modal.$isShown = scope.$isShown = true;
  2327. safeDigest(scope);
  2328. var el = modalElement[0];
  2329. requestAnimationFrame(function() {
  2330. el.focus();
  2331. });
  2332. bodyElement.addClass(options.prefixClass + '-open');
  2333. if (options.animation) {
  2334. bodyElement.addClass(options.prefixClass + '-with-' + options.animation);
  2335. }
  2336. bindBackdropEvents();
  2337. bindKeyboardEvents();
  2338. };
  2339. function enterAnimateCallback() {
  2340. scope.$emit(options.prefixEvent + '.show', $modal);
  2341. if (angular.isDefined(options.onShow) && angular.isFunction(options.onShow)) {
  2342. options.onShow($modal);
  2343. }
  2344. }
  2345. $modal.hide = function() {
  2346. if (!$modal.$isShown) return;
  2347. if (scope.$emit(options.prefixEvent + '.hide.before', $modal).defaultPrevented) {
  2348. return;
  2349. }
  2350. if (angular.isDefined(options.onBeforeHide) && angular.isFunction(options.onBeforeHide)) {
  2351. options.onBeforeHide($modal);
  2352. }
  2353. if (angular.version.minor <= 2) {
  2354. $animate.leave(modalElement, leaveAnimateCallback);
  2355. } else {
  2356. $animate.leave(modalElement).then(leaveAnimateCallback);
  2357. }
  2358. if (options.backdrop) {
  2359. backdropCount--;
  2360. $animate.leave(backdropElement);
  2361. }
  2362. $modal.$isShown = scope.$isShown = false;
  2363. safeDigest(scope);
  2364. unbindBackdropEvents();
  2365. unbindKeyboardEvents();
  2366. };
  2367. function leaveAnimateCallback() {
  2368. scope.$emit(options.prefixEvent + '.hide', $modal);
  2369. if (angular.isDefined(options.onHide) && angular.isFunction(options.onHide)) {
  2370. options.onHide($modal);
  2371. }
  2372. if (findElement('.modal').length <= 0) {
  2373. bodyElement.removeClass(options.prefixClass + '-open');
  2374. }
  2375. if (options.animation) {
  2376. bodyElement.removeClass(options.prefixClass + '-with-' + options.animation);
  2377. }
  2378. }
  2379. $modal.toggle = function() {
  2380. if ($modal.$isShown) {
  2381. $modal.hide();
  2382. } else {
  2383. $modal.show();
  2384. }
  2385. };
  2386. $modal.focus = function() {
  2387. modalElement[0].focus();
  2388. };
  2389. $modal.$onKeyUp = function(evt) {
  2390. if (evt.which === 27 && $modal.$isShown) {
  2391. $modal.hide();
  2392. evt.stopPropagation();
  2393. }
  2394. };
  2395. function bindBackdropEvents() {
  2396. if (options.backdrop) {
  2397. modalElement.on('click', hideOnBackdropClick);
  2398. backdropElement.on('click', hideOnBackdropClick);
  2399. backdropElement.on('wheel', preventEventDefault);
  2400. }
  2401. }
  2402. function unbindBackdropEvents() {
  2403. if (options.backdrop) {
  2404. modalElement.off('click', hideOnBackdropClick);
  2405. backdropElement.off('click', hideOnBackdropClick);
  2406. backdropElement.off('wheel', preventEventDefault);
  2407. }
  2408. }
  2409. function bindKeyboardEvents() {
  2410. if (options.keyboard) {
  2411. modalElement.on('keyup', $modal.$onKeyUp);
  2412. }
  2413. }
  2414. function unbindKeyboardEvents() {
  2415. if (options.keyboard) {
  2416. modalElement.off('keyup', $modal.$onKeyUp);
  2417. }
  2418. }
  2419. function hideOnBackdropClick(evt) {
  2420. if (evt.target !== evt.currentTarget) return;
  2421. if (options.backdrop === 'static') {
  2422. $modal.focus();
  2423. } else {
  2424. $modal.hide();
  2425. }
  2426. }
  2427. function preventEventDefault(evt) {
  2428. evt.preventDefault();
  2429. }
  2430. function destroyModalElement() {
  2431. if ($modal.$isShown && modalElement !== null) {
  2432. unbindBackdropEvents();
  2433. unbindKeyboardEvents();
  2434. }
  2435. if (modalScope) {
  2436. modalScope.$destroy();
  2437. modalScope = null;
  2438. }
  2439. if (modalElement) {
  2440. modalElement.remove();
  2441. modalElement = $modal.$element = null;
  2442. }
  2443. }
  2444. return $modal;
  2445. }
  2446. function safeDigest(scope) {
  2447. scope.$$phase || scope.$root && scope.$root.$$phase || scope.$digest();
  2448. }
  2449. function findElement(query, element) {
  2450. return angular.element((element || document).querySelectorAll(query));
  2451. }
  2452. return ModalFactory;
  2453. } ];
  2454. }).directive('bsModal', [ '$window', '$sce', '$parse', '$modal', function($window, $sce, $parse, $modal) {
  2455. return {
  2456. restrict: 'EAC',
  2457. scope: true,
  2458. link: function postLink(scope, element, attr, transclusion) {
  2459. var options = {
  2460. scope: scope,
  2461. element: element,
  2462. show: false
  2463. };
  2464. angular.forEach([ 'template', 'templateUrl', 'controller', 'controllerAs', 'contentTemplate', 'placement', 'backdrop', 'keyboard', 'html', 'container', 'animation', 'backdropAnimation', 'id', 'prefixEvent', 'prefixClass', 'customClass', 'modalClass', 'size', 'zIndex' ], function(key) {
  2465. if (angular.isDefined(attr[key])) options[key] = attr[key];
  2466. });
  2467. if (options.modalClass) {
  2468. options.customClass = options.modalClass;
  2469. }
  2470. var falseValueRegExp = /^(false|0|)$/i;
  2471. angular.forEach([ 'backdrop', 'keyboard', 'html', 'container' ], function(key) {
  2472. if (angular.isDefined(attr[key]) && falseValueRegExp.test(attr[key])) options[key] = false;
  2473. });
  2474. angular.forEach([ 'onBeforeShow', 'onShow', 'onBeforeHide', 'onHide' ], function(key) {
  2475. var bsKey = 'bs' + key.charAt(0).toUpperCase() + key.slice(1);
  2476. if (angular.isDefined(attr[bsKey])) {
  2477. options[key] = scope.$eval(attr[bsKey]);
  2478. }
  2479. });
  2480. angular.forEach([ 'title', 'content' ], function(key) {
  2481. if (attr[key]) {
  2482. attr.$observe(key, function(newValue, oldValue) {
  2483. scope[key] = $sce.trustAsHtml(newValue);
  2484. });
  2485. }
  2486. });
  2487. if (attr.bsModal) {
  2488. scope.$watch(attr.bsModal, function(newValue, oldValue) {
  2489. if (angular.isObject(newValue)) {
  2490. angular.extend(scope, newValue);
  2491. } else {
  2492. scope.content = newValue;
  2493. }
  2494. }, true);
  2495. }
  2496. var modal = $modal(options);
  2497. element.on(attr.trigger || 'click', modal.toggle);
  2498. scope.$on('$destroy', function() {
  2499. if (modal) modal.destroy();
  2500. options = null;
  2501. modal = null;
  2502. });
  2503. }
  2504. };
  2505. } ]);
  2506. if (angular.version.minor < 3 && angular.version.dot < 14) {
  2507. angular.module('ng').factory('$$rAF', [ '$window', '$timeout', function($window, $timeout) {
  2508. var requestAnimationFrame = $window.requestAnimationFrame || $window.webkitRequestAnimationFrame || $window.mozRequestAnimationFrame;
  2509. var cancelAnimationFrame = $window.cancelAnimationFrame || $window.webkitCancelAnimationFrame || $window.mozCancelAnimationFrame || $window.webkitCancelRequestAnimationFrame;
  2510. var rafSupported = !!requestAnimationFrame;
  2511. var raf = rafSupported ? function(fn) {
  2512. var id = requestAnimationFrame(fn);
  2513. return function() {
  2514. cancelAnimationFrame(id);
  2515. };
  2516. } : function(fn) {
  2517. var timer = $timeout(fn, 16.66, false);
  2518. return function() {
  2519. $timeout.cancel(timer);
  2520. };
  2521. };
  2522. raf.supported = rafSupported;
  2523. return raf;
  2524. } ]);
  2525. }
  2526. angular.module('mgcrea.ngStrap.helpers.parseOptions', []).provider('$parseOptions', function() {
  2527. var defaults = this.defaults = {
  2528. regexp: /^\s*(.*?)(?:\s+as\s+(.*?))?(?:\s+group\s+by\s+(.*))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+(.*?)(?:\s+track\s+by\s+(.*?))?$/
  2529. };
  2530. this.$get = [ '$parse', '$q', function($parse, $q) {
  2531. function ParseOptionsFactory(attr, config) {
  2532. var $parseOptions = {};
  2533. var options = angular.extend({}, defaults, config);
  2534. $parseOptions.$values = [];
  2535. var match;
  2536. var displayFn;
  2537. var valueName;
  2538. var keyName;
  2539. var groupByFn;
  2540. var valueFn;
  2541. var valuesFn;
  2542. $parseOptions.init = function() {
  2543. $parseOptions.$match = match = attr.match(options.regexp);
  2544. displayFn = $parse(match[2] || match[1]);
  2545. valueName = match[4] || match[6];
  2546. keyName = match[5];
  2547. groupByFn = $parse(match[3] || '');
  2548. valueFn = $parse(match[2] ? match[1] : valueName);
  2549. valuesFn = $parse(match[7]);
  2550. };
  2551. $parseOptions.valuesFn = function(scope, controller) {
  2552. return $q.when(valuesFn(scope, controller)).then(function(values) {
  2553. if (!angular.isArray(values)) {
  2554. values = [];
  2555. }
  2556. $parseOptions.$values = values.length ? parseValues(values, scope) : [];
  2557. return $parseOptions.$values;
  2558. });
  2559. };
  2560. $parseOptions.displayValue = function(modelValue) {
  2561. var scope = {};
  2562. scope[valueName] = modelValue;
  2563. return displayFn(scope);
  2564. };
  2565. function parseValues(values, scope) {
  2566. return values.map(function(match, index) {
  2567. var locals = {};
  2568. var label;
  2569. var value;
  2570. locals[valueName] = match;
  2571. label = displayFn(scope, locals);
  2572. value = valueFn(scope, locals);
  2573. return {
  2574. label: label,
  2575. value: value,
  2576. index: index
  2577. };
  2578. });
  2579. }
  2580. $parseOptions.init();
  2581. return $parseOptions;
  2582. }
  2583. return ParseOptionsFactory;
  2584. } ];
  2585. });
  2586. angular.module('mgcrea.ngStrap.helpers.dimensions', []).factory('dimensions', function() {
  2587. var fn = {};
  2588. var nodeName = fn.nodeName = function(element, name) {
  2589. return element.nodeName && element.nodeName.toLowerCase() === name.toLowerCase();
  2590. };
  2591. fn.css = function(element, prop, extra) {
  2592. var value;
  2593. if (element.currentStyle) {
  2594. value = element.currentStyle[prop];
  2595. } else if (window.getComputedStyle) {
  2596. value = window.getComputedStyle(element)[prop];
  2597. } else {
  2598. value = element.style[prop];
  2599. }
  2600. return extra === true ? parseFloat(value) || 0 : value;
  2601. };
  2602. fn.offset = function(element) {
  2603. var boxRect = element.getBoundingClientRect();
  2604. var docElement = element.ownerDocument;
  2605. return {
  2606. width: boxRect.width || element.offsetWidth,
  2607. height: boxRect.height || element.offsetHeight,
  2608. top: boxRect.top + (window.pageYOffset || docElement.documentElement.scrollTop) - (docElement.documentElement.clientTop || 0),
  2609. left: boxRect.left + (window.pageXOffset || docElement.documentElement.scrollLeft) - (docElement.documentElement.clientLeft || 0)
  2610. };
  2611. };
  2612. fn.setOffset = function(element, options, i) {
  2613. var curPosition;
  2614. var curLeft;
  2615. var curCSSTop;
  2616. var curTop;
  2617. var curOffset;
  2618. var curCSSLeft;
  2619. var calculatePosition;
  2620. var position = fn.css(element, 'position');
  2621. var curElem = angular.element(element);
  2622. var props = {};
  2623. if (position === 'static') {
  2624. element.style.position = 'relative';
  2625. }
  2626. curOffset = fn.offset(element);
  2627. curCSSTop = fn.css(element, 'top');
  2628. curCSSLeft = fn.css(element, 'left');
  2629. calculatePosition = (position === 'absolute' || position === 'fixed') && (curCSSTop + curCSSLeft).indexOf('auto') > -1;
  2630. if (calculatePosition) {
  2631. curPosition = fn.position(element);
  2632. curTop = curPosition.top;
  2633. curLeft = curPosition.left;
  2634. } else {
  2635. curTop = parseFloat(curCSSTop) || 0;
  2636. curLeft = parseFloat(curCSSLeft) || 0;
  2637. }
  2638. if (angular.isFunction(options)) {
  2639. options = options.call(element, i, curOffset);
  2640. }
  2641. if (options.top !== null) {
  2642. props.top = options.top - curOffset.top + curTop;
  2643. }
  2644. if (options.left !== null) {
  2645. props.left = options.left - curOffset.left + curLeft;
  2646. }
  2647. if ('using' in options) {
  2648. options.using.call(curElem, props);
  2649. } else {
  2650. curElem.css({
  2651. top: props.top + 'px',
  2652. left: props.left + 'px'
  2653. });
  2654. }
  2655. };
  2656. fn.position = function(element) {
  2657. var offsetParentRect = {
  2658. top: 0,
  2659. left: 0
  2660. };
  2661. var offsetParentEl;
  2662. var offset;
  2663. if (fn.css(element, 'position') === 'fixed') {
  2664. offset = element.getBoundingClientRect();
  2665. } else {
  2666. offsetParentEl = offsetParentElement(element);
  2667. offset = fn.offset(element);
  2668. if (!nodeName(offsetParentEl, 'html')) {
  2669. offsetParentRect = fn.offset(offsetParentEl);
  2670. }
  2671. offsetParentRect.top += fn.css(offsetParentEl, 'borderTopWidth', true);
  2672. offsetParentRect.left += fn.css(offsetParentEl, 'borderLeftWidth', true);
  2673. }
  2674. return {
  2675. width: element.offsetWidth,
  2676. height: element.offsetHeight,
  2677. top: offset.top - offsetParentRect.top - fn.css(element, 'marginTop', true),
  2678. left: offset.left - offsetParentRect.left - fn.css(element, 'marginLeft', true)
  2679. };
  2680. };
  2681. function offsetParentElement(element) {
  2682. var docElement = element.ownerDocument;
  2683. var offsetParent = element.offsetParent || docElement;
  2684. if (nodeName(offsetParent, '#document')) return docElement.documentElement;
  2685. while (offsetParent && !nodeName(offsetParent, 'html') && fn.css(offsetParent, 'position') === 'static') {
  2686. offsetParent = offsetParent.offsetParent;
  2687. }
  2688. return offsetParent || docElement.documentElement;
  2689. }
  2690. fn.height = function(element, outer) {
  2691. var value = element.offsetHeight;
  2692. if (outer) {
  2693. value += fn.css(element, 'marginTop', true) + fn.css(element, 'marginBottom', true);
  2694. } else {
  2695. value -= fn.css(element, 'paddingTop', true) + fn.css(element, 'paddingBottom', true) + fn.css(element, 'borderTopWidth', true) + fn.css(element, 'borderBottomWidth', true);
  2696. }
  2697. return value;
  2698. };
  2699. fn.width = function(element, outer) {
  2700. var value = element.offsetWidth;
  2701. if (outer) {
  2702. value += fn.css(element, 'marginLeft', true) + fn.css(element, 'marginRight', true);
  2703. } else {
  2704. value -= fn.css(element, 'paddingLeft', true) + fn.css(element, 'paddingRight', true) + fn.css(element, 'borderLeftWidth', true) + fn.css(element, 'borderRightWidth', true);
  2705. }
  2706. return value;
  2707. };
  2708. return fn;
  2709. });
  2710. angular.module('mgcrea.ngStrap.helpers.debounce', []).factory('debounce', [ '$timeout', function($timeout) {
  2711. return function(func, wait, immediate) {
  2712. var timeout = null;
  2713. return function() {
  2714. var context = this;
  2715. var args = arguments;
  2716. var callNow = immediate && !timeout;
  2717. if (timeout) {
  2718. $timeout.cancel(timeout);
  2719. }
  2720. timeout = $timeout(function later() {
  2721. timeout = null;
  2722. if (!immediate) {
  2723. func.apply(context, args);
  2724. }
  2725. }, wait, false);
  2726. if (callNow) {
  2727. func.apply(context, args);
  2728. }
  2729. return timeout;
  2730. };
  2731. };
  2732. } ]).factory('throttle', [ '$timeout', function($timeout) {
  2733. return function(func, wait, options) {
  2734. var timeout = null;
  2735. if (!options) options = {};
  2736. return function() {
  2737. var context = this;
  2738. var args = arguments;
  2739. if (!timeout) {
  2740. if (options.leading !== false) {
  2741. func.apply(context, args);
  2742. }
  2743. timeout = $timeout(function later() {
  2744. timeout = null;
  2745. if (options.trailing !== false) {
  2746. func.apply(context, args);
  2747. }
  2748. }, wait, false);
  2749. }
  2750. };
  2751. };
  2752. } ]);
  2753. angular.module('mgcrea.ngStrap.helpers.dateParser', []).provider('$dateParser', [ '$localeProvider', function($localeProvider) {
  2754. function ParseDate() {
  2755. this.year = 1970;
  2756. this.month = 0;
  2757. this.day = 1;
  2758. this.hours = 0;
  2759. this.minutes = 0;
  2760. this.seconds = 0;
  2761. this.milliseconds = 0;
  2762. }
  2763. ParseDate.prototype.setMilliseconds = function(value) {
  2764. this.milliseconds = value;
  2765. };
  2766. ParseDate.prototype.setSeconds = function(value) {
  2767. this.seconds = value;
  2768. };
  2769. ParseDate.prototype.setMinutes = function(value) {
  2770. this.minutes = value;
  2771. };
  2772. ParseDate.prototype.setHours = function(value) {
  2773. this.hours = value;
  2774. };
  2775. ParseDate.prototype.getHours = function() {
  2776. return this.hours;
  2777. };
  2778. ParseDate.prototype.setDate = function(value) {
  2779. this.day = value;
  2780. };
  2781. ParseDate.prototype.setMonth = function(value) {
  2782. this.month = value;
  2783. };
  2784. ParseDate.prototype.setFullYear = function(value) {
  2785. this.year = value;
  2786. };
  2787. ParseDate.prototype.fromDate = function(value) {
  2788. this.year = value.getFullYear();
  2789. this.month = value.getMonth();
  2790. this.day = value.getDate();
  2791. this.hours = value.getHours();
  2792. this.minutes = value.getMinutes();
  2793. this.seconds = value.getSeconds();
  2794. this.milliseconds = value.getMilliseconds();
  2795. return this;
  2796. };
  2797. ParseDate.prototype.toDate = function() {
  2798. return new Date(this.year, this.month, this.day, this.hours, this.minutes, this.seconds, this.milliseconds);
  2799. };
  2800. var proto = ParseDate.prototype;
  2801. function noop() {}
  2802. function isNumeric(n) {
  2803. return !isNaN(parseFloat(n)) && isFinite(n);
  2804. }
  2805. function indexOfCaseInsensitive(array, value) {
  2806. var len = array.length;
  2807. var str = value.toString().toLowerCase();
  2808. for (var i = 0; i < len; i++) {
  2809. if (array[i].toLowerCase() === str) {
  2810. return i;
  2811. }
  2812. }
  2813. return -1;
  2814. }
  2815. var defaults = this.defaults = {
  2816. format: 'shortDate',
  2817. strict: false
  2818. };
  2819. this.$get = [ '$locale', 'dateFilter', function($locale, dateFilter) {
  2820. var DateParserFactory = function(config) {
  2821. var options = angular.extend({}, defaults, config);
  2822. var $dateParser = {};
  2823. var regExpMap = {
  2824. sss: '[0-9]{3}',
  2825. ss: '[0-5][0-9]',
  2826. s: options.strict ? '[1-5]?[0-9]' : '[0-9]|[0-5][0-9]',
  2827. mm: '[0-5][0-9]',
  2828. m: options.strict ? '[1-5]?[0-9]' : '[0-9]|[0-5][0-9]',
  2829. HH: '[01][0-9]|2[0-3]',
  2830. H: options.strict ? '1?[0-9]|2[0-3]' : '[01]?[0-9]|2[0-3]',
  2831. hh: '[0][1-9]|[1][012]',
  2832. h: options.strict ? '[1-9]|1[012]' : '0?[1-9]|1[012]',
  2833. a: 'AM|PM',
  2834. EEEE: $locale.DATETIME_FORMATS.DAY.join('|'),
  2835. EEE: $locale.DATETIME_FORMATS.SHORTDAY.join('|'),
  2836. dd: '0[1-9]|[12][0-9]|3[01]',
  2837. d: options.strict ? '[1-9]|[1-2][0-9]|3[01]' : '0?[1-9]|[1-2][0-9]|3[01]',
  2838. MMMM: $locale.DATETIME_FORMATS.MONTH.join('|'),
  2839. MMM: $locale.DATETIME_FORMATS.SHORTMONTH.join('|'),
  2840. MM: '0[1-9]|1[012]',
  2841. M: options.strict ? '[1-9]|1[012]' : '0?[1-9]|1[012]',
  2842. yyyy: '[1]{1}[0-9]{3}|[2]{1}[0-9]{3}',
  2843. yy: '[0-9]{2}',
  2844. y: options.strict ? '-?(0|[1-9][0-9]{0,3})' : '-?0*[0-9]{1,4}'
  2845. };
  2846. var setFnMap = {
  2847. sss: proto.setMilliseconds,
  2848. ss: proto.setSeconds,
  2849. s: proto.setSeconds,
  2850. mm: proto.setMinutes,
  2851. m: proto.setMinutes,
  2852. HH: proto.setHours,
  2853. H: proto.setHours,
  2854. hh: proto.setHours,
  2855. h: proto.setHours,
  2856. EEEE: noop,
  2857. EEE: noop,
  2858. dd: proto.setDate,
  2859. d: proto.setDate,
  2860. a: function(value) {
  2861. var hours = this.getHours() % 12;
  2862. return this.setHours(value.match(/pm/i) ? hours + 12 : hours);
  2863. },
  2864. MMMM: function(value) {
  2865. return this.setMonth(indexOfCaseInsensitive($locale.DATETIME_FORMATS.MONTH, value));
  2866. },
  2867. MMM: function(value) {
  2868. return this.setMonth(indexOfCaseInsensitive($locale.DATETIME_FORMATS.SHORTMONTH, value));
  2869. },
  2870. MM: function(value) {
  2871. return this.setMonth(1 * value - 1);
  2872. },
  2873. M: function(value) {
  2874. return this.setMonth(1 * value - 1);
  2875. },
  2876. yyyy: proto.setFullYear,
  2877. yy: function(value) {
  2878. return this.setFullYear(2e3 + 1 * value);
  2879. },
  2880. y: function(value) {
  2881. return 1 * value <= 50 && value.length === 2 ? this.setFullYear(2e3 + 1 * value) : this.setFullYear(1 * value);
  2882. }
  2883. };
  2884. var regex;
  2885. var setMap;
  2886. $dateParser.init = function() {
  2887. $dateParser.$format = $locale.DATETIME_FORMATS[options.format] || options.format;
  2888. regex = regExpForFormat($dateParser.$format);
  2889. setMap = setMapForFormat($dateParser.$format);
  2890. };
  2891. $dateParser.isValid = function(date) {
  2892. if (angular.isDate(date)) return !isNaN(date.getTime());
  2893. return regex.test(date);
  2894. };
  2895. $dateParser.parse = function(value, baseDate, format, timezone) {
  2896. if (format) format = $locale.DATETIME_FORMATS[format] || format;
  2897. if (angular.isDate(value)) value = dateFilter(value, format || $dateParser.$format, timezone);
  2898. var formatRegex = format ? regExpForFormat(format) : regex;
  2899. var formatSetMap = format ? setMapForFormat(format) : setMap;
  2900. var matches = formatRegex.exec(value);
  2901. if (!matches) return false;
  2902. var date = baseDate && !isNaN(baseDate.getTime()) ? new ParseDate().fromDate(baseDate) : new ParseDate().fromDate(new Date(1970, 0, 1, 0));
  2903. for (var i = 0; i < matches.length - 1; i++) {
  2904. if (formatSetMap[i]) formatSetMap[i].call(date, matches[i + 1]);
  2905. }
  2906. var newDate = date.toDate();
  2907. if (parseInt(date.day, 10) !== newDate.getDate()) {
  2908. return false;
  2909. }
  2910. return newDate;
  2911. };
  2912. $dateParser.getDateForAttribute = function(key, value) {
  2913. var date;
  2914. if (value === 'today') {
  2915. var today = new Date();
  2916. date = new Date(today.getFullYear(), today.getMonth(), today.getDate() + (key === 'maxDate' ? 1 : 0), 0, 0, 0, key === 'minDate' ? 0 : -1);
  2917. } else if (angular.isString(value) && value.match(/^".+"$/)) {
  2918. date = new Date(value.substr(1, value.length - 2));
  2919. } else if (isNumeric(value)) {
  2920. date = new Date(parseInt(value, 10));
  2921. } else if (angular.isString(value) && value.length === 0) {
  2922. date = key === 'minDate' ? -Infinity : +Infinity;
  2923. } else {
  2924. date = new Date(value);
  2925. }
  2926. return date;
  2927. };
  2928. $dateParser.getTimeForAttribute = function(key, value) {
  2929. var time;
  2930. if (value === 'now') {
  2931. time = new Date().setFullYear(1970, 0, 1);
  2932. } else if (angular.isString(value) && value.match(/^".+"$/)) {
  2933. time = new Date(value.substr(1, value.length - 2)).setFullYear(1970, 0, 1);
  2934. } else if (isNumeric(value)) {
  2935. time = new Date(parseInt(value, 10)).setFullYear(1970, 0, 1);
  2936. } else if (angular.isString(value) && value.length === 0) {
  2937. time = key === 'minTime' ? -Infinity : +Infinity;
  2938. } else {
  2939. time = $dateParser.parse(value, new Date(1970, 0, 1, 0));
  2940. }
  2941. return time;
  2942. };
  2943. $dateParser.daylightSavingAdjust = function(date) {
  2944. if (!date) {
  2945. return null;
  2946. }
  2947. date.setHours(date.getHours() > 12 ? date.getHours() + 2 : 0);
  2948. return date;
  2949. };
  2950. $dateParser.timezoneOffsetAdjust = function(date, timezone, undo) {
  2951. if (!date) {
  2952. return null;
  2953. }
  2954. if (timezone && timezone === 'UTC') {
  2955. date = new Date(date.getTime());
  2956. date.setMinutes(date.getMinutes() + (undo ? -1 : 1) * date.getTimezoneOffset());
  2957. }
  2958. return date;
  2959. };
  2960. function regExpForFormat(format) {
  2961. var re = buildDateAbstractRegex(format);
  2962. return buildDateParseRegex(re);
  2963. }
  2964. function buildDateAbstractRegex(format) {
  2965. var escapedFormat = escapeReservedSymbols(format);
  2966. var escapedLiteralFormat = escapedFormat.replace(/''/g, '\\\'');
  2967. var literalRegex = /('(?:\\'|.)*?')/;
  2968. var formatParts = escapedLiteralFormat.split(literalRegex);
  2969. var dateElements = Object.keys(regExpMap);
  2970. var dateRegexParts = [];
  2971. angular.forEach(formatParts, function(part) {
  2972. if (isFormatStringLiteral(part)) {
  2973. part = trimLiteralEscapeChars(part);
  2974. } else {
  2975. for (var i = 0; i < dateElements.length; i++) {
  2976. part = part.split(dateElements[i]).join('${' + i + '}');
  2977. }
  2978. }
  2979. dateRegexParts.push(part);
  2980. });
  2981. return dateRegexParts.join('');
  2982. }
  2983. function escapeReservedSymbols(text) {
  2984. return text.replace(/\\/g, '[\\\\]').replace(/-/g, '[-]').replace(/\./g, '[.]').replace(/\*/g, '[*]').replace(/\+/g, '[+]').replace(/\?/g, '[?]').replace(/\$/g, '[$]').replace(/\^/g, '[^]').replace(/\//g, '[/]').replace(/\\s/g, '[\\s]');
  2985. }
  2986. function isFormatStringLiteral(text) {
  2987. return /^'.*'$/.test(text);
  2988. }
  2989. function trimLiteralEscapeChars(text) {
  2990. return text.replace(/^'(.*)'$/, '$1');
  2991. }
  2992. function buildDateParseRegex(abstractRegex) {
  2993. var dateElements = Object.keys(regExpMap);
  2994. var re = abstractRegex;
  2995. for (var i = 0; i < dateElements.length; i++) {
  2996. re = re.split('${' + i + '}').join('(' + regExpMap[dateElements[i]] + ')');
  2997. }
  2998. return new RegExp('^' + re + '$', [ 'i' ]);
  2999. }
  3000. function setMapForFormat(format) {
  3001. var re = buildDateAbstractRegex(format);
  3002. return buildDateParseValuesMap(re);
  3003. }
  3004. function buildDateParseValuesMap(abstractRegex) {
  3005. var dateElements = Object.keys(regExpMap);
  3006. var valuesRegex = new RegExp('\\${(\\d+)}', 'g');
  3007. var valuesMatch;
  3008. var keyIndex;
  3009. var valueKey;
  3010. var valueFunction;
  3011. var valuesFunctionMap = [];
  3012. while ((valuesMatch = valuesRegex.exec(abstractRegex)) !== null) {
  3013. keyIndex = valuesMatch[1];
  3014. valueKey = dateElements[keyIndex];
  3015. valueFunction = setFnMap[valueKey];
  3016. valuesFunctionMap.push(valueFunction);
  3017. }
  3018. return valuesFunctionMap;
  3019. }
  3020. $dateParser.init();
  3021. return $dateParser;
  3022. };
  3023. return DateParserFactory;
  3024. } ];
  3025. } ]);
  3026. angular.module('mgcrea.ngStrap.helpers.dateFormatter', []).service('$dateFormatter', [ '$locale', 'dateFilter', function($locale, dateFilter) {
  3027. this.getDefaultLocale = function() {
  3028. return $locale.id;
  3029. };
  3030. this.getDatetimeFormat = function(format, lang) {
  3031. return $locale.DATETIME_FORMATS[format] || format;
  3032. };
  3033. this.weekdaysShort = function(lang) {
  3034. return $locale.DATETIME_FORMATS.SHORTDAY;
  3035. };
  3036. function splitTimeFormat(format) {
  3037. return /(h+)([:\.])?(m+)([:\.])?(s*)[ ]?(a?)/i.exec(format).slice(1);
  3038. }
  3039. this.hoursFormat = function(timeFormat) {
  3040. return splitTimeFormat(timeFormat)[0];
  3041. };
  3042. this.minutesFormat = function(timeFormat) {
  3043. return splitTimeFormat(timeFormat)[2];
  3044. };
  3045. this.secondsFormat = function(timeFormat) {
  3046. return splitTimeFormat(timeFormat)[4];
  3047. };
  3048. this.timeSeparator = function(timeFormat) {
  3049. return splitTimeFormat(timeFormat)[1];
  3050. };
  3051. this.showSeconds = function(timeFormat) {
  3052. return !!splitTimeFormat(timeFormat)[4];
  3053. };
  3054. this.showAM = function(timeFormat) {
  3055. return !!splitTimeFormat(timeFormat)[5];
  3056. };
  3057. this.formatDate = function(date, format, lang, timezone) {
  3058. return dateFilter(date, format, timezone);
  3059. };
  3060. } ]);
  3061. angular.module('mgcrea.ngStrap.core', []).service('$bsCompiler', bsCompilerService);
  3062. function bsCompilerService($q, $http, $injector, $compile, $controller, $templateCache) {
  3063. this.compile = function(options) {
  3064. if (options.template && /\.html$/.test(options.template)) {
  3065. console.warn('Deprecated use of `template` option to pass a file. Please use the `templateUrl` option instead.');
  3066. options.templateUrl = options.template;
  3067. options.template = '';
  3068. }
  3069. var templateUrl = options.templateUrl;
  3070. var template = options.template || '';
  3071. var controller = options.controller;
  3072. var controllerAs = options.controllerAs;
  3073. var resolve = options.resolve || {};
  3074. var locals = options.locals || {};
  3075. var transformTemplate = options.transformTemplate || angular.identity;
  3076. var bindToController = options.bindToController;
  3077. angular.forEach(resolve, function(value, key) {
  3078. if (angular.isString(value)) {
  3079. resolve[key] = $injector.get(value);
  3080. } else {
  3081. resolve[key] = $injector.invoke(value);
  3082. }
  3083. });
  3084. angular.extend(resolve, locals);
  3085. if (template) {
  3086. resolve.$template = $q.when(template);
  3087. } else if (templateUrl) {
  3088. resolve.$template = fetchTemplate(templateUrl);
  3089. } else {
  3090. throw new Error('Missing `template` / `templateUrl` option.');
  3091. }
  3092. if (options.titleTemplate) {
  3093. resolve.$template = $q.all([ resolve.$template, fetchTemplate(options.titleTemplate) ]).then(function(templates) {
  3094. var templateEl = angular.element(templates[0]);
  3095. findElement('[ng-bind="title"]', templateEl[0]).removeAttr('ng-bind').html(templates[1]);
  3096. return templateEl[0].outerHTML;
  3097. });
  3098. }
  3099. if (options.contentTemplate) {
  3100. resolve.$template = $q.all([ resolve.$template, fetchTemplate(options.contentTemplate) ]).then(function(templates) {
  3101. var templateEl = angular.element(templates[0]);
  3102. var contentEl = findElement('[ng-bind="content"]', templateEl[0]).removeAttr('ng-bind').html(templates[1]);
  3103. if (!options.templateUrl) contentEl.next().remove();
  3104. return templateEl[0].outerHTML;
  3105. });
  3106. }
  3107. return $q.all(resolve).then(function(locals) {
  3108. var template = transformTemplate(locals.$template);
  3109. if (options.html) {
  3110. template = template.replace(/ng-bind="/gi, 'ng-bind-html="');
  3111. }
  3112. var element = angular.element('<div>').html(template.trim()).contents();
  3113. var linkFn = $compile(element);
  3114. return {
  3115. locals: locals,
  3116. element: element,
  3117. link: function link(scope) {
  3118. locals.$scope = scope;
  3119. if (controller) {
  3120. var invokeCtrl = $controller(controller, locals, true);
  3121. if (bindToController) {
  3122. angular.extend(invokeCtrl.instance, locals);
  3123. }
  3124. var ctrl = angular.isObject(invokeCtrl) ? invokeCtrl : invokeCtrl();
  3125. element.data('$ngControllerController', ctrl);
  3126. element.children().data('$ngControllerController', ctrl);
  3127. if (controllerAs) {
  3128. scope[controllerAs] = ctrl;
  3129. }
  3130. }
  3131. return linkFn.apply(null, arguments);
  3132. }
  3133. };
  3134. });
  3135. };
  3136. function findElement(query, element) {
  3137. return angular.element((element || document).querySelectorAll(query));
  3138. }
  3139. var fetchPromises = {};
  3140. function fetchTemplate(template) {
  3141. if (fetchPromises[template]) return fetchPromises[template];
  3142. return fetchPromises[template] = $http.get(template, {
  3143. cache: $templateCache
  3144. }).then(function(res) {
  3145. return res.data;
  3146. });
  3147. }
  3148. }
  3149. angular.module('mgcrea.ngStrap.dropdown', [ 'mgcrea.ngStrap.tooltip' ]).provider('$dropdown', function() {
  3150. var defaults = this.defaults = {
  3151. animation: 'am-fade',
  3152. prefixClass: 'dropdown',
  3153. prefixEvent: 'dropdown',
  3154. placement: 'bottom-left',
  3155. templateUrl: 'dropdown/dropdown.tpl.html',
  3156. trigger: 'click',
  3157. container: false,
  3158. keyboard: true,
  3159. html: false,
  3160. delay: 0
  3161. };
  3162. this.$get = [ '$window', '$rootScope', '$tooltip', '$timeout', function($window, $rootScope, $tooltip, $timeout) {
  3163. var bodyEl = angular.element($window.document.body);
  3164. var matchesSelector = Element.prototype.matchesSelector || Element.prototype.webkitMatchesSelector || Element.prototype.mozMatchesSelector || Element.prototype.msMatchesSelector || Element.prototype.oMatchesSelector;
  3165. function DropdownFactory(element, config) {
  3166. var $dropdown = {};
  3167. var options = angular.extend({}, defaults, config);
  3168. $dropdown.$scope = options.scope && options.scope.$new() || $rootScope.$new();
  3169. $dropdown = $tooltip(element, options);
  3170. var parentEl = element.parent();
  3171. $dropdown.$onKeyDown = function(evt) {
  3172. if (!/(38|40)/.test(evt.keyCode)) return;
  3173. evt.preventDefault();
  3174. evt.stopPropagation();
  3175. var items = angular.element($dropdown.$element[0].querySelectorAll('li:not(.divider) a'));
  3176. if (!items.length) return;
  3177. var index;
  3178. angular.forEach(items, function(el, i) {
  3179. if (matchesSelector && matchesSelector.call(el, ':focus')) index = i;
  3180. });
  3181. if (evt.keyCode === 38 && index > 0) index--; else if (evt.keyCode === 40 && index < items.length - 1) index++; else if (angular.isUndefined(index)) index = 0;
  3182. items.eq(index)[0].focus();
  3183. };
  3184. var show = $dropdown.show;
  3185. $dropdown.show = function() {
  3186. show();
  3187. $timeout(function() {
  3188. if (options.keyboard && $dropdown.$element) $dropdown.$element.on('keydown', $dropdown.$onKeyDown);
  3189. bodyEl.on('click', onBodyClick);
  3190. }, 0, false);
  3191. if (parentEl.hasClass('dropdown')) parentEl.addClass('open');
  3192. };
  3193. var hide = $dropdown.hide;
  3194. $dropdown.hide = function() {
  3195. if (!$dropdown.$isShown) return;
  3196. if (options.keyboard && $dropdown.$element) $dropdown.$element.off('keydown', $dropdown.$onKeyDown);
  3197. bodyEl.off('click', onBodyClick);
  3198. if (parentEl.hasClass('dropdown')) parentEl.removeClass('open');
  3199. hide();
  3200. };
  3201. var destroy = $dropdown.destroy;
  3202. $dropdown.destroy = function() {
  3203. bodyEl.off('click', onBodyClick);
  3204. destroy();
  3205. };
  3206. function onBodyClick(evt) {
  3207. if (evt.target === element[0]) return;
  3208. return evt.target !== element[0] && $dropdown.hide();
  3209. }
  3210. return $dropdown;
  3211. }
  3212. return DropdownFactory;
  3213. } ];
  3214. }).directive('bsDropdown', [ '$window', '$sce', '$dropdown', function($window, $sce, $dropdown) {
  3215. return {
  3216. restrict: 'EAC',
  3217. scope: true,
  3218. compile: function(tElement, tAttrs) {
  3219. if (!tAttrs.bsDropdown) {
  3220. var nextSibling = tElement[0].nextSibling;
  3221. while (nextSibling && nextSibling.nodeType !== 1) {
  3222. nextSibling = nextSibling.nextSibling;
  3223. }
  3224. if (nextSibling && nextSibling.className.split(' ').indexOf('dropdown-menu') >= 0) {
  3225. tAttrs.template = nextSibling.outerHTML;
  3226. tAttrs.templateUrl = undefined;
  3227. nextSibling.parentNode.removeChild(nextSibling);
  3228. }
  3229. }
  3230. return function postLink(scope, element, attr) {
  3231. var options = {
  3232. scope: scope
  3233. };
  3234. angular.forEach([ 'template', 'templateUrl', 'controller', 'controllerAs', 'placement', 'container', 'delay', 'trigger', 'keyboard', 'html', 'animation', 'id', 'autoClose' ], function(key) {
  3235. if (angular.isDefined(tAttrs[key])) options[key] = tAttrs[key];
  3236. });
  3237. var falseValueRegExp = /^(false|0|)$/i;
  3238. angular.forEach([ 'html', 'container' ], function(key) {
  3239. if (angular.isDefined(attr[key]) && falseValueRegExp.test(attr[key])) options[key] = false;
  3240. });
  3241. angular.forEach([ 'onBeforeShow', 'onShow', 'onBeforeHide', 'onHide' ], function(key) {
  3242. var bsKey = 'bs' + key.charAt(0).toUpperCase() + key.slice(1);
  3243. if (angular.isDefined(attr[bsKey])) {
  3244. options[key] = scope.$eval(attr[bsKey]);
  3245. }
  3246. });
  3247. if (attr.bsDropdown) {
  3248. scope.$watch(attr.bsDropdown, function(newValue, oldValue) {
  3249. scope.content = newValue;
  3250. }, true);
  3251. }
  3252. var dropdown = $dropdown(element, options);
  3253. if (attr.bsShow) {
  3254. scope.$watch(attr.bsShow, function(newValue, oldValue) {
  3255. if (!dropdown || !angular.isDefined(newValue)) return;
  3256. if (angular.isString(newValue)) newValue = !!newValue.match(/true|,?(dropdown),?/i);
  3257. if (newValue === true) {
  3258. dropdown.show();
  3259. } else {
  3260. dropdown.hide();
  3261. }
  3262. });
  3263. }
  3264. scope.$on('$destroy', function() {
  3265. if (dropdown) dropdown.destroy();
  3266. options = null;
  3267. dropdown = null;
  3268. });
  3269. };
  3270. }
  3271. };
  3272. } ]);
  3273. angular.module('mgcrea.ngStrap.button', []).provider('$button', function() {
  3274. var defaults = this.defaults = {
  3275. activeClass: 'active',
  3276. toggleEvent: 'click'
  3277. };
  3278. this.$get = function() {
  3279. return {
  3280. defaults: defaults
  3281. };
  3282. };
  3283. }).directive('bsCheckboxGroup', function() {
  3284. return {
  3285. restrict: 'A',
  3286. require: 'ngModel',
  3287. compile: function postLink(element, attr) {
  3288. element.attr('data-toggle', 'buttons');
  3289. element.removeAttr('ng-model');
  3290. var children = element[0].querySelectorAll('input[type="checkbox"]');
  3291. angular.forEach(children, function(child) {
  3292. var childEl = angular.element(child);
  3293. childEl.attr('bs-checkbox', '');
  3294. childEl.attr('ng-model', attr.ngModel + '.' + childEl.attr('value'));
  3295. });
  3296. }
  3297. };
  3298. }).directive('bsCheckbox', [ '$button', '$$rAF', function($button, $$rAF) {
  3299. var defaults = $button.defaults;
  3300. var constantValueRegExp = /^(true|false|\d+)$/;
  3301. return {
  3302. restrict: 'A',
  3303. require: 'ngModel',
  3304. link: function postLink(scope, element, attr, controller) {
  3305. var options = defaults;
  3306. var isInput = element[0].nodeName === 'INPUT';
  3307. var activeElement = isInput ? element.parent() : element;
  3308. var trueValue = angular.isDefined(attr.trueValue) ? attr.trueValue : true;
  3309. if (constantValueRegExp.test(attr.trueValue)) {
  3310. trueValue = scope.$eval(attr.trueValue);
  3311. }
  3312. var falseValue = angular.isDefined(attr.falseValue) ? attr.falseValue : false;
  3313. if (constantValueRegExp.test(attr.falseValue)) {
  3314. falseValue = scope.$eval(attr.falseValue);
  3315. }
  3316. var hasExoticValues = typeof trueValue !== 'boolean' || typeof falseValue !== 'boolean';
  3317. if (hasExoticValues) {
  3318. controller.$parsers.push(function(viewValue) {
  3319. return viewValue ? trueValue : falseValue;
  3320. });
  3321. controller.$formatters.push(function(modelValue) {
  3322. return angular.equals(modelValue, trueValue);
  3323. });
  3324. }
  3325. controller.$render = function() {
  3326. var isActive = !!controller.$viewValue;
  3327. $$rAF(function() {
  3328. if (isInput) element[0].checked = isActive;
  3329. activeElement.toggleClass(options.activeClass, isActive);
  3330. });
  3331. };
  3332. element.bind(options.toggleEvent, function() {
  3333. scope.$apply(function() {
  3334. if (!isInput) {
  3335. controller.$setViewValue(!activeElement.hasClass('active'));
  3336. }
  3337. controller.$render();
  3338. });
  3339. });
  3340. }
  3341. };
  3342. } ]).directive('bsRadioGroup', function() {
  3343. return {
  3344. restrict: 'A',
  3345. require: 'ngModel',
  3346. compile: function postLink(element, attr) {
  3347. element.attr('data-toggle', 'buttons');
  3348. element.removeAttr('ng-model');
  3349. var children = element[0].querySelectorAll('input[type="radio"]');
  3350. angular.forEach(children, function(child) {
  3351. angular.element(child).attr('bs-radio', '');
  3352. angular.element(child).attr('ng-model', attr.ngModel);
  3353. });
  3354. }
  3355. };
  3356. }).directive('bsRadio', [ '$button', '$$rAF', function($button, $$rAF) {
  3357. var defaults = $button.defaults;
  3358. var constantValueRegExp = /^(true|false|\d+)$/;
  3359. return {
  3360. restrict: 'A',
  3361. require: 'ngModel',
  3362. link: function postLink(scope, element, attr, controller) {
  3363. var options = defaults;
  3364. var isInput = element[0].nodeName === 'INPUT';
  3365. var activeElement = isInput ? element.parent() : element;
  3366. var value;
  3367. attr.$observe('value', function(v) {
  3368. if (typeof v !== 'boolean' && constantValueRegExp.test(v)) {
  3369. value = scope.$eval(v);
  3370. } else {
  3371. value = v;
  3372. }
  3373. controller.$render();
  3374. });
  3375. controller.$render = function() {
  3376. var isActive = angular.equals(controller.$viewValue, value);
  3377. $$rAF(function() {
  3378. if (isInput) element[0].checked = isActive;
  3379. activeElement.toggleClass(options.activeClass, isActive);
  3380. });
  3381. };
  3382. element.bind(options.toggleEvent, function() {
  3383. scope.$apply(function() {
  3384. controller.$setViewValue(value);
  3385. controller.$render();
  3386. });
  3387. });
  3388. }
  3389. };
  3390. } ]);
  3391. angular.module('mgcrea.ngStrap.datepicker', [ 'mgcrea.ngStrap.helpers.dateParser', 'mgcrea.ngStrap.helpers.dateFormatter', 'mgcrea.ngStrap.tooltip' ]).provider('$datepicker', function() {
  3392. var defaults = this.defaults = {
  3393. animation: 'am-fade',
  3394. prefixClass: 'datepicker',
  3395. placement: 'bottom-left',
  3396. templateUrl: 'datepicker/datepicker.tpl.html',
  3397. trigger: 'focus',
  3398. container: false,
  3399. keyboard: true,
  3400. html: false,
  3401. delay: 0,
  3402. useNative: false,
  3403. dateType: 'date',
  3404. dateFormat: 'shortDate',
  3405. timezone: null,
  3406. modelDateFormat: null,
  3407. dayFormat: 'dd',
  3408. monthFormat: 'MMM',
  3409. yearFormat: 'yyyy',
  3410. monthTitleFormat: 'MMMM yyyy',
  3411. yearTitleFormat: 'yyyy',
  3412. strictFormat: false,
  3413. autoclose: false,
  3414. minDate: -Infinity,
  3415. maxDate: +Infinity,
  3416. startView: 0,
  3417. minView: 0,
  3418. startWeek: 0,
  3419. daysOfWeekDisabled: '',
  3420. hasToday: false,
  3421. hasClear: false,
  3422. iconLeft: 'glyphicon glyphicon-chevron-left',
  3423. iconRight: 'glyphicon glyphicon-chevron-right'
  3424. };
  3425. this.$get = [ '$window', '$document', '$rootScope', '$sce', '$dateFormatter', 'datepickerViews', '$tooltip', '$timeout', function($window, $document, $rootScope, $sce, $dateFormatter, datepickerViews, $tooltip, $timeout) {
  3426. var isNative = /(ip[ao]d|iphone|android)/gi.test($window.navigator.userAgent);
  3427. var isTouch = 'createTouch' in $window.document && isNative;
  3428. if (!defaults.lang) defaults.lang = $dateFormatter.getDefaultLocale();
  3429. function DatepickerFactory(element, controller, config) {
  3430. var $datepicker = $tooltip(element, angular.extend({}, defaults, config));
  3431. var parentScope = config.scope;
  3432. var options = $datepicker.$options;
  3433. var scope = $datepicker.$scope;
  3434. if (options.startView) options.startView -= options.minView;
  3435. var pickerViews = datepickerViews($datepicker);
  3436. $datepicker.$views = pickerViews.views;
  3437. var viewDate = pickerViews.viewDate;
  3438. scope.$mode = options.startView;
  3439. scope.$iconLeft = options.iconLeft;
  3440. scope.$iconRight = options.iconRight;
  3441. scope.$hasToday = options.hasToday;
  3442. scope.$hasClear = options.hasClear;
  3443. var $picker = $datepicker.$views[scope.$mode];
  3444. scope.$select = function(date, disabled) {
  3445. if (disabled) return;
  3446. $datepicker.select(date);
  3447. };
  3448. scope.$selectPane = function(value) {
  3449. $datepicker.$selectPane(value);
  3450. };
  3451. scope.$toggleMode = function() {
  3452. $datepicker.setMode((scope.$mode + 1) % $datepicker.$views.length);
  3453. };
  3454. scope.$setToday = function() {
  3455. if (options.autoclose) {
  3456. $datepicker.setMode(0);
  3457. $datepicker.select(new Date());
  3458. } else {
  3459. $datepicker.select(new Date(), true);
  3460. }
  3461. };
  3462. scope.$clear = function() {
  3463. if (options.autoclose) {
  3464. $datepicker.setMode(0);
  3465. $datepicker.select(null);
  3466. } else {
  3467. $datepicker.select(null, true);
  3468. }
  3469. };
  3470. $datepicker.update = function(date) {
  3471. if (angular.isDate(date) && !isNaN(date.getTime())) {
  3472. $datepicker.$date = date;
  3473. $picker.update.call($picker, date);
  3474. }
  3475. $datepicker.$build(true);
  3476. };
  3477. $datepicker.updateDisabledDates = function(dateRanges) {
  3478. options.disabledDateRanges = dateRanges;
  3479. for (var i = 0, l = scope.rows.length; i < l; i++) {
  3480. angular.forEach(scope.rows[i], $datepicker.$setDisabledEl);
  3481. }
  3482. };
  3483. $datepicker.select = function(date, keep) {
  3484. if (angular.isDate(date)) {
  3485. if (!angular.isDate(controller.$dateValue) || isNaN(controller.$dateValue.getTime())) {
  3486. controller.$dateValue = new Date(date);
  3487. }
  3488. } else {
  3489. controller.$dateValue = null;
  3490. }
  3491. if (!scope.$mode || keep) {
  3492. controller.$setViewValue(angular.copy(date));
  3493. controller.$render();
  3494. if (options.autoclose && !keep) {
  3495. $timeout(function() {
  3496. $datepicker.hide(true);
  3497. });
  3498. }
  3499. } else {
  3500. angular.extend(viewDate, {
  3501. year: date.getFullYear(),
  3502. month: date.getMonth(),
  3503. date: date.getDate()
  3504. });
  3505. $datepicker.setMode(scope.$mode - 1);
  3506. $datepicker.$build();
  3507. }
  3508. };
  3509. $datepicker.setMode = function(mode) {
  3510. scope.$mode = mode;
  3511. $picker = $datepicker.$views[scope.$mode];
  3512. $datepicker.$build();
  3513. };
  3514. $datepicker.$build = function(pristine) {
  3515. if (pristine === true && $picker.built) return;
  3516. if (pristine === false && !$picker.built) return;
  3517. $picker.build.call($picker);
  3518. };
  3519. $datepicker.$updateSelected = function() {
  3520. for (var i = 0, l = scope.rows.length; i < l; i++) {
  3521. angular.forEach(scope.rows[i], updateSelected);
  3522. }
  3523. };
  3524. $datepicker.$isSelected = function(date) {
  3525. return $picker.isSelected(date);
  3526. };
  3527. $datepicker.$setDisabledEl = function(el) {
  3528. el.disabled = $picker.isDisabled(el.date);
  3529. };
  3530. $datepicker.$selectPane = function(value) {
  3531. var steps = $picker.steps;
  3532. var targetDate = new Date(Date.UTC(viewDate.year + (steps.year || 0) * value, viewDate.month + (steps.month || 0) * value, 1));
  3533. angular.extend(viewDate, {
  3534. year: targetDate.getUTCFullYear(),
  3535. month: targetDate.getUTCMonth(),
  3536. date: targetDate.getUTCDate()
  3537. });
  3538. $datepicker.$build();
  3539. };
  3540. $datepicker.$onMouseDown = function(evt) {
  3541. evt.preventDefault();
  3542. evt.stopPropagation();
  3543. if (isTouch) {
  3544. var targetEl = angular.element(evt.target);
  3545. if (targetEl[0].nodeName.toLowerCase() !== 'button') {
  3546. targetEl = targetEl.parent();
  3547. }
  3548. targetEl.triggerHandler('click');
  3549. }
  3550. };
  3551. $datepicker.$onKeyDown = function(evt) {
  3552. if (!/(38|37|39|40|13)/.test(evt.keyCode) || evt.shiftKey || evt.altKey) return;
  3553. evt.preventDefault();
  3554. evt.stopPropagation();
  3555. if (evt.keyCode === 13) {
  3556. if (!scope.$mode) {
  3557. $datepicker.hide(true);
  3558. } else {
  3559. scope.$apply(function() {
  3560. $datepicker.setMode(scope.$mode - 1);
  3561. });
  3562. }
  3563. return;
  3564. }
  3565. $picker.onKeyDown(evt);
  3566. parentScope.$digest();
  3567. };
  3568. function updateSelected(el) {
  3569. el.selected = $datepicker.$isSelected(el.date);
  3570. }
  3571. function focusElement() {
  3572. element[0].focus();
  3573. }
  3574. var _init = $datepicker.init;
  3575. $datepicker.init = function() {
  3576. if (isNative && options.useNative) {
  3577. element.prop('type', 'date');
  3578. element.css('-webkit-appearance', 'textfield');
  3579. return;
  3580. } else if (isTouch) {
  3581. element.prop('type', 'text');
  3582. element.attr('readonly', 'true');
  3583. element.on('click', focusElement);
  3584. }
  3585. _init();
  3586. };
  3587. var _destroy = $datepicker.destroy;
  3588. $datepicker.destroy = function() {
  3589. if (isNative && options.useNative) {
  3590. element.off('click', focusElement);
  3591. }
  3592. _destroy();
  3593. };
  3594. var _show = $datepicker.show;
  3595. $datepicker.show = function() {
  3596. if (!isTouch && element.attr('readonly') || element.attr('disabled')) return;
  3597. _show();
  3598. $timeout(function() {
  3599. if (!$datepicker.$isShown) return;
  3600. $datepicker.$element.on(isTouch ? 'touchstart' : 'mousedown', $datepicker.$onMouseDown);
  3601. if (options.keyboard) {
  3602. element.on('keydown', $datepicker.$onKeyDown);
  3603. }
  3604. }, 0, false);
  3605. };
  3606. var _hide = $datepicker.hide;
  3607. $datepicker.hide = function(blur) {
  3608. if (!$datepicker.$isShown) return;
  3609. $datepicker.$element.off(isTouch ? 'touchstart' : 'mousedown', $datepicker.$onMouseDown);
  3610. if (options.keyboard) {
  3611. element.off('keydown', $datepicker.$onKeyDown);
  3612. }
  3613. _hide(blur);
  3614. };
  3615. return $datepicker;
  3616. }
  3617. DatepickerFactory.defaults = defaults;
  3618. return DatepickerFactory;
  3619. } ];
  3620. }).directive('bsDatepicker', [ '$window', '$parse', '$q', '$dateFormatter', '$dateParser', '$datepicker', function($window, $parse, $q, $dateFormatter, $dateParser, $datepicker) {
  3621. var isNative = /(ip[ao]d|iphone|android)/gi.test($window.navigator.userAgent);
  3622. return {
  3623. restrict: 'EAC',
  3624. require: 'ngModel',
  3625. link: function postLink(scope, element, attr, controller) {
  3626. var options = {
  3627. scope: scope
  3628. };
  3629. angular.forEach([ 'template', 'templateUrl', 'controller', 'controllerAs', 'placement', 'container', 'delay', 'trigger', 'html', 'animation', 'autoclose', 'dateType', 'dateFormat', 'timezone', 'modelDateFormat', 'dayFormat', 'strictFormat', 'startWeek', 'startDate', 'useNative', 'lang', 'startView', 'minView', 'iconLeft', 'iconRight', 'daysOfWeekDisabled', 'id', 'prefixClass', 'prefixEvent', 'hasToday', 'hasClear' ], function(key) {
  3630. if (angular.isDefined(attr[key])) options[key] = attr[key];
  3631. });
  3632. var falseValueRegExp = /^(false|0|)$/i;
  3633. angular.forEach([ 'html', 'container', 'autoclose', 'useNative', 'hasToday', 'hasClear' ], function(key) {
  3634. if (angular.isDefined(attr[key]) && falseValueRegExp.test(attr[key])) {
  3635. options[key] = false;
  3636. }
  3637. });
  3638. angular.forEach([ 'onBeforeShow', 'onShow', 'onBeforeHide', 'onHide' ], function(key) {
  3639. var bsKey = 'bs' + key.charAt(0).toUpperCase() + key.slice(1);
  3640. if (angular.isDefined(attr[bsKey])) {
  3641. options[key] = scope.$eval(attr[bsKey]);
  3642. }
  3643. });
  3644. var datepicker = $datepicker(element, controller, options);
  3645. options = datepicker.$options;
  3646. if (isNative && options.useNative) options.dateFormat = 'yyyy-MM-dd';
  3647. var lang = options.lang;
  3648. var formatDate = function(date, format) {
  3649. return $dateFormatter.formatDate(date, format, lang);
  3650. };
  3651. var dateParser = $dateParser({
  3652. format: options.dateFormat,
  3653. lang: lang,
  3654. strict: options.strictFormat
  3655. });
  3656. if (attr.bsShow) {
  3657. scope.$watch(attr.bsShow, function(newValue, oldValue) {
  3658. if (!datepicker || !angular.isDefined(newValue)) return;
  3659. if (angular.isString(newValue)) newValue = !!newValue.match(/true|,?(datepicker),?/i);
  3660. if (newValue === true) {
  3661. datepicker.show();
  3662. } else {
  3663. datepicker.hide();
  3664. }
  3665. });
  3666. }
  3667. angular.forEach([ 'minDate', 'maxDate' ], function(key) {
  3668. if (angular.isDefined(attr[key])) {
  3669. attr.$observe(key, function(newValue) {
  3670. datepicker.$options[key] = dateParser.getDateForAttribute(key, newValue);
  3671. if (!isNaN(datepicker.$options[key])) datepicker.$build(false);
  3672. validateAgainstMinMaxDate(controller.$dateValue);
  3673. });
  3674. }
  3675. });
  3676. if (angular.isDefined(attr.dateFormat)) {
  3677. attr.$observe('dateFormat', function(newValue) {
  3678. datepicker.$options.dateFormat = newValue;
  3679. });
  3680. }
  3681. scope.$watch(attr.ngModel, function(newValue, oldValue) {
  3682. datepicker.update(controller.$dateValue);
  3683. }, true);
  3684. function normalizeDateRanges(ranges) {
  3685. if (!ranges || !ranges.length) return null;
  3686. return ranges;
  3687. }
  3688. if (angular.isDefined(attr.disabledDates)) {
  3689. scope.$watch(attr.disabledDates, function(disabledRanges, previousValue) {
  3690. disabledRanges = normalizeDateRanges(disabledRanges);
  3691. previousValue = normalizeDateRanges(previousValue);
  3692. if (disabledRanges) {
  3693. datepicker.updateDisabledDates(disabledRanges);
  3694. }
  3695. });
  3696. }
  3697. function validateAgainstMinMaxDate(parsedDate) {
  3698. if (!angular.isDate(parsedDate)) return;
  3699. var isMinValid = isNaN(datepicker.$options.minDate) || parsedDate.getTime() >= datepicker.$options.minDate;
  3700. var isMaxValid = isNaN(datepicker.$options.maxDate) || parsedDate.getTime() <= datepicker.$options.maxDate;
  3701. var isValid = isMinValid && isMaxValid;
  3702. controller.$setValidity('date', isValid);
  3703. controller.$setValidity('min', isMinValid);
  3704. controller.$setValidity('max', isMaxValid);
  3705. if (isValid) controller.$dateValue = parsedDate;
  3706. }
  3707. controller.$parsers.unshift(function(viewValue) {
  3708. var date;
  3709. if (!viewValue) {
  3710. controller.$setValidity('date', true);
  3711. return null;
  3712. }
  3713. var parsedDate = dateParser.parse(viewValue, controller.$dateValue);
  3714. if (!parsedDate || isNaN(parsedDate.getTime())) {
  3715. controller.$setValidity('date', false);
  3716. return;
  3717. }
  3718. validateAgainstMinMaxDate(parsedDate);
  3719. if (options.dateType === 'string') {
  3720. date = dateParser.timezoneOffsetAdjust(parsedDate, options.timezone, true);
  3721. return formatDate(date, options.modelDateFormat || options.dateFormat);
  3722. }
  3723. date = dateParser.timezoneOffsetAdjust(controller.$dateValue, options.timezone, true);
  3724. if (options.dateType === 'number') {
  3725. return date.getTime();
  3726. } else if (options.dateType === 'unix') {
  3727. return date.getTime() / 1e3;
  3728. } else if (options.dateType === 'iso') {
  3729. return date.toISOString();
  3730. }
  3731. return new Date(date);
  3732. });
  3733. controller.$formatters.push(function(modelValue) {
  3734. var date;
  3735. if (angular.isUndefined(modelValue) || modelValue === null) {
  3736. date = NaN;
  3737. } else if (angular.isDate(modelValue)) {
  3738. date = modelValue;
  3739. } else if (options.dateType === 'string') {
  3740. date = dateParser.parse(modelValue, null, options.modelDateFormat);
  3741. } else if (options.dateType === 'unix') {
  3742. date = new Date(modelValue * 1e3);
  3743. } else {
  3744. date = new Date(modelValue);
  3745. }
  3746. controller.$dateValue = dateParser.timezoneOffsetAdjust(date, options.timezone);
  3747. return getDateFormattedString();
  3748. });
  3749. controller.$render = function() {
  3750. element.val(getDateFormattedString());
  3751. };
  3752. function getDateFormattedString() {
  3753. return !controller.$dateValue || isNaN(controller.$dateValue.getTime()) ? '' : formatDate(controller.$dateValue, options.dateFormat);
  3754. }
  3755. scope.$on('$destroy', function() {
  3756. if (datepicker) datepicker.destroy();
  3757. options = null;
  3758. datepicker = null;
  3759. });
  3760. }
  3761. };
  3762. } ]).provider('datepickerViews', function() {
  3763. function split(arr, size) {
  3764. var arrays = [];
  3765. while (arr.length > 0) {
  3766. arrays.push(arr.splice(0, size));
  3767. }
  3768. return arrays;
  3769. }
  3770. function mod(n, m) {
  3771. return (n % m + m) % m;
  3772. }
  3773. this.$get = [ '$dateFormatter', '$dateParser', '$sce', function($dateFormatter, $dateParser, $sce) {
  3774. return function(picker) {
  3775. var scope = picker.$scope;
  3776. var options = picker.$options;
  3777. var lang = options.lang;
  3778. var formatDate = function(date, format) {
  3779. return $dateFormatter.formatDate(date, format, lang);
  3780. };
  3781. var dateParser = $dateParser({
  3782. format: options.dateFormat,
  3783. lang: lang,
  3784. strict: options.strictFormat
  3785. });
  3786. var weekDaysMin = $dateFormatter.weekdaysShort(lang);
  3787. var weekDaysLabels = weekDaysMin.slice(options.startWeek).concat(weekDaysMin.slice(0, options.startWeek));
  3788. var weekDaysLabelsHtml = $sce.trustAsHtml('<th class="dow text-center">' + weekDaysLabels.join('</th><th class="dow text-center">') + '</th>');
  3789. var startDate = picker.$date || (options.startDate ? dateParser.getDateForAttribute('startDate', options.startDate) : new Date());
  3790. var viewDate = {
  3791. year: startDate.getFullYear(),
  3792. month: startDate.getMonth(),
  3793. date: startDate.getDate()
  3794. };
  3795. var views = [ {
  3796. format: options.dayFormat,
  3797. split: 7,
  3798. steps: {
  3799. month: 1
  3800. },
  3801. update: function(date, force) {
  3802. if (!this.built || force || date.getFullYear() !== viewDate.year || date.getMonth() !== viewDate.month) {
  3803. angular.extend(viewDate, {
  3804. year: picker.$date.getFullYear(),
  3805. month: picker.$date.getMonth(),
  3806. date: picker.$date.getDate()
  3807. });
  3808. picker.$build();
  3809. } else if (date.getDate() !== viewDate.date || date.getDate() === 1) {
  3810. viewDate.date = picker.$date.getDate();
  3811. picker.$updateSelected();
  3812. }
  3813. },
  3814. build: function() {
  3815. var firstDayOfMonth = new Date(viewDate.year, viewDate.month, 1);
  3816. var firstDayOfMonthOffset = firstDayOfMonth.getTimezoneOffset();
  3817. var firstDate = new Date(+firstDayOfMonth - mod(firstDayOfMonth.getDay() - options.startWeek, 7) * 864e5);
  3818. var firstDateOffset = firstDate.getTimezoneOffset();
  3819. var today = dateParser.timezoneOffsetAdjust(new Date(), options.timezone).toDateString();
  3820. if (firstDateOffset !== firstDayOfMonthOffset) firstDate = new Date(+firstDate + (firstDateOffset - firstDayOfMonthOffset) * 6e4);
  3821. var days = [];
  3822. var day;
  3823. for (var i = 0; i < 42; i++) {
  3824. day = dateParser.daylightSavingAdjust(new Date(firstDate.getFullYear(), firstDate.getMonth(), firstDate.getDate() + i));
  3825. days.push({
  3826. date: day,
  3827. isToday: day.toDateString() === today,
  3828. label: formatDate(day, this.format),
  3829. selected: picker.$date && this.isSelected(day),
  3830. muted: day.getMonth() !== viewDate.month,
  3831. disabled: this.isDisabled(day)
  3832. });
  3833. }
  3834. scope.title = formatDate(firstDayOfMonth, options.monthTitleFormat);
  3835. scope.showLabels = true;
  3836. scope.labels = weekDaysLabelsHtml;
  3837. scope.rows = split(days, this.split);
  3838. scope.isTodayDisabled = this.isDisabled(new Date());
  3839. this.built = true;
  3840. },
  3841. isSelected: function(date) {
  3842. return picker.$date && date.getFullYear() === picker.$date.getFullYear() && date.getMonth() === picker.$date.getMonth() && date.getDate() === picker.$date.getDate();
  3843. },
  3844. isDisabled: function(date) {
  3845. var time = date.getTime();
  3846. if (time < options.minDate || time > options.maxDate) return true;
  3847. if (options.daysOfWeekDisabled.indexOf(date.getDay()) !== -1) return true;
  3848. if (options.disabledDateRanges) {
  3849. for (var i = 0; i < options.disabledDateRanges.length; i++) {
  3850. if (time >= options.disabledDateRanges[i].start && time <= options.disabledDateRanges[i].end) {
  3851. return true;
  3852. }
  3853. }
  3854. }
  3855. return false;
  3856. },
  3857. onKeyDown: function(evt) {
  3858. if (!picker.$date) {
  3859. return;
  3860. }
  3861. var actualTime = picker.$date.getTime();
  3862. var newDate;
  3863. if (evt.keyCode === 37) newDate = new Date(actualTime - 1 * 864e5); else if (evt.keyCode === 38) newDate = new Date(actualTime - 7 * 864e5); else if (evt.keyCode === 39) newDate = new Date(actualTime + 1 * 864e5); else if (evt.keyCode === 40) newDate = new Date(actualTime + 7 * 864e5);
  3864. if (!this.isDisabled(newDate)) picker.select(newDate, true);
  3865. }
  3866. }, {
  3867. name: 'month',
  3868. format: options.monthFormat,
  3869. split: 4,
  3870. steps: {
  3871. year: 1
  3872. },
  3873. update: function(date, force) {
  3874. if (!this.built || date.getFullYear() !== viewDate.year) {
  3875. angular.extend(viewDate, {
  3876. year: picker.$date.getFullYear(),
  3877. month: picker.$date.getMonth(),
  3878. date: picker.$date.getDate()
  3879. });
  3880. picker.$build();
  3881. } else if (date.getMonth() !== viewDate.month) {
  3882. angular.extend(viewDate, {
  3883. month: picker.$date.getMonth(),
  3884. date: picker.$date.getDate()
  3885. });
  3886. picker.$updateSelected();
  3887. }
  3888. },
  3889. build: function() {
  3890. var months = [];
  3891. var month;
  3892. for (var i = 0; i < 12; i++) {
  3893. month = new Date(viewDate.year, i, 1);
  3894. months.push({
  3895. date: month,
  3896. label: formatDate(month, this.format),
  3897. selected: picker.$isSelected(month),
  3898. disabled: this.isDisabled(month)
  3899. });
  3900. }
  3901. scope.title = formatDate(month, options.yearTitleFormat);
  3902. scope.showLabels = false;
  3903. scope.rows = split(months, this.split);
  3904. this.built = true;
  3905. },
  3906. isSelected: function(date) {
  3907. return picker.$date && date.getFullYear() === picker.$date.getFullYear() && date.getMonth() === picker.$date.getMonth();
  3908. },
  3909. isDisabled: function(date) {
  3910. var lastDate = +new Date(date.getFullYear(), date.getMonth() + 1, 0);
  3911. return lastDate < options.minDate || date.getTime() > options.maxDate;
  3912. },
  3913. onKeyDown: function(evt) {
  3914. if (!picker.$date) {
  3915. return;
  3916. }
  3917. var actualMonth = picker.$date.getMonth();
  3918. var newDate = new Date(picker.$date);
  3919. if (evt.keyCode === 37) newDate.setMonth(actualMonth - 1); else if (evt.keyCode === 38) newDate.setMonth(actualMonth - 4); else if (evt.keyCode === 39) newDate.setMonth(actualMonth + 1); else if (evt.keyCode === 40) newDate.setMonth(actualMonth + 4);
  3920. if (!this.isDisabled(newDate)) picker.select(newDate, true);
  3921. }
  3922. }, {
  3923. name: 'year',
  3924. format: options.yearFormat,
  3925. split: 4,
  3926. steps: {
  3927. year: 12
  3928. },
  3929. update: function(date, force) {
  3930. if (!this.built || force || parseInt(date.getFullYear() / 20, 10) !== parseInt(viewDate.year / 20, 10)) {
  3931. angular.extend(viewDate, {
  3932. year: picker.$date.getFullYear(),
  3933. month: picker.$date.getMonth(),
  3934. date: picker.$date.getDate()
  3935. });
  3936. picker.$build();
  3937. } else if (date.getFullYear() !== viewDate.year) {
  3938. angular.extend(viewDate, {
  3939. year: picker.$date.getFullYear(),
  3940. month: picker.$date.getMonth(),
  3941. date: picker.$date.getDate()
  3942. });
  3943. picker.$updateSelected();
  3944. }
  3945. },
  3946. build: function() {
  3947. var firstYear = viewDate.year - viewDate.year % (this.split * 3);
  3948. var years = [];
  3949. var year;
  3950. for (var i = 0; i < 12; i++) {
  3951. year = new Date(firstYear + i, 0, 1);
  3952. years.push({
  3953. date: year,
  3954. label: formatDate(year, this.format),
  3955. selected: picker.$isSelected(year),
  3956. disabled: this.isDisabled(year)
  3957. });
  3958. }
  3959. scope.title = years[0].label + '-' + years[years.length - 1].label;
  3960. scope.showLabels = false;
  3961. scope.rows = split(years, this.split);
  3962. this.built = true;
  3963. },
  3964. isSelected: function(date) {
  3965. return picker.$date && date.getFullYear() === picker.$date.getFullYear();
  3966. },
  3967. isDisabled: function(date) {
  3968. var lastDate = +new Date(date.getFullYear() + 1, 0, 0);
  3969. return lastDate < options.minDate || date.getTime() > options.maxDate;
  3970. },
  3971. onKeyDown: function(evt) {
  3972. if (!picker.$date) {
  3973. return;
  3974. }
  3975. var actualYear = picker.$date.getFullYear();
  3976. var newDate = new Date(picker.$date);
  3977. if (evt.keyCode === 37) newDate.setYear(actualYear - 1); else if (evt.keyCode === 38) newDate.setYear(actualYear - 4); else if (evt.keyCode === 39) newDate.setYear(actualYear + 1); else if (evt.keyCode === 40) newDate.setYear(actualYear + 4);
  3978. if (!this.isDisabled(newDate)) picker.select(newDate, true);
  3979. }
  3980. } ];
  3981. return {
  3982. views: options.minView ? Array.prototype.slice.call(views, options.minView) : views,
  3983. viewDate: viewDate
  3984. };
  3985. };
  3986. } ];
  3987. });
  3988. angular.module('mgcrea.ngStrap.collapse', []).provider('$collapse', function() {
  3989. var defaults = this.defaults = {
  3990. animation: 'am-collapse',
  3991. disallowToggle: false,
  3992. activeClass: 'in',
  3993. startCollapsed: false,
  3994. allowMultiple: false
  3995. };
  3996. var controller = this.controller = function($scope, $element, $attrs) {
  3997. var self = this;
  3998. self.$options = angular.copy(defaults);
  3999. angular.forEach([ 'animation', 'disallowToggle', 'activeClass', 'startCollapsed', 'allowMultiple' ], function(key) {
  4000. if (angular.isDefined($attrs[key])) self.$options[key] = $attrs[key];
  4001. });
  4002. var falseValueRegExp = /^(false|0|)$/i;
  4003. angular.forEach([ 'disallowToggle', 'startCollapsed', 'allowMultiple' ], function(key) {
  4004. if (angular.isDefined($attrs[key]) && falseValueRegExp.test($attrs[key])) {
  4005. self.$options[key] = false;
  4006. }
  4007. });
  4008. self.$toggles = [];
  4009. self.$targets = [];
  4010. self.$viewChangeListeners = [];
  4011. self.$registerToggle = function(element) {
  4012. self.$toggles.push(element);
  4013. };
  4014. self.$registerTarget = function(element) {
  4015. self.$targets.push(element);
  4016. };
  4017. self.$unregisterToggle = function(element) {
  4018. var index = self.$toggles.indexOf(element);
  4019. self.$toggles.splice(index, 1);
  4020. };
  4021. self.$unregisterTarget = function(element) {
  4022. var index = self.$targets.indexOf(element);
  4023. self.$targets.splice(index, 1);
  4024. if (self.$options.allowMultiple) {
  4025. deactivateItem(element);
  4026. }
  4027. fixActiveItemIndexes(index);
  4028. self.$viewChangeListeners.forEach(function(fn) {
  4029. fn();
  4030. });
  4031. };
  4032. self.$targets.$active = !self.$options.startCollapsed ? [ 0 ] : [];
  4033. self.$setActive = $scope.$setActive = function(value) {
  4034. if (angular.isArray(value)) {
  4035. self.$targets.$active = value;
  4036. } else if (!self.$options.disallowToggle && isActive(value)) {
  4037. deactivateItem(value);
  4038. } else {
  4039. activateItem(value);
  4040. }
  4041. self.$viewChangeListeners.forEach(function(fn) {
  4042. fn();
  4043. });
  4044. };
  4045. self.$activeIndexes = function() {
  4046. if (self.$options.allowMultiple) {
  4047. return self.$targets.$active;
  4048. }
  4049. return self.$targets.$active.length === 1 ? self.$targets.$active[0] : -1;
  4050. };
  4051. function fixActiveItemIndexes(index) {
  4052. var activeIndexes = self.$targets.$active;
  4053. for (var i = 0; i < activeIndexes.length; i++) {
  4054. if (index < activeIndexes[i]) {
  4055. activeIndexes[i] = activeIndexes[i] - 1;
  4056. }
  4057. if (activeIndexes[i] === self.$targets.length) {
  4058. activeIndexes[i] = self.$targets.length - 1;
  4059. }
  4060. }
  4061. }
  4062. function isActive(value) {
  4063. var activeItems = self.$targets.$active;
  4064. return activeItems.indexOf(value) !== -1;
  4065. }
  4066. function deactivateItem(value) {
  4067. var index = self.$targets.$active.indexOf(value);
  4068. if (index !== -1) {
  4069. self.$targets.$active.splice(index, 1);
  4070. }
  4071. }
  4072. function activateItem(value) {
  4073. if (!self.$options.allowMultiple) {
  4074. self.$targets.$active.splice(0, 1);
  4075. }
  4076. if (self.$targets.$active.indexOf(value) === -1) {
  4077. self.$targets.$active.push(value);
  4078. }
  4079. }
  4080. };
  4081. this.$get = function() {
  4082. var $collapse = {};
  4083. $collapse.defaults = defaults;
  4084. $collapse.controller = controller;
  4085. return $collapse;
  4086. };
  4087. }).directive('bsCollapse', [ '$window', '$animate', '$collapse', function($window, $animate, $collapse) {
  4088. return {
  4089. require: [ '?ngModel', 'bsCollapse' ],
  4090. controller: [ '$scope', '$element', '$attrs', $collapse.controller ],
  4091. link: function postLink(scope, element, attrs, controllers) {
  4092. var ngModelCtrl = controllers[0];
  4093. var bsCollapseCtrl = controllers[1];
  4094. if (ngModelCtrl) {
  4095. bsCollapseCtrl.$viewChangeListeners.push(function() {
  4096. ngModelCtrl.$setViewValue(bsCollapseCtrl.$activeIndexes());
  4097. });
  4098. ngModelCtrl.$formatters.push(function(modelValue) {
  4099. if (angular.isArray(modelValue)) {
  4100. bsCollapseCtrl.$setActive(modelValue);
  4101. } else {
  4102. var activeIndexes = bsCollapseCtrl.$activeIndexes();
  4103. if (angular.isArray(activeIndexes)) {
  4104. if (activeIndexes.indexOf(modelValue * 1) === -1) {
  4105. bsCollapseCtrl.$setActive(modelValue * 1);
  4106. }
  4107. } else if (activeIndexes !== modelValue * 1) {
  4108. bsCollapseCtrl.$setActive(modelValue * 1);
  4109. }
  4110. }
  4111. return modelValue;
  4112. });
  4113. }
  4114. }
  4115. };
  4116. } ]).directive('bsCollapseToggle', function() {
  4117. return {
  4118. require: [ '^?ngModel', '^bsCollapse' ],
  4119. link: function postLink(scope, element, attrs, controllers) {
  4120. var bsCollapseCtrl = controllers[1];
  4121. element.attr('data-toggle', 'collapse');
  4122. bsCollapseCtrl.$registerToggle(element);
  4123. scope.$on('$destroy', function() {
  4124. bsCollapseCtrl.$unregisterToggle(element);
  4125. });
  4126. element.on('click', function() {
  4127. if (!attrs.disabled) {
  4128. var index = attrs.bsCollapseToggle && attrs.bsCollapseToggle !== 'bs-collapse-toggle' ? attrs.bsCollapseToggle : bsCollapseCtrl.$toggles.indexOf(element);
  4129. bsCollapseCtrl.$setActive(index * 1);
  4130. scope.$apply();
  4131. }
  4132. });
  4133. }
  4134. };
  4135. }).directive('bsCollapseTarget', [ '$animate', function($animate) {
  4136. return {
  4137. require: [ '^?ngModel', '^bsCollapse' ],
  4138. link: function postLink(scope, element, attrs, controllers) {
  4139. var bsCollapseCtrl = controllers[1];
  4140. element.addClass('collapse');
  4141. if (bsCollapseCtrl.$options.animation) {
  4142. element.addClass(bsCollapseCtrl.$options.animation);
  4143. }
  4144. bsCollapseCtrl.$registerTarget(element);
  4145. scope.$on('$destroy', function() {
  4146. bsCollapseCtrl.$unregisterTarget(element);
  4147. });
  4148. function render() {
  4149. var index = bsCollapseCtrl.$targets.indexOf(element);
  4150. var active = bsCollapseCtrl.$activeIndexes();
  4151. var action = 'removeClass';
  4152. if (angular.isArray(active)) {
  4153. if (active.indexOf(index) !== -1) {
  4154. action = 'addClass';
  4155. }
  4156. } else if (index === active) {
  4157. action = 'addClass';
  4158. }
  4159. $animate[action](element, bsCollapseCtrl.$options.activeClass);
  4160. }
  4161. bsCollapseCtrl.$viewChangeListeners.push(function() {
  4162. render();
  4163. });
  4164. render();
  4165. }
  4166. };
  4167. } ]);
  4168. angular.module('mgcrea.ngStrap.aside', [ 'mgcrea.ngStrap.modal' ]).provider('$aside', function() {
  4169. var defaults = this.defaults = {
  4170. animation: 'am-fade-and-slide-right',
  4171. prefixClass: 'aside',
  4172. prefixEvent: 'aside',
  4173. placement: 'right',
  4174. templateUrl: 'aside/aside.tpl.html',
  4175. contentTemplate: false,
  4176. container: false,
  4177. element: null,
  4178. backdrop: true,
  4179. keyboard: true,
  4180. html: false,
  4181. show: true
  4182. };
  4183. this.$get = [ '$modal', function($modal) {
  4184. function AsideFactory(config) {
  4185. var $aside = {};
  4186. var options = angular.extend({}, defaults, config);
  4187. $aside = $modal(options);
  4188. return $aside;
  4189. }
  4190. return AsideFactory;
  4191. } ];
  4192. }).directive('bsAside', [ '$window', '$sce', '$aside', function($window, $sce, $aside) {
  4193. return {
  4194. restrict: 'EAC',
  4195. scope: true,
  4196. link: function postLink(scope, element, attr, transclusion) {
  4197. var options = {
  4198. scope: scope,
  4199. element: element,
  4200. show: false
  4201. };
  4202. angular.forEach([ 'template', 'templateUrl', 'controller', 'controllerAs', 'contentTemplate', 'placement', 'backdrop', 'keyboard', 'html', 'container', 'animation' ], function(key) {
  4203. if (angular.isDefined(attr[key])) options[key] = attr[key];
  4204. });
  4205. var falseValueRegExp = /^(false|0|)$/i;
  4206. angular.forEach([ 'backdrop', 'keyboard', 'html', 'container' ], function(key) {
  4207. if (angular.isDefined(attr[key]) && falseValueRegExp.test(attr[key])) options[key] = false;
  4208. });
  4209. angular.forEach([ 'onBeforeShow', 'onShow', 'onBeforeHide', 'onHide' ], function(key) {
  4210. var bsKey = 'bs' + key.charAt(0).toUpperCase() + key.slice(1);
  4211. if (angular.isDefined(attr[bsKey])) {
  4212. options[key] = scope.$eval(attr[bsKey]);
  4213. }
  4214. });
  4215. angular.forEach([ 'title', 'content' ], function(key) {
  4216. if (attr[key]) {
  4217. attr.$observe(key, function(newValue, oldValue) {
  4218. scope[key] = $sce.trustAsHtml(newValue);
  4219. });
  4220. }
  4221. });
  4222. if (attr.bsAside) {
  4223. scope.$watch(attr.bsAside, function(newValue, oldValue) {
  4224. if (angular.isObject(newValue)) {
  4225. angular.extend(scope, newValue);
  4226. } else {
  4227. scope.content = newValue;
  4228. }
  4229. }, true);
  4230. }
  4231. var aside = $aside(options);
  4232. element.on(attr.trigger || 'click', aside.toggle);
  4233. scope.$on('$destroy', function() {
  4234. if (aside) aside.destroy();
  4235. options = null;
  4236. aside = null;
  4237. });
  4238. }
  4239. };
  4240. } ]);
  4241. angular.module('mgcrea.ngStrap.alert', [ 'mgcrea.ngStrap.modal' ]).provider('$alert', function() {
  4242. var defaults = this.defaults = {
  4243. animation: 'am-fade',
  4244. prefixClass: 'alert',
  4245. prefixEvent: 'alert',
  4246. placement: null,
  4247. templateUrl: 'alert/alert.tpl.html',
  4248. container: false,
  4249. element: null,
  4250. backdrop: false,
  4251. keyboard: true,
  4252. show: true,
  4253. duration: false,
  4254. type: false,
  4255. dismissable: true
  4256. };
  4257. this.$get = [ '$modal', '$timeout', function($modal, $timeout) {
  4258. function AlertFactory(config) {
  4259. var $alert = {};
  4260. var options = angular.extend({}, defaults, config);
  4261. $alert = $modal(options);
  4262. $alert.$scope.dismissable = !!options.dismissable;
  4263. if (options.type) {
  4264. $alert.$scope.type = options.type;
  4265. }
  4266. var show = $alert.show;
  4267. if (options.duration) {
  4268. $alert.show = function() {
  4269. show();
  4270. $timeout(function() {
  4271. $alert.hide();
  4272. }, options.duration * 1e3);
  4273. };
  4274. }
  4275. return $alert;
  4276. }
  4277. return AlertFactory;
  4278. } ];
  4279. }).directive('bsAlert', [ '$window', '$sce', '$alert', function($window, $sce, $alert) {
  4280. return {
  4281. restrict: 'EAC',
  4282. scope: true,
  4283. link: function postLink(scope, element, attr, transclusion) {
  4284. var options = {
  4285. scope: scope,
  4286. element: element,
  4287. show: false
  4288. };
  4289. angular.forEach([ 'template', 'templateUrl', 'controller', 'controllerAs', 'placement', 'keyboard', 'html', 'container', 'animation', 'duration', 'dismissable' ], function(key) {
  4290. if (angular.isDefined(attr[key])) options[key] = attr[key];
  4291. });
  4292. var falseValueRegExp = /^(false|0|)$/i;
  4293. angular.forEach([ 'keyboard', 'html', 'container', 'dismissable' ], function(key) {
  4294. if (angular.isDefined(attr[key]) && falseValueRegExp.test(attr[key])) options[key] = false;
  4295. });
  4296. angular.forEach([ 'onBeforeShow', 'onShow', 'onBeforeHide', 'onHide' ], function(key) {
  4297. var bsKey = 'bs' + key.charAt(0).toUpperCase() + key.slice(1);
  4298. if (angular.isDefined(attr[bsKey])) {
  4299. options[key] = scope.$eval(attr[bsKey]);
  4300. }
  4301. });
  4302. if (!scope.hasOwnProperty('title')) {
  4303. scope.title = '';
  4304. }
  4305. angular.forEach([ 'title', 'content', 'type' ], function(key) {
  4306. if (attr[key]) {
  4307. attr.$observe(key, function(newValue, oldValue) {
  4308. scope[key] = $sce.trustAsHtml(newValue);
  4309. });
  4310. }
  4311. });
  4312. if (attr.bsAlert) {
  4313. scope.$watch(attr.bsAlert, function(newValue, oldValue) {
  4314. if (angular.isObject(newValue)) {
  4315. angular.extend(scope, newValue);
  4316. } else {
  4317. scope.content = newValue;
  4318. }
  4319. }, true);
  4320. }
  4321. var alert = $alert(options);
  4322. element.on(attr.trigger || 'click', alert.toggle);
  4323. scope.$on('$destroy', function() {
  4324. if (alert) alert.destroy();
  4325. options = null;
  4326. alert = null;
  4327. });
  4328. }
  4329. };
  4330. } ]);
  4331. angular.module('mgcrea.ngStrap.affix', [ 'mgcrea.ngStrap.helpers.dimensions', 'mgcrea.ngStrap.helpers.debounce' ]).provider('$affix', function() {
  4332. var defaults = this.defaults = {
  4333. offsetTop: 'auto',
  4334. inlineStyles: true,
  4335. setWidth: true
  4336. };
  4337. this.$get = [ '$window', 'debounce', 'dimensions', function($window, debounce, dimensions) {
  4338. var bodyEl = angular.element($window.document.body);
  4339. var windowEl = angular.element($window);
  4340. function AffixFactory(element, config) {
  4341. var $affix = {};
  4342. var options = angular.extend({}, defaults, config);
  4343. var targetEl = options.target;
  4344. var reset = 'affix affix-top affix-bottom';
  4345. var setWidth = false;
  4346. var initialAffixTop = 0;
  4347. var initialOffsetTop = 0;
  4348. var offsetTop = 0;
  4349. var offsetBottom = 0;
  4350. var affixed = null;
  4351. var unpin = null;
  4352. var parent = element.parent();
  4353. if (options.offsetParent) {
  4354. if (options.offsetParent.match(/^\d+$/)) {
  4355. for (var i = 0; i < options.offsetParent * 1 - 1; i++) {
  4356. parent = parent.parent();
  4357. }
  4358. } else {
  4359. parent = angular.element(options.offsetParent);
  4360. }
  4361. }
  4362. $affix.init = function() {
  4363. this.$parseOffsets();
  4364. initialOffsetTop = dimensions.offset(element[0]).top + initialAffixTop;
  4365. setWidth = options.setWidth && !element[0].style.width;
  4366. targetEl.on('scroll', this.checkPosition);
  4367. targetEl.on('click', this.checkPositionWithEventLoop);
  4368. windowEl.on('resize', this.$debouncedOnResize);
  4369. this.checkPosition();
  4370. this.checkPositionWithEventLoop();
  4371. };
  4372. $affix.destroy = function() {
  4373. targetEl.off('scroll', this.checkPosition);
  4374. targetEl.off('click', this.checkPositionWithEventLoop);
  4375. windowEl.off('resize', this.$debouncedOnResize);
  4376. };
  4377. $affix.checkPositionWithEventLoop = function() {
  4378. setTimeout($affix.checkPosition, 1);
  4379. };
  4380. $affix.checkPosition = function() {
  4381. var scrollTop = getScrollTop();
  4382. var position = dimensions.offset(element[0]);
  4383. var elementHeight = dimensions.height(element[0]);
  4384. var affix = getRequiredAffixClass(unpin, position, elementHeight);
  4385. if (affixed === affix) return;
  4386. affixed = affix;
  4387. if (affix === 'top') {
  4388. unpin = null;
  4389. if (setWidth) {
  4390. element.css('width', '');
  4391. }
  4392. if (options.inlineStyles) {
  4393. element.css('position', options.offsetParent ? '' : 'relative');
  4394. element.css('top', '');
  4395. }
  4396. } else if (affix === 'bottom') {
  4397. if (options.offsetUnpin) {
  4398. unpin = -(options.offsetUnpin * 1);
  4399. } else {
  4400. unpin = position.top - scrollTop;
  4401. }
  4402. if (setWidth) {
  4403. element.css('width', '');
  4404. }
  4405. if (options.inlineStyles) {
  4406. element.css('position', options.offsetParent ? '' : 'relative');
  4407. element.css('top', options.offsetParent ? '' : bodyEl[0].offsetHeight - offsetBottom - elementHeight - initialOffsetTop + 'px');
  4408. }
  4409. } else {
  4410. unpin = null;
  4411. if (setWidth) {
  4412. element.css('width', element[0].offsetWidth + 'px');
  4413. }
  4414. if (options.inlineStyles) {
  4415. element.css('position', 'fixed');
  4416. element.css('top', initialAffixTop + 'px');
  4417. }
  4418. }
  4419. element.removeClass(reset).addClass('affix' + (affix !== 'middle' ? '-' + affix : ''));
  4420. };
  4421. $affix.$onResize = function() {
  4422. $affix.$parseOffsets();
  4423. $affix.checkPosition();
  4424. };
  4425. $affix.$debouncedOnResize = debounce($affix.$onResize, 50);
  4426. $affix.$parseOffsets = function() {
  4427. var initialPosition = element[0].style.position;
  4428. var initialTop = element[0].style.top;
  4429. if (options.inlineStyles) {
  4430. element.css('position', options.offsetParent ? '' : 'relative');
  4431. element.css('top', '');
  4432. }
  4433. if (options.offsetTop) {
  4434. if (options.offsetTop === 'auto') {
  4435. options.offsetTop = '+0';
  4436. }
  4437. if (options.offsetTop.match(/^[-+]\d+$/)) {
  4438. initialAffixTop = -options.offsetTop * 1;
  4439. if (options.offsetParent) {
  4440. offsetTop = dimensions.offset(parent[0]).top + options.offsetTop * 1;
  4441. } else {
  4442. offsetTop = dimensions.offset(element[0]).top - dimensions.css(element[0], 'marginTop', true) + options.offsetTop * 1;
  4443. }
  4444. } else {
  4445. offsetTop = options.offsetTop * 1;
  4446. }
  4447. }
  4448. if (options.offsetBottom) {
  4449. if (options.offsetParent && options.offsetBottom.match(/^[-+]\d+$/)) {
  4450. offsetBottom = getScrollHeight() - (dimensions.offset(parent[0]).top + dimensions.height(parent[0])) + options.offsetBottom * 1 + 1;
  4451. } else {
  4452. offsetBottom = options.offsetBottom * 1;
  4453. }
  4454. }
  4455. if (options.inlineStyles) {
  4456. element.css('position', initialPosition);
  4457. element.css('top', initialTop);
  4458. }
  4459. };
  4460. function getRequiredAffixClass(_unpin, position, elementHeight) {
  4461. var scrollTop = getScrollTop();
  4462. var scrollHeight = getScrollHeight();
  4463. if (scrollTop <= offsetTop) {
  4464. return 'top';
  4465. } else if (_unpin !== null) {
  4466. return scrollTop + _unpin <= position.top ? 'middle' : 'bottom';
  4467. } else if (offsetBottom !== null && position.top + elementHeight + initialAffixTop >= scrollHeight - offsetBottom) {
  4468. return 'bottom';
  4469. }
  4470. return 'middle';
  4471. }
  4472. function getScrollTop() {
  4473. return targetEl[0] === $window ? $window.pageYOffset : targetEl[0].scrollTop;
  4474. }
  4475. function getScrollHeight() {
  4476. return targetEl[0] === $window ? $window.document.body.scrollHeight : targetEl[0].scrollHeight;
  4477. }
  4478. $affix.init();
  4479. return $affix;
  4480. }
  4481. return AffixFactory;
  4482. } ];
  4483. }).directive('bsAffix', [ '$affix', '$window', '$timeout', function($affix, $window, $timeout) {
  4484. return {
  4485. restrict: 'EAC',
  4486. require: '^?bsAffixTarget',
  4487. link: function postLink(scope, element, attr, affixTarget) {
  4488. var options = {
  4489. scope: scope,
  4490. target: affixTarget ? affixTarget.$element : angular.element($window)
  4491. };
  4492. angular.forEach([ 'offsetTop', 'offsetBottom', 'offsetParent', 'offsetUnpin', 'inlineStyles', 'setWidth' ], function(key) {
  4493. if (angular.isDefined(attr[key])) {
  4494. var option = attr[key];
  4495. if (/true/i.test(option)) option = true;
  4496. if (/false/i.test(option)) option = false;
  4497. options[key] = option;
  4498. }
  4499. });
  4500. var affix;
  4501. $timeout(function() {
  4502. affix = $affix(element, options);
  4503. });
  4504. scope.$on('$destroy', function() {
  4505. if (affix) affix.destroy();
  4506. options = null;
  4507. affix = null;
  4508. });
  4509. }
  4510. };
  4511. } ]).directive('bsAffixTarget', function() {
  4512. return {
  4513. controller: [ '$element', function($element) {
  4514. this.$element = $element;
  4515. } ]
  4516. };
  4517. });
  4518. angular.module('mgcrea.ngStrap', [ 'mgcrea.ngStrap.modal', 'mgcrea.ngStrap.aside', 'mgcrea.ngStrap.alert', 'mgcrea.ngStrap.button', 'mgcrea.ngStrap.select', 'mgcrea.ngStrap.datepicker', 'mgcrea.ngStrap.timepicker', 'mgcrea.ngStrap.navbar', 'mgcrea.ngStrap.tooltip', 'mgcrea.ngStrap.popover', 'mgcrea.ngStrap.dropdown', 'mgcrea.ngStrap.typeahead', 'mgcrea.ngStrap.scrollspy', 'mgcrea.ngStrap.affix', 'mgcrea.ngStrap.tab', 'mgcrea.ngStrap.collapse' ]);
  4519. })(window, document);