voltar 0.0.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +3 -0
  4. data/Rakefile +37 -0
  5. data/app/assets/fonts/FontAwesome.otf +0 -0
  6. data/app/assets/fonts/Simple-Line-Icons.eot +0 -0
  7. data/app/assets/fonts/Simple-Line-Icons.svg +1369 -0
  8. data/app/assets/fonts/Simple-Line-Icons.ttf +0 -0
  9. data/app/assets/fonts/Simple-Line-Icons.woff +0 -0
  10. data/app/assets/fonts/fontawesome-webfont.eot +0 -0
  11. data/app/assets/fonts/fontawesome-webfont.svg +520 -0
  12. data/app/assets/fonts/fontawesome-webfont.ttf +0 -0
  13. data/app/assets/fonts/fontawesome-webfont.woff +0 -0
  14. data/app/assets/fonts/glyphicons-halflings-regular.eot +0 -0
  15. data/app/assets/fonts/glyphicons-halflings-regular.svg +229 -0
  16. data/app/assets/fonts/glyphicons-halflings-regular.ttf +0 -0
  17. data/app/assets/fonts/glyphicons-halflings-regular.woff +0 -0
  18. data/app/assets/fonts/sourcesanspro/sourcesanspro-bold.woff +0 -0
  19. data/app/assets/fonts/sourcesanspro/sourcesanspro-light.woff +0 -0
  20. data/app/assets/fonts/sourcesanspro/sourcesanspro.woff +0 -0
  21. data/app/assets/javascripts/voltar/app.js +203 -0
  22. data/app/assets/javascripts/voltar/application.js +33 -0
  23. data/app/assets/javascripts/voltar/controllers/app_ctrl.js.erb +673 -0
  24. data/app/assets/javascripts/voltar/directives/app_directives.js +345 -0
  25. data/app/assets/javascripts/voltar/factories/app_services.js +255 -0
  26. data/app/assets/javascripts/voltar/stripe.js.coffee +2 -0
  27. data/app/assets/stylesheets/voltar/app.css +4990 -0
  28. data/app/assets/stylesheets/voltar/application.css +23 -0
  29. data/app/assets/stylesheets/voltar/voltar.css.scss +93 -0
  30. data/app/controllers/voltar/application_controller.rb +4 -0
  31. data/app/controllers/voltar/dashboard_controller.rb +11 -0
  32. data/app/helpers/voltar/application_helper.rb +4 -0
  33. data/app/views/layouts/voltar/application.html.erb +40 -0
  34. data/app/views/voltar/account/_billing.html.erb +552 -0
  35. data/app/views/voltar/account/_locations.html.erb +135 -0
  36. data/app/views/voltar/account/_managers.html +134 -0
  37. data/app/views/voltar/account/_password.html.erb +57 -0
  38. data/app/views/voltar/account/_profile.html.erb +84 -0
  39. data/app/views/voltar/dashboard/index.html.erb +0 -0
  40. data/app/views/voltar/inventory/_delete_dialog.html.erb +16 -0
  41. data/app/views/voltar/inventory/_edit.html.erb +244 -0
  42. data/app/views/voltar/inventory/_index.html.erb +160 -0
  43. data/app/views/voltar/inventory/_mark_as_sold_dialog.html.erb +26 -0
  44. data/app/views/voltar/shared/_keen_js.html.haml +11 -0
  45. data/app/views/voltar/shared/_voltar_app.html.erb +83 -0
  46. data/app/views/voltar/shared/_voltar_aside.html.erb +71 -0
  47. data/app/views/voltar/shared/_voltar_footer.html.erb +13 -0
  48. data/app/views/voltar/shared/_voltar_header.html.erb +156 -0
  49. data/app/views/voltar/shared/app/_country_province_select.html +19 -0
  50. data/app/views/voltar/shared/app/_dashboard.html.erb +243 -0
  51. data/app/views/voltar/shared/app/_notifications.html.erb +13 -0
  52. data/app/views/voltar/shared/app/_spinner.html.erb +8 -0
  53. data/config/routes.rb +4 -0
  54. data/lib/tasks/voltar_tasks.rake +4 -0
  55. data/lib/voltar.rb +5 -0
  56. data/lib/voltar/engine.rb +15 -0
  57. data/lib/voltar/version.rb +3 -0
  58. data/test/dummy/README.rdoc +28 -0
  59. data/test/dummy/Rakefile +6 -0
  60. data/test/dummy/app/assets/javascripts/application.js +13 -0
  61. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  62. data/test/dummy/app/controllers/application_controller.rb +5 -0
  63. data/test/dummy/app/helpers/application_helper.rb +2 -0
  64. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  65. data/test/dummy/bin/bundle +3 -0
  66. data/test/dummy/bin/rails +4 -0
  67. data/test/dummy/bin/rake +4 -0
  68. data/test/dummy/bin/setup +29 -0
  69. data/test/dummy/config.ru +4 -0
  70. data/test/dummy/config/application.rb +26 -0
  71. data/test/dummy/config/boot.rb +5 -0
  72. data/test/dummy/config/database.yml +25 -0
  73. data/test/dummy/config/environment.rb +5 -0
  74. data/test/dummy/config/environments/development.rb +41 -0
  75. data/test/dummy/config/environments/production.rb +76 -0
  76. data/test/dummy/config/environments/test.rb +39 -0
  77. data/test/dummy/config/initializers/assets.rb +11 -0
  78. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  79. data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
  80. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  81. data/test/dummy/config/initializers/inflections.rb +16 -0
  82. data/test/dummy/config/initializers/mime_types.rb +4 -0
  83. data/test/dummy/config/initializers/session_store.rb +3 -0
  84. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  85. data/test/dummy/config/locales/en.yml +23 -0
  86. data/test/dummy/config/routes.rb +4 -0
  87. data/test/dummy/config/secrets.yml +22 -0
  88. data/test/dummy/log/development.log +0 -0
  89. data/test/dummy/public/404.html +67 -0
  90. data/test/dummy/public/422.html +67 -0
  91. data/test/dummy/public/500.html +66 -0
  92. data/test/dummy/public/favicon.ico +0 -0
  93. data/test/integration/navigation_test.rb +10 -0
  94. data/test/test_helper.rb +15 -0
  95. data/test/voltar_test.rb +7 -0
  96. data/vendor/assets/images/voltar/a0.jpg +0 -0
  97. data/vendor/assets/images/voltar/a1.jpg +0 -0
  98. data/vendor/assets/images/voltar/a10.jpg +0 -0
  99. data/vendor/assets/images/voltar/a2.jpg +0 -0
  100. data/vendor/assets/images/voltar/a3.jpg +0 -0
  101. data/vendor/assets/images/voltar/a4.jpg +0 -0
  102. data/vendor/assets/images/voltar/a5.jpg +0 -0
  103. data/vendor/assets/images/voltar/a6.jpg +0 -0
  104. data/vendor/assets/images/voltar/a7.jpg +0 -0
  105. data/vendor/assets/images/voltar/a8.jpg +0 -0
  106. data/vendor/assets/images/voltar/a9.jpg +0 -0
  107. data/vendor/assets/images/voltar/b0.jpg +0 -0
  108. data/vendor/assets/images/voltar/b1.jpg +0 -0
  109. data/vendor/assets/images/voltar/b2.jpg +0 -0
  110. data/vendor/assets/images/voltar/b3.jpg +0 -0
  111. data/vendor/assets/images/voltar/b4.jpg +0 -0
  112. data/vendor/assets/images/voltar/b5.jpg +0 -0
  113. data/vendor/assets/images/voltar/c0.jpg +0 -0
  114. data/vendor/assets/images/voltar/c1.jpg +0 -0
  115. data/vendor/assets/images/voltar/c2.jpg +0 -0
  116. data/vendor/assets/images/voltar/c3.jpg +0 -0
  117. data/vendor/assets/images/voltar/c4.jpg +0 -0
  118. data/vendor/assets/images/voltar/c5.jpg +0 -0
  119. data/vendor/assets/images/voltar/chosen-sprite.png +0 -0
  120. data/vendor/assets/images/voltar/chosen-sprite@2x.png +0 -0
  121. data/vendor/assets/images/voltar/logo.png +0 -0
  122. data/vendor/assets/images/voltar/p0.jpg +0 -0
  123. data/vendor/assets/javascripts/voltar/angular-animate.js +1689 -0
  124. data/vendor/assets/javascripts/voltar/angular-contenteditable.js +98 -0
  125. data/vendor/assets/javascripts/voltar/angular-cookies.js +206 -0
  126. data/vendor/assets/javascripts/voltar/angular-sanitize.js +647 -0
  127. data/vendor/assets/javascripts/voltar/angular-ui-router.js +3658 -0
  128. data/vendor/assets/javascripts/voltar/angular.js +22024 -0
  129. data/vendor/assets/javascripts/voltar/chosen.jquery.min.js +2 -0
  130. data/vendor/assets/javascripts/voltar/easypiechart/jquery.easy-pie-chart.js +209 -0
  131. data/vendor/assets/javascripts/voltar/flot/jquery.flot.min.js +29 -0
  132. data/vendor/assets/javascripts/voltar/flot/jquery.flot.orderBars.js +187 -0
  133. data/vendor/assets/javascripts/voltar/flot/jquery.flot.pie.min.js +56 -0
  134. data/vendor/assets/javascripts/voltar/flot/jquery.flot.resize.js +60 -0
  135. data/vendor/assets/javascripts/voltar/flot/jquery.flot.spline.js +212 -0
  136. data/vendor/assets/javascripts/voltar/flot/jquery.flot.tooltip.min.js +12 -0
  137. data/vendor/assets/javascripts/voltar/jquery.min.js +5 -0
  138. data/vendor/assets/javascripts/voltar/moment.js +2856 -0
  139. data/vendor/assets/javascripts/voltar/ngStorage.js +103 -0
  140. data/vendor/assets/javascripts/voltar/ocLazyLoad.js +906 -0
  141. data/vendor/assets/javascripts/voltar/smart-table.min.js +1 -0
  142. data/vendor/assets/javascripts/voltar/sparkline/jquery.sparkline.min.js +2 -0
  143. data/vendor/assets/javascripts/voltar/toaster.js +185 -0
  144. data/vendor/assets/javascripts/voltar/ui-bootstrap-tpls.js +4116 -0
  145. data/vendor/assets/javascripts/voltar/ui-jq.js +86 -0
  146. data/vendor/assets/javascripts/voltar/ui-load.js +93 -0
  147. data/vendor/assets/javascripts/voltar/ui-validate.js +119 -0
  148. data/vendor/assets/stylesheets/voltar/animate.css +1098 -0
  149. data/vendor/assets/stylesheets/voltar/bootstrap.css +6202 -0
  150. data/vendor/assets/stylesheets/voltar/chosen.css +399 -0
  151. data/vendor/assets/stylesheets/voltar/font-awesome.min.css +4 -0
  152. data/vendor/assets/stylesheets/voltar/font.css +18 -0
  153. data/vendor/assets/stylesheets/voltar/simple-line-icons.css +526 -0
  154. data/vendor/assets/stylesheets/voltar/toaster.css +213 -0
  155. metadata +333 -0
