adminlte2assets-rails 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (278) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/CODE_OF_CONDUCT.md +49 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +244 -0
  7. data/Rakefile +2 -0
  8. data/adminlte2assets-rails.gemspec +42 -0
  9. data/bin/console +14 -0
  10. data/bin/setup +8 -0
  11. data/lib/adminlte2assets/rails/engine.rb +7 -0
  12. data/lib/adminlte2assets/rails/version.rb +5 -0
  13. data/lib/adminlte2assets/rails.rb +8 -0
  14. data/lib/adminlte2assets-rails.rb +7 -0
  15. data/vendor/assets/images/avatar.png +0 -0
  16. data/vendor/assets/images/avatar04.png +0 -0
  17. data/vendor/assets/images/avatar2.png +0 -0
  18. data/vendor/assets/images/avatar3.png +0 -0
  19. data/vendor/assets/images/avatar5.png +0 -0
  20. data/vendor/assets/images/boxed-bg.jpg +0 -0
  21. data/vendor/assets/images/boxed-bg.png +0 -0
  22. data/vendor/assets/images/credit/american-express.png +0 -0
  23. data/vendor/assets/images/credit/cirrus.png +0 -0
  24. data/vendor/assets/images/credit/mastercard.png +0 -0
  25. data/vendor/assets/images/credit/mestro.png +0 -0
  26. data/vendor/assets/images/credit/paypal.png +0 -0
  27. data/vendor/assets/images/credit/paypal2.png +0 -0
  28. data/vendor/assets/images/credit/visa.png +0 -0
  29. data/vendor/assets/images/default-50x50.gif +0 -0
  30. data/vendor/assets/images/icons.png +0 -0
  31. data/vendor/assets/images/ionslider/sprite-skin-flat.png +0 -0
  32. data/vendor/assets/images/ionslider/sprite-skin-nice.png +0 -0
  33. data/vendor/assets/images/photo1.png +0 -0
  34. data/vendor/assets/images/photo2.png +0 -0
  35. data/vendor/assets/images/photo3.jpg +0 -0
  36. data/vendor/assets/images/photo4.jpg +0 -0
  37. data/vendor/assets/images/user1-128x128.jpg +0 -0
  38. data/vendor/assets/images/user2-160x160.jpg +0 -0
  39. data/vendor/assets/images/user3-128x128.jpg +0 -0
  40. data/vendor/assets/images/user4-128x128.jpg +0 -0
  41. data/vendor/assets/images/user5-128x128.jpg +0 -0
  42. data/vendor/assets/images/user6-128x128.jpg +0 -0
  43. data/vendor/assets/images/user7-128x128.jpg +0 -0
  44. data/vendor/assets/images/user8-128x128.jpg +0 -0
  45. data/vendor/assets/javascript/AdminLTE/app.min.js +13 -0
  46. data/vendor/assets/javascript/AdminLTE/plugins/bootstrap-slider/bootstrap-slider.js +1576 -0
  47. data/vendor/assets/javascript/AdminLTE/plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.all.js +14975 -0
  48. data/vendor/assets/javascript/AdminLTE/plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.all.min.js +8 -0
  49. data/vendor/assets/javascript/AdminLTE/plugins/chartjs/Chart.js +3736 -0
  50. data/vendor/assets/javascript/AdminLTE/plugins/chartjs/Chart.min.js +11 -0
  51. data/vendor/assets/javascript/AdminLTE/plugins/colorpicker/bootstrap-colorpicker.js +1079 -0
  52. data/vendor/assets/javascript/AdminLTE/plugins/colorpicker/bootstrap-colorpicker.min.js +1 -0
  53. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/bootstrap-datepicker.js +1671 -0
  54. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/locales/bootstrap-datepicker.ar.js +15 -0
  55. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/locales/bootstrap-datepicker.az.js +12 -0
  56. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/locales/bootstrap-datepicker.bg.js +14 -0
  57. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/locales/bootstrap-datepicker.ca.js +14 -0
  58. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/locales/bootstrap-datepicker.cs.js +15 -0
  59. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/locales/bootstrap-datepicker.cy.js +14 -0
  60. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/locales/bootstrap-datepicker.da.js +15 -0
  61. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/locales/bootstrap-datepicker.de.js +17 -0
  62. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/locales/bootstrap-datepicker.el.js +13 -0
  63. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/locales/bootstrap-datepicker.es.js +14 -0
  64. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/locales/bootstrap-datepicker.et.js +18 -0
  65. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/locales/bootstrap-datepicker.fa.js +17 -0
  66. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/locales/bootstrap-datepicker.fi.js +16 -0
  67. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/locales/bootstrap-datepicker.fr.js +17 -0
  68. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/locales/bootstrap-datepicker.gl.js +11 -0
  69. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/locales/bootstrap-datepicker.he.js +15 -0
  70. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/locales/bootstrap-datepicker.hr.js +13 -0
  71. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/locales/bootstrap-datepicker.hu.js +16 -0
  72. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/locales/bootstrap-datepicker.id.js +15 -0
  73. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/locales/bootstrap-datepicker.is.js +14 -0
  74. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/locales/bootstrap-datepicker.it.js +17 -0
  75. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/locales/bootstrap-datepicker.ja.js +15 -0
  76. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/locales/bootstrap-datepicker.ka.js +17 -0
  77. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/locales/bootstrap-datepicker.kk.js +15 -0
  78. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/locales/bootstrap-datepicker.kr.js +13 -0
  79. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/locales/bootstrap-datepicker.lt.js +16 -0
  80. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/locales/bootstrap-datepicker.lv.js +16 -0
  81. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/locales/bootstrap-datepicker.mk.js +15 -0
  82. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/locales/bootstrap-datepicker.ms.js +14 -0
  83. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/locales/bootstrap-datepicker.nb.js +14 -0
  84. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/locales/bootstrap-datepicker.nl-BE.js +17 -0
  85. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/locales/bootstrap-datepicker.nl.js +14 -0
  86. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/locales/bootstrap-datepicker.no.js +16 -0
  87. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/locales/bootstrap-datepicker.pl.js +15 -0
  88. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/locales/bootstrap-datepicker.pt-BR.js +15 -0
  89. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/locales/bootstrap-datepicker.pt.js +16 -0
  90. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/locales/bootstrap-datepicker.ro.js +16 -0
  91. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/locales/bootstrap-datepicker.rs-latin.js +14 -0
  92. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/locales/bootstrap-datepicker.rs.js +14 -0
  93. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/locales/bootstrap-datepicker.ru.js +15 -0
  94. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/locales/bootstrap-datepicker.sk.js +15 -0
  95. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/locales/bootstrap-datepicker.sl.js +14 -0
  96. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/locales/bootstrap-datepicker.sq.js +15 -0
  97. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/locales/bootstrap-datepicker.sv.js +16 -0
  98. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/locales/bootstrap-datepicker.sw.js +15 -0
  99. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/locales/bootstrap-datepicker.th.js +14 -0
  100. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/locales/bootstrap-datepicker.tr.js +16 -0
  101. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/locales/bootstrap-datepicker.ua.js +15 -0
  102. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/locales/bootstrap-datepicker.vi.js +16 -0
  103. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/locales/bootstrap-datepicker.zh-CN.js +16 -0
  104. data/vendor/assets/javascript/AdminLTE/plugins/datepicker/locales/bootstrap-datepicker.zh-TW.js +17 -0
  105. data/vendor/assets/javascript/AdminLTE/plugins/daterangepicker/daterangepicker.js +1542 -0
  106. data/vendor/assets/javascript/AdminLTE/plugins/daterangepicker/moment.js +3111 -0
  107. data/vendor/assets/javascript/AdminLTE/plugins/daterangepicker/moment.min.js +7 -0
  108. data/vendor/assets/javascript/AdminLTE/plugins/fastclick/fastclick.js +841 -0
  109. data/vendor/assets/javascript/AdminLTE/plugins/fastclick/fastclick.min.js +1 -0
  110. data/vendor/assets/javascript/AdminLTE/plugins/flot/excanvas.js +1427 -0
  111. data/vendor/assets/javascript/AdminLTE/plugins/flot/excanvas.min.js +1 -0
  112. data/vendor/assets/javascript/AdminLTE/plugins/flot/jquery.colorhelpers.js +180 -0
  113. data/vendor/assets/javascript/AdminLTE/plugins/flot/jquery.colorhelpers.min.js +1 -0
  114. data/vendor/assets/javascript/AdminLTE/plugins/flot/jquery.flot.canvas.js +345 -0
  115. data/vendor/assets/javascript/AdminLTE/plugins/flot/jquery.flot.canvas.min.js +1 -0
  116. data/vendor/assets/javascript/AdminLTE/plugins/flot/jquery.flot.categories.js +190 -0
  117. data/vendor/assets/javascript/AdminLTE/plugins/flot/jquery.flot.categories.min.js +1 -0
  118. data/vendor/assets/javascript/AdminLTE/plugins/flot/jquery.flot.crosshair.js +176 -0
  119. data/vendor/assets/javascript/AdminLTE/plugins/flot/jquery.flot.crosshair.min.js +1 -0
  120. data/vendor/assets/javascript/AdminLTE/plugins/flot/jquery.flot.errorbars.js +353 -0
  121. data/vendor/assets/javascript/AdminLTE/plugins/flot/jquery.flot.errorbars.min.js +1 -0
  122. data/vendor/assets/javascript/AdminLTE/plugins/flot/jquery.flot.fillbetween.js +226 -0
  123. data/vendor/assets/javascript/AdminLTE/plugins/flot/jquery.flot.fillbetween.min.js +1 -0
  124. data/vendor/assets/javascript/AdminLTE/plugins/flot/jquery.flot.image.js +241 -0
  125. data/vendor/assets/javascript/AdminLTE/plugins/flot/jquery.flot.image.min.js +1 -0
  126. data/vendor/assets/javascript/AdminLTE/plugins/flot/jquery.flot.js +3137 -0
  127. data/vendor/assets/javascript/AdminLTE/plugins/flot/jquery.flot.min.js +2 -0
  128. data/vendor/assets/javascript/AdminLTE/plugins/flot/jquery.flot.navigate.js +346 -0
  129. data/vendor/assets/javascript/AdminLTE/plugins/flot/jquery.flot.navigate.min.js +1 -0
  130. data/vendor/assets/javascript/AdminLTE/plugins/flot/jquery.flot.pie.js +817 -0
  131. data/vendor/assets/javascript/AdminLTE/plugins/flot/jquery.flot.pie.min.js +1 -0
  132. data/vendor/assets/javascript/AdminLTE/plugins/flot/jquery.flot.resize.js +60 -0
  133. data/vendor/assets/javascript/AdminLTE/plugins/flot/jquery.flot.resize.min.js +1 -0
  134. data/vendor/assets/javascript/AdminLTE/plugins/flot/jquery.flot.selection.js +360 -0
  135. data/vendor/assets/javascript/AdminLTE/plugins/flot/jquery.flot.selection.min.js +1 -0
  136. data/vendor/assets/javascript/AdminLTE/plugins/flot/jquery.flot.stack.js +188 -0
  137. data/vendor/assets/javascript/AdminLTE/plugins/flot/jquery.flot.stack.min.js +1 -0
  138. data/vendor/assets/javascript/AdminLTE/plugins/flot/jquery.flot.symbol.js +71 -0
  139. data/vendor/assets/javascript/AdminLTE/plugins/flot/jquery.flot.symbol.min.js +1 -0
  140. data/vendor/assets/javascript/AdminLTE/plugins/flot/jquery.flot.threshold.js +142 -0
  141. data/vendor/assets/javascript/AdminLTE/plugins/flot/jquery.flot.threshold.min.js +1 -0
  142. data/vendor/assets/javascript/AdminLTE/plugins/flot/jquery.flot.time.js +429 -0
  143. data/vendor/assets/javascript/AdminLTE/plugins/flot/jquery.flot.time.min.js +1 -0
  144. data/vendor/assets/javascript/AdminLTE/plugins/fullcalendar/fullcalendar.js +9732 -0
  145. data/vendor/assets/javascript/AdminLTE/plugins/fullcalendar/fullcalendar.min.js +8 -0
  146. data/vendor/assets/javascript/AdminLTE/plugins/input-mask/jquery.inputmask.date.extensions.js +488 -0
  147. data/vendor/assets/javascript/AdminLTE/plugins/input-mask/jquery.inputmask.extensions.js +122 -0
  148. data/vendor/assets/javascript/AdminLTE/plugins/input-mask/jquery.inputmask.js +1627 -0
  149. data/vendor/assets/javascript/AdminLTE/plugins/input-mask/jquery.inputmask.numeric.extensions.js +177 -0
  150. data/vendor/assets/javascript/AdminLTE/plugins/input-mask/jquery.inputmask.phone.extensions.js +50 -0
  151. data/vendor/assets/javascript/AdminLTE/plugins/input-mask/jquery.inputmask.regex.extensions.js +169 -0
  152. data/vendor/assets/javascript/AdminLTE/plugins/input-mask/phone-codes/phone-be.json +45 -0
  153. data/vendor/assets/javascript/AdminLTE/plugins/input-mask/phone-codes/phone-codes.json +294 -0
  154. data/vendor/assets/javascript/AdminLTE/plugins/input-mask/phone-codes/readme.txt +1 -0
  155. data/vendor/assets/javascript/AdminLTE/plugins/ionslider/ion.rangeSlider.min.js +76 -0
  156. data/vendor/assets/javascript/AdminLTE/plugins/jQueryUI/jquery-ui.js +16617 -0
  157. data/vendor/assets/javascript/AdminLTE/plugins/jQueryUI/jquery-ui.min.js +13 -0
  158. data/vendor/assets/javascript/AdminLTE/plugins/jvectormap/jquery-jvectormap-1.2.2.min.js +8 -0
  159. data/vendor/assets/javascript/AdminLTE/plugins/jvectormap/jquery-jvectormap-usa-en.js +1 -0
  160. data/vendor/assets/javascript/AdminLTE/plugins/jvectormap/jquery-jvectormap-world-mill-en.js +1 -0
  161. data/vendor/assets/javascript/AdminLTE/plugins/knob/jquery.knob.js +805 -0
  162. data/vendor/assets/javascript/AdminLTE/plugins/morris/morris.js +1892 -0
  163. data/vendor/assets/javascript/AdminLTE/plugins/morris/morris.min.js +7 -0
  164. data/vendor/assets/javascript/AdminLTE/plugins/pace/pace.js +935 -0
  165. data/vendor/assets/javascript/AdminLTE/plugins/pace/pace.min.js +2 -0
  166. data/vendor/assets/javascript/AdminLTE/plugins/select2/i18n/ar.js +3 -0
  167. data/vendor/assets/javascript/AdminLTE/plugins/select2/i18n/az.js +3 -0
  168. data/vendor/assets/javascript/AdminLTE/plugins/select2/i18n/bg.js +3 -0
  169. data/vendor/assets/javascript/AdminLTE/plugins/select2/i18n/ca.js +3 -0
  170. data/vendor/assets/javascript/AdminLTE/plugins/select2/i18n/cs.js +3 -0
  171. data/vendor/assets/javascript/AdminLTE/plugins/select2/i18n/da.js +3 -0
  172. data/vendor/assets/javascript/AdminLTE/plugins/select2/i18n/de.js +3 -0
  173. data/vendor/assets/javascript/AdminLTE/plugins/select2/i18n/el.js +3 -0
  174. data/vendor/assets/javascript/AdminLTE/plugins/select2/i18n/en.js +3 -0
  175. data/vendor/assets/javascript/AdminLTE/plugins/select2/i18n/es.js +3 -0
  176. data/vendor/assets/javascript/AdminLTE/plugins/select2/i18n/et.js +3 -0
  177. data/vendor/assets/javascript/AdminLTE/plugins/select2/i18n/eu.js +3 -0
  178. data/vendor/assets/javascript/AdminLTE/plugins/select2/i18n/fa.js +3 -0
  179. data/vendor/assets/javascript/AdminLTE/plugins/select2/i18n/fi.js +3 -0
  180. data/vendor/assets/javascript/AdminLTE/plugins/select2/i18n/fr.js +3 -0
  181. data/vendor/assets/javascript/AdminLTE/plugins/select2/i18n/gl.js +3 -0
  182. data/vendor/assets/javascript/AdminLTE/plugins/select2/i18n/he.js +3 -0
  183. data/vendor/assets/javascript/AdminLTE/plugins/select2/i18n/hi.js +3 -0
  184. data/vendor/assets/javascript/AdminLTE/plugins/select2/i18n/hr.js +3 -0
  185. data/vendor/assets/javascript/AdminLTE/plugins/select2/i18n/hu.js +3 -0
  186. data/vendor/assets/javascript/AdminLTE/plugins/select2/i18n/id.js +3 -0
  187. data/vendor/assets/javascript/AdminLTE/plugins/select2/i18n/is.js +3 -0
  188. data/vendor/assets/javascript/AdminLTE/plugins/select2/i18n/it.js +3 -0
  189. data/vendor/assets/javascript/AdminLTE/plugins/select2/i18n/ja.js +3 -0
  190. data/vendor/assets/javascript/AdminLTE/plugins/select2/i18n/km.js +3 -0
  191. data/vendor/assets/javascript/AdminLTE/plugins/select2/i18n/ko.js +3 -0
  192. data/vendor/assets/javascript/AdminLTE/plugins/select2/i18n/lt.js +3 -0
  193. data/vendor/assets/javascript/AdminLTE/plugins/select2/i18n/lv.js +3 -0
  194. data/vendor/assets/javascript/AdminLTE/plugins/select2/i18n/mk.js +3 -0
  195. data/vendor/assets/javascript/AdminLTE/plugins/select2/i18n/ms.js +3 -0
  196. data/vendor/assets/javascript/AdminLTE/plugins/select2/i18n/nb.js +3 -0
  197. data/vendor/assets/javascript/AdminLTE/plugins/select2/i18n/nl.js +3 -0
  198. data/vendor/assets/javascript/AdminLTE/plugins/select2/i18n/pl.js +3 -0
  199. data/vendor/assets/javascript/AdminLTE/plugins/select2/i18n/pt-BR.js +3 -0
  200. data/vendor/assets/javascript/AdminLTE/plugins/select2/i18n/pt.js +3 -0
  201. data/vendor/assets/javascript/AdminLTE/plugins/select2/i18n/ro.js +3 -0
  202. data/vendor/assets/javascript/AdminLTE/plugins/select2/i18n/ru.js +3 -0
  203. data/vendor/assets/javascript/AdminLTE/plugins/select2/i18n/sk.js +3 -0
  204. data/vendor/assets/javascript/AdminLTE/plugins/select2/i18n/sr-Cyrl.js +3 -0
  205. data/vendor/assets/javascript/AdminLTE/plugins/select2/i18n/sr.js +3 -0
  206. data/vendor/assets/javascript/AdminLTE/plugins/select2/i18n/sv.js +3 -0
  207. data/vendor/assets/javascript/AdminLTE/plugins/select2/i18n/th.js +3 -0
  208. data/vendor/assets/javascript/AdminLTE/plugins/select2/i18n/tr.js +3 -0
  209. data/vendor/assets/javascript/AdminLTE/plugins/select2/i18n/uk.js +3 -0
  210. data/vendor/assets/javascript/AdminLTE/plugins/select2/i18n/vi.js +3 -0
  211. data/vendor/assets/javascript/AdminLTE/plugins/select2/i18n/zh-CN.js +3 -0
  212. data/vendor/assets/javascript/AdminLTE/plugins/select2/i18n/zh-TW.js +3 -0
  213. data/vendor/assets/javascript/AdminLTE/plugins/select2/select2.full.js +6436 -0
  214. data/vendor/assets/javascript/AdminLTE/plugins/select2/select2.full.min.js +3 -0
  215. data/vendor/assets/javascript/AdminLTE/plugins/select2/select2.js +5725 -0
  216. data/vendor/assets/javascript/AdminLTE/plugins/select2/select2.min.js +3 -0
  217. data/vendor/assets/javascript/AdminLTE/plugins/slimScroll/jquery.slimscroll.js +474 -0
  218. data/vendor/assets/javascript/AdminLTE/plugins/slimScroll/jquery.slimscroll.min.js +16 -0
  219. data/vendor/assets/javascript/AdminLTE/plugins/sparkline/jquery.sparkline.js +3054 -0
  220. data/vendor/assets/javascript/AdminLTE/plugins/sparkline/jquery.sparkline.min.js +5 -0
  221. data/vendor/assets/javascript/AdminLTE/plugins/timepicker/bootstrap-timepicker.js +903 -0
  222. data/vendor/assets/javascript/AdminLTE/plugins/timepicker/bootstrap-timepicker.min.js +5 -0
  223. data/vendor/assets/javascript/AdminLTE.js +4 -0
  224. data/vendor/assets/stylesheets/AdminLTE/AdminLTE.min.css +7 -0
  225. data/vendor/assets/stylesheets/AdminLTE/plugins/bootstrap-slider/slider.css +282 -0
  226. data/vendor/assets/stylesheets/AdminLTE/plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.css +117 -0
  227. data/vendor/assets/stylesheets/AdminLTE/plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.min.css +3 -0
  228. data/vendor/assets/stylesheets/AdminLTE/plugins/colorpicker/bootstrap-colorpicker.css +251 -0
  229. data/vendor/assets/stylesheets/AdminLTE/plugins/colorpicker/bootstrap-colorpicker.min.css +9 -0
  230. data/vendor/assets/stylesheets/AdminLTE/plugins/colorpicker/img/alpha-horizontal.png +0 -0
  231. data/vendor/assets/stylesheets/AdminLTE/plugins/colorpicker/img/alpha.png +0 -0
  232. data/vendor/assets/stylesheets/AdminLTE/plugins/colorpicker/img/hue-horizontal.png +0 -0
  233. data/vendor/assets/stylesheets/AdminLTE/plugins/colorpicker/img/hue.png +0 -0
  234. data/vendor/assets/stylesheets/AdminLTE/plugins/colorpicker/img/saturation.png +0 -0
  235. data/vendor/assets/stylesheets/AdminLTE/plugins/datepicker/datepicker3.css +790 -0
  236. data/vendor/assets/stylesheets/AdminLTE/plugins/daterangepicker/daterangepicker.css +232 -0
  237. data/vendor/assets/stylesheets/AdminLTE/plugins/fullcalendar/fullcalendar.css +977 -0
  238. data/vendor/assets/stylesheets/AdminLTE/plugins/fullcalendar/fullcalendar.min.css +5 -0
  239. data/vendor/assets/stylesheets/AdminLTE/plugins/fullcalendar/fullcalendar.print.css +202 -0
  240. data/vendor/assets/stylesheets/AdminLTE/plugins/ionslider/ion.rangeSlider.css +149 -0
  241. data/vendor/assets/stylesheets/AdminLTE/plugins/ionslider/ion.rangeSlider.skinFlat.css +106 -0
  242. data/vendor/assets/stylesheets/AdminLTE/plugins/ionslider/ion.rangeSlider.skinNice.css +102 -0
  243. data/vendor/assets/stylesheets/AdminLTE/plugins/jvectormap/jquery-jvectormap-1.2.2.css +40 -0
  244. data/vendor/assets/stylesheets/AdminLTE/plugins/morris/morris.css +2 -0
  245. data/vendor/assets/stylesheets/AdminLTE/plugins/pace/pace.css +85 -0
  246. data/vendor/assets/stylesheets/AdminLTE/plugins/pace/pace.min.css +1 -0
  247. data/vendor/assets/stylesheets/AdminLTE/plugins/select2/select2.css +484 -0
  248. data/vendor/assets/stylesheets/AdminLTE/plugins/select2/select2.min.css +1 -0
  249. data/vendor/assets/stylesheets/AdminLTE/plugins/timepicker/bootstrap-timepicker.css +121 -0
  250. data/vendor/assets/stylesheets/AdminLTE/plugins/timepicker/bootstrap-timepicker.min.css +10 -0
  251. data/vendor/assets/stylesheets/AdminLTE/skins/_all-skins.css +1770 -0
  252. data/vendor/assets/stylesheets/AdminLTE/skins/_all-skins.min.css +1 -0
  253. data/vendor/assets/stylesheets/AdminLTE/skins/skin-black-light.css +173 -0
  254. data/vendor/assets/stylesheets/AdminLTE/skins/skin-black-light.min.css +1 -0
  255. data/vendor/assets/stylesheets/AdminLTE/skins/skin-black.css +158 -0
  256. data/vendor/assets/stylesheets/AdminLTE/skins/skin-black.min.css +1 -0
  257. data/vendor/assets/stylesheets/AdminLTE/skins/skin-blue-light.css +164 -0
  258. data/vendor/assets/stylesheets/AdminLTE/skins/skin-blue-light.min.css +1 -0
  259. data/vendor/assets/stylesheets/AdminLTE/skins/skin-blue.css +139 -0
  260. data/vendor/assets/stylesheets/AdminLTE/skins/skin-blue.min.css +1 -0
  261. data/vendor/assets/stylesheets/AdminLTE/skins/skin-green-light.css +153 -0
  262. data/vendor/assets/stylesheets/AdminLTE/skins/skin-green-light.min.css +1 -0
  263. data/vendor/assets/stylesheets/AdminLTE/skins/skin-green.css +131 -0
  264. data/vendor/assets/stylesheets/AdminLTE/skins/skin-green.min.css +1 -0
  265. data/vendor/assets/stylesheets/AdminLTE/skins/skin-purple-light.css +153 -0
  266. data/vendor/assets/stylesheets/AdminLTE/skins/skin-purple-light.min.css +1 -0
  267. data/vendor/assets/stylesheets/AdminLTE/skins/skin-purple.css +131 -0
  268. data/vendor/assets/stylesheets/AdminLTE/skins/skin-purple.min.css +1 -0
  269. data/vendor/assets/stylesheets/AdminLTE/skins/skin-red-light.css +153 -0
  270. data/vendor/assets/stylesheets/AdminLTE/skins/skin-red-light.min.css +1 -0
  271. data/vendor/assets/stylesheets/AdminLTE/skins/skin-red.css +131 -0
  272. data/vendor/assets/stylesheets/AdminLTE/skins/skin-red.min.css +1 -0
  273. data/vendor/assets/stylesheets/AdminLTE/skins/skin-yellow-light.css +153 -0
  274. data/vendor/assets/stylesheets/AdminLTE/skins/skin-yellow-light.min.css +1 -0
  275. data/vendor/assets/stylesheets/AdminLTE/skins/skin-yellow.css +131 -0
  276. data/vendor/assets/stylesheets/AdminLTE/skins/skin-yellow.min.css +1 -0
  277. data/vendor/assets/stylesheets/AdminLTE.scss +5 -0
  278. metadata +474 -0
