voltar 0.0.23

Sign up to get free protection for your applications and to get access to all the features.
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
+ }]);