@@ -0,0 +1 @@
1
+ !function(t){"use strict";t.module("smart-table",[])}(angular),function(t,e){"use strict";t.module("smart-table").controller("stTableController",["$scope","$parse","$filter","$attrs",function(t,a,s,n){function i(t){return[].concat(t)}function r(){d=i(c(t)),h===!0&&m.pipe()}var c,l,u=n.stTable,o=a(u),p=o.assign,g=s("orderBy"),f=s("filter"),d=i(o(t)),b={sort:{},search:{},pagination:{start:0}},h=!0,m=this;n.stSafeSrc&&(c=a(n.stSafeSrc),t.$watch(function(){var e=c(t);return e?e.length:0},function(t,e){t!==e&&r()}),t.$watch(function(){return c(t)},function(t,e){t!==e&&r()})),this.sortBy=function(t,e){b.sort.predicate=t,b.sort.reverse=e===!0,b.pagination.start=0,this.pipe()},this.search=function(t,e){var a=b.search.predicateObject||{},s=e?e:"$";a[s]=t,t||delete a[s],b.search.predicateObject=a,b.pagination.start=0,this.pipe()},this.pipe=function(){var a=b.pagination,s=b.search.predicateObject?f(d,b.search.predicateObject):d;s=g(s,b.sort.predicate,b.sort.reverse),a.number!==e&&(a.numberOfPages=s.length>0?Math.ceil(s.length/a.number):1,a.start=a.start>=s.length?(a.numberOfPages-1)*a.number:a.start,s=s.slice(a.start,a.start+a.number)),p(t,s)},this.select=function(t,a){var s=d,n=s.indexOf(t);-1!==n&&("single"===a?(t.isSelected=t.isSelected!==!0,l&&(l.isSelected=!1),l=t.isSelected===!0?t:e):s[n].isSelected=!s[n].isSelected)},this.slice=function(t,e){b.pagination.start=t,b.pagination.number=e,this.pipe()},this.tableState=function(){return b},this.setFilterFunction=function(t){f=s(t)},this.setSortFunction=function(t){g=s(t)},this.preventPipeOnWatch=function(){h=!1}}]).directive("stTable",function(){return{restrict:"A",controller:"stTableController",link:function(){}}})}(angular),function(t){"use strict";t.module("smart-table").directive("stSearch",["$timeout",function(t){return{replace:!0,require:"^stTable",scope:{predicate:"=?stSearch"},link:function(e,a,s,n){var i=n,r=null,c=s.stDelay||400;e.$watch("predicate",function(t,e){t!==e&&(n.tableState().search={},i.search(a[0].value||"",t))}),e.$watch(function(){return n.tableState().search},function(t){var s=e.predicate||"$";t.predicateObject&&t.predicateObject[s]!==a[0].value&&(a[0].value=t.predicateObject[s]||"")},!0),a.bind("input",function(a){a=a.originalEvent||a,null!==r&&t.cancel(r),r=t(function(){i.search(a.target.value,e.predicate||""),r=null},c)})}}}])}(angular),function(t){"use strict";t.module("smart-table").directive("stSelectRow",function(){return{restrict:"A",require:"^stTable",scope:{row:"=stSelectRow"},link:function(t,e,a,s){var n=a.stSelectMode||"single";e.bind("click",function(){t.$apply(function(){s.select(t.row,n)})}),t.$watch("row.isSelected",function(t){t===!0?e.addClass("st-selected"):e.removeClass("st-selected")})}}})}(angular),function(t,e){"use strict";t.module("smart-table").directive("stSort",["$parse",function(a){return{restrict:"A",require:"^stTable",link:function(s,n,i,r){function c(){o++,o%3===0?(o=0,r.tableState().sort={},r.tableState().pagination.start=0,r.pipe()):r.sortBy(l,o%2===0)}var l=i.stSort,u=a(l),o=0,p=i.stClassAscent||"st-sort-ascent",g=i.stClassDescent||"st-sort-descent",f=["st-sort-natural",p,g];t.isFunction(u(s))&&(l=u(s)),n.bind("click",function(){l&&s.$apply(c)}),i.stSortDefault!==e&&(o="reverse"===i.stSortDefault?1:0,c()),s.$watch(function(){return r.tableState().sort},function(t){t.predicate!==l?(o=0,n.removeClass(p).removeClass(g)):(o=t.reverse===!0?2:1,n.removeClass(f[(o+1)%2]).addClass(f[o]))},!0)}}}])}(angular),function(t){"use strict";t.module("smart-table").directive("stPagination",function(){return{restrict:"EA",require:"^stTable",scope:{stItemsByPage:"=?",stDisplayedPages:"=?"},template:'<div class="pagination" ng-if="pages.length >= 2"><ul class="pagination"><li ng-repeat="page in pages" ng-class="{active: page==currentPage}"><a ng-click="selectPage(page)">{{page}}</a></li></ul></div>',replace:!0,link:function(t,e,a,s){function n(){var e,a,n=s.tableState().pagination,i=1;for(t.currentPage=Math.floor(n.start/n.number)+1,i=Math.max(i,t.currentPage-Math.abs(Math.floor(t.stDisplayedPages/2))),e=i+t.stDisplayedPages,e>n.numberOfPages&&(e=n.numberOfPages+1,i=Math.max(1,e-t.stDisplayedPages)),t.pages=[],t.numPages=n.numberOfPages,a=i;e>a;a++)t.pages.push(a)}t.stItemsByPage=t.stItemsByPage?+t.stItemsByPage:10,t.stDisplayedPages=t.stDisplayedPages?+t.stDisplayedPages:5,t.currentPage=1,t.pages=[],t.$watch(function(){return s.tableState().pagination},n,!0),t.$watch("stItemsByPage",function(){t.selectPage(1)}),t.$watch("stDisplayedPages",n),t.selectPage=function(e){e>0&&e<=t.numPages&&s.slice((e-1)*t.stItemsByPage,t.stItemsByPage)},s.slice(0,t.stItemsByPage)}}})}(angular),function(t){"use strict";t.module("smart-table").directive("stPipe",function(){return{require:"stTable",scope:{stPipe:"="},link:{pre:function(e,a,s,n){t.isFunction(e.stPipe)&&(n.preventPipeOnWatch(),n.pipe=t.bind(n,e.stPipe,n.tableState()))}}}})}(angular);
@@ -0,0 +1,2 @@
1
+ !function(a,b,c){!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):jQuery&&!jQuery.fn.sparkline&&a(jQuery)}(function(d){"use strict";var e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K={},L=0;e=function(){return{common:{type:"line",lineColor:"#00f",fillColor:"#cdf",defaultPixelsPerValue:3,width:"auto",height:"auto",composite:!1,tagValuesAttribute:"values",tagOptionsPrefix:"spark",enableTagOptions:!1,enableHighlight:!0,highlightLighten:1.4,tooltipSkipNull:!0,tooltipPrefix:"",tooltipSuffix:"",disableHiddenCheck:!1,numberFormatter:!1,numberDigitGroupCount:3,numberDigitGroupSep:",",numberDecimalMark:".",disableTooltips:!1,disableInteraction:!1},line:{spotColor:"#f80",highlightSpotColor:"#5f5",highlightLineColor:"#f22",spotRadius:1.5,minSpotColor:"#f80",maxSpotColor:"#f80",lineWidth:1,normalRangeMin:c,normalRangeMax:c,normalRangeColor:"#ccc",drawNormalOnTop:!1,chartRangeMin:c,chartRangeMax:c,chartRangeMinX:c,chartRangeMaxX:c,tooltipFormat:new g('<span style="color: {{color}}">&#9679;</span> {{prefix}}{{y}}{{suffix}}')},bar:{barColor:"#3366cc",negBarColor:"#f44",stackedBarColor:["#3366cc","#dc3912","#ff9900","#109618","#66aa00","#dd4477","#0099c6","#990099"],zeroColor:c,nullColor:c,zeroAxis:!0,barWidth:4,barSpacing:1,chartRangeMax:c,chartRangeMin:c,chartRangeClip:!1,colorMap:c,tooltipFormat:new g('<span style="color: {{color}}">&#9679;</span> {{prefix}}{{value}}{{suffix}}')},tristate:{barWidth:4,barSpacing:1,posBarColor:"#6f6",negBarColor:"#f44",zeroBarColor:"#999",colorMap:{},tooltipFormat:new g('<span style="color: {{color}}">&#9679;</span> {{value:map}}'),tooltipValueLookups:{map:{"-1":"Loss",0:"Draw",1:"Win"}}},discrete:{lineHeight:"auto",thresholdColor:c,thresholdValue:0,chartRangeMax:c,chartRangeMin:c,chartRangeClip:!1,tooltipFormat:new g("{{prefix}}{{value}}{{suffix}}")},bullet:{targetColor:"#f33",targetWidth:3,performanceColor:"#33f",rangeColors:["#d3dafe","#a8b6ff","#7f94ff"],base:c,tooltipFormat:new g("{{fieldkey:fields}} - {{value}}"),tooltipValueLookups:{fields:{r:"Range",p:"Performance",t:"Target"}}},pie:{offset:0,sliceColors:["#3366cc","#dc3912","#ff9900","#109618","#66aa00","#dd4477","#0099c6","#990099"],borderWidth:0,borderColor:"#000",tooltipFormat:new g('<span style="color: {{color}}">&#9679;</span> {{value}} ({{percent.1}}%)')},box:{raw:!1,boxLineColor:"#000",boxFillColor:"#cdf",whiskerColor:"#000",outlierLineColor:"#333",outlierFillColor:"#fff",medianColor:"#f00",showOutliers:!0,outlierIQR:1.5,spotRadius:1.5,target:c,targetColor:"#4a2",chartRangeMax:c,chartRangeMin:c,tooltipFormat:new g("{{field:fields}}: {{value}}"),tooltipFormatFieldlistKey:"field",tooltipValueLookups:{fields:{lq:"Lower Quartile",med:"Median",uq:"Upper Quartile",lo:"Left Outlier",ro:"Right Outlier",lw:"Left Whisker",rw:"Right Whisker"}}}}},D='.jqstooltip { position: absolute;left: 0px;top: 0px;visibility: hidden;background: rgb(0, 0, 0) transparent;background-color: rgba(0,0,0,0.6);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000);-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000)";color: white;font: 10px arial, san serif;text-align: left;white-space: nowrap;padding: 5px;border: 1px solid white;z-index: 10000;}.jqsfield { color: white;font: 10px arial, san serif;text-align: left;}',f=function(){var a,b;return a=function(){this.init.apply(this,arguments)},arguments.length>1?(arguments[0]?(a.prototype=d.extend(new arguments[0],arguments[arguments.length-1]),a._super=arguments[0].prototype):a.prototype=arguments[arguments.length-1],arguments.length>2&&(b=Array.prototype.slice.call(arguments,1,-1),b.unshift(a.prototype),d.extend.apply(d,b))):a.prototype=arguments[0],a.prototype.cls=a,a},d.SPFormatClass=g=f({fre:/\{\{([\w.]+?)(:(.+?))?\}\}/g,precre:/(\w+)\.(\d+)/,init:function(a,b){this.format=a,this.fclass=b},render:function(a,b,d){var e,f,g,h,i,j=this,k=a;return this.format.replace(this.fre,function(){var a;return f=arguments[1],g=arguments[3],e=j.precre.exec(f),e?(i=e[2],f=e[1]):i=!1,h=k[f],h===c?"":g&&b&&b[g]?(a=b[g],a.get?b[g].get(h)||h:b[g][h]||h):(m(h)&&(h=d.get("numberFormatter")?d.get("numberFormatter")(h):r(h,i,d.get("numberDigitGroupCount"),d.get("numberDigitGroupSep"),d.get("numberDecimalMark"))),h)})}}),d.spformat=function(a,b){return new g(a,b)},h=function(a,b,c){return b>a?b:a>c?c:a},i=function(a,c){var d;return 2===c?(d=b.floor(a.length/2),a.length%2?a[d]:(a[d-1]+a[d])/2):a.length%2?(d=(a.length*c+c)/4,d%1?(a[b.floor(d)]+a[b.floor(d)-1])/2:a[d-1]):(d=(a.length*c+2)/4,d%1?(a[b.floor(d)]+a[b.floor(d)-1])/2:a[d-1])},j=function(a){var b;switch(a){case"undefined":a=c;break;case"null":a=null;break;case"true":a=!0;break;case"false":a=!1;break;default:b=parseFloat(a),a==b&&(a=b)}return a},k=function(a){var b,c=[];for(b=a.length;b--;)c[b]=j(a[b]);return c},l=function(a,b){var c,d,e=[];for(c=0,d=a.length;d>c;c++)a[c]!==b&&e.push(a[c]);return e},m=function(a){return!isNaN(parseFloat(a))&&isFinite(a)},r=function(a,b,c,e,f){var g,h;for(a=(b===!1?parseFloat(a).toString():a.toFixed(b)).split(""),g=(g=d.inArray(".",a))<0?a.length:g,g<a.length&&(a[g]=f),h=g-c;h>0;h-=c)a.splice(h,0,e);return a.join("")},n=function(a,b,c){var d;for(d=b.length;d--;)if((!c||null!==b[d])&&b[d]!==a)return!1;return!0},o=function(a){var b,c=0;for(b=a.length;b--;)c+="number"==typeof a[b]?a[b]:0;return c},q=function(a){return d.isArray(a)?a:[a]},p=function(b){var c;a.createStyleSheet?a.createStyleSheet().cssText=b:(c=a.createElement("style"),c.type="text/css",a.getElementsByTagName("head")[0].appendChild(c),c["string"==typeof a.body.style.WebkitAppearance?"innerText":"innerHTML"]=b)},d.fn.simpledraw=function(b,e,f,g){var h,i;if(f&&(h=this.data("_jqs_vcanvas")))return h;if(d.fn.sparkline.canvas===!1)return!1;if(d.fn.sparkline.canvas===c){var j=a.createElement("canvas");if(j.getContext&&j.getContext("2d"))d.fn.sparkline.canvas=function(a,b,c,d){return new H(a,b,c,d)};else{if(!a.namespaces||a.namespaces.v)return d.fn.sparkline.canvas=!1,!1;a.namespaces.add("v","urn:schemas-microsoft-com:vml","#default#VML"),d.fn.sparkline.canvas=function(a,b,c){return new I(a,b,c)}}}return b===c&&(b=d(this).innerWidth()),e===c&&(e=d(this).innerHeight()),h=d.fn.sparkline.canvas(b,e,this,g),i=d(this).data("_jqs_mhandler"),i&&i.registerCanvas(h),h},d.fn.cleardraw=function(){var a=this.data("_jqs_vcanvas");a&&a.reset()},d.RangeMapClass=s=f({init:function(a){var b,c,d=[];for(b in a)a.hasOwnProperty(b)&&"string"==typeof b&&b.indexOf(":")>-1&&(c=b.split(":"),c[0]=0===c[0].length?-1/0:parseFloat(c[0]),c[1]=0===c[1].length?1/0:parseFloat(c[1]),c[2]=a[b],d.push(c));this.map=a,this.rangelist=d||!1},get:function(a){var b,d,e,f=this.rangelist;if((e=this.map[a])!==c)return e;if(f)for(b=f.length;b--;)if(d=f[b],d[0]<=a&&d[1]>=a)return d[2];return c}}),d.range_map=function(a){return new s(a)},t=f({init:function(a,b){var c=d(a);this.$el=c,this.options=b,this.currentPageX=0,this.currentPageY=0,this.el=a,this.splist=[],this.tooltip=null,this.over=!1,this.displayTooltips=!b.get("disableTooltips"),this.highlightEnabled=!b.get("disableHighlight")},registerSparkline:function(a){this.splist.push(a),this.over&&this.updateDisplay()},registerCanvas:function(a){var b=d(a.canvas);this.canvas=a,this.$canvas=b,b.mouseenter(d.proxy(this.mouseenter,this)),b.mouseleave(d.proxy(this.mouseleave,this)),b.click(d.proxy(this.mouseclick,this))},reset:function(a){this.splist=[],this.tooltip&&a&&(this.tooltip.remove(),this.tooltip=c)},mouseclick:function(a){var b=d.Event("sparklineClick");b.originalEvent=a,b.sparklines=this.splist,this.$el.trigger(b)},mouseenter:function(b){d(a.body).unbind("mousemove.jqs"),d(a.body).bind("mousemove.jqs",d.proxy(this.mousemove,this)),this.over=!0,this.currentPageX=b.pageX,this.currentPageY=b.pageY,this.currentEl=b.target,!this.tooltip&&this.displayTooltips&&(this.tooltip=new u(this.options),this.tooltip.updatePosition(b.pageX,b.pageY)),this.updateDisplay()},mouseleave:function(){d(a.body).unbind("mousemove.jqs");var b,c,e=this.splist,f=e.length,g=!1;for(this.over=!1,this.currentEl=null,this.tooltip&&(this.tooltip.remove(),this.tooltip=null),c=0;f>c;c++)b=e[c],b.clearRegionHighlight()&&(g=!0);g&&this.canvas.render()},mousemove:function(a){this.currentPageX=a.pageX,this.currentPageY=a.pageY,this.currentEl=a.target,this.tooltip&&this.tooltip.updatePosition(a.pageX,a.pageY),this.updateDisplay()},updateDisplay:function(){var a,b,c,e,f,g=this.splist,h=g.length,i=!1,j=this.$canvas.offset(),k=this.currentPageX-j.left,l=this.currentPageY-j.top;if(this.over){for(c=0;h>c;c++)b=g[c],e=b.setRegionHighlight(this.currentEl,k,l),e&&(i=!0);if(i){if(f=d.Event("sparklineRegionChange"),f.sparklines=this.splist,this.$el.trigger(f),this.tooltip){for(a="",c=0;h>c;c++)b=g[c],a+=b.getCurrentRegionTooltip();this.tooltip.setContent(a)}this.disableHighlight||this.canvas.render()}null===e&&this.mouseleave()}}}),u=f({sizeStyle:"position: static !important;display: block !important;visibility: hidden !important;float: left !important;",init:function(b){var c,e=b.get("tooltipClassname","jqstooltip"),f=this.sizeStyle;this.container=b.get("tooltipContainer")||a.body,this.tooltipOffsetX=b.get("tooltipOffsetX",10),this.tooltipOffsetY=b.get("tooltipOffsetY",12),d("#jqssizetip").remove(),d("#jqstooltip").remove(),this.sizetip=d("<div/>",{id:"jqssizetip",style:f,"class":e}),this.tooltip=d("<div/>",{id:"jqstooltip","class":e}).appendTo(this.container),c=this.tooltip.offset(),this.offsetLeft=c.left,this.offsetTop=c.top,this.hidden=!0,d(window).unbind("resize.jqs scroll.jqs"),d(window).bind("resize.jqs scroll.jqs",d.proxy(this.updateWindowDims,this)),this.updateWindowDims()},updateWindowDims:function(){this.scrollTop=d(window).scrollTop(),this.scrollLeft=d(window).scrollLeft(),this.scrollRight=this.scrollLeft+d(window).width(),this.updatePosition()},getSize:function(a){this.sizetip.html(a).appendTo(this.container),this.width=this.sizetip.width()+1,this.height=this.sizetip.height(),this.sizetip.remove()},setContent:function(a){return a?(this.getSize(a),this.tooltip.html(a).css({width:this.width,height:this.height,visibility:"visible"}),void(this.hidden&&(this.hidden=!1,this.updatePosition()))):(this.tooltip.css("visibility","hidden"),void(this.hidden=!0))},updatePosition:function(a,b){if(a===c){if(this.mousex===c)return;a=this.mousex-this.offsetLeft,b=this.mousey-this.offsetTop}else this.mousex=a-=this.offsetLeft,this.mousey=b-=this.offsetTop;this.height&&this.width&&!this.hidden&&(b-=this.height+this.tooltipOffsetY,a+=this.tooltipOffsetX,b<this.scrollTop&&(b=this.scrollTop),a<this.scrollLeft?a=this.scrollLeft:a+this.width>this.scrollRight&&(a=this.scrollRight-this.width),this.tooltip.css({left:a,top:b}))},remove:function(){this.tooltip.remove(),this.sizetip.remove(),this.sizetip=this.tooltip=c,d(window).unbind("resize.jqs scroll.jqs")}}),E=function(){p(D)},d(E),J=[],d.fn.sparkline=function(b,e){return this.each(function(){var f,g,h=new d.fn.sparkline.options(this,e),i=d(this);if(f=function(){var e,f,g,j,k,l,m;return"html"===b||b===c?(m=this.getAttribute(h.get("tagValuesAttribute")),(m===c||null===m)&&(m=i.html()),e=m.replace(/(^\s*<!--)|(-->\s*$)|\s+/g,"").split(",")):e=b,f="auto"===h.get("width")?e.length*h.get("defaultPixelsPerValue"):h.get("width"),"auto"===h.get("height")?h.get("composite")&&d.data(this,"_jqs_vcanvas")||(j=a.createElement("span"),j.innerHTML="a",i.html(j),g=d(j).innerHeight()||d(j).height(),d(j).remove(),j=null):g=h.get("height"),h.get("disableInteraction")?k=!1:(k=d.data(this,"_jqs_mhandler"),k?h.get("composite")||k.reset():(k=new t(this,h),d.data(this,"_jqs_mhandler",k))),h.get("composite")&&!d.data(this,"_jqs_vcanvas")?void(d.data(this,"_jqs_errnotify")||(alert("Attempted to attach a composite sparkline to an element with no existing sparkline"),d.data(this,"_jqs_errnotify",!0))):(l=new(d.fn.sparkline[h.get("type")])(this,e,h,f,g),l.render(),void(k&&k.registerSparkline(l)))},d(this).html()&&!h.get("disableHiddenCheck")&&d(this).is(":hidden")||!d(this).parents("body").length){if(!h.get("composite")&&d.data(this,"_jqs_pending"))for(g=J.length;g;g--)J[g-1][0]==this&&J.splice(g-1,1);J.push([this,f]),d.data(this,"_jqs_pending",!0)}else f.call(this)})},d.fn.sparkline.defaults=e(),d.sparkline_display_visible=function(){var a,b,c,e=[];for(b=0,c=J.length;c>b;b++)a=J[b][0],d(a).is(":visible")&&!d(a).parents().is(":hidden")?(J[b][1].call(a),d.data(J[b][0],"_jqs_pending",!1),e.push(b)):d(a).closest("html").length||d.data(a,"_jqs_pending")||(d.data(J[b][0],"_jqs_pending",!1),e.push(b));for(b=e.length;b;b--)J.splice(e[b-1],1)},d.fn.sparkline.options=f({init:function(a,b){var c,e,f,g;this.userOptions=b=b||{},this.tag=a,this.tagValCache={},e=d.fn.sparkline.defaults,f=e.common,this.tagOptionsPrefix=b.enableTagOptions&&(b.tagOptionsPrefix||f.tagOptionsPrefix),g=this.getTagSetting("type"),c=g===K?e[b.type||f.type]:e[g],this.mergedOptions=d.extend({},f,c,b)},getTagSetting:function(a){var b,d,e,f,g=this.tagOptionsPrefix;if(g===!1||g===c)return K;if(this.tagValCache.hasOwnProperty(a))b=this.tagValCache.key;else{if(b=this.tag.getAttribute(g+a),b===c||null===b)b=K;else if("["===b.substr(0,1))for(b=b.substr(1,b.length-2).split(","),d=b.length;d--;)b[d]=j(b[d].replace(/(^\s*)|(\s*$)/g,""));else if("{"===b.substr(0,1))for(e=b.substr(1,b.length-2).split(","),b={},d=e.length;d--;)f=e[d].split(":",2),b[f[0].replace(/(^\s*)|(\s*$)/g,"")]=j(f[1].replace(/(^\s*)|(\s*$)/g,""));else b=j(b);this.tagValCache.key=b}return b},get:function(a,b){var d,e=this.getTagSetting(a);return e!==K?e:(d=this.mergedOptions[a])===c?b:d}}),d.fn.sparkline._base=f({disabled:!1,init:function(a,b,e,f,g){this.el=a,this.$el=d(a),this.values=b,this.options=e,this.width=f,this.height=g,this.currentRegion=c},initTarget:function(){var a=!this.options.get("disableInteraction");(this.target=this.$el.simpledraw(this.width,this.height,this.options.get("composite"),a))?(this.canvasWidth=this.target.pixelWidth,this.canvasHeight=this.target.pixelHeight):this.disabled=!0},render:function(){return this.disabled?(this.el.innerHTML="",!1):!0},getRegion:function(){},setRegionHighlight:function(a,b,d){var e,f=this.currentRegion,g=!this.options.get("disableHighlight");return b>this.canvasWidth||d>this.canvasHeight||0>b||0>d?null:(e=this.getRegion(a,b,d),f!==e?(f!==c&&g&&this.removeHighlight(),this.currentRegion=e,e!==c&&g&&this.renderHighlight(),!0):!1)},clearRegionHighlight:function(){return this.currentRegion!==c?(this.removeHighlight(),this.currentRegion=c,!0):!1},renderHighlight:function(){this.changeHighlight(!0)},removeHighlight:function(){this.changeHighlight(!1)},changeHighlight:function(){},getCurrentRegionTooltip:function(){var a,b,e,f,h,i,j,k,l,m,n,o,p,q,r=this.options,s="",t=[];if(this.currentRegion===c)return"";if(a=this.getCurrentRegionFields(),n=r.get("tooltipFormatter"))return n(this,r,a);if(r.get("tooltipChartTitle")&&(s+='<div class="jqs jqstitle">'+r.get("tooltipChartTitle")+"</div>\n"),b=this.options.get("tooltipFormat"),!b)return"";if(d.isArray(b)||(b=[b]),d.isArray(a)||(a=[a]),j=this.options.get("tooltipFormatFieldlist"),k=this.options.get("tooltipFormatFieldlistKey"),j&&k){for(l=[],i=a.length;i--;)m=a[i][k],-1!=(q=d.inArray(m,j))&&(l[q]=a[i]);a=l}for(e=b.length,p=a.length,i=0;e>i;i++)for(o=b[i],"string"==typeof o&&(o=new g(o)),f=o.fclass||"jqsfield",q=0;p>q;q++)a[q].isNull&&r.get("tooltipSkipNull")||(d.extend(a[q],{prefix:r.get("tooltipPrefix"),suffix:r.get("tooltipSuffix")}),h=o.render(a[q],r.get("tooltipValueLookups"),r),t.push('<div class="'+f+'">'+h+"</div>"));return t.length?s+t.join("\n"):""},getCurrentRegionFields:function(){},calcHighlightColor:function(a,c){var d,e,f,g,i=c.get("highlightColor"),j=c.get("highlightLighten");if(i)return i;if(j&&(d=/^#([0-9a-f])([0-9a-f])([0-9a-f])$/i.exec(a)||/^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(a))){for(f=[],e=4===a.length?16:1,g=0;3>g;g++)f[g]=h(b.round(parseInt(d[g+1],16)*e*j),0,255);return"rgb("+f.join(",")+")"}return a}}),v={changeHighlight:function(a){var b,c=this.currentRegion,e=this.target,f=this.regionShapes[c];f&&(b=this.renderRegion(c,a),d.isArray(b)||d.isArray(f)?(e.replaceWithShapes(f,b),this.regionShapes[c]=d.map(b,function(a){return a.id})):(e.replaceWithShape(f,b),this.regionShapes[c]=b.id))},render:function(){var a,b,c,e,f=this.values,g=this.target,h=this.regionShapes;if(this.cls._super.render.call(this)){for(c=f.length;c--;)if(a=this.renderRegion(c))if(d.isArray(a)){for(b=[],e=a.length;e--;)a[e].append(),b.push(a[e].id);h[c]=b}else a.append(),h[c]=a.id;else h[c]=null;g.render()}}},d.fn.sparkline.line=w=f(d.fn.sparkline._base,{type:"line",init:function(a,b,c,d,e){w._super.init.call(this,a,b,c,d,e),this.vertices=[],this.regionMap=[],this.xvalues=[],this.yvalues=[],this.yminmax=[],this.hightlightSpotId=null,this.lastShapeId=null,this.initTarget()},getRegion:function(a,b){var d,e=this.regionMap;for(d=e.length;d--;)if(null!==e[d]&&b>=e[d][0]&&b<=e[d][1])return e[d][2];return c},getCurrentRegionFields:function(){var a=this.currentRegion;return{isNull:null===this.yvalues[a],x:this.xvalues[a],y:this.yvalues[a],color:this.options.get("lineColor"),fillColor:this.options.get("fillColor"),offset:a}},renderHighlight:function(){var a,b,d=this.currentRegion,e=this.target,f=this.vertices[d],g=this.options,h=g.get("spotRadius"),i=g.get("highlightSpotColor"),j=g.get("highlightLineColor");f&&(h&&i&&(a=e.drawCircle(f[0],f[1],h,c,i),this.highlightSpotId=a.id,e.insertAfterShape(this.lastShapeId,a)),j&&(b=e.drawLine(f[0],this.canvasTop,f[0],this.canvasTop+this.canvasHeight,j),this.highlightLineId=b.id,e.insertAfterShape(this.lastShapeId,b)))},removeHighlight:function(){var a=this.target;this.highlightSpotId&&(a.removeShapeId(this.highlightSpotId),this.highlightSpotId=null),this.highlightLineId&&(a.removeShapeId(this.highlightLineId),this.highlightLineId=null)},scanValues:function(){var a,c,d,e,f,g=this.values,h=g.length,i=this.xvalues,j=this.yvalues,k=this.yminmax;for(a=0;h>a;a++)c=g[a],d="string"==typeof g[a],e="object"==typeof g[a]&&g[a]instanceof Array,f=d&&g[a].split(":"),d&&2===f.length?(i.push(Number(f[0])),j.push(Number(f[1])),k.push(Number(f[1]))):e?(i.push(c[0]),j.push(c[1]),k.push(c[1])):(i.push(a),null===g[a]||"null"===g[a]?j.push(null):(j.push(Number(c)),k.push(Number(c))));this.options.get("xvalues")&&(i=this.options.get("xvalues")),this.maxy=this.maxyorg=b.max.apply(b,k),this.miny=this.minyorg=b.min.apply(b,k),this.maxx=b.max.apply(b,i),this.minx=b.min.apply(b,i),this.xvalues=i,this.yvalues=j,this.yminmax=k},processRangeOptions:function(){var a=this.options,b=a.get("normalRangeMin"),d=a.get("normalRangeMax");b!==c&&(b<this.miny&&(this.miny=b),d>this.maxy&&(this.maxy=d)),a.get("chartRangeMin")!==c&&(a.get("chartRangeClip")||a.get("chartRangeMin")<this.miny)&&(this.miny=a.get("chartRangeMin")),a.get("chartRangeMax")!==c&&(a.get("chartRangeClip")||a.get("chartRangeMax")>this.maxy)&&(this.maxy=a.get("chartRangeMax")),a.get("chartRangeMinX")!==c&&(a.get("chartRangeClipX")||a.get("chartRangeMinX")<this.minx)&&(this.minx=a.get("chartRangeMinX")),a.get("chartRangeMaxX")!==c&&(a.get("chartRangeClipX")||a.get("chartRangeMaxX")>this.maxx)&&(this.maxx=a.get("chartRangeMaxX"))},drawNormalRange:function(a,d,e,f,g){var h=this.options.get("normalRangeMin"),i=this.options.get("normalRangeMax"),j=d+b.round(e-e*((i-this.miny)/g)),k=b.round(e*(i-h)/g);this.target.drawRect(a,j,f,k,c,this.options.get("normalRangeColor")).append()},render:function(){var a,e,f,g,h,i,j,k,l,m,n,o,p,q,r,t,u,v,x,y,z,A,B,C,D,E=this.options,F=this.target,G=this.canvasWidth,H=this.canvasHeight,I=this.vertices,J=E.get("spotRadius"),K=this.regionMap;if(w._super.render.call(this)&&(this.scanValues(),this.processRangeOptions(),B=this.xvalues,C=this.yvalues,this.yminmax.length&&!(this.yvalues.length<2))){for(g=h=0,a=this.maxx-this.minx===0?1:this.maxx-this.minx,e=this.maxy-this.miny===0?1:this.maxy-this.miny,f=this.yvalues.length-1,J&&(4*J>G||4*J>H)&&(J=0),J&&(z=E.get("highlightSpotColor")&&!E.get("disableInteraction"),(z||E.get("minSpotColor")||E.get("spotColor")&&C[f]===this.miny)&&(H-=b.ceil(J)),(z||E.get("maxSpotColor")||E.get("spotColor")&&C[f]===this.maxy)&&(H-=b.ceil(J),g+=b.ceil(J)),(z||(E.get("minSpotColor")||E.get("maxSpotColor"))&&(C[0]===this.miny||C[0]===this.maxy))&&(h+=b.ceil(J),G-=b.ceil(J)),(z||E.get("spotColor")||E.get("minSpotColor")||E.get("maxSpotColor")&&(C[f]===this.miny||C[f]===this.maxy))&&(G-=b.ceil(J))),H--,E.get("normalRangeMin")===c||E.get("drawNormalOnTop")||this.drawNormalRange(h,g,H,G,e),j=[],k=[j],q=r=null,t=C.length,D=0;t>D;D++)l=B[D],n=B[D+1],m=C[D],o=h+b.round((l-this.minx)*(G/a)),p=t-1>D?h+b.round((n-this.minx)*(G/a)):G,r=o+(p-o)/2,K[D]=[q||0,r,D],q=r,null===m?D&&(null!==C[D-1]&&(j=[],k.push(j)),I.push(null)):(m<this.miny&&(m=this.miny),m>this.maxy&&(m=this.maxy),j.length||j.push([o,g+H]),i=[o,g+b.round(H-H*((m-this.miny)/e))],j.push(i),I.push(i));for(u=[],v=[],x=k.length,D=0;x>D;D++)j=k[D],j.length&&(E.get("fillColor")&&(j.push([j[j.length-1][0],g+H]),v.push(j.slice(0)),j.pop()),j.length>2&&(j[0]=[j[0][0],j[1][1]]),u.push(j));for(x=v.length,D=0;x>D;D++)F.drawShape(v[D],E.get("fillColor"),E.get("fillColor")).append();for(E.get("normalRangeMin")!==c&&E.get("drawNormalOnTop")&&this.drawNormalRange(h,g,H,G,e),x=u.length,D=0;x>D;D++)F.drawShape(u[D],E.get("lineColor"),c,E.get("lineWidth")).append();if(J&&E.get("valueSpots"))for(y=E.get("valueSpots"),y.get===c&&(y=new s(y)),D=0;t>D;D++)A=y.get(C[D]),A&&F.drawCircle(h+b.round((B[D]-this.minx)*(G/a)),g+b.round(H-H*((C[D]-this.miny)/e)),J,c,A).append();J&&E.get("spotColor")&&null!==C[f]&&F.drawCircle(h+b.round((B[B.length-1]-this.minx)*(G/a)),g+b.round(H-H*((C[f]-this.miny)/e)),J,c,E.get("spotColor")).append(),this.maxy!==this.minyorg&&(J&&E.get("minSpotColor")&&(l=B[d.inArray(this.minyorg,C)],F.drawCircle(h+b.round((l-this.minx)*(G/a)),g+b.round(H-H*((this.minyorg-this.miny)/e)),J,c,E.get("minSpotColor")).append()),J&&E.get("maxSpotColor")&&(l=B[d.inArray(this.maxyorg,C)],F.drawCircle(h+b.round((l-this.minx)*(G/a)),g+b.round(H-H*((this.maxyorg-this.miny)/e)),J,c,E.get("maxSpotColor")).append())),this.lastShapeId=F.getLastShapeId(),this.canvasTop=g,F.render()}}}),d.fn.sparkline.bar=x=f(d.fn.sparkline._base,v,{type:"bar",init:function(a,e,f,g,i){var m,n,o,p,q,r,t,u,v,w,y,z,A,B,C,D,E,F,G,H,I,J,K=parseInt(f.get("barWidth"),10),L=parseInt(f.get("barSpacing"),10),M=f.get("chartRangeMin"),N=f.get("chartRangeMax"),O=f.get("chartRangeClip"),P=1/0,Q=-1/0;for(x._super.init.call(this,a,e,f,g,i),r=0,t=e.length;t>r;r++)H=e[r],m="string"==typeof H&&H.indexOf(":")>-1,(m||d.isArray(H))&&(C=!0,m&&(H=e[r]=k(H.split(":"))),H=l(H,null),n=b.min.apply(b,H),o=b.max.apply(b,H),P>n&&(P=n),o>Q&&(Q=o));this.stacked=C,this.regionShapes={},this.barWidth=K,this.barSpacing=L,this.totalBarWidth=K+L,this.width=g=e.length*K+(e.length-1)*L,this.initTarget(),O&&(A=M===c?-1/0:M,B=N===c?1/0:N),q=[],p=C?[]:q;var R=[],S=[];for(r=0,t=e.length;t>r;r++)if(C)for(D=e[r],e[r]=G=[],R[r]=0,p[r]=S[r]=0,E=0,F=D.length;F>E;E++)H=G[E]=O?h(D[E],A,B):D[E],null!==H&&(H>0&&(R[r]+=H),0>P&&Q>0?0>H?S[r]+=b.abs(H):p[r]+=H:p[r]+=b.abs(H-(0>H?Q:P)),q.push(H));else H=O?h(e[r],A,B):e[r],H=e[r]=j(H),null!==H&&q.push(H);this.max=z=b.max.apply(b,q),this.min=y=b.min.apply(b,q),this.stackMax=Q=C?b.max.apply(b,R):z,this.stackMin=P=C?b.min.apply(b,q):y,f.get("chartRangeMin")!==c&&(f.get("chartRangeClip")||f.get("chartRangeMin")<y)&&(y=f.get("chartRangeMin")),f.get("chartRangeMax")!==c&&(f.get("chartRangeClip")||f.get("chartRangeMax")>z)&&(z=f.get("chartRangeMax")),this.zeroAxis=v=f.get("zeroAxis",!0),w=0>=y&&z>=0&&v?0:0==v?y:y>0?y:z,this.xaxisOffset=w,u=C?b.max.apply(b,p)+b.max.apply(b,S):z-y,this.canvasHeightEf=v&&0>y?this.canvasHeight-2:this.canvasHeight-1,w>y?(J=C&&z>=0?Q:z,I=(J-w)/u*this.canvasHeight,I!==b.ceil(I)&&(this.canvasHeightEf-=2,I=b.ceil(I))):I=this.canvasHeight,this.yoffset=I,d.isArray(f.get("colorMap"))?(this.colorMapByIndex=f.get("colorMap"),this.colorMapByValue=null):(this.colorMapByIndex=null,this.colorMapByValue=f.get("colorMap"),this.colorMapByValue&&this.colorMapByValue.get===c&&(this.colorMapByValue=new s(this.colorMapByValue))),this.range=u},getRegion:function(a,d){var e=b.floor(d/this.totalBarWidth);return 0>e||e>=this.values.length?c:e},getCurrentRegionFields:function(){var a,b,c=this.currentRegion,d=q(this.values[c]),e=[];for(b=d.length;b--;)a=d[b],e.push({isNull:null===a,value:a,color:this.calcColor(b,a,c),offset:c});return e},calcColor:function(a,b,e){var f,g,h=this.colorMapByIndex,i=this.colorMapByValue,j=this.options;return f=j.get(this.stacked?"stackedBarColor":0>b?"negBarColor":"barColor"),0===b&&j.get("zeroColor")!==c&&(f=j.get("zeroColor")),i&&(g=i.get(b))?f=g:h&&h.length>e&&(f=h[e]),d.isArray(f)?f[a%f.length]:f},renderRegion:function(a,e){var f,g,h,i,j,k,l,m,o,p,q=this.values[a],r=this.options,s=this.xaxisOffset,t=[],u=this.range,v=this.stacked,w=this.target,x=a*this.totalBarWidth,y=this.canvasHeightEf,z=this.yoffset;if(q=d.isArray(q)?q:[q],l=q.length,m=q[0],i=n(null,q),p=n(s,q,!0),i)return r.get("nullColor")?(h=e?r.get("nullColor"):this.calcHighlightColor(r.get("nullColor"),r),f=z>0?z-1:z,w.drawRect(x,f,this.barWidth-1,0,h,h)):c;for(j=z,k=0;l>k;k++){if(m=q[k],v&&m===s){if(!p||o)continue;o=!0}g=u>0?b.floor(y*(b.abs(m-s)/u))+1:1,s>m||m===s&&0===z?(f=j,j+=g):(f=z-g,z-=g),h=this.calcColor(k,m,a),e&&(h=this.calcHighlightColor(h,r)),t.push(w.drawRect(x,f,this.barWidth-1,g-1,h,h))}return 1===t.length?t[0]:t}}),d.fn.sparkline.tristate=y=f(d.fn.sparkline._base,v,{type:"tristate",init:function(a,b,e,f,g){var h=parseInt(e.get("barWidth"),10),i=parseInt(e.get("barSpacing"),10);y._super.init.call(this,a,b,e,f,g),this.regionShapes={},this.barWidth=h,this.barSpacing=i,this.totalBarWidth=h+i,this.values=d.map(b,Number),this.width=f=b.length*h+(b.length-1)*i,d.isArray(e.get("colorMap"))?(this.colorMapByIndex=e.get("colorMap"),this.colorMapByValue=null):(this.colorMapByIndex=null,this.colorMapByValue=e.get("colorMap"),this.colorMapByValue&&this.colorMapByValue.get===c&&(this.colorMapByValue=new s(this.colorMapByValue))),this.initTarget()},getRegion:function(a,c){return b.floor(c/this.totalBarWidth)},getCurrentRegionFields:function(){var a=this.currentRegion;return{isNull:this.values[a]===c,value:this.values[a],color:this.calcColor(this.values[a],a),offset:a}},calcColor:function(a,b){var c,d,e=this.values,f=this.options,g=this.colorMapByIndex,h=this.colorMapByValue;return c=h&&(d=h.get(a))?d:g&&g.length>b?g[b]:f.get(e[b]<0?"negBarColor":e[b]>0?"posBarColor":"zeroBarColor")},renderRegion:function(a,c){var d,e,f,g,h,i,j=this.values,k=this.options,l=this.target;return d=l.pixelHeight,f=b.round(d/2),g=a*this.totalBarWidth,j[a]<0?(h=f,e=f-1):j[a]>0?(h=0,e=f-1):(h=f-1,e=2),i=this.calcColor(j[a],a),null!==i?(c&&(i=this.calcHighlightColor(i,k)),l.drawRect(g,h,this.barWidth-1,e-1,i,i)):void 0}}),d.fn.sparkline.discrete=z=f(d.fn.sparkline._base,v,{type:"discrete",init:function(a,e,f,g,h){z._super.init.call(this,a,e,f,g,h),this.regionShapes={},this.values=e=d.map(e,Number),this.min=b.min.apply(b,e),this.max=b.max.apply(b,e),this.range=this.max-this.min,this.width=g="auto"===f.get("width")?2*e.length:this.width,this.interval=b.floor(g/e.length),this.itemWidth=g/e.length,f.get("chartRangeMin")!==c&&(f.get("chartRangeClip")||f.get("chartRangeMin")<this.min)&&(this.min=f.get("chartRangeMin")),f.get("chartRangeMax")!==c&&(f.get("chartRangeClip")||f.get("chartRangeMax")>this.max)&&(this.max=f.get("chartRangeMax")),this.initTarget(),this.target&&(this.lineHeight="auto"===f.get("lineHeight")?b.round(.3*this.canvasHeight):f.get("lineHeight"))},getRegion:function(a,c){return b.floor(c/this.itemWidth)},getCurrentRegionFields:function(){var a=this.currentRegion;return{isNull:this.values[a]===c,value:this.values[a],offset:a}},renderRegion:function(a,c){var d,e,f,g,i=this.values,j=this.options,k=this.min,l=this.max,m=this.range,n=this.interval,o=this.target,p=this.canvasHeight,q=this.lineHeight,r=p-q;return e=h(i[a],k,l),g=a*n,d=b.round(r-r*((e-k)/m)),f=j.get(j.get("thresholdColor")&&e<j.get("thresholdValue")?"thresholdColor":"lineColor"),c&&(f=this.calcHighlightColor(f,j)),o.drawLine(g,d,g,d+q,f)}}),d.fn.sparkline.bullet=A=f(d.fn.sparkline._base,{type:"bullet",init:function(a,d,e,f,g){var h,i,j;A._super.init.call(this,a,d,e,f,g),this.values=d=k(d),j=d.slice(),j[0]=null===j[0]?j[2]:j[0],j[1]=null===d[1]?j[2]:j[1],h=b.min.apply(b,d),i=b.max.apply(b,d),h=e.get("base")===c?0>h?h:0:e.get("base"),this.min=h,this.max=i,this.range=i-h,this.shapes={},this.valueShapes={},this.regiondata={},this.width=f="auto"===e.get("width")?"4.0em":f,this.target=this.$el.simpledraw(f,g,e.get("composite")),d.length||(this.disabled=!0),this.initTarget()},getRegion:function(a,b,d){var e=this.target.getShapeAt(a,b,d);return e!==c&&this.shapes[e]!==c?this.shapes[e]:c},getCurrentRegionFields:function(){var a=this.currentRegion;return{fieldkey:a.substr(0,1),value:this.values[a.substr(1)],region:a}},changeHighlight:function(a){var b,c=this.currentRegion,d=this.valueShapes[c];switch(delete this.shapes[d],c.substr(0,1)){case"r":b=this.renderRange(c.substr(1),a);break;case"p":b=this.renderPerformance(a);break;case"t":b=this.renderTarget(a)}this.valueShapes[c]=b.id,this.shapes[b.id]=c,this.target.replaceWithShape(d,b)},renderRange:function(a,c){var d=this.values[a],e=b.round(this.canvasWidth*((d-this.min)/this.range)),f=this.options.get("rangeColors")[a-2];return c&&(f=this.calcHighlightColor(f,this.options)),this.target.drawRect(0,0,e-1,this.canvasHeight-1,f,f)},renderPerformance:function(a){var c=this.values[1],d=b.round(this.canvasWidth*((c-this.min)/this.range)),e=this.options.get("performanceColor");return a&&(e=this.calcHighlightColor(e,this.options)),this.target.drawRect(0,b.round(.3*this.canvasHeight),d-1,b.round(.4*this.canvasHeight)-1,e,e)},renderTarget:function(a){var c=this.values[0],d=b.round(this.canvasWidth*((c-this.min)/this.range)-this.options.get("targetWidth")/2),e=b.round(.1*this.canvasHeight),f=this.canvasHeight-2*e,g=this.options.get("targetColor");return a&&(g=this.calcHighlightColor(g,this.options)),this.target.drawRect(d,e,this.options.get("targetWidth")-1,f-1,g,g)},render:function(){var a,b,c=this.values.length,d=this.target;if(A._super.render.call(this)){for(a=2;c>a;a++)b=this.renderRange(a).append(),this.shapes[b.id]="r"+a,this.valueShapes["r"+a]=b.id;null!==this.values[1]&&(b=this.renderPerformance().append(),this.shapes[b.id]="p1",this.valueShapes.p1=b.id),null!==this.values[0]&&(b=this.renderTarget().append(),this.shapes[b.id]="t0",this.valueShapes.t0=b.id),d.render()}}}),d.fn.sparkline.pie=B=f(d.fn.sparkline._base,{type:"pie",init:function(a,c,e,f,g){var h,i=0;if(B._super.init.call(this,a,c,e,f,g),this.shapes={},this.valueShapes={},this.values=c=d.map(c,Number),"auto"===e.get("width")&&(this.width=this.height),c.length>0)for(h=c.length;h--;)i+=c[h];this.total=i,this.initTarget(),this.radius=b.floor(b.min(this.canvasWidth,this.canvasHeight)/2)},getRegion:function(a,b,d){var e=this.target.getShapeAt(a,b,d);return e!==c&&this.shapes[e]!==c?this.shapes[e]:c},getCurrentRegionFields:function(){var a=this.currentRegion;return{isNull:this.values[a]===c,value:this.values[a],percent:this.values[a]/this.total*100,color:this.options.get("sliceColors")[a%this.options.get("sliceColors").length],offset:a}},changeHighlight:function(a){var b=this.currentRegion,c=this.renderSlice(b,a),d=this.valueShapes[b];delete this.shapes[d],this.target.replaceWithShape(d,c),this.valueShapes[b]=c.id,this.shapes[c.id]=b},renderSlice:function(a,d){var e,f,g,h,i,j=this.target,k=this.options,l=this.radius,m=k.get("borderWidth"),n=k.get("offset"),o=2*b.PI,p=this.values,q=this.total,r=n?2*b.PI*(n/360):0;for(h=p.length,g=0;h>g;g++){if(e=r,f=r,q>0&&(f=r+o*(p[g]/q)),a===g)return i=k.get("sliceColors")[g%k.get("sliceColors").length],d&&(i=this.calcHighlightColor(i,k)),j.drawPieSlice(l,l,l-m,e,f,c,i);r=f}},render:function(){var a,d,e=this.target,f=this.values,g=this.options,h=this.radius,i=g.get("borderWidth");if(B._super.render.call(this)){for(i&&e.drawCircle(h,h,b.floor(h-i/2),g.get("borderColor"),c,i).append(),d=f.length;d--;)f[d]&&(a=this.renderSlice(d).append(),this.valueShapes[d]=a.id,this.shapes[a.id]=d);
2
+ e.render()}}}),d.fn.sparkline.box=C=f(d.fn.sparkline._base,{type:"box",init:function(a,b,c,e,f){C._super.init.call(this,a,b,c,e,f),this.values=d.map(b,Number),this.width="auto"===c.get("width")?"4.0em":e,this.initTarget(),this.values.length||(this.disabled=1)},getRegion:function(){return 1},getCurrentRegionFields:function(){var a=[{field:"lq",value:this.quartiles[0]},{field:"med",value:this.quartiles[1]},{field:"uq",value:this.quartiles[2]}];return this.loutlier!==c&&a.push({field:"lo",value:this.loutlier}),this.routlier!==c&&a.push({field:"ro",value:this.routlier}),this.lwhisker!==c&&a.push({field:"lw",value:this.lwhisker}),this.rwhisker!==c&&a.push({field:"rw",value:this.rwhisker}),a},render:function(){var a,d,e,f,g,h,j,k,l,m,n,o=this.target,p=this.values,q=p.length,r=this.options,s=this.canvasWidth,t=this.canvasHeight,u=r.get("chartRangeMin")===c?b.min.apply(b,p):r.get("chartRangeMin"),v=r.get("chartRangeMax")===c?b.max.apply(b,p):r.get("chartRangeMax"),w=0;if(C._super.render.call(this)){if(r.get("raw"))r.get("showOutliers")&&p.length>5?(d=p[0],a=p[1],f=p[2],g=p[3],h=p[4],j=p[5],k=p[6]):(a=p[0],f=p[1],g=p[2],h=p[3],j=p[4]);else if(p.sort(function(a,b){return a-b}),f=i(p,1),g=i(p,2),h=i(p,3),e=h-f,r.get("showOutliers")){for(a=j=c,l=0;q>l;l++)a===c&&p[l]>f-e*r.get("outlierIQR")&&(a=p[l]),p[l]<h+e*r.get("outlierIQR")&&(j=p[l]);d=p[0],k=p[q-1]}else a=p[0],j=p[q-1];this.quartiles=[f,g,h],this.lwhisker=a,this.rwhisker=j,this.loutlier=d,this.routlier=k,n=s/(v-u+1),r.get("showOutliers")&&(w=b.ceil(r.get("spotRadius")),s-=2*b.ceil(r.get("spotRadius")),n=s/(v-u+1),a>d&&o.drawCircle((d-u)*n+w,t/2,r.get("spotRadius"),r.get("outlierLineColor"),r.get("outlierFillColor")).append(),k>j&&o.drawCircle((k-u)*n+w,t/2,r.get("spotRadius"),r.get("outlierLineColor"),r.get("outlierFillColor")).append()),o.drawRect(b.round((f-u)*n+w),b.round(.1*t),b.round((h-f)*n),b.round(.8*t),r.get("boxLineColor"),r.get("boxFillColor")).append(),o.drawLine(b.round((a-u)*n+w),b.round(t/2),b.round((f-u)*n+w),b.round(t/2),r.get("lineColor")).append(),o.drawLine(b.round((a-u)*n+w),b.round(t/4),b.round((a-u)*n+w),b.round(t-t/4),r.get("whiskerColor")).append(),o.drawLine(b.round((j-u)*n+w),b.round(t/2),b.round((h-u)*n+w),b.round(t/2),r.get("lineColor")).append(),o.drawLine(b.round((j-u)*n+w),b.round(t/4),b.round((j-u)*n+w),b.round(t-t/4),r.get("whiskerColor")).append(),o.drawLine(b.round((g-u)*n+w),b.round(.1*t),b.round((g-u)*n+w),b.round(.9*t),r.get("medianColor")).append(),r.get("target")&&(m=b.ceil(r.get("spotRadius")),o.drawLine(b.round((r.get("target")-u)*n+w),b.round(t/2-m),b.round((r.get("target")-u)*n+w),b.round(t/2+m),r.get("targetColor")).append(),o.drawLine(b.round((r.get("target")-u)*n+w-m),b.round(t/2),b.round((r.get("target")-u)*n+w+m),b.round(t/2),r.get("targetColor")).append()),o.render()}}}),F=f({init:function(a,b,c,d){this.target=a,this.id=b,this.type=c,this.args=d},append:function(){return this.target.appendShape(this),this}}),G=f({_pxregex:/(\d+)(px)?\s*$/i,init:function(a,b,c){a&&(this.width=a,this.height=b,this.target=c,this.lastShapeId=null,c[0]&&(c=c[0]),d.data(c,"_jqs_vcanvas",this))},drawLine:function(a,b,c,d,e,f){return this.drawShape([[a,b],[c,d]],e,f)},drawShape:function(a,b,c,d){return this._genShape("Shape",[a,b,c,d])},drawCircle:function(a,b,c,d,e,f){return this._genShape("Circle",[a,b,c,d,e,f])},drawPieSlice:function(a,b,c,d,e,f,g){return this._genShape("PieSlice",[a,b,c,d,e,f,g])},drawRect:function(a,b,c,d,e,f){return this._genShape("Rect",[a,b,c,d,e,f])},getElement:function(){return this.canvas},getLastShapeId:function(){return this.lastShapeId},reset:function(){alert("reset not implemented")},_insert:function(a,b){d(b).html(a)},_calculatePixelDims:function(a,b,c){var e;e=this._pxregex.exec(b),this.pixelHeight=e?e[1]:d(c).height(),e=this._pxregex.exec(a),this.pixelWidth=e?e[1]:d(c).width()},_genShape:function(a,b){var c=L++;return b.unshift(c),new F(this,c,a,b)},appendShape:function(){alert("appendShape not implemented")},replaceWithShape:function(){alert("replaceWithShape not implemented")},insertAfterShape:function(){alert("insertAfterShape not implemented")},removeShapeId:function(){alert("removeShapeId not implemented")},getShapeAt:function(){alert("getShapeAt not implemented")},render:function(){alert("render not implemented")}}),H=f(G,{init:function(b,e,f,g){H._super.init.call(this,b,e,f),this.canvas=a.createElement("canvas"),f[0]&&(f=f[0]),this.context=this.canvas.getContext("2d");var h=window.devicePixelRatio||1,i=this.context.webkitBackingStorePixelRatio||this.context.mozBackingStorePixelRatio||this.context.msBackingStorePixelRatio||this.context.oBackingStorePixelRatio||this.context.backingStorePixelRatio||1,j=h/i;d.data(f,"_jqs_vcanvas",this),d(this.canvas).css({display:"inline-block",width:b,height:e,verticalAlign:"top"}),this._insert(this.canvas,f),this._calculatePixelDims(b,e,this.canvas),this.canvas.width=this.pixelWidth*j,this.canvas.height=this.pixelHeight*j,this.context.scale(j,j),this.interact=g,this.shapes={},this.shapeseq=[],this.currentTargetShapeId=c,d(this.canvas).css({width:this.pixelWidth,height:this.pixelHeight})},_getContext:function(a,b,d){var e=this.canvas.getContext("2d");return a!==c&&(e.strokeStyle=a),e.lineWidth=d===c?1:d,b!==c&&(e.fillStyle=b),e},reset:function(){var a=this._getContext();a.clearRect(0,0,this.pixelWidth,this.pixelHeight),this.shapes={},this.shapeseq=[],this.currentTargetShapeId=c},_drawShape:function(a,b,d,e,f){var g,h,i=this._getContext(d,e,f);for(i.beginPath(),i.moveTo(b[0][0]+.5,b[0][1]+.5),g=1,h=b.length;h>g;g++)i.lineTo(b[g][0]+.5,b[g][1]+.5);d!==c&&i.stroke(),e!==c&&i.fill(),this.targetX!==c&&this.targetY!==c&&i.isPointInPath(this.targetX,this.targetY)&&(this.currentTargetShapeId=a)},_drawCircle:function(a,d,e,f,g,h,i){var j=this._getContext(g,h,i);j.beginPath(),j.arc(d,e,f,0,2*b.PI,!1),this.targetX!==c&&this.targetY!==c&&j.isPointInPath(this.targetX,this.targetY)&&(this.currentTargetShapeId=a),g!==c&&j.stroke(),h!==c&&j.fill()},_drawPieSlice:function(a,b,d,e,f,g,h,i){var j=this._getContext(h,i);j.beginPath(),j.moveTo(b,d),j.arc(b,d,e,f,g,!1),j.lineTo(b,d),j.closePath(),h!==c&&j.stroke(),i&&j.fill(),this.targetX!==c&&this.targetY!==c&&j.isPointInPath(this.targetX,this.targetY)&&(this.currentTargetShapeId=a)},_drawRect:function(a,b,c,d,e,f,g){return this._drawShape(a,[[b,c],[b+d,c],[b+d,c+e],[b,c+e],[b,c]],f,g)},appendShape:function(a){return this.shapes[a.id]=a,this.shapeseq.push(a.id),this.lastShapeId=a.id,a.id},replaceWithShape:function(a,b){var c,d=this.shapeseq;for(this.shapes[b.id]=b,c=d.length;c--;)d[c]==a&&(d[c]=b.id);delete this.shapes[a]},replaceWithShapes:function(a,b){var c,d,e,f=this.shapeseq,g={};for(d=a.length;d--;)g[a[d]]=!0;for(d=f.length;d--;)c=f[d],g[c]&&(f.splice(d,1),delete this.shapes[c],e=d);for(d=b.length;d--;)f.splice(e,0,b[d].id),this.shapes[b[d].id]=b[d]},insertAfterShape:function(a,b){var c,d=this.shapeseq;for(c=d.length;c--;)if(d[c]===a)return d.splice(c+1,0,b.id),void(this.shapes[b.id]=b)},removeShapeId:function(a){var b,c=this.shapeseq;for(b=c.length;b--;)if(c[b]===a){c.splice(b,1);break}delete this.shapes[a]},getShapeAt:function(a,b,c){return this.targetX=b,this.targetY=c,this.render(),this.currentTargetShapeId},render:function(){var a,b,c,d=this.shapeseq,e=this.shapes,f=d.length,g=this._getContext();for(g.clearRect(0,0,this.pixelWidth,this.pixelHeight),c=0;f>c;c++)a=d[c],b=e[a],this["_draw"+b.type].apply(this,b.args);this.interact||(this.shapes={},this.shapeseq=[])}}),I=f(G,{init:function(b,c,e){var f;I._super.init.call(this,b,c,e),e[0]&&(e=e[0]),d.data(e,"_jqs_vcanvas",this),this.canvas=a.createElement("span"),d(this.canvas).css({display:"inline-block",position:"relative",overflow:"hidden",width:b,height:c,margin:"0px",padding:"0px",verticalAlign:"top"}),this._insert(this.canvas,e),this._calculatePixelDims(b,c,this.canvas),this.canvas.width=this.pixelWidth,this.canvas.height=this.pixelHeight,f='<v:group coordorigin="0 0" coordsize="'+this.pixelWidth+" "+this.pixelHeight+'" style="position:absolute;top:0;left:0;width:'+this.pixelWidth+"px;height="+this.pixelHeight+'px;"></v:group>',this.canvas.insertAdjacentHTML("beforeEnd",f),this.group=d(this.canvas).children()[0],this.rendered=!1,this.prerender=""},_drawShape:function(a,b,d,e,f){var g,h,i,j,k,l,m,n=[];for(m=0,l=b.length;l>m;m++)n[m]=""+b[m][0]+","+b[m][1];return g=n.splice(0,1),f=f===c?1:f,h=d===c?' stroked="false" ':' strokeWeight="'+f+'px" strokeColor="'+d+'" ',i=e===c?' filled="false"':' fillColor="'+e+'" filled="true" ',j=n[0]===n[n.length-1]?"x ":"",k='<v:shape coordorigin="0 0" coordsize="'+this.pixelWidth+" "+this.pixelHeight+'" id="jqsshape'+a+'" '+h+i+' style="position:absolute;left:0px;top:0px;height:'+this.pixelHeight+"px;width:"+this.pixelWidth+'px;padding:0px;margin:0px;" path="m '+g+" l "+n.join(", ")+" "+j+'e"> </v:shape>'},_drawCircle:function(a,b,d,e,f,g,h){var i,j,k;return b-=e,d-=e,i=f===c?' stroked="false" ':' strokeWeight="'+h+'px" strokeColor="'+f+'" ',j=g===c?' filled="false"':' fillColor="'+g+'" filled="true" ',k='<v:oval id="jqsshape'+a+'" '+i+j+' style="position:absolute;top:'+d+"px; left:"+b+"px; width:"+2*e+"px; height:"+2*e+'px"></v:oval>'},_drawPieSlice:function(a,d,e,f,g,h,i,j){var k,l,m,n,o,p,q,r;if(g===h)return"";if(h-g===2*b.PI&&(g=0,h=2*b.PI),l=d+b.round(b.cos(g)*f),m=e+b.round(b.sin(g)*f),n=d+b.round(b.cos(h)*f),o=e+b.round(b.sin(h)*f),l===n&&m===o){if(h-g<b.PI)return"";l=n=d+f,m=o=e}return l===n&&m===o&&h-g<b.PI?"":(k=[d-f,e-f,d+f,e+f,l,m,n,o],p=i===c?' stroked="false" ':' strokeWeight="1px" strokeColor="'+i+'" ',q=j===c?' filled="false"':' fillColor="'+j+'" filled="true" ',r='<v:shape coordorigin="0 0" coordsize="'+this.pixelWidth+" "+this.pixelHeight+'" id="jqsshape'+a+'" '+p+q+' style="position:absolute;left:0px;top:0px;height:'+this.pixelHeight+"px;width:"+this.pixelWidth+'px;padding:0px;margin:0px;" path="m '+d+","+e+" wa "+k.join(", ")+' x e"> </v:shape>')},_drawRect:function(a,b,c,d,e,f,g){return this._drawShape(a,[[b,c],[b,c+e],[b+d,c+e],[b+d,c],[b,c]],f,g)},reset:function(){this.group.innerHTML=""},appendShape:function(a){var b=this["_draw"+a.type].apply(this,a.args);return this.rendered?this.group.insertAdjacentHTML("beforeEnd",b):this.prerender+=b,this.lastShapeId=a.id,a.id},replaceWithShape:function(a,b){var c=d("#jqsshape"+a),e=this["_draw"+b.type].apply(this,b.args);c[0].outerHTML=e},replaceWithShapes:function(a,b){var c,e=d("#jqsshape"+a[0]),f="",g=b.length;for(c=0;g>c;c++)f+=this["_draw"+b[c].type].apply(this,b[c].args);for(e[0].outerHTML=f,c=1;c<a.length;c++)d("#jqsshape"+a[c]).remove()},insertAfterShape:function(a,b){var c=d("#jqsshape"+a),e=this["_draw"+b.type].apply(this,b.args);c[0].insertAdjacentHTML("afterEnd",e)},removeShapeId:function(a){var b=d("#jqsshape"+a);this.group.removeChild(b[0])},getShapeAt:function(a){var b=a.id.substr(8);return b},render:function(){this.rendered||(this.group.innerHTML=this.prerender,this.rendered=!0)}})})}(document,Math);
@@ -0,0 +1,185 @@
1
+ 'use strict';
2
+
3
+ /*
4
+ * AngularJS Toaster
5
+ * Version: 0.4.7
6
+ *
7
+ * Copyright 2013 Jiri Kavulak.
8
+ * All Rights Reserved.
9
+ * Use, reproduction, distribution, and modification of this code is subject to the terms and
10
+ * conditions of the MIT license, available at http://www.opensource.org/licenses/mit-license.php
11
+ *
12
+ * Author: Jiri Kavulak
13
+ * Related to project of John Papa and Hans Fjällemark
14
+ */
15
+
16
+ angular.module('toaster', ['ngAnimate'])
17
+ .service('toaster', ['$rootScope', function ($rootScope) {
18
+ this.pop = function (type, title, body, timeout, bodyOutputType, clickHandler) {
19
+ this.toast = {
20
+ type: type,
21
+ title: title,
22
+ body: body,
23
+ timeout: timeout,
24
+ bodyOutputType: bodyOutputType,
25
+ clickHandler: clickHandler
26
+ };
27
+ $rootScope.$broadcast('toaster-newToast');
28
+ };
29
+
30
+ this.clear = function () {
31
+ $rootScope.$broadcast('toaster-clearToasts');
32
+ };
33
+ }])
34
+ .constant('toasterConfig', {
35
+ 'limit': 0, // limits max number of toasts
36
+ 'tap-to-dismiss': true,
37
+ 'close-button': false,
38
+ 'newest-on-top': true,
39
+ //'fade-in': 1000, // done in css
40
+ //'on-fade-in': undefined, // not implemented
41
+ //'fade-out': 1000, // done in css
42
+ // 'on-fade-out': undefined, // not implemented
43
+ //'extended-time-out': 1000, // not implemented
44
+ 'time-out': 5000, // Set timeOut and extendedTimeout to 0 to make it sticky
45
+ 'icon-classes': {
46
+ error: 'toast-error',
47
+ info: 'toast-info',
48
+ wait: 'toast-wait',
49
+ success: 'toast-success',
50
+ warning: 'toast-warning'
51
+ },
52
+ 'body-output-type': '', // Options: '', 'trustedHtml', 'template'
53
+ 'body-template': 'toasterBodyTmpl.html',
54
+ 'icon-class': 'toast-info',
55
+ 'position-class': 'toast-top-right',
56
+ 'title-class': 'toast-title',
57
+ 'message-class': 'toast-message'
58
+ })
59
+ .directive('toasterContainer', ['$compile', '$timeout', '$sce', 'toasterConfig', 'toaster',
60
+ function ($compile, $timeout, $sce, toasterConfig, toaster) {
61
+ return {
62
+ replace: true,
63
+ restrict: 'EA',
64
+ scope: true, // creates an internal scope for this directive
65
+ link: function (scope, elm, attrs) {
66
+
67
+ var id = 0,
68
+ mergedConfig;
69
+
70
+ mergedConfig = angular.extend({}, toasterConfig, scope.$eval(attrs.toasterOptions));
71
+
72
+ scope.config = {
73
+ position: mergedConfig['position-class'],
74
+ title: mergedConfig['title-class'],
75
+ message: mergedConfig['message-class'],
76
+ tap: mergedConfig['tap-to-dismiss'],
77
+ closeButton: mergedConfig['close-button']
78
+ };
79
+
80
+ scope.configureTimer = function configureTimer(toast) {
81
+ var timeout = typeof (toast.timeout) == "number" ? toast.timeout : mergedConfig['time-out'];
82
+ if (timeout > 0)
83
+ setTimeout(toast, timeout);
84
+ };
85
+
86
+ function addToast(toast) {
87
+ toast.type = mergedConfig['icon-classes'][toast.type];
88
+ if (!toast.type)
89
+ toast.type = mergedConfig['icon-class'];
90
+
91
+ id++;
92
+ angular.extend(toast, { id: id });
93
+
94
+ // Set the toast.bodyOutputType to the default if it isn't set
95
+ toast.bodyOutputType = toast.bodyOutputType || mergedConfig['body-output-type'];
96
+ switch (toast.bodyOutputType) {
97
+ case 'trustedHtml':
98
+ toast.html = $sce.trustAsHtml(toast.body);
99
+ break;
100
+ case 'template':
101
+ toast.bodyTemplate = toast.body || mergedConfig['body-template'];
102
+ break;
103
+ }
104
+
105
+ scope.configureTimer(toast);
106
+
107
+ if (mergedConfig['newest-on-top'] === true) {
108
+ scope.toasters.unshift(toast);
109
+ if (mergedConfig['limit'] > 0 && scope.toasters.length > mergedConfig['limit']) {
110
+ scope.toasters.pop();
111
+ }
112
+ } else {
113
+ scope.toasters.push(toast);
114
+ if (mergedConfig['limit'] > 0 && scope.toasters.length > mergedConfig['limit']) {
115
+ scope.toasters.shift();
116
+ }
117
+ }
118
+ }
119
+
120
+ function setTimeout(toast, time) {
121
+ toast.timeout = $timeout(function () {
122
+ scope.removeToast(toast.id);
123
+ }, time);
124
+ }
125
+
126
+ scope.toasters = [];
127
+ scope.$on('toaster-newToast', function () {
128
+ addToast(toaster.toast);
129
+ });
130
+
131
+ scope.$on('toaster-clearToasts', function () {
132
+ scope.toasters.splice(0, scope.toasters.length);
133
+ });
134
+ },
135
+ controller: ['$scope', '$element', '$attrs', function ($scope, $element, $attrs) {
136
+
137
+ $scope.stopTimer = function (toast) {
138
+ if (toast.timeout) {
139
+ $timeout.cancel(toast.timeout);
140
+ toast.timeout = null;
141
+ }
142
+ };
143
+
144
+ $scope.restartTimer = function (toast) {
145
+ if (!toast.timeout)
146
+ $scope.configureTimer(toast);
147
+ };
148
+
149
+ $scope.removeToast = function (id) {
150
+ var i = 0;
151
+ for (i; i < $scope.toasters.length; i++) {
152
+ if ($scope.toasters[i].id === id)
153
+ break;
154
+ }
155
+ $scope.toasters.splice(i, 1);
156
+ };
157
+
158
+ $scope.click = function (toaster) {
159
+ if ($scope.config.tap === true) {
160
+ if (toaster.clickHandler && angular.isFunction($scope.$parent.$eval(toaster.clickHandler))) {
161
+ var result = $scope.$parent.$eval(toaster.clickHandler)(toaster);
162
+ if (result === true)
163
+ $scope.removeToast(toaster.id);
164
+ } else {
165
+ if (angular.isString(toaster.clickHandler))
166
+ console.log("TOAST-NOTE: Your click handler is not inside a parent scope of toaster-container.");
167
+ $scope.removeToast(toaster.id);
168
+ }
169
+ }
170
+ };
171
+ }],
172
+ template:
173
+ '<div id="toast-container" ng-class="config.position">' +
174
+ '<div ng-repeat="toaster in toasters" class="toast" ng-class="toaster.type" ng-click="click(toaster)" ng-mouseover="stopTimer(toaster)" ng-mouseout="restartTimer(toaster)">' +
175
+ '<button class="toast-close-button" ng-show="config.closeButton">&times;</button>' +
176
+ '<div ng-class="config.title">{{toaster.title}}</div>' +
177
+ '<div ng-class="config.message" ng-switch on="toaster.bodyOutputType">' +
178
+ '<div ng-switch-when="trustedHtml" ng-bind-html="toaster.html"></div>' +
179
+ '<div ng-switch-when="template"><div ng-include="toaster.bodyTemplate"></div></div>' +
180
+ '<div ng-switch-default >{{toaster.body}}</div>' +
181
+ '</div>' +
182
+ '</div>' +
183
+ '</div>'
184
+ };
185
+ }]);
@@ -0,0 +1,4116 @@
1
+ /*
2
+ * angular-ui-bootstrap
3
+ * http://angular-ui.github.io/bootstrap/
4
+
5
+ * Version: 0.11.0 - 2014-05-01
6
+ * License: MIT
7
+ */
8
+ angular.module("ui.bootstrap", ["ui.bootstrap.tpls", "ui.bootstrap.transition","ui.bootstrap.collapse","ui.bootstrap.accordion","ui.bootstrap.alert","ui.bootstrap.bindHtml","ui.bootstrap.buttons","ui.bootstrap.carousel","ui.bootstrap.dateparser","ui.bootstrap.position","ui.bootstrap.datepicker","ui.bootstrap.dropdown","ui.bootstrap.modal","ui.bootstrap.pagination","ui.bootstrap.tooltip","ui.bootstrap.popover","ui.bootstrap.progressbar","ui.bootstrap.rating","ui.bootstrap.tabs","ui.bootstrap.timepicker","ui.bootstrap.typeahead"]);
9
+ angular.module("ui.bootstrap.tpls", ["template/accordion/accordion-group.html","template/accordion/accordion.html","template/alert/alert.html","template/carousel/carousel.html","template/carousel/slide.html","template/datepicker/datepicker.html","template/datepicker/day.html","template/datepicker/month.html","template/datepicker/popup.html","template/datepicker/year.html","template/modal/backdrop.html","template/modal/window.html","template/pagination/pager.html","template/pagination/pagination.html","template/tooltip/tooltip-html-unsafe-popup.html","template/tooltip/tooltip-popup.html","template/popover/popover.html","template/progressbar/bar.html","template/progressbar/progress.html","template/progressbar/progressbar.html","template/rating/rating.html","template/tabs/tab.html","template/tabs/tabset.html","template/timepicker/timepicker.html","template/typeahead/typeahead-match.html","template/typeahead/typeahead-popup.html"]);
10
+ angular.module('ui.bootstrap.transition', [])
11
+
12
+ /**
13
+ * $transition service provides a consistent interface to trigger CSS 3 transitions and to be informed when they complete.
14
+ * @param {DOMElement} element The DOMElement that will be animated.
15
+ * @param {string|object|function} trigger The thing that will cause the transition to start:
16
+ * - As a string, it represents the css class to be added to the element.
17
+ * - As an object, it represents a hash of style attributes to be applied to the element.
18
+ * - As a function, it represents a function to be called that will cause the transition to occur.
19
+ * @return {Promise} A promise that is resolved when the transition finishes.
20
+ */
21
+ .factory('$transition', ['$q', '$timeout', '$rootScope', function($q, $timeout, $rootScope) {
22
+
23
+ var $transition = function(element, trigger, options) {
24
+ options = options || {};
25
+ var deferred = $q.defer();
26
+ var endEventName = $transition[options.animation ? 'animationEndEventName' : 'transitionEndEventName'];
27
+
28
+ var transitionEndHandler = function(event) {
29
+ $rootScope.$apply(function() {
30
+ element.unbind(endEventName, transitionEndHandler);
31
+ deferred.resolve(element);
32
+ });
33
+ };
34
+
35
+ if (endEventName) {
36
+ element.bind(endEventName, transitionEndHandler);
37
+ }
38
+
39
+ // Wrap in a timeout to allow the browser time to update the DOM before the transition is to occur
40
+ $timeout(function() {
41
+ if ( angular.isString(trigger) ) {
42
+ element.addClass(trigger);
43
+ } else if ( angular.isFunction(trigger) ) {
44
+ trigger(element);
45
+ } else if ( angular.isObject(trigger) ) {
46
+ element.css(trigger);
47
+ }
48
+ //If browser does not support transitions, instantly resolve
49
+ if ( !endEventName ) {
50
+ deferred.resolve(element);
51
+ }
52
+ });
53
+
54
+ // Add our custom cancel function to the promise that is returned
55
+ // We can call this if we are about to run a new transition, which we know will prevent this transition from ending,
56
+ // i.e. it will therefore never raise a transitionEnd event for that transition
57
+ deferred.promise.cancel = function() {
58
+ if ( endEventName ) {
59
+ element.unbind(endEventName, transitionEndHandler);
60
+ }
61
+ deferred.reject('Transition cancelled');
62
+ };
63
+
64
+ return deferred.promise;
65
+ };
66
+
67
+ // Work out the name of the transitionEnd event
68
+ var transElement = document.createElement('trans');
69
+ var transitionEndEventNames = {
70
+ 'WebkitTransition': 'webkitTransitionEnd',
71
+ 'MozTransition': 'transitionend',
72
+ 'OTransition': 'oTransitionEnd',
73
+ 'transition': 'transitionend'
74
+ };
75
+ var animationEndEventNames = {
76
+ 'WebkitTransition': 'webkitAnimationEnd',
77
+ 'MozTransition': 'animationend',
78
+ 'OTransition': 'oAnimationEnd',
79
+ 'transition': 'animationend'
80
+ };
81
+ function findEndEventName(endEventNames) {
82
+ for (var name in endEventNames){
83
+ if (transElement.style[name] !== undefined) {
84
+ return endEventNames[name];
85
+ }
86
+ }
87
+ }
88
+ $transition.transitionEndEventName = findEndEventName(transitionEndEventNames);
89
+ $transition.animationEndEventName = findEndEventName(animationEndEventNames);
90
+ return $transition;
91
+ }]);
92
+
93
+ angular.module('ui.bootstrap.collapse', ['ui.bootstrap.transition'])
94
+
95
+ .directive('collapse', ['$transition', function ($transition) {
96
+
97
+ return {
98
+ link: function (scope, element, attrs) {
99
+
100
+ var initialAnimSkip = true;
101
+ var currentTransition;
102
+
103
+ function doTransition(change) {
104
+ var newTransition = $transition(element, change);
105
+ if (currentTransition) {
106
+ currentTransition.cancel();
107
+ }
108
+ currentTransition = newTransition;
109
+ newTransition.then(newTransitionDone, newTransitionDone);
110
+ return newTransition;
111
+
112
+ function newTransitionDone() {
113
+ // Make sure it's this transition, otherwise, leave it alone.
114
+ if (currentTransition === newTransition) {
115
+ currentTransition = undefined;
116
+ }
117
+ }
118
+ }
119
+
120
+ function expand() {
121
+ if (initialAnimSkip) {
122
+ initialAnimSkip = false;
123
+ expandDone();
124
+ } else {
125
+ element.removeClass('collapse').addClass('collapsing');
126
+ doTransition({ height: element[0].scrollHeight + 'px' }).then(expandDone);
127
+ }
128
+ }
129
+
130
+ function expandDone() {
131
+ element.removeClass('collapsing');
132
+ element.addClass('collapse in');
133
+ element.css({height: 'auto'});
134
+ }
135
+
136
+ function collapse() {
137
+ if (initialAnimSkip) {
138
+ initialAnimSkip = false;
139
+ collapseDone();
140
+ element.css({height: 0});
141
+ } else {
142
+ // CSS transitions don't work with height: auto, so we have to manually change the height to a specific value
143
+ element.css({ height: element[0].scrollHeight + 'px' });
144
+ //trigger reflow so a browser realizes that height was updated from auto to a specific value
145
+ var x = element[0].offsetWidth;
146
+
147
+ element.removeClass('collapse in').addClass('collapsing');
148
+
149
+ doTransition({ height: 0 }).then(collapseDone);
150
+ }
151
+ }
152
+
153
+ function collapseDone() {
154
+ element.removeClass('collapsing');
155
+ element.addClass('collapse');
156
+ }
157
+
158
+ scope.$watch(attrs.collapse, function (shouldCollapse) {
159
+ if (shouldCollapse) {
160
+ collapse();
161
+ } else {
162
+ expand();
163
+ }
164
+ });
165
+ }
166
+ };
167
+ }]);
168
+
169
+ angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])
170
+
171
+ .constant('accordionConfig', {
172
+ closeOthers: true
173
+ })
174
+
175
+ .controller('AccordionController', ['$scope', '$attrs', 'accordionConfig', function ($scope, $attrs, accordionConfig) {
176
+
177
+ // This array keeps track of the accordion groups
178
+ this.groups = [];
179
+
180
+ // Ensure that all the groups in this accordion are closed, unless close-others explicitly says not to
181
+ this.closeOthers = function(openGroup) {
182
+ var closeOthers = angular.isDefined($attrs.closeOthers) ? $scope.$eval($attrs.closeOthers) : accordionConfig.closeOthers;
183
+ if ( closeOthers ) {
184
+ angular.forEach(this.groups, function (group) {
185
+ if ( group !== openGroup ) {
186
+ group.isOpen = false;
187
+ }
188
+ });
189
+ }
190
+ };
191
+
192
+ // This is called from the accordion-group directive to add itself to the accordion
193
+ this.addGroup = function(groupScope) {
194
+ var that = this;
195
+ this.groups.push(groupScope);
196
+
197
+ groupScope.$on('$destroy', function (event) {
198
+ that.removeGroup(groupScope);
199
+ });
200
+ };
201
+
202
+ // This is called from the accordion-group directive when to remove itself
203
+ this.removeGroup = function(group) {
204
+ var index = this.groups.indexOf(group);
205
+ if ( index !== -1 ) {
206
+ this.groups.splice(index, 1);
207
+ }
208
+ };
209
+
210
+ }])
211
+
212
+ // The accordion directive simply sets up the directive controller
213
+ // and adds an accordion CSS class to itself element.
214
+ .directive('accordion', function () {
215
+ return {
216
+ restrict:'EA',
217
+ controller:'AccordionController',
218
+ transclude: true,
219
+ replace: false,
220
+ templateUrl: 'template/accordion/accordion.html'
221
+ };
222
+ })
223
+
224
+ // The accordion-group directive indicates a block of html that will expand and collapse in an accordion
225
+ .directive('accordionGroup', function() {
226
+ return {
227
+ require:'^accordion', // We need this directive to be inside an accordion
228
+ restrict:'EA',
229
+ transclude:true, // It transcludes the contents of the directive into the template
230
+ replace: true, // The element containing the directive will be replaced with the template
231
+ templateUrl:'template/accordion/accordion-group.html',
232
+ scope: {
233
+ heading: '@', // Interpolate the heading attribute onto this scope
234
+ isOpen: '=?',
235
+ isDisabled: '=?'
236
+ },
237
+ controller: function() {
238
+ this.setHeading = function(element) {
239
+ this.heading = element;
240
+ };
241
+ },
242
+ link: function(scope, element, attrs, accordionCtrl) {
243
+ accordionCtrl.addGroup(scope);
244
+
245
+ scope.$watch('isOpen', function(value) {
246
+ if ( value ) {
247
+ accordionCtrl.closeOthers(scope);
248
+ }
249
+ });
250
+
251
+ scope.toggleOpen = function() {
252
+ if ( !scope.isDisabled ) {
253
+ scope.isOpen = !scope.isOpen;
254
+ }
255
+ };
256
+ }
257
+ };
258
+ })
259
+
260
+ // Use accordion-heading below an accordion-group to provide a heading containing HTML
261
+ // <accordion-group>
262
+ // <accordion-heading>Heading containing HTML - <img src="..."></accordion-heading>
263
+ // </accordion-group>
264
+ .directive('accordionHeading', function() {
265
+ return {
266
+ restrict: 'EA',
267
+ transclude: true, // Grab the contents to be used as the heading
268
+ template: '', // In effect remove this element!
269
+ replace: true,
270
+ require: '^accordionGroup',
271
+ link: function(scope, element, attr, accordionGroupCtrl, transclude) {
272
+ // Pass the heading to the accordion-group controller
273
+ // so that it can be transcluded into the right place in the template
274
+ // [The second parameter to transclude causes the elements to be cloned so that they work in ng-repeat]
275
+ accordionGroupCtrl.setHeading(transclude(scope, function() {}));
276
+ }
277
+ };
278
+ })
279
+
280
+ // Use in the accordion-group template to indicate where you want the heading to be transcluded
281
+ // You must provide the property on the accordion-group controller that will hold the transcluded element
282
+ // <div class="accordion-group">
283
+ // <div class="accordion-heading" ><a ... accordion-transclude="heading">...</a></div>
284
+ // ...
285
+ // </div>
286
+ .directive('accordionTransclude', function() {
287
+ return {
288
+ require: '^accordionGroup',
289
+ link: function(scope, element, attr, controller) {
290
+ scope.$watch(function() { return controller[attr.accordionTransclude]; }, function(heading) {
291
+ if ( heading ) {
292
+ element.html('');
293
+ element.append(heading);
294
+ }
295
+ });
296
+ }
297
+ };
298
+ });
299
+
300
+ angular.module('ui.bootstrap.alert', [])
301
+
302
+ .controller('AlertController', ['$scope', '$attrs', function ($scope, $attrs) {
303
+ $scope.closeable = 'close' in $attrs;
304
+ }])
305
+
306
+ .directive('alert', function () {
307
+ return {
308
+ restrict:'EA',
309
+ controller:'AlertController',
310
+ templateUrl:'template/alert/alert.html',
311
+ transclude:true,
312
+ replace:true,
313
+ scope: {
314
+ type: '@',
315
+ close: '&'
316
+ }
317
+ };
318
+ });
319
+
320
+ angular.module('ui.bootstrap.bindHtml', [])
321
+
322
+ .directive('bindHtmlUnsafe', function () {
323
+ return function (scope, element, attr) {
324
+ element.addClass('ng-binding').data('$binding', attr.bindHtmlUnsafe);
325
+ scope.$watch(attr.bindHtmlUnsafe, function bindHtmlUnsafeWatchAction(value) {
326
+ element.html(value || '');
327
+ });
328
+ };
329
+ });
330
+ angular.module('ui.bootstrap.buttons', [])
331
+
332
+ .constant('buttonConfig', {
333
+ activeClass: 'active',
334
+ toggleEvent: 'click'
335
+ })
336
+
337
+ .controller('ButtonsController', ['buttonConfig', function(buttonConfig) {
338
+ this.activeClass = buttonConfig.activeClass || 'active';
339
+ this.toggleEvent = buttonConfig.toggleEvent || 'click';
340
+ }])
341
+
342
+ .directive('btnRadio', function () {
343
+ return {
344
+ require: ['btnRadio', 'ngModel'],
345
+ controller: 'ButtonsController',
346
+ link: function (scope, element, attrs, ctrls) {
347
+ var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1];
348
+
349
+ //model -> UI
350
+ ngModelCtrl.$render = function () {
351
+ element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, scope.$eval(attrs.btnRadio)));
352
+ };
353
+
354
+ //ui->model
355
+ element.bind(buttonsCtrl.toggleEvent, function () {
356
+ var isActive = element.hasClass(buttonsCtrl.activeClass);
357
+
358
+ if (!isActive || angular.isDefined(attrs.uncheckable)) {
359
+ scope.$apply(function () {
360
+ ngModelCtrl.$setViewValue(isActive ? null : scope.$eval(attrs.btnRadio));
361
+ ngModelCtrl.$render();
362
+ });
363
+ }
364
+ });
365
+ }
366
+ };
367
+ })
368
+
369
+ .directive('btnCheckbox', function () {
370
+ return {
371
+ require: ['btnCheckbox', 'ngModel'],
372
+ controller: 'ButtonsController',
373
+ link: function (scope, element, attrs, ctrls) {
374
+ var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1];
375
+
376
+ function getTrueValue() {
377
+ return getCheckboxValue(attrs.btnCheckboxTrue, true);
378
+ }
379
+
380
+ function getFalseValue() {
381
+ return getCheckboxValue(attrs.btnCheckboxFalse, false);
382
+ }
383
+
384
+ function getCheckboxValue(attributeValue, defaultValue) {
385
+ var val = scope.$eval(attributeValue);
386
+ return angular.isDefined(val) ? val : defaultValue;
387
+ }
388
+
389
+ //model -> UI
390
+ ngModelCtrl.$render = function () {
391
+ element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, getTrueValue()));
392
+ };
393
+
394
+ //ui->model
395
+ element.bind(buttonsCtrl.toggleEvent, function () {
396
+ scope.$apply(function () {
397
+ ngModelCtrl.$setViewValue(element.hasClass(buttonsCtrl.activeClass) ? getFalseValue() : getTrueValue());
398
+ ngModelCtrl.$render();
399
+ });
400
+ });
401
+ }
402
+ };
403
+ });
404
+
405
+ /**
406
+ * @ngdoc overview
407
+ * @name ui.bootstrap.carousel
408
+ *
409
+ * @description
410
+ * AngularJS version of an image carousel.
411
+ *
412
+ */
413
+ angular.module('ui.bootstrap.carousel', ['ui.bootstrap.transition'])
414
+ .controller('CarouselController', ['$scope', '$timeout', '$transition', function ($scope, $timeout, $transition) {
415
+ var self = this,
416
+ slides = self.slides = $scope.slides = [],
417
+ currentIndex = -1,
418
+ currentTimeout, isPlaying;
419
+ self.currentSlide = null;
420
+
421
+ var destroyed = false;
422
+ /* direction: "prev" or "next" */
423
+ self.select = $scope.select = function(nextSlide, direction) {
424
+ var nextIndex = slides.indexOf(nextSlide);
425
+ //Decide direction if it's not given
426
+ if (direction === undefined) {
427
+ direction = nextIndex > currentIndex ? 'next' : 'prev';
428
+ }
429
+ if (nextSlide && nextSlide !== self.currentSlide) {
430
+ if ($scope.$currentTransition) {
431
+ $scope.$currentTransition.cancel();
432
+ //Timeout so ng-class in template has time to fix classes for finished slide
433
+ $timeout(goNext);
434
+ } else {
435
+ goNext();
436
+ }
437
+ }
438
+ function goNext() {
439
+ // Scope has been destroyed, stop here.
440
+ if (destroyed) { return; }
441
+ //If we have a slide to transition from and we have a transition type and we're allowed, go
442
+ if (self.currentSlide && angular.isString(direction) && !$scope.noTransition && nextSlide.$element) {
443
+ //We shouldn't do class manip in here, but it's the same weird thing bootstrap does. need to fix sometime
444
+ nextSlide.$element.addClass(direction);
445
+ var reflow = nextSlide.$element[0].offsetWidth; //force reflow
446
+
447
+ //Set all other slides to stop doing their stuff for the new transition
448
+ angular.forEach(slides, function(slide) {
449
+ angular.extend(slide, {direction: '', entering: false, leaving: false, active: false});
450
+ });
451
+ angular.extend(nextSlide, {direction: direction, active: true, entering: true});
452
+ angular.extend(self.currentSlide||{}, {direction: direction, leaving: true});
453
+
454
+ $scope.$currentTransition = $transition(nextSlide.$element, {});
455
+ //We have to create new pointers inside a closure since next & current will change
456
+ (function(next,current) {
457
+ $scope.$currentTransition.then(
458
+ function(){ transitionDone(next, current); },
459
+ function(){ transitionDone(next, current); }
460
+ );
461
+ }(nextSlide, self.currentSlide));
462
+ } else {
463
+ transitionDone(nextSlide, self.currentSlide);
464
+ }
465
+ self.currentSlide = nextSlide;
466
+ currentIndex = nextIndex;
467
+ //every time you change slides, reset the timer
468
+ restartTimer();
469
+ }
470
+ function transitionDone(next, current) {
471
+ angular.extend(next, {direction: '', active: true, leaving: false, entering: false});
472
+ angular.extend(current||{}, {direction: '', active: false, leaving: false, entering: false});
473
+ $scope.$currentTransition = null;
474
+ }
475
+ };
476
+ $scope.$on('$destroy', function () {
477
+ destroyed = true;
478
+ });
479
+
480
+ /* Allow outside people to call indexOf on slides array */
481
+ self.indexOfSlide = function(slide) {
482
+ return slides.indexOf(slide);
483
+ };
484
+
485
+ $scope.next = function() {
486
+ var newIndex = (currentIndex + 1) % slides.length;
487
+
488
+ //Prevent this user-triggered transition from occurring if there is already one in progress
489
+ if (!$scope.$currentTransition) {
490
+ return self.select(slides[newIndex], 'next');
491
+ }
492
+ };
493
+
494
+ $scope.prev = function() {
495
+ var newIndex = currentIndex - 1 < 0 ? slides.length - 1 : currentIndex - 1;
496
+
497
+ //Prevent this user-triggered transition from occurring if there is already one in progress
498
+ if (!$scope.$currentTransition) {
499
+ return self.select(slides[newIndex], 'prev');
500
+ }
501
+ };
502
+
503
+ $scope.isActive = function(slide) {
504
+ return self.currentSlide === slide;
505
+ };
506
+
507
+ $scope.$watch('interval', restartTimer);
508
+ $scope.$on('$destroy', resetTimer);
509
+
510
+ function restartTimer() {
511
+ resetTimer();
512
+ var interval = +$scope.interval;
513
+ if (!isNaN(interval) && interval>=0) {
514
+ currentTimeout = $timeout(timerFn, interval);
515
+ }
516
+ }
517
+
518
+ function resetTimer() {
519
+ if (currentTimeout) {
520
+ $timeout.cancel(currentTimeout);
521
+ currentTimeout = null;
522
+ }
523
+ }
524
+
525
+ function timerFn() {
526
+ if (isPlaying) {
527
+ $scope.next();
528
+ restartTimer();
529
+ } else {
530
+ $scope.pause();
531
+ }
532
+ }
533
+
534
+ $scope.play = function() {
535
+ if (!isPlaying) {
536
+ isPlaying = true;
537
+ restartTimer();
538
+ }
539
+ };
540
+ $scope.pause = function() {
541
+ if (!$scope.noPause) {
542
+ isPlaying = false;
543
+ resetTimer();
544
+ }
545
+ };
546
+
547
+ self.addSlide = function(slide, element) {
548
+ slide.$element = element;
549
+ slides.push(slide);
550
+ //if this is the first slide or the slide is set to active, select it
551
+ if(slides.length === 1 || slide.active) {
552
+ self.select(slides[slides.length-1]);
553
+ if (slides.length == 1) {
554
+ $scope.play();
555
+ }
556
+ } else {
557
+ slide.active = false;
558
+ }
559
+ };
560
+
561
+ self.removeSlide = function(slide) {
562
+ //get the index of the slide inside the carousel
563
+ var index = slides.indexOf(slide);
564
+ slides.splice(index, 1);
565
+ if (slides.length > 0 && slide.active) {
566
+ if (index >= slides.length) {
567
+ self.select(slides[index-1]);
568
+ } else {
569
+ self.select(slides[index]);
570
+ }
571
+ } else if (currentIndex > index) {
572
+ currentIndex--;
573
+ }
574
+ };
575
+
576
+ }])
577
+
578
+ /**
579
+ * @ngdoc directive
580
+ * @name ui.bootstrap.carousel.directive:carousel
581
+ * @restrict EA
582
+ *
583
+ * @description
584
+ * Carousel is the outer container for a set of image 'slides' to showcase.
585
+ *
586
+ * @param {number=} interval The time, in milliseconds, that it will take the carousel to go to the next slide.
587
+ * @param {boolean=} noTransition Whether to disable transitions on the carousel.
588
+ * @param {boolean=} noPause Whether to disable pausing on the carousel (by default, the carousel interval pauses on hover).
589
+ *
590
+ * @example
591
+ <example module="ui.bootstrap">
592
+ <file name="index.html">
593
+ <carousel>
594
+ <slide>
595
+ <img src="http://placekitten.com/150/150" style="margin:auto;">
596
+ <div class="carousel-caption">
597
+ <p>Beautiful!</p>
598
+ </div>
599
+ </slide>
600
+ <slide>
601
+ <img src="http://placekitten.com/100/150" style="margin:auto;">
602
+ <div class="carousel-caption">
603
+ <p>D'aww!</p>
604
+ </div>
605
+ </slide>
606
+ </carousel>
607
+ </file>
608
+ <file name="demo.css">
609
+ .carousel-indicators {
610
+ top: auto;
611
+ bottom: 15px;
612
+ }
613
+ </file>
614
+ </example>
615
+ */
616
+ .directive('carousel', [function() {
617
+ return {
618
+ restrict: 'EA',
619
+ transclude: true,
620
+ replace: true,
621
+ controller: 'CarouselController',
622
+ require: 'carousel',
623
+ templateUrl: 'template/carousel/carousel.html',
624
+ scope: {
625
+ interval: '=',
626
+ noTransition: '=',
627
+ noPause: '='
628
+ }
629
+ };
630
+ }])
631
+
632
+ /**
633
+ * @ngdoc directive
634
+ * @name ui.bootstrap.carousel.directive:slide
635
+ * @restrict EA
636
+ *
637
+ * @description
638
+ * Creates a slide inside a {@link ui.bootstrap.carousel.directive:carousel carousel}. Must be placed as a child of a carousel element.
639
+ *
640
+ * @param {boolean=} active Model binding, whether or not this slide is currently active.
641
+ *
642
+ * @example
643
+ <example module="ui.bootstrap">
644
+ <file name="index.html">
645
+ <div ng-controller="CarouselDemoCtrl">
646
+ <carousel>
647
+ <slide ng-repeat="slide in slides" active="slide.active">
648
+ <img ng-src="{{slide.image}}" style="margin:auto;">
649
+ <div class="carousel-caption">
650
+ <h4>Slide {{$index}}</h4>
651
+ <p>{{slide.text}}</p>
652
+ </div>
653
+ </slide>
654
+ </carousel>
655
+ Interval, in milliseconds: <input type="number" ng-model="myInterval">
656
+ <br />Enter a negative number to stop the interval.
657
+ </div>
658
+ </file>
659
+ <file name="script.js">
660
+ function CarouselDemoCtrl($scope) {
661
+ $scope.myInterval = 5000;
662
+ }
663
+ </file>
664
+ <file name="demo.css">
665
+ .carousel-indicators {
666
+ top: auto;
667
+ bottom: 15px;
668
+ }
669
+ </file>
670
+ </example>
671
+ */
672
+
673
+ .directive('slide', function() {
674
+ return {
675
+ require: '^carousel',
676
+ restrict: 'EA',
677
+ transclude: true,
678
+ replace: true,
679
+ templateUrl: 'template/carousel/slide.html',
680
+ scope: {
681
+ active: '=?'
682
+ },
683
+ link: function (scope, element, attrs, carouselCtrl) {
684
+ carouselCtrl.addSlide(scope, element);
685
+ //when the scope is destroyed then remove the slide from the current slides array
686
+ scope.$on('$destroy', function() {
687
+ carouselCtrl.removeSlide(scope);
688
+ });
689
+
690
+ scope.$watch('active', function(active) {
691
+ if (active) {
692
+ carouselCtrl.select(scope);
693
+ }
694
+ });
695
+ }
696
+ };
697
+ });
698
+
699
+ angular.module('ui.bootstrap.dateparser', [])
700
+
701
+ .service('dateParser', ['$locale', 'orderByFilter', function($locale, orderByFilter) {
702
+
703
+ this.parsers = {};
704
+
705
+ var formatCodeToRegex = {
706
+ 'yyyy': {
707
+ regex: '\\d{4}',
708
+ apply: function(value) { this.year = +value; }
709
+ },
710
+ 'yy': {
711
+ regex: '\\d{2}',
712
+ apply: function(value) { this.year = +value + 2000; }
713
+ },
714
+ 'y': {
715
+ regex: '\\d{1,4}',
716
+ apply: function(value) { this.year = +value; }
717
+ },
718
+ 'MMMM': {
719
+ regex: $locale.DATETIME_FORMATS.MONTH.join('|'),
720
+ apply: function(value) { this.month = $locale.DATETIME_FORMATS.MONTH.indexOf(value); }
721
+ },
722
+ 'MMM': {
723
+ regex: $locale.DATETIME_FORMATS.SHORTMONTH.join('|'),
724
+ apply: function(value) { this.month = $locale.DATETIME_FORMATS.SHORTMONTH.indexOf(value); }
725
+ },
726
+ 'MM': {
727
+ regex: '0[1-9]|1[0-2]',
728
+ apply: function(value) { this.month = value - 1; }
729
+ },
730
+ 'M': {
731
+ regex: '[1-9]|1[0-2]',
732
+ apply: function(value) { this.month = value - 1; }
733
+ },
734
+ 'dd': {
735
+ regex: '[0-2][0-9]{1}|3[0-1]{1}',
736
+ apply: function(value) { this.date = +value; }
737
+ },
738
+ 'd': {
739
+ regex: '[1-2]?[0-9]{1}|3[0-1]{1}',
740
+ apply: function(value) { this.date = +value; }
741
+ },
742
+ 'EEEE': {
743
+ regex: $locale.DATETIME_FORMATS.DAY.join('|')
744
+ },
745
+ 'EEE': {
746
+ regex: $locale.DATETIME_FORMATS.SHORTDAY.join('|')
747
+ }
748
+ };
749
+
750
+ this.createParser = function(format) {
751
+ var map = [], regex = format.split('');
752
+
753
+ angular.forEach(formatCodeToRegex, function(data, code) {
754
+ var index = format.indexOf(code);
755
+
756
+ if (index > -1) {
757
+ format = format.split('');
758
+
759
+ regex[index] = '(' + data.regex + ')';
760
+ format[index] = '$'; // Custom symbol to define consumed part of format
761
+ for (var i = index + 1, n = index + code.length; i < n; i++) {
762
+ regex[i] = '';
763
+ format[i] = '$';
764
+ }
765
+ format = format.join('');
766
+
767
+ map.push({ index: index, apply: data.apply });
768
+ }
769
+ });
770
+
771
+ return {
772
+ regex: new RegExp('^' + regex.join('') + '$'),
773
+ map: orderByFilter(map, 'index')
774
+ };
775
+ };
776
+
777
+ this.parse = function(input, format) {
778
+ if ( !angular.isString(input) ) {
779
+ return input;
780
+ }
781
+
782
+ format = $locale.DATETIME_FORMATS[format] || format;
783
+
784
+ if ( !this.parsers[format] ) {
785
+ this.parsers[format] = this.createParser(format);
786
+ }
787
+
788
+ var parser = this.parsers[format],
789
+ regex = parser.regex,
790
+ map = parser.map,
791
+ results = input.match(regex);
792
+
793
+ if ( results && results.length ) {
794
+ var fields = { year: 1900, month: 0, date: 1, hours: 0 }, dt;
795
+
796
+ for( var i = 1, n = results.length; i < n; i++ ) {
797
+ var mapper = map[i-1];
798
+ if ( mapper.apply ) {
799
+ mapper.apply.call(fields, results[i]);
800
+ }
801
+ }
802
+
803
+ if ( isValid(fields.year, fields.month, fields.date) ) {
804
+ dt = new Date( fields.year, fields.month, fields.date, fields.hours);
805
+ }
806
+
807
+ return dt;
808
+ }
809
+ };
810
+
811
+ // Check if date is valid for specific month (and year for February).
812
+ // Month: 0 = Jan, 1 = Feb, etc
813
+ function isValid(year, month, date) {
814
+ if ( month === 1 && date > 28) {
815
+ return date === 29 && ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0);
816
+ }
817
+
818
+ if ( month === 3 || month === 5 || month === 8 || month === 10) {
819
+ return date < 31;
820
+ }
821
+
822
+ return true;
823
+ }
824
+ }]);
825
+
826
+ angular.module('ui.bootstrap.position', [])
827
+
828
+ /**
829
+ * A set of utility methods that can be use to retrieve position of DOM elements.
830
+ * It is meant to be used where we need to absolute-position DOM elements in
831
+ * relation to other, existing elements (this is the case for tooltips, popovers,
832
+ * typeahead suggestions etc.).
833
+ */
834
+ .factory('$position', ['$document', '$window', function ($document, $window) {
835
+
836
+ function getStyle(el, cssprop) {
837
+ if (el.currentStyle) { //IE
838
+ return el.currentStyle[cssprop];
839
+ } else if ($window.getComputedStyle) {
840
+ return $window.getComputedStyle(el)[cssprop];
841
+ }
842
+ // finally try and get inline style
843
+ return el.style[cssprop];
844
+ }
845
+
846
+ /**
847
+ * Checks if a given element is statically positioned
848
+ * @param element - raw DOM element
849
+ */
850
+ function isStaticPositioned(element) {
851
+ return (getStyle(element, 'position') || 'static' ) === 'static';
852
+ }
853
+
854
+ /**
855
+ * returns the closest, non-statically positioned parentOffset of a given element
856
+ * @param element
857
+ */
858
+ var parentOffsetEl = function (element) {
859
+ var docDomEl = $document[0];
860
+ var offsetParent = element.offsetParent || docDomEl;
861
+ while (offsetParent && offsetParent !== docDomEl && isStaticPositioned(offsetParent) ) {
862
+ offsetParent = offsetParent.offsetParent;
863
+ }
864
+ return offsetParent || docDomEl;
865
+ };
866
+
867
+ return {
868
+ /**
869
+ * Provides read-only equivalent of jQuery's position function:
870
+ * http://api.jquery.com/position/
871
+ */
872
+ position: function (element) {
873
+ var elBCR = this.offset(element);
874
+ var offsetParentBCR = { top: 0, left: 0 };
875
+ var offsetParentEl = parentOffsetEl(element[0]);
876
+ if (offsetParentEl != $document[0]) {
877
+ offsetParentBCR = this.offset(angular.element(offsetParentEl));
878
+ offsetParentBCR.top += offsetParentEl.clientTop - offsetParentEl.scrollTop;
879
+ offsetParentBCR.left += offsetParentEl.clientLeft - offsetParentEl.scrollLeft;
880
+ }
881
+
882
+ var boundingClientRect = element[0].getBoundingClientRect();
883
+ return {
884
+ width: boundingClientRect.width || element.prop('offsetWidth'),
885
+ height: boundingClientRect.height || element.prop('offsetHeight'),
886
+ top: elBCR.top - offsetParentBCR.top,
887
+ left: elBCR.left - offsetParentBCR.left
888
+ };
889
+ },
890
+
891
+ /**
892
+ * Provides read-only equivalent of jQuery's offset function:
893
+ * http://api.jquery.com/offset/
894
+ */
895
+ offset: function (element) {
896
+ var boundingClientRect = element[0].getBoundingClientRect();
897
+ return {
898
+ width: boundingClientRect.width || element.prop('offsetWidth'),
899
+ height: boundingClientRect.height || element.prop('offsetHeight'),
900
+ top: boundingClientRect.top + ($window.pageYOffset || $document[0].documentElement.scrollTop),
901
+ left: boundingClientRect.left + ($window.pageXOffset || $document[0].documentElement.scrollLeft)
902
+ };
903
+ },
904
+
905
+ /**
906
+ * Provides coordinates for the targetEl in relation to hostEl
907
+ */
908
+ positionElements: function (hostEl, targetEl, positionStr, appendToBody) {
909
+
910
+ var positionStrParts = positionStr.split('-');
911
+ var pos0 = positionStrParts[0], pos1 = positionStrParts[1] || 'center';
912
+
913
+ var hostElPos,
914
+ targetElWidth,
915
+ targetElHeight,
916
+ targetElPos;
917
+
918
+ hostElPos = appendToBody ? this.offset(hostEl) : this.position(hostEl);
919
+
920
+ targetElWidth = targetEl.prop('offsetWidth');
921
+ targetElHeight = targetEl.prop('offsetHeight');
922
+
923
+ var shiftWidth = {
924
+ center: function () {
925
+ return hostElPos.left + hostElPos.width / 2 - targetElWidth / 2;
926
+ },
927
+ left: function () {
928
+ return hostElPos.left;
929
+ },
930
+ right: function () {
931
+ return hostElPos.left + hostElPos.width;
932
+ }
933
+ };
934
+
935
+ var shiftHeight = {
936
+ center: function () {
937
+ return hostElPos.top + hostElPos.height / 2 - targetElHeight / 2;
938
+ },
939
+ top: function () {
940
+ return hostElPos.top;
941
+ },
942
+ bottom: function () {
943
+ return hostElPos.top + hostElPos.height;
944
+ }
945
+ };
946
+
947
+ switch (pos0) {
948
+ case 'right':
949
+ targetElPos = {
950
+ top: shiftHeight[pos1](),
951
+ left: shiftWidth[pos0]()
952
+ };
953
+ break;
954
+ case 'left':
955
+ targetElPos = {
956
+ top: shiftHeight[pos1](),
957
+ left: hostElPos.left - targetElWidth
958
+ };
959
+ break;
960
+ case 'bottom':
961
+ targetElPos = {
962
+ top: shiftHeight[pos0](),
963
+ left: shiftWidth[pos1]()
964
+ };
965
+ break;
966
+ default:
967
+ targetElPos = {
968
+ top: hostElPos.top - targetElHeight,
969
+ left: shiftWidth[pos1]()
970
+ };
971
+ break;
972
+ }
973
+
974
+ return targetElPos;
975
+ }
976
+ };
977
+ }]);
978
+
979
+ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootstrap.position'])
980
+
981
+ .constant('datepickerConfig', {
982
+ formatDay: 'dd',
983
+ formatMonth: 'MMMM',
984
+ formatYear: 'yyyy',
985
+ formatDayHeader: 'EEE',
986
+ formatDayTitle: 'MMMM yyyy',
987
+ formatMonthTitle: 'yyyy',
988
+ datepickerMode: 'day',
989
+ minMode: 'day',
990
+ maxMode: 'year',
991
+ showWeeks: true,
992
+ startingDay: 0,
993
+ yearRange: 20,
994
+ minDate: null,
995
+ maxDate: null
996
+ })
997
+
998
+ .controller('DatepickerController', ['$scope', '$attrs', '$parse', '$interpolate', '$timeout', '$log', 'dateFilter', 'datepickerConfig', function($scope, $attrs, $parse, $interpolate, $timeout, $log, dateFilter, datepickerConfig) {
999
+ var self = this,
1000
+ ngModelCtrl = { $setViewValue: angular.noop }; // nullModelCtrl;
1001
+
1002
+ // Modes chain
1003
+ this.modes = ['day', 'month', 'year'];
1004
+
1005
+ // Configuration attributes
1006
+ angular.forEach(['formatDay', 'formatMonth', 'formatYear', 'formatDayHeader', 'formatDayTitle', 'formatMonthTitle',
1007
+ 'minMode', 'maxMode', 'showWeeks', 'startingDay', 'yearRange'], function( key, index ) {
1008
+ self[key] = angular.isDefined($attrs[key]) ? (index < 8 ? $interpolate($attrs[key])($scope.$parent) : $scope.$parent.$eval($attrs[key])) : datepickerConfig[key];
1009
+ });
1010
+
1011
+ // Watchable attributes
1012
+ angular.forEach(['minDate', 'maxDate'], function( key ) {
1013
+ if ( $attrs[key] ) {
1014
+ $scope.$parent.$watch($parse($attrs[key]), function(value) {
1015
+ self[key] = value ? new Date(value) : null;
1016
+ self.refreshView();
1017
+ });
1018
+ } else {
1019
+ self[key] = datepickerConfig[key] ? new Date(datepickerConfig[key]) : null;
1020
+ }
1021
+ });
1022
+
1023
+ $scope.datepickerMode = $scope.datepickerMode || datepickerConfig.datepickerMode;
1024
+ $scope.uniqueId = 'datepicker-' + $scope.$id + '-' + Math.floor(Math.random() * 10000);
1025
+ this.activeDate = angular.isDefined($attrs.initDate) ? $scope.$parent.$eval($attrs.initDate) : new Date();
1026
+
1027
+ $scope.isActive = function(dateObject) {
1028
+ if (self.compare(dateObject.date, self.activeDate) === 0) {
1029
+ $scope.activeDateId = dateObject.uid;
1030
+ return true;
1031
+ }
1032
+ return false;
1033
+ };
1034
+
1035
+ this.init = function( ngModelCtrl_ ) {
1036
+ ngModelCtrl = ngModelCtrl_;
1037
+
1038
+ ngModelCtrl.$render = function() {
1039
+ self.render();
1040
+ };
1041
+ };
1042
+
1043
+ this.render = function() {
1044
+ if ( ngModelCtrl.$modelValue ) {
1045
+ var date = new Date( ngModelCtrl.$modelValue ),
1046
+ isValid = !isNaN(date);
1047
+
1048
+ if ( isValid ) {
1049
+ this.activeDate = date;
1050
+ } else {
1051
+ $log.error('Datepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.');
1052
+ }
1053
+ ngModelCtrl.$setValidity('date', isValid);
1054
+ }
1055
+ this.refreshView();
1056
+ };
1057
+
1058
+ this.refreshView = function() {
1059
+ if ( this.element ) {
1060
+ this._refreshView();
1061
+
1062
+ var date = ngModelCtrl.$modelValue ? new Date(ngModelCtrl.$modelValue) : null;
1063
+ ngModelCtrl.$setValidity('date-disabled', !date || (this.element && !this.isDisabled(date)));
1064
+ }
1065
+ };
1066
+
1067
+ this.createDateObject = function(date, format) {
1068
+ var model = ngModelCtrl.$modelValue ? new Date(ngModelCtrl.$modelValue) : null;
1069
+ return {
1070
+ date: date,
1071
+ label: dateFilter(date, format),
1072
+ selected: model && this.compare(date, model) === 0,
1073
+ disabled: this.isDisabled(date),
1074
+ current: this.compare(date, new Date()) === 0
1075
+ };
1076
+ };
1077
+
1078
+ this.isDisabled = function( date ) {
1079
+ return ((this.minDate && this.compare(date, this.minDate) < 0) || (this.maxDate && this.compare(date, this.maxDate) > 0) || ($attrs.dateDisabled && $scope.dateDisabled({date: date, mode: $scope.datepickerMode})));
1080
+ };
1081
+
1082
+ // Split array into smaller arrays
1083
+ this.split = function(arr, size) {
1084
+ var arrays = [];
1085
+ while (arr.length > 0) {
1086
+ arrays.push(arr.splice(0, size));
1087
+ }
1088
+ return arrays;
1089
+ };
1090
+
1091
+ $scope.select = function( date ) {
1092
+ if ( $scope.datepickerMode === self.minMode ) {
1093
+ var dt = ngModelCtrl.$modelValue ? new Date( ngModelCtrl.$modelValue ) : new Date(0, 0, 0, 0, 0, 0, 0);
1094
+ dt.setFullYear( date.getFullYear(), date.getMonth(), date.getDate() );
1095
+ ngModelCtrl.$setViewValue( dt );
1096
+ ngModelCtrl.$render();
1097
+ } else {
1098
+ self.activeDate = date;
1099
+ $scope.datepickerMode = self.modes[ self.modes.indexOf( $scope.datepickerMode ) - 1 ];
1100
+ }
1101
+ };
1102
+
1103
+ $scope.move = function( direction ) {
1104
+ var year = self.activeDate.getFullYear() + direction * (self.step.years || 0),
1105
+ month = self.activeDate.getMonth() + direction * (self.step.months || 0);
1106
+ self.activeDate.setFullYear(year, month, 1);
1107
+ self.refreshView();
1108
+ };
1109
+
1110
+ $scope.toggleMode = function( direction ) {
1111
+ direction = direction || 1;
1112
+
1113
+ if (($scope.datepickerMode === self.maxMode && direction === 1) || ($scope.datepickerMode === self.minMode && direction === -1)) {
1114
+ return;
1115
+ }
1116
+
1117
+ $scope.datepickerMode = self.modes[ self.modes.indexOf( $scope.datepickerMode ) + direction ];
1118
+ };
1119
+
1120
+ // Key event mapper
1121
+ $scope.keys = { 13:'enter', 32:'space', 33:'pageup', 34:'pagedown', 35:'end', 36:'home', 37:'left', 38:'up', 39:'right', 40:'down' };
1122
+
1123
+ var focusElement = function() {
1124
+ $timeout(function() {
1125
+ self.element[0].focus();
1126
+ }, 0 , false);
1127
+ };
1128
+
1129
+ // Listen for focus requests from popup directive
1130
+ $scope.$on('datepicker.focus', focusElement);
1131
+
1132
+ $scope.keydown = function( evt ) {
1133
+ var key = $scope.keys[evt.which];
1134
+
1135
+ if ( !key || evt.shiftKey || evt.altKey ) {
1136
+ return;
1137
+ }
1138
+
1139
+ evt.preventDefault();
1140
+ evt.stopPropagation();
1141
+
1142
+ if (key === 'enter' || key === 'space') {
1143
+ if ( self.isDisabled(self.activeDate)) {
1144
+ return; // do nothing
1145
+ }
1146
+ $scope.select(self.activeDate);
1147
+ focusElement();
1148
+ } else if (evt.ctrlKey && (key === 'up' || key === 'down')) {
1149
+ $scope.toggleMode(key === 'up' ? 1 : -1);
1150
+ focusElement();
1151
+ } else {
1152
+ self.handleKeyDown(key, evt);
1153
+ self.refreshView();
1154
+ }
1155
+ };
1156
+ }])
1157
+
1158
+ .directive( 'datepicker', function () {
1159
+ return {
1160
+ restrict: 'EA',
1161
+ replace: true,
1162
+ templateUrl: 'template/datepicker/datepicker.html',
1163
+ scope: {
1164
+ datepickerMode: '=?',
1165
+ dateDisabled: '&'
1166
+ },
1167
+ require: ['datepicker', '?^ngModel'],
1168
+ controller: 'DatepickerController',
1169
+ link: function(scope, element, attrs, ctrls) {
1170
+ var datepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1];
1171
+
1172
+ if ( ngModelCtrl ) {
1173
+ datepickerCtrl.init( ngModelCtrl );
1174
+ }
1175
+ }
1176
+ };
1177
+ })
1178
+
1179
+ .directive('daypicker', ['dateFilter', function (dateFilter) {
1180
+ return {
1181
+ restrict: 'EA',
1182
+ replace: true,
1183
+ templateUrl: 'template/datepicker/day.html',
1184
+ require: '^datepicker',
1185
+ link: function(scope, element, attrs, ctrl) {
1186
+ scope.showWeeks = ctrl.showWeeks;
1187
+
1188
+ ctrl.step = { months: 1 };
1189
+ ctrl.element = element;
1190
+
1191
+ var DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
1192
+ function getDaysInMonth( year, month ) {
1193
+ return ((month === 1) && (year % 4 === 0) && ((year % 100 !== 0) || (year % 400 === 0))) ? 29 : DAYS_IN_MONTH[month];
1194
+ }
1195
+
1196
+ function getDates(startDate, n) {
1197
+ var dates = new Array(n), current = new Date(startDate), i = 0;
1198
+ current.setHours(12); // Prevent repeated dates because of timezone bug
1199
+ while ( i < n ) {
1200
+ dates[i++] = new Date(current);
1201
+ current.setDate( current.getDate() + 1 );
1202
+ }
1203
+ return dates;
1204
+ }
1205
+
1206
+ ctrl._refreshView = function() {
1207
+ var year = ctrl.activeDate.getFullYear(),
1208
+ month = ctrl.activeDate.getMonth(),
1209
+ firstDayOfMonth = new Date(year, month, 1),
1210
+ difference = ctrl.startingDay - firstDayOfMonth.getDay(),
1211
+ numDisplayedFromPreviousMonth = (difference > 0) ? 7 - difference : - difference,
1212
+ firstDate = new Date(firstDayOfMonth);
1213
+
1214
+ if ( numDisplayedFromPreviousMonth > 0 ) {
1215
+ firstDate.setDate( - numDisplayedFromPreviousMonth + 1 );
1216
+ }
1217
+
1218
+ // 42 is the number of days on a six-month calendar
1219
+ var days = getDates(firstDate, 42);
1220
+ for (var i = 0; i < 42; i ++) {
1221
+ days[i] = angular.extend(ctrl.createDateObject(days[i], ctrl.formatDay), {
1222
+ secondary: days[i].getMonth() !== month,
1223
+ uid: scope.uniqueId + '-' + i
1224
+ });
1225
+ }
1226
+
1227
+ scope.labels = new Array(7);
1228
+ for (var j = 0; j < 7; j++) {
1229
+ scope.labels[j] = {
1230
+ abbr: dateFilter(days[j].date, ctrl.formatDayHeader),
1231
+ full: dateFilter(days[j].date, 'EEEE')
1232
+ };
1233
+ }
1234
+
1235
+ scope.title = dateFilter(ctrl.activeDate, ctrl.formatDayTitle);
1236
+ scope.rows = ctrl.split(days, 7);
1237
+
1238
+ if ( scope.showWeeks ) {
1239
+ scope.weekNumbers = [];
1240
+ var weekNumber = getISO8601WeekNumber( scope.rows[0][0].date ),
1241
+ numWeeks = scope.rows.length;
1242
+ while( scope.weekNumbers.push(weekNumber++) < numWeeks ) {}
1243
+ }
1244
+ };
1245
+
1246
+ ctrl.compare = function(date1, date2) {
1247
+ return (new Date( date1.getFullYear(), date1.getMonth(), date1.getDate() ) - new Date( date2.getFullYear(), date2.getMonth(), date2.getDate() ) );
1248
+ };
1249
+
1250
+ function getISO8601WeekNumber(date) {
1251
+ var checkDate = new Date(date);
1252
+ checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7)); // Thursday
1253
+ var time = checkDate.getTime();
1254
+ checkDate.setMonth(0); // Compare with Jan 1
1255
+ checkDate.setDate(1);
1256
+ return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;
1257
+ }
1258
+
1259
+ ctrl.handleKeyDown = function( key, evt ) {
1260
+ var date = ctrl.activeDate.getDate();
1261
+
1262
+ if (key === 'left') {
1263
+ date = date - 1; // up
1264
+ } else if (key === 'up') {
1265
+ date = date - 7; // down
1266
+ } else if (key === 'right') {
1267
+ date = date + 1; // down
1268
+ } else if (key === 'down') {
1269
+ date = date + 7;
1270
+ } else if (key === 'pageup' || key === 'pagedown') {
1271
+ var month = ctrl.activeDate.getMonth() + (key === 'pageup' ? - 1 : 1);
1272
+ ctrl.activeDate.setMonth(month, 1);
1273
+ date = Math.min(getDaysInMonth(ctrl.activeDate.getFullYear(), ctrl.activeDate.getMonth()), date);
1274
+ } else if (key === 'home') {
1275
+ date = 1;
1276
+ } else if (key === 'end') {
1277
+ date = getDaysInMonth(ctrl.activeDate.getFullYear(), ctrl.activeDate.getMonth());
1278
+ }
1279
+ ctrl.activeDate.setDate(date);
1280
+ };
1281
+
1282
+ ctrl.refreshView();
1283
+ }
1284
+ };
1285
+ }])
1286
+
1287
+ .directive('monthpicker', ['dateFilter', function (dateFilter) {
1288
+ return {
1289
+ restrict: 'EA',
1290
+ replace: true,
1291
+ templateUrl: 'template/datepicker/month.html',
1292
+ require: '^datepicker',
1293
+ link: function(scope, element, attrs, ctrl) {
1294
+ ctrl.step = { years: 1 };
1295
+ ctrl.element = element;
1296
+
1297
+ ctrl._refreshView = function() {
1298
+ var months = new Array(12),
1299
+ year = ctrl.activeDate.getFullYear();
1300
+
1301
+ for ( var i = 0; i < 12; i++ ) {
1302
+ months[i] = angular.extend(ctrl.createDateObject(new Date(year, i, 1), ctrl.formatMonth), {
1303
+ uid: scope.uniqueId + '-' + i
1304
+ });
1305
+ }
1306
+
1307
+ scope.title = dateFilter(ctrl.activeDate, ctrl.formatMonthTitle);
1308
+ scope.rows = ctrl.split(months, 3);
1309
+ };
1310
+
1311
+ ctrl.compare = function(date1, date2) {
1312
+ return new Date( date1.getFullYear(), date1.getMonth() ) - new Date( date2.getFullYear(), date2.getMonth() );
1313
+ };
1314
+
1315
+ ctrl.handleKeyDown = function( key, evt ) {
1316
+ var date = ctrl.activeDate.getMonth();
1317
+
1318
+ if (key === 'left') {
1319
+ date = date - 1; // up
1320
+ } else if (key === 'up') {
1321
+ date = date - 3; // down
1322
+ } else if (key === 'right') {
1323
+ date = date + 1; // down
1324
+ } else if (key === 'down') {
1325
+ date = date + 3;
1326
+ } else if (key === 'pageup' || key === 'pagedown') {
1327
+ var year = ctrl.activeDate.getFullYear() + (key === 'pageup' ? - 1 : 1);
1328
+ ctrl.activeDate.setFullYear(year);
1329
+ } else if (key === 'home') {
1330
+ date = 0;
1331
+ } else if (key === 'end') {
1332
+ date = 11;
1333
+ }
1334
+ ctrl.activeDate.setMonth(date);
1335
+ };
1336
+
1337
+ ctrl.refreshView();
1338
+ }
1339
+ };
1340
+ }])
1341
+
1342
+ .directive('yearpicker', ['dateFilter', function (dateFilter) {
1343
+ return {
1344
+ restrict: 'EA',
1345
+ replace: true,
1346
+ templateUrl: 'template/datepicker/year.html',
1347
+ require: '^datepicker',
1348
+ link: function(scope, element, attrs, ctrl) {
1349
+ var range = ctrl.yearRange;
1350
+
1351
+ ctrl.step = { years: range };
1352
+ ctrl.element = element;
1353
+
1354
+ function getStartingYear( year ) {
1355
+ return parseInt((year - 1) / range, 10) * range + 1;
1356
+ }
1357
+
1358
+ ctrl._refreshView = function() {
1359
+ var years = new Array(range);
1360
+
1361
+ for ( var i = 0, start = getStartingYear(ctrl.activeDate.getFullYear()); i < range; i++ ) {
1362
+ years[i] = angular.extend(ctrl.createDateObject(new Date(start + i, 0, 1), ctrl.formatYear), {
1363
+ uid: scope.uniqueId + '-' + i
1364
+ });
1365
+ }
1366
+
1367
+ scope.title = [years[0].label, years[range - 1].label].join(' - ');
1368
+ scope.rows = ctrl.split(years, 5);
1369
+ };
1370
+
1371
+ ctrl.compare = function(date1, date2) {
1372
+ return date1.getFullYear() - date2.getFullYear();
1373
+ };
1374
+
1375
+ ctrl.handleKeyDown = function( key, evt ) {
1376
+ var date = ctrl.activeDate.getFullYear();
1377
+
1378
+ if (key === 'left') {
1379
+ date = date - 1; // up
1380
+ } else if (key === 'up') {
1381
+ date = date - 5; // down
1382
+ } else if (key === 'right') {
1383
+ date = date + 1; // down
1384
+ } else if (key === 'down') {
1385
+ date = date + 5;
1386
+ } else if (key === 'pageup' || key === 'pagedown') {
1387
+ date += (key === 'pageup' ? - 1 : 1) * ctrl.step.years;
1388
+ } else if (key === 'home') {
1389
+ date = getStartingYear( ctrl.activeDate.getFullYear() );
1390
+ } else if (key === 'end') {
1391
+ date = getStartingYear( ctrl.activeDate.getFullYear() ) + range - 1;
1392
+ }
1393
+ ctrl.activeDate.setFullYear(date);
1394
+ };
1395
+
1396
+ ctrl.refreshView();
1397
+ }
1398
+ };
1399
+ }])
1400
+
1401
+ .constant('datepickerPopupConfig', {
1402
+ datepickerPopup: 'yyyy-MM-dd',
1403
+ currentText: 'Today',
1404
+ clearText: 'Clear',
1405
+ closeText: 'Done',
1406
+ closeOnDateSelection: true,
1407
+ appendToBody: false,
1408
+ showButtonBar: true
1409
+ })
1410
+
1411
+ .directive('datepickerPopup', ['$compile', '$parse', '$document', '$position', 'dateFilter', 'dateParser', 'datepickerPopupConfig',
1412
+ function ($compile, $parse, $document, $position, dateFilter, dateParser, datepickerPopupConfig) {
1413
+ return {
1414
+ restrict: 'EA',
1415
+ require: 'ngModel',
1416
+ scope: {
1417
+ isOpen: '=?',
1418
+ currentText: '@',
1419
+ clearText: '@',
1420
+ closeText: '@',
1421
+ dateDisabled: '&'
1422
+ },
1423
+ link: function(scope, element, attrs, ngModel) {
1424
+ var dateFormat,
1425
+ closeOnDateSelection = angular.isDefined(attrs.closeOnDateSelection) ? scope.$parent.$eval(attrs.closeOnDateSelection) : datepickerPopupConfig.closeOnDateSelection,
1426
+ appendToBody = angular.isDefined(attrs.datepickerAppendToBody) ? scope.$parent.$eval(attrs.datepickerAppendToBody) : datepickerPopupConfig.appendToBody;
1427
+
1428
+ scope.showButtonBar = angular.isDefined(attrs.showButtonBar) ? scope.$parent.$eval(attrs.showButtonBar) : datepickerPopupConfig.showButtonBar;
1429
+
1430
+ scope.getText = function( key ) {
1431
+ return scope[key + 'Text'] || datepickerPopupConfig[key + 'Text'];
1432
+ };
1433
+
1434
+ attrs.$observe('datepickerPopup', function(value) {
1435
+ dateFormat = value || datepickerPopupConfig.datepickerPopup;
1436
+ ngModel.$render();
1437
+ });
1438
+
1439
+ // popup element used to display calendar
1440
+ var popupEl = angular.element('<div datepicker-popup-wrap><div datepicker></div></div>');
1441
+ popupEl.attr({
1442
+ 'ng-model': 'date',
1443
+ 'ng-change': 'dateSelection()'
1444
+ });
1445
+
1446
+ function cameltoDash( string ){
1447
+ return string.replace(/([A-Z])/g, function($1) { return '-' + $1.toLowerCase(); });
1448
+ }
1449
+
1450
+ // datepicker element
1451
+ var datepickerEl = angular.element(popupEl.children()[0]);
1452
+ if ( attrs.datepickerOptions ) {
1453
+ angular.forEach(scope.$parent.$eval(attrs.datepickerOptions), function( value, option ) {
1454
+ datepickerEl.attr( cameltoDash(option), value );
1455
+ });
1456
+ }
1457
+
1458
+ angular.forEach(['minDate', 'maxDate'], function( key ) {
1459
+ if ( attrs[key] ) {
1460
+ scope.$parent.$watch($parse(attrs[key]), function(value){
1461
+ scope[key] = value;
1462
+ });
1463
+ datepickerEl.attr(cameltoDash(key), key);
1464
+ }
1465
+ });
1466
+ if (attrs.dateDisabled) {
1467
+ datepickerEl.attr('date-disabled', 'dateDisabled({ date: date, mode: mode })');
1468
+ }
1469
+
1470
+ function parseDate(viewValue) {
1471
+ if (!viewValue) {
1472
+ ngModel.$setValidity('date', true);
1473
+ return null;
1474
+ } else if (angular.isDate(viewValue) && !isNaN(viewValue)) {
1475
+ ngModel.$setValidity('date', true);
1476
+ return viewValue;
1477
+ } else if (angular.isString(viewValue)) {
1478
+ var date = dateParser.parse(viewValue, dateFormat) || new Date(viewValue);
1479
+ if (isNaN(date)) {
1480
+ ngModel.$setValidity('date', false);
1481
+ return undefined;
1482
+ } else {
1483
+ ngModel.$setValidity('date', true);
1484
+ return date;
1485
+ }
1486
+ } else {
1487
+ ngModel.$setValidity('date', false);
1488
+ return undefined;
1489
+ }
1490
+ }
1491
+ ngModel.$parsers.unshift(parseDate);
1492
+
1493
+ // Inner change
1494
+ scope.dateSelection = function(dt) {
1495
+ if (angular.isDefined(dt)) {
1496
+ scope.date = dt;
1497
+ }
1498
+ ngModel.$setViewValue(scope.date);
1499
+ ngModel.$render();
1500
+
1501
+ if ( closeOnDateSelection ) {
1502
+ scope.isOpen = false;
1503
+ element[0].focus();
1504
+ }
1505
+ };
1506
+
1507
+ element.bind('input change keyup', function() {
1508
+ scope.$apply(function() {
1509
+ scope.date = ngModel.$modelValue;
1510
+ });
1511
+ });
1512
+
1513
+ // Outter change
1514
+ ngModel.$render = function() {
1515
+ var date = ngModel.$viewValue ? dateFilter(ngModel.$viewValue, dateFormat) : '';
1516
+ element.val(date);
1517
+ scope.date = parseDate( ngModel.$modelValue );
1518
+ };
1519
+
1520
+ var documentClickBind = function(event) {
1521
+ if (scope.isOpen && event.target !== element[0]) {
1522
+ scope.$apply(function() {
1523
+ scope.isOpen = false;
1524
+ });
1525
+ }
1526
+ };
1527
+
1528
+ var keydown = function(evt, noApply) {
1529
+ scope.keydown(evt);
1530
+ };
1531
+ element.bind('keydown', keydown);
1532
+
1533
+ scope.keydown = function(evt) {
1534
+ if (evt.which === 27) {
1535
+ evt.preventDefault();
1536
+ evt.stopPropagation();
1537
+ scope.close();
1538
+ } else if (evt.which === 40 && !scope.isOpen) {
1539
+ scope.isOpen = true;
1540
+ }
1541
+ };
1542
+
1543
+ scope.$watch('isOpen', function(value) {
1544
+ if (value) {
1545
+ scope.$broadcast('datepicker.focus');
1546
+ scope.position = appendToBody ? $position.offset(element) : $position.position(element);
1547
+ scope.position.top = scope.position.top + element.prop('offsetHeight');
1548
+
1549
+ $document.bind('click', documentClickBind);
1550
+ } else {
1551
+ $document.unbind('click', documentClickBind);
1552
+ }
1553
+ });
1554
+
1555
+ scope.select = function( date ) {
1556
+ if (date === 'today') {
1557
+ var today = new Date();
1558
+ if (angular.isDate(ngModel.$modelValue)) {
1559
+ date = new Date(ngModel.$modelValue);
1560
+ date.setFullYear(today.getFullYear(), today.getMonth(), today.getDate());
1561
+ } else {
1562
+ date = new Date(today.setHours(0, 0, 0, 0));
1563
+ }
1564
+ }
1565
+ scope.dateSelection( date );
1566
+ };
1567
+
1568
+ scope.close = function() {
1569
+ scope.isOpen = false;
1570
+ element[0].focus();
1571
+ };
1572
+
1573
+ var $popup = $compile(popupEl)(scope);
1574
+ if ( appendToBody ) {
1575
+ $document.find('body').append($popup);
1576
+ } else {
1577
+ element.after($popup);
1578
+ }
1579
+
1580
+ scope.$on('$destroy', function() {
1581
+ $popup.remove();
1582
+ element.unbind('keydown', keydown);
1583
+ $document.unbind('click', documentClickBind);
1584
+ });
1585
+ }
1586
+ };
1587
+ }])
1588
+
1589
+ .directive('datepickerPopupWrap', function() {
1590
+ return {
1591
+ restrict:'EA',
1592
+ replace: true,
1593
+ transclude: true,
1594
+ templateUrl: 'template/datepicker/popup.html',
1595
+ link:function (scope, element, attrs) {
1596
+ element.bind('click', function(event) {
1597
+ event.preventDefault();
1598
+ event.stopPropagation();
1599
+ });
1600
+ }
1601
+ };
1602
+ });
1603
+
1604
+ angular.module('ui.bootstrap.dropdown', [])
1605
+
1606
+ .constant('dropdownConfig', {
1607
+ openClass: 'open'
1608
+ })
1609
+
1610
+ .service('dropdownService', ['$document', function($document) {
1611
+ var openScope = null;
1612
+
1613
+ this.open = function( dropdownScope ) {
1614
+ if ( !openScope ) {
1615
+ $document.bind('click', closeDropdown);
1616
+ $document.bind('keydown', escapeKeyBind);
1617
+ }
1618
+
1619
+ if ( openScope && openScope !== dropdownScope ) {
1620
+ openScope.isOpen = false;
1621
+ }
1622
+
1623
+ openScope = dropdownScope;
1624
+ };
1625
+
1626
+ this.close = function( dropdownScope ) {
1627
+ if ( openScope === dropdownScope ) {
1628
+ openScope = null;
1629
+ $document.unbind('click', closeDropdown);
1630
+ $document.unbind('keydown', escapeKeyBind);
1631
+ }
1632
+ };
1633
+
1634
+ var closeDropdown = function( evt ) {
1635
+ if (evt && evt.isDefaultPrevented()) {
1636
+ return;
1637
+ }
1638
+
1639
+ openScope.$apply(function() {
1640
+ openScope.isOpen = false;
1641
+ });
1642
+ };
1643
+
1644
+ var escapeKeyBind = function( evt ) {
1645
+ if ( evt.which === 27 ) {
1646
+ openScope.focusToggleElement();
1647
+ closeDropdown();
1648
+ }
1649
+ };
1650
+ }])
1651
+
1652
+ .controller('DropdownController', ['$scope', '$attrs', '$parse', 'dropdownConfig', 'dropdownService', '$animate', function($scope, $attrs, $parse, dropdownConfig, dropdownService, $animate) {
1653
+ var self = this,
1654
+ scope = $scope.$new(), // create a child scope so we are not polluting original one
1655
+ openClass = dropdownConfig.openClass,
1656
+ getIsOpen,
1657
+ setIsOpen = angular.noop,
1658
+ toggleInvoker = $attrs.onToggle ? $parse($attrs.onToggle) : angular.noop;
1659
+
1660
+ this.init = function( element ) {
1661
+ self.$element = element;
1662
+
1663
+ if ( $attrs.isOpen ) {
1664
+ getIsOpen = $parse($attrs.isOpen);
1665
+ setIsOpen = getIsOpen.assign;
1666
+
1667
+ $scope.$watch(getIsOpen, function(value) {
1668
+ scope.isOpen = !!value;
1669
+ });
1670
+ }
1671
+ };
1672
+
1673
+ this.toggle = function( open ) {
1674
+ return scope.isOpen = arguments.length ? !!open : !scope.isOpen;
1675
+ };
1676
+
1677
+ // Allow other directives to watch status
1678
+ this.isOpen = function() {
1679
+ return scope.isOpen;
1680
+ };
1681
+
1682
+ scope.focusToggleElement = function() {
1683
+ if ( self.toggleElement ) {
1684
+ self.toggleElement[0].focus();
1685
+ }
1686
+ };
1687
+
1688
+ scope.$watch('isOpen', function( isOpen, wasOpen ) {
1689
+ $animate[isOpen ? 'addClass' : 'removeClass'](self.$element, openClass);
1690
+
1691
+ if ( isOpen ) {
1692
+ scope.focusToggleElement();
1693
+ dropdownService.open( scope );
1694
+ } else {
1695
+ dropdownService.close( scope );
1696
+ }
1697
+
1698
+ setIsOpen($scope, isOpen);
1699
+ if (angular.isDefined(isOpen) && isOpen !== wasOpen) {
1700
+ toggleInvoker($scope, { open: !!isOpen });
1701
+ }
1702
+ });
1703
+
1704
+ $scope.$on('$locationChangeSuccess', function() {
1705
+ scope.isOpen = false;
1706
+ });
1707
+
1708
+ $scope.$on('$destroy', function() {
1709
+ scope.$destroy();
1710
+ });
1711
+ }])
1712
+
1713
+ .directive('dropdown', function() {
1714
+ return {
1715
+ restrict: 'CA',
1716
+ controller: 'DropdownController',
1717
+ link: function(scope, element, attrs, dropdownCtrl) {
1718
+ dropdownCtrl.init( element );
1719
+ }
1720
+ };
1721
+ })
1722
+
1723
+ .directive('dropdownToggle', function() {
1724
+ return {
1725
+ restrict: 'CA',
1726
+ require: '?^dropdown',
1727
+ link: function(scope, element, attrs, dropdownCtrl) {
1728
+ if ( !dropdownCtrl ) {
1729
+ return;
1730
+ }
1731
+
1732
+ dropdownCtrl.toggleElement = element;
1733
+
1734
+ var toggleDropdown = function(event) {
1735
+ event.preventDefault();
1736
+
1737
+ if ( !element.hasClass('disabled') && !attrs.disabled ) {
1738
+ scope.$apply(function() {
1739
+ dropdownCtrl.toggle();
1740
+ });
1741
+ }
1742
+ };
1743
+
1744
+ element.bind('click', toggleDropdown);
1745
+
1746
+ // WAI-ARIA
1747
+ element.attr({ 'aria-haspopup': true, 'aria-expanded': false });
1748
+ scope.$watch(dropdownCtrl.isOpen, function( isOpen ) {
1749
+ element.attr('aria-expanded', !!isOpen);
1750
+ });
1751
+
1752
+ scope.$on('$destroy', function() {
1753
+ element.unbind('click', toggleDropdown);
1754
+ });
1755
+ }
1756
+ };
1757
+ });
1758
+
1759
+ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition'])
1760
+
1761
+ /**
1762
+ * A helper, internal data structure that acts as a map but also allows getting / removing
1763
+ * elements in the LIFO order
1764
+ */
1765
+ .factory('$$stackedMap', function () {
1766
+ return {
1767
+ createNew: function () {
1768
+ var stack = [];
1769
+
1770
+ return {
1771
+ add: function (key, value) {
1772
+ stack.push({
1773
+ key: key,
1774
+ value: value
1775
+ });
1776
+ },
1777
+ get: function (key) {
1778
+ for (var i = 0; i < stack.length; i++) {
1779
+ if (key == stack[i].key) {
1780
+ return stack[i];
1781
+ }
1782
+ }
1783
+ },
1784
+ keys: function() {
1785
+ var keys = [];
1786
+ for (var i = 0; i < stack.length; i++) {
1787
+ keys.push(stack[i].key);
1788
+ }
1789
+ return keys;
1790
+ },
1791
+ top: function () {
1792
+ return stack[stack.length - 1];
1793
+ },
1794
+ remove: function (key) {
1795
+ var idx = -1;
1796
+ for (var i = 0; i < stack.length; i++) {
1797
+ if (key == stack[i].key) {
1798
+ idx = i;
1799
+ break;
1800
+ }
1801
+ }
1802
+ return stack.splice(idx, 1)[0];
1803
+ },
1804
+ removeTop: function () {
1805
+ return stack.splice(stack.length - 1, 1)[0];
1806
+ },
1807
+ length: function () {
1808
+ return stack.length;
1809
+ }
1810
+ };
1811
+ }
1812
+ };
1813
+ })
1814
+
1815
+ /**
1816
+ * A helper directive for the $modal service. It creates a backdrop element.
1817
+ */
1818
+ .directive('modalBackdrop', ['$timeout', function ($timeout) {
1819
+ return {
1820
+ restrict: 'EA',
1821
+ replace: true,
1822
+ templateUrl: 'template/modal/backdrop.html',
1823
+ link: function (scope) {
1824
+
1825
+ scope.animate = false;
1826
+
1827
+ //trigger CSS transitions
1828
+ $timeout(function () {
1829
+ scope.animate = true;
1830
+ });
1831
+ }
1832
+ };
1833
+ }])
1834
+
1835
+ .directive('modalWindow', ['$modalStack', '$timeout', function ($modalStack, $timeout) {
1836
+ return {
1837
+ restrict: 'EA',
1838
+ scope: {
1839
+ index: '@',
1840
+ animate: '='
1841
+ },
1842
+ replace: true,
1843
+ transclude: true,
1844
+ templateUrl: function(tElement, tAttrs) {
1845
+ return tAttrs.templateUrl || 'template/modal/window.html';
1846
+ },
1847
+ link: function (scope, element, attrs) {
1848
+ element.addClass(attrs.windowClass || '');
1849
+ scope.size = attrs.size;
1850
+
1851
+ $timeout(function () {
1852
+ // trigger CSS transitions
1853
+ scope.animate = true;
1854
+ // focus a freshly-opened modal
1855
+ element[0].focus();
1856
+ });
1857
+
1858
+ scope.close = function (evt) {
1859
+ var modal = $modalStack.getTop();
1860
+ if (modal && modal.value.backdrop && modal.value.backdrop != 'static' && (evt.target === evt.currentTarget)) {
1861
+ evt.preventDefault();
1862
+ evt.stopPropagation();
1863
+ $modalStack.dismiss(modal.key, 'backdrop click');
1864
+ }
1865
+ };
1866
+ }
1867
+ };
1868
+ }])
1869
+
1870
+ .factory('$modalStack', ['$transition', '$timeout', '$document', '$compile', '$rootScope', '$$stackedMap',
1871
+ function ($transition, $timeout, $document, $compile, $rootScope, $$stackedMap) {
1872
+
1873
+ var OPENED_MODAL_CLASS = 'modal-open';
1874
+
1875
+ var backdropDomEl, backdropScope;
1876
+ var openedWindows = $$stackedMap.createNew();
1877
+ var $modalStack = {};
1878
+
1879
+ function backdropIndex() {
1880
+ var topBackdropIndex = -1;
1881
+ var opened = openedWindows.keys();
1882
+ for (var i = 0; i < opened.length; i++) {
1883
+ if (openedWindows.get(opened[i]).value.backdrop) {
1884
+ topBackdropIndex = i;
1885
+ }
1886
+ }
1887
+ return topBackdropIndex;
1888
+ }
1889
+
1890
+ $rootScope.$watch(backdropIndex, function(newBackdropIndex){
1891
+ if (backdropScope) {
1892
+ backdropScope.index = newBackdropIndex;
1893
+ }
1894
+ });
1895
+
1896
+ function removeModalWindow(modalInstance) {
1897
+
1898
+ var body = $document.find('body').eq(0);
1899
+ var modalWindow = openedWindows.get(modalInstance).value;
1900
+
1901
+ //clean up the stack
1902
+ openedWindows.remove(modalInstance);
1903
+
1904
+ //remove window DOM element
1905
+ removeAfterAnimate(modalWindow.modalDomEl, modalWindow.modalScope, 300, function() {
1906
+ modalWindow.modalScope.$destroy();
1907
+ body.toggleClass(OPENED_MODAL_CLASS, openedWindows.length() > 0);
1908
+ checkRemoveBackdrop();
1909
+ });
1910
+ }
1911
+
1912
+ function checkRemoveBackdrop() {
1913
+ //remove backdrop if no longer needed
1914
+ if (backdropDomEl && backdropIndex() == -1) {
1915
+ var backdropScopeRef = backdropScope;
1916
+ removeAfterAnimate(backdropDomEl, backdropScope, 150, function () {
1917
+ backdropScopeRef.$destroy();
1918
+ backdropScopeRef = null;
1919
+ });
1920
+ backdropDomEl = undefined;
1921
+ backdropScope = undefined;
1922
+ }
1923
+ }
1924
+
1925
+ function removeAfterAnimate(domEl, scope, emulateTime, done) {
1926
+ // Closing animation
1927
+ scope.animate = false;
1928
+
1929
+ var transitionEndEventName = $transition.transitionEndEventName;
1930
+ if (transitionEndEventName) {
1931
+ // transition out
1932
+ var timeout = $timeout(afterAnimating, emulateTime);
1933
+
1934
+ domEl.bind(transitionEndEventName, function () {
1935
+ $timeout.cancel(timeout);
1936
+ afterAnimating();
1937
+ scope.$apply();
1938
+ });
1939
+ } else {
1940
+ // Ensure this call is async
1941
+ $timeout(afterAnimating, 0);
1942
+ }
1943
+
1944
+ function afterAnimating() {
1945
+ if (afterAnimating.done) {
1946
+ return;
1947
+ }
1948
+ afterAnimating.done = true;
1949
+
1950
+ domEl.remove();
1951
+ if (done) {
1952
+ done();
1953
+ }
1954
+ }
1955
+ }
1956
+
1957
+ $document.bind('keydown', function (evt) {
1958
+ var modal;
1959
+
1960
+ if (evt.which === 27) {
1961
+ modal = openedWindows.top();
1962
+ if (modal && modal.value.keyboard) {
1963
+ evt.preventDefault();
1964
+ $rootScope.$apply(function () {
1965
+ $modalStack.dismiss(modal.key, 'escape key press');
1966
+ });
1967
+ }
1968
+ }
1969
+ });
1970
+
1971
+ $modalStack.open = function (modalInstance, modal) {
1972
+
1973
+ openedWindows.add(modalInstance, {
1974
+ deferred: modal.deferred,
1975
+ modalScope: modal.scope,
1976
+ backdrop: modal.backdrop,
1977
+ keyboard: modal.keyboard
1978
+ });
1979
+
1980
+ var body = $document.find('body').eq(0),
1981
+ currBackdropIndex = backdropIndex();
1982
+
1983
+ if (currBackdropIndex >= 0 && !backdropDomEl) {
1984
+ backdropScope = $rootScope.$new(true);
1985
+ backdropScope.index = currBackdropIndex;
1986
+ backdropDomEl = $compile('<div modal-backdrop></div>')(backdropScope);
1987
+ body.append(backdropDomEl);
1988
+ }
1989
+
1990
+ var angularDomEl = angular.element('<div modal-window></div>');
1991
+ angularDomEl.attr({
1992
+ 'template-url': modal.windowTemplateUrl,
1993
+ 'window-class': modal.windowClass,
1994
+ 'size': modal.size,
1995
+ 'index': openedWindows.length() - 1,
1996
+ 'animate': 'animate'
1997
+ }).html(modal.content);
1998
+
1999
+ var modalDomEl = $compile(angularDomEl)(modal.scope);
2000
+ openedWindows.top().value.modalDomEl = modalDomEl;
2001
+ body.append(modalDomEl);
2002
+ body.addClass(OPENED_MODAL_CLASS);
2003
+ };
2004
+
2005
+ $modalStack.close = function (modalInstance, result) {
2006
+ var modalWindow = openedWindows.get(modalInstance).value;
2007
+ if (modalWindow) {
2008
+ modalWindow.deferred.resolve(result);
2009
+ removeModalWindow(modalInstance);
2010
+ }
2011
+ };
2012
+
2013
+ $modalStack.dismiss = function (modalInstance, reason) {
2014
+ var modalWindow = openedWindows.get(modalInstance).value;
2015
+ if (modalWindow) {
2016
+ modalWindow.deferred.reject(reason);
2017
+ removeModalWindow(modalInstance);
2018
+ }
2019
+ };
2020
+
2021
+ $modalStack.dismissAll = function (reason) {
2022
+ var topModal = this.getTop();
2023
+ while (topModal) {
2024
+ this.dismiss(topModal.key, reason);
2025
+ topModal = this.getTop();
2026
+ }
2027
+ };
2028
+
2029
+ $modalStack.getTop = function () {
2030
+ return openedWindows.top();
2031
+ };
2032
+
2033
+ return $modalStack;
2034
+ }])
2035
+
2036
+ .provider('$modal', function () {
2037
+
2038
+ var $modalProvider = {
2039
+ options: {
2040
+ backdrop: true, //can be also false or 'static'
2041
+ keyboard: true
2042
+ },
2043
+ $get: ['$injector', '$rootScope', '$q', '$http', '$templateCache', '$controller', '$modalStack',
2044
+ function ($injector, $rootScope, $q, $http, $templateCache, $controller, $modalStack) {
2045
+
2046
+ var $modal = {};
2047
+
2048
+ function getTemplatePromise(options) {
2049
+ return options.template ? $q.when(options.template) :
2050
+ $http.get(options.templateUrl, {cache: $templateCache}).then(function (result) {
2051
+ return result.data;
2052
+ });
2053
+ }
2054
+
2055
+ function getResolvePromises(resolves) {
2056
+ var promisesArr = [];
2057
+ angular.forEach(resolves, function (value, key) {
2058
+ if (angular.isFunction(value) || angular.isArray(value)) {
2059
+ promisesArr.push($q.when($injector.invoke(value)));
2060
+ }
2061
+ });
2062
+ return promisesArr;
2063
+ }
2064
+
2065
+ $modal.open = function (modalOptions) {
2066
+
2067
+ var modalResultDeferred = $q.defer();
2068
+ var modalOpenedDeferred = $q.defer();
2069
+
2070
+ //prepare an instance of a modal to be injected into controllers and returned to a caller
2071
+ var modalInstance = {
2072
+ result: modalResultDeferred.promise,
2073
+ opened: modalOpenedDeferred.promise,
2074
+ close: function (result) {
2075
+ $modalStack.close(modalInstance, result);
2076
+ },
2077
+ dismiss: function (reason) {
2078
+ $modalStack.dismiss(modalInstance, reason);
2079
+ }
2080
+ };
2081
+
2082
+ //merge and clean up options
2083
+ modalOptions = angular.extend({}, $modalProvider.options, modalOptions);
2084
+ modalOptions.resolve = modalOptions.resolve || {};
2085
+
2086
+ //verify options
2087
+ if (!modalOptions.template && !modalOptions.templateUrl) {
2088
+ throw new Error('One of template or templateUrl options is required.');
2089
+ }
2090
+
2091
+ var templateAndResolvePromise =
2092
+ $q.all([getTemplatePromise(modalOptions)].concat(getResolvePromises(modalOptions.resolve)));
2093
+
2094
+
2095
+ templateAndResolvePromise.then(function resolveSuccess(tplAndVars) {
2096
+
2097
+ var modalScope = (modalOptions.scope || $rootScope).$new();
2098
+ modalScope.$close = modalInstance.close;
2099
+ modalScope.$dismiss = modalInstance.dismiss;
2100
+
2101
+ var ctrlInstance, ctrlLocals = {};
2102
+ var resolveIter = 1;
2103
+
2104
+ //controllers
2105
+ if (modalOptions.controller) {
2106
+ ctrlLocals.$scope = modalScope;
2107
+ ctrlLocals.$modalInstance = modalInstance;
2108
+ angular.forEach(modalOptions.resolve, function (value, key) {
2109
+ ctrlLocals[key] = tplAndVars[resolveIter++];
2110
+ });
2111
+
2112
+ ctrlInstance = $controller(modalOptions.controller, ctrlLocals);
2113
+ }
2114
+
2115
+ $modalStack.open(modalInstance, {
2116
+ scope: modalScope,
2117
+ deferred: modalResultDeferred,
2118
+ content: tplAndVars[0],
2119
+ backdrop: modalOptions.backdrop,
2120
+ keyboard: modalOptions.keyboard,
2121
+ windowClass: modalOptions.windowClass,
2122
+ windowTemplateUrl: modalOptions.windowTemplateUrl,
2123
+ size: modalOptions.size
2124
+ });
2125
+
2126
+ }, function resolveError(reason) {
2127
+ modalResultDeferred.reject(reason);
2128
+ });
2129
+
2130
+ templateAndResolvePromise.then(function () {
2131
+ modalOpenedDeferred.resolve(true);
2132
+ }, function () {
2133
+ modalOpenedDeferred.reject(false);
2134
+ });
2135
+
2136
+ return modalInstance;
2137
+ };
2138
+
2139
+ return $modal;
2140
+ }]
2141
+ };
2142
+
2143
+ return $modalProvider;
2144
+ });
2145
+
2146
+ angular.module('ui.bootstrap.pagination', [])
2147
+
2148
+ .controller('PaginationController', ['$scope', '$attrs', '$parse', function ($scope, $attrs, $parse) {
2149
+ var self = this,
2150
+ ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl
2151
+ setNumPages = $attrs.numPages ? $parse($attrs.numPages).assign : angular.noop;
2152
+
2153
+ this.init = function(ngModelCtrl_, config) {
2154
+ ngModelCtrl = ngModelCtrl_;
2155
+ this.config = config;
2156
+
2157
+ ngModelCtrl.$render = function() {
2158
+ self.render();
2159
+ };
2160
+
2161
+ if ($attrs.itemsPerPage) {
2162
+ $scope.$parent.$watch($parse($attrs.itemsPerPage), function(value) {
2163
+ self.itemsPerPage = parseInt(value, 10);
2164
+ $scope.totalPages = self.calculateTotalPages();
2165
+ });
2166
+ } else {
2167
+ this.itemsPerPage = config.itemsPerPage;
2168
+ }
2169
+ };
2170
+
2171
+ this.calculateTotalPages = function() {
2172
+ var totalPages = this.itemsPerPage < 1 ? 1 : Math.ceil($scope.totalItems / this.itemsPerPage);
2173
+ return Math.max(totalPages || 0, 1);
2174
+ };
2175
+
2176
+ this.render = function() {
2177
+ $scope.page = parseInt(ngModelCtrl.$viewValue, 10) || 1;
2178
+ };
2179
+
2180
+ $scope.selectPage = function(page) {
2181
+ if ( $scope.page !== page && page > 0 && page <= $scope.totalPages) {
2182
+ ngModelCtrl.$setViewValue(page);
2183
+ ngModelCtrl.$render();
2184
+ }
2185
+ };
2186
+
2187
+ $scope.getText = function( key ) {
2188
+ return $scope[key + 'Text'] || self.config[key + 'Text'];
2189
+ };
2190
+ $scope.noPrevious = function() {
2191
+ return $scope.page === 1;
2192
+ };
2193
+ $scope.noNext = function() {
2194
+ return $scope.page === $scope.totalPages;
2195
+ };
2196
+
2197
+ $scope.$watch('totalItems', function() {
2198
+ $scope.totalPages = self.calculateTotalPages();
2199
+ });
2200
+
2201
+ $scope.$watch('totalPages', function(value) {
2202
+ setNumPages($scope.$parent, value); // Readonly variable
2203
+
2204
+ if ( $scope.page > value ) {
2205
+ $scope.selectPage(value);
2206
+ } else {
2207
+ ngModelCtrl.$render();
2208
+ }
2209
+ });
2210
+ }])
2211
+
2212
+ .constant('paginationConfig', {
2213
+ itemsPerPage: 10,
2214
+ boundaryLinks: false,
2215
+ directionLinks: true,
2216
+ firstText: 'First',
2217
+ previousText: 'Previous',
2218
+ nextText: 'Next',
2219
+ lastText: 'Last',
2220
+ rotate: true
2221
+ })
2222
+
2223
+ .directive('pagination', ['$parse', 'paginationConfig', function($parse, paginationConfig) {
2224
+ return {
2225
+ restrict: 'EA',
2226
+ scope: {
2227
+ totalItems: '=',
2228
+ firstText: '@',
2229
+ previousText: '@',
2230
+ nextText: '@',
2231
+ lastText: '@'
2232
+ },
2233
+ require: ['pagination', '?ngModel'],
2234
+ controller: 'PaginationController',
2235
+ templateUrl: 'template/pagination/pagination.html',
2236
+ replace: true,
2237
+ link: function(scope, element, attrs, ctrls) {
2238
+ var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1];
2239
+
2240
+ if (!ngModelCtrl) {
2241
+ return; // do nothing if no ng-model
2242
+ }
2243
+
2244
+ // Setup configuration parameters
2245
+ var maxSize = angular.isDefined(attrs.maxSize) ? scope.$parent.$eval(attrs.maxSize) : paginationConfig.maxSize,
2246
+ rotate = angular.isDefined(attrs.rotate) ? scope.$parent.$eval(attrs.rotate) : paginationConfig.rotate;
2247
+ scope.boundaryLinks = angular.isDefined(attrs.boundaryLinks) ? scope.$parent.$eval(attrs.boundaryLinks) : paginationConfig.boundaryLinks;
2248
+ scope.directionLinks = angular.isDefined(attrs.directionLinks) ? scope.$parent.$eval(attrs.directionLinks) : paginationConfig.directionLinks;
2249
+
2250
+ paginationCtrl.init(ngModelCtrl, paginationConfig);
2251
+
2252
+ if (attrs.maxSize) {
2253
+ scope.$parent.$watch($parse(attrs.maxSize), function(value) {
2254
+ maxSize = parseInt(value, 10);
2255
+ paginationCtrl.render();
2256
+ });
2257
+ }
2258
+
2259
+ // Create page object used in template
2260
+ function makePage(number, text, isActive) {
2261
+ return {
2262
+ number: number,
2263
+ text: text,
2264
+ active: isActive
2265
+ };
2266
+ }
2267
+
2268
+ function getPages(currentPage, totalPages) {
2269
+ var pages = [];
2270
+
2271
+ // Default page limits
2272
+ var startPage = 1, endPage = totalPages;
2273
+ var isMaxSized = ( angular.isDefined(maxSize) && maxSize < totalPages );
2274
+
2275
+ // recompute if maxSize
2276
+ if ( isMaxSized ) {
2277
+ if ( rotate ) {
2278
+ // Current page is displayed in the middle of the visible ones
2279
+ startPage = Math.max(currentPage - Math.floor(maxSize/2), 1);
2280
+ endPage = startPage + maxSize - 1;
2281
+
2282
+ // Adjust if limit is exceeded
2283
+ if (endPage > totalPages) {
2284
+ endPage = totalPages;
2285
+ startPage = endPage - maxSize + 1;
2286
+ }
2287
+ } else {
2288
+ // Visible pages are paginated with maxSize
2289
+ startPage = ((Math.ceil(currentPage / maxSize) - 1) * maxSize) + 1;
2290
+
2291
+ // Adjust last page if limit is exceeded
2292
+ endPage = Math.min(startPage + maxSize - 1, totalPages);
2293
+ }
2294
+ }
2295
+
2296
+ // Add page number links
2297
+ for (var number = startPage; number <= endPage; number++) {
2298
+ var page = makePage(number, number, number === currentPage);
2299
+ pages.push(page);
2300
+ }
2301
+
2302
+ // Add links to move between page sets
2303
+ if ( isMaxSized && ! rotate ) {
2304
+ if ( startPage > 1 ) {
2305
+ var previousPageSet = makePage(startPage - 1, '...', false);
2306
+ pages.unshift(previousPageSet);
2307
+ }
2308
+
2309
+ if ( endPage < totalPages ) {
2310
+ var nextPageSet = makePage(endPage + 1, '...', false);
2311
+ pages.push(nextPageSet);
2312
+ }
2313
+ }
2314
+
2315
+ return pages;
2316
+ }
2317
+
2318
+ var originalRender = paginationCtrl.render;
2319
+ paginationCtrl.render = function() {
2320
+ originalRender();
2321
+ if (scope.page > 0 && scope.page <= scope.totalPages) {
2322
+ scope.pages = getPages(scope.page, scope.totalPages);
2323
+ }
2324
+ };
2325
+ }
2326
+ };
2327
+ }])
2328
+
2329
+ .constant('pagerConfig', {
2330
+ itemsPerPage: 10,
2331
+ previousText: '« Previous',
2332
+ nextText: 'Next »',
2333
+ align: true
2334
+ })
2335
+
2336
+ .directive('pager', ['pagerConfig', function(pagerConfig) {
2337
+ return {
2338
+ restrict: 'EA',
2339
+ scope: {
2340
+ totalItems: '=',
2341
+ previousText: '@',
2342
+ nextText: '@'
2343
+ },
2344
+ require: ['pager', '?ngModel'],
2345
+ controller: 'PaginationController',
2346
+ templateUrl: 'template/pagination/pager.html',
2347
+ replace: true,
2348
+ link: function(scope, element, attrs, ctrls) {
2349
+ var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1];
2350
+
2351
+ if (!ngModelCtrl) {
2352
+ return; // do nothing if no ng-model
2353
+ }
2354
+
2355
+ scope.align = angular.isDefined(attrs.align) ? scope.$parent.$eval(attrs.align) : pagerConfig.align;
2356
+ paginationCtrl.init(ngModelCtrl, pagerConfig);
2357
+ }
2358
+ };
2359
+ }]);
2360
+
2361
+ /**
2362
+ * The following features are still outstanding: animation as a
2363
+ * function, placement as a function, inside, support for more triggers than
2364
+ * just mouse enter/leave, html tooltips, and selector delegation.
2365
+ */
2366
+ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap.bindHtml' ] )
2367
+
2368
+ /**
2369
+ * The $tooltip service creates tooltip- and popover-like directives as well as
2370
+ * houses global options for them.
2371
+ */
2372
+ .provider( '$tooltip', function () {
2373
+ // The default options tooltip and popover.
2374
+ var defaultOptions = {
2375
+ placement: 'top',
2376
+ animation: true,
2377
+ popupDelay: 0
2378
+ };
2379
+
2380
+ // Default hide triggers for each show trigger
2381
+ var triggerMap = {
2382
+ 'mouseenter': 'mouseleave',
2383
+ 'click': 'click',
2384
+ 'focus': 'blur'
2385
+ };
2386
+
2387
+ // The options specified to the provider globally.
2388
+ var globalOptions = {};
2389
+
2390
+ /**
2391
+ * `options({})` allows global configuration of all tooltips in the
2392
+ * application.
2393
+ *
2394
+ * var app = angular.module( 'App', ['ui.bootstrap.tooltip'], function( $tooltipProvider ) {
2395
+ * // place tooltips left instead of top by default
2396
+ * $tooltipProvider.options( { placement: 'left' } );
2397
+ * });
2398
+ */
2399
+ this.options = function( value ) {
2400
+ angular.extend( globalOptions, value );
2401
+ };
2402
+
2403
+ /**
2404
+ * This allows you to extend the set of trigger mappings available. E.g.:
2405
+ *
2406
+ * $tooltipProvider.setTriggers( 'openTrigger': 'closeTrigger' );
2407
+ */
2408
+ this.setTriggers = function setTriggers ( triggers ) {
2409
+ angular.extend( triggerMap, triggers );
2410
+ };
2411
+
2412
+ /**
2413
+ * This is a helper function for translating camel-case to snake-case.
2414
+ */
2415
+ function snake_case(name){
2416
+ var regexp = /[A-Z]/g;
2417
+ var separator = '-';
2418
+ return name.replace(regexp, function(letter, pos) {
2419
+ return (pos ? separator : '') + letter.toLowerCase();
2420
+ });
2421
+ }
2422
+
2423
+ /**
2424
+ * Returns the actual instance of the $tooltip service.
2425
+ * TODO support multiple triggers
2426
+ */
2427
+ this.$get = [ '$window', '$compile', '$timeout', '$parse', '$document', '$position', '$interpolate', function ( $window, $compile, $timeout, $parse, $document, $position, $interpolate ) {
2428
+ return function $tooltip ( type, prefix, defaultTriggerShow ) {
2429
+ var options = angular.extend( {}, defaultOptions, globalOptions );
2430
+
2431
+ /**
2432
+ * Returns an object of show and hide triggers.
2433
+ *
2434
+ * If a trigger is supplied,
2435
+ * it is used to show the tooltip; otherwise, it will use the `trigger`
2436
+ * option passed to the `$tooltipProvider.options` method; else it will
2437
+ * default to the trigger supplied to this directive factory.
2438
+ *
2439
+ * The hide trigger is based on the show trigger. If the `trigger` option
2440
+ * was passed to the `$tooltipProvider.options` method, it will use the
2441
+ * mapped trigger from `triggerMap` or the passed trigger if the map is
2442
+ * undefined; otherwise, it uses the `triggerMap` value of the show
2443
+ * trigger; else it will just use the show trigger.
2444
+ */
2445
+ function getTriggers ( trigger ) {
2446
+ var show = trigger || options.trigger || defaultTriggerShow;
2447
+ var hide = triggerMap[show] || show;
2448
+ return {
2449
+ show: show,
2450
+ hide: hide
2451
+ };
2452
+ }
2453
+
2454
+ var directiveName = snake_case( type );
2455
+
2456
+ var startSym = $interpolate.startSymbol();
2457
+ var endSym = $interpolate.endSymbol();
2458
+ var template =
2459
+ '<div '+ directiveName +'-popup '+
2460
+ 'title="'+startSym+'tt_title'+endSym+'" '+
2461
+ 'content="'+startSym+'tt_content'+endSym+'" '+
2462
+ 'placement="'+startSym+'tt_placement'+endSym+'" '+
2463
+ 'animation="tt_animation" '+
2464
+ 'is-open="tt_isOpen"'+
2465
+ '>'+
2466
+ '</div>';
2467
+
2468
+ return {
2469
+ restrict: 'EA',
2470
+ scope: true,
2471
+ compile: function (tElem, tAttrs) {
2472
+ var tooltipLinker = $compile( template );
2473
+
2474
+ return function link ( scope, element, attrs ) {
2475
+ var tooltip;
2476
+ var transitionTimeout;
2477
+ var popupTimeout;
2478
+ var appendToBody = angular.isDefined( options.appendToBody ) ? options.appendToBody : false;
2479
+ var triggers = getTriggers( undefined );
2480
+ var hasEnableExp = angular.isDefined(attrs[prefix+'Enable']);
2481
+
2482
+ var positionTooltip = function () {
2483
+
2484
+ var ttPosition = $position.positionElements(element, tooltip, scope.tt_placement, appendToBody);
2485
+ ttPosition.top += 'px';
2486
+ ttPosition.left += 'px';
2487
+
2488
+ // Now set the calculated positioning.
2489
+ tooltip.css( ttPosition );
2490
+ };
2491
+
2492
+ // By default, the tooltip is not open.
2493
+ // TODO add ability to start tooltip opened
2494
+ scope.tt_isOpen = false;
2495
+
2496
+ function toggleTooltipBind () {
2497
+ if ( ! scope.tt_isOpen ) {
2498
+ showTooltipBind();
2499
+ } else {
2500
+ hideTooltipBind();
2501
+ }
2502
+ }
2503
+
2504
+ // Show the tooltip with delay if specified, otherwise show it immediately
2505
+ function showTooltipBind() {
2506
+ if(hasEnableExp && !scope.$eval(attrs[prefix+'Enable'])) {
2507
+ return;
2508
+ }
2509
+ if ( scope.tt_popupDelay ) {
2510
+ // Do nothing if the tooltip was already scheduled to pop-up.
2511
+ // This happens if show is triggered multiple times before any hide is triggered.
2512
+ if (!popupTimeout) {
2513
+ popupTimeout = $timeout( show, scope.tt_popupDelay, false );
2514
+ popupTimeout.then(function(reposition){reposition();});
2515
+ }
2516
+ } else {
2517
+ show()();
2518
+ }
2519
+ }
2520
+
2521
+ function hideTooltipBind () {
2522
+ scope.$apply(function () {
2523
+ hide();
2524
+ });
2525
+ }
2526
+
2527
+ // Show the tooltip popup element.
2528
+ function show() {
2529
+
2530
+ popupTimeout = null;
2531
+
2532
+ // If there is a pending remove transition, we must cancel it, lest the
2533
+ // tooltip be mysteriously removed.
2534
+ if ( transitionTimeout ) {
2535
+ $timeout.cancel( transitionTimeout );
2536
+ transitionTimeout = null;
2537
+ }
2538
+
2539
+ // Don't show empty tooltips.
2540
+ if ( ! scope.tt_content ) {
2541
+ return angular.noop;
2542
+ }
2543
+
2544
+ createTooltip();
2545
+
2546
+ // Set the initial positioning.
2547
+ tooltip.css({ top: 0, left: 0, display: 'block' });
2548
+
2549
+ // Now we add it to the DOM because need some info about it. But it's not
2550
+ // visible yet anyway.
2551
+ if ( appendToBody ) {
2552
+ $document.find( 'body' ).append( tooltip );
2553
+ } else {
2554
+ element.after( tooltip );
2555
+ }
2556
+
2557
+ positionTooltip();
2558
+
2559
+ // And show the tooltip.
2560
+ scope.tt_isOpen = true;
2561
+ scope.$digest(); // digest required as $apply is not called
2562
+
2563
+ // Return positioning function as promise callback for correct
2564
+ // positioning after draw.
2565
+ return positionTooltip;
2566
+ }
2567
+
2568
+ // Hide the tooltip popup element.
2569
+ function hide() {
2570
+ // First things first: we don't show it anymore.
2571
+ scope.tt_isOpen = false;
2572
+
2573
+ //if tooltip is going to be shown after delay, we must cancel this
2574
+ $timeout.cancel( popupTimeout );
2575
+ popupTimeout = null;
2576
+
2577
+ // And now we remove it from the DOM. However, if we have animation, we
2578
+ // need to wait for it to expire beforehand.
2579
+ // FIXME: this is a placeholder for a port of the transitions library.
2580
+ if ( scope.tt_animation ) {
2581
+ if (!transitionTimeout) {
2582
+ transitionTimeout = $timeout(removeTooltip, 500);
2583
+ }
2584
+ } else {
2585
+ removeTooltip();
2586
+ }
2587
+ }
2588
+
2589
+ function createTooltip() {
2590
+ // There can only be one tooltip element per directive shown at once.
2591
+ if (tooltip) {
2592
+ removeTooltip();
2593
+ }
2594
+ tooltip = tooltipLinker(scope, function () {});
2595
+
2596
+ // Get contents rendered into the tooltip
2597
+ scope.$digest();
2598
+ }
2599
+
2600
+ function removeTooltip() {
2601
+ transitionTimeout = null;
2602
+ if (tooltip) {
2603
+ tooltip.remove();
2604
+ tooltip = null;
2605
+ }
2606
+ }
2607
+
2608
+ /**
2609
+ * Observe the relevant attributes.
2610
+ */
2611
+ attrs.$observe( type, function ( val ) {
2612
+ scope.tt_content = val;
2613
+
2614
+ if (!val && scope.tt_isOpen ) {
2615
+ hide();
2616
+ }
2617
+ });
2618
+
2619
+ attrs.$observe( prefix+'Title', function ( val ) {
2620
+ scope.tt_title = val;
2621
+ });
2622
+
2623
+ attrs.$observe( prefix+'Placement', function ( val ) {
2624
+ scope.tt_placement = angular.isDefined( val ) ? val : options.placement;
2625
+ });
2626
+
2627
+ attrs.$observe( prefix+'PopupDelay', function ( val ) {
2628
+ var delay = parseInt( val, 10 );
2629
+ scope.tt_popupDelay = ! isNaN(delay) ? delay : options.popupDelay;
2630
+ });
2631
+
2632
+ var unregisterTriggers = function () {
2633
+ element.unbind(triggers.show, showTooltipBind);
2634
+ element.unbind(triggers.hide, hideTooltipBind);
2635
+ };
2636
+
2637
+ attrs.$observe( prefix+'Trigger', function ( val ) {
2638
+ unregisterTriggers();
2639
+
2640
+ triggers = getTriggers( val );
2641
+
2642
+ if ( triggers.show === triggers.hide ) {
2643
+ element.bind( triggers.show, toggleTooltipBind );
2644
+ } else {
2645
+ element.bind( triggers.show, showTooltipBind );
2646
+ element.bind( triggers.hide, hideTooltipBind );
2647
+ }
2648
+ });
2649
+
2650
+ var animation = scope.$eval(attrs[prefix + 'Animation']);
2651
+ scope.tt_animation = angular.isDefined(animation) ? !!animation : options.animation;
2652
+
2653
+ attrs.$observe( prefix+'AppendToBody', function ( val ) {
2654
+ appendToBody = angular.isDefined( val ) ? $parse( val )( scope ) : appendToBody;
2655
+ });
2656
+
2657
+ // if a tooltip is attached to <body> we need to remove it on
2658
+ // location change as its parent scope will probably not be destroyed
2659
+ // by the change.
2660
+ if ( appendToBody ) {
2661
+ scope.$on('$locationChangeSuccess', function closeTooltipOnLocationChangeSuccess () {
2662
+ if ( scope.tt_isOpen ) {
2663
+ hide();
2664
+ }
2665
+ });
2666
+ }
2667
+
2668
+ // Make sure tooltip is destroyed and removed.
2669
+ scope.$on('$destroy', function onDestroyTooltip() {
2670
+ $timeout.cancel( transitionTimeout );
2671
+ $timeout.cancel( popupTimeout );
2672
+ unregisterTriggers();
2673
+ removeTooltip();
2674
+ });
2675
+ };
2676
+ }
2677
+ };
2678
+ };
2679
+ }];
2680
+ })
2681
+
2682
+ .directive( 'tooltipPopup', function () {
2683
+ return {
2684
+ restrict: 'EA',
2685
+ replace: true,
2686
+ scope: { content: '@', placement: '@', animation: '&', isOpen: '&' },
2687
+ templateUrl: 'template/tooltip/tooltip-popup.html'
2688
+ };
2689
+ })
2690
+
2691
+ .directive( 'tooltip', [ '$tooltip', function ( $tooltip ) {
2692
+ return $tooltip( 'tooltip', 'tooltip', 'mouseenter' );
2693
+ }])
2694
+
2695
+ .directive( 'tooltipHtmlUnsafePopup', function () {
2696
+ return {
2697
+ restrict: 'EA',
2698
+ replace: true,
2699
+ scope: { content: '@', placement: '@', animation: '&', isOpen: '&' },
2700
+ templateUrl: 'template/tooltip/tooltip-html-unsafe-popup.html'
2701
+ };
2702
+ })
2703
+
2704
+ .directive( 'tooltipHtmlUnsafe', [ '$tooltip', function ( $tooltip ) {
2705
+ return $tooltip( 'tooltipHtmlUnsafe', 'tooltip', 'mouseenter' );
2706
+ }]);
2707
+
2708
+ /**
2709
+ * The following features are still outstanding: popup delay, animation as a
2710
+ * function, placement as a function, inside, support for more triggers than
2711
+ * just mouse enter/leave, html popovers, and selector delegatation.
2712
+ */
2713
+ angular.module( 'ui.bootstrap.popover', [ 'ui.bootstrap.tooltip' ] )
2714
+
2715
+ .directive( 'popoverPopup', function () {
2716
+ return {
2717
+ restrict: 'EA',
2718
+ replace: true,
2719
+ scope: { title: '@', content: '@', placement: '@', animation: '&', isOpen: '&' },
2720
+ templateUrl: 'template/popover/popover.html'
2721
+ };
2722
+ })
2723
+
2724
+ .directive( 'popover', [ '$tooltip', function ( $tooltip ) {
2725
+ return $tooltip( 'popover', 'popover', 'click' );
2726
+ }]);
2727
+
2728
+ angular.module('ui.bootstrap.progressbar', [])
2729
+
2730
+ .constant('progressConfig', {
2731
+ animate: true,
2732
+ max: 100
2733
+ })
2734
+
2735
+ .controller('ProgressController', ['$scope', '$attrs', 'progressConfig', function($scope, $attrs, progressConfig) {
2736
+ var self = this,
2737
+ animate = angular.isDefined($attrs.animate) ? $scope.$parent.$eval($attrs.animate) : progressConfig.animate;
2738
+
2739
+ this.bars = [];
2740
+ $scope.max = angular.isDefined($attrs.max) ? $scope.$parent.$eval($attrs.max) : progressConfig.max;
2741
+
2742
+ this.addBar = function(bar, element) {
2743
+ if ( !animate ) {
2744
+ element.css({'transition': 'none'});
2745
+ }
2746
+
2747
+ this.bars.push(bar);
2748
+
2749
+ bar.$watch('value', function( value ) {
2750
+ bar.percent = +(100 * value / $scope.max).toFixed(2);
2751
+ });
2752
+
2753
+ bar.$on('$destroy', function() {
2754
+ element = null;
2755
+ self.removeBar(bar);
2756
+ });
2757
+ };
2758
+
2759
+ this.removeBar = function(bar) {
2760
+ this.bars.splice(this.bars.indexOf(bar), 1);
2761
+ };
2762
+ }])
2763
+
2764
+ .directive('progress', function() {
2765
+ return {
2766
+ restrict: 'EA',
2767
+ replace: true,
2768
+ transclude: true,
2769
+ controller: 'ProgressController',
2770
+ require: 'progress',
2771
+ scope: {},
2772
+ templateUrl: 'template/progressbar/progress.html'
2773
+ };
2774
+ })
2775
+
2776
+ .directive('bar', function() {
2777
+ return {
2778
+ restrict: 'EA',
2779
+ replace: true,
2780
+ transclude: true,
2781
+ require: '^progress',
2782
+ scope: {
2783
+ value: '=',
2784
+ type: '@'
2785
+ },
2786
+ templateUrl: 'template/progressbar/bar.html',
2787
+ link: function(scope, element, attrs, progressCtrl) {
2788
+ progressCtrl.addBar(scope, element);
2789
+ }
2790
+ };
2791
+ })
2792
+
2793
+ .directive('progressbar', function() {
2794
+ return {
2795
+ restrict: 'EA',
2796
+ replace: true,
2797
+ transclude: true,
2798
+ controller: 'ProgressController',
2799
+ scope: {
2800
+ value: '=',
2801
+ type: '@'
2802
+ },
2803
+ templateUrl: 'template/progressbar/progressbar.html',
2804
+ link: function(scope, element, attrs, progressCtrl) {
2805
+ progressCtrl.addBar(scope, angular.element(element.children()[0]));
2806
+ }
2807
+ };
2808
+ });
2809
+ angular.module('ui.bootstrap.rating', [])
2810
+
2811
+ .constant('ratingConfig', {
2812
+ max: 5,
2813
+ stateOn: null,
2814
+ stateOff: null
2815
+ })
2816
+
2817
+ .controller('RatingController', ['$scope', '$attrs', 'ratingConfig', function($scope, $attrs, ratingConfig) {
2818
+ var ngModelCtrl = { $setViewValue: angular.noop };
2819
+
2820
+ this.init = function(ngModelCtrl_) {
2821
+ ngModelCtrl = ngModelCtrl_;
2822
+ ngModelCtrl.$render = this.render;
2823
+
2824
+ this.stateOn = angular.isDefined($attrs.stateOn) ? $scope.$parent.$eval($attrs.stateOn) : ratingConfig.stateOn;
2825
+ this.stateOff = angular.isDefined($attrs.stateOff) ? $scope.$parent.$eval($attrs.stateOff) : ratingConfig.stateOff;
2826
+
2827
+ var ratingStates = angular.isDefined($attrs.ratingStates) ? $scope.$parent.$eval($attrs.ratingStates) :
2828
+ new Array( angular.isDefined($attrs.max) ? $scope.$parent.$eval($attrs.max) : ratingConfig.max );
2829
+ $scope.range = this.buildTemplateObjects(ratingStates);
2830
+ };
2831
+
2832
+ this.buildTemplateObjects = function(states) {
2833
+ for (var i = 0, n = states.length; i < n; i++) {
2834
+ states[i] = angular.extend({ index: i }, { stateOn: this.stateOn, stateOff: this.stateOff }, states[i]);
2835
+ }
2836
+ return states;
2837
+ };
2838
+
2839
+ $scope.rate = function(value) {
2840
+ if ( !$scope.readonly && value >= 0 && value <= $scope.range.length ) {
2841
+ ngModelCtrl.$setViewValue(value);
2842
+ ngModelCtrl.$render();
2843
+ }
2844
+ };
2845
+
2846
+ $scope.enter = function(value) {
2847
+ if ( !$scope.readonly ) {
2848
+ $scope.value = value;
2849
+ }
2850
+ $scope.onHover({value: value});
2851
+ };
2852
+
2853
+ $scope.reset = function() {
2854
+ $scope.value = ngModelCtrl.$viewValue;
2855
+ $scope.onLeave();
2856
+ };
2857
+
2858
+ $scope.onKeydown = function(evt) {
2859
+ if (/(37|38|39|40)/.test(evt.which)) {
2860
+ evt.preventDefault();
2861
+ evt.stopPropagation();
2862
+ $scope.rate( $scope.value + (evt.which === 38 || evt.which === 39 ? 1 : -1) );
2863
+ }
2864
+ };
2865
+
2866
+ this.render = function() {
2867
+ $scope.value = ngModelCtrl.$viewValue;
2868
+ };
2869
+ }])
2870
+
2871
+ .directive('rating', function() {
2872
+ return {
2873
+ restrict: 'EA',
2874
+ require: ['rating', 'ngModel'],
2875
+ scope: {
2876
+ readonly: '=?',
2877
+ onHover: '&',
2878
+ onLeave: '&'
2879
+ },
2880
+ controller: 'RatingController',
2881
+ templateUrl: 'template/rating/rating.html',
2882
+ replace: true,
2883
+ link: function(scope, element, attrs, ctrls) {
2884
+ var ratingCtrl = ctrls[0], ngModelCtrl = ctrls[1];
2885
+
2886
+ if ( ngModelCtrl ) {
2887
+ ratingCtrl.init( ngModelCtrl );
2888
+ }
2889
+ }
2890
+ };
2891
+ });
2892
+
2893
+ /**
2894
+ * @ngdoc overview
2895
+ * @name ui.bootstrap.tabs
2896
+ *
2897
+ * @description
2898
+ * AngularJS version of the tabs directive.
2899
+ */
2900
+
2901
+ angular.module('ui.bootstrap.tabs', [])
2902
+
2903
+ .controller('TabsetController', ['$scope', function TabsetCtrl($scope) {
2904
+ var ctrl = this,
2905
+ tabs = ctrl.tabs = $scope.tabs = [];
2906
+
2907
+ ctrl.select = function(selectedTab) {
2908
+ angular.forEach(tabs, function(tab) {
2909
+ if (tab.active && tab !== selectedTab) {
2910
+ tab.active = false;
2911
+ tab.onDeselect();
2912
+ }
2913
+ });
2914
+ selectedTab.active = true;
2915
+ selectedTab.onSelect();
2916
+ };
2917
+
2918
+ ctrl.addTab = function addTab(tab) {
2919
+ tabs.push(tab);
2920
+ // we can't run the select function on the first tab
2921
+ // since that would select it twice
2922
+ if (tabs.length === 1) {
2923
+ tab.active = true;
2924
+ } else if (tab.active) {
2925
+ ctrl.select(tab);
2926
+ }
2927
+ };
2928
+
2929
+ ctrl.removeTab = function removeTab(tab) {
2930
+ var index = tabs.indexOf(tab);
2931
+ //Select a new tab if the tab to be removed is selected
2932
+ if (tab.active && tabs.length > 1) {
2933
+ //If this is the last tab, select the previous tab. else, the next tab.
2934
+ var newActiveIndex = index == tabs.length - 1 ? index - 1 : index + 1;
2935
+ ctrl.select(tabs[newActiveIndex]);
2936
+ }
2937
+ tabs.splice(index, 1);
2938
+ };
2939
+ }])
2940
+
2941
+ /**
2942
+ * @ngdoc directive
2943
+ * @name ui.bootstrap.tabs.directive:tabset
2944
+ * @restrict EA
2945
+ *
2946
+ * @description
2947
+ * Tabset is the outer container for the tabs directive
2948
+ *
2949
+ * @param {boolean=} vertical Whether or not to use vertical styling for the tabs.
2950
+ * @param {boolean=} justified Whether or not to use justified styling for the tabs.
2951
+ *
2952
+ * @example
2953
+ <example module="ui.bootstrap">
2954
+ <file name="index.html">
2955
+ <tabset>
2956
+ <tab heading="Tab 1"><b>First</b> Content!</tab>
2957
+ <tab heading="Tab 2"><i>Second</i> Content!</tab>
2958
+ </tabset>
2959
+ <hr />
2960
+ <tabset vertical="true">
2961
+ <tab heading="Vertical Tab 1"><b>First</b> Vertical Content!</tab>
2962
+ <tab heading="Vertical Tab 2"><i>Second</i> Vertical Content!</tab>
2963
+ </tabset>
2964
+ <tabset justified="true">
2965
+ <tab heading="Justified Tab 1"><b>First</b> Justified Content!</tab>
2966
+ <tab heading="Justified Tab 2"><i>Second</i> Justified Content!</tab>
2967
+ </tabset>
2968
+ </file>
2969
+ </example>
2970
+ */
2971
+ .directive('tabset', function() {
2972
+ return {
2973
+ restrict: 'EA',
2974
+ transclude: true,
2975
+ replace: true,
2976
+ scope: {
2977
+ type: '@'
2978
+ },
2979
+ controller: 'TabsetController',
2980
+ templateUrl: 'template/tabs/tabset.html',
2981
+ link: function(scope, element, attrs) {
2982
+ scope.vertical = angular.isDefined(attrs.vertical) ? scope.$parent.$eval(attrs.vertical) : false;
2983
+ scope.justified = angular.isDefined(attrs.justified) ? scope.$parent.$eval(attrs.justified) : false;
2984
+ }
2985
+ };
2986
+ })
2987
+
2988
+ /**
2989
+ * @ngdoc directive
2990
+ * @name ui.bootstrap.tabs.directive:tab
2991
+ * @restrict EA
2992
+ *
2993
+ * @param {string=} heading The visible heading, or title, of the tab. Set HTML headings with {@link ui.bootstrap.tabs.directive:tabHeading tabHeading}.
2994
+ * @param {string=} select An expression to evaluate when the tab is selected.
2995
+ * @param {boolean=} active A binding, telling whether or not this tab is selected.
2996
+ * @param {boolean=} disabled A binding, telling whether or not this tab is disabled.
2997
+ *
2998
+ * @description
2999
+ * Creates a tab with a heading and content. Must be placed within a {@link ui.bootstrap.tabs.directive:tabset tabset}.
3000
+ *
3001
+ * @example
3002
+ <example module="ui.bootstrap">
3003
+ <file name="index.html">
3004
+ <div ng-controller="TabsDemoCtrl">
3005
+ <button class="btn btn-small" ng-click="items[0].active = true">
3006
+ Select item 1, using active binding
3007
+ </button>
3008
+ <button class="btn btn-small" ng-click="items[1].disabled = !items[1].disabled">
3009
+ Enable/disable item 2, using disabled binding
3010
+ </button>
3011
+ <br />
3012
+ <tabset>
3013
+ <tab heading="Tab 1">First Tab</tab>
3014
+ <tab select="alertMe()">
3015
+ <tab-heading><i class="icon-bell"></i> Alert me!</tab-heading>
3016
+ Second Tab, with alert callback and html heading!
3017
+ </tab>
3018
+ <tab ng-repeat="item in items"
3019
+ heading="{{item.title}}"
3020
+ disabled="item.disabled"
3021
+ active="item.active">
3022
+ {{item.content}}
3023
+ </tab>
3024
+ </tabset>
3025
+ </div>
3026
+ </file>
3027
+ <file name="script.js">
3028
+ function TabsDemoCtrl($scope) {
3029
+ $scope.items = [
3030
+ { title:"Dynamic Title 1", content:"Dynamic Item 0" },
3031
+ { title:"Dynamic Title 2", content:"Dynamic Item 1", disabled: true }
3032
+ ];
3033
+
3034
+ $scope.alertMe = function() {
3035
+ setTimeout(function() {
3036
+ alert("You've selected the alert tab!");
3037
+ });
3038
+ };
3039
+ };
3040
+ </file>
3041
+ </example>
3042
+ */
3043
+
3044
+ /**
3045
+ * @ngdoc directive
3046
+ * @name ui.bootstrap.tabs.directive:tabHeading
3047
+ * @restrict EA
3048
+ *
3049
+ * @description
3050
+ * Creates an HTML heading for a {@link ui.bootstrap.tabs.directive:tab tab}. Must be placed as a child of a tab element.
3051
+ *
3052
+ * @example
3053
+ <example module="ui.bootstrap">
3054
+ <file name="index.html">
3055
+ <tabset>
3056
+ <tab>
3057
+ <tab-heading><b>HTML</b> in my titles?!</tab-heading>
3058
+ And some content, too!
3059
+ </tab>
3060
+ <tab>
3061
+ <tab-heading><i class="icon-heart"></i> Icon heading?!?</tab-heading>
3062
+ That's right.
3063
+ </tab>
3064
+ </tabset>
3065
+ </file>
3066
+ </example>
3067
+ */
3068
+ .directive('tab', ['$parse', function($parse) {
3069
+ return {
3070
+ require: '^tabset',
3071
+ restrict: 'EA',
3072
+ replace: true,
3073
+ templateUrl: 'template/tabs/tab.html',
3074
+ transclude: true,
3075
+ scope: {
3076
+ active: '=?',
3077
+ heading: '@',
3078
+ onSelect: '&select', //This callback is called in contentHeadingTransclude
3079
+ //once it inserts the tab's content into the dom
3080
+ onDeselect: '&deselect'
3081
+ },
3082
+ controller: function() {
3083
+ //Empty controller so other directives can require being 'under' a tab
3084
+ },
3085
+ compile: function(elm, attrs, transclude) {
3086
+ return function postLink(scope, elm, attrs, tabsetCtrl) {
3087
+ scope.$watch('active', function(active) {
3088
+ if (active) {
3089
+ tabsetCtrl.select(scope);
3090
+ }
3091
+ });
3092
+
3093
+ scope.disabled = false;
3094
+ if ( attrs.disabled ) {
3095
+ scope.$parent.$watch($parse(attrs.disabled), function(value) {
3096
+ scope.disabled = !! value;
3097
+ });
3098
+ }
3099
+
3100
+ scope.select = function() {
3101
+ if ( !scope.disabled ) {
3102
+ scope.active = true;
3103
+ }
3104
+ };
3105
+
3106
+ tabsetCtrl.addTab(scope);
3107
+ scope.$on('$destroy', function() {
3108
+ tabsetCtrl.removeTab(scope);
3109
+ });
3110
+
3111
+ //We need to transclude later, once the content container is ready.
3112
+ //when this link happens, we're inside a tab heading.
3113
+ scope.$transcludeFn = transclude;
3114
+ };
3115
+ }
3116
+ };
3117
+ }])
3118
+
3119
+ .directive('tabHeadingTransclude', [function() {
3120
+ return {
3121
+ restrict: 'A',
3122
+ require: '^tab',
3123
+ link: function(scope, elm, attrs, tabCtrl) {
3124
+ scope.$watch('headingElement', function updateHeadingElement(heading) {
3125
+ if (heading) {
3126
+ elm.html('');
3127
+ elm.append(heading);
3128
+ }
3129
+ });
3130
+ }
3131
+ };
3132
+ }])
3133
+
3134
+ .directive('tabContentTransclude', function() {
3135
+ return {
3136
+ restrict: 'A',
3137
+ require: '^tabset',
3138
+ link: function(scope, elm, attrs) {
3139
+ var tab = scope.$eval(attrs.tabContentTransclude);
3140
+
3141
+ //Now our tab is ready to be transcluded: both the tab heading area
3142
+ //and the tab content area are loaded. Transclude 'em both.
3143
+ tab.$transcludeFn(tab.$parent, function(contents) {
3144
+ angular.forEach(contents, function(node) {
3145
+ if (isTabHeading(node)) {
3146
+ //Let tabHeadingTransclude know.
3147
+ tab.headingElement = node;
3148
+ } else {
3149
+ elm.append(node);
3150
+ }
3151
+ });
3152
+ });
3153
+ }
3154
+ };
3155
+ function isTabHeading(node) {
3156
+ return node.tagName && (
3157
+ node.hasAttribute('tab-heading') ||
3158
+ node.hasAttribute('data-tab-heading') ||
3159
+ node.tagName.toLowerCase() === 'tab-heading' ||
3160
+ node.tagName.toLowerCase() === 'data-tab-heading'
3161
+ );
3162
+ }
3163
+ })
3164
+
3165
+ ;
3166
+
3167
+ angular.module('ui.bootstrap.timepicker', [])
3168
+
3169
+ .constant('timepickerConfig', {
3170
+ hourStep: 1,
3171
+ minuteStep: 1,
3172
+ showMeridian: true,
3173
+ meridians: null,
3174
+ readonlyInput: false,
3175
+ mousewheel: true
3176
+ })
3177
+
3178
+ .controller('TimepickerController', ['$scope', '$attrs', '$parse', '$log', '$locale', 'timepickerConfig', function($scope, $attrs, $parse, $log, $locale, timepickerConfig) {
3179
+ var selected = new Date(),
3180
+ ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl
3181
+ meridians = angular.isDefined($attrs.meridians) ? $scope.$parent.$eval($attrs.meridians) : timepickerConfig.meridians || $locale.DATETIME_FORMATS.AMPMS;
3182
+
3183
+ this.init = function( ngModelCtrl_, inputs ) {
3184
+ ngModelCtrl = ngModelCtrl_;
3185
+ ngModelCtrl.$render = this.render;
3186
+
3187
+ var hoursInputEl = inputs.eq(0),
3188
+ minutesInputEl = inputs.eq(1);
3189
+
3190
+ var mousewheel = angular.isDefined($attrs.mousewheel) ? $scope.$parent.$eval($attrs.mousewheel) : timepickerConfig.mousewheel;
3191
+ if ( mousewheel ) {
3192
+ this.setupMousewheelEvents( hoursInputEl, minutesInputEl );
3193
+ }
3194
+
3195
+ $scope.readonlyInput = angular.isDefined($attrs.readonlyInput) ? $scope.$parent.$eval($attrs.readonlyInput) : timepickerConfig.readonlyInput;
3196
+ this.setupInputEvents( hoursInputEl, minutesInputEl );
3197
+ };
3198
+
3199
+ var hourStep = timepickerConfig.hourStep;
3200
+ if ($attrs.hourStep) {
3201
+ $scope.$parent.$watch($parse($attrs.hourStep), function(value) {
3202
+ hourStep = parseInt(value, 10);
3203
+ });
3204
+ }
3205
+
3206
+ var minuteStep = timepickerConfig.minuteStep;
3207
+ if ($attrs.minuteStep) {
3208
+ $scope.$parent.$watch($parse($attrs.minuteStep), function(value) {
3209
+ minuteStep = parseInt(value, 10);
3210
+ });
3211
+ }
3212
+
3213
+ // 12H / 24H mode
3214
+ $scope.showMeridian = timepickerConfig.showMeridian;
3215
+ if ($attrs.showMeridian) {
3216
+ $scope.$parent.$watch($parse($attrs.showMeridian), function(value) {
3217
+ $scope.showMeridian = !!value;
3218
+
3219
+ if ( ngModelCtrl.$error.time ) {
3220
+ // Evaluate from template
3221
+ var hours = getHoursFromTemplate(), minutes = getMinutesFromTemplate();
3222
+ if (angular.isDefined( hours ) && angular.isDefined( minutes )) {
3223
+ selected.setHours( hours );
3224
+ refresh();
3225
+ }
3226
+ } else {
3227
+ updateTemplate();
3228
+ }
3229
+ });
3230
+ }
3231
+
3232
+ // Get $scope.hours in 24H mode if valid
3233
+ function getHoursFromTemplate ( ) {
3234
+ var hours = parseInt( $scope.hours, 10 );
3235
+ var valid = ( $scope.showMeridian ) ? (hours > 0 && hours < 13) : (hours >= 0 && hours < 24);
3236
+ if ( !valid ) {
3237
+ return undefined;
3238
+ }
3239
+
3240
+ if ( $scope.showMeridian ) {
3241
+ if ( hours === 12 ) {
3242
+ hours = 0;
3243
+ }
3244
+ if ( $scope.meridian === meridians[1] ) {
3245
+ hours = hours + 12;
3246
+ }
3247
+ }
3248
+ return hours;
3249
+ }
3250
+
3251
+ function getMinutesFromTemplate() {
3252
+ var minutes = parseInt($scope.minutes, 10);
3253
+ return ( minutes >= 0 && minutes < 60 ) ? minutes : undefined;
3254
+ }
3255
+
3256
+ function pad( value ) {
3257
+ return ( angular.isDefined(value) && value.toString().length < 2 ) ? '0' + value : value;
3258
+ }
3259
+
3260
+ // Respond on mousewheel spin
3261
+ this.setupMousewheelEvents = function( hoursInputEl, minutesInputEl ) {
3262
+ var isScrollingUp = function(e) {
3263
+ if (e.originalEvent) {
3264
+ e = e.originalEvent;
3265
+ }
3266
+ //pick correct delta variable depending on event
3267
+ var delta = (e.wheelDelta) ? e.wheelDelta : -e.deltaY;
3268
+ return (e.detail || delta > 0);
3269
+ };
3270
+
3271
+ hoursInputEl.bind('mousewheel wheel', function(e) {
3272
+ $scope.$apply( (isScrollingUp(e)) ? $scope.incrementHours() : $scope.decrementHours() );
3273
+ e.preventDefault();
3274
+ });
3275
+
3276
+ minutesInputEl.bind('mousewheel wheel', function(e) {
3277
+ $scope.$apply( (isScrollingUp(e)) ? $scope.incrementMinutes() : $scope.decrementMinutes() );
3278
+ e.preventDefault();
3279
+ });
3280
+
3281
+ };
3282
+
3283
+ this.setupInputEvents = function( hoursInputEl, minutesInputEl ) {
3284
+ if ( $scope.readonlyInput ) {
3285
+ $scope.updateHours = angular.noop;
3286
+ $scope.updateMinutes = angular.noop;
3287
+ return;
3288
+ }
3289
+
3290
+ var invalidate = function(invalidHours, invalidMinutes) {
3291
+ ngModelCtrl.$setViewValue( null );
3292
+ ngModelCtrl.$setValidity('time', false);
3293
+ if (angular.isDefined(invalidHours)) {
3294
+ $scope.invalidHours = invalidHours;
3295
+ }
3296
+ if (angular.isDefined(invalidMinutes)) {
3297
+ $scope.invalidMinutes = invalidMinutes;
3298
+ }
3299
+ };
3300
+
3301
+ $scope.updateHours = function() {
3302
+ var hours = getHoursFromTemplate();
3303
+
3304
+ if ( angular.isDefined(hours) ) {
3305
+ selected.setHours( hours );
3306
+ refresh( 'h' );
3307
+ } else {
3308
+ invalidate(true);
3309
+ }
3310
+ };
3311
+
3312
+ hoursInputEl.bind('blur', function(e) {
3313
+ if ( !$scope.invalidHours && $scope.hours < 10) {
3314
+ $scope.$apply( function() {
3315
+ $scope.hours = pad( $scope.hours );
3316
+ });
3317
+ }
3318
+ });
3319
+
3320
+ $scope.updateMinutes = function() {
3321
+ var minutes = getMinutesFromTemplate();
3322
+
3323
+ if ( angular.isDefined(minutes) ) {
3324
+ selected.setMinutes( minutes );
3325
+ refresh( 'm' );
3326
+ } else {
3327
+ invalidate(undefined, true);
3328
+ }
3329
+ };
3330
+
3331
+ minutesInputEl.bind('blur', function(e) {
3332
+ if ( !$scope.invalidMinutes && $scope.minutes < 10 ) {
3333
+ $scope.$apply( function() {
3334
+ $scope.minutes = pad( $scope.minutes );
3335
+ });
3336
+ }
3337
+ });
3338
+
3339
+ };
3340
+
3341
+ this.render = function() {
3342
+ var date = ngModelCtrl.$modelValue ? new Date( ngModelCtrl.$modelValue ) : null;
3343
+
3344
+ if ( isNaN(date) ) {
3345
+ ngModelCtrl.$setValidity('time', false);
3346
+ $log.error('Timepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.');
3347
+ } else {
3348
+ if ( date ) {
3349
+ selected = date;
3350
+ }
3351
+ makeValid();
3352
+ updateTemplate();
3353
+ }
3354
+ };
3355
+
3356
+ // Call internally when we know that model is valid.
3357
+ function refresh( keyboardChange ) {
3358
+ makeValid();
3359
+ ngModelCtrl.$setViewValue( new Date(selected) );
3360
+ updateTemplate( keyboardChange );
3361
+ }
3362
+
3363
+ function makeValid() {
3364
+ ngModelCtrl.$setValidity('time', true);
3365
+ $scope.invalidHours = false;
3366
+ $scope.invalidMinutes = false;
3367
+ }
3368
+
3369
+ function updateTemplate( keyboardChange ) {
3370
+ var hours = selected.getHours(), minutes = selected.getMinutes();
3371
+
3372
+ if ( $scope.showMeridian ) {
3373
+ hours = ( hours === 0 || hours === 12 ) ? 12 : hours % 12; // Convert 24 to 12 hour system
3374
+ }
3375
+
3376
+ $scope.hours = keyboardChange === 'h' ? hours : pad(hours);
3377
+ $scope.minutes = keyboardChange === 'm' ? minutes : pad(minutes);
3378
+ $scope.meridian = selected.getHours() < 12 ? meridians[0] : meridians[1];
3379
+ }
3380
+
3381
+ function addMinutes( minutes ) {
3382
+ var dt = new Date( selected.getTime() + minutes * 60000 );
3383
+ selected.setHours( dt.getHours(), dt.getMinutes() );
3384
+ refresh();
3385
+ }
3386
+
3387
+ $scope.incrementHours = function() {
3388
+ addMinutes( hourStep * 60 );
3389
+ };
3390
+ $scope.decrementHours = function() {
3391
+ addMinutes( - hourStep * 60 );
3392
+ };
3393
+ $scope.incrementMinutes = function() {
3394
+ addMinutes( minuteStep );
3395
+ };
3396
+ $scope.decrementMinutes = function() {
3397
+ addMinutes( - minuteStep );
3398
+ };
3399
+ $scope.toggleMeridian = function() {
3400
+ addMinutes( 12 * 60 * (( selected.getHours() < 12 ) ? 1 : -1) );
3401
+ };
3402
+ }])
3403
+
3404
+ .directive('timepicker', function () {
3405
+ return {
3406
+ restrict: 'EA',
3407
+ require: ['timepicker', '?^ngModel'],
3408
+ controller:'TimepickerController',
3409
+ replace: true,
3410
+ scope: {},
3411
+ templateUrl: 'template/timepicker/timepicker.html',
3412
+ link: function(scope, element, attrs, ctrls) {
3413
+ var timepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1];
3414
+
3415
+ if ( ngModelCtrl ) {
3416
+ timepickerCtrl.init( ngModelCtrl, element.find('input') );
3417
+ }
3418
+ }
3419
+ };
3420
+ });
3421
+
3422
+ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap.bindHtml'])
3423
+
3424
+ /**
3425
+ * A helper service that can parse typeahead's syntax (string provided by users)
3426
+ * Extracted to a separate service for ease of unit testing
3427
+ */
3428
+ .factory('typeaheadParser', ['$parse', function ($parse) {
3429
+
3430
+ // 00000111000000000000022200000000000000003333333333333330000000000044000
3431
+ var TYPEAHEAD_REGEXP = /^\s*(.*?)(?:\s+as\s+(.*?))?\s+for\s+(?:([\$\w][\$\w\d]*))\s+in\s+(.*)$/;
3432
+
3433
+ return {
3434
+ parse:function (input) {
3435
+
3436
+ var match = input.match(TYPEAHEAD_REGEXP);
3437
+ if (!match) {
3438
+ throw new Error(
3439
+ 'Expected typeahead specification in form of "_modelValue_ (as _label_)? for _item_ in _collection_"' +
3440
+ ' but got "' + input + '".');
3441
+ }
3442
+
3443
+ return {
3444
+ itemName:match[3],
3445
+ source:$parse(match[4]),
3446
+ viewMapper:$parse(match[2] || match[1]),
3447
+ modelMapper:$parse(match[1])
3448
+ };
3449
+ }
3450
+ };
3451
+ }])
3452
+
3453
+ .directive('typeahead', ['$compile', '$parse', '$q', '$timeout', '$document', '$position', 'typeaheadParser',
3454
+ function ($compile, $parse, $q, $timeout, $document, $position, typeaheadParser) {
3455
+
3456
+ var HOT_KEYS = [9, 13, 27, 38, 40];
3457
+
3458
+ return {
3459
+ require:'ngModel',
3460
+ link:function (originalScope, element, attrs, modelCtrl) {
3461
+
3462
+ //SUPPORTED ATTRIBUTES (OPTIONS)
3463
+
3464
+ //minimal no of characters that needs to be entered before typeahead kicks-in
3465
+ var minSearch = originalScope.$eval(attrs.typeaheadMinLength) || 1;
3466
+
3467
+ //minimal wait time after last character typed before typehead kicks-in
3468
+ var waitTime = originalScope.$eval(attrs.typeaheadWaitMs) || 0;
3469
+
3470
+ //should it restrict model values to the ones selected from the popup only?
3471
+ var isEditable = originalScope.$eval(attrs.typeaheadEditable) !== false;
3472
+
3473
+ //binding to a variable that indicates if matches are being retrieved asynchronously
3474
+ var isLoadingSetter = $parse(attrs.typeaheadLoading).assign || angular.noop;
3475
+
3476
+ //a callback executed when a match is selected
3477
+ var onSelectCallback = $parse(attrs.typeaheadOnSelect);
3478
+
3479
+ var inputFormatter = attrs.typeaheadInputFormatter ? $parse(attrs.typeaheadInputFormatter) : undefined;
3480
+
3481
+ var appendToBody = attrs.typeaheadAppendToBody ? originalScope.$eval(attrs.typeaheadAppendToBody) : false;
3482
+
3483
+ //INTERNAL VARIABLES
3484
+
3485
+ //model setter executed upon match selection
3486
+ var $setModelValue = $parse(attrs.ngModel).assign;
3487
+
3488
+ //expressions used by typeahead
3489
+ var parserResult = typeaheadParser.parse(attrs.typeahead);
3490
+
3491
+ var hasFocus;
3492
+
3493
+ //create a child scope for the typeahead directive so we are not polluting original scope
3494
+ //with typeahead-specific data (matches, query etc.)
3495
+ var scope = originalScope.$new();
3496
+ originalScope.$on('$destroy', function(){
3497
+ scope.$destroy();
3498
+ });
3499
+
3500
+ // WAI-ARIA
3501
+ var popupId = 'typeahead-' + scope.$id + '-' + Math.floor(Math.random() * 10000);
3502
+ element.attr({
3503
+ 'aria-autocomplete': 'list',
3504
+ 'aria-expanded': false,
3505
+ 'aria-owns': popupId
3506
+ });
3507
+
3508
+ //pop-up element used to display matches
3509
+ var popUpEl = angular.element('<div typeahead-popup></div>');
3510
+ popUpEl.attr({
3511
+ id: popupId,
3512
+ matches: 'matches',
3513
+ active: 'activeIdx',
3514
+ select: 'select(activeIdx)',
3515
+ query: 'query',
3516
+ position: 'position'
3517
+ });
3518
+ //custom item template
3519
+ if (angular.isDefined(attrs.typeaheadTemplateUrl)) {
3520
+ popUpEl.attr('template-url', attrs.typeaheadTemplateUrl);
3521
+ }
3522
+
3523
+ var resetMatches = function() {
3524
+ scope.matches = [];
3525
+ scope.activeIdx = -1;
3526
+ element.attr('aria-expanded', false);
3527
+ };
3528
+
3529
+ var getMatchId = function(index) {
3530
+ return popupId + '-option-' + index;
3531
+ };
3532
+
3533
+ // Indicate that the specified match is the active (pre-selected) item in the list owned by this typeahead.
3534
+ // This attribute is added or removed automatically when the `activeIdx` changes.
3535
+ scope.$watch('activeIdx', function(index) {
3536
+ if (index < 0) {
3537
+ element.removeAttr('aria-activedescendant');
3538
+ } else {
3539
+ element.attr('aria-activedescendant', getMatchId(index));
3540
+ }
3541
+ });
3542
+
3543
+ var getMatchesAsync = function(inputValue) {
3544
+
3545
+ var locals = {$viewValue: inputValue};
3546
+ isLoadingSetter(originalScope, true);
3547
+ $q.when(parserResult.source(originalScope, locals)).then(function(matches) {
3548
+
3549
+ //it might happen that several async queries were in progress if a user were typing fast
3550
+ //but we are interested only in responses that correspond to the current view value
3551
+ var onCurrentRequest = (inputValue === modelCtrl.$viewValue);
3552
+ if (onCurrentRequest && hasFocus) {
3553
+ if (matches.length > 0) {
3554
+
3555
+ scope.activeIdx = 0;
3556
+ scope.matches.length = 0;
3557
+
3558
+ //transform labels
3559
+ for(var i=0; i<matches.length; i++) {
3560
+ locals[parserResult.itemName] = matches[i];
3561
+ scope.matches.push({
3562
+ id: getMatchId(i),
3563
+ label: parserResult.viewMapper(scope, locals),
3564
+ model: matches[i]
3565
+ });
3566
+ }
3567
+
3568
+ scope.query = inputValue;
3569
+ //position pop-up with matches - we need to re-calculate its position each time we are opening a window
3570
+ //with matches as a pop-up might be absolute-positioned and position of an input might have changed on a page
3571
+ //due to other elements being rendered
3572
+ scope.position = appendToBody ? $position.offset(element) : $position.position(element);
3573
+ scope.position.top = scope.position.top + element.prop('offsetHeight');
3574
+
3575
+ element.attr('aria-expanded', true);
3576
+ } else {
3577
+ resetMatches();
3578
+ }
3579
+ }
3580
+ if (onCurrentRequest) {
3581
+ isLoadingSetter(originalScope, false);
3582
+ }
3583
+ }, function(){
3584
+ resetMatches();
3585
+ isLoadingSetter(originalScope, false);
3586
+ });
3587
+ };
3588
+
3589
+ resetMatches();
3590
+
3591
+ //we need to propagate user's query so we can higlight matches
3592
+ scope.query = undefined;
3593
+
3594
+ //Declare the timeout promise var outside the function scope so that stacked calls can be cancelled later
3595
+ var timeoutPromise;
3596
+
3597
+ //plug into $parsers pipeline to open a typeahead on view changes initiated from DOM
3598
+ //$parsers kick-in on all the changes coming from the view as well as manually triggered by $setViewValue
3599
+ modelCtrl.$parsers.unshift(function (inputValue) {
3600
+
3601
+ hasFocus = true;
3602
+
3603
+ if (inputValue && inputValue.length >= minSearch) {
3604
+ if (waitTime > 0) {
3605
+ if (timeoutPromise) {
3606
+ $timeout.cancel(timeoutPromise);//cancel previous timeout
3607
+ }
3608
+ timeoutPromise = $timeout(function () {
3609
+ getMatchesAsync(inputValue);
3610
+ }, waitTime);
3611
+ } else {
3612
+ getMatchesAsync(inputValue);
3613
+ }
3614
+ } else {
3615
+ isLoadingSetter(originalScope, false);
3616
+ resetMatches();
3617
+ }
3618
+
3619
+ if (isEditable) {
3620
+ return inputValue;
3621
+ } else {
3622
+ if (!inputValue) {
3623
+ // Reset in case user had typed something previously.
3624
+ modelCtrl.$setValidity('editable', true);
3625
+ return inputValue;
3626
+ } else {
3627
+ modelCtrl.$setValidity('editable', false);
3628
+ return undefined;
3629
+ }
3630
+ }
3631
+ });
3632
+
3633
+ modelCtrl.$formatters.push(function (modelValue) {
3634
+
3635
+ var candidateViewValue, emptyViewValue;
3636
+ var locals = {};
3637
+
3638
+ if (inputFormatter) {
3639
+
3640
+ locals['$model'] = modelValue;
3641
+ return inputFormatter(originalScope, locals);
3642
+
3643
+ } else {
3644
+
3645
+ //it might happen that we don't have enough info to properly render input value
3646
+ //we need to check for this situation and simply return model value if we can't apply custom formatting
3647
+ locals[parserResult.itemName] = modelValue;
3648
+ candidateViewValue = parserResult.viewMapper(originalScope, locals);
3649
+ locals[parserResult.itemName] = undefined;
3650
+ emptyViewValue = parserResult.viewMapper(originalScope, locals);
3651
+
3652
+ return candidateViewValue!== emptyViewValue ? candidateViewValue : modelValue;
3653
+ }
3654
+ });
3655
+
3656
+ scope.select = function (activeIdx) {
3657
+ //called from within the $digest() cycle
3658
+ var locals = {};
3659
+ var model, item;
3660
+
3661
+ locals[parserResult.itemName] = item = scope.matches[activeIdx].model;
3662
+ model = parserResult.modelMapper(originalScope, locals);
3663
+ $setModelValue(originalScope, model);
3664
+ modelCtrl.$setValidity('editable', true);
3665
+
3666
+ onSelectCallback(originalScope, {
3667
+ $item: item,
3668
+ $model: model,
3669
+ $label: parserResult.viewMapper(originalScope, locals)
3670
+ });
3671
+
3672
+ resetMatches();
3673
+
3674
+ //return focus to the input element if a match was selected via a mouse click event
3675
+ // use timeout to avoid $rootScope:inprog error
3676
+ $timeout(function() { element[0].focus(); }, 0, false);
3677
+ };
3678
+
3679
+ //bind keyboard events: arrows up(38) / down(40), enter(13) and tab(9), esc(27)
3680
+ element.bind('keydown', function (evt) {
3681
+
3682
+ //typeahead is open and an "interesting" key was pressed
3683
+ if (scope.matches.length === 0 || HOT_KEYS.indexOf(evt.which) === -1) {
3684
+ return;
3685
+ }
3686
+
3687
+ evt.preventDefault();
3688
+
3689
+ if (evt.which === 40) {
3690
+ scope.activeIdx = (scope.activeIdx + 1) % scope.matches.length;
3691
+ scope.$digest();
3692
+
3693
+ } else if (evt.which === 38) {
3694
+ scope.activeIdx = (scope.activeIdx ? scope.activeIdx : scope.matches.length) - 1;
3695
+ scope.$digest();
3696
+
3697
+ } else if (evt.which === 13 || evt.which === 9) {
3698
+ scope.$apply(function () {
3699
+ scope.select(scope.activeIdx);
3700
+ });
3701
+
3702
+ } else if (evt.which === 27) {
3703
+ evt.stopPropagation();
3704
+
3705
+ resetMatches();
3706
+ scope.$digest();
3707
+ }
3708
+ });
3709
+
3710
+ element.bind('blur', function (evt) {
3711
+ hasFocus = false;
3712
+ });
3713
+
3714
+ // Keep reference to click handler to unbind it.
3715
+ var dismissClickHandler = function (evt) {
3716
+ if (element[0] !== evt.target) {
3717
+ resetMatches();
3718
+ scope.$digest();
3719
+ }
3720
+ };
3721
+
3722
+ $document.bind('click', dismissClickHandler);
3723
+
3724
+ originalScope.$on('$destroy', function(){
3725
+ $document.unbind('click', dismissClickHandler);
3726
+ });
3727
+
3728
+ var $popup = $compile(popUpEl)(scope);
3729
+ if ( appendToBody ) {
3730
+ $document.find('body').append($popup);
3731
+ } else {
3732
+ element.after($popup);
3733
+ }
3734
+ }
3735
+ };
3736
+
3737
+ }])
3738
+
3739
+ .directive('typeaheadPopup', function () {
3740
+ return {
3741
+ restrict:'EA',
3742
+ scope:{
3743
+ matches:'=',
3744
+ query:'=',
3745
+ active:'=',
3746
+ position:'=',
3747
+ select:'&'
3748
+ },
3749
+ replace:true,
3750
+ templateUrl:'template/typeahead/typeahead-popup.html',
3751
+ link:function (scope, element, attrs) {
3752
+
3753
+ scope.templateUrl = attrs.templateUrl;
3754
+
3755
+ scope.isOpen = function () {
3756
+ return scope.matches.length > 0;
3757
+ };
3758
+
3759
+ scope.isActive = function (matchIdx) {
3760
+ return scope.active == matchIdx;
3761
+ };
3762
+
3763
+ scope.selectActive = function (matchIdx) {
3764
+ scope.active = matchIdx;
3765
+ };
3766
+
3767
+ scope.selectMatch = function (activeIdx) {
3768
+ scope.select({activeIdx:activeIdx});
3769
+ };
3770
+ }
3771
+ };
3772
+ })
3773
+
3774
+ .directive('typeaheadMatch', ['$http', '$templateCache', '$compile', '$parse', function ($http, $templateCache, $compile, $parse) {
3775
+ return {
3776
+ restrict:'EA',
3777
+ scope:{
3778
+ index:'=',
3779
+ match:'=',
3780
+ query:'='
3781
+ },
3782
+ link:function (scope, element, attrs) {
3783
+ var tplUrl = $parse(attrs.templateUrl)(scope.$parent) || 'template/typeahead/typeahead-match.html';
3784
+ $http.get(tplUrl, {cache: $templateCache}).success(function(tplContent){
3785
+ element.replaceWith($compile(tplContent.trim())(scope));
3786
+ });
3787
+ }
3788
+ };
3789
+ }])
3790
+
3791
+ .filter('typeaheadHighlight', function() {
3792
+
3793
+ function escapeRegexp(queryToEscape) {
3794
+ return queryToEscape.replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1');
3795
+ }
3796
+
3797
+ return function(matchItem, query) {
3798
+ return query ? ('' + matchItem).replace(new RegExp(escapeRegexp(query), 'gi'), '<strong>$&</strong>') : matchItem;
3799
+ };
3800
+ });
3801
+
3802
+ angular.module("template/accordion/accordion-group.html", []).run(["$templateCache", function($templateCache) {
3803
+ $templateCache.put("template/accordion/accordion-group.html",
3804
+ "<div class=\"panel panel-default\">\n" +
3805
+ " <div class=\"panel-heading\">\n" +
3806
+ " <h4 class=\"panel-title\">\n" +
3807
+ " <a class=\"accordion-toggle\" ng-click=\"toggleOpen()\" accordion-transclude=\"heading\"><span ng-class=\"{'text-muted': isDisabled}\">{{heading}}</span></a>\n" +
3808
+ " </h4>\n" +
3809
+ " </div>\n" +
3810
+ " <div class=\"panel-collapse\" collapse=\"!isOpen\">\n" +
3811
+ " <div class=\"panel-body\" ng-transclude></div>\n" +
3812
+ " </div>\n" +
3813
+ "</div>");
3814
+ }]);
3815
+
3816
+ angular.module("template/accordion/accordion.html", []).run(["$templateCache", function($templateCache) {
3817
+ $templateCache.put("template/accordion/accordion.html",
3818
+ "<div class=\"panel-group\" ng-transclude></div>");
3819
+ }]);
3820
+
3821
+ angular.module("template/alert/alert.html", []).run(["$templateCache", function($templateCache) {
3822
+ $templateCache.put("template/alert/alert.html",
3823
+ "<div class=\"alert\" ng-class=\"{'alert-{{type || 'warning'}}': true, 'alert-dismissable': closeable}\" role=\"alert\">\n" +
3824
+ " <button ng-show=\"closeable\" type=\"button\" class=\"close\" ng-click=\"close()\">\n" +
3825
+ " <span aria-hidden=\"true\">&times;</span>\n" +
3826
+ " <span class=\"sr-only\">Close</span>\n" +
3827
+ " </button>\n" +
3828
+ " <div ng-transclude></div>\n" +
3829
+ "</div>\n" +
3830
+ "");
3831
+ }]);
3832
+
3833
+ angular.module("template/carousel/carousel.html", []).run(["$templateCache", function($templateCache) {
3834
+ $templateCache.put("template/carousel/carousel.html",
3835
+ "<div ng-mouseenter=\"pause()\" ng-mouseleave=\"play()\" class=\"carousel\" ng-swipe-right=\"prev()\" ng-swipe-left=\"next()\">\n" +
3836
+ " <ol class=\"carousel-indicators\" ng-show=\"slides.length > 1\">\n" +
3837
+ " <li ng-repeat=\"slide in slides track by $index\" ng-class=\"{active: isActive(slide)}\" ng-click=\"select(slide)\"></li>\n" +
3838
+ " </ol>\n" +
3839
+ " <div class=\"carousel-inner\" ng-transclude></div>\n" +
3840
+ " <a class=\"left carousel-control\" ng-click=\"prev()\" ng-show=\"slides.length > 1\"><span class=\"glyphicon glyphicon-chevron-left\"></span></a>\n" +
3841
+ " <a class=\"right carousel-control\" ng-click=\"next()\" ng-show=\"slides.length > 1\"><span class=\"glyphicon glyphicon-chevron-right\"></span></a>\n" +
3842
+ "</div>\n" +
3843
+ "");
3844
+ }]);
3845
+
3846
+ angular.module("template/carousel/slide.html", []).run(["$templateCache", function($templateCache) {
3847
+ $templateCache.put("template/carousel/slide.html",
3848
+ "<div ng-class=\"{\n" +
3849
+ " 'active': leaving || (active && !entering),\n" +
3850
+ " 'prev': (next || active) && direction=='prev',\n" +
3851
+ " 'next': (next || active) && direction=='next',\n" +
3852
+ " 'right': direction=='prev',\n" +
3853
+ " 'left': direction=='next'\n" +
3854
+ " }\" class=\"item text-center\" ng-transclude></div>\n" +
3855
+ "");
3856
+ }]);
3857
+
3858
+ angular.module("template/datepicker/datepicker.html", []).run(["$templateCache", function($templateCache) {
3859
+ $templateCache.put("template/datepicker/datepicker.html",
3860
+ "<div ng-switch=\"datepickerMode\" role=\"application\" ng-keydown=\"keydown($event)\">\n" +
3861
+ " <daypicker ng-switch-when=\"day\" tabindex=\"0\"></daypicker>\n" +
3862
+ " <monthpicker ng-switch-when=\"month\" tabindex=\"0\"></monthpicker>\n" +
3863
+ " <yearpicker ng-switch-when=\"year\" tabindex=\"0\"></yearpicker>\n" +
3864
+ "</div>");
3865
+ }]);
3866
+
3867
+ angular.module("template/datepicker/day.html", []).run(["$templateCache", function($templateCache) {
3868
+ $templateCache.put("template/datepicker/day.html",
3869
+ "<table role=\"grid\" aria-labelledby=\"{{uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\n" +
3870
+ " <thead>\n" +
3871
+ " <tr>\n" +
3872
+ " <th><button type=\"button\" class=\"btn btn-default btn-sm pull-left\" ng-click=\"move(-1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-left\"></i></button></th>\n" +
3873
+ " <th colspan=\"{{5 + showWeeks}}\"><button id=\"{{uniqueId}}-title\" role=\"heading\" aria-live=\"assertive\" aria-atomic=\"true\" type=\"button\" class=\"btn btn-default btn-sm\" ng-click=\"toggleMode()\" tabindex=\"-1\" style=\"width:100%;\"><strong>{{title}}</strong></button></th>\n" +
3874
+ " <th><button type=\"button\" class=\"btn btn-default btn-sm pull-right\" ng-click=\"move(1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-right\"></i></button></th>\n" +
3875
+ " </tr>\n" +
3876
+ " <tr>\n" +
3877
+ " <th ng-show=\"showWeeks\" class=\"text-center\"></th>\n" +
3878
+ " <th ng-repeat=\"label in labels track by $index\" class=\"text-center\"><small aria-label=\"{{label.full}}\">{{label.abbr}}</small></th>\n" +
3879
+ " </tr>\n" +
3880
+ " </thead>\n" +
3881
+ " <tbody>\n" +
3882
+ " <tr ng-repeat=\"row in rows track by $index\">\n" +
3883
+ " <td ng-show=\"showWeeks\" class=\"text-center h6\"><em>{{ weekNumbers[$index] }}</em></td>\n" +
3884
+ " <td ng-repeat=\"dt in row track by dt.date\" class=\"text-center\" role=\"gridcell\" id=\"{{dt.uid}}\" aria-disabled=\"{{!!dt.disabled}}\">\n" +
3885
+ " <button type=\"button\" style=\"width:100%;\" class=\"btn btn-default btn-sm\" ng-class=\"{'btn-info': dt.selected, active: isActive(dt)}\" ng-click=\"select(dt.date)\" ng-disabled=\"dt.disabled\" tabindex=\"-1\"><span ng-class=\"{'text-muted': dt.secondary, 'text-info': dt.current}\">{{dt.label}}</span></button>\n" +
3886
+ " </td>\n" +
3887
+ " </tr>\n" +
3888
+ " </tbody>\n" +
3889
+ "</table>\n" +
3890
+ "");
3891
+ }]);
3892
+
3893
+ angular.module("template/datepicker/month.html", []).run(["$templateCache", function($templateCache) {
3894
+ $templateCache.put("template/datepicker/month.html",
3895
+ "<table role=\"grid\" aria-labelledby=\"{{uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\n" +
3896
+ " <thead>\n" +
3897
+ " <tr>\n" +
3898
+ " <th><button type=\"button\" class=\"btn btn-default btn-sm pull-left\" ng-click=\"move(-1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-left\"></i></button></th>\n" +
3899
+ " <th><button id=\"{{uniqueId}}-title\" role=\"heading\" aria-live=\"assertive\" aria-atomic=\"true\" type=\"button\" class=\"btn btn-default btn-sm\" ng-click=\"toggleMode()\" tabindex=\"-1\" style=\"width:100%;\"><strong>{{title}}</strong></button></th>\n" +
3900
+ " <th><button type=\"button\" class=\"btn btn-default btn-sm pull-right\" ng-click=\"move(1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-right\"></i></button></th>\n" +
3901
+ " </tr>\n" +
3902
+ " </thead>\n" +
3903
+ " <tbody>\n" +
3904
+ " <tr ng-repeat=\"row in rows track by $index\">\n" +
3905
+ " <td ng-repeat=\"dt in row track by dt.date\" class=\"text-center\" role=\"gridcell\" id=\"{{dt.uid}}\" aria-disabled=\"{{!!dt.disabled}}\">\n" +
3906
+ " <button type=\"button\" style=\"width:100%;\" class=\"btn btn-default\" ng-class=\"{'btn-info': dt.selected, active: isActive(dt)}\" ng-click=\"select(dt.date)\" ng-disabled=\"dt.disabled\" tabindex=\"-1\"><span ng-class=\"{'text-info': dt.current}\">{{dt.label}}</span></button>\n" +
3907
+ " </td>\n" +
3908
+ " </tr>\n" +
3909
+ " </tbody>\n" +
3910
+ "</table>\n" +
3911
+ "");
3912
+ }]);
3913
+
3914
+ angular.module("template/datepicker/popup.html", []).run(["$templateCache", function($templateCache) {
3915
+ $templateCache.put("template/datepicker/popup.html",
3916
+ "<ul class=\"dropdown-menu\" ng-style=\"{display: (isOpen && 'block') || 'none', top: position.top+'px', left: position.left+'px'}\" ng-keydown=\"keydown($event)\">\n" +
3917
+ " <li ng-transclude></li>\n" +
3918
+ " <li ng-if=\"showButtonBar\" style=\"padding:10px 9px 2px\">\n" +
3919
+ " <span class=\"btn-group\">\n" +
3920
+ " <button type=\"button\" class=\"btn btn-sm btn-info\" ng-click=\"select('today')\">{{ getText('current') }}</button>\n" +
3921
+ " <button type=\"button\" class=\"btn btn-sm btn-danger\" ng-click=\"select(null)\">{{ getText('clear') }}</button>\n" +
3922
+ " </span>\n" +
3923
+ " <button type=\"button\" class=\"btn btn-sm btn-success pull-right\" ng-click=\"close()\">{{ getText('close') }}</button>\n" +
3924
+ " </li>\n" +
3925
+ "</ul>\n" +
3926
+ "");
3927
+ }]);
3928
+
3929
+ angular.module("template/datepicker/year.html", []).run(["$templateCache", function($templateCache) {
3930
+ $templateCache.put("template/datepicker/year.html",
3931
+ "<table role=\"grid\" aria-labelledby=\"{{uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\n" +
3932
+ " <thead>\n" +
3933
+ " <tr>\n" +
3934
+ " <th><button type=\"button\" class=\"btn btn-default btn-sm pull-left\" ng-click=\"move(-1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-left\"></i></button></th>\n" +
3935
+ " <th colspan=\"3\"><button id=\"{{uniqueId}}-title\" role=\"heading\" aria-live=\"assertive\" aria-atomic=\"true\" type=\"button\" class=\"btn btn-default btn-sm\" ng-click=\"toggleMode()\" tabindex=\"-1\" style=\"width:100%;\"><strong>{{title}}</strong></button></th>\n" +
3936
+ " <th><button type=\"button\" class=\"btn btn-default btn-sm pull-right\" ng-click=\"move(1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-right\"></i></button></th>\n" +
3937
+ " </tr>\n" +
3938
+ " </thead>\n" +
3939
+ " <tbody>\n" +
3940
+ " <tr ng-repeat=\"row in rows track by $index\">\n" +
3941
+ " <td ng-repeat=\"dt in row track by dt.date\" class=\"text-center\" role=\"gridcell\" id=\"{{dt.uid}}\" aria-disabled=\"{{!!dt.disabled}}\">\n" +
3942
+ " <button type=\"button\" style=\"width:100%;\" class=\"btn btn-default\" ng-class=\"{'btn-info': dt.selected, active: isActive(dt)}\" ng-click=\"select(dt.date)\" ng-disabled=\"dt.disabled\" tabindex=\"-1\"><span ng-class=\"{'text-info': dt.current}\">{{dt.label}}</span></button>\n" +
3943
+ " </td>\n" +
3944
+ " </tr>\n" +
3945
+ " </tbody>\n" +
3946
+ "</table>\n" +
3947
+ "");
3948
+ }]);
3949
+
3950
+ angular.module("template/modal/backdrop.html", []).run(["$templateCache", function($templateCache) {
3951
+ $templateCache.put("template/modal/backdrop.html",
3952
+ "<div class=\"modal-backdrop fade\"\n" +
3953
+ " ng-class=\"{in: animate}\"\n" +
3954
+ " ng-style=\"{'z-index': 1040 + (index && 1 || 0) + index*10}\"\n" +
3955
+ "></div>\n" +
3956
+ "");
3957
+ }]);
3958
+
3959
+ angular.module("template/modal/window.html", []).run(["$templateCache", function($templateCache) {
3960
+ $templateCache.put("template/modal/window.html",
3961
+ "<div tabindex=\"-1\" role=\"dialog\" class=\"modal fade\" ng-class=\"{in: animate}\" ng-style=\"{'z-index': 1050 + index*10, display: 'block'}\" ng-click=\"close($event)\">\n" +
3962
+ " <div class=\"modal-dialog\" ng-class=\"{'modal-sm': size == 'sm', 'modal-lg': size == 'lg'}\"><div class=\"modal-content\" ng-transclude></div></div>\n" +
3963
+ "</div>");
3964
+ }]);
3965
+
3966
+ angular.module("template/pagination/pager.html", []).run(["$templateCache", function($templateCache) {
3967
+ $templateCache.put("template/pagination/pager.html",
3968
+ "<ul class=\"pager\">\n" +
3969
+ " <li ng-class=\"{disabled: noPrevious(), previous: align}\"><a href ng-click=\"selectPage(page - 1)\">{{getText('previous')}}</a></li>\n" +
3970
+ " <li ng-class=\"{disabled: noNext(), next: align}\"><a href ng-click=\"selectPage(page + 1)\">{{getText('next')}}</a></li>\n" +
3971
+ "</ul>");
3972
+ }]);
3973
+
3974
+ angular.module("template/pagination/pagination.html", []).run(["$templateCache", function($templateCache) {
3975
+ $templateCache.put("template/pagination/pagination.html",
3976
+ "<ul class=\"pagination\">\n" +
3977
+ " <li ng-if=\"boundaryLinks\" ng-class=\"{disabled: noPrevious()}\"><a href ng-click=\"selectPage(1)\">{{getText('first')}}</a></li>\n" +
3978
+ " <li ng-if=\"directionLinks\" ng-class=\"{disabled: noPrevious()}\"><a href ng-click=\"selectPage(page - 1)\">{{getText('previous')}}</a></li>\n" +
3979
+ " <li ng-repeat=\"page in pages track by $index\" ng-class=\"{active: page.active}\"><a href ng-click=\"selectPage(page.number)\">{{page.text}}</a></li>\n" +
3980
+ " <li ng-if=\"directionLinks\" ng-class=\"{disabled: noNext()}\"><a href ng-click=\"selectPage(page + 1)\">{{getText('next')}}</a></li>\n" +
3981
+ " <li ng-if=\"boundaryLinks\" ng-class=\"{disabled: noNext()}\"><a href ng-click=\"selectPage(totalPages)\">{{getText('last')}}</a></li>\n" +
3982
+ "</ul>");
3983
+ }]);
3984
+
3985
+ angular.module("template/tooltip/tooltip-html-unsafe-popup.html", []).run(["$templateCache", function($templateCache) {
3986
+ $templateCache.put("template/tooltip/tooltip-html-unsafe-popup.html",
3987
+ "<div class=\"tooltip {{placement}}\" ng-class=\"{ in: isOpen(), fade: animation() }\">\n" +
3988
+ " <div class=\"tooltip-arrow\"></div>\n" +
3989
+ " <div class=\"tooltip-inner\" bind-html-unsafe=\"content\"></div>\n" +
3990
+ "</div>\n" +
3991
+ "");
3992
+ }]);
3993
+
3994
+ angular.module("template/tooltip/tooltip-popup.html", []).run(["$templateCache", function($templateCache) {
3995
+ $templateCache.put("template/tooltip/tooltip-popup.html",
3996
+ "<div class=\"tooltip {{placement}}\" ng-class=\"{ in: isOpen(), fade: animation() }\">\n" +
3997
+ " <div class=\"tooltip-arrow\"></div>\n" +
3998
+ " <div class=\"tooltip-inner\" ng-bind=\"content\"></div>\n" +
3999
+ "</div>\n" +
4000
+ "");
4001
+ }]);
4002
+
4003
+ angular.module("template/popover/popover.html", []).run(["$templateCache", function($templateCache) {
4004
+ $templateCache.put("template/popover/popover.html",
4005
+ "<div class=\"popover {{placement}}\" ng-class=\"{ in: isOpen(), fade: animation() }\">\n" +
4006
+ " <div class=\"arrow\"></div>\n" +
4007
+ "\n" +
4008
+ " <div class=\"popover-inner\">\n" +
4009
+ " <h3 class=\"popover-title\" ng-bind=\"title\" ng-show=\"title\"></h3>\n" +
4010
+ " <div class=\"popover-content\" ng-bind=\"content\"></div>\n" +
4011
+ " </div>\n" +
4012
+ "</div>\n" +
4013
+ "");
4014
+ }]);
4015
+
4016
+ angular.module("template/progressbar/bar.html", []).run(["$templateCache", function($templateCache) {
4017
+ $templateCache.put("template/progressbar/bar.html",
4018
+ "<div class=\"progress-bar\" ng-class=\"type && 'progress-bar-' + type\" role=\"progressbar\" aria-valuenow=\"{{value}}\" aria-valuemin=\"0\" aria-valuemax=\"{{max}}\" ng-style=\"{width: percent + '%'}\" aria-valuetext=\"{{percent | number:0}}%\" ng-transclude></div>");
4019
+ }]);
4020
+
4021
+ angular.module("template/progressbar/progress.html", []).run(["$templateCache", function($templateCache) {
4022
+ $templateCache.put("template/progressbar/progress.html",
4023
+ "<div class=\"progress\" ng-transclude></div>");
4024
+ }]);
4025
+
4026
+ angular.module("template/progressbar/progressbar.html", []).run(["$templateCache", function($templateCache) {
4027
+ $templateCache.put("template/progressbar/progressbar.html",
4028
+ "<div class=\"progress\">\n" +
4029
+ " <div class=\"progress-bar\" ng-class=\"type && 'progress-bar-' + type\" role=\"progressbar\" aria-valuenow=\"{{value}}\" aria-valuemin=\"0\" aria-valuemax=\"{{max}}\" ng-style=\"{width: percent + '%'}\" aria-valuetext=\"{{percent | number:0}}%\" ng-transclude></div>\n" +
4030
+ "</div>");
4031
+ }]);
4032
+
4033
+ angular.module("template/rating/rating.html", []).run(["$templateCache", function($templateCache) {
4034
+ $templateCache.put("template/rating/rating.html",
4035
+ "<span ng-mouseleave=\"reset()\" ng-keydown=\"onKeydown($event)\" tabindex=\"0\" role=\"slider\" aria-valuemin=\"0\" aria-valuemax=\"{{range.length}}\" aria-valuenow=\"{{value}}\">\n" +
4036
+ " <i ng-repeat=\"r in range track by $index\" ng-mouseenter=\"enter($index + 1)\" ng-click=\"rate($index + 1)\" class=\"glyphicon\" ng-class=\"$index < value && (r.stateOn || 'glyphicon-star') || (r.stateOff || 'glyphicon-star-empty')\">\n" +
4037
+ " <span class=\"sr-only\">({{ $index < value ? '*' : ' ' }})</span>\n" +
4038
+ " </i>\n" +
4039
+ "</span>");
4040
+ }]);
4041
+
4042
+ angular.module("template/tabs/tab.html", []).run(["$templateCache", function($templateCache) {
4043
+ $templateCache.put("template/tabs/tab.html",
4044
+ "<li ng-class=\"{active: active, disabled: disabled}\">\n" +
4045
+ " <a ng-click=\"select()\" tab-heading-transclude>{{heading}}</a>\n" +
4046
+ "</li>\n" +
4047
+ "");
4048
+ }]);
4049
+
4050
+ angular.module("template/tabs/tabset-titles.html", []).run(["$templateCache", function($templateCache) {
4051
+ $templateCache.put("template/tabs/tabset-titles.html",
4052
+ "<ul class=\"nav {{type && 'nav-' + type}}\" ng-class=\"{'nav-stacked': vertical}\">\n" +
4053
+ "</ul>\n" +
4054
+ "");
4055
+ }]);
4056
+
4057
+ angular.module("template/tabs/tabset.html", []).run(["$templateCache", function($templateCache) {
4058
+ $templateCache.put("template/tabs/tabset.html",
4059
+ "\n" +
4060
+ "<div>\n" +
4061
+ " <ul class=\"nav nav-{{type || 'tabs'}}\" ng-class=\"{'nav-stacked': vertical, 'nav-justified': justified}\" ng-transclude></ul>\n" +
4062
+ " <div class=\"tab-content\">\n" +
4063
+ " <div class=\"tab-pane\" \n" +
4064
+ " ng-repeat=\"tab in tabs\" \n" +
4065
+ " ng-class=\"{active: tab.active}\"\n" +
4066
+ " tab-content-transclude=\"tab\">\n" +
4067
+ " </div>\n" +
4068
+ " </div>\n" +
4069
+ "</div>\n" +
4070
+ "");
4071
+ }]);
4072
+
4073
+ angular.module("template/timepicker/timepicker.html", []).run(["$templateCache", function($templateCache) {
4074
+ $templateCache.put("template/timepicker/timepicker.html",
4075
+ "<table>\n" +
4076
+ " <tbody>\n" +
4077
+ " <tr class=\"text-center\">\n" +
4078
+ " <td><a ng-click=\"incrementHours()\" class=\"btn btn-link\"><span class=\"glyphicon glyphicon-chevron-up\"></span></a></td>\n" +
4079
+ " <td>&nbsp;</td>\n" +
4080
+ " <td><a ng-click=\"incrementMinutes()\" class=\"btn btn-link\"><span class=\"glyphicon glyphicon-chevron-up\"></span></a></td>\n" +
4081
+ " <td ng-show=\"showMeridian\"></td>\n" +
4082
+ " </tr>\n" +
4083
+ " <tr>\n" +
4084
+ " <td style=\"width:50px;\" class=\"form-group\" ng-class=\"{'has-error': invalidHours}\">\n" +
4085
+ " <input type=\"text\" ng-model=\"hours\" ng-change=\"updateHours()\" class=\"form-control text-center\" ng-mousewheel=\"incrementHours()\" ng-readonly=\"readonlyInput\" maxlength=\"2\">\n" +
4086
+ " </td>\n" +
4087
+ " <td>:</td>\n" +
4088
+ " <td style=\"width:50px;\" class=\"form-group\" ng-class=\"{'has-error': invalidMinutes}\">\n" +
4089
+ " <input type=\"text\" ng-model=\"minutes\" ng-change=\"updateMinutes()\" class=\"form-control text-center\" ng-readonly=\"readonlyInput\" maxlength=\"2\">\n" +
4090
+ " </td>\n" +
4091
+ " <td ng-show=\"showMeridian\"><button type=\"button\" class=\"btn btn-default text-center\" ng-click=\"toggleMeridian()\">{{meridian}}</button></td>\n" +
4092
+ " </tr>\n" +
4093
+ " <tr class=\"text-center\">\n" +
4094
+ " <td><a ng-click=\"decrementHours()\" class=\"btn btn-link\"><span class=\"glyphicon glyphicon-chevron-down\"></span></a></td>\n" +
4095
+ " <td>&nbsp;</td>\n" +
4096
+ " <td><a ng-click=\"decrementMinutes()\" class=\"btn btn-link\"><span class=\"glyphicon glyphicon-chevron-down\"></span></a></td>\n" +
4097
+ " <td ng-show=\"showMeridian\"></td>\n" +
4098
+ " </tr>\n" +
4099
+ " </tbody>\n" +
4100
+ "</table>\n" +
4101
+ "");
4102
+ }]);
4103
+
4104
+ angular.module("template/typeahead/typeahead-match.html", []).run(["$templateCache", function($templateCache) {
4105
+ $templateCache.put("template/typeahead/typeahead-match.html",
4106
+ "<a tabindex=\"-1\" bind-html-unsafe=\"match.label | typeaheadHighlight:query\"></a>");
4107
+ }]);
4108
+
4109
+ angular.module("template/typeahead/typeahead-popup.html", []).run(["$templateCache", function($templateCache) {
4110
+ $templateCache.put("template/typeahead/typeahead-popup.html",
4111
+ "<ul class=\"dropdown-menu\" ng-if=\"isOpen()\" ng-style=\"{top: position.top+'px', left: position.left+'px'}\" style=\"display: block;\" role=\"listbox\" aria-hidden=\"{{!isOpen()}}\">\n" +
4112
+ " <li ng-repeat=\"match in matches track by $index\" ng-class=\"{active: isActive($index) }\" ng-mouseenter=\"selectActive($index)\" ng-click=\"selectMatch($index)\" role=\"option\" id=\"{{match.id}}\">\n" +
4113
+ " <div typeahead-match index=\"$index\" match=\"match\" query=\"query\" template-url=\"templateUrl\"></div>\n" +
4114
+ " </li>\n" +
4115
+ "</ul>");
4116
+ }]);