@@ -0,0 +1,3736 @@
1
+ /*!
2
+ * Chart.js
3
+ * http://chartjs.org/
4
+ * Version: 1.1.1
5
+ *
6
+ * Copyright 2015 Nick Downie
7
+ * Released under the MIT license
8
+ * https://github.com/nnnick/Chart.js/blob/master/LICENSE.md
9
+ */
10
+
11
+
12
+ (function(){
13
+
14
+ "use strict";
15
+
16
+ //Declare root variable - window in the browser, global on the server
17
+ var root = this,
18
+ previous = root.Chart;
19
+
20
+ //Occupy the global variable of Chart, and create a simple base class
21
+ var Chart = function(context){
22
+ var chart = this;
23
+ this.canvas = context.canvas;
24
+
25
+ this.ctx = context;
26
+
27
+ //Variables global to the chart
28
+ var computeDimension = function(element,dimension)
29
+ {
30
+ if (element['offset'+dimension])
31
+ {
32
+ return element['offset'+dimension];
33
+ }
34
+ else
35
+ {
36
+ return document.defaultView.getComputedStyle(element).getPropertyValue(dimension);
37
+ }
38
+ };
39
+
40
+ var width = this.width = computeDimension(context.canvas,'Width') || context.canvas.width;
41
+ var height = this.height = computeDimension(context.canvas,'Height') || context.canvas.height;
42
+
43
+ this.aspectRatio = this.width / this.height;
44
+ //High pixel density displays - multiply the size of the canvas height/width by the device pixel ratio, then scale.
45
+ helpers.retinaScale(this);
46
+
47
+ return this;
48
+ };
49
+ //Globally expose the defaults to allow for user updating/changing
50
+ Chart.defaults = {
51
+ global: {
52
+ // Boolean - Whether to animate the chart
53
+ animation: true,
54
+
55
+ // Number - Number of animation steps
56
+ animationSteps: 60,
57
+
58
+ // String - Animation easing effect
59
+ animationEasing: "easeOutQuart",
60
+
61
+ // Boolean - If we should show the scale at all
62
+ showScale: true,
63
+
64
+ // Boolean - If we want to override with a hard coded scale
65
+ scaleOverride: false,
66
+
67
+ // ** Required if scaleOverride is true **
68
+ // Number - The number of steps in a hard coded scale
69
+ scaleSteps: null,
70
+ // Number - The value jump in the hard coded scale
71
+ scaleStepWidth: null,
72
+ // Number - The scale starting value
73
+ scaleStartValue: null,
74
+
75
+ // String - Colour of the scale line
76
+ scaleLineColor: "rgba(0,0,0,.1)",
77
+
78
+ // Number - Pixel width of the scale line
79
+ scaleLineWidth: 1,
80
+
81
+ // Boolean - Whether to show labels on the scale
82
+ scaleShowLabels: true,
83
+
84
+ // Interpolated JS string - can access value
85
+ scaleLabel: "<%=value%>",
86
+
87
+ // Boolean - Whether the scale should stick to integers, and not show any floats even if drawing space is there
88
+ scaleIntegersOnly: true,
89
+
90
+ // Boolean - Whether the scale should start at zero, or an order of magnitude down from the lowest value
91
+ scaleBeginAtZero: false,
92
+
93
+ // String - Scale label font declaration for the scale label
94
+ scaleFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
95
+
96
+ // Number - Scale label font size in pixels
97
+ scaleFontSize: 12,
98
+
99
+ // String - Scale label font weight style
100
+ scaleFontStyle: "normal",
101
+
102
+ // String - Scale label font colour
103
+ scaleFontColor: "#666",
104
+
105
+ // Boolean - whether or not the chart should be responsive and resize when the browser does.
106
+ responsive: false,
107
+
108
+ // Boolean - whether to maintain the starting aspect ratio or not when responsive, if set to false, will take up entire container
109
+ maintainAspectRatio: true,
110
+
111
+ // Boolean - Determines whether to draw tooltips on the canvas or not - attaches events to touchmove & mousemove
112
+ showTooltips: true,
113
+
114
+ // Boolean - Determines whether to draw built-in tooltip or call custom tooltip function
115
+ customTooltips: false,
116
+
117
+ // Array - Array of string names to attach tooltip events
118
+ tooltipEvents: ["mousemove", "touchstart", "touchmove", "mouseout"],
119
+
120
+ // String - Tooltip background colour
121
+ tooltipFillColor: "rgba(0,0,0,0.8)",
122
+
123
+ // String - Tooltip label font declaration for the scale label
124
+ tooltipFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
125
+
126
+ // Number - Tooltip label font size in pixels
127
+ tooltipFontSize: 14,
128
+
129
+ // String - Tooltip font weight style
130
+ tooltipFontStyle: "normal",
131
+
132
+ // String - Tooltip label font colour
133
+ tooltipFontColor: "#fff",
134
+
135
+ // String - Tooltip title font declaration for the scale label
136
+ tooltipTitleFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
137
+
138
+ // Number - Tooltip title font size in pixels
139
+ tooltipTitleFontSize: 14,
140
+
141
+ // String - Tooltip title font weight style
142
+ tooltipTitleFontStyle: "bold",
143
+
144
+ // String - Tooltip title font colour
145
+ tooltipTitleFontColor: "#fff",
146
+
147
+ // String - Tooltip title template
148
+ tooltipTitleTemplate: "<%= label%>",
149
+
150
+ // Number - pixel width of padding around tooltip text
151
+ tooltipYPadding: 6,
152
+
153
+ // Number - pixel width of padding around tooltip text
154
+ tooltipXPadding: 6,
155
+
156
+ // Number - Size of the caret on the tooltip
157
+ tooltipCaretSize: 8,
158
+
159
+ // Number - Pixel radius of the tooltip border
160
+ tooltipCornerRadius: 6,
161
+
162
+ // Number - Pixel offset from point x to tooltip edge
163
+ tooltipXOffset: 10,
164
+
165
+ // String - Template string for single tooltips
166
+ tooltipTemplate: "<%if (label){%><%=label%>: <%}%><%= value %>",
167
+
168
+ // String - Template string for single tooltips
169
+ multiTooltipTemplate: "<%= datasetLabel %>: <%= value %>",
170
+
171
+ // String - Colour behind the legend colour block
172
+ multiTooltipKeyBackground: '#fff',
173
+
174
+ // Array - A list of colors to use as the defaults
175
+ segmentColorDefault: ["#A6CEE3", "#1F78B4", "#B2DF8A", "#33A02C", "#FB9A99", "#E31A1C", "#FDBF6F", "#FF7F00", "#CAB2D6", "#6A3D9A", "#B4B482", "#B15928" ],
176
+
177
+ // Array - A list of highlight colors to use as the defaults
178
+ segmentHighlightColorDefaults: [ "#CEF6FF", "#47A0DC", "#DAFFB2", "#5BC854", "#FFC2C1", "#FF4244", "#FFE797", "#FFA728", "#F2DAFE", "#9265C2", "#DCDCAA", "#D98150" ],
179
+
180
+ // Function - Will fire on animation progression.
181
+ onAnimationProgress: function(){},
182
+
183
+ // Function - Will fire on animation completion.
184
+ onAnimationComplete: function(){}
185
+
186
+ }
187
+ };
188
+
189
+ //Create a dictionary of chart types, to allow for extension of existing types
190
+ Chart.types = {};
191
+
192
+ //Global Chart helpers object for utility methods and classes
193
+ var helpers = Chart.helpers = {};
194
+
195
+ //-- Basic js utility methods
196
+ var each = helpers.each = function(loopable,callback,self){
197
+ var additionalArgs = Array.prototype.slice.call(arguments, 3);
198
+ // Check to see if null or undefined firstly.
199
+ if (loopable){
200
+ if (loopable.length === +loopable.length){
201
+ var i;
202
+ for (i=0; i<loopable.length; i++){
203
+ callback.apply(self,[loopable[i], i].concat(additionalArgs));
204
+ }
205
+ }
206
+ else{
207
+ for (var item in loopable){
208
+ callback.apply(self,[loopable[item],item].concat(additionalArgs));
209
+ }
210
+ }
211
+ }
212
+ },
213
+ clone = helpers.clone = function(obj){
214
+ var objClone = {};
215
+ each(obj,function(value,key){
216
+ if (obj.hasOwnProperty(key)){
217
+ objClone[key] = value;
218
+ }
219
+ });
220
+ return objClone;
221
+ },
222
+ extend = helpers.extend = function(base){
223
+ each(Array.prototype.slice.call(arguments,1), function(extensionObject) {
224
+ each(extensionObject,function(value,key){
225
+ if (extensionObject.hasOwnProperty(key)){
226
+ base[key] = value;
227
+ }
228
+ });
229
+ });
230
+ return base;
231
+ },
232
+ merge = helpers.merge = function(base,master){
233
+ //Merge properties in left object over to a shallow clone of object right.
234
+ var args = Array.prototype.slice.call(arguments,0);
235
+ args.unshift({});
236
+ return extend.apply(null, args);
237
+ },
238
+ indexOf = helpers.indexOf = function(arrayToSearch, item){
239
+ if (Array.prototype.indexOf) {
240
+ return arrayToSearch.indexOf(item);
241
+ }
242
+ else{
243
+ for (var i = 0; i < arrayToSearch.length; i++) {
244
+ if (arrayToSearch[i] === item) return i;
245
+ }
246
+ return -1;
247
+ }
248
+ },
249
+ where = helpers.where = function(collection, filterCallback){
250
+ var filtered = [];
251
+
252
+ helpers.each(collection, function(item){
253
+ if (filterCallback(item)){
254
+ filtered.push(item);
255
+ }
256
+ });
257
+
258
+ return filtered;
259
+ },
260
+ findNextWhere = helpers.findNextWhere = function(arrayToSearch, filterCallback, startIndex){
261
+ // Default to start of the array
262
+ if (!startIndex){
263
+ startIndex = -1;
264
+ }
265
+ for (var i = startIndex + 1; i < arrayToSearch.length; i++) {
266
+ var currentItem = arrayToSearch[i];
267
+ if (filterCallback(currentItem)){
268
+ return currentItem;
269
+ }
270
+ }
271
+ },
272
+ findPreviousWhere = helpers.findPreviousWhere = function(arrayToSearch, filterCallback, startIndex){
273
+ // Default to end of the array
274
+ if (!startIndex){
275
+ startIndex = arrayToSearch.length;
276
+ }
277
+ for (var i = startIndex - 1; i >= 0; i--) {
278
+ var currentItem = arrayToSearch[i];
279
+ if (filterCallback(currentItem)){
280
+ return currentItem;
281
+ }
282
+ }
283
+ },
284
+ inherits = helpers.inherits = function(extensions){
285
+ //Basic javascript inheritance based on the model created in Backbone.js
286
+ var parent = this;
287
+ var ChartElement = (extensions && extensions.hasOwnProperty("constructor")) ? extensions.constructor : function(){ return parent.apply(this, arguments); };
288
+
289
+ var Surrogate = function(){ this.constructor = ChartElement;};
290
+ Surrogate.prototype = parent.prototype;
291
+ ChartElement.prototype = new Surrogate();
292
+
293
+ ChartElement.extend = inherits;
294
+
295
+ if (extensions) extend(ChartElement.prototype, extensions);
296
+
297
+ ChartElement.__super__ = parent.prototype;
298
+
299
+ return ChartElement;
300
+ },
301
+ noop = helpers.noop = function(){},
302
+ uid = helpers.uid = (function(){
303
+ var id=0;
304
+ return function(){
305
+ return "chart-" + id++;
306
+ };
307
+ })(),
308
+ warn = helpers.warn = function(str){
309
+ //Method for warning of errors
310
+ if (window.console && typeof window.console.warn === "function") console.warn(str);
311
+ },
312
+ amd = helpers.amd = (typeof define === 'function' && define.amd),
313
+ //-- Math methods
314
+ isNumber = helpers.isNumber = function(n){
315
+ return !isNaN(parseFloat(n)) && isFinite(n);
316
+ },
317
+ max = helpers.max = function(array){
318
+ return Math.max.apply( Math, array );
319
+ },
320
+ min = helpers.min = function(array){
321
+ return Math.min.apply( Math, array );
322
+ },
323
+ cap = helpers.cap = function(valueToCap,maxValue,minValue){
324
+ if(isNumber(maxValue)) {
325
+ if( valueToCap > maxValue ) {
326
+ return maxValue;
327
+ }
328
+ }
329
+ else if(isNumber(minValue)){
330
+ if ( valueToCap < minValue ){
331
+ return minValue;
332
+ }
333
+ }
334
+ return valueToCap;
335
+ },
336
+ getDecimalPlaces = helpers.getDecimalPlaces = function(num){
337
+ if (num%1!==0 && isNumber(num)){
338
+ var s = num.toString();
339
+ if(s.indexOf("e-") < 0){
340
+ // no exponent, e.g. 0.01
341
+ return s.split(".")[1].length;
342
+ }
343
+ else if(s.indexOf(".") < 0) {
344
+ // no decimal point, e.g. 1e-9
345
+ return parseInt(s.split("e-")[1]);
346
+ }
347
+ else {
348
+ // exponent and decimal point, e.g. 1.23e-9
349
+ var parts = s.split(".")[1].split("e-");
350
+ return parts[0].length + parseInt(parts[1]);
351
+ }
352
+ }
353
+ else {
354
+ return 0;
355
+ }
356
+ },
357
+ toRadians = helpers.radians = function(degrees){
358
+ return degrees * (Math.PI/180);
359
+ },
360
+ // Gets the angle from vertical upright to the point about a centre.
361
+ getAngleFromPoint = helpers.getAngleFromPoint = function(centrePoint, anglePoint){
362
+ var distanceFromXCenter = anglePoint.x - centrePoint.x,
363
+ distanceFromYCenter = anglePoint.y - centrePoint.y,
364
+ radialDistanceFromCenter = Math.sqrt( distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter);
365
+
366
+
367
+ var angle = Math.PI * 2 + Math.atan2(distanceFromYCenter, distanceFromXCenter);
368
+
369
+ //If the segment is in the top left quadrant, we need to add another rotation to the angle
370
+ if (distanceFromXCenter < 0 && distanceFromYCenter < 0){
371
+ angle += Math.PI*2;
372
+ }
373
+
374
+ return {
375
+ angle: angle,
376
+ distance: radialDistanceFromCenter
377
+ };
378
+ },
379
+ aliasPixel = helpers.aliasPixel = function(pixelWidth){
380
+ return (pixelWidth % 2 === 0) ? 0 : 0.5;
381
+ },
382
+ splineCurve = helpers.splineCurve = function(FirstPoint,MiddlePoint,AfterPoint,t){
383
+ //Props to Rob Spencer at scaled innovation for his post on splining between points
384
+ //http://scaledinnovation.com/analytics/splines/aboutSplines.html
385
+ var d01=Math.sqrt(Math.pow(MiddlePoint.x-FirstPoint.x,2)+Math.pow(MiddlePoint.y-FirstPoint.y,2)),
386
+ d12=Math.sqrt(Math.pow(AfterPoint.x-MiddlePoint.x,2)+Math.pow(AfterPoint.y-MiddlePoint.y,2)),
387
+ fa=t*d01/(d01+d12),// scaling factor for triangle Ta
388
+ fb=t*d12/(d01+d12);
389
+ return {
390
+ inner : {
391
+ x : MiddlePoint.x-fa*(AfterPoint.x-FirstPoint.x),
392
+ y : MiddlePoint.y-fa*(AfterPoint.y-FirstPoint.y)
393
+ },
394
+ outer : {
395
+ x: MiddlePoint.x+fb*(AfterPoint.x-FirstPoint.x),
396
+ y : MiddlePoint.y+fb*(AfterPoint.y-FirstPoint.y)
397
+ }
398
+ };
399
+ },
400
+ calculateOrderOfMagnitude = helpers.calculateOrderOfMagnitude = function(val){
401
+ return Math.floor(Math.log(val) / Math.LN10);
402
+ },
403
+ calculateScaleRange = helpers.calculateScaleRange = function(valuesArray, drawingSize, textSize, startFromZero, integersOnly){
404
+
405
+ //Set a minimum step of two - a point at the top of the graph, and a point at the base
406
+ var minSteps = 2,
407
+ maxSteps = Math.floor(drawingSize/(textSize * 1.5)),
408
+ skipFitting = (minSteps >= maxSteps);
409
+
410
+ // Filter out null values since these would min() to zero
411
+ var values = [];
412
+ each(valuesArray, function( v ){
413
+ v == null || values.push( v );
414
+ });
415
+ var minValue = min(values),
416
+ maxValue = max(values);
417
+
418
+ // We need some degree of separation here to calculate the scales if all the values are the same
419
+ // Adding/minusing 0.5 will give us a range of 1.
420
+ if (maxValue === minValue){
421
+ maxValue += 0.5;
422
+ // So we don't end up with a graph with a negative start value if we've said always start from zero
423
+ if (minValue >= 0.5 && !startFromZero){
424
+ minValue -= 0.5;
425
+ }
426
+ else{
427
+ // Make up a whole number above the values
428
+ maxValue += 0.5;
429
+ }
430
+ }
431
+
432
+ var valueRange = Math.abs(maxValue - minValue),
433
+ rangeOrderOfMagnitude = calculateOrderOfMagnitude(valueRange),
434
+ graphMax = Math.ceil(maxValue / (1 * Math.pow(10, rangeOrderOfMagnitude))) * Math.pow(10, rangeOrderOfMagnitude),
435
+ graphMin = (startFromZero) ? 0 : Math.floor(minValue / (1 * Math.pow(10, rangeOrderOfMagnitude))) * Math.pow(10, rangeOrderOfMagnitude),
436
+ graphRange = graphMax - graphMin,
437
+ stepValue = Math.pow(10, rangeOrderOfMagnitude),
438
+ numberOfSteps = Math.round(graphRange / stepValue);
439
+
440
+ //If we have more space on the graph we'll use it to give more definition to the data
441
+ while((numberOfSteps > maxSteps || (numberOfSteps * 2) < maxSteps) && !skipFitting) {
442
+ if(numberOfSteps > maxSteps){
443
+ stepValue *=2;
444
+ numberOfSteps = Math.round(graphRange/stepValue);
445
+ // Don't ever deal with a decimal number of steps - cancel fitting and just use the minimum number of steps.
446
+ if (numberOfSteps % 1 !== 0){
447
+ skipFitting = true;
448
+ }
449
+ }
450
+ //We can fit in double the amount of scale points on the scale
451
+ else{
452
+ //If user has declared ints only, and the step value isn't a decimal
453
+ if (integersOnly && rangeOrderOfMagnitude >= 0){
454
+ //If the user has said integers only, we need to check that making the scale more granular wouldn't make it a float
455
+ if(stepValue/2 % 1 === 0){
456
+ stepValue /=2;
457
+ numberOfSteps = Math.round(graphRange/stepValue);
458
+ }
459
+ //If it would make it a float break out of the loop
460
+ else{
461
+ break;
462
+ }
463
+ }
464
+ //If the scale doesn't have to be an int, make the scale more granular anyway.
465
+ else{
466
+ stepValue /=2;
467
+ numberOfSteps = Math.round(graphRange/stepValue);
468
+ }
469
+
470
+ }
471
+ }
472
+
473
+ if (skipFitting){
474
+ numberOfSteps = minSteps;
475
+ stepValue = graphRange / numberOfSteps;
476
+ }
477
+
478
+ return {
479
+ steps : numberOfSteps,
480
+ stepValue : stepValue,
481
+ min : graphMin,
482
+ max : graphMin + (numberOfSteps * stepValue)
483
+ };
484
+
485
+ },
486
+ /* jshint ignore:start */
487
+ // Blows up jshint errors based on the new Function constructor
488
+ //Templating methods
489
+ //Javascript micro templating by John Resig - source at http://ejohn.org/blog/javascript-micro-templating/
490
+ template = helpers.template = function(templateString, valuesObject){
491
+
492
+ // If templateString is function rather than string-template - call the function for valuesObject
493
+
494
+ if(templateString instanceof Function){
495
+ return templateString(valuesObject);
496
+ }
497
+
498
+ var cache = {};
499
+ function tmpl(str, data){
500
+ // Figure out if we're getting a template, or if we need to
501
+ // load the template - and be sure to cache the result.
502
+ var fn = !/\W/.test(str) ?
503
+ cache[str] = cache[str] :
504
+
505
+ // Generate a reusable function that will serve as a template
506
+ // generator (and which will be cached).
507
+ new Function("obj",
508
+ "var p=[],print=function(){p.push.apply(p,arguments);};" +
509
+
510
+ // Introduce the data as local variables using with(){}
511
+ "with(obj){p.push('" +
512
+
513
+ // Convert the template into pure JavaScript
514
+ str
515
+ .replace(/[\r\t\n]/g, " ")
516
+ .split("<%").join("\t")
517
+ .replace(/((^|%>)[^\t]*)'/g, "$1\r")
518
+ .replace(/\t=(.*?)%>/g, "',$1,'")
519
+ .split("\t").join("');")
520
+ .split("%>").join("p.push('")
521
+ .split("\r").join("\\'") +
522
+ "');}return p.join('');"
523
+ );
524
+
525
+ // Provide some basic currying to the user
526
+ return data ? fn( data ) : fn;
527
+ }
528
+ return tmpl(templateString,valuesObject);
529
+ },
530
+ /* jshint ignore:end */
531
+ generateLabels = helpers.generateLabels = function(templateString,numberOfSteps,graphMin,stepValue){
532
+ var labelsArray = new Array(numberOfSteps);
533
+ if (templateString){
534
+ each(labelsArray,function(val,index){
535
+ labelsArray[index] = template(templateString,{value: (graphMin + (stepValue*(index+1)))});
536
+ });
537
+ }
538
+ return labelsArray;
539
+ },
540
+ //--Animation methods
541
+ //Easing functions adapted from Robert Penner's easing equations
542
+ //http://www.robertpenner.com/easing/
543
+ easingEffects = helpers.easingEffects = {
544
+ linear: function (t) {
545
+ return t;
546
+ },
547
+ easeInQuad: function (t) {
548
+ return t * t;
549
+ },
550
+ easeOutQuad: function (t) {
551
+ return -1 * t * (t - 2);
552
+ },
553
+ easeInOutQuad: function (t) {
554
+ if ((t /= 1 / 2) < 1){
555
+ return 1 / 2 * t * t;
556
+ }
557
+ return -1 / 2 * ((--t) * (t - 2) - 1);
558
+ },
559
+ easeInCubic: function (t) {
560
+ return t * t * t;
561
+ },
562
+ easeOutCubic: function (t) {
563
+ return 1 * ((t = t / 1 - 1) * t * t + 1);
564
+ },
565
+ easeInOutCubic: function (t) {
566
+ if ((t /= 1 / 2) < 1){
567
+ return 1 / 2 * t * t * t;
568
+ }
569
+ return 1 / 2 * ((t -= 2) * t * t + 2);
570
+ },
571
+ easeInQuart: function (t) {
572
+ return t * t * t * t;
573
+ },
574
+ easeOutQuart: function (t) {
575
+ return -1 * ((t = t / 1 - 1) * t * t * t - 1);
576
+ },
577
+ easeInOutQuart: function (t) {
578
+ if ((t /= 1 / 2) < 1){
579
+ return 1 / 2 * t * t * t * t;
580
+ }
581
+ return -1 / 2 * ((t -= 2) * t * t * t - 2);
582
+ },
583
+ easeInQuint: function (t) {
584
+ return 1 * (t /= 1) * t * t * t * t;
585
+ },
586
+ easeOutQuint: function (t) {
587
+ return 1 * ((t = t / 1 - 1) * t * t * t * t + 1);
588
+ },
589
+ easeInOutQuint: function (t) {
590
+ if ((t /= 1 / 2) < 1){
591
+ return 1 / 2 * t * t * t * t * t;
592
+ }
593
+ return 1 / 2 * ((t -= 2) * t * t * t * t + 2);
594
+ },
595
+ easeInSine: function (t) {
596
+ return -1 * Math.cos(t / 1 * (Math.PI / 2)) + 1;
597
+ },
598
+ easeOutSine: function (t) {
599
+ return 1 * Math.sin(t / 1 * (Math.PI / 2));
600
+ },
601
+ easeInOutSine: function (t) {
602
+ return -1 / 2 * (Math.cos(Math.PI * t / 1) - 1);
603
+ },
604
+ easeInExpo: function (t) {
605
+ return (t === 0) ? 1 : 1 * Math.pow(2, 10 * (t / 1 - 1));
606
+ },
607
+ easeOutExpo: function (t) {
608
+ return (t === 1) ? 1 : 1 * (-Math.pow(2, -10 * t / 1) + 1);
609
+ },
610
+ easeInOutExpo: function (t) {
611
+ if (t === 0){
612
+ return 0;
613
+ }
614
+ if (t === 1){
615
+ return 1;
616
+ }
617
+ if ((t /= 1 / 2) < 1){
618
+ return 1 / 2 * Math.pow(2, 10 * (t - 1));
619
+ }
620
+ return 1 / 2 * (-Math.pow(2, -10 * --t) + 2);
621
+ },
622
+ easeInCirc: function (t) {
623
+ if (t >= 1){
624
+ return t;
625
+ }
626
+ return -1 * (Math.sqrt(1 - (t /= 1) * t) - 1);
627
+ },
628
+ easeOutCirc: function (t) {
629
+ return 1 * Math.sqrt(1 - (t = t / 1 - 1) * t);
630
+ },
631
+ easeInOutCirc: function (t) {
632
+ if ((t /= 1 / 2) < 1){
633
+ return -1 / 2 * (Math.sqrt(1 - t * t) - 1);
634
+ }
635
+ return 1 / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1);
636
+ },
637
+ easeInElastic: function (t) {
638
+ var s = 1.70158;
639
+ var p = 0;
640
+ var a = 1;
641
+ if (t === 0){
642
+ return 0;
643
+ }
644
+ if ((t /= 1) == 1){
645
+ return 1;
646
+ }
647
+ if (!p){
648
+ p = 1 * 0.3;
649
+ }
650
+ if (a < Math.abs(1)) {
651
+ a = 1;
652
+ s = p / 4;
653
+ } else{
654
+ s = p / (2 * Math.PI) * Math.asin(1 / a);
655
+ }
656
+ return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p));
657
+ },
658
+ easeOutElastic: function (t) {
659
+ var s = 1.70158;
660
+ var p = 0;
661
+ var a = 1;
662
+ if (t === 0){
663
+ return 0;
664
+ }
665
+ if ((t /= 1) == 1){
666
+ return 1;
667
+ }
668
+ if (!p){
669
+ p = 1 * 0.3;
670
+ }
671
+ if (a < Math.abs(1)) {
672
+ a = 1;
673
+ s = p / 4;
674
+ } else{
675
+ s = p / (2 * Math.PI) * Math.asin(1 / a);
676
+ }
677
+ return a * Math.pow(2, -10 * t) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) + 1;
678
+ },
679
+ easeInOutElastic: function (t) {
680
+ var s = 1.70158;
681
+ var p = 0;
682
+ var a = 1;
683
+ if (t === 0){
684
+ return 0;
685
+ }
686
+ if ((t /= 1 / 2) == 2){
687
+ return 1;
688
+ }
689
+ if (!p){
690
+ p = 1 * (0.3 * 1.5);
691
+ }
692
+ if (a < Math.abs(1)) {
693
+ a = 1;
694
+ s = p / 4;
695
+ } else {
696
+ s = p / (2 * Math.PI) * Math.asin(1 / a);
697
+ }
698
+ if (t < 1){
699
+ return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p));}
700
+ return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) * 0.5 + 1;
701
+ },
702
+ easeInBack: function (t) {
703
+ var s = 1.70158;
704
+ return 1 * (t /= 1) * t * ((s + 1) * t - s);
705
+ },
706
+ easeOutBack: function (t) {
707
+ var s = 1.70158;
708
+ return 1 * ((t = t / 1 - 1) * t * ((s + 1) * t + s) + 1);
709
+ },
710
+ easeInOutBack: function (t) {
711
+ var s = 1.70158;
712
+ if ((t /= 1 / 2) < 1){
713
+ return 1 / 2 * (t * t * (((s *= (1.525)) + 1) * t - s));
714
+ }
715
+ return 1 / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2);
716
+ },
717
+ easeInBounce: function (t) {
718
+ return 1 - easingEffects.easeOutBounce(1 - t);
719
+ },
720
+ easeOutBounce: function (t) {
721
+ if ((t /= 1) < (1 / 2.75)) {
722
+ return 1 * (7.5625 * t * t);
723
+ } else if (t < (2 / 2.75)) {
724
+ return 1 * (7.5625 * (t -= (1.5 / 2.75)) * t + 0.75);
725
+ } else if (t < (2.5 / 2.75)) {
726
+ return 1 * (7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375);
727
+ } else {
728
+ return 1 * (7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375);
729
+ }
730
+ },
731
+ easeInOutBounce: function (t) {
732
+ if (t < 1 / 2){
733
+ return easingEffects.easeInBounce(t * 2) * 0.5;
734
+ }
735
+ return easingEffects.easeOutBounce(t * 2 - 1) * 0.5 + 1 * 0.5;
736
+ }
737
+ },
738
+ //Request animation polyfill - http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
739
+ requestAnimFrame = helpers.requestAnimFrame = (function(){
740
+ return window.requestAnimationFrame ||
741
+ window.webkitRequestAnimationFrame ||
742
+ window.mozRequestAnimationFrame ||
743
+ window.oRequestAnimationFrame ||
744
+ window.msRequestAnimationFrame ||
745
+ function(callback) {
746
+ return window.setTimeout(callback, 1000 / 60);
747
+ };
748
+ })(),
749
+ cancelAnimFrame = helpers.cancelAnimFrame = (function(){
750
+ return window.cancelAnimationFrame ||
751
+ window.webkitCancelAnimationFrame ||
752
+ window.mozCancelAnimationFrame ||
753
+ window.oCancelAnimationFrame ||
754
+ window.msCancelAnimationFrame ||
755
+ function(callback) {
756
+ return window.clearTimeout(callback, 1000 / 60);
757
+ };
758
+ })(),
759
+ animationLoop = helpers.animationLoop = function(callback,totalSteps,easingString,onProgress,onComplete,chartInstance){
760
+
761
+ var currentStep = 0,
762
+ easingFunction = easingEffects[easingString] || easingEffects.linear;
763
+
764
+ var animationFrame = function(){
765
+ currentStep++;
766
+ var stepDecimal = currentStep/totalSteps;
767
+ var easeDecimal = easingFunction(stepDecimal);
768
+
769
+ callback.call(chartInstance,easeDecimal,stepDecimal, currentStep);
770
+ onProgress.call(chartInstance,easeDecimal,stepDecimal);
771
+ if (currentStep < totalSteps){
772
+ chartInstance.animationFrame = requestAnimFrame(animationFrame);
773
+ } else{
774
+ onComplete.apply(chartInstance);
775
+ }
776
+ };
777
+ requestAnimFrame(animationFrame);
778
+ },
779
+ //-- DOM methods
780
+ getRelativePosition = helpers.getRelativePosition = function(evt){
781
+ var mouseX, mouseY;
782
+ var e = evt.originalEvent || evt,
783
+ canvas = evt.currentTarget || evt.srcElement,
784
+ boundingRect = canvas.getBoundingClientRect();
785
+
786
+ if (e.touches){
787
+ mouseX = e.touches[0].clientX - boundingRect.left;
788
+ mouseY = e.touches[0].clientY - boundingRect.top;
789
+
790
+ }
791
+ else{
792
+ mouseX = e.clientX - boundingRect.left;
793
+ mouseY = e.clientY - boundingRect.top;
794
+ }
795
+
796
+ return {
797
+ x : mouseX,
798
+ y : mouseY
799
+ };
800
+
801
+ },
802
+ addEvent = helpers.addEvent = function(node,eventType,method){
803
+ if (node.addEventListener){
804
+ node.addEventListener(eventType,method);
805
+ } else if (node.attachEvent){
806
+ node.attachEvent("on"+eventType, method);
807
+ } else {
808
+ node["on"+eventType] = method;
809
+ }
810
+ },
811
+ removeEvent = helpers.removeEvent = function(node, eventType, handler){
812
+ if (node.removeEventListener){
813
+ node.removeEventListener(eventType, handler, false);
814
+ } else if (node.detachEvent){
815
+ node.detachEvent("on"+eventType,handler);
816
+ } else{
817
+ node["on" + eventType] = noop;
818
+ }
819
+ },
820
+ bindEvents = helpers.bindEvents = function(chartInstance, arrayOfEvents, handler){
821
+ // Create the events object if it's not already present
822
+ if (!chartInstance.events) chartInstance.events = {};
823
+
824
+ each(arrayOfEvents,function(eventName){
825
+ chartInstance.events[eventName] = function(){
826
+ handler.apply(chartInstance, arguments);
827
+ };
828
+ addEvent(chartInstance.chart.canvas,eventName,chartInstance.events[eventName]);
829
+ });
830
+ },
831
+ unbindEvents = helpers.unbindEvents = function (chartInstance, arrayOfEvents) {
832
+ each(arrayOfEvents, function(handler,eventName){
833
+ removeEvent(chartInstance.chart.canvas, eventName, handler);
834
+ });
835
+ },
836
+ getMaximumWidth = helpers.getMaximumWidth = function(domNode){
837
+ var container = domNode.parentNode,
838
+ padding = parseInt(getStyle(container, 'padding-left')) + parseInt(getStyle(container, 'padding-right'));
839
+ // TODO = check cross browser stuff with this.
840
+ return container ? container.clientWidth - padding : 0;
841
+ },
842
+ getMaximumHeight = helpers.getMaximumHeight = function(domNode){
843
+ var container = domNode.parentNode,
844
+ padding = parseInt(getStyle(container, 'padding-bottom')) + parseInt(getStyle(container, 'padding-top'));
845
+ // TODO = check cross browser stuff with this.
846
+ return container ? container.clientHeight - padding : 0;
847
+ },
848
+ getStyle = helpers.getStyle = function (el, property) {
849
+ return el.currentStyle ?
850
+ el.currentStyle[property] :
851
+ document.defaultView.getComputedStyle(el, null).getPropertyValue(property);
852
+ },
853
+ getMaximumSize = helpers.getMaximumSize = helpers.getMaximumWidth, // legacy support
854
+ retinaScale = helpers.retinaScale = function(chart){
855
+ var ctx = chart.ctx,
856
+ width = chart.canvas.width,
857
+ height = chart.canvas.height;
858
+
859
+ if (window.devicePixelRatio) {
860
+ ctx.canvas.style.width = width + "px";
861
+ ctx.canvas.style.height = height + "px";
862
+ ctx.canvas.height = height * window.devicePixelRatio;
863
+ ctx.canvas.width = width * window.devicePixelRatio;
864
+ ctx.scale(window.devicePixelRatio, window.devicePixelRatio);
865
+ }
866
+ },
867
+ //-- Canvas methods
868
+ clear = helpers.clear = function(chart){
869
+ chart.ctx.clearRect(0,0,chart.width,chart.height);
870
+ },
871
+ fontString = helpers.fontString = function(pixelSize,fontStyle,fontFamily){
872
+ return fontStyle + " " + pixelSize+"px " + fontFamily;
873
+ },
874
+ longestText = helpers.longestText = function(ctx,font,arrayOfStrings){
875
+ ctx.font = font;
876
+ var longest = 0;
877
+ each(arrayOfStrings,function(string){
878
+ var textWidth = ctx.measureText(string).width;
879
+ longest = (textWidth > longest) ? textWidth : longest;
880
+ });
881
+ return longest;
882
+ },
883
+ drawRoundedRectangle = helpers.drawRoundedRectangle = function(ctx,x,y,width,height,radius){
884
+ ctx.beginPath();
885
+ ctx.moveTo(x + radius, y);
886
+ ctx.lineTo(x + width - radius, y);
887
+ ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
888
+ ctx.lineTo(x + width, y + height - radius);
889
+ ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
890
+ ctx.lineTo(x + radius, y + height);
891
+ ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
892
+ ctx.lineTo(x, y + radius);
893
+ ctx.quadraticCurveTo(x, y, x + radius, y);
894
+ ctx.closePath();
895
+ };
896
+
897
+
898
+ //Store a reference to each instance - allowing us to globally resize chart instances on window resize.
899
+ //Destroy method on the chart will remove the instance of the chart from this reference.
900
+ Chart.instances = {};
901
+
902
+ Chart.Type = function(data,options,chart){
903
+ this.options = options;
904
+ this.chart = chart;
905
+ this.id = uid();
906
+ //Add the chart instance to the global namespace
907
+ Chart.instances[this.id] = this;
908
+
909
+ // Initialize is always called when a chart type is created
910
+ // By default it is a no op, but it should be extended
911
+ if (options.responsive){
912
+ this.resize();
913
+ }
914
+ this.initialize.call(this,data);
915
+ };
916
+
917
+ //Core methods that'll be a part of every chart type
918
+ extend(Chart.Type.prototype,{
919
+ initialize : function(){return this;},
920
+ clear : function(){
921
+ clear(this.chart);
922
+ return this;
923
+ },
924
+ stop : function(){
925
+ // Stops any current animation loop occuring
926
+ Chart.animationService.cancelAnimation(this);
927
+ return this;
928
+ },
929
+ resize : function(callback){
930
+ this.stop();
931
+ var canvas = this.chart.canvas,
932
+ newWidth = getMaximumWidth(this.chart.canvas),
933
+ newHeight = this.options.maintainAspectRatio ? newWidth / this.chart.aspectRatio : getMaximumHeight(this.chart.canvas);
934
+
935
+ canvas.width = this.chart.width = newWidth;
936
+ canvas.height = this.chart.height = newHeight;
937
+
938
+ retinaScale(this.chart);
939
+
940
+ if (typeof callback === "function"){
941
+ callback.apply(this, Array.prototype.slice.call(arguments, 1));
942
+ }
943
+ return this;
944
+ },
945
+ reflow : noop,
946
+ render : function(reflow){
947
+ if (reflow){
948
+ this.reflow();
949
+ }
950
+
951
+ if (this.options.animation && !reflow){
952
+ var animation = new Chart.Animation();
953
+ animation.numSteps = this.options.animationSteps;
954
+ animation.easing = this.options.animationEasing;
955
+
956
+ // render function
957
+ animation.render = function(chartInstance, animationObject) {
958
+ var easingFunction = helpers.easingEffects[animationObject.easing];
959
+ var stepDecimal = animationObject.currentStep / animationObject.numSteps;
960
+ var easeDecimal = easingFunction(stepDecimal);
961
+
962
+ chartInstance.draw(easeDecimal, stepDecimal, animationObject.currentStep);
963
+ };
964
+
965
+ // user events
966
+ animation.onAnimationProgress = this.options.onAnimationProgress;
967
+ animation.onAnimationComplete = this.options.onAnimationComplete;
968
+
969
+ Chart.animationService.addAnimation(this, animation);
970
+ }
971
+ else{
972
+ this.draw();
973
+ this.options.onAnimationComplete.call(this);
974
+ }
975
+ return this;
976
+ },
977
+ generateLegend : function(){
978
+ return helpers.template(this.options.legendTemplate, this);
979
+ },
980
+ destroy : function(){
981
+ this.stop();
982
+ this.clear();
983
+ unbindEvents(this, this.events);
984
+ var canvas = this.chart.canvas;
985
+
986
+ // Reset canvas height/width attributes starts a fresh with the canvas context
987
+ canvas.width = this.chart.width;
988
+ canvas.height = this.chart.height;
989
+
990
+ // < IE9 doesn't support removeProperty
991
+ if (canvas.style.removeProperty) {
992
+ canvas.style.removeProperty('width');
993
+ canvas.style.removeProperty('height');
994
+ } else {
995
+ canvas.style.removeAttribute('width');
996
+ canvas.style.removeAttribute('height');
997
+ }
998
+
999
+ delete Chart.instances[this.id];
1000
+ },
1001
+ showTooltip : function(ChartElements, forceRedraw){
1002
+ // Only redraw the chart if we've actually changed what we're hovering on.
1003
+ if (typeof this.activeElements === 'undefined') this.activeElements = [];
1004
+
1005
+ var isChanged = (function(Elements){
1006
+ var changed = false;
1007
+
1008
+ if (Elements.length !== this.activeElements.length){
1009
+ changed = true;
1010
+ return changed;
1011
+ }
1012
+
1013
+ each(Elements, function(element, index){
1014
+ if (element !== this.activeElements[index]){
1015
+ changed = true;
1016
+ }
1017
+ }, this);
1018
+ return changed;
1019
+ }).call(this, ChartElements);
1020
+
1021
+ if (!isChanged && !forceRedraw){
1022
+ return;
1023
+ }
1024
+ else{
1025
+ this.activeElements = ChartElements;
1026
+ }
1027
+ this.draw();
1028
+ if(this.options.customTooltips){
1029
+ this.options.customTooltips(false);
1030
+ }
1031
+ if (ChartElements.length > 0){
1032
+ // If we have multiple datasets, show a MultiTooltip for all of the data points at that index
1033
+ if (this.datasets && this.datasets.length > 1) {
1034
+ var dataArray,
1035
+ dataIndex;
1036
+
1037
+ for (var i = this.datasets.length - 1; i >= 0; i--) {
1038
+ dataArray = this.datasets[i].points || this.datasets[i].bars || this.datasets[i].segments;
1039
+ dataIndex = indexOf(dataArray, ChartElements[0]);
1040
+ if (dataIndex !== -1){
1041
+ break;
1042
+ }
1043
+ }
1044
+ var tooltipLabels = [],
1045
+ tooltipColors = [],
1046
+ medianPosition = (function(index) {
1047
+
1048
+ // Get all the points at that particular index
1049
+ var Elements = [],
1050
+ dataCollection,
1051
+ xPositions = [],
1052
+ yPositions = [],
1053
+ xMax,
1054
+ yMax,
1055
+ xMin,
1056
+ yMin;
1057
+ helpers.each(this.datasets, function(dataset){
1058
+ dataCollection = dataset.points || dataset.bars || dataset.segments;
1059
+ if (dataCollection[dataIndex] && dataCollection[dataIndex].hasValue()){
1060
+ Elements.push(dataCollection[dataIndex]);
1061
+ }
1062
+ });
1063
+
1064
+ helpers.each(Elements, function(element) {
1065
+ xPositions.push(element.x);
1066
+ yPositions.push(element.y);
1067
+
1068
+
1069
+ //Include any colour information about the element
1070
+ tooltipLabels.push(helpers.template(this.options.multiTooltipTemplate, element));
1071
+ tooltipColors.push({
1072
+ fill: element._saved.fillColor || element.fillColor,
1073
+ stroke: element._saved.strokeColor || element.strokeColor
1074
+ });
1075
+
1076
+ }, this);
1077
+
1078
+ yMin = min(yPositions);
1079
+ yMax = max(yPositions);
1080
+
1081
+ xMin = min(xPositions);
1082
+ xMax = max(xPositions);
1083
+
1084
+ return {
1085
+ x: (xMin > this.chart.width/2) ? xMin : xMax,
1086
+ y: (yMin + yMax)/2
1087
+ };
1088
+ }).call(this, dataIndex);
1089
+
1090
+ new Chart.MultiTooltip({
1091
+ x: medianPosition.x,
1092
+ y: medianPosition.y,
1093
+ xPadding: this.options.tooltipXPadding,
1094
+ yPadding: this.options.tooltipYPadding,
1095
+ xOffset: this.options.tooltipXOffset,
1096
+ fillColor: this.options.tooltipFillColor,
1097
+ textColor: this.options.tooltipFontColor,
1098
+ fontFamily: this.options.tooltipFontFamily,
1099
+ fontStyle: this.options.tooltipFontStyle,
1100
+ fontSize: this.options.tooltipFontSize,
1101
+ titleTextColor: this.options.tooltipTitleFontColor,
1102
+ titleFontFamily: this.options.tooltipTitleFontFamily,
1103
+ titleFontStyle: this.options.tooltipTitleFontStyle,
1104
+ titleFontSize: this.options.tooltipTitleFontSize,
1105
+ cornerRadius: this.options.tooltipCornerRadius,
1106
+ labels: tooltipLabels,
1107
+ legendColors: tooltipColors,
1108
+ legendColorBackground : this.options.multiTooltipKeyBackground,
1109
+ title: template(this.options.tooltipTitleTemplate,ChartElements[0]),
1110
+ chart: this.chart,
1111
+ ctx: this.chart.ctx,
1112
+ custom: this.options.customTooltips
1113
+ }).draw();
1114
+
1115
+ } else {
1116
+ each(ChartElements, function(Element) {
1117
+ var tooltipPosition = Element.tooltipPosition();
1118
+ new Chart.Tooltip({
1119
+ x: Math.round(tooltipPosition.x),
1120
+ y: Math.round(tooltipPosition.y),
1121
+ xPadding: this.options.tooltipXPadding,
1122
+ yPadding: this.options.tooltipYPadding,
1123
+ fillColor: this.options.tooltipFillColor,
1124
+ textColor: this.options.tooltipFontColor,
1125
+ fontFamily: this.options.tooltipFontFamily,
1126
+ fontStyle: this.options.tooltipFontStyle,
1127
+ fontSize: this.options.tooltipFontSize,
1128
+ caretHeight: this.options.tooltipCaretSize,
1129
+ cornerRadius: this.options.tooltipCornerRadius,
1130
+ text: template(this.options.tooltipTemplate, Element),
1131
+ chart: this.chart,
1132
+ custom: this.options.customTooltips
1133
+ }).draw();
1134
+ }, this);
1135
+ }
1136
+ }
1137
+ return this;
1138
+ },
1139
+ toBase64Image : function(){
1140
+ return this.chart.canvas.toDataURL.apply(this.chart.canvas, arguments);
1141
+ }
1142
+ });
1143
+
1144
+ Chart.Type.extend = function(extensions){
1145
+
1146
+ var parent = this;
1147
+
1148
+ var ChartType = function(){
1149
+ return parent.apply(this,arguments);
1150
+ };
1151
+
1152
+ //Copy the prototype object of the this class
1153
+ ChartType.prototype = clone(parent.prototype);
1154
+ //Now overwrite some of the properties in the base class with the new extensions
1155
+ extend(ChartType.prototype, extensions);
1156
+
1157
+ ChartType.extend = Chart.Type.extend;
1158
+
1159
+ if (extensions.name || parent.prototype.name){
1160
+
1161
+ var chartName = extensions.name || parent.prototype.name;
1162
+ //Assign any potential default values of the new chart type
1163
+
1164
+ //If none are defined, we'll use a clone of the chart type this is being extended from.
1165
+ //I.e. if we extend a line chart, we'll use the defaults from the line chart if our new chart
1166
+ //doesn't define some defaults of their own.
1167
+
1168
+ var baseDefaults = (Chart.defaults[parent.prototype.name]) ? clone(Chart.defaults[parent.prototype.name]) : {};
1169
+
1170
+ Chart.defaults[chartName] = extend(baseDefaults,extensions.defaults);
1171
+
1172
+ Chart.types[chartName] = ChartType;
1173
+
1174
+ //Register this new chart type in the Chart prototype
1175
+ Chart.prototype[chartName] = function(data,options){
1176
+ var config = merge(Chart.defaults.global, Chart.defaults[chartName], options || {});
1177
+ return new ChartType(data,config,this);
1178
+ };
1179
+ } else{
1180
+ warn("Name not provided for this chart, so it hasn't been registered");
1181
+ }
1182
+ return parent;
1183
+ };
1184
+
1185
+ Chart.Element = function(configuration){
1186
+ extend(this,configuration);
1187
+ this.initialize.apply(this,arguments);
1188
+ this.save();
1189
+ };
1190
+ extend(Chart.Element.prototype,{
1191
+ initialize : function(){},
1192
+ restore : function(props){
1193
+ if (!props){
1194
+ extend(this,this._saved);
1195
+ } else {
1196
+ each(props,function(key){
1197
+ this[key] = this._saved[key];
1198
+ },this);
1199
+ }
1200
+ return this;
1201
+ },
1202
+ save : function(){
1203
+ this._saved = clone(this);
1204
+ delete this._saved._saved;
1205
+ return this;
1206
+ },
1207
+ update : function(newProps){
1208
+ each(newProps,function(value,key){
1209
+ this._saved[key] = this[key];
1210
+ this[key] = value;
1211
+ },this);
1212
+ return this;
1213
+ },
1214
+ transition : function(props,ease){
1215
+ each(props,function(value,key){
1216
+ this[key] = ((value - this._saved[key]) * ease) + this._saved[key];
1217
+ },this);
1218
+ return this;
1219
+ },
1220
+ tooltipPosition : function(){
1221
+ return {
1222
+ x : this.x,
1223
+ y : this.y
1224
+ };
1225
+ },
1226
+ hasValue: function(){
1227
+ return isNumber(this.value);
1228
+ }
1229
+ });
1230
+
1231
+ Chart.Element.extend = inherits;
1232
+
1233
+
1234
+ Chart.Point = Chart.Element.extend({
1235
+ display: true,
1236
+ inRange: function(chartX,chartY){
1237
+ var hitDetectionRange = this.hitDetectionRadius + this.radius;
1238
+ return ((Math.pow(chartX-this.x, 2)+Math.pow(chartY-this.y, 2)) < Math.pow(hitDetectionRange,2));
1239
+ },
1240
+ draw : function(){
1241
+ if (this.display){
1242
+ var ctx = this.ctx;
1243
+ ctx.beginPath();
1244
+
1245
+ ctx.arc(this.x, this.y, this.radius, 0, Math.PI*2);
1246
+ ctx.closePath();
1247
+
1248
+ ctx.strokeStyle = this.strokeColor;
1249
+ ctx.lineWidth = this.strokeWidth;
1250
+
1251
+ ctx.fillStyle = this.fillColor;
1252
+
1253
+ ctx.fill();
1254
+ ctx.stroke();
1255
+ }
1256
+
1257
+
1258
+ //Quick debug for bezier curve splining
1259
+ //Highlights control points and the line between them.
1260
+ //Handy for dev - stripped in the min version.
1261
+
1262
+ // ctx.save();
1263
+ // ctx.fillStyle = "black";
1264
+ // ctx.strokeStyle = "black"
1265
+ // ctx.beginPath();
1266
+ // ctx.arc(this.controlPoints.inner.x,this.controlPoints.inner.y, 2, 0, Math.PI*2);
1267
+ // ctx.fill();
1268
+
1269
+ // ctx.beginPath();
1270
+ // ctx.arc(this.controlPoints.outer.x,this.controlPoints.outer.y, 2, 0, Math.PI*2);
1271
+ // ctx.fill();
1272
+
1273
+ // ctx.moveTo(this.controlPoints.inner.x,this.controlPoints.inner.y);
1274
+ // ctx.lineTo(this.x, this.y);
1275
+ // ctx.lineTo(this.controlPoints.outer.x,this.controlPoints.outer.y);
1276
+ // ctx.stroke();
1277
+
1278
+ // ctx.restore();
1279
+
1280
+
1281
+
1282
+ }
1283
+ });
1284
+
1285
+ Chart.Arc = Chart.Element.extend({
1286
+ inRange : function(chartX,chartY){
1287
+
1288
+ var pointRelativePosition = helpers.getAngleFromPoint(this, {
1289
+ x: chartX,
1290
+ y: chartY
1291
+ });
1292
+
1293
+ // Normalize all angles to 0 - 2*PI (0 - 360°)
1294
+ var pointRelativeAngle = pointRelativePosition.angle % (Math.PI * 2),
1295
+ startAngle = (Math.PI * 2 + this.startAngle) % (Math.PI * 2),
1296
+ endAngle = (Math.PI * 2 + this.endAngle) % (Math.PI * 2) || 360;
1297
+
1298
+ // Calculate wether the pointRelativeAngle is between the start and the end angle
1299
+ var betweenAngles = (endAngle < startAngle) ?
1300
+ pointRelativeAngle <= endAngle || pointRelativeAngle >= startAngle:
1301
+ pointRelativeAngle >= startAngle && pointRelativeAngle <= endAngle;
1302
+
1303
+ //Check if within the range of the open/close angle
1304
+ var withinRadius = (pointRelativePosition.distance >= this.innerRadius && pointRelativePosition.distance <= this.outerRadius);
1305
+
1306
+ return (betweenAngles && withinRadius);
1307
+ //Ensure within the outside of the arc centre, but inside arc outer
1308
+ },
1309
+ tooltipPosition : function(){
1310
+ var centreAngle = this.startAngle + ((this.endAngle - this.startAngle) / 2),
1311
+ rangeFromCentre = (this.outerRadius - this.innerRadius) / 2 + this.innerRadius;
1312
+ return {
1313
+ x : this.x + (Math.cos(centreAngle) * rangeFromCentre),
1314
+ y : this.y + (Math.sin(centreAngle) * rangeFromCentre)
1315
+ };
1316
+ },
1317
+ draw : function(animationPercent){
1318
+
1319
+ var easingDecimal = animationPercent || 1;
1320
+
1321
+ var ctx = this.ctx;
1322
+
1323
+ ctx.beginPath();
1324
+
1325
+ ctx.arc(this.x, this.y, this.outerRadius < 0 ? 0 : this.outerRadius, this.startAngle, this.endAngle);
1326
+
1327
+ ctx.arc(this.x, this.y, this.innerRadius < 0 ? 0 : this.innerRadius, this.endAngle, this.startAngle, true);
1328
+
1329
+ ctx.closePath();
1330
+ ctx.strokeStyle = this.strokeColor;
1331
+ ctx.lineWidth = this.strokeWidth;
1332
+
1333
+ ctx.fillStyle = this.fillColor;
1334
+
1335
+ ctx.fill();
1336
+ ctx.lineJoin = 'bevel';
1337
+
1338
+ if (this.showStroke){
1339
+ ctx.stroke();
1340
+ }
1341
+ }
1342
+ });
1343
+
1344
+ Chart.Rectangle = Chart.Element.extend({
1345
+ draw : function(){
1346
+ var ctx = this.ctx,
1347
+ halfWidth = this.width/2,
1348
+ leftX = this.x - halfWidth,
1349
+ rightX = this.x + halfWidth,
1350
+ top = this.base - (this.base - this.y),
1351
+ halfStroke = this.strokeWidth / 2;
1352
+
1353
+ // Canvas doesn't allow us to stroke inside the width so we can
1354
+ // adjust the sizes to fit if we're setting a stroke on the line
1355
+ if (this.showStroke){
1356
+ leftX += halfStroke;
1357
+ rightX -= halfStroke;
1358
+ top += halfStroke;
1359
+ }
1360
+
1361
+ ctx.beginPath();
1362
+
1363
+ ctx.fillStyle = this.fillColor;
1364
+ ctx.strokeStyle = this.strokeColor;
1365
+ ctx.lineWidth = this.strokeWidth;
1366
+
1367
+ // It'd be nice to keep this class totally generic to any rectangle
1368
+ // and simply specify which border to miss out.
1369
+ ctx.moveTo(leftX, this.base);
1370
+ ctx.lineTo(leftX, top);
1371
+ ctx.lineTo(rightX, top);
1372
+ ctx.lineTo(rightX, this.base);
1373
+ ctx.fill();
1374
+ if (this.showStroke){
1375
+ ctx.stroke();
1376
+ }
1377
+ },
1378
+ height : function(){
1379
+ return this.base - this.y;
1380
+ },
1381
+ inRange : function(chartX,chartY){
1382
+ return (chartX >= this.x - this.width/2 && chartX <= this.x + this.width/2) && (chartY >= this.y && chartY <= this.base);
1383
+ }
1384
+ });
1385
+
1386
+ Chart.Animation = Chart.Element.extend({
1387
+ currentStep: null, // the current animation step
1388
+ numSteps: 60, // default number of steps
1389
+ easing: "", // the easing to use for this animation
1390
+ render: null, // render function used by the animation service
1391
+
1392
+ onAnimationProgress: null, // user specified callback to fire on each step of the animation
1393
+ onAnimationComplete: null, // user specified callback to fire when the animation finishes
1394
+ });
1395
+
1396
+ Chart.Tooltip = Chart.Element.extend({
1397
+ draw : function(){
1398
+
1399
+ var ctx = this.chart.ctx;
1400
+
1401
+ ctx.font = fontString(this.fontSize,this.fontStyle,this.fontFamily);
1402
+
1403
+ this.xAlign = "center";
1404
+ this.yAlign = "above";
1405
+
1406
+ //Distance between the actual element.y position and the start of the tooltip caret
1407
+ var caretPadding = this.caretPadding = 2;
1408
+
1409
+ var tooltipWidth = ctx.measureText(this.text).width + 2*this.xPadding,
1410
+ tooltipRectHeight = this.fontSize + 2*this.yPadding,
1411
+ tooltipHeight = tooltipRectHeight + this.caretHeight + caretPadding;
1412
+
1413
+ if (this.x + tooltipWidth/2 >this.chart.width){
1414
+ this.xAlign = "left";
1415
+ } else if (this.x - tooltipWidth/2 < 0){
1416
+ this.xAlign = "right";
1417
+ }
1418
+
1419
+ if (this.y - tooltipHeight < 0){
1420
+ this.yAlign = "below";
1421
+ }
1422
+
1423
+
1424
+ var tooltipX = this.x - tooltipWidth/2,
1425
+ tooltipY = this.y - tooltipHeight;
1426
+
1427
+ ctx.fillStyle = this.fillColor;
1428
+
1429
+ // Custom Tooltips
1430
+ if(this.custom){
1431
+ this.custom(this);
1432
+ }
1433
+ else{
1434
+ switch(this.yAlign)
1435
+ {
1436
+ case "above":
1437
+ //Draw a caret above the x/y
1438
+ ctx.beginPath();
1439
+ ctx.moveTo(this.x,this.y - caretPadding);
1440
+ ctx.lineTo(this.x + this.caretHeight, this.y - (caretPadding + this.caretHeight));
1441
+ ctx.lineTo(this.x - this.caretHeight, this.y - (caretPadding + this.caretHeight));
1442
+ ctx.closePath();
1443
+ ctx.fill();
1444
+ break;
1445
+ case "below":
1446
+ tooltipY = this.y + caretPadding + this.caretHeight;
1447
+ //Draw a caret below the x/y
1448
+ ctx.beginPath();
1449
+ ctx.moveTo(this.x, this.y + caretPadding);
1450
+ ctx.lineTo(this.x + this.caretHeight, this.y + caretPadding + this.caretHeight);
1451
+ ctx.lineTo(this.x - this.caretHeight, this.y + caretPadding + this.caretHeight);
1452
+ ctx.closePath();
1453
+ ctx.fill();
1454
+ break;
1455
+ }
1456
+
1457
+ switch(this.xAlign)
1458
+ {
1459
+ case "left":
1460
+ tooltipX = this.x - tooltipWidth + (this.cornerRadius + this.caretHeight);
1461
+ break;
1462
+ case "right":
1463
+ tooltipX = this.x - (this.cornerRadius + this.caretHeight);
1464
+ break;
1465
+ }
1466
+
1467
+ drawRoundedRectangle(ctx,tooltipX,tooltipY,tooltipWidth,tooltipRectHeight,this.cornerRadius);
1468
+
1469
+ ctx.fill();
1470
+
1471
+ ctx.fillStyle = this.textColor;
1472
+ ctx.textAlign = "center";
1473
+ ctx.textBaseline = "middle";
1474
+ ctx.fillText(this.text, tooltipX + tooltipWidth/2, tooltipY + tooltipRectHeight/2);
1475
+ }
1476
+ }
1477
+ });
1478
+
1479
+ Chart.MultiTooltip = Chart.Element.extend({
1480
+ initialize : function(){
1481
+ this.font = fontString(this.fontSize,this.fontStyle,this.fontFamily);
1482
+
1483
+ this.titleFont = fontString(this.titleFontSize,this.titleFontStyle,this.titleFontFamily);
1484
+
1485
+ this.titleHeight = this.title ? this.titleFontSize * 1.5 : 0;
1486
+ this.height = (this.labels.length * this.fontSize) + ((this.labels.length-1) * (this.fontSize/2)) + (this.yPadding*2) + this.titleHeight;
1487
+
1488
+ this.ctx.font = this.titleFont;
1489
+
1490
+ var titleWidth = this.ctx.measureText(this.title).width,
1491
+ //Label has a legend square as well so account for this.
1492
+ labelWidth = longestText(this.ctx,this.font,this.labels) + this.fontSize + 3,
1493
+ longestTextWidth = max([labelWidth,titleWidth]);
1494
+
1495
+ this.width = longestTextWidth + (this.xPadding*2);
1496
+
1497
+
1498
+ var halfHeight = this.height/2;
1499
+
1500
+ //Check to ensure the height will fit on the canvas
1501
+ if (this.y - halfHeight < 0 ){
1502
+ this.y = halfHeight;
1503
+ } else if (this.y + halfHeight > this.chart.height){
1504
+ this.y = this.chart.height - halfHeight;
1505
+ }
1506
+
1507
+ //Decide whether to align left or right based on position on canvas
1508
+ if (this.x > this.chart.width/2){
1509
+ this.x -= this.xOffset + this.width;
1510
+ } else {
1511
+ this.x += this.xOffset;
1512
+ }
1513
+
1514
+
1515
+ },
1516
+ getLineHeight : function(index){
1517
+ var baseLineHeight = this.y - (this.height/2) + this.yPadding,
1518
+ afterTitleIndex = index-1;
1519
+
1520
+ //If the index is zero, we're getting the title
1521
+ if (index === 0){
1522
+ return baseLineHeight + this.titleHeight / 3;
1523
+ } else{
1524
+ return baseLineHeight + ((this.fontSize * 1.5 * afterTitleIndex) + this.fontSize / 2) + this.titleHeight;
1525
+ }
1526
+
1527
+ },
1528
+ draw : function(){
1529
+ // Custom Tooltips
1530
+ if(this.custom){
1531
+ this.custom(this);
1532
+ }
1533
+ else{
1534
+ drawRoundedRectangle(this.ctx,this.x,this.y - this.height/2,this.width,this.height,this.cornerRadius);
1535
+ var ctx = this.ctx;
1536
+ ctx.fillStyle = this.fillColor;
1537
+ ctx.fill();
1538
+ ctx.closePath();
1539
+
1540
+ ctx.textAlign = "left";
1541
+ ctx.textBaseline = "middle";
1542
+ ctx.fillStyle = this.titleTextColor;
1543
+ ctx.font = this.titleFont;
1544
+
1545
+ ctx.fillText(this.title,this.x + this.xPadding, this.getLineHeight(0));
1546
+
1547
+ ctx.font = this.font;
1548
+ helpers.each(this.labels,function(label,index){
1549
+ ctx.fillStyle = this.textColor;
1550
+ ctx.fillText(label,this.x + this.xPadding + this.fontSize + 3, this.getLineHeight(index + 1));
1551
+
1552
+ //A bit gnarly, but clearing this rectangle breaks when using explorercanvas (clears whole canvas)
1553
+ //ctx.clearRect(this.x + this.xPadding, this.getLineHeight(index + 1) - this.fontSize/2, this.fontSize, this.fontSize);
1554
+ //Instead we'll make a white filled block to put the legendColour palette over.
1555
+
1556
+ ctx.fillStyle = this.legendColorBackground;
1557
+ ctx.fillRect(this.x + this.xPadding, this.getLineHeight(index + 1) - this.fontSize/2, this.fontSize, this.fontSize);
1558
+
1559
+ ctx.fillStyle = this.legendColors[index].fill;
1560
+ ctx.fillRect(this.x + this.xPadding, this.getLineHeight(index + 1) - this.fontSize/2, this.fontSize, this.fontSize);
1561
+
1562
+
1563
+ },this);
1564
+ }
1565
+ }
1566
+ });
1567
+
1568
+ Chart.Scale = Chart.Element.extend({
1569
+ initialize : function(){
1570
+ this.fit();
1571
+ },
1572
+ buildYLabels : function(){
1573
+ this.yLabels = [];
1574
+
1575
+ var stepDecimalPlaces = getDecimalPlaces(this.stepValue);
1576
+
1577
+ for (var i=0; i<=this.steps; i++){
1578
+ this.yLabels.push(template(this.templateString,{value:(this.min + (i * this.stepValue)).toFixed(stepDecimalPlaces)}));
1579
+ }
1580
+ this.yLabelWidth = (this.display && this.showLabels) ? longestText(this.ctx,this.font,this.yLabels) + 10 : 0;
1581
+ },
1582
+ addXLabel : function(label){
1583
+ this.xLabels.push(label);
1584
+ this.valuesCount++;
1585
+ this.fit();
1586
+ },
1587
+ removeXLabel : function(){
1588
+ this.xLabels.shift();
1589
+ this.valuesCount--;
1590
+ this.fit();
1591
+ },
1592
+ // Fitting loop to rotate x Labels and figure out what fits there, and also calculate how many Y steps to use
1593
+ fit: function(){
1594
+ // First we need the width of the yLabels, assuming the xLabels aren't rotated
1595
+
1596
+ // To do that we need the base line at the top and base of the chart, assuming there is no x label rotation
1597
+ this.startPoint = (this.display) ? this.fontSize : 0;
1598
+ this.endPoint = (this.display) ? this.height - (this.fontSize * 1.5) - 5 : this.height; // -5 to pad labels
1599
+
1600
+ // Apply padding settings to the start and end point.
1601
+ this.startPoint += this.padding;
1602
+ this.endPoint -= this.padding;
1603
+
1604
+ // Cache the starting endpoint, excluding the space for x labels
1605
+ var cachedEndPoint = this.endPoint;
1606
+
1607
+ // Cache the starting height, so can determine if we need to recalculate the scale yAxis
1608
+ var cachedHeight = this.endPoint - this.startPoint,
1609
+ cachedYLabelWidth;
1610
+
1611
+ // Build the current yLabels so we have an idea of what size they'll be to start
1612
+ /*
1613
+ * This sets what is returned from calculateScaleRange as static properties of this class:
1614
+ *
1615
+ this.steps;
1616
+ this.stepValue;
1617
+ this.min;
1618
+ this.max;
1619
+ *
1620
+ */
1621
+ this.calculateYRange(cachedHeight);
1622
+
1623
+ // With these properties set we can now build the array of yLabels
1624
+ // and also the width of the largest yLabel
1625
+ this.buildYLabels();
1626
+
1627
+ this.calculateXLabelRotation();
1628
+
1629
+ while((cachedHeight > this.endPoint - this.startPoint)){
1630
+ cachedHeight = this.endPoint - this.startPoint;
1631
+ cachedYLabelWidth = this.yLabelWidth;
1632
+
1633
+ this.calculateYRange(cachedHeight);
1634
+ this.buildYLabels();
1635
+
1636
+ // Only go through the xLabel loop again if the yLabel width has changed
1637
+ if (cachedYLabelWidth < this.yLabelWidth){
1638
+ this.endPoint = cachedEndPoint;
1639
+ this.calculateXLabelRotation();
1640
+ }
1641
+ }
1642
+
1643
+ },
1644
+ calculateXLabelRotation : function(){
1645
+ //Get the width of each grid by calculating the difference
1646
+ //between x offsets between 0 and 1.
1647
+
1648
+ this.ctx.font = this.font;
1649
+
1650
+ var firstWidth = this.ctx.measureText(this.xLabels[0]).width,
1651
+ lastWidth = this.ctx.measureText(this.xLabels[this.xLabels.length - 1]).width,
1652
+ firstRotated,
1653
+ lastRotated;
1654
+
1655
+
1656
+ this.xScalePaddingRight = lastWidth/2 + 3;
1657
+ this.xScalePaddingLeft = (firstWidth/2 > this.yLabelWidth) ? firstWidth/2 : this.yLabelWidth;
1658
+
1659
+ this.xLabelRotation = 0;
1660
+ if (this.display){
1661
+ var originalLabelWidth = longestText(this.ctx,this.font,this.xLabels),
1662
+ cosRotation,
1663
+ firstRotatedWidth;
1664
+ this.xLabelWidth = originalLabelWidth;
1665
+ //Allow 3 pixels x2 padding either side for label readability
1666
+ var xGridWidth = Math.floor(this.calculateX(1) - this.calculateX(0)) - 6;
1667
+
1668
+ //Max label rotate should be 90 - also act as a loop counter
1669
+ while ((this.xLabelWidth > xGridWidth && this.xLabelRotation === 0) || (this.xLabelWidth > xGridWidth && this.xLabelRotation <= 90 && this.xLabelRotation > 0)){
1670
+ cosRotation = Math.cos(toRadians(this.xLabelRotation));
1671
+
1672
+ firstRotated = cosRotation * firstWidth;
1673
+ lastRotated = cosRotation * lastWidth;
1674
+
1675
+ // We're right aligning the text now.
1676
+ if (firstRotated + this.fontSize / 2 > this.yLabelWidth){
1677
+ this.xScalePaddingLeft = firstRotated + this.fontSize / 2;
1678
+ }
1679
+ this.xScalePaddingRight = this.fontSize/2;
1680
+
1681
+
1682
+ this.xLabelRotation++;
1683
+ this.xLabelWidth = cosRotation * originalLabelWidth;
1684
+
1685
+ }
1686
+ if (this.xLabelRotation > 0){
1687
+ this.endPoint -= Math.sin(toRadians(this.xLabelRotation))*originalLabelWidth + 3;
1688
+ }
1689
+ }
1690
+ else{
1691
+ this.xLabelWidth = 0;
1692
+ this.xScalePaddingRight = this.padding;
1693
+ this.xScalePaddingLeft = this.padding;
1694
+ }
1695
+
1696
+ },
1697
+ // Needs to be overidden in each Chart type
1698
+ // Otherwise we need to pass all the data into the scale class
1699
+ calculateYRange: noop,
1700
+ drawingArea: function(){
1701
+ return this.startPoint - this.endPoint;
1702
+ },
1703
+ calculateY : function(value){
1704
+ var scalingFactor = this.drawingArea() / (this.min - this.max);
1705
+ return this.endPoint - (scalingFactor * (value - this.min));
1706
+ },
1707
+ calculateX : function(index){
1708
+ var isRotated = (this.xLabelRotation > 0),
1709
+ // innerWidth = (this.offsetGridLines) ? this.width - offsetLeft - this.padding : this.width - (offsetLeft + halfLabelWidth * 2) - this.padding,
1710
+ innerWidth = this.width - (this.xScalePaddingLeft + this.xScalePaddingRight),
1711
+ valueWidth = innerWidth/Math.max((this.valuesCount - ((this.offsetGridLines) ? 0 : 1)), 1),
1712
+ valueOffset = (valueWidth * index) + this.xScalePaddingLeft;
1713
+
1714
+ if (this.offsetGridLines){
1715
+ valueOffset += (valueWidth/2);
1716
+ }
1717
+
1718
+ return Math.round(valueOffset);
1719
+ },
1720
+ update : function(newProps){
1721
+ helpers.extend(this, newProps);
1722
+ this.fit();
1723
+ },
1724
+ draw : function(){
1725
+ var ctx = this.ctx,
1726
+ yLabelGap = (this.endPoint - this.startPoint) / this.steps,
1727
+ xStart = Math.round(this.xScalePaddingLeft);
1728
+ if (this.display){
1729
+ ctx.fillStyle = this.textColor;
1730
+ ctx.font = this.font;
1731
+ each(this.yLabels,function(labelString,index){
1732
+ var yLabelCenter = this.endPoint - (yLabelGap * index),
1733
+ linePositionY = Math.round(yLabelCenter),
1734
+ drawHorizontalLine = this.showHorizontalLines;
1735
+
1736
+ ctx.textAlign = "right";
1737
+ ctx.textBaseline = "middle";
1738
+ if (this.showLabels){
1739
+ ctx.fillText(labelString,xStart - 10,yLabelCenter);
1740
+ }
1741
+
1742
+ // This is X axis, so draw it
1743
+ if (index === 0 && !drawHorizontalLine){
1744
+ drawHorizontalLine = true;
1745
+ }
1746
+
1747
+ if (drawHorizontalLine){
1748
+ ctx.beginPath();
1749
+ }
1750
+
1751
+ if (index > 0){
1752
+ // This is a grid line in the centre, so drop that
1753
+ ctx.lineWidth = this.gridLineWidth;
1754
+ ctx.strokeStyle = this.gridLineColor;
1755
+ } else {
1756
+ // This is the first line on the scale
1757
+ ctx.lineWidth = this.lineWidth;
1758
+ ctx.strokeStyle = this.lineColor;
1759
+ }
1760
+
1761
+ linePositionY += helpers.aliasPixel(ctx.lineWidth);
1762
+
1763
+ if(drawHorizontalLine){
1764
+ ctx.moveTo(xStart, linePositionY);
1765
+ ctx.lineTo(this.width, linePositionY);
1766
+ ctx.stroke();
1767
+ ctx.closePath();
1768
+ }
1769
+
1770
+ ctx.lineWidth = this.lineWidth;
1771
+ ctx.strokeStyle = this.lineColor;
1772
+ ctx.beginPath();
1773
+ ctx.moveTo(xStart - 5, linePositionY);
1774
+ ctx.lineTo(xStart, linePositionY);
1775
+ ctx.stroke();
1776
+ ctx.closePath();
1777
+
1778
+ },this);
1779
+
1780
+ each(this.xLabels,function(label,index){
1781
+ var xPos = this.calculateX(index) + aliasPixel(this.lineWidth),
1782
+ // Check to see if line/bar here and decide where to place the line
1783
+ linePos = this.calculateX(index - (this.offsetGridLines ? 0.5 : 0)) + aliasPixel(this.lineWidth),
1784
+ isRotated = (this.xLabelRotation > 0),
1785
+ drawVerticalLine = this.showVerticalLines;
1786
+
1787
+ // This is Y axis, so draw it
1788
+ if (index === 0 && !drawVerticalLine){
1789
+ drawVerticalLine = true;
1790
+ }
1791
+
1792
+ if (drawVerticalLine){
1793
+ ctx.beginPath();
1794
+ }
1795
+
1796
+ if (index > 0){
1797
+ // This is a grid line in the centre, so drop that
1798
+ ctx.lineWidth = this.gridLineWidth;
1799
+ ctx.strokeStyle = this.gridLineColor;
1800
+ } else {
1801
+ // This is the first line on the scale
1802
+ ctx.lineWidth = this.lineWidth;
1803
+ ctx.strokeStyle = this.lineColor;
1804
+ }
1805
+
1806
+ if (drawVerticalLine){
1807
+ ctx.moveTo(linePos,this.endPoint);
1808
+ ctx.lineTo(linePos,this.startPoint - 3);
1809
+ ctx.stroke();
1810
+ ctx.closePath();
1811
+ }
1812
+
1813
+
1814
+ ctx.lineWidth = this.lineWidth;
1815
+ ctx.strokeStyle = this.lineColor;
1816
+
1817
+
1818
+ // Small lines at the bottom of the base grid line
1819
+ ctx.beginPath();
1820
+ ctx.moveTo(linePos,this.endPoint);
1821
+ ctx.lineTo(linePos,this.endPoint + 5);
1822
+ ctx.stroke();
1823
+ ctx.closePath();
1824
+
1825
+ ctx.save();
1826
+ ctx.translate(xPos,(isRotated) ? this.endPoint + 12 : this.endPoint + 8);
1827
+ ctx.rotate(toRadians(this.xLabelRotation)*-1);
1828
+ ctx.font = this.font;
1829
+ ctx.textAlign = (isRotated) ? "right" : "center";
1830
+ ctx.textBaseline = (isRotated) ? "middle" : "top";
1831
+ ctx.fillText(label, 0, 0);
1832
+ ctx.restore();
1833
+ },this);
1834
+
1835
+ }
1836
+ }
1837
+
1838
+ });
1839
+
1840
+ Chart.RadialScale = Chart.Element.extend({
1841
+ initialize: function(){
1842
+ this.size = min([this.height, this.width]);
1843
+ this.drawingArea = (this.display) ? (this.size/2) - (this.fontSize/2 + this.backdropPaddingY) : (this.size/2);
1844
+ },
1845
+ calculateCenterOffset: function(value){
1846
+ // Take into account half font size + the yPadding of the top value
1847
+ var scalingFactor = this.drawingArea / (this.max - this.min);
1848
+
1849
+ return (value - this.min) * scalingFactor;
1850
+ },
1851
+ update : function(){
1852
+ if (!this.lineArc){
1853
+ this.setScaleSize();
1854
+ } else {
1855
+ this.drawingArea = (this.display) ? (this.size/2) - (this.fontSize/2 + this.backdropPaddingY) : (this.size/2);
1856
+ }
1857
+ this.buildYLabels();
1858
+ },
1859
+ buildYLabels: function(){
1860
+ this.yLabels = [];
1861
+
1862
+ var stepDecimalPlaces = getDecimalPlaces(this.stepValue);
1863
+
1864
+ for (var i=0; i<=this.steps; i++){
1865
+ this.yLabels.push(template(this.templateString,{value:(this.min + (i * this.stepValue)).toFixed(stepDecimalPlaces)}));
1866
+ }
1867
+ },
1868
+ getCircumference : function(){
1869
+ return ((Math.PI*2) / this.valuesCount);
1870
+ },
1871
+ setScaleSize: function(){
1872
+ /*
1873
+ * Right, this is really confusing and there is a lot of maths going on here
1874
+ * The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9
1875
+ *
1876
+ * Reaction: https://dl.dropboxusercontent.com/u/34601363/toomuchscience.gif
1877
+ *
1878
+ * Solution:
1879
+ *
1880
+ * We assume the radius of the polygon is half the size of the canvas at first
1881
+ * at each index we check if the text overlaps.
1882
+ *
1883
+ * Where it does, we store that angle and that index.
1884
+ *
1885
+ * After finding the largest index and angle we calculate how much we need to remove
1886
+ * from the shape radius to move the point inwards by that x.
1887
+ *
1888
+ * We average the left and right distances to get the maximum shape radius that can fit in the box
1889
+ * along with labels.
1890
+ *
1891
+ * Once we have that, we can find the centre point for the chart, by taking the x text protrusion
1892
+ * on each side, removing that from the size, halving it and adding the left x protrusion width.
1893
+ *
1894
+ * This will mean we have a shape fitted to the canvas, as large as it can be with the labels
1895
+ * and position it in the most space efficient manner
1896
+ *
1897
+ * https://dl.dropboxusercontent.com/u/34601363/yeahscience.gif
1898
+ */
1899
+
1900
+
1901
+ // Get maximum radius of the polygon. Either half the height (minus the text width) or half the width.
1902
+ // Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points
1903
+ var largestPossibleRadius = min([(this.height/2 - this.pointLabelFontSize - 5), this.width/2]),
1904
+ pointPosition,
1905
+ i,
1906
+ textWidth,
1907
+ halfTextWidth,
1908
+ furthestRight = this.width,
1909
+ furthestRightIndex,
1910
+ furthestRightAngle,
1911
+ furthestLeft = 0,
1912
+ furthestLeftIndex,
1913
+ furthestLeftAngle,
1914
+ xProtrusionLeft,
1915
+ xProtrusionRight,
1916
+ radiusReductionRight,
1917
+ radiusReductionLeft,
1918
+ maxWidthRadius;
1919
+ this.ctx.font = fontString(this.pointLabelFontSize,this.pointLabelFontStyle,this.pointLabelFontFamily);
1920
+ for (i=0;i<this.valuesCount;i++){
1921
+ // 5px to space the text slightly out - similar to what we do in the draw function.
1922
+ pointPosition = this.getPointPosition(i, largestPossibleRadius);
1923
+ textWidth = this.ctx.measureText(template(this.templateString, { value: this.labels[i] })).width + 5;
1924
+ if (i === 0 || i === this.valuesCount/2){
1925
+ // If we're at index zero, or exactly the middle, we're at exactly the top/bottom
1926
+ // of the radar chart, so text will be aligned centrally, so we'll half it and compare
1927
+ // w/left and right text sizes
1928
+ halfTextWidth = textWidth/2;
1929
+ if (pointPosition.x + halfTextWidth > furthestRight) {
1930
+ furthestRight = pointPosition.x + halfTextWidth;
1931
+ furthestRightIndex = i;
1932
+ }
1933
+ if (pointPosition.x - halfTextWidth < furthestLeft) {
1934
+ furthestLeft = pointPosition.x - halfTextWidth;
1935
+ furthestLeftIndex = i;
1936
+ }
1937
+ }
1938
+ else if (i < this.valuesCount/2) {
1939
+ // Less than half the values means we'll left align the text
1940
+ if (pointPosition.x + textWidth > furthestRight) {
1941
+ furthestRight = pointPosition.x + textWidth;
1942
+ furthestRightIndex = i;
1943
+ }
1944
+ }
1945
+ else if (i > this.valuesCount/2){
1946
+ // More than half the values means we'll right align the text
1947
+ if (pointPosition.x - textWidth < furthestLeft) {
1948
+ furthestLeft = pointPosition.x - textWidth;
1949
+ furthestLeftIndex = i;
1950
+ }
1951
+ }
1952
+ }
1953
+
1954
+ xProtrusionLeft = furthestLeft;
1955
+
1956
+ xProtrusionRight = Math.ceil(furthestRight - this.width);
1957
+
1958
+ furthestRightAngle = this.getIndexAngle(furthestRightIndex);
1959
+
1960
+ furthestLeftAngle = this.getIndexAngle(furthestLeftIndex);
1961
+
1962
+ radiusReductionRight = xProtrusionRight / Math.sin(furthestRightAngle + Math.PI/2);
1963
+
1964
+ radiusReductionLeft = xProtrusionLeft / Math.sin(furthestLeftAngle + Math.PI/2);
1965
+
1966
+ // Ensure we actually need to reduce the size of the chart
1967
+ radiusReductionRight = (isNumber(radiusReductionRight)) ? radiusReductionRight : 0;
1968
+ radiusReductionLeft = (isNumber(radiusReductionLeft)) ? radiusReductionLeft : 0;
1969
+
1970
+ this.drawingArea = largestPossibleRadius - (radiusReductionLeft + radiusReductionRight)/2;
1971
+
1972
+ //this.drawingArea = min([maxWidthRadius, (this.height - (2 * (this.pointLabelFontSize + 5)))/2])
1973
+ this.setCenterPoint(radiusReductionLeft, radiusReductionRight);
1974
+
1975
+ },
1976
+ setCenterPoint: function(leftMovement, rightMovement){
1977
+
1978
+ var maxRight = this.width - rightMovement - this.drawingArea,
1979
+ maxLeft = leftMovement + this.drawingArea;
1980
+
1981
+ this.xCenter = (maxLeft + maxRight)/2;
1982
+ // Always vertically in the centre as the text height doesn't change
1983
+ this.yCenter = (this.height/2);
1984
+ },
1985
+
1986
+ getIndexAngle : function(index){
1987
+ var angleMultiplier = (Math.PI * 2) / this.valuesCount;
1988
+ // Start from the top instead of right, so remove a quarter of the circle
1989
+
1990
+ return index * angleMultiplier - (Math.PI/2);
1991
+ },
1992
+ getPointPosition : function(index, distanceFromCenter){
1993
+ var thisAngle = this.getIndexAngle(index);
1994
+ return {
1995
+ x : (Math.cos(thisAngle) * distanceFromCenter) + this.xCenter,
1996
+ y : (Math.sin(thisAngle) * distanceFromCenter) + this.yCenter
1997
+ };
1998
+ },
1999
+ draw: function(){
2000
+ if (this.display){
2001
+ var ctx = this.ctx;
2002
+ each(this.yLabels, function(label, index){
2003
+ // Don't draw a centre value
2004
+ if (index > 0){
2005
+ var yCenterOffset = index * (this.drawingArea/this.steps),
2006
+ yHeight = this.yCenter - yCenterOffset,
2007
+ pointPosition;
2008
+
2009
+ // Draw circular lines around the scale
2010
+ if (this.lineWidth > 0){
2011
+ ctx.strokeStyle = this.lineColor;
2012
+ ctx.lineWidth = this.lineWidth;
2013
+
2014
+ if(this.lineArc){
2015
+ ctx.beginPath();
2016
+ ctx.arc(this.xCenter, this.yCenter, yCenterOffset, 0, Math.PI*2);
2017
+ ctx.closePath();
2018
+ ctx.stroke();
2019
+ } else{
2020
+ ctx.beginPath();
2021
+ for (var i=0;i<this.valuesCount;i++)
2022
+ {
2023
+ pointPosition = this.getPointPosition(i, this.calculateCenterOffset(this.min + (index * this.stepValue)));
2024
+ if (i === 0){
2025
+ ctx.moveTo(pointPosition.x, pointPosition.y);
2026
+ } else {
2027
+ ctx.lineTo(pointPosition.x, pointPosition.y);
2028
+ }
2029
+ }
2030
+ ctx.closePath();
2031
+ ctx.stroke();
2032
+ }
2033
+ }
2034
+ if(this.showLabels){
2035
+ ctx.font = fontString(this.fontSize,this.fontStyle,this.fontFamily);
2036
+ if (this.showLabelBackdrop){
2037
+ var labelWidth = ctx.measureText(label).width;
2038
+ ctx.fillStyle = this.backdropColor;
2039
+ ctx.fillRect(
2040
+ this.xCenter - labelWidth/2 - this.backdropPaddingX,
2041
+ yHeight - this.fontSize/2 - this.backdropPaddingY,
2042
+ labelWidth + this.backdropPaddingX*2,
2043
+ this.fontSize + this.backdropPaddingY*2
2044
+ );
2045
+ }
2046
+ ctx.textAlign = 'center';
2047
+ ctx.textBaseline = "middle";
2048
+ ctx.fillStyle = this.fontColor;
2049
+ ctx.fillText(label, this.xCenter, yHeight);
2050
+ }
2051
+ }
2052
+ }, this);
2053
+
2054
+ if (!this.lineArc){
2055
+ ctx.lineWidth = this.angleLineWidth;
2056
+ ctx.strokeStyle = this.angleLineColor;
2057
+ for (var i = this.valuesCount - 1; i >= 0; i--) {
2058
+ var centerOffset = null, outerPosition = null;
2059
+
2060
+ if (this.angleLineWidth > 0 && (i % this.angleLineInterval === 0)){
2061
+ centerOffset = this.calculateCenterOffset(this.max);
2062
+ outerPosition = this.getPointPosition(i, centerOffset);
2063
+ ctx.beginPath();
2064
+ ctx.moveTo(this.xCenter, this.yCenter);
2065
+ ctx.lineTo(outerPosition.x, outerPosition.y);
2066
+ ctx.stroke();
2067
+ ctx.closePath();
2068
+ }
2069
+
2070
+ if (this.backgroundColors && this.backgroundColors.length == this.valuesCount) {
2071
+ if (centerOffset == null)
2072
+ centerOffset = this.calculateCenterOffset(this.max);
2073
+
2074
+ if (outerPosition == null)
2075
+ outerPosition = this.getPointPosition(i, centerOffset);
2076
+
2077
+ var previousOuterPosition = this.getPointPosition(i === 0 ? this.valuesCount - 1 : i - 1, centerOffset);
2078
+ var nextOuterPosition = this.getPointPosition(i === this.valuesCount - 1 ? 0 : i + 1, centerOffset);
2079
+
2080
+ var previousOuterHalfway = { x: (previousOuterPosition.x + outerPosition.x) / 2, y: (previousOuterPosition.y + outerPosition.y) / 2 };
2081
+ var nextOuterHalfway = { x: (outerPosition.x + nextOuterPosition.x) / 2, y: (outerPosition.y + nextOuterPosition.y) / 2 };
2082
+
2083
+ ctx.beginPath();
2084
+ ctx.moveTo(this.xCenter, this.yCenter);
2085
+ ctx.lineTo(previousOuterHalfway.x, previousOuterHalfway.y);
2086
+ ctx.lineTo(outerPosition.x, outerPosition.y);
2087
+ ctx.lineTo(nextOuterHalfway.x, nextOuterHalfway.y);
2088
+ ctx.fillStyle = this.backgroundColors[i];
2089
+ ctx.fill();
2090
+ ctx.closePath();
2091
+ }
2092
+ // Extra 3px out for some label spacing
2093
+ var pointLabelPosition = this.getPointPosition(i, this.calculateCenterOffset(this.max) + 5);
2094
+ ctx.font = fontString(this.pointLabelFontSize,this.pointLabelFontStyle,this.pointLabelFontFamily);
2095
+ ctx.fillStyle = this.pointLabelFontColor;
2096
+
2097
+ var labelsCount = this.labels.length,
2098
+ halfLabelsCount = this.labels.length/2,
2099
+ quarterLabelsCount = halfLabelsCount/2,
2100
+ upperHalf = (i < quarterLabelsCount || i > labelsCount - quarterLabelsCount),
2101
+ exactQuarter = (i === quarterLabelsCount || i === labelsCount - quarterLabelsCount);
2102
+ if (i === 0){
2103
+ ctx.textAlign = 'center';
2104
+ } else if(i === halfLabelsCount){
2105
+ ctx.textAlign = 'center';
2106
+ } else if (i < halfLabelsCount){
2107
+ ctx.textAlign = 'left';
2108
+ } else {
2109
+ ctx.textAlign = 'right';
2110
+ }
2111
+
2112
+ // Set the correct text baseline based on outer positioning
2113
+ if (exactQuarter){
2114
+ ctx.textBaseline = 'middle';
2115
+ } else if (upperHalf){
2116
+ ctx.textBaseline = 'bottom';
2117
+ } else {
2118
+ ctx.textBaseline = 'top';
2119
+ }
2120
+
2121
+ ctx.fillText(this.labels[i], pointLabelPosition.x, pointLabelPosition.y);
2122
+ }
2123
+ }
2124
+ }
2125
+ }
2126
+ });
2127
+
2128
+ Chart.animationService = {
2129
+ frameDuration: 17,
2130
+ animations: [],
2131
+ dropFrames: 0,
2132
+ addAnimation: function(chartInstance, animationObject) {
2133
+ for (var index = 0; index < this.animations.length; ++ index){
2134
+ if (this.animations[index].chartInstance === chartInstance){
2135
+ // replacing an in progress animation
2136
+ this.animations[index].animationObject = animationObject;
2137
+ return;
2138
+ }
2139
+ }
2140
+
2141
+ this.animations.push({
2142
+ chartInstance: chartInstance,
2143
+ animationObject: animationObject
2144
+ });
2145
+
2146
+ // If there are no animations queued, manually kickstart a digest, for lack of a better word
2147
+ if (this.animations.length == 1) {
2148
+ helpers.requestAnimFrame.call(window, this.digestWrapper);
2149
+ }
2150
+ },
2151
+ // Cancel the animation for a given chart instance
2152
+ cancelAnimation: function(chartInstance) {
2153
+ var index = helpers.findNextWhere(this.animations, function(animationWrapper) {
2154
+ return animationWrapper.chartInstance === chartInstance;
2155
+ });
2156
+
2157
+ if (index)
2158
+ {
2159
+ this.animations.splice(index, 1);
2160
+ }
2161
+ },
2162
+ // calls startDigest with the proper context
2163
+ digestWrapper: function() {
2164
+ Chart.animationService.startDigest.call(Chart.animationService);
2165
+ },
2166
+ startDigest: function() {
2167
+
2168
+ var startTime = Date.now();
2169
+ var framesToDrop = 0;
2170
+
2171
+ if(this.dropFrames > 1){
2172
+ framesToDrop = Math.floor(this.dropFrames);
2173
+ this.dropFrames -= framesToDrop;
2174
+ }
2175
+
2176
+ for (var i = 0; i < this.animations.length; i++) {
2177
+
2178
+ if (this.animations[i].animationObject.currentStep === null){
2179
+ this.animations[i].animationObject.currentStep = 0;
2180
+ }
2181
+
2182
+ this.animations[i].animationObject.currentStep += 1 + framesToDrop;
2183
+ if(this.animations[i].animationObject.currentStep > this.animations[i].animationObject.numSteps){
2184
+ this.animations[i].animationObject.currentStep = this.animations[i].animationObject.numSteps;
2185
+ }
2186
+
2187
+ this.animations[i].animationObject.render(this.animations[i].chartInstance, this.animations[i].animationObject);
2188
+
2189
+ // Check if executed the last frame.
2190
+ if (this.animations[i].animationObject.currentStep == this.animations[i].animationObject.numSteps){
2191
+ // Call onAnimationComplete
2192
+ this.animations[i].animationObject.onAnimationComplete.call(this.animations[i].chartInstance);
2193
+ // Remove the animation.
2194
+ this.animations.splice(i, 1);
2195
+ // Keep the index in place to offset the splice
2196
+ i--;
2197
+ }
2198
+ }
2199
+
2200
+ var endTime = Date.now();
2201
+ var delay = endTime - startTime - this.frameDuration;
2202
+ var frameDelay = delay / this.frameDuration;
2203
+
2204
+ if(frameDelay > 1){
2205
+ this.dropFrames += frameDelay;
2206
+ }
2207
+
2208
+ // Do we have more stuff to animate?
2209
+ if (this.animations.length > 0){
2210
+ helpers.requestAnimFrame.call(window, this.digestWrapper);
2211
+ }
2212
+ }
2213
+ };
2214
+
2215
+ // Attach global event to resize each chart instance when the browser resizes
2216
+ helpers.addEvent(window, "resize", (function(){
2217
+ // Basic debounce of resize function so it doesn't hurt performance when resizing browser.
2218
+ var timeout;
2219
+ return function(){
2220
+ clearTimeout(timeout);
2221
+ timeout = setTimeout(function(){
2222
+ each(Chart.instances,function(instance){
2223
+ // If the responsive flag is set in the chart instance config
2224
+ // Cascade the resize event down to the chart.
2225
+ if (instance.options.responsive){
2226
+ instance.resize(instance.render, true);
2227
+ }
2228
+ });
2229
+ }, 50);
2230
+ };
2231
+ })());
2232
+
2233
+
2234
+ if (amd) {
2235
+ define('Chart', [], function(){
2236
+ return Chart;
2237
+ });
2238
+ } else if (typeof module === 'object' && module.exports) {
2239
+ module.exports = Chart;
2240
+ }
2241
+
2242
+ root.Chart = Chart;
2243
+
2244
+ Chart.noConflict = function(){
2245
+ root.Chart = previous;
2246
+ return Chart;
2247
+ };
2248
+
2249
+ }).call(this);
2250
+
2251
+ (function(){
2252
+ "use strict";
2253
+
2254
+ var root = this,
2255
+ Chart = root.Chart,
2256
+ helpers = Chart.helpers;
2257
+
2258
+
2259
+ var defaultConfig = {
2260
+ //Boolean - Whether the scale should start at zero, or an order of magnitude down from the lowest value
2261
+ scaleBeginAtZero : true,
2262
+
2263
+ //Boolean - Whether grid lines are shown across the chart
2264
+ scaleShowGridLines : true,
2265
+
2266
+ //String - Colour of the grid lines
2267
+ scaleGridLineColor : "rgba(0,0,0,.05)",
2268
+
2269
+ //Number - Width of the grid lines
2270
+ scaleGridLineWidth : 1,
2271
+
2272
+ //Boolean - Whether to show horizontal lines (except X axis)
2273
+ scaleShowHorizontalLines: true,
2274
+
2275
+ //Boolean - Whether to show vertical lines (except Y axis)
2276
+ scaleShowVerticalLines: true,
2277
+
2278
+ //Boolean - If there is a stroke on each bar
2279
+ barShowStroke : true,
2280
+
2281
+ //Number - Pixel width of the bar stroke
2282
+ barStrokeWidth : 2,
2283
+
2284
+ //Number - Spacing between each of the X value sets
2285
+ barValueSpacing : 5,
2286
+
2287
+ //Number - Spacing between data sets within X values
2288
+ barDatasetSpacing : 1,
2289
+
2290
+ //String - A legend template
2291
+ legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span class=\"<%=name.toLowerCase()%>-legend-icon\" style=\"background-color:<%=datasets[i].fillColor%>\"></span><span class=\"<%=name.toLowerCase()%>-legend-text\"><%if(datasets[i].label){%><%=datasets[i].label%><%}%></span></li><%}%></ul>"
2292
+
2293
+ };
2294
+
2295
+
2296
+ Chart.Type.extend({
2297
+ name: "Bar",
2298
+ defaults : defaultConfig,
2299
+ initialize: function(data){
2300
+
2301
+ //Expose options as a scope variable here so we can access it in the ScaleClass
2302
+ var options = this.options;
2303
+
2304
+ this.ScaleClass = Chart.Scale.extend({
2305
+ offsetGridLines : true,
2306
+ calculateBarX : function(datasetCount, datasetIndex, barIndex){
2307
+ //Reusable method for calculating the xPosition of a given bar based on datasetIndex & width of the bar
2308
+ var xWidth = this.calculateBaseWidth(),
2309
+ xAbsolute = this.calculateX(barIndex) - (xWidth/2),
2310
+ barWidth = this.calculateBarWidth(datasetCount);
2311
+
2312
+ return xAbsolute + (barWidth * datasetIndex) + (datasetIndex * options.barDatasetSpacing) + barWidth/2;
2313
+ },
2314
+ calculateBaseWidth : function(){
2315
+ return (this.calculateX(1) - this.calculateX(0)) - (2*options.barValueSpacing);
2316
+ },
2317
+ calculateBarWidth : function(datasetCount){
2318
+ //The padding between datasets is to the right of each bar, providing that there are more than 1 dataset
2319
+ var baseWidth = this.calculateBaseWidth() - ((datasetCount - 1) * options.barDatasetSpacing);
2320
+
2321
+ return (baseWidth / datasetCount);
2322
+ }
2323
+ });
2324
+
2325
+ this.datasets = [];
2326
+
2327
+ //Set up tooltip events on the chart
2328
+ if (this.options.showTooltips){
2329
+ helpers.bindEvents(this, this.options.tooltipEvents, function(evt){
2330
+ var activeBars = (evt.type !== 'mouseout') ? this.getBarsAtEvent(evt) : [];
2331
+
2332
+ this.eachBars(function(bar){
2333
+ bar.restore(['fillColor', 'strokeColor']);
2334
+ });
2335
+ helpers.each(activeBars, function(activeBar){
2336
+ if (activeBar) {
2337
+ activeBar.fillColor = activeBar.highlightFill;
2338
+ activeBar.strokeColor = activeBar.highlightStroke;
2339
+ }
2340
+ });
2341
+ this.showTooltip(activeBars);
2342
+ });
2343
+ }
2344
+
2345
+ //Declare the extension of the default point, to cater for the options passed in to the constructor
2346
+ this.BarClass = Chart.Rectangle.extend({
2347
+ strokeWidth : this.options.barStrokeWidth,
2348
+ showStroke : this.options.barShowStroke,
2349
+ ctx : this.chart.ctx
2350
+ });
2351
+
2352
+ //Iterate through each of the datasets, and build this into a property of the chart
2353
+ helpers.each(data.datasets,function(dataset,datasetIndex){
2354
+
2355
+ var datasetObject = {
2356
+ label : dataset.label || null,
2357
+ fillColor : dataset.fillColor,
2358
+ strokeColor : dataset.strokeColor,
2359
+ bars : []
2360
+ };
2361
+
2362
+ this.datasets.push(datasetObject);
2363
+
2364
+ helpers.each(dataset.data,function(dataPoint,index){
2365
+ //Add a new point for each piece of data, passing any required data to draw.
2366
+ datasetObject.bars.push(new this.BarClass({
2367
+ value : dataPoint,
2368
+ label : data.labels[index],
2369
+ datasetLabel: dataset.label,
2370
+ strokeColor : (typeof dataset.strokeColor == 'object') ? dataset.strokeColor[index] : dataset.strokeColor,
2371
+ fillColor : (typeof dataset.fillColor == 'object') ? dataset.fillColor[index] : dataset.fillColor,
2372
+ highlightFill : (dataset.highlightFill) ? (typeof dataset.highlightFill == 'object') ? dataset.highlightFill[index] : dataset.highlightFill : (typeof dataset.fillColor == 'object') ? dataset.fillColor[index] : dataset.fillColor,
2373
+ highlightStroke : (dataset.highlightStroke) ? (typeof dataset.highlightStroke == 'object') ? dataset.highlightStroke[index] : dataset.highlightStroke : (typeof dataset.strokeColor == 'object') ? dataset.strokeColor[index] : dataset.strokeColor
2374
+ }));
2375
+ },this);
2376
+
2377
+ },this);
2378
+
2379
+ this.buildScale(data.labels);
2380
+
2381
+ this.BarClass.prototype.base = this.scale.endPoint;
2382
+
2383
+ this.eachBars(function(bar, index, datasetIndex){
2384
+ helpers.extend(bar, {
2385
+ width : this.scale.calculateBarWidth(this.datasets.length),
2386
+ x: this.scale.calculateBarX(this.datasets.length, datasetIndex, index),
2387
+ y: this.scale.endPoint
2388
+ });
2389
+ bar.save();
2390
+ }, this);
2391
+
2392
+ this.render();
2393
+ },
2394
+ update : function(){
2395
+ this.scale.update();
2396
+ // Reset any highlight colours before updating.
2397
+ helpers.each(this.activeElements, function(activeElement){
2398
+ activeElement.restore(['fillColor', 'strokeColor']);
2399
+ });
2400
+
2401
+ this.eachBars(function(bar){
2402
+ bar.save();
2403
+ });
2404
+ this.render();
2405
+ },
2406
+ eachBars : function(callback){
2407
+ helpers.each(this.datasets,function(dataset, datasetIndex){
2408
+ helpers.each(dataset.bars, callback, this, datasetIndex);
2409
+ },this);
2410
+ },
2411
+ getBarsAtEvent : function(e){
2412
+ var barsArray = [],
2413
+ eventPosition = helpers.getRelativePosition(e),
2414
+ datasetIterator = function(dataset){
2415
+ barsArray.push(dataset.bars[barIndex]);
2416
+ },
2417
+ barIndex;
2418
+
2419
+ for (var datasetIndex = 0; datasetIndex < this.datasets.length; datasetIndex++) {
2420
+ for (barIndex = 0; barIndex < this.datasets[datasetIndex].bars.length; barIndex++) {
2421
+ if (this.datasets[datasetIndex].bars[barIndex].inRange(eventPosition.x,eventPosition.y)){
2422
+ helpers.each(this.datasets, datasetIterator);
2423
+ return barsArray;
2424
+ }
2425
+ }
2426
+ }
2427
+
2428
+ return barsArray;
2429
+ },
2430
+ buildScale : function(labels){
2431
+ var self = this;
2432
+
2433
+ var dataTotal = function(){
2434
+ var values = [];
2435
+ self.eachBars(function(bar){
2436
+ values.push(bar.value);
2437
+ });
2438
+ return values;
2439
+ };
2440
+
2441
+ var scaleOptions = {
2442
+ templateString : this.options.scaleLabel,
2443
+ height : this.chart.height,
2444
+ width : this.chart.width,
2445
+ ctx : this.chart.ctx,
2446
+ textColor : this.options.scaleFontColor,
2447
+ fontSize : this.options.scaleFontSize,
2448
+ fontStyle : this.options.scaleFontStyle,
2449
+ fontFamily : this.options.scaleFontFamily,
2450
+ valuesCount : labels.length,
2451
+ beginAtZero : this.options.scaleBeginAtZero,
2452
+ integersOnly : this.options.scaleIntegersOnly,
2453
+ calculateYRange: function(currentHeight){
2454
+ var updatedRanges = helpers.calculateScaleRange(
2455
+ dataTotal(),
2456
+ currentHeight,
2457
+ this.fontSize,
2458
+ this.beginAtZero,
2459
+ this.integersOnly
2460
+ );
2461
+ helpers.extend(this, updatedRanges);
2462
+ },
2463
+ xLabels : labels,
2464
+ font : helpers.fontString(this.options.scaleFontSize, this.options.scaleFontStyle, this.options.scaleFontFamily),
2465
+ lineWidth : this.options.scaleLineWidth,
2466
+ lineColor : this.options.scaleLineColor,
2467
+ showHorizontalLines : this.options.scaleShowHorizontalLines,
2468
+ showVerticalLines : this.options.scaleShowVerticalLines,
2469
+ gridLineWidth : (this.options.scaleShowGridLines) ? this.options.scaleGridLineWidth : 0,
2470
+ gridLineColor : (this.options.scaleShowGridLines) ? this.options.scaleGridLineColor : "rgba(0,0,0,0)",
2471
+ padding : (this.options.showScale) ? 0 : (this.options.barShowStroke) ? this.options.barStrokeWidth : 0,
2472
+ showLabels : this.options.scaleShowLabels,
2473
+ display : this.options.showScale
2474
+ };
2475
+
2476
+ if (this.options.scaleOverride){
2477
+ helpers.extend(scaleOptions, {
2478
+ calculateYRange: helpers.noop,
2479
+ steps: this.options.scaleSteps,
2480
+ stepValue: this.options.scaleStepWidth,
2481
+ min: this.options.scaleStartValue,
2482
+ max: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth)
2483
+ });
2484
+ }
2485
+
2486
+ this.scale = new this.ScaleClass(scaleOptions);
2487
+ },
2488
+ addData : function(valuesArray,label){
2489
+ //Map the values array for each of the datasets
2490
+ helpers.each(valuesArray,function(value,datasetIndex){
2491
+ //Add a new point for each piece of data, passing any required data to draw.
2492
+ this.datasets[datasetIndex].bars.push(new this.BarClass({
2493
+ value : value,
2494
+ label : label,
2495
+ datasetLabel: this.datasets[datasetIndex].label,
2496
+ x: this.scale.calculateBarX(this.datasets.length, datasetIndex, this.scale.valuesCount+1),
2497
+ y: this.scale.endPoint,
2498
+ width : this.scale.calculateBarWidth(this.datasets.length),
2499
+ base : this.scale.endPoint,
2500
+ strokeColor : this.datasets[datasetIndex].strokeColor,
2501
+ fillColor : this.datasets[datasetIndex].fillColor
2502
+ }));
2503
+ },this);
2504
+
2505
+ this.scale.addXLabel(label);
2506
+ //Then re-render the chart.
2507
+ this.update();
2508
+ },
2509
+ removeData : function(){
2510
+ this.scale.removeXLabel();
2511
+ //Then re-render the chart.
2512
+ helpers.each(this.datasets,function(dataset){
2513
+ dataset.bars.shift();
2514
+ },this);
2515
+ this.update();
2516
+ },
2517
+ reflow : function(){
2518
+ helpers.extend(this.BarClass.prototype,{
2519
+ y: this.scale.endPoint,
2520
+ base : this.scale.endPoint
2521
+ });
2522
+ var newScaleProps = helpers.extend({
2523
+ height : this.chart.height,
2524
+ width : this.chart.width
2525
+ });
2526
+ this.scale.update(newScaleProps);
2527
+ },
2528
+ draw : function(ease){
2529
+ var easingDecimal = ease || 1;
2530
+ this.clear();
2531
+
2532
+ var ctx = this.chart.ctx;
2533
+
2534
+ this.scale.draw(easingDecimal);
2535
+
2536
+ //Draw all the bars for each dataset
2537
+ helpers.each(this.datasets,function(dataset,datasetIndex){
2538
+ helpers.each(dataset.bars,function(bar,index){
2539
+ if (bar.hasValue()){
2540
+ bar.base = this.scale.endPoint;
2541
+ //Transition then draw
2542
+ bar.transition({
2543
+ x : this.scale.calculateBarX(this.datasets.length, datasetIndex, index),
2544
+ y : this.scale.calculateY(bar.value),
2545
+ width : this.scale.calculateBarWidth(this.datasets.length)
2546
+ }, easingDecimal).draw();
2547
+ }
2548
+ },this);
2549
+
2550
+ },this);
2551
+ }
2552
+ });
2553
+
2554
+
2555
+ }).call(this);
2556
+
2557
+ (function(){
2558
+ "use strict";
2559
+
2560
+ var root = this,
2561
+ Chart = root.Chart,
2562
+ //Cache a local reference to Chart.helpers
2563
+ helpers = Chart.helpers;
2564
+
2565
+ var defaultConfig = {
2566
+ //Boolean - Whether we should show a stroke on each segment
2567
+ segmentShowStroke : true,
2568
+
2569
+ //String - The colour of each segment stroke
2570
+ segmentStrokeColor : "#fff",
2571
+
2572
+ //Number - The width of each segment stroke
2573
+ segmentStrokeWidth : 2,
2574
+
2575
+ //The percentage of the chart that we cut out of the middle.
2576
+ percentageInnerCutout : 50,
2577
+
2578
+ //Number - Amount of animation steps
2579
+ animationSteps : 100,
2580
+
2581
+ //String - Animation easing effect
2582
+ animationEasing : "easeOutBounce",
2583
+
2584
+ //Boolean - Whether we animate the rotation of the Doughnut
2585
+ animateRotate : true,
2586
+
2587
+ //Boolean - Whether we animate scaling the Doughnut from the centre
2588
+ animateScale : false,
2589
+
2590
+ //String - A legend template
2591
+ legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<segments.length; i++){%><li><span class=\"<%=name.toLowerCase()%>-legend-icon\" style=\"background-color:<%=segments[i].fillColor%>\"></span><span class=\"<%=name.toLowerCase()%>-legend-text\"><%if(segments[i].label){%><%=segments[i].label%><%}%></span></li><%}%></ul>"
2592
+
2593
+ };
2594
+
2595
+ Chart.Type.extend({
2596
+ //Passing in a name registers this chart in the Chart namespace
2597
+ name: "Doughnut",
2598
+ //Providing a defaults will also register the defaults in the chart namespace
2599
+ defaults : defaultConfig,
2600
+ //Initialize is fired when the chart is initialized - Data is passed in as a parameter
2601
+ //Config is automatically merged by the core of Chart.js, and is available at this.options
2602
+ initialize: function(data){
2603
+
2604
+ //Declare segments as a static property to prevent inheriting across the Chart type prototype
2605
+ this.segments = [];
2606
+ this.outerRadius = (helpers.min([this.chart.width,this.chart.height]) - this.options.segmentStrokeWidth/2)/2;
2607
+
2608
+ this.SegmentArc = Chart.Arc.extend({
2609
+ ctx : this.chart.ctx,
2610
+ x : this.chart.width/2,
2611
+ y : this.chart.height/2
2612
+ });
2613
+
2614
+ //Set up tooltip events on the chart
2615
+ if (this.options.showTooltips){
2616
+ helpers.bindEvents(this, this.options.tooltipEvents, function(evt){
2617
+ var activeSegments = (evt.type !== 'mouseout') ? this.getSegmentsAtEvent(evt) : [];
2618
+
2619
+ helpers.each(this.segments,function(segment){
2620
+ segment.restore(["fillColor"]);
2621
+ });
2622
+ helpers.each(activeSegments,function(activeSegment){
2623
+ activeSegment.fillColor = activeSegment.highlightColor;
2624
+ });
2625
+ this.showTooltip(activeSegments);
2626
+ });
2627
+ }
2628
+ this.calculateTotal(data);
2629
+
2630
+ helpers.each(data,function(datapoint, index){
2631
+ if (!datapoint.color) {
2632
+ datapoint.color = 'hsl(' + (360 * index / data.length) + ', 100%, 50%)';
2633
+ }
2634
+ this.addData(datapoint, index, true);
2635
+ },this);
2636
+
2637
+ this.render();
2638
+ },
2639
+ getSegmentsAtEvent : function(e){
2640
+ var segmentsArray = [];
2641
+
2642
+ var location = helpers.getRelativePosition(e);
2643
+
2644
+ helpers.each(this.segments,function(segment){
2645
+ if (segment.inRange(location.x,location.y)) segmentsArray.push(segment);
2646
+ },this);
2647
+ return segmentsArray;
2648
+ },
2649
+ addData : function(segment, atIndex, silent){
2650
+ var index = atIndex !== undefined ? atIndex : this.segments.length;
2651
+ if ( typeof(segment.color) === "undefined" ) {
2652
+ segment.color = Chart.defaults.global.segmentColorDefault[index % Chart.defaults.global.segmentColorDefault.length];
2653
+ segment.highlight = Chart.defaults.global.segmentHighlightColorDefaults[index % Chart.defaults.global.segmentHighlightColorDefaults.length];
2654
+ }
2655
+ this.segments.splice(index, 0, new this.SegmentArc({
2656
+ value : segment.value,
2657
+ outerRadius : (this.options.animateScale) ? 0 : this.outerRadius,
2658
+ innerRadius : (this.options.animateScale) ? 0 : (this.outerRadius/100) * this.options.percentageInnerCutout,
2659
+ fillColor : segment.color,
2660
+ highlightColor : segment.highlight || segment.color,
2661
+ showStroke : this.options.segmentShowStroke,
2662
+ strokeWidth : this.options.segmentStrokeWidth,
2663
+ strokeColor : this.options.segmentStrokeColor,
2664
+ startAngle : Math.PI * 1.5,
2665
+ circumference : (this.options.animateRotate) ? 0 : this.calculateCircumference(segment.value),
2666
+ label : segment.label
2667
+ }));
2668
+ if (!silent){
2669
+ this.reflow();
2670
+ this.update();
2671
+ }
2672
+ },
2673
+ calculateCircumference : function(value) {
2674
+ if ( this.total > 0 ) {
2675
+ return (Math.PI*2)*(value / this.total);
2676
+ } else {
2677
+ return 0;
2678
+ }
2679
+ },
2680
+ calculateTotal : function(data){
2681
+ this.total = 0;
2682
+ helpers.each(data,function(segment){
2683
+ this.total += Math.abs(segment.value);
2684
+ },this);
2685
+ },
2686
+ update : function(){
2687
+ this.calculateTotal(this.segments);
2688
+
2689
+ // Reset any highlight colours before updating.
2690
+ helpers.each(this.activeElements, function(activeElement){
2691
+ activeElement.restore(['fillColor']);
2692
+ });
2693
+
2694
+ helpers.each(this.segments,function(segment){
2695
+ segment.save();
2696
+ });
2697
+ this.render();
2698
+ },
2699
+
2700
+ removeData: function(atIndex){
2701
+ var indexToDelete = (helpers.isNumber(atIndex)) ? atIndex : this.segments.length-1;
2702
+ this.segments.splice(indexToDelete, 1);
2703
+ this.reflow();
2704
+ this.update();
2705
+ },
2706
+
2707
+ reflow : function(){
2708
+ helpers.extend(this.SegmentArc.prototype,{
2709
+ x : this.chart.width/2,
2710
+ y : this.chart.height/2
2711
+ });
2712
+ this.outerRadius = (helpers.min([this.chart.width,this.chart.height]) - this.options.segmentStrokeWidth/2)/2;
2713
+ helpers.each(this.segments, function(segment){
2714
+ segment.update({
2715
+ outerRadius : this.outerRadius,
2716
+ innerRadius : (this.outerRadius/100) * this.options.percentageInnerCutout
2717
+ });
2718
+ }, this);
2719
+ },
2720
+ draw : function(easeDecimal){
2721
+ var animDecimal = (easeDecimal) ? easeDecimal : 1;
2722
+ this.clear();
2723
+ helpers.each(this.segments,function(segment,index){
2724
+ segment.transition({
2725
+ circumference : this.calculateCircumference(segment.value),
2726
+ outerRadius : this.outerRadius,
2727
+ innerRadius : (this.outerRadius/100) * this.options.percentageInnerCutout
2728
+ },animDecimal);
2729
+
2730
+ segment.endAngle = segment.startAngle + segment.circumference;
2731
+
2732
+ segment.draw();
2733
+ if (index === 0){
2734
+ segment.startAngle = Math.PI * 1.5;
2735
+ }
2736
+ //Check to see if it's the last segment, if not get the next and update the start angle
2737
+ if (index < this.segments.length-1){
2738
+ this.segments[index+1].startAngle = segment.endAngle;
2739
+ }
2740
+ },this);
2741
+
2742
+ }
2743
+ });
2744
+
2745
+ Chart.types.Doughnut.extend({
2746
+ name : "Pie",
2747
+ defaults : helpers.merge(defaultConfig,{percentageInnerCutout : 0})
2748
+ });
2749
+
2750
+ }).call(this);
2751
+
2752
+ (function(){
2753
+ "use strict";
2754
+
2755
+ var root = this,
2756
+ Chart = root.Chart,
2757
+ helpers = Chart.helpers;
2758
+
2759
+ var defaultConfig = {
2760
+
2761
+ ///Boolean - Whether grid lines are shown across the chart
2762
+ scaleShowGridLines : true,
2763
+
2764
+ //String - Colour of the grid lines
2765
+ scaleGridLineColor : "rgba(0,0,0,.05)",
2766
+
2767
+ //Number - Width of the grid lines
2768
+ scaleGridLineWidth : 1,
2769
+
2770
+ //Boolean - Whether to show horizontal lines (except X axis)
2771
+ scaleShowHorizontalLines: true,
2772
+
2773
+ //Boolean - Whether to show vertical lines (except Y axis)
2774
+ scaleShowVerticalLines: true,
2775
+
2776
+ //Boolean - Whether the line is curved between points
2777
+ bezierCurve : true,
2778
+
2779
+ //Number - Tension of the bezier curve between points
2780
+ bezierCurveTension : 0.4,
2781
+
2782
+ //Boolean - Whether to show a dot for each point
2783
+ pointDot : true,
2784
+
2785
+ //Number - Radius of each point dot in pixels
2786
+ pointDotRadius : 4,
2787
+
2788
+ //Number - Pixel width of point dot stroke
2789
+ pointDotStrokeWidth : 1,
2790
+
2791
+ //Number - amount extra to add to the radius to cater for hit detection outside the drawn point
2792
+ pointHitDetectionRadius : 20,
2793
+
2794
+ //Boolean - Whether to show a stroke for datasets
2795
+ datasetStroke : true,
2796
+
2797
+ //Number - Pixel width of dataset stroke
2798
+ datasetStrokeWidth : 2,
2799
+
2800
+ //Boolean - Whether to fill the dataset with a colour
2801
+ datasetFill : true,
2802
+
2803
+ //String - A legend template
2804
+ legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span class=\"<%=name.toLowerCase()%>-legend-icon\" style=\"background-color:<%=datasets[i].strokeColor%>\"></span><span class=\"<%=name.toLowerCase()%>-legend-text\"><%if(datasets[i].label){%><%=datasets[i].label%><%}%></span></li><%}%></ul>",
2805
+
2806
+ //Boolean - Whether to horizontally center the label and point dot inside the grid
2807
+ offsetGridLines : false
2808
+
2809
+ };
2810
+
2811
+
2812
+ Chart.Type.extend({
2813
+ name: "Line",
2814
+ defaults : defaultConfig,
2815
+ initialize: function(data){
2816
+ //Declare the extension of the default point, to cater for the options passed in to the constructor
2817
+ this.PointClass = Chart.Point.extend({
2818
+ offsetGridLines : this.options.offsetGridLines,
2819
+ strokeWidth : this.options.pointDotStrokeWidth,
2820
+ radius : this.options.pointDotRadius,
2821
+ display: this.options.pointDot,
2822
+ hitDetectionRadius : this.options.pointHitDetectionRadius,
2823
+ ctx : this.chart.ctx,
2824
+ inRange : function(mouseX){
2825
+ return (Math.pow(mouseX-this.x, 2) < Math.pow(this.radius + this.hitDetectionRadius,2));
2826
+ }
2827
+ });
2828
+
2829
+ this.datasets = [];
2830
+
2831
+ //Set up tooltip events on the chart
2832
+ if (this.options.showTooltips){
2833
+ helpers.bindEvents(this, this.options.tooltipEvents, function(evt){
2834
+ var activePoints = (evt.type !== 'mouseout') ? this.getPointsAtEvent(evt) : [];
2835
+ this.eachPoints(function(point){
2836
+ point.restore(['fillColor', 'strokeColor']);
2837
+ });
2838
+ helpers.each(activePoints, function(activePoint){
2839
+ activePoint.fillColor = activePoint.highlightFill;
2840
+ activePoint.strokeColor = activePoint.highlightStroke;
2841
+ });
2842
+ this.showTooltip(activePoints);
2843
+ });
2844
+ }
2845
+
2846
+ //Iterate through each of the datasets, and build this into a property of the chart
2847
+ helpers.each(data.datasets,function(dataset){
2848
+
2849
+ var datasetObject = {
2850
+ label : dataset.label || null,
2851
+ fillColor : dataset.fillColor,
2852
+ strokeColor : dataset.strokeColor,
2853
+ pointColor : dataset.pointColor,
2854
+ pointStrokeColor : dataset.pointStrokeColor,
2855
+ points : []
2856
+ };
2857
+
2858
+ this.datasets.push(datasetObject);
2859
+
2860
+
2861
+ helpers.each(dataset.data,function(dataPoint,index){
2862
+ //Add a new point for each piece of data, passing any required data to draw.
2863
+ datasetObject.points.push(new this.PointClass({
2864
+ value : dataPoint,
2865
+ label : data.labels[index],
2866
+ datasetLabel: dataset.label,
2867
+ strokeColor : dataset.pointStrokeColor,
2868
+ fillColor : dataset.pointColor,
2869
+ highlightFill : dataset.pointHighlightFill || dataset.pointColor,
2870
+ highlightStroke : dataset.pointHighlightStroke || dataset.pointStrokeColor
2871
+ }));
2872
+ },this);
2873
+
2874
+ this.buildScale(data.labels);
2875
+
2876
+
2877
+ this.eachPoints(function(point, index){
2878
+ helpers.extend(point, {
2879
+ x: this.scale.calculateX(index),
2880
+ y: this.scale.endPoint
2881
+ });
2882
+ point.save();
2883
+ }, this);
2884
+
2885
+ },this);
2886
+
2887
+
2888
+ this.render();
2889
+ },
2890
+ update : function(){
2891
+ this.scale.update();
2892
+ // Reset any highlight colours before updating.
2893
+ helpers.each(this.activeElements, function(activeElement){
2894
+ activeElement.restore(['fillColor', 'strokeColor']);
2895
+ });
2896
+ this.eachPoints(function(point){
2897
+ point.save();
2898
+ });
2899
+ this.render();
2900
+ },
2901
+ eachPoints : function(callback){
2902
+ helpers.each(this.datasets,function(dataset){
2903
+ helpers.each(dataset.points,callback,this);
2904
+ },this);
2905
+ },
2906
+ getPointsAtEvent : function(e){
2907
+ var pointsArray = [],
2908
+ eventPosition = helpers.getRelativePosition(e);
2909
+ helpers.each(this.datasets,function(dataset){
2910
+ helpers.each(dataset.points,function(point){
2911
+ if (point.inRange(eventPosition.x,eventPosition.y)) pointsArray.push(point);
2912
+ });
2913
+ },this);
2914
+ return pointsArray;
2915
+ },
2916
+ buildScale : function(labels){
2917
+ var self = this;
2918
+
2919
+ var dataTotal = function(){
2920
+ var values = [];
2921
+ self.eachPoints(function(point){
2922
+ values.push(point.value);
2923
+ });
2924
+
2925
+ return values;
2926
+ };
2927
+
2928
+ var scaleOptions = {
2929
+ templateString : this.options.scaleLabel,
2930
+ height : this.chart.height,
2931
+ width : this.chart.width,
2932
+ ctx : this.chart.ctx,
2933
+ textColor : this.options.scaleFontColor,
2934
+ offsetGridLines : this.options.offsetGridLines,
2935
+ fontSize : this.options.scaleFontSize,
2936
+ fontStyle : this.options.scaleFontStyle,
2937
+ fontFamily : this.options.scaleFontFamily,
2938
+ valuesCount : labels.length,
2939
+ beginAtZero : this.options.scaleBeginAtZero,
2940
+ integersOnly : this.options.scaleIntegersOnly,
2941
+ calculateYRange : function(currentHeight){
2942
+ var updatedRanges = helpers.calculateScaleRange(
2943
+ dataTotal(),
2944
+ currentHeight,
2945
+ this.fontSize,
2946
+ this.beginAtZero,
2947
+ this.integersOnly
2948
+ );
2949
+ helpers.extend(this, updatedRanges);
2950
+ },
2951
+ xLabels : labels,
2952
+ font : helpers.fontString(this.options.scaleFontSize, this.options.scaleFontStyle, this.options.scaleFontFamily),
2953
+ lineWidth : this.options.scaleLineWidth,
2954
+ lineColor : this.options.scaleLineColor,
2955
+ showHorizontalLines : this.options.scaleShowHorizontalLines,
2956
+ showVerticalLines : this.options.scaleShowVerticalLines,
2957
+ gridLineWidth : (this.options.scaleShowGridLines) ? this.options.scaleGridLineWidth : 0,
2958
+ gridLineColor : (this.options.scaleShowGridLines) ? this.options.scaleGridLineColor : "rgba(0,0,0,0)",
2959
+ padding: (this.options.showScale) ? 0 : this.options.pointDotRadius + this.options.pointDotStrokeWidth,
2960
+ showLabels : this.options.scaleShowLabels,
2961
+ display : this.options.showScale
2962
+ };
2963
+
2964
+ if (this.options.scaleOverride){
2965
+ helpers.extend(scaleOptions, {
2966
+ calculateYRange: helpers.noop,
2967
+ steps: this.options.scaleSteps,
2968
+ stepValue: this.options.scaleStepWidth,
2969
+ min: this.options.scaleStartValue,
2970
+ max: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth)
2971
+ });
2972
+ }
2973
+
2974
+
2975
+ this.scale = new Chart.Scale(scaleOptions);
2976
+ },
2977
+ addData : function(valuesArray,label){
2978
+ //Map the values array for each of the datasets
2979
+
2980
+ helpers.each(valuesArray,function(value,datasetIndex){
2981
+ //Add a new point for each piece of data, passing any required data to draw.
2982
+ this.datasets[datasetIndex].points.push(new this.PointClass({
2983
+ value : value,
2984
+ label : label,
2985
+ datasetLabel: this.datasets[datasetIndex].label,
2986
+ x: this.scale.calculateX(this.scale.valuesCount+1),
2987
+ y: this.scale.endPoint,
2988
+ strokeColor : this.datasets[datasetIndex].pointStrokeColor,
2989
+ fillColor : this.datasets[datasetIndex].pointColor
2990
+ }));
2991
+ },this);
2992
+
2993
+ this.scale.addXLabel(label);
2994
+ //Then re-render the chart.
2995
+ this.update();
2996
+ },
2997
+ removeData : function(){
2998
+ this.scale.removeXLabel();
2999
+ //Then re-render the chart.
3000
+ helpers.each(this.datasets,function(dataset){
3001
+ dataset.points.shift();
3002
+ },this);
3003
+ this.update();
3004
+ },
3005
+ reflow : function(){
3006
+ var newScaleProps = helpers.extend({
3007
+ height : this.chart.height,
3008
+ width : this.chart.width
3009
+ });
3010
+ this.scale.update(newScaleProps);
3011
+ },
3012
+ draw : function(ease){
3013
+ var easingDecimal = ease || 1;
3014
+ this.clear();
3015
+
3016
+ var ctx = this.chart.ctx;
3017
+
3018
+ // Some helper methods for getting the next/prev points
3019
+ var hasValue = function(item){
3020
+ return item.value !== null;
3021
+ },
3022
+ nextPoint = function(point, collection, index){
3023
+ return helpers.findNextWhere(collection, hasValue, index) || point;
3024
+ },
3025
+ previousPoint = function(point, collection, index){
3026
+ return helpers.findPreviousWhere(collection, hasValue, index) || point;
3027
+ };
3028
+
3029
+ if (!this.scale) return;
3030
+ this.scale.draw(easingDecimal);
3031
+
3032
+
3033
+ helpers.each(this.datasets,function(dataset){
3034
+ var pointsWithValues = helpers.where(dataset.points, hasValue);
3035
+
3036
+ //Transition each point first so that the line and point drawing isn't out of sync
3037
+ //We can use this extra loop to calculate the control points of this dataset also in this loop
3038
+
3039
+ helpers.each(dataset.points, function(point, index){
3040
+ if (point.hasValue()){
3041
+ point.transition({
3042
+ y : this.scale.calculateY(point.value),
3043
+ x : this.scale.calculateX(index)
3044
+ }, easingDecimal);
3045
+ }
3046
+ },this);
3047
+
3048
+
3049
+ // Control points need to be calculated in a separate loop, because we need to know the current x/y of the point
3050
+ // This would cause issues when there is no animation, because the y of the next point would be 0, so beziers would be skewed
3051
+ if (this.options.bezierCurve){
3052
+ helpers.each(pointsWithValues, function(point, index){
3053
+ var tension = (index > 0 && index < pointsWithValues.length - 1) ? this.options.bezierCurveTension : 0;
3054
+ point.controlPoints = helpers.splineCurve(
3055
+ previousPoint(point, pointsWithValues, index),
3056
+ point,
3057
+ nextPoint(point, pointsWithValues, index),
3058
+ tension
3059
+ );
3060
+
3061
+ // Prevent the bezier going outside of the bounds of the graph
3062
+
3063
+ // Cap puter bezier handles to the upper/lower scale bounds
3064
+ if (point.controlPoints.outer.y > this.scale.endPoint){
3065
+ point.controlPoints.outer.y = this.scale.endPoint;
3066
+ }
3067
+ else if (point.controlPoints.outer.y < this.scale.startPoint){
3068
+ point.controlPoints.outer.y = this.scale.startPoint;
3069
+ }
3070
+
3071
+ // Cap inner bezier handles to the upper/lower scale bounds
3072
+ if (point.controlPoints.inner.y > this.scale.endPoint){
3073
+ point.controlPoints.inner.y = this.scale.endPoint;
3074
+ }
3075
+ else if (point.controlPoints.inner.y < this.scale.startPoint){
3076
+ point.controlPoints.inner.y = this.scale.startPoint;
3077
+ }
3078
+ },this);
3079
+ }
3080
+
3081
+
3082
+ //Draw the line between all the points
3083
+ ctx.lineWidth = this.options.datasetStrokeWidth;
3084
+ ctx.strokeStyle = dataset.strokeColor;
3085
+ ctx.beginPath();
3086
+
3087
+ helpers.each(pointsWithValues, function(point, index){
3088
+ if (index === 0){
3089
+ ctx.moveTo(point.x, point.y);
3090
+ }
3091
+ else{
3092
+ if(this.options.bezierCurve){
3093
+ var previous = previousPoint(point, pointsWithValues, index);
3094
+
3095
+ ctx.bezierCurveTo(
3096
+ previous.controlPoints.outer.x,
3097
+ previous.controlPoints.outer.y,
3098
+ point.controlPoints.inner.x,
3099
+ point.controlPoints.inner.y,
3100
+ point.x,
3101
+ point.y
3102
+ );
3103
+ }
3104
+ else{
3105
+ ctx.lineTo(point.x,point.y);
3106
+ }
3107
+ }
3108
+ }, this);
3109
+
3110
+ if (this.options.datasetStroke) {
3111
+ ctx.stroke();
3112
+ }
3113
+
3114
+ if (this.options.datasetFill && pointsWithValues.length > 0){
3115
+ //Round off the line by going to the base of the chart, back to the start, then fill.
3116
+ ctx.lineTo(pointsWithValues[pointsWithValues.length - 1].x, this.scale.endPoint);
3117
+ ctx.lineTo(pointsWithValues[0].x, this.scale.endPoint);
3118
+ ctx.fillStyle = dataset.fillColor;
3119
+ ctx.closePath();
3120
+ ctx.fill();
3121
+ }
3122
+
3123
+ //Now draw the points over the line
3124
+ //A little inefficient double looping, but better than the line
3125
+ //lagging behind the point positions
3126
+ helpers.each(pointsWithValues,function(point){
3127
+ point.draw();
3128
+ });
3129
+ },this);
3130
+ }
3131
+ });
3132
+
3133
+
3134
+ }).call(this);
3135
+
3136
+ (function(){
3137
+ "use strict";
3138
+
3139
+ var root = this,
3140
+ Chart = root.Chart,
3141
+ //Cache a local reference to Chart.helpers
3142
+ helpers = Chart.helpers;
3143
+
3144
+ var defaultConfig = {
3145
+ //Boolean - Show a backdrop to the scale label
3146
+ scaleShowLabelBackdrop : true,
3147
+
3148
+ //String - The colour of the label backdrop
3149
+ scaleBackdropColor : "rgba(255,255,255,0.75)",
3150
+
3151
+ // Boolean - Whether the scale should begin at zero
3152
+ scaleBeginAtZero : true,
3153
+
3154
+ //Number - The backdrop padding above & below the label in pixels
3155
+ scaleBackdropPaddingY : 2,
3156
+
3157
+ //Number - The backdrop padding to the side of the label in pixels
3158
+ scaleBackdropPaddingX : 2,
3159
+
3160
+ //Boolean - Show line for each value in the scale
3161
+ scaleShowLine : true,
3162
+
3163
+ //Boolean - Stroke a line around each segment in the chart
3164
+ segmentShowStroke : true,
3165
+
3166
+ //String - The colour of the stroke on each segment.
3167
+ segmentStrokeColor : "#fff",
3168
+
3169
+ //Number - The width of the stroke value in pixels
3170
+ segmentStrokeWidth : 2,
3171
+
3172
+ //Number - Amount of animation steps
3173
+ animationSteps : 100,
3174
+
3175
+ //String - Animation easing effect.
3176
+ animationEasing : "easeOutBounce",
3177
+
3178
+ //Boolean - Whether to animate the rotation of the chart
3179
+ animateRotate : true,
3180
+
3181
+ //Boolean - Whether to animate scaling the chart from the centre
3182
+ animateScale : false,
3183
+
3184
+ //String - A legend template
3185
+ legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<segments.length; i++){%><li><span class=\"<%=name.toLowerCase()%>-legend-icon\" style=\"background-color:<%=segments[i].fillColor%>\"></span><span class=\"<%=name.toLowerCase()%>-legend-text\"><%if(segments[i].label){%><%=segments[i].label%><%}%></span></li><%}%></ul>"
3186
+ };
3187
+
3188
+
3189
+ Chart.Type.extend({
3190
+ //Passing in a name registers this chart in the Chart namespace
3191
+ name: "PolarArea",
3192
+ //Providing a defaults will also register the defaults in the chart namespace
3193
+ defaults : defaultConfig,
3194
+ //Initialize is fired when the chart is initialized - Data is passed in as a parameter
3195
+ //Config is automatically merged by the core of Chart.js, and is available at this.options
3196
+ initialize: function(data){
3197
+ this.segments = [];
3198
+ //Declare segment class as a chart instance specific class, so it can share props for this instance
3199
+ this.SegmentArc = Chart.Arc.extend({
3200
+ showStroke : this.options.segmentShowStroke,
3201
+ strokeWidth : this.options.segmentStrokeWidth,
3202
+ strokeColor : this.options.segmentStrokeColor,
3203
+ ctx : this.chart.ctx,
3204
+ innerRadius : 0,
3205
+ x : this.chart.width/2,
3206
+ y : this.chart.height/2
3207
+ });
3208
+ this.scale = new Chart.RadialScale({
3209
+ display: this.options.showScale,
3210
+ fontStyle: this.options.scaleFontStyle,
3211
+ fontSize: this.options.scaleFontSize,
3212
+ fontFamily: this.options.scaleFontFamily,
3213
+ fontColor: this.options.scaleFontColor,
3214
+ showLabels: this.options.scaleShowLabels,
3215
+ showLabelBackdrop: this.options.scaleShowLabelBackdrop,
3216
+ backdropColor: this.options.scaleBackdropColor,
3217
+ backdropPaddingY : this.options.scaleBackdropPaddingY,
3218
+ backdropPaddingX: this.options.scaleBackdropPaddingX,
3219
+ lineWidth: (this.options.scaleShowLine) ? this.options.scaleLineWidth : 0,
3220
+ lineColor: this.options.scaleLineColor,
3221
+ lineArc: true,
3222
+ width: this.chart.width,
3223
+ height: this.chart.height,
3224
+ xCenter: this.chart.width/2,
3225
+ yCenter: this.chart.height/2,
3226
+ ctx : this.chart.ctx,
3227
+ templateString: this.options.scaleLabel,
3228
+ valuesCount: data.length
3229
+ });
3230
+
3231
+ this.updateScaleRange(data);
3232
+
3233
+ this.scale.update();
3234
+
3235
+ helpers.each(data,function(segment,index){
3236
+ this.addData(segment,index,true);
3237
+ },this);
3238
+
3239
+ //Set up tooltip events on the chart
3240
+ if (this.options.showTooltips){
3241
+ helpers.bindEvents(this, this.options.tooltipEvents, function(evt){
3242
+ var activeSegments = (evt.type !== 'mouseout') ? this.getSegmentsAtEvent(evt) : [];
3243
+ helpers.each(this.segments,function(segment){
3244
+ segment.restore(["fillColor"]);
3245
+ });
3246
+ helpers.each(activeSegments,function(activeSegment){
3247
+ activeSegment.fillColor = activeSegment.highlightColor;
3248
+ });
3249
+ this.showTooltip(activeSegments);
3250
+ });
3251
+ }
3252
+
3253
+ this.render();
3254
+ },
3255
+ getSegmentsAtEvent : function(e){
3256
+ var segmentsArray = [];
3257
+
3258
+ var location = helpers.getRelativePosition(e);
3259
+
3260
+ helpers.each(this.segments,function(segment){
3261
+ if (segment.inRange(location.x,location.y)) segmentsArray.push(segment);
3262
+ },this);
3263
+ return segmentsArray;
3264
+ },
3265
+ addData : function(segment, atIndex, silent){
3266
+ var index = atIndex || this.segments.length;
3267
+
3268
+ this.segments.splice(index, 0, new this.SegmentArc({
3269
+ fillColor: segment.color,
3270
+ highlightColor: segment.highlight || segment.color,
3271
+ label: segment.label,
3272
+ value: segment.value,
3273
+ outerRadius: (this.options.animateScale) ? 0 : this.scale.calculateCenterOffset(segment.value),
3274
+ circumference: (this.options.animateRotate) ? 0 : this.scale.getCircumference(),
3275
+ startAngle: Math.PI * 1.5
3276
+ }));
3277
+ if (!silent){
3278
+ this.reflow();
3279
+ this.update();
3280
+ }
3281
+ },
3282
+ removeData: function(atIndex){
3283
+ var indexToDelete = (helpers.isNumber(atIndex)) ? atIndex : this.segments.length-1;
3284
+ this.segments.splice(indexToDelete, 1);
3285
+ this.reflow();
3286
+ this.update();
3287
+ },
3288
+ calculateTotal: function(data){
3289
+ this.total = 0;
3290
+ helpers.each(data,function(segment){
3291
+ this.total += segment.value;
3292
+ },this);
3293
+ this.scale.valuesCount = this.segments.length;
3294
+ },
3295
+ updateScaleRange: function(datapoints){
3296
+ var valuesArray = [];
3297
+ helpers.each(datapoints,function(segment){
3298
+ valuesArray.push(segment.value);
3299
+ });
3300
+
3301
+ var scaleSizes = (this.options.scaleOverride) ?
3302
+ {
3303
+ steps: this.options.scaleSteps,
3304
+ stepValue: this.options.scaleStepWidth,
3305
+ min: this.options.scaleStartValue,
3306
+ max: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth)
3307
+ } :
3308
+ helpers.calculateScaleRange(
3309
+ valuesArray,
3310
+ helpers.min([this.chart.width, this.chart.height])/2,
3311
+ this.options.scaleFontSize,
3312
+ this.options.scaleBeginAtZero,
3313
+ this.options.scaleIntegersOnly
3314
+ );
3315
+
3316
+ helpers.extend(
3317
+ this.scale,
3318
+ scaleSizes,
3319
+ {
3320
+ size: helpers.min([this.chart.width, this.chart.height]),
3321
+ xCenter: this.chart.width/2,
3322
+ yCenter: this.chart.height/2
3323
+ }
3324
+ );
3325
+
3326
+ },
3327
+ update : function(){
3328
+ this.calculateTotal(this.segments);
3329
+
3330
+ helpers.each(this.segments,function(segment){
3331
+ segment.save();
3332
+ });
3333
+
3334
+ this.reflow();
3335
+ this.render();
3336
+ },
3337
+ reflow : function(){
3338
+ helpers.extend(this.SegmentArc.prototype,{
3339
+ x : this.chart.width/2,
3340
+ y : this.chart.height/2
3341
+ });
3342
+ this.updateScaleRange(this.segments);
3343
+ this.scale.update();
3344
+
3345
+ helpers.extend(this.scale,{
3346
+ xCenter: this.chart.width/2,
3347
+ yCenter: this.chart.height/2
3348
+ });
3349
+
3350
+ helpers.each(this.segments, function(segment){
3351
+ segment.update({
3352
+ outerRadius : this.scale.calculateCenterOffset(segment.value)
3353
+ });
3354
+ }, this);
3355
+
3356
+ },
3357
+ draw : function(ease){
3358
+ var easingDecimal = ease || 1;
3359
+ //Clear & draw the canvas
3360
+ this.clear();
3361
+ helpers.each(this.segments,function(segment, index){
3362
+ segment.transition({
3363
+ circumference : this.scale.getCircumference(),
3364
+ outerRadius : this.scale.calculateCenterOffset(segment.value)
3365
+ },easingDecimal);
3366
+
3367
+ segment.endAngle = segment.startAngle + segment.circumference;
3368
+
3369
+ // If we've removed the first segment we need to set the first one to
3370
+ // start at the top.
3371
+ if (index === 0){
3372
+ segment.startAngle = Math.PI * 1.5;
3373
+ }
3374
+
3375
+ //Check to see if it's the last segment, if not get the next and update the start angle
3376
+ if (index < this.segments.length - 1){
3377
+ this.segments[index+1].startAngle = segment.endAngle;
3378
+ }
3379
+ segment.draw();
3380
+ }, this);
3381
+ this.scale.draw();
3382
+ }
3383
+ });
3384
+
3385
+ }).call(this);
3386
+
3387
+ (function(){
3388
+ "use strict";
3389
+
3390
+ var root = this,
3391
+ Chart = root.Chart,
3392
+ helpers = Chart.helpers;
3393
+
3394
+
3395
+
3396
+ Chart.Type.extend({
3397
+ name: "Radar",
3398
+ defaults:{
3399
+ //Boolean - Whether to show lines for each scale point
3400
+ scaleShowLine : true,
3401
+
3402
+ //Boolean - Whether we show the angle lines out of the radar
3403
+ angleShowLineOut : true,
3404
+
3405
+ //Boolean - Whether to show labels on the scale
3406
+ scaleShowLabels : false,
3407
+
3408
+ // Boolean - Whether the scale should begin at zero
3409
+ scaleBeginAtZero : true,
3410
+
3411
+ //String - Colour of the angle line
3412
+ angleLineColor : "rgba(0,0,0,.1)",
3413
+
3414
+ //Number - Pixel width of the angle line
3415
+ angleLineWidth : 1,
3416
+
3417
+ //Number - Interval at which to draw angle lines ("every Nth point")
3418
+ angleLineInterval: 1,
3419
+
3420
+ //String - Point label font declaration
3421
+ pointLabelFontFamily : "'Arial'",
3422
+
3423
+ //String - Point label font weight
3424
+ pointLabelFontStyle : "normal",
3425
+
3426
+ //Number - Point label font size in pixels
3427
+ pointLabelFontSize : 10,
3428
+
3429
+ //String - Point label font colour
3430
+ pointLabelFontColor : "#666",
3431
+
3432
+ //Boolean - Whether to show a dot for each point
3433
+ pointDot : true,
3434
+
3435
+ //Number - Radius of each point dot in pixels
3436
+ pointDotRadius : 3,
3437
+
3438
+ //Number - Pixel width of point dot stroke
3439
+ pointDotStrokeWidth : 1,
3440
+
3441
+ //Number - amount extra to add to the radius to cater for hit detection outside the drawn point
3442
+ pointHitDetectionRadius : 20,
3443
+
3444
+ //Boolean - Whether to show a stroke for datasets
3445
+ datasetStroke : true,
3446
+
3447
+ //Number - Pixel width of dataset stroke
3448
+ datasetStrokeWidth : 2,
3449
+
3450
+ //Boolean - Whether to fill the dataset with a colour
3451
+ datasetFill : true,
3452
+
3453
+ //String - A legend template
3454
+ legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span class=\"<%=name.toLowerCase()%>-legend-icon\" style=\"background-color:<%=datasets[i].strokeColor%>\"></span><span class=\"<%=name.toLowerCase()%>-legend-text\"><%if(datasets[i].label){%><%=datasets[i].label%><%}%></span></li><%}%></ul>"
3455
+
3456
+ },
3457
+
3458
+ initialize: function(data){
3459
+ this.PointClass = Chart.Point.extend({
3460
+ strokeWidth : this.options.pointDotStrokeWidth,
3461
+ radius : this.options.pointDotRadius,
3462
+ display: this.options.pointDot,
3463
+ hitDetectionRadius : this.options.pointHitDetectionRadius,
3464
+ ctx : this.chart.ctx
3465
+ });
3466
+
3467
+ this.datasets = [];
3468
+
3469
+ this.buildScale(data);
3470
+
3471
+ //Set up tooltip events on the chart
3472
+ if (this.options.showTooltips){
3473
+ helpers.bindEvents(this, this.options.tooltipEvents, function(evt){
3474
+ var activePointsCollection = (evt.type !== 'mouseout') ? this.getPointsAtEvent(evt) : [];
3475
+
3476
+ this.eachPoints(function(point){
3477
+ point.restore(['fillColor', 'strokeColor']);
3478
+ });
3479
+ helpers.each(activePointsCollection, function(activePoint){
3480
+ activePoint.fillColor = activePoint.highlightFill;
3481
+ activePoint.strokeColor = activePoint.highlightStroke;
3482
+ });
3483
+
3484
+ this.showTooltip(activePointsCollection);
3485
+ });
3486
+ }
3487
+
3488
+ //Iterate through each of the datasets, and build this into a property of the chart
3489
+ helpers.each(data.datasets,function(dataset){
3490
+
3491
+ var datasetObject = {
3492
+ label: dataset.label || null,
3493
+ fillColor : dataset.fillColor,
3494
+ strokeColor : dataset.strokeColor,
3495
+ pointColor : dataset.pointColor,
3496
+ pointStrokeColor : dataset.pointStrokeColor,
3497
+ points : []
3498
+ };
3499
+
3500
+ this.datasets.push(datasetObject);
3501
+
3502
+ helpers.each(dataset.data,function(dataPoint,index){
3503
+ //Add a new point for each piece of data, passing any required data to draw.
3504
+ var pointPosition;
3505
+ if (!this.scale.animation){
3506
+ pointPosition = this.scale.getPointPosition(index, this.scale.calculateCenterOffset(dataPoint));
3507
+ }
3508
+ datasetObject.points.push(new this.PointClass({
3509
+ value : dataPoint,
3510
+ label : data.labels[index],
3511
+ datasetLabel: dataset.label,
3512
+ x: (this.options.animation) ? this.scale.xCenter : pointPosition.x,
3513
+ y: (this.options.animation) ? this.scale.yCenter : pointPosition.y,
3514
+ strokeColor : dataset.pointStrokeColor,
3515
+ fillColor : dataset.pointColor,
3516
+ highlightFill : dataset.pointHighlightFill || dataset.pointColor,
3517
+ highlightStroke : dataset.pointHighlightStroke || dataset.pointStrokeColor
3518
+ }));
3519
+ },this);
3520
+
3521
+ },this);
3522
+
3523
+ this.render();
3524
+ },
3525
+ eachPoints : function(callback){
3526
+ helpers.each(this.datasets,function(dataset){
3527
+ helpers.each(dataset.points,callback,this);
3528
+ },this);
3529
+ },
3530
+
3531
+ getPointsAtEvent : function(evt){
3532
+ var mousePosition = helpers.getRelativePosition(evt),
3533
+ fromCenter = helpers.getAngleFromPoint({
3534
+ x: this.scale.xCenter,
3535
+ y: this.scale.yCenter
3536
+ }, mousePosition);
3537
+
3538
+ var anglePerIndex = (Math.PI * 2) /this.scale.valuesCount,
3539
+ pointIndex = Math.round((fromCenter.angle - Math.PI * 1.5) / anglePerIndex),
3540
+ activePointsCollection = [];
3541
+
3542
+ // If we're at the top, make the pointIndex 0 to get the first of the array.
3543
+ if (pointIndex >= this.scale.valuesCount || pointIndex < 0){
3544
+ pointIndex = 0;
3545
+ }
3546
+
3547
+ if (fromCenter.distance <= this.scale.drawingArea){
3548
+ helpers.each(this.datasets, function(dataset){
3549
+ activePointsCollection.push(dataset.points[pointIndex]);
3550
+ });
3551
+ }
3552
+
3553
+ return activePointsCollection;
3554
+ },
3555
+
3556
+ buildScale : function(data){
3557
+ this.scale = new Chart.RadialScale({
3558
+ display: this.options.showScale,
3559
+ fontStyle: this.options.scaleFontStyle,
3560
+ fontSize: this.options.scaleFontSize,
3561
+ fontFamily: this.options.scaleFontFamily,
3562
+ fontColor: this.options.scaleFontColor,
3563
+ showLabels: this.options.scaleShowLabels,
3564
+ showLabelBackdrop: this.options.scaleShowLabelBackdrop,
3565
+ backdropColor: this.options.scaleBackdropColor,
3566
+ backgroundColors: this.options.scaleBackgroundColors,
3567
+ backdropPaddingY : this.options.scaleBackdropPaddingY,
3568
+ backdropPaddingX: this.options.scaleBackdropPaddingX,
3569
+ lineWidth: (this.options.scaleShowLine) ? this.options.scaleLineWidth : 0,
3570
+ lineColor: this.options.scaleLineColor,
3571
+ angleLineColor : this.options.angleLineColor,
3572
+ angleLineWidth : (this.options.angleShowLineOut) ? this.options.angleLineWidth : 0,
3573
+ angleLineInterval: (this.options.angleLineInterval) ? this.options.angleLineInterval : 1,
3574
+ // Point labels at the edge of each line
3575
+ pointLabelFontColor : this.options.pointLabelFontColor,
3576
+ pointLabelFontSize : this.options.pointLabelFontSize,
3577
+ pointLabelFontFamily : this.options.pointLabelFontFamily,
3578
+ pointLabelFontStyle : this.options.pointLabelFontStyle,
3579
+ height : this.chart.height,
3580
+ width: this.chart.width,
3581
+ xCenter: this.chart.width/2,
3582
+ yCenter: this.chart.height/2,
3583
+ ctx : this.chart.ctx,
3584
+ templateString: this.options.scaleLabel,
3585
+ labels: data.labels,
3586
+ valuesCount: data.datasets[0].data.length
3587
+ });
3588
+
3589
+ this.scale.setScaleSize();
3590
+ this.updateScaleRange(data.datasets);
3591
+ this.scale.buildYLabels();
3592
+ },
3593
+ updateScaleRange: function(datasets){
3594
+ var valuesArray = (function(){
3595
+ var totalDataArray = [];
3596
+ helpers.each(datasets,function(dataset){
3597
+ if (dataset.data){
3598
+ totalDataArray = totalDataArray.concat(dataset.data);
3599
+ }
3600
+ else {
3601
+ helpers.each(dataset.points, function(point){
3602
+ totalDataArray.push(point.value);
3603
+ });
3604
+ }
3605
+ });
3606
+ return totalDataArray;
3607
+ })();
3608
+
3609
+
3610
+ var scaleSizes = (this.options.scaleOverride) ?
3611
+ {
3612
+ steps: this.options.scaleSteps,
3613
+ stepValue: this.options.scaleStepWidth,
3614
+ min: this.options.scaleStartValue,
3615
+ max: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth)
3616
+ } :
3617
+ helpers.calculateScaleRange(
3618
+ valuesArray,
3619
+ helpers.min([this.chart.width, this.chart.height])/2,
3620
+ this.options.scaleFontSize,
3621
+ this.options.scaleBeginAtZero,
3622
+ this.options.scaleIntegersOnly
3623
+ );
3624
+
3625
+ helpers.extend(
3626
+ this.scale,
3627
+ scaleSizes
3628
+ );
3629
+
3630
+ },
3631
+ addData : function(valuesArray,label){
3632
+ //Map the values array for each of the datasets
3633
+ this.scale.valuesCount++;
3634
+ helpers.each(valuesArray,function(value,datasetIndex){
3635
+ var pointPosition = this.scale.getPointPosition(this.scale.valuesCount, this.scale.calculateCenterOffset(value));
3636
+ this.datasets[datasetIndex].points.push(new this.PointClass({
3637
+ value : value,
3638
+ label : label,
3639
+ datasetLabel: this.datasets[datasetIndex].label,
3640
+ x: pointPosition.x,
3641
+ y: pointPosition.y,
3642
+ strokeColor : this.datasets[datasetIndex].pointStrokeColor,
3643
+ fillColor : this.datasets[datasetIndex].pointColor
3644
+ }));
3645
+ },this);
3646
+
3647
+ this.scale.labels.push(label);
3648
+
3649
+ this.reflow();
3650
+
3651
+ this.update();
3652
+ },
3653
+ removeData : function(){
3654
+ this.scale.valuesCount--;
3655
+ this.scale.labels.shift();
3656
+ helpers.each(this.datasets,function(dataset){
3657
+ dataset.points.shift();
3658
+ },this);
3659
+ this.reflow();
3660
+ this.update();
3661
+ },
3662
+ update : function(){
3663
+ this.eachPoints(function(point){
3664
+ point.save();
3665
+ });
3666
+ this.reflow();
3667
+ this.render();
3668
+ },
3669
+ reflow: function(){
3670
+ helpers.extend(this.scale, {
3671
+ width : this.chart.width,
3672
+ height: this.chart.height,
3673
+ size : helpers.min([this.chart.width, this.chart.height]),
3674
+ xCenter: this.chart.width/2,
3675
+ yCenter: this.chart.height/2
3676
+ });
3677
+ this.updateScaleRange(this.datasets);
3678
+ this.scale.setScaleSize();
3679
+ this.scale.buildYLabels();
3680
+ },
3681
+ draw : function(ease){
3682
+ var easeDecimal = ease || 1,
3683
+ ctx = this.chart.ctx;
3684
+ this.clear();
3685
+ this.scale.draw();
3686
+
3687
+ helpers.each(this.datasets,function(dataset){
3688
+
3689
+ //Transition each point first so that the line and point drawing isn't out of sync
3690
+ helpers.each(dataset.points,function(point,index){
3691
+ if (point.hasValue()){
3692
+ point.transition(this.scale.getPointPosition(index, this.scale.calculateCenterOffset(point.value)), easeDecimal);
3693
+ }
3694
+ },this);
3695
+
3696
+
3697
+
3698
+ //Draw the line between all the points
3699
+ ctx.lineWidth = this.options.datasetStrokeWidth;
3700
+ ctx.strokeStyle = dataset.strokeColor;
3701
+ ctx.beginPath();
3702
+ helpers.each(dataset.points,function(point,index){
3703
+ if (index === 0){
3704
+ ctx.moveTo(point.x,point.y);
3705
+ }
3706
+ else{
3707
+ ctx.lineTo(point.x,point.y);
3708
+ }
3709
+ },this);
3710
+ ctx.closePath();
3711
+ ctx.stroke();
3712
+
3713
+ ctx.fillStyle = dataset.fillColor;
3714
+ if(this.options.datasetFill){
3715
+ ctx.fill();
3716
+ }
3717
+ //Now draw the points over the line
3718
+ //A little inefficient double looping, but better than the line
3719
+ //lagging behind the point positions
3720
+ helpers.each(dataset.points,function(point){
3721
+ if (point.hasValue()){
3722
+ point.draw();
3723
+ }
3724
+ });
3725
+
3726
+ },this);
3727
+
3728
+ }
3729
+
3730
+ });
3731
+
3732
+
3733
+
3734
+
3735
+
3736
+ }).call(this);