rails_adminlte2 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (407) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +4 -0
  5. data/CODE_OF_CONDUCT.md +49 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +56 -0
  9. data/Rakefile +6 -0
  10. data/bin/console +14 -0
  11. data/bin/setup +8 -0
  12. data/lib/rails_adminlte2/engine.rb +6 -0
  13. data/lib/rails_adminlte2/version.rb +3 -0
  14. data/lib/rails_adminlte2.rb +7 -0
  15. data/rails_adminlte2.gemspec +24 -0
  16. data/vendor/assets/fonts/FontAwesome.otf +0 -0
  17. data/vendor/assets/fonts/fontawesome-webfont.eot +0 -0
  18. data/vendor/assets/fonts/fontawesome-webfont.svg +414 -0
  19. data/vendor/assets/fonts/fontawesome-webfont.ttf +0 -0
  20. data/vendor/assets/fonts/fontawesome-webfont.woff +0 -0
  21. data/vendor/assets/fonts/glyphicons-halflings-regular.eot +0 -0
  22. data/vendor/assets/fonts/glyphicons-halflings-regular.svg +229 -0
  23. data/vendor/assets/fonts/glyphicons-halflings-regular.ttf +0 -0
  24. data/vendor/assets/fonts/glyphicons-halflings-regular.woff +0 -0
  25. data/vendor/assets/fonts/ionicons.eot +0 -0
  26. data/vendor/assets/fonts/ionicons.svg +1623 -0
  27. data/vendor/assets/fonts/ionicons.ttf +0 -0
  28. data/vendor/assets/fonts/ionicons.woff +0 -0
  29. data/vendor/assets/images/avatar.png +0 -0
  30. data/vendor/assets/images/avatar04.png +0 -0
  31. data/vendor/assets/images/avatar2.png +0 -0
  32. data/vendor/assets/images/avatar3.png +0 -0
  33. data/vendor/assets/images/avatar5.png +0 -0
  34. data/vendor/assets/images/boxed-bg.jpg +0 -0
  35. data/vendor/assets/images/boxed-bg.png +0 -0
  36. data/vendor/assets/images/credit/american-express.png +0 -0
  37. data/vendor/assets/images/credit/cirrus.png +0 -0
  38. data/vendor/assets/images/credit/mastercard.png +0 -0
  39. data/vendor/assets/images/credit/mestro.png +0 -0
  40. data/vendor/assets/images/credit/paypal.png +0 -0
  41. data/vendor/assets/images/credit/paypal2.png +0 -0
  42. data/vendor/assets/images/credit/visa.png +0 -0
  43. data/vendor/assets/images/default-50x50.gif +0 -0
  44. data/vendor/assets/images/icons.png +0 -0
  45. data/vendor/assets/images/photo1.png +0 -0
  46. data/vendor/assets/images/photo2.png +0 -0
  47. data/vendor/assets/images/photo3.jpg +0 -0
  48. data/vendor/assets/images/photo4.jpg +0 -0
  49. data/vendor/assets/images/user1-128x128.jpg +0 -0
  50. data/vendor/assets/images/user2-160x160.jpg +0 -0
  51. data/vendor/assets/images/user3-128x128.jpg +0 -0
  52. data/vendor/assets/images/user4-128x128.jpg +0 -0
  53. data/vendor/assets/images/user5-128x128.jpg +0 -0
  54. data/vendor/assets/images/user6-128x128.jpg +0 -0
  55. data/vendor/assets/images/user7-128x128.jpg +0 -0
  56. data/vendor/assets/images/user8-128x128.jpg +0 -0
  57. data/vendor/assets/javascripts/adminlte/adminlte.js +763 -0
  58. data/vendor/assets/javascripts/adminlte/adminlte.min.js +13 -0
  59. data/vendor/assets/javascripts/adminlte/plugins/bootstrap-slider/bootstrap-slider.js +1167 -0
  60. data/vendor/assets/javascripts/adminlte/plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.all.js +14975 -0
  61. data/vendor/assets/javascripts/adminlte/plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.all.min.js +8 -0
  62. data/vendor/assets/javascripts/adminlte/plugins/chartjs/Chart.js +3477 -0
  63. data/vendor/assets/javascripts/adminlte/plugins/chartjs/Chart.min.js +11 -0
  64. data/vendor/assets/javascripts/adminlte/plugins/colorpicker/bootstrap-colorpicker.js +949 -0
  65. data/vendor/assets/javascripts/adminlte/plugins/colorpicker/bootstrap-colorpicker.min.js +1 -0
  66. data/vendor/assets/javascripts/adminlte/plugins/datepicker/bootstrap-datepicker.js +1671 -0
  67. data/vendor/assets/javascripts/adminlte/plugins/datepicker/locales/bootstrap-datepicker.ar.js +15 -0
  68. data/vendor/assets/javascripts/adminlte/plugins/datepicker/locales/bootstrap-datepicker.az.js +12 -0
  69. data/vendor/assets/javascripts/adminlte/plugins/datepicker/locales/bootstrap-datepicker.bg.js +14 -0
  70. data/vendor/assets/javascripts/adminlte/plugins/datepicker/locales/bootstrap-datepicker.ca.js +14 -0
  71. data/vendor/assets/javascripts/adminlte/plugins/datepicker/locales/bootstrap-datepicker.cs.js +15 -0
  72. data/vendor/assets/javascripts/adminlte/plugins/datepicker/locales/bootstrap-datepicker.cy.js +14 -0
  73. data/vendor/assets/javascripts/adminlte/plugins/datepicker/locales/bootstrap-datepicker.da.js +15 -0
  74. data/vendor/assets/javascripts/adminlte/plugins/datepicker/locales/bootstrap-datepicker.de.js +17 -0
  75. data/vendor/assets/javascripts/adminlte/plugins/datepicker/locales/bootstrap-datepicker.el.js +13 -0
  76. data/vendor/assets/javascripts/adminlte/plugins/datepicker/locales/bootstrap-datepicker.es.js +14 -0
  77. data/vendor/assets/javascripts/adminlte/plugins/datepicker/locales/bootstrap-datepicker.et.js +18 -0
  78. data/vendor/assets/javascripts/adminlte/plugins/datepicker/locales/bootstrap-datepicker.fa.js +17 -0
  79. data/vendor/assets/javascripts/adminlte/plugins/datepicker/locales/bootstrap-datepicker.fi.js +16 -0
  80. data/vendor/assets/javascripts/adminlte/plugins/datepicker/locales/bootstrap-datepicker.fr.js +17 -0
  81. data/vendor/assets/javascripts/adminlte/plugins/datepicker/locales/bootstrap-datepicker.gl.js +11 -0
  82. data/vendor/assets/javascripts/adminlte/plugins/datepicker/locales/bootstrap-datepicker.he.js +15 -0
  83. data/vendor/assets/javascripts/adminlte/plugins/datepicker/locales/bootstrap-datepicker.hr.js +13 -0
  84. data/vendor/assets/javascripts/adminlte/plugins/datepicker/locales/bootstrap-datepicker.hu.js +16 -0
  85. data/vendor/assets/javascripts/adminlte/plugins/datepicker/locales/bootstrap-datepicker.id.js +15 -0
  86. data/vendor/assets/javascripts/adminlte/plugins/datepicker/locales/bootstrap-datepicker.is.js +14 -0
  87. data/vendor/assets/javascripts/adminlte/plugins/datepicker/locales/bootstrap-datepicker.it.js +17 -0
  88. data/vendor/assets/javascripts/adminlte/plugins/datepicker/locales/bootstrap-datepicker.ja.js +15 -0
  89. data/vendor/assets/javascripts/adminlte/plugins/datepicker/locales/bootstrap-datepicker.ka.js +17 -0
  90. data/vendor/assets/javascripts/adminlte/plugins/datepicker/locales/bootstrap-datepicker.kk.js +15 -0
  91. data/vendor/assets/javascripts/adminlte/plugins/datepicker/locales/bootstrap-datepicker.kr.js +13 -0
  92. data/vendor/assets/javascripts/adminlte/plugins/datepicker/locales/bootstrap-datepicker.lt.js +16 -0
  93. data/vendor/assets/javascripts/adminlte/plugins/datepicker/locales/bootstrap-datepicker.lv.js +16 -0
  94. data/vendor/assets/javascripts/adminlte/plugins/datepicker/locales/bootstrap-datepicker.mk.js +15 -0
  95. data/vendor/assets/javascripts/adminlte/plugins/datepicker/locales/bootstrap-datepicker.ms.js +14 -0
  96. data/vendor/assets/javascripts/adminlte/plugins/datepicker/locales/bootstrap-datepicker.nb.js +14 -0
  97. data/vendor/assets/javascripts/adminlte/plugins/datepicker/locales/bootstrap-datepicker.nl-BE.js +17 -0
  98. data/vendor/assets/javascripts/adminlte/plugins/datepicker/locales/bootstrap-datepicker.nl.js +14 -0
  99. data/vendor/assets/javascripts/adminlte/plugins/datepicker/locales/bootstrap-datepicker.no.js +16 -0
  100. data/vendor/assets/javascripts/adminlte/plugins/datepicker/locales/bootstrap-datepicker.pl.js +15 -0
  101. data/vendor/assets/javascripts/adminlte/plugins/datepicker/locales/bootstrap-datepicker.pt-BR.js +15 -0
  102. data/vendor/assets/javascripts/adminlte/plugins/datepicker/locales/bootstrap-datepicker.pt.js +16 -0
  103. data/vendor/assets/javascripts/adminlte/plugins/datepicker/locales/bootstrap-datepicker.ro.js +16 -0
  104. data/vendor/assets/javascripts/adminlte/plugins/datepicker/locales/bootstrap-datepicker.rs-latin.js +14 -0
  105. data/vendor/assets/javascripts/adminlte/plugins/datepicker/locales/bootstrap-datepicker.rs.js +14 -0
  106. data/vendor/assets/javascripts/adminlte/plugins/datepicker/locales/bootstrap-datepicker.ru.js +15 -0
  107. data/vendor/assets/javascripts/adminlte/plugins/datepicker/locales/bootstrap-datepicker.sk.js +15 -0
  108. data/vendor/assets/javascripts/adminlte/plugins/datepicker/locales/bootstrap-datepicker.sl.js +14 -0
  109. data/vendor/assets/javascripts/adminlte/plugins/datepicker/locales/bootstrap-datepicker.sq.js +15 -0
  110. data/vendor/assets/javascripts/adminlte/plugins/datepicker/locales/bootstrap-datepicker.sv.js +16 -0
  111. data/vendor/assets/javascripts/adminlte/plugins/datepicker/locales/bootstrap-datepicker.sw.js +15 -0
  112. data/vendor/assets/javascripts/adminlte/plugins/datepicker/locales/bootstrap-datepicker.th.js +14 -0
  113. data/vendor/assets/javascripts/adminlte/plugins/datepicker/locales/bootstrap-datepicker.tr.js +16 -0
  114. data/vendor/assets/javascripts/adminlte/plugins/datepicker/locales/bootstrap-datepicker.ua.js +15 -0
  115. data/vendor/assets/javascripts/adminlte/plugins/datepicker/locales/bootstrap-datepicker.vi.js +16 -0
  116. data/vendor/assets/javascripts/adminlte/plugins/datepicker/locales/bootstrap-datepicker.zh-CN.js +16 -0
  117. data/vendor/assets/javascripts/adminlte/plugins/datepicker/locales/bootstrap-datepicker.zh-TW.js +17 -0
  118. data/vendor/assets/javascripts/adminlte/plugins/daterangepicker/daterangepicker.js +1304 -0
  119. data/vendor/assets/javascripts/adminlte/plugins/daterangepicker/moment.js +3043 -0
  120. data/vendor/assets/javascripts/adminlte/plugins/daterangepicker/moment.min.js +7 -0
  121. data/vendor/assets/javascripts/adminlte/plugins/fastclick/fastclick.js +841 -0
  122. data/vendor/assets/javascripts/adminlte/plugins/fastclick/fastclick.min.js +1 -0
  123. data/vendor/assets/javascripts/adminlte/plugins/flot/excanvas.js +1427 -0
  124. data/vendor/assets/javascripts/adminlte/plugins/flot/excanvas.min.js +1 -0
  125. data/vendor/assets/javascripts/adminlte/plugins/flot/jquery.colorhelpers.js +180 -0
  126. data/vendor/assets/javascripts/adminlte/plugins/flot/jquery.colorhelpers.min.js +1 -0
  127. data/vendor/assets/javascripts/adminlte/plugins/flot/jquery.flot.canvas.js +345 -0
  128. data/vendor/assets/javascripts/adminlte/plugins/flot/jquery.flot.canvas.min.js +1 -0
  129. data/vendor/assets/javascripts/adminlte/plugins/flot/jquery.flot.categories.js +190 -0
  130. data/vendor/assets/javascripts/adminlte/plugins/flot/jquery.flot.categories.min.js +1 -0
  131. data/vendor/assets/javascripts/adminlte/plugins/flot/jquery.flot.crosshair.js +176 -0
  132. data/vendor/assets/javascripts/adminlte/plugins/flot/jquery.flot.crosshair.min.js +1 -0
  133. data/vendor/assets/javascripts/adminlte/plugins/flot/jquery.flot.errorbars.js +353 -0
  134. data/vendor/assets/javascripts/adminlte/plugins/flot/jquery.flot.errorbars.min.js +1 -0
  135. data/vendor/assets/javascripts/adminlte/plugins/flot/jquery.flot.fillbetween.js +226 -0
  136. data/vendor/assets/javascripts/adminlte/plugins/flot/jquery.flot.fillbetween.min.js +1 -0
  137. data/vendor/assets/javascripts/adminlte/plugins/flot/jquery.flot.image.js +241 -0
  138. data/vendor/assets/javascripts/adminlte/plugins/flot/jquery.flot.image.min.js +1 -0
  139. data/vendor/assets/javascripts/adminlte/plugins/flot/jquery.flot.js +3137 -0
  140. data/vendor/assets/javascripts/adminlte/plugins/flot/jquery.flot.min.js +2 -0
  141. data/vendor/assets/javascripts/adminlte/plugins/flot/jquery.flot.navigate.js +346 -0
  142. data/vendor/assets/javascripts/adminlte/plugins/flot/jquery.flot.navigate.min.js +1 -0
  143. data/vendor/assets/javascripts/adminlte/plugins/flot/jquery.flot.pie.js +817 -0
  144. data/vendor/assets/javascripts/adminlte/plugins/flot/jquery.flot.pie.min.js +1 -0
  145. data/vendor/assets/javascripts/adminlte/plugins/flot/jquery.flot.resize.js +60 -0
  146. data/vendor/assets/javascripts/adminlte/plugins/flot/jquery.flot.resize.min.js +1 -0
  147. data/vendor/assets/javascripts/adminlte/plugins/flot/jquery.flot.selection.js +360 -0
  148. data/vendor/assets/javascripts/adminlte/plugins/flot/jquery.flot.selection.min.js +1 -0
  149. data/vendor/assets/javascripts/adminlte/plugins/flot/jquery.flot.stack.js +188 -0
  150. data/vendor/assets/javascripts/adminlte/plugins/flot/jquery.flot.stack.min.js +1 -0
  151. data/vendor/assets/javascripts/adminlte/plugins/flot/jquery.flot.symbol.js +71 -0
  152. data/vendor/assets/javascripts/adminlte/plugins/flot/jquery.flot.symbol.min.js +1 -0
  153. data/vendor/assets/javascripts/adminlte/plugins/flot/jquery.flot.threshold.js +142 -0
  154. data/vendor/assets/javascripts/adminlte/plugins/flot/jquery.flot.threshold.min.js +1 -0
  155. data/vendor/assets/javascripts/adminlte/plugins/flot/jquery.flot.time.js +429 -0
  156. data/vendor/assets/javascripts/adminlte/plugins/flot/jquery.flot.time.min.js +1 -0
  157. data/vendor/assets/javascripts/adminlte/plugins/fullcalendar/fullcalendar.js +9732 -0
  158. data/vendor/assets/javascripts/adminlte/plugins/fullcalendar/fullcalendar.min.js +8 -0
  159. data/vendor/assets/javascripts/adminlte/plugins/iCheck/icheck.js +478 -0
  160. data/vendor/assets/javascripts/adminlte/plugins/iCheck/icheck.min.js +10 -0
  161. data/vendor/assets/javascripts/adminlte/plugins/input-mask/jquery.inputmask.date.extensions.js +488 -0
  162. data/vendor/assets/javascripts/adminlte/plugins/input-mask/jquery.inputmask.extensions.js +122 -0
  163. data/vendor/assets/javascripts/adminlte/plugins/input-mask/jquery.inputmask.js +1627 -0
  164. data/vendor/assets/javascripts/adminlte/plugins/input-mask/jquery.inputmask.numeric.extensions.js +177 -0
  165. data/vendor/assets/javascripts/adminlte/plugins/input-mask/jquery.inputmask.phone.extensions.js +50 -0
  166. data/vendor/assets/javascripts/adminlte/plugins/input-mask/jquery.inputmask.regex.extensions.js +169 -0
  167. data/vendor/assets/javascripts/adminlte/plugins/input-mask/phone-codes/phone-be.json +45 -0
  168. data/vendor/assets/javascripts/adminlte/plugins/input-mask/phone-codes/phone-codes.json +294 -0
  169. data/vendor/assets/javascripts/adminlte/plugins/input-mask/phone-codes/readme.txt +1 -0
  170. data/vendor/assets/javascripts/adminlte/plugins/ionslider/ion.rangeSlider.min.js +22 -0
  171. data/vendor/assets/javascripts/adminlte/plugins/jQuery/jQuery-2.1.4.min.js +4 -0
  172. data/vendor/assets/javascripts/adminlte/plugins/jQueryUI/jquery-ui.js +16617 -0
  173. data/vendor/assets/javascripts/adminlte/plugins/jQueryUI/jquery-ui.min.js +13 -0
  174. data/vendor/assets/javascripts/adminlte/plugins/jvectormap/jquery-jvectormap-1.2.2.min.js +8 -0
  175. data/vendor/assets/javascripts/adminlte/plugins/jvectormap/jquery-jvectormap-usa-en.js +1 -0
  176. data/vendor/assets/javascripts/adminlte/plugins/jvectormap/jquery-jvectormap-world-mill-en.js +1 -0
  177. data/vendor/assets/javascripts/adminlte/plugins/knob/jquery.knob.js +805 -0
  178. data/vendor/assets/javascripts/adminlte/plugins/morris/morris.js +1892 -0
  179. data/vendor/assets/javascripts/adminlte/plugins/morris/morris.min.js +7 -0
  180. data/vendor/assets/javascripts/adminlte/plugins/pace/pace.js +935 -0
  181. data/vendor/assets/javascripts/adminlte/plugins/pace/pace.min.js +2 -0
  182. data/vendor/assets/javascripts/adminlte/plugins/select2/i18n/az.js +3 -0
  183. data/vendor/assets/javascripts/adminlte/plugins/select2/i18n/bg.js +3 -0
  184. data/vendor/assets/javascripts/adminlte/plugins/select2/i18n/ca.js +3 -0
  185. data/vendor/assets/javascripts/adminlte/plugins/select2/i18n/cs.js +3 -0
  186. data/vendor/assets/javascripts/adminlte/plugins/select2/i18n/da.js +3 -0
  187. data/vendor/assets/javascripts/adminlte/plugins/select2/i18n/de.js +3 -0
  188. data/vendor/assets/javascripts/adminlte/plugins/select2/i18n/en.js +3 -0
  189. data/vendor/assets/javascripts/adminlte/plugins/select2/i18n/es.js +3 -0
  190. data/vendor/assets/javascripts/adminlte/plugins/select2/i18n/et.js +3 -0
  191. data/vendor/assets/javascripts/adminlte/plugins/select2/i18n/eu.js +3 -0
  192. data/vendor/assets/javascripts/adminlte/plugins/select2/i18n/fa.js +3 -0
  193. data/vendor/assets/javascripts/adminlte/plugins/select2/i18n/fi.js +3 -0
  194. data/vendor/assets/javascripts/adminlte/plugins/select2/i18n/fr.js +3 -0
  195. data/vendor/assets/javascripts/adminlte/plugins/select2/i18n/gl.js +3 -0
  196. data/vendor/assets/javascripts/adminlte/plugins/select2/i18n/he.js +3 -0
  197. data/vendor/assets/javascripts/adminlte/plugins/select2/i18n/hi.js +3 -0
  198. data/vendor/assets/javascripts/adminlte/plugins/select2/i18n/hr.js +3 -0
  199. data/vendor/assets/javascripts/adminlte/plugins/select2/i18n/hu.js +3 -0
  200. data/vendor/assets/javascripts/adminlte/plugins/select2/i18n/id.js +3 -0
  201. data/vendor/assets/javascripts/adminlte/plugins/select2/i18n/is.js +3 -0
  202. data/vendor/assets/javascripts/adminlte/plugins/select2/i18n/it.js +3 -0
  203. data/vendor/assets/javascripts/adminlte/plugins/select2/i18n/ko.js +3 -0
  204. data/vendor/assets/javascripts/adminlte/plugins/select2/i18n/lt.js +3 -0
  205. data/vendor/assets/javascripts/adminlte/plugins/select2/i18n/lv.js +3 -0
  206. data/vendor/assets/javascripts/adminlte/plugins/select2/i18n/mk.js +3 -0
  207. data/vendor/assets/javascripts/adminlte/plugins/select2/i18n/nb.js +3 -0
  208. data/vendor/assets/javascripts/adminlte/plugins/select2/i18n/nl.js +3 -0
  209. data/vendor/assets/javascripts/adminlte/plugins/select2/i18n/pl.js +3 -0
  210. data/vendor/assets/javascripts/adminlte/plugins/select2/i18n/pt-BR.js +3 -0
  211. data/vendor/assets/javascripts/adminlte/plugins/select2/i18n/pt.js +3 -0
  212. data/vendor/assets/javascripts/adminlte/plugins/select2/i18n/ro.js +3 -0
  213. data/vendor/assets/javascripts/adminlte/plugins/select2/i18n/ru.js +3 -0
  214. data/vendor/assets/javascripts/adminlte/plugins/select2/i18n/sk.js +3 -0
  215. data/vendor/assets/javascripts/adminlte/plugins/select2/i18n/sr.js +3 -0
  216. data/vendor/assets/javascripts/adminlte/plugins/select2/i18n/sv.js +3 -0
  217. data/vendor/assets/javascripts/adminlte/plugins/select2/i18n/th.js +3 -0
  218. data/vendor/assets/javascripts/adminlte/plugins/select2/i18n/tr.js +3 -0
  219. data/vendor/assets/javascripts/adminlte/plugins/select2/i18n/uk.js +3 -0
  220. data/vendor/assets/javascripts/adminlte/plugins/select2/i18n/vi.js +3 -0
  221. data/vendor/assets/javascripts/adminlte/plugins/select2/i18n/zh-CN.js +3 -0
  222. data/vendor/assets/javascripts/adminlte/plugins/select2/i18n/zh-TW.js +3 -0
  223. data/vendor/assets/javascripts/adminlte/plugins/select2/select2.full.js +6114 -0
  224. data/vendor/assets/javascripts/adminlte/plugins/select2/select2.full.min.js +3 -0
  225. data/vendor/assets/javascripts/adminlte/plugins/select2/select2.js +5403 -0
  226. data/vendor/assets/javascripts/adminlte/plugins/select2/select2.min.js +2 -0
  227. data/vendor/assets/javascripts/adminlte/plugins/slimScroll/jquery.slimscroll.js +490 -0
  228. data/vendor/assets/javascripts/adminlte/plugins/slimScroll/jquery.slimscroll.min.js +1 -0
  229. data/vendor/assets/javascripts/adminlte/plugins/sparkline/jquery.sparkline.js +3054 -0
  230. data/vendor/assets/javascripts/adminlte/plugins/sparkline/jquery.sparkline.min.js +5 -0
  231. data/vendor/assets/javascripts/adminlte/plugins/timepicker/bootstrap-timepicker.js +903 -0
  232. data/vendor/assets/javascripts/adminlte/plugins/timepicker/bootstrap-timepicker.min.js +5 -0
  233. data/vendor/assets/javascripts/bootstrap.js +2317 -0
  234. data/vendor/assets/javascripts/bootstrap.min.js +7 -0
  235. data/vendor/assets/stylesheets/adminlte/adminlte.css +4928 -0
  236. data/vendor/assets/stylesheets/adminlte/adminlte.min.css +7 -0
  237. data/vendor/assets/stylesheets/adminlte/plugins/bootstrap-slider/slider.css +169 -0
  238. data/vendor/assets/stylesheets/adminlte/plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.css +117 -0
  239. data/vendor/assets/stylesheets/adminlte/plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.min.css +3 -0
  240. data/vendor/assets/stylesheets/adminlte/plugins/colorpicker/bootstrap-colorpicker.css +214 -0
  241. data/vendor/assets/stylesheets/adminlte/plugins/colorpicker/bootstrap-colorpicker.min.css +9 -0
  242. data/vendor/assets/stylesheets/adminlte/plugins/colorpicker/img/alpha-horizontal.png +0 -0
  243. data/vendor/assets/stylesheets/adminlte/plugins/colorpicker/img/alpha.png +0 -0
  244. data/vendor/assets/stylesheets/adminlte/plugins/colorpicker/img/hue-horizontal.png +0 -0
  245. data/vendor/assets/stylesheets/adminlte/plugins/colorpicker/img/hue.png +0 -0
  246. data/vendor/assets/stylesheets/adminlte/plugins/colorpicker/img/saturation.png +0 -0
  247. data/vendor/assets/stylesheets/adminlte/plugins/datepicker/datepicker3.css +790 -0
  248. data/vendor/assets/stylesheets/adminlte/plugins/daterangepicker/daterangepicker-bs3.css +335 -0
  249. data/vendor/assets/stylesheets/adminlte/plugins/fullcalendar/fullcalendar.css +977 -0
  250. data/vendor/assets/stylesheets/adminlte/plugins/fullcalendar/fullcalendar.min.css +5 -0
  251. data/vendor/assets/stylesheets/adminlte/plugins/fullcalendar/fullcalendar.print.css +202 -0
  252. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/all.css +61 -0
  253. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/flat/_all.css +560 -0
  254. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/flat/aero.css +56 -0
  255. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/flat/aero.png +0 -0
  256. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/flat/aero@2x.png +0 -0
  257. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/flat/blue.css +56 -0
  258. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/flat/blue.png +0 -0
  259. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/flat/blue@2x.png +0 -0
  260. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/flat/flat.css +56 -0
  261. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/flat/flat.png +0 -0
  262. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/flat/flat@2x.png +0 -0
  263. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/flat/green.css +56 -0
  264. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/flat/green.png +0 -0
  265. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/flat/green@2x.png +0 -0
  266. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/flat/grey.css +56 -0
  267. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/flat/grey.png +0 -0
  268. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/flat/grey@2x.png +0 -0
  269. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/flat/orange.css +56 -0
  270. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/flat/orange.png +0 -0
  271. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/flat/orange@2x.png +0 -0
  272. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/flat/pink.css +56 -0
  273. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/flat/pink.png +0 -0
  274. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/flat/pink@2x.png +0 -0
  275. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/flat/purple.css +56 -0
  276. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/flat/purple.png +0 -0
  277. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/flat/purple@2x.png +0 -0
  278. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/flat/red.css +56 -0
  279. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/flat/red.png +0 -0
  280. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/flat/red@2x.png +0 -0
  281. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/flat/yellow.css +56 -0
  282. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/flat/yellow.png +0 -0
  283. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/flat/yellow@2x.png +0 -0
  284. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/futurico/futurico.css +56 -0
  285. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/futurico/futurico.png +0 -0
  286. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/futurico/futurico@2x.png +0 -0
  287. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/line/_all.css +740 -0
  288. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/line/aero.css +74 -0
  289. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/line/blue.css +74 -0
  290. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/line/green.css +74 -0
  291. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/line/grey.css +74 -0
  292. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/line/line.css +74 -0
  293. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/line/line.png +0 -0
  294. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/line/line@2x.png +0 -0
  295. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/line/orange.css +74 -0
  296. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/line/pink.css +74 -0
  297. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/line/purple.css +74 -0
  298. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/line/red.css +74 -0
  299. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/line/yellow.css +74 -0
  300. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/minimal/_all.css +557 -0
  301. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/minimal/aero.css +62 -0
  302. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/minimal/aero.png +0 -0
  303. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/minimal/aero@2x.png +0 -0
  304. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/minimal/blue.css +62 -0
  305. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/minimal/blue.png +0 -0
  306. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/minimal/blue@2x.png +0 -0
  307. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/minimal/green.css +62 -0
  308. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/minimal/green.png +0 -0
  309. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/minimal/green@2x.png +0 -0
  310. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/minimal/grey.css +62 -0
  311. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/minimal/grey.png +0 -0
  312. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/minimal/grey@2x.png +0 -0
  313. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/minimal/minimal.css +62 -0
  314. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/minimal/minimal.png +0 -0
  315. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/minimal/minimal@2x.png +0 -0
  316. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/minimal/orange.css +62 -0
  317. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/minimal/orange.png +0 -0
  318. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/minimal/orange@2x.png +0 -0
  319. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/minimal/pink.css +62 -0
  320. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/minimal/pink.png +0 -0
  321. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/minimal/pink@2x.png +0 -0
  322. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/minimal/purple.css +62 -0
  323. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/minimal/purple.png +0 -0
  324. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/minimal/purple@2x.png +0 -0
  325. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/minimal/red.css +62 -0
  326. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/minimal/red.png +0 -0
  327. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/minimal/red@2x.png +0 -0
  328. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/minimal/yellow.css +62 -0
  329. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/minimal/yellow.png +0 -0
  330. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/minimal/yellow@2x.png +0 -0
  331. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/polaris/polaris.css +62 -0
  332. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/polaris/polaris.png +0 -0
  333. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/polaris/polaris@2x.png +0 -0
  334. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/square/_all.css +620 -0
  335. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/square/aero.css +62 -0
  336. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/square/aero.png +0 -0
  337. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/square/aero@2x.png +0 -0
  338. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/square/blue.css +62 -0
  339. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/square/blue.png +0 -0
  340. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/square/blue@2x.png +0 -0
  341. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/square/green.css +62 -0
  342. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/square/green.png +0 -0
  343. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/square/green@2x.png +0 -0
  344. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/square/grey.css +62 -0
  345. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/square/grey.png +0 -0
  346. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/square/grey@2x.png +0 -0
  347. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/square/orange.css +62 -0
  348. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/square/orange.png +0 -0
  349. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/square/orange@2x.png +0 -0
  350. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/square/pink.css +62 -0
  351. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/square/pink.png +0 -0
  352. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/square/pink@2x.png +0 -0
  353. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/square/purple.css +62 -0
  354. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/square/purple.png +0 -0
  355. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/square/purple@2x.png +0 -0
  356. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/square/red.css +62 -0
  357. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/square/red.png +0 -0
  358. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/square/red@2x.png +0 -0
  359. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/square/square.css +62 -0
  360. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/square/square.png +0 -0
  361. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/square/square@2x.png +0 -0
  362. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/square/yellow.css +62 -0
  363. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/square/yellow.png +0 -0
  364. data/vendor/assets/stylesheets/adminlte/plugins/iCheck/square/yellow@2x.png +0 -0
  365. data/vendor/assets/stylesheets/adminlte/plugins/ionslider/img/sprite-skin-flat.png +0 -0
  366. data/vendor/assets/stylesheets/adminlte/plugins/ionslider/img/sprite-skin-nice.png +0 -0
  367. data/vendor/assets/stylesheets/adminlte/plugins/ionslider/ion.rangeSlider.css +126 -0
  368. data/vendor/assets/stylesheets/adminlte/plugins/ionslider/ion.rangeSlider.skinFlat.css +89 -0
  369. data/vendor/assets/stylesheets/adminlte/plugins/ionslider/ion.rangeSlider.skinNice.css +85 -0
  370. data/vendor/assets/stylesheets/adminlte/plugins/jvectormap/jquery-jvectormap-1.2.2.css +40 -0
  371. data/vendor/assets/stylesheets/adminlte/plugins/morris/morris.css +2 -0
  372. data/vendor/assets/stylesheets/adminlte/plugins/pace/pace.css +85 -0
  373. data/vendor/assets/stylesheets/adminlte/plugins/pace/pace.min.css +1 -0
  374. data/vendor/assets/stylesheets/adminlte/plugins/select2/select2.css +431 -0
  375. data/vendor/assets/stylesheets/adminlte/plugins/select2/select2.min.css +1 -0
  376. data/vendor/assets/stylesheets/adminlte/plugins/timepicker/bootstrap-timepicker.css +121 -0
  377. data/vendor/assets/stylesheets/adminlte/plugins/timepicker/bootstrap-timepicker.min.css +10 -0
  378. data/vendor/assets/stylesheets/adminlte/skins/_all-skins.css +1799 -0
  379. data/vendor/assets/stylesheets/adminlte/skins/_all-skins.min.css +1 -0
  380. data/vendor/assets/stylesheets/adminlte/skins/skin-black-light.css +176 -0
  381. data/vendor/assets/stylesheets/adminlte/skins/skin-black-light.min.css +1 -0
  382. data/vendor/assets/stylesheets/adminlte/skins/skin-black.css +154 -0
  383. data/vendor/assets/stylesheets/adminlte/skins/skin-black.min.css +1 -0
  384. data/vendor/assets/stylesheets/adminlte/skins/skin-blue-light.css +167 -0
  385. data/vendor/assets/stylesheets/adminlte/skins/skin-blue-light.min.css +1 -0
  386. data/vendor/assets/stylesheets/adminlte/skins/skin-blue.css +142 -0
  387. data/vendor/assets/stylesheets/adminlte/skins/skin-blue.min.css +1 -0
  388. data/vendor/assets/stylesheets/adminlte/skins/skin-green-light.css +156 -0
  389. data/vendor/assets/stylesheets/adminlte/skins/skin-green-light.min.css +1 -0
  390. data/vendor/assets/stylesheets/adminlte/skins/skin-green.css +134 -0
  391. data/vendor/assets/stylesheets/adminlte/skins/skin-green.min.css +1 -0
  392. data/vendor/assets/stylesheets/adminlte/skins/skin-purple-light.css +156 -0
  393. data/vendor/assets/stylesheets/adminlte/skins/skin-purple-light.min.css +1 -0
  394. data/vendor/assets/stylesheets/adminlte/skins/skin-purple.css +134 -0
  395. data/vendor/assets/stylesheets/adminlte/skins/skin-purple.min.css +1 -0
  396. data/vendor/assets/stylesheets/adminlte/skins/skin-red-light.css +156 -0
  397. data/vendor/assets/stylesheets/adminlte/skins/skin-red-light.min.css +1 -0
  398. data/vendor/assets/stylesheets/adminlte/skins/skin-red.css +134 -0
  399. data/vendor/assets/stylesheets/adminlte/skins/skin-red.min.css +1 -0
  400. data/vendor/assets/stylesheets/adminlte/skins/skin-yellow-light.css +156 -0
  401. data/vendor/assets/stylesheets/adminlte/skins/skin-yellow-light.min.css +1 -0
  402. data/vendor/assets/stylesheets/adminlte/skins/skin-yellow.css +134 -0
  403. data/vendor/assets/stylesheets/adminlte/skins/skin-yellow.min.css +1 -0
  404. data/vendor/assets/stylesheets/bootstrap.css +6800 -0
  405. data/vendor/assets/stylesheets/bootstrap.css.map +1 -0
  406. data/vendor/assets/stylesheets/bootstrap.min.css +5 -0
  407. metadata +491 -0
@@ -0,0 +1,3137 @@
1
+ /* Javascript plotting library for jQuery, version 0.8.2.
2
+
3
+ Copyright (c) 2007-2013 IOLA and Ole Laursen.
4
+ Licensed under the MIT license.
5
+
6
+ */
7
+
8
+ // first an inline dependency, jquery.colorhelpers.js, we inline it here
9
+ // for convenience
10
+
11
+ /* Plugin for jQuery for working with colors.
12
+ *
13
+ * Version 1.1.
14
+ *
15
+ * Inspiration from jQuery color animation plugin by John Resig.
16
+ *
17
+ * Released under the MIT license by Ole Laursen, October 2009.
18
+ *
19
+ * Examples:
20
+ *
21
+ * $.color.parse("#fff").scale('rgb', 0.25).add('a', -0.5).toString()
22
+ * var c = $.color.extract($("#mydiv"), 'background-color');
23
+ * console.log(c.r, c.g, c.b, c.a);
24
+ * $.color.make(100, 50, 25, 0.4).toString() // returns "rgba(100,50,25,0.4)"
25
+ *
26
+ * Note that .scale() and .add() return the same modified object
27
+ * instead of making a new one.
28
+ *
29
+ * V. 1.1: Fix error handling so e.g. parsing an empty string does
30
+ * produce a color rather than just crashing.
31
+ */
32
+ (function($){$.color={};$.color.make=function(r,g,b,a){var o={};o.r=r||0;o.g=g||0;o.b=b||0;o.a=a!=null?a:1;o.add=function(c,d){for(var i=0;i<c.length;++i)o[c.charAt(i)]+=d;return o.normalize()};o.scale=function(c,f){for(var i=0;i<c.length;++i)o[c.charAt(i)]*=f;return o.normalize()};o.toString=function(){if(o.a>=1){return"rgb("+[o.r,o.g,o.b].join(",")+")"}else{return"rgba("+[o.r,o.g,o.b,o.a].join(",")+")"}};o.normalize=function(){function clamp(min,value,max){return value<min?min:value>max?max:value}o.r=clamp(0,parseInt(o.r),255);o.g=clamp(0,parseInt(o.g),255);o.b=clamp(0,parseInt(o.b),255);o.a=clamp(0,o.a,1);return o};o.clone=function(){return $.color.make(o.r,o.b,o.g,o.a)};return o.normalize()};$.color.extract=function(elem,css){var c;do{c=elem.css(css).toLowerCase();if(c!=""&&c!="transparent")break;elem=elem.parent()}while(elem.length&&!$.nodeName(elem.get(0),"body"));if(c=="rgba(0, 0, 0, 0)")c="transparent";return $.color.parse(c)};$.color.parse=function(str){var res,m=$.color.make;if(res=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(str))return m(parseInt(res[1],10),parseInt(res[2],10),parseInt(res[3],10));if(res=/rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str))return m(parseInt(res[1],10),parseInt(res[2],10),parseInt(res[3],10),parseFloat(res[4]));if(res=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(str))return m(parseFloat(res[1])*2.55,parseFloat(res[2])*2.55,parseFloat(res[3])*2.55);if(res=/rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str))return m(parseFloat(res[1])*2.55,parseFloat(res[2])*2.55,parseFloat(res[3])*2.55,parseFloat(res[4]));if(res=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(str))return m(parseInt(res[1],16),parseInt(res[2],16),parseInt(res[3],16));if(res=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(str))return m(parseInt(res[1]+res[1],16),parseInt(res[2]+res[2],16),parseInt(res[3]+res[3],16));var name=$.trim(str).toLowerCase();if(name=="transparent")return m(255,255,255,0);else{res=lookupColors[name]||[0,0,0];return m(res[0],res[1],res[2])}};var lookupColors={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0]}})(jQuery);
33
+
34
+ // the actual Flot code
35
+ (function($) {
36
+
37
+ // Cache the prototype hasOwnProperty for faster access
38
+
39
+ var hasOwnProperty = Object.prototype.hasOwnProperty;
40
+
41
+ ///////////////////////////////////////////////////////////////////////////
42
+ // The Canvas object is a wrapper around an HTML5 <canvas> tag.
43
+ //
44
+ // @constructor
45
+ // @param {string} cls List of classes to apply to the canvas.
46
+ // @param {element} container Element onto which to append the canvas.
47
+ //
48
+ // Requiring a container is a little iffy, but unfortunately canvas
49
+ // operations don't work unless the canvas is attached to the DOM.
50
+
51
+ function Canvas(cls, container) {
52
+
53
+ var element = container.children("." + cls)[0];
54
+
55
+ if (element == null) {
56
+
57
+ element = document.createElement("canvas");
58
+ element.className = cls;
59
+
60
+ $(element).css({ direction: "ltr", position: "absolute", left: 0, top: 0 })
61
+ .appendTo(container);
62
+
63
+ // If HTML5 Canvas isn't available, fall back to [Ex|Flash]canvas
64
+
65
+ if (!element.getContext) {
66
+ if (window.G_vmlCanvasManager) {
67
+ element = window.G_vmlCanvasManager.initElement(element);
68
+ } else {
69
+ throw new Error("Canvas is not available. If you're using IE with a fall-back such as Excanvas, then there's either a mistake in your conditional include, or the page has no DOCTYPE and is rendering in Quirks Mode.");
70
+ }
71
+ }
72
+ }
73
+
74
+ this.element = element;
75
+
76
+ var context = this.context = element.getContext("2d");
77
+
78
+ // Determine the screen's ratio of physical to device-independent
79
+ // pixels. This is the ratio between the canvas width that the browser
80
+ // advertises and the number of pixels actually present in that space.
81
+
82
+ // The iPhone 4, for example, has a device-independent width of 320px,
83
+ // but its screen is actually 640px wide. It therefore has a pixel
84
+ // ratio of 2, while most normal devices have a ratio of 1.
85
+
86
+ var devicePixelRatio = window.devicePixelRatio || 1,
87
+ backingStoreRatio =
88
+ context.webkitBackingStorePixelRatio ||
89
+ context.mozBackingStorePixelRatio ||
90
+ context.msBackingStorePixelRatio ||
91
+ context.oBackingStorePixelRatio ||
92
+ context.backingStorePixelRatio || 1;
93
+
94
+ this.pixelRatio = devicePixelRatio / backingStoreRatio;
95
+
96
+ // Size the canvas to match the internal dimensions of its container
97
+
98
+ this.resize(container.width(), container.height());
99
+
100
+ // Collection of HTML div layers for text overlaid onto the canvas
101
+
102
+ this.textContainer = null;
103
+ this.text = {};
104
+
105
+ // Cache of text fragments and metrics, so we can avoid expensively
106
+ // re-calculating them when the plot is re-rendered in a loop.
107
+
108
+ this._textCache = {};
109
+ }
110
+
111
+ // Resizes the canvas to the given dimensions.
112
+ //
113
+ // @param {number} width New width of the canvas, in pixels.
114
+ // @param {number} width New height of the canvas, in pixels.
115
+
116
+ Canvas.prototype.resize = function(width, height) {
117
+
118
+ if (width <= 0 || height <= 0) {
119
+ throw new Error("Invalid dimensions for plot, width = " + width + ", height = " + height);
120
+ }
121
+
122
+ var element = this.element,
123
+ context = this.context,
124
+ pixelRatio = this.pixelRatio;
125
+
126
+ // Resize the canvas, increasing its density based on the display's
127
+ // pixel ratio; basically giving it more pixels without increasing the
128
+ // size of its element, to take advantage of the fact that retina
129
+ // displays have that many more pixels in the same advertised space.
130
+
131
+ // Resizing should reset the state (excanvas seems to be buggy though)
132
+
133
+ if (this.width != width) {
134
+ element.width = width * pixelRatio;
135
+ element.style.width = width + "px";
136
+ this.width = width;
137
+ }
138
+
139
+ if (this.height != height) {
140
+ element.height = height * pixelRatio;
141
+ element.style.height = height + "px";
142
+ this.height = height;
143
+ }
144
+
145
+ // Save the context, so we can reset in case we get replotted. The
146
+ // restore ensure that we're really back at the initial state, and
147
+ // should be safe even if we haven't saved the initial state yet.
148
+
149
+ context.restore();
150
+ context.save();
151
+
152
+ // Scale the coordinate space to match the display density; so even though we
153
+ // may have twice as many pixels, we still want lines and other drawing to
154
+ // appear at the same size; the extra pixels will just make them crisper.
155
+
156
+ context.scale(pixelRatio, pixelRatio);
157
+ };
158
+
159
+ // Clears the entire canvas area, not including any overlaid HTML text
160
+
161
+ Canvas.prototype.clear = function() {
162
+ this.context.clearRect(0, 0, this.width, this.height);
163
+ };
164
+
165
+ // Finishes rendering the canvas, including managing the text overlay.
166
+
167
+ Canvas.prototype.render = function() {
168
+
169
+ var cache = this._textCache;
170
+
171
+ // For each text layer, add elements marked as active that haven't
172
+ // already been rendered, and remove those that are no longer active.
173
+
174
+ for (var layerKey in cache) {
175
+ if (hasOwnProperty.call(cache, layerKey)) {
176
+
177
+ var layer = this.getTextLayer(layerKey),
178
+ layerCache = cache[layerKey];
179
+
180
+ layer.hide();
181
+
182
+ for (var styleKey in layerCache) {
183
+ if (hasOwnProperty.call(layerCache, styleKey)) {
184
+ var styleCache = layerCache[styleKey];
185
+ for (var key in styleCache) {
186
+ if (hasOwnProperty.call(styleCache, key)) {
187
+
188
+ var positions = styleCache[key].positions;
189
+
190
+ for (var i = 0, position; position = positions[i]; i++) {
191
+ if (position.active) {
192
+ if (!position.rendered) {
193
+ layer.append(position.element);
194
+ position.rendered = true;
195
+ }
196
+ } else {
197
+ positions.splice(i--, 1);
198
+ if (position.rendered) {
199
+ position.element.detach();
200
+ }
201
+ }
202
+ }
203
+
204
+ if (positions.length == 0) {
205
+ delete styleCache[key];
206
+ }
207
+ }
208
+ }
209
+ }
210
+ }
211
+
212
+ layer.show();
213
+ }
214
+ }
215
+ };
216
+
217
+ // Creates (if necessary) and returns the text overlay container.
218
+ //
219
+ // @param {string} classes String of space-separated CSS classes used to
220
+ // uniquely identify the text layer.
221
+ // @return {object} The jQuery-wrapped text-layer div.
222
+
223
+ Canvas.prototype.getTextLayer = function(classes) {
224
+
225
+ var layer = this.text[classes];
226
+
227
+ // Create the text layer if it doesn't exist
228
+
229
+ if (layer == null) {
230
+
231
+ // Create the text layer container, if it doesn't exist
232
+
233
+ if (this.textContainer == null) {
234
+ this.textContainer = $("<div class='flot-text'></div>")
235
+ .css({
236
+ position: "absolute",
237
+ top: 0,
238
+ left: 0,
239
+ bottom: 0,
240
+ right: 0,
241
+ 'font-size': "smaller",
242
+ color: "#545454"
243
+ })
244
+ .insertAfter(this.element);
245
+ }
246
+
247
+ layer = this.text[classes] = $("<div></div>")
248
+ .addClass(classes)
249
+ .css({
250
+ position: "absolute",
251
+ top: 0,
252
+ left: 0,
253
+ bottom: 0,
254
+ right: 0
255
+ })
256
+ .appendTo(this.textContainer);
257
+ }
258
+
259
+ return layer;
260
+ };
261
+
262
+ // Creates (if necessary) and returns a text info object.
263
+ //
264
+ // The object looks like this:
265
+ //
266
+ // {
267
+ // width: Width of the text's wrapper div.
268
+ // height: Height of the text's wrapper div.
269
+ // element: The jQuery-wrapped HTML div containing the text.
270
+ // positions: Array of positions at which this text is drawn.
271
+ // }
272
+ //
273
+ // The positions array contains objects that look like this:
274
+ //
275
+ // {
276
+ // active: Flag indicating whether the text should be visible.
277
+ // rendered: Flag indicating whether the text is currently visible.
278
+ // element: The jQuery-wrapped HTML div containing the text.
279
+ // x: X coordinate at which to draw the text.
280
+ // y: Y coordinate at which to draw the text.
281
+ // }
282
+ //
283
+ // Each position after the first receives a clone of the original element.
284
+ //
285
+ // The idea is that that the width, height, and general 'identity' of the
286
+ // text is constant no matter where it is placed; the placements are a
287
+ // secondary property.
288
+ //
289
+ // Canvas maintains a cache of recently-used text info objects; getTextInfo
290
+ // either returns the cached element or creates a new entry.
291
+ //
292
+ // @param {string} layer A string of space-separated CSS classes uniquely
293
+ // identifying the layer containing this text.
294
+ // @param {string} text Text string to retrieve info for.
295
+ // @param {(string|object)=} font Either a string of space-separated CSS
296
+ // classes or a font-spec object, defining the text's font and style.
297
+ // @param {number=} angle Angle at which to rotate the text, in degrees.
298
+ // Angle is currently unused, it will be implemented in the future.
299
+ // @param {number=} width Maximum width of the text before it wraps.
300
+ // @return {object} a text info object.
301
+
302
+ Canvas.prototype.getTextInfo = function(layer, text, font, angle, width) {
303
+
304
+ var textStyle, layerCache, styleCache, info;
305
+
306
+ // Cast the value to a string, in case we were given a number or such
307
+
308
+ text = "" + text;
309
+
310
+ // If the font is a font-spec object, generate a CSS font definition
311
+
312
+ if (typeof font === "object") {
313
+ textStyle = font.style + " " + font.variant + " " + font.weight + " " + font.size + "px/" + font.lineHeight + "px " + font.family;
314
+ } else {
315
+ textStyle = font;
316
+ }
317
+
318
+ // Retrieve (or create) the cache for the text's layer and styles
319
+
320
+ layerCache = this._textCache[layer];
321
+
322
+ if (layerCache == null) {
323
+ layerCache = this._textCache[layer] = {};
324
+ }
325
+
326
+ styleCache = layerCache[textStyle];
327
+
328
+ if (styleCache == null) {
329
+ styleCache = layerCache[textStyle] = {};
330
+ }
331
+
332
+ info = styleCache[text];
333
+
334
+ // If we can't find a matching element in our cache, create a new one
335
+
336
+ if (info == null) {
337
+
338
+ var element = $("<div></div>").html(text)
339
+ .css({
340
+ position: "absolute",
341
+ 'max-width': width,
342
+ top: -9999
343
+ })
344
+ .appendTo(this.getTextLayer(layer));
345
+
346
+ if (typeof font === "object") {
347
+ element.css({
348
+ font: textStyle,
349
+ color: font.color
350
+ });
351
+ } else if (typeof font === "string") {
352
+ element.addClass(font);
353
+ }
354
+
355
+ info = styleCache[text] = {
356
+ width: element.outerWidth(true),
357
+ height: element.outerHeight(true),
358
+ element: element,
359
+ positions: []
360
+ };
361
+
362
+ element.detach();
363
+ }
364
+
365
+ return info;
366
+ };
367
+
368
+ // Adds a text string to the canvas text overlay.
369
+ //
370
+ // The text isn't drawn immediately; it is marked as rendering, which will
371
+ // result in its addition to the canvas on the next render pass.
372
+ //
373
+ // @param {string} layer A string of space-separated CSS classes uniquely
374
+ // identifying the layer containing this text.
375
+ // @param {number} x X coordinate at which to draw the text.
376
+ // @param {number} y Y coordinate at which to draw the text.
377
+ // @param {string} text Text string to draw.
378
+ // @param {(string|object)=} font Either a string of space-separated CSS
379
+ // classes or a font-spec object, defining the text's font and style.
380
+ // @param {number=} angle Angle at which to rotate the text, in degrees.
381
+ // Angle is currently unused, it will be implemented in the future.
382
+ // @param {number=} width Maximum width of the text before it wraps.
383
+ // @param {string=} halign Horizontal alignment of the text; either "left",
384
+ // "center" or "right".
385
+ // @param {string=} valign Vertical alignment of the text; either "top",
386
+ // "middle" or "bottom".
387
+
388
+ Canvas.prototype.addText = function(layer, x, y, text, font, angle, width, halign, valign) {
389
+
390
+ var info = this.getTextInfo(layer, text, font, angle, width),
391
+ positions = info.positions;
392
+
393
+ // Tweak the div's position to match the text's alignment
394
+
395
+ if (halign == "center") {
396
+ x -= info.width / 2;
397
+ } else if (halign == "right") {
398
+ x -= info.width;
399
+ }
400
+
401
+ if (valign == "middle") {
402
+ y -= info.height / 2;
403
+ } else if (valign == "bottom") {
404
+ y -= info.height;
405
+ }
406
+
407
+ // Determine whether this text already exists at this position.
408
+ // If so, mark it for inclusion in the next render pass.
409
+
410
+ for (var i = 0, position; position = positions[i]; i++) {
411
+ if (position.x == x && position.y == y) {
412
+ position.active = true;
413
+ return;
414
+ }
415
+ }
416
+
417
+ // If the text doesn't exist at this position, create a new entry
418
+
419
+ // For the very first position we'll re-use the original element,
420
+ // while for subsequent ones we'll clone it.
421
+
422
+ position = {
423
+ active: true,
424
+ rendered: false,
425
+ element: positions.length ? info.element.clone() : info.element,
426
+ x: x,
427
+ y: y
428
+ };
429
+
430
+ positions.push(position);
431
+
432
+ // Move the element to its final position within the container
433
+
434
+ position.element.css({
435
+ top: Math.round(y),
436
+ left: Math.round(x),
437
+ 'text-align': halign // In case the text wraps
438
+ });
439
+ };
440
+
441
+ // Removes one or more text strings from the canvas text overlay.
442
+ //
443
+ // If no parameters are given, all text within the layer is removed.
444
+ //
445
+ // Note that the text is not immediately removed; it is simply marked as
446
+ // inactive, which will result in its removal on the next render pass.
447
+ // This avoids the performance penalty for 'clear and redraw' behavior,
448
+ // where we potentially get rid of all text on a layer, but will likely
449
+ // add back most or all of it later, as when redrawing axes, for example.
450
+ //
451
+ // @param {string} layer A string of space-separated CSS classes uniquely
452
+ // identifying the layer containing this text.
453
+ // @param {number=} x X coordinate of the text.
454
+ // @param {number=} y Y coordinate of the text.
455
+ // @param {string=} text Text string to remove.
456
+ // @param {(string|object)=} font Either a string of space-separated CSS
457
+ // classes or a font-spec object, defining the text's font and style.
458
+ // @param {number=} angle Angle at which the text is rotated, in degrees.
459
+ // Angle is currently unused, it will be implemented in the future.
460
+
461
+ Canvas.prototype.removeText = function(layer, x, y, text, font, angle) {
462
+ if (text == null) {
463
+ var layerCache = this._textCache[layer];
464
+ if (layerCache != null) {
465
+ for (var styleKey in layerCache) {
466
+ if (hasOwnProperty.call(layerCache, styleKey)) {
467
+ var styleCache = layerCache[styleKey];
468
+ for (var key in styleCache) {
469
+ if (hasOwnProperty.call(styleCache, key)) {
470
+ var positions = styleCache[key].positions;
471
+ for (var i = 0, position; position = positions[i]; i++) {
472
+ position.active = false;
473
+ }
474
+ }
475
+ }
476
+ }
477
+ }
478
+ }
479
+ } else {
480
+ var positions = this.getTextInfo(layer, text, font, angle).positions;
481
+ for (var i = 0, position; position = positions[i]; i++) {
482
+ if (position.x == x && position.y == y) {
483
+ position.active = false;
484
+ }
485
+ }
486
+ }
487
+ };
488
+
489
+ ///////////////////////////////////////////////////////////////////////////
490
+ // The top-level container for the entire plot.
491
+
492
+ function Plot(placeholder, data_, options_, plugins) {
493
+ // data is on the form:
494
+ // [ series1, series2 ... ]
495
+ // where series is either just the data as [ [x1, y1], [x2, y2], ... ]
496
+ // or { data: [ [x1, y1], [x2, y2], ... ], label: "some label", ... }
497
+
498
+ var series = [],
499
+ options = {
500
+ // the color theme used for graphs
501
+ colors: ["#edc240", "#afd8f8", "#cb4b4b", "#4da74d", "#9440ed"],
502
+ legend: {
503
+ show: true,
504
+ noColumns: 1, // number of colums in legend table
505
+ labelFormatter: null, // fn: string -> string
506
+ labelBoxBorderColor: "#ccc", // border color for the little label boxes
507
+ container: null, // container (as jQuery object) to put legend in, null means default on top of graph
508
+ position: "ne", // position of default legend container within plot
509
+ margin: 5, // distance from grid edge to default legend container within plot
510
+ backgroundColor: null, // null means auto-detect
511
+ backgroundOpacity: 0.85, // set to 0 to avoid background
512
+ sorted: null // default to no legend sorting
513
+ },
514
+ xaxis: {
515
+ show: null, // null = auto-detect, true = always, false = never
516
+ position: "bottom", // or "top"
517
+ mode: null, // null or "time"
518
+ font: null, // null (derived from CSS in placeholder) or object like { size: 11, lineHeight: 13, style: "italic", weight: "bold", family: "sans-serif", variant: "small-caps" }
519
+ color: null, // base color, labels, ticks
520
+ tickColor: null, // possibly different color of ticks, e.g. "rgba(0,0,0,0.15)"
521
+ transform: null, // null or f: number -> number to transform axis
522
+ inverseTransform: null, // if transform is set, this should be the inverse function
523
+ min: null, // min. value to show, null means set automatically
524
+ max: null, // max. value to show, null means set automatically
525
+ autoscaleMargin: null, // margin in % to add if auto-setting min/max
526
+ ticks: null, // either [1, 3] or [[1, "a"], 3] or (fn: axis info -> ticks) or app. number of ticks for auto-ticks
527
+ tickFormatter: null, // fn: number -> string
528
+ labelWidth: null, // size of tick labels in pixels
529
+ labelHeight: null,
530
+ reserveSpace: null, // whether to reserve space even if axis isn't shown
531
+ tickLength: null, // size in pixels of ticks, or "full" for whole line
532
+ alignTicksWithAxis: null, // axis number or null for no sync
533
+ tickDecimals: null, // no. of decimals, null means auto
534
+ tickSize: null, // number or [number, "unit"]
535
+ minTickSize: null // number or [number, "unit"]
536
+ },
537
+ yaxis: {
538
+ autoscaleMargin: 0.02,
539
+ position: "left" // or "right"
540
+ },
541
+ xaxes: [],
542
+ yaxes: [],
543
+ series: {
544
+ points: {
545
+ show: false,
546
+ radius: 3,
547
+ lineWidth: 2, // in pixels
548
+ fill: true,
549
+ fillColor: "#ffffff",
550
+ symbol: "circle" // or callback
551
+ },
552
+ lines: {
553
+ // we don't put in show: false so we can see
554
+ // whether lines were actively disabled
555
+ lineWidth: 2, // in pixels
556
+ fill: false,
557
+ fillColor: null,
558
+ steps: false
559
+ // Omit 'zero', so we can later default its value to
560
+ // match that of the 'fill' option.
561
+ },
562
+ bars: {
563
+ show: false,
564
+ lineWidth: 2, // in pixels
565
+ barWidth: 1, // in units of the x axis
566
+ fill: true,
567
+ fillColor: null,
568
+ align: "left", // "left", "right", or "center"
569
+ horizontal: false,
570
+ zero: true
571
+ },
572
+ shadowSize: 3,
573
+ highlightColor: null
574
+ },
575
+ grid: {
576
+ show: true,
577
+ aboveData: false,
578
+ color: "#545454", // primary color used for outline and labels
579
+ backgroundColor: null, // null for transparent, else color
580
+ borderColor: null, // set if different from the grid color
581
+ tickColor: null, // color for the ticks, e.g. "rgba(0,0,0,0.15)"
582
+ margin: 0, // distance from the canvas edge to the grid
583
+ labelMargin: 5, // in pixels
584
+ axisMargin: 8, // in pixels
585
+ borderWidth: 2, // in pixels
586
+ minBorderMargin: null, // in pixels, null means taken from points radius
587
+ markings: null, // array of ranges or fn: axes -> array of ranges
588
+ markingsColor: "#f4f4f4",
589
+ markingsLineWidth: 2,
590
+ // interactive stuff
591
+ clickable: false,
592
+ hoverable: false,
593
+ autoHighlight: true, // highlight in case mouse is near
594
+ mouseActiveRadius: 10 // how far the mouse can be away to activate an item
595
+ },
596
+ interaction: {
597
+ redrawOverlayInterval: 1000/60 // time between updates, -1 means in same flow
598
+ },
599
+ hooks: {}
600
+ },
601
+ surface = null, // the canvas for the plot itself
602
+ overlay = null, // canvas for interactive stuff on top of plot
603
+ eventHolder = null, // jQuery object that events should be bound to
604
+ ctx = null, octx = null,
605
+ xaxes = [], yaxes = [],
606
+ plotOffset = { left: 0, right: 0, top: 0, bottom: 0},
607
+ plotWidth = 0, plotHeight = 0,
608
+ hooks = {
609
+ processOptions: [],
610
+ processRawData: [],
611
+ processDatapoints: [],
612
+ processOffset: [],
613
+ drawBackground: [],
614
+ drawSeries: [],
615
+ draw: [],
616
+ bindEvents: [],
617
+ drawOverlay: [],
618
+ shutdown: []
619
+ },
620
+ plot = this;
621
+
622
+ // public functions
623
+ plot.setData = setData;
624
+ plot.setupGrid = setupGrid;
625
+ plot.draw = draw;
626
+ plot.getPlaceholder = function() { return placeholder; };
627
+ plot.getCanvas = function() { return surface.element; };
628
+ plot.getPlotOffset = function() { return plotOffset; };
629
+ plot.width = function () { return plotWidth; };
630
+ plot.height = function () { return plotHeight; };
631
+ plot.offset = function () {
632
+ var o = eventHolder.offset();
633
+ o.left += plotOffset.left;
634
+ o.top += plotOffset.top;
635
+ return o;
636
+ };
637
+ plot.getData = function () { return series; };
638
+ plot.getAxes = function () {
639
+ var res = {}, i;
640
+ $.each(xaxes.concat(yaxes), function (_, axis) {
641
+ if (axis)
642
+ res[axis.direction + (axis.n != 1 ? axis.n : "") + "axis"] = axis;
643
+ });
644
+ return res;
645
+ };
646
+ plot.getXAxes = function () { return xaxes; };
647
+ plot.getYAxes = function () { return yaxes; };
648
+ plot.c2p = canvasToAxisCoords;
649
+ plot.p2c = axisToCanvasCoords;
650
+ plot.getOptions = function () { return options; };
651
+ plot.highlight = highlight;
652
+ plot.unhighlight = unhighlight;
653
+ plot.triggerRedrawOverlay = triggerRedrawOverlay;
654
+ plot.pointOffset = function(point) {
655
+ return {
656
+ left: parseInt(xaxes[axisNumber(point, "x") - 1].p2c(+point.x) + plotOffset.left, 10),
657
+ top: parseInt(yaxes[axisNumber(point, "y") - 1].p2c(+point.y) + plotOffset.top, 10)
658
+ };
659
+ };
660
+ plot.shutdown = shutdown;
661
+ plot.destroy = function () {
662
+ shutdown();
663
+ placeholder.removeData("plot").empty();
664
+
665
+ series = [];
666
+ options = null;
667
+ surface = null;
668
+ overlay = null;
669
+ eventHolder = null;
670
+ ctx = null;
671
+ octx = null;
672
+ xaxes = [];
673
+ yaxes = [];
674
+ hooks = null;
675
+ highlights = [];
676
+ plot = null;
677
+ };
678
+ plot.resize = function () {
679
+ var width = placeholder.width(),
680
+ height = placeholder.height();
681
+ surface.resize(width, height);
682
+ overlay.resize(width, height);
683
+ };
684
+
685
+ // public attributes
686
+ plot.hooks = hooks;
687
+
688
+ // initialize
689
+ initPlugins(plot);
690
+ parseOptions(options_);
691
+ setupCanvases();
692
+ setData(data_);
693
+ setupGrid();
694
+ draw();
695
+ bindEvents();
696
+
697
+
698
+ function executeHooks(hook, args) {
699
+ args = [plot].concat(args);
700
+ for (var i = 0; i < hook.length; ++i)
701
+ hook[i].apply(this, args);
702
+ }
703
+
704
+ function initPlugins() {
705
+
706
+ // References to key classes, allowing plugins to modify them
707
+
708
+ var classes = {
709
+ Canvas: Canvas
710
+ };
711
+
712
+ for (var i = 0; i < plugins.length; ++i) {
713
+ var p = plugins[i];
714
+ p.init(plot, classes);
715
+ if (p.options)
716
+ $.extend(true, options, p.options);
717
+ }
718
+ }
719
+
720
+ function parseOptions(opts) {
721
+
722
+ $.extend(true, options, opts);
723
+
724
+ // $.extend merges arrays, rather than replacing them. When less
725
+ // colors are provided than the size of the default palette, we
726
+ // end up with those colors plus the remaining defaults, which is
727
+ // not expected behavior; avoid it by replacing them here.
728
+
729
+ if (opts && opts.colors) {
730
+ options.colors = opts.colors;
731
+ }
732
+
733
+ if (options.xaxis.color == null)
734
+ options.xaxis.color = $.color.parse(options.grid.color).scale('a', 0.22).toString();
735
+ if (options.yaxis.color == null)
736
+ options.yaxis.color = $.color.parse(options.grid.color).scale('a', 0.22).toString();
737
+
738
+ if (options.xaxis.tickColor == null) // grid.tickColor for back-compatibility
739
+ options.xaxis.tickColor = options.grid.tickColor || options.xaxis.color;
740
+ if (options.yaxis.tickColor == null) // grid.tickColor for back-compatibility
741
+ options.yaxis.tickColor = options.grid.tickColor || options.yaxis.color;
742
+
743
+ if (options.grid.borderColor == null)
744
+ options.grid.borderColor = options.grid.color;
745
+ if (options.grid.tickColor == null)
746
+ options.grid.tickColor = $.color.parse(options.grid.color).scale('a', 0.22).toString();
747
+
748
+ // Fill in defaults for axis options, including any unspecified
749
+ // font-spec fields, if a font-spec was provided.
750
+
751
+ // If no x/y axis options were provided, create one of each anyway,
752
+ // since the rest of the code assumes that they exist.
753
+
754
+ var i, axisOptions, axisCount,
755
+ fontSize = placeholder.css("font-size"),
756
+ fontSizeDefault = fontSize ? +fontSize.replace("px", "") : 13,
757
+ fontDefaults = {
758
+ style: placeholder.css("font-style"),
759
+ size: Math.round(0.8 * fontSizeDefault),
760
+ variant: placeholder.css("font-variant"),
761
+ weight: placeholder.css("font-weight"),
762
+ family: placeholder.css("font-family")
763
+ };
764
+
765
+ axisCount = options.xaxes.length || 1;
766
+ for (i = 0; i < axisCount; ++i) {
767
+
768
+ axisOptions = options.xaxes[i];
769
+ if (axisOptions && !axisOptions.tickColor) {
770
+ axisOptions.tickColor = axisOptions.color;
771
+ }
772
+
773
+ axisOptions = $.extend(true, {}, options.xaxis, axisOptions);
774
+ options.xaxes[i] = axisOptions;
775
+
776
+ if (axisOptions.font) {
777
+ axisOptions.font = $.extend({}, fontDefaults, axisOptions.font);
778
+ if (!axisOptions.font.color) {
779
+ axisOptions.font.color = axisOptions.color;
780
+ }
781
+ if (!axisOptions.font.lineHeight) {
782
+ axisOptions.font.lineHeight = Math.round(axisOptions.font.size * 1.15);
783
+ }
784
+ }
785
+ }
786
+
787
+ axisCount = options.yaxes.length || 1;
788
+ for (i = 0; i < axisCount; ++i) {
789
+
790
+ axisOptions = options.yaxes[i];
791
+ if (axisOptions && !axisOptions.tickColor) {
792
+ axisOptions.tickColor = axisOptions.color;
793
+ }
794
+
795
+ axisOptions = $.extend(true, {}, options.yaxis, axisOptions);
796
+ options.yaxes[i] = axisOptions;
797
+
798
+ if (axisOptions.font) {
799
+ axisOptions.font = $.extend({}, fontDefaults, axisOptions.font);
800
+ if (!axisOptions.font.color) {
801
+ axisOptions.font.color = axisOptions.color;
802
+ }
803
+ if (!axisOptions.font.lineHeight) {
804
+ axisOptions.font.lineHeight = Math.round(axisOptions.font.size * 1.15);
805
+ }
806
+ }
807
+ }
808
+
809
+ // backwards compatibility, to be removed in future
810
+ if (options.xaxis.noTicks && options.xaxis.ticks == null)
811
+ options.xaxis.ticks = options.xaxis.noTicks;
812
+ if (options.yaxis.noTicks && options.yaxis.ticks == null)
813
+ options.yaxis.ticks = options.yaxis.noTicks;
814
+ if (options.x2axis) {
815
+ options.xaxes[1] = $.extend(true, {}, options.xaxis, options.x2axis);
816
+ options.xaxes[1].position = "top";
817
+ }
818
+ if (options.y2axis) {
819
+ options.yaxes[1] = $.extend(true, {}, options.yaxis, options.y2axis);
820
+ options.yaxes[1].position = "right";
821
+ }
822
+ if (options.grid.coloredAreas)
823
+ options.grid.markings = options.grid.coloredAreas;
824
+ if (options.grid.coloredAreasColor)
825
+ options.grid.markingsColor = options.grid.coloredAreasColor;
826
+ if (options.lines)
827
+ $.extend(true, options.series.lines, options.lines);
828
+ if (options.points)
829
+ $.extend(true, options.series.points, options.points);
830
+ if (options.bars)
831
+ $.extend(true, options.series.bars, options.bars);
832
+ if (options.shadowSize != null)
833
+ options.series.shadowSize = options.shadowSize;
834
+ if (options.highlightColor != null)
835
+ options.series.highlightColor = options.highlightColor;
836
+
837
+ // save options on axes for future reference
838
+ for (i = 0; i < options.xaxes.length; ++i)
839
+ getOrCreateAxis(xaxes, i + 1).options = options.xaxes[i];
840
+ for (i = 0; i < options.yaxes.length; ++i)
841
+ getOrCreateAxis(yaxes, i + 1).options = options.yaxes[i];
842
+
843
+ // add hooks from options
844
+ for (var n in hooks)
845
+ if (options.hooks[n] && options.hooks[n].length)
846
+ hooks[n] = hooks[n].concat(options.hooks[n]);
847
+
848
+ executeHooks(hooks.processOptions, [options]);
849
+ }
850
+
851
+ function setData(d) {
852
+ series = parseData(d);
853
+ fillInSeriesOptions();
854
+ processData();
855
+ }
856
+
857
+ function parseData(d) {
858
+ var res = [];
859
+ for (var i = 0; i < d.length; ++i) {
860
+ var s = $.extend(true, {}, options.series);
861
+
862
+ if (d[i].data != null) {
863
+ s.data = d[i].data; // move the data instead of deep-copy
864
+ delete d[i].data;
865
+
866
+ $.extend(true, s, d[i]);
867
+
868
+ d[i].data = s.data;
869
+ }
870
+ else
871
+ s.data = d[i];
872
+ res.push(s);
873
+ }
874
+
875
+ return res;
876
+ }
877
+
878
+ function axisNumber(obj, coord) {
879
+ var a = obj[coord + "axis"];
880
+ if (typeof a == "object") // if we got a real axis, extract number
881
+ a = a.n;
882
+ if (typeof a != "number")
883
+ a = 1; // default to first axis
884
+ return a;
885
+ }
886
+
887
+ function allAxes() {
888
+ // return flat array without annoying null entries
889
+ return $.grep(xaxes.concat(yaxes), function (a) { return a; });
890
+ }
891
+
892
+ function canvasToAxisCoords(pos) {
893
+ // return an object with x/y corresponding to all used axes
894
+ var res = {}, i, axis;
895
+ for (i = 0; i < xaxes.length; ++i) {
896
+ axis = xaxes[i];
897
+ if (axis && axis.used)
898
+ res["x" + axis.n] = axis.c2p(pos.left);
899
+ }
900
+
901
+ for (i = 0; i < yaxes.length; ++i) {
902
+ axis = yaxes[i];
903
+ if (axis && axis.used)
904
+ res["y" + axis.n] = axis.c2p(pos.top);
905
+ }
906
+
907
+ if (res.x1 !== undefined)
908
+ res.x = res.x1;
909
+ if (res.y1 !== undefined)
910
+ res.y = res.y1;
911
+
912
+ return res;
913
+ }
914
+
915
+ function axisToCanvasCoords(pos) {
916
+ // get canvas coords from the first pair of x/y found in pos
917
+ var res = {}, i, axis, key;
918
+
919
+ for (i = 0; i < xaxes.length; ++i) {
920
+ axis = xaxes[i];
921
+ if (axis && axis.used) {
922
+ key = "x" + axis.n;
923
+ if (pos[key] == null && axis.n == 1)
924
+ key = "x";
925
+
926
+ if (pos[key] != null) {
927
+ res.left = axis.p2c(pos[key]);
928
+ break;
929
+ }
930
+ }
931
+ }
932
+
933
+ for (i = 0; i < yaxes.length; ++i) {
934
+ axis = yaxes[i];
935
+ if (axis && axis.used) {
936
+ key = "y" + axis.n;
937
+ if (pos[key] == null && axis.n == 1)
938
+ key = "y";
939
+
940
+ if (pos[key] != null) {
941
+ res.top = axis.p2c(pos[key]);
942
+ break;
943
+ }
944
+ }
945
+ }
946
+
947
+ return res;
948
+ }
949
+
950
+ function getOrCreateAxis(axes, number) {
951
+ if (!axes[number - 1])
952
+ axes[number - 1] = {
953
+ n: number, // save the number for future reference
954
+ direction: axes == xaxes ? "x" : "y",
955
+ options: $.extend(true, {}, axes == xaxes ? options.xaxis : options.yaxis)
956
+ };
957
+
958
+ return axes[number - 1];
959
+ }
960
+
961
+ function fillInSeriesOptions() {
962
+
963
+ var neededColors = series.length, maxIndex = -1, i;
964
+
965
+ // Subtract the number of series that already have fixed colors or
966
+ // color indexes from the number that we still need to generate.
967
+
968
+ for (i = 0; i < series.length; ++i) {
969
+ var sc = series[i].color;
970
+ if (sc != null) {
971
+ neededColors--;
972
+ if (typeof sc == "number" && sc > maxIndex) {
973
+ maxIndex = sc;
974
+ }
975
+ }
976
+ }
977
+
978
+ // If any of the series have fixed color indexes, then we need to
979
+ // generate at least as many colors as the highest index.
980
+
981
+ if (neededColors <= maxIndex) {
982
+ neededColors = maxIndex + 1;
983
+ }
984
+
985
+ // Generate all the colors, using first the option colors and then
986
+ // variations on those colors once they're exhausted.
987
+
988
+ var c, colors = [], colorPool = options.colors,
989
+ colorPoolSize = colorPool.length, variation = 0;
990
+
991
+ for (i = 0; i < neededColors; i++) {
992
+
993
+ c = $.color.parse(colorPool[i % colorPoolSize] || "#666");
994
+
995
+ // Each time we exhaust the colors in the pool we adjust
996
+ // a scaling factor used to produce more variations on
997
+ // those colors. The factor alternates negative/positive
998
+ // to produce lighter/darker colors.
999
+
1000
+ // Reset the variation after every few cycles, or else
1001
+ // it will end up producing only white or black colors.
1002
+
1003
+ if (i % colorPoolSize == 0 && i) {
1004
+ if (variation >= 0) {
1005
+ if (variation < 0.5) {
1006
+ variation = -variation - 0.2;
1007
+ } else variation = 0;
1008
+ } else variation = -variation;
1009
+ }
1010
+
1011
+ colors[i] = c.scale('rgb', 1 + variation);
1012
+ }
1013
+
1014
+ // Finalize the series options, filling in their colors
1015
+
1016
+ var colori = 0, s;
1017
+ for (i = 0; i < series.length; ++i) {
1018
+ s = series[i];
1019
+
1020
+ // assign colors
1021
+ if (s.color == null) {
1022
+ s.color = colors[colori].toString();
1023
+ ++colori;
1024
+ }
1025
+ else if (typeof s.color == "number")
1026
+ s.color = colors[s.color].toString();
1027
+
1028
+ // turn on lines automatically in case nothing is set
1029
+ if (s.lines.show == null) {
1030
+ var v, show = true;
1031
+ for (v in s)
1032
+ if (s[v] && s[v].show) {
1033
+ show = false;
1034
+ break;
1035
+ }
1036
+ if (show)
1037
+ s.lines.show = true;
1038
+ }
1039
+
1040
+ // If nothing was provided for lines.zero, default it to match
1041
+ // lines.fill, since areas by default should extend to zero.
1042
+
1043
+ if (s.lines.zero == null) {
1044
+ s.lines.zero = !!s.lines.fill;
1045
+ }
1046
+
1047
+ // setup axes
1048
+ s.xaxis = getOrCreateAxis(xaxes, axisNumber(s, "x"));
1049
+ s.yaxis = getOrCreateAxis(yaxes, axisNumber(s, "y"));
1050
+ }
1051
+ }
1052
+
1053
+ function processData() {
1054
+ var topSentry = Number.POSITIVE_INFINITY,
1055
+ bottomSentry = Number.NEGATIVE_INFINITY,
1056
+ fakeInfinity = Number.MAX_VALUE,
1057
+ i, j, k, m, length,
1058
+ s, points, ps, x, y, axis, val, f, p,
1059
+ data, format;
1060
+
1061
+ function updateAxis(axis, min, max) {
1062
+ if (min < axis.datamin && min != -fakeInfinity)
1063
+ axis.datamin = min;
1064
+ if (max > axis.datamax && max != fakeInfinity)
1065
+ axis.datamax = max;
1066
+ }
1067
+
1068
+ $.each(allAxes(), function (_, axis) {
1069
+ // init axis
1070
+ axis.datamin = topSentry;
1071
+ axis.datamax = bottomSentry;
1072
+ axis.used = false;
1073
+ });
1074
+
1075
+ for (i = 0; i < series.length; ++i) {
1076
+ s = series[i];
1077
+ s.datapoints = { points: [] };
1078
+
1079
+ executeHooks(hooks.processRawData, [ s, s.data, s.datapoints ]);
1080
+ }
1081
+
1082
+ // first pass: clean and copy data
1083
+ for (i = 0; i < series.length; ++i) {
1084
+ s = series[i];
1085
+
1086
+ data = s.data;
1087
+ format = s.datapoints.format;
1088
+
1089
+ if (!format) {
1090
+ format = [];
1091
+ // find out how to copy
1092
+ format.push({ x: true, number: true, required: true });
1093
+ format.push({ y: true, number: true, required: true });
1094
+
1095
+ if (s.bars.show || (s.lines.show && s.lines.fill)) {
1096
+ var autoscale = !!((s.bars.show && s.bars.zero) || (s.lines.show && s.lines.zero));
1097
+ format.push({ y: true, number: true, required: false, defaultValue: 0, autoscale: autoscale });
1098
+ if (s.bars.horizontal) {
1099
+ delete format[format.length - 1].y;
1100
+ format[format.length - 1].x = true;
1101
+ }
1102
+ }
1103
+
1104
+ s.datapoints.format = format;
1105
+ }
1106
+
1107
+ if (s.datapoints.pointsize != null)
1108
+ continue; // already filled in
1109
+
1110
+ s.datapoints.pointsize = format.length;
1111
+
1112
+ ps = s.datapoints.pointsize;
1113
+ points = s.datapoints.points;
1114
+
1115
+ var insertSteps = s.lines.show && s.lines.steps;
1116
+ s.xaxis.used = s.yaxis.used = true;
1117
+
1118
+ for (j = k = 0; j < data.length; ++j, k += ps) {
1119
+ p = data[j];
1120
+
1121
+ var nullify = p == null;
1122
+ if (!nullify) {
1123
+ for (m = 0; m < ps; ++m) {
1124
+ val = p[m];
1125
+ f = format[m];
1126
+
1127
+ if (f) {
1128
+ if (f.number && val != null) {
1129
+ val = +val; // convert to number
1130
+ if (isNaN(val))
1131
+ val = null;
1132
+ else if (val == Infinity)
1133
+ val = fakeInfinity;
1134
+ else if (val == -Infinity)
1135
+ val = -fakeInfinity;
1136
+ }
1137
+
1138
+ if (val == null) {
1139
+ if (f.required)
1140
+ nullify = true;
1141
+
1142
+ if (f.defaultValue != null)
1143
+ val = f.defaultValue;
1144
+ }
1145
+ }
1146
+
1147
+ points[k + m] = val;
1148
+ }
1149
+ }
1150
+
1151
+ if (nullify) {
1152
+ for (m = 0; m < ps; ++m) {
1153
+ val = points[k + m];
1154
+ if (val != null) {
1155
+ f = format[m];
1156
+ // extract min/max info
1157
+ if (f.autoscale !== false) {
1158
+ if (f.x) {
1159
+ updateAxis(s.xaxis, val, val);
1160
+ }
1161
+ if (f.y) {
1162
+ updateAxis(s.yaxis, val, val);
1163
+ }
1164
+ }
1165
+ }
1166
+ points[k + m] = null;
1167
+ }
1168
+ }
1169
+ else {
1170
+ // a little bit of line specific stuff that
1171
+ // perhaps shouldn't be here, but lacking
1172
+ // better means...
1173
+ if (insertSteps && k > 0
1174
+ && points[k - ps] != null
1175
+ && points[k - ps] != points[k]
1176
+ && points[k - ps + 1] != points[k + 1]) {
1177
+ // copy the point to make room for a middle point
1178
+ for (m = 0; m < ps; ++m)
1179
+ points[k + ps + m] = points[k + m];
1180
+
1181
+ // middle point has same y
1182
+ points[k + 1] = points[k - ps + 1];
1183
+
1184
+ // we've added a point, better reflect that
1185
+ k += ps;
1186
+ }
1187
+ }
1188
+ }
1189
+ }
1190
+
1191
+ // give the hooks a chance to run
1192
+ for (i = 0; i < series.length; ++i) {
1193
+ s = series[i];
1194
+
1195
+ executeHooks(hooks.processDatapoints, [ s, s.datapoints]);
1196
+ }
1197
+
1198
+ // second pass: find datamax/datamin for auto-scaling
1199
+ for (i = 0; i < series.length; ++i) {
1200
+ s = series[i];
1201
+ points = s.datapoints.points;
1202
+ ps = s.datapoints.pointsize;
1203
+ format = s.datapoints.format;
1204
+
1205
+ var xmin = topSentry, ymin = topSentry,
1206
+ xmax = bottomSentry, ymax = bottomSentry;
1207
+
1208
+ for (j = 0; j < points.length; j += ps) {
1209
+ if (points[j] == null)
1210
+ continue;
1211
+
1212
+ for (m = 0; m < ps; ++m) {
1213
+ val = points[j + m];
1214
+ f = format[m];
1215
+ if (!f || f.autoscale === false || val == fakeInfinity || val == -fakeInfinity)
1216
+ continue;
1217
+
1218
+ if (f.x) {
1219
+ if (val < xmin)
1220
+ xmin = val;
1221
+ if (val > xmax)
1222
+ xmax = val;
1223
+ }
1224
+ if (f.y) {
1225
+ if (val < ymin)
1226
+ ymin = val;
1227
+ if (val > ymax)
1228
+ ymax = val;
1229
+ }
1230
+ }
1231
+ }
1232
+
1233
+ if (s.bars.show) {
1234
+ // make sure we got room for the bar on the dancing floor
1235
+ var delta;
1236
+
1237
+ switch (s.bars.align) {
1238
+ case "left":
1239
+ delta = 0;
1240
+ break;
1241
+ case "right":
1242
+ delta = -s.bars.barWidth;
1243
+ break;
1244
+ default:
1245
+ delta = -s.bars.barWidth / 2;
1246
+ }
1247
+
1248
+ if (s.bars.horizontal) {
1249
+ ymin += delta;
1250
+ ymax += delta + s.bars.barWidth;
1251
+ }
1252
+ else {
1253
+ xmin += delta;
1254
+ xmax += delta + s.bars.barWidth;
1255
+ }
1256
+ }
1257
+
1258
+ updateAxis(s.xaxis, xmin, xmax);
1259
+ updateAxis(s.yaxis, ymin, ymax);
1260
+ }
1261
+
1262
+ $.each(allAxes(), function (_, axis) {
1263
+ if (axis.datamin == topSentry)
1264
+ axis.datamin = null;
1265
+ if (axis.datamax == bottomSentry)
1266
+ axis.datamax = null;
1267
+ });
1268
+ }
1269
+
1270
+ function setupCanvases() {
1271
+
1272
+ // Make sure the placeholder is clear of everything except canvases
1273
+ // from a previous plot in this container that we'll try to re-use.
1274
+
1275
+ placeholder.css("padding", 0) // padding messes up the positioning
1276
+ .children().filter(function(){
1277
+ return !$(this).hasClass("flot-overlay") && !$(this).hasClass('flot-base');
1278
+ }).remove();
1279
+
1280
+ if (placeholder.css("position") == 'static')
1281
+ placeholder.css("position", "relative"); // for positioning labels and overlay
1282
+
1283
+ surface = new Canvas("flot-base", placeholder);
1284
+ overlay = new Canvas("flot-overlay", placeholder); // overlay canvas for interactive features
1285
+
1286
+ ctx = surface.context;
1287
+ octx = overlay.context;
1288
+
1289
+ // define which element we're listening for events on
1290
+ eventHolder = $(overlay.element).unbind();
1291
+
1292
+ // If we're re-using a plot object, shut down the old one
1293
+
1294
+ var existing = placeholder.data("plot");
1295
+
1296
+ if (existing) {
1297
+ existing.shutdown();
1298
+ overlay.clear();
1299
+ }
1300
+
1301
+ // save in case we get replotted
1302
+ placeholder.data("plot", plot);
1303
+ }
1304
+
1305
+ function bindEvents() {
1306
+ // bind events
1307
+ if (options.grid.hoverable) {
1308
+ eventHolder.mousemove(onMouseMove);
1309
+
1310
+ // Use bind, rather than .mouseleave, because we officially
1311
+ // still support jQuery 1.2.6, which doesn't define a shortcut
1312
+ // for mouseenter or mouseleave. This was a bug/oversight that
1313
+ // was fixed somewhere around 1.3.x. We can return to using
1314
+ // .mouseleave when we drop support for 1.2.6.
1315
+
1316
+ eventHolder.bind("mouseleave", onMouseLeave);
1317
+ }
1318
+
1319
+ if (options.grid.clickable)
1320
+ eventHolder.click(onClick);
1321
+
1322
+ executeHooks(hooks.bindEvents, [eventHolder]);
1323
+ }
1324
+
1325
+ function shutdown() {
1326
+ if (redrawTimeout)
1327
+ clearTimeout(redrawTimeout);
1328
+
1329
+ eventHolder.unbind("mousemove", onMouseMove);
1330
+ eventHolder.unbind("mouseleave", onMouseLeave);
1331
+ eventHolder.unbind("click", onClick);
1332
+
1333
+ executeHooks(hooks.shutdown, [eventHolder]);
1334
+ }
1335
+
1336
+ function setTransformationHelpers(axis) {
1337
+ // set helper functions on the axis, assumes plot area
1338
+ // has been computed already
1339
+
1340
+ function identity(x) { return x; }
1341
+
1342
+ var s, m, t = axis.options.transform || identity,
1343
+ it = axis.options.inverseTransform;
1344
+
1345
+ // precompute how much the axis is scaling a point
1346
+ // in canvas space
1347
+ if (axis.direction == "x") {
1348
+ s = axis.scale = plotWidth / Math.abs(t(axis.max) - t(axis.min));
1349
+ m = Math.min(t(axis.max), t(axis.min));
1350
+ }
1351
+ else {
1352
+ s = axis.scale = plotHeight / Math.abs(t(axis.max) - t(axis.min));
1353
+ s = -s;
1354
+ m = Math.max(t(axis.max), t(axis.min));
1355
+ }
1356
+
1357
+ // data point to canvas coordinate
1358
+ if (t == identity) // slight optimization
1359
+ axis.p2c = function (p) { return (p - m) * s; };
1360
+ else
1361
+ axis.p2c = function (p) { return (t(p) - m) * s; };
1362
+ // canvas coordinate to data point
1363
+ if (!it)
1364
+ axis.c2p = function (c) { return m + c / s; };
1365
+ else
1366
+ axis.c2p = function (c) { return it(m + c / s); };
1367
+ }
1368
+
1369
+ function measureTickLabels(axis) {
1370
+
1371
+ var opts = axis.options,
1372
+ ticks = axis.ticks || [],
1373
+ labelWidth = opts.labelWidth || 0,
1374
+ labelHeight = opts.labelHeight || 0,
1375
+ maxWidth = labelWidth || (axis.direction == "x" ? Math.floor(surface.width / (ticks.length || 1)) : null),
1376
+ legacyStyles = axis.direction + "Axis " + axis.direction + axis.n + "Axis",
1377
+ layer = "flot-" + axis.direction + "-axis flot-" + axis.direction + axis.n + "-axis " + legacyStyles,
1378
+ font = opts.font || "flot-tick-label tickLabel";
1379
+
1380
+ for (var i = 0; i < ticks.length; ++i) {
1381
+
1382
+ var t = ticks[i];
1383
+
1384
+ if (!t.label)
1385
+ continue;
1386
+
1387
+ var info = surface.getTextInfo(layer, t.label, font, null, maxWidth);
1388
+
1389
+ labelWidth = Math.max(labelWidth, info.width);
1390
+ labelHeight = Math.max(labelHeight, info.height);
1391
+ }
1392
+
1393
+ axis.labelWidth = opts.labelWidth || labelWidth;
1394
+ axis.labelHeight = opts.labelHeight || labelHeight;
1395
+ }
1396
+
1397
+ function allocateAxisBoxFirstPhase(axis) {
1398
+ // find the bounding box of the axis by looking at label
1399
+ // widths/heights and ticks, make room by diminishing the
1400
+ // plotOffset; this first phase only looks at one
1401
+ // dimension per axis, the other dimension depends on the
1402
+ // other axes so will have to wait
1403
+
1404
+ var lw = axis.labelWidth,
1405
+ lh = axis.labelHeight,
1406
+ pos = axis.options.position,
1407
+ isXAxis = axis.direction === "x",
1408
+ tickLength = axis.options.tickLength,
1409
+ axisMargin = options.grid.axisMargin,
1410
+ padding = options.grid.labelMargin,
1411
+ innermost = true,
1412
+ outermost = true,
1413
+ first = true,
1414
+ found = false;
1415
+
1416
+ // Determine the axis's position in its direction and on its side
1417
+
1418
+ $.each(isXAxis ? xaxes : yaxes, function(i, a) {
1419
+ if (a && a.reserveSpace) {
1420
+ if (a === axis) {
1421
+ found = true;
1422
+ } else if (a.options.position === pos) {
1423
+ if (found) {
1424
+ outermost = false;
1425
+ } else {
1426
+ innermost = false;
1427
+ }
1428
+ }
1429
+ if (!found) {
1430
+ first = false;
1431
+ }
1432
+ }
1433
+ });
1434
+
1435
+ // The outermost axis on each side has no margin
1436
+
1437
+ if (outermost) {
1438
+ axisMargin = 0;
1439
+ }
1440
+
1441
+ // The ticks for the first axis in each direction stretch across
1442
+
1443
+ if (tickLength == null) {
1444
+ tickLength = first ? "full" : 5;
1445
+ }
1446
+
1447
+ if (!isNaN(+tickLength))
1448
+ padding += +tickLength;
1449
+
1450
+ if (isXAxis) {
1451
+ lh += padding;
1452
+
1453
+ if (pos == "bottom") {
1454
+ plotOffset.bottom += lh + axisMargin;
1455
+ axis.box = { top: surface.height - plotOffset.bottom, height: lh };
1456
+ }
1457
+ else {
1458
+ axis.box = { top: plotOffset.top + axisMargin, height: lh };
1459
+ plotOffset.top += lh + axisMargin;
1460
+ }
1461
+ }
1462
+ else {
1463
+ lw += padding;
1464
+
1465
+ if (pos == "left") {
1466
+ axis.box = { left: plotOffset.left + axisMargin, width: lw };
1467
+ plotOffset.left += lw + axisMargin;
1468
+ }
1469
+ else {
1470
+ plotOffset.right += lw + axisMargin;
1471
+ axis.box = { left: surface.width - plotOffset.right, width: lw };
1472
+ }
1473
+ }
1474
+
1475
+ // save for future reference
1476
+ axis.position = pos;
1477
+ axis.tickLength = tickLength;
1478
+ axis.box.padding = padding;
1479
+ axis.innermost = innermost;
1480
+ }
1481
+
1482
+ function allocateAxisBoxSecondPhase(axis) {
1483
+ // now that all axis boxes have been placed in one
1484
+ // dimension, we can set the remaining dimension coordinates
1485
+ if (axis.direction == "x") {
1486
+ axis.box.left = plotOffset.left - axis.labelWidth / 2;
1487
+ axis.box.width = surface.width - plotOffset.left - plotOffset.right + axis.labelWidth;
1488
+ }
1489
+ else {
1490
+ axis.box.top = plotOffset.top - axis.labelHeight / 2;
1491
+ axis.box.height = surface.height - plotOffset.bottom - plotOffset.top + axis.labelHeight;
1492
+ }
1493
+ }
1494
+
1495
+ function adjustLayoutForThingsStickingOut() {
1496
+ // possibly adjust plot offset to ensure everything stays
1497
+ // inside the canvas and isn't clipped off
1498
+
1499
+ var minMargin = options.grid.minBorderMargin,
1500
+ axis, i;
1501
+
1502
+ // check stuff from the plot (FIXME: this should just read
1503
+ // a value from the series, otherwise it's impossible to
1504
+ // customize)
1505
+ if (minMargin == null) {
1506
+ minMargin = 0;
1507
+ for (i = 0; i < series.length; ++i)
1508
+ minMargin = Math.max(minMargin, 2 * (series[i].points.radius + series[i].points.lineWidth/2));
1509
+ }
1510
+
1511
+ var margins = {
1512
+ left: minMargin,
1513
+ right: minMargin,
1514
+ top: minMargin,
1515
+ bottom: minMargin
1516
+ };
1517
+
1518
+ // check axis labels, note we don't check the actual
1519
+ // labels but instead use the overall width/height to not
1520
+ // jump as much around with replots
1521
+ $.each(allAxes(), function (_, axis) {
1522
+ if (axis.reserveSpace && axis.ticks && axis.ticks.length) {
1523
+ var lastTick = axis.ticks[axis.ticks.length - 1];
1524
+ if (axis.direction === "x") {
1525
+ margins.left = Math.max(margins.left, axis.labelWidth / 2);
1526
+ if (lastTick.v <= axis.max) {
1527
+ margins.right = Math.max(margins.right, axis.labelWidth / 2);
1528
+ }
1529
+ } else {
1530
+ margins.bottom = Math.max(margins.bottom, axis.labelHeight / 2);
1531
+ if (lastTick.v <= axis.max) {
1532
+ margins.top = Math.max(margins.top, axis.labelHeight / 2);
1533
+ }
1534
+ }
1535
+ }
1536
+ });
1537
+
1538
+ plotOffset.left = Math.ceil(Math.max(margins.left, plotOffset.left));
1539
+ plotOffset.right = Math.ceil(Math.max(margins.right, plotOffset.right));
1540
+ plotOffset.top = Math.ceil(Math.max(margins.top, plotOffset.top));
1541
+ plotOffset.bottom = Math.ceil(Math.max(margins.bottom, plotOffset.bottom));
1542
+ }
1543
+
1544
+ function setupGrid() {
1545
+ var i, axes = allAxes(), showGrid = options.grid.show;
1546
+
1547
+ // Initialize the plot's offset from the edge of the canvas
1548
+
1549
+ for (var a in plotOffset) {
1550
+ var margin = options.grid.margin || 0;
1551
+ plotOffset[a] = typeof margin == "number" ? margin : margin[a] || 0;
1552
+ }
1553
+
1554
+ executeHooks(hooks.processOffset, [plotOffset]);
1555
+
1556
+ // If the grid is visible, add its border width to the offset
1557
+
1558
+ for (var a in plotOffset) {
1559
+ if(typeof(options.grid.borderWidth) == "object") {
1560
+ plotOffset[a] += showGrid ? options.grid.borderWidth[a] : 0;
1561
+ }
1562
+ else {
1563
+ plotOffset[a] += showGrid ? options.grid.borderWidth : 0;
1564
+ }
1565
+ }
1566
+
1567
+ // init axes
1568
+ $.each(axes, function (_, axis) {
1569
+ axis.show = axis.options.show;
1570
+ if (axis.show == null)
1571
+ axis.show = axis.used; // by default an axis is visible if it's got data
1572
+
1573
+ axis.reserveSpace = axis.show || axis.options.reserveSpace;
1574
+
1575
+ setRange(axis);
1576
+ });
1577
+
1578
+ if (showGrid) {
1579
+
1580
+ var allocatedAxes = $.grep(axes, function (axis) { return axis.reserveSpace; });
1581
+
1582
+ $.each(allocatedAxes, function (_, axis) {
1583
+ // make the ticks
1584
+ setupTickGeneration(axis);
1585
+ setTicks(axis);
1586
+ snapRangeToTicks(axis, axis.ticks);
1587
+ // find labelWidth/Height for axis
1588
+ measureTickLabels(axis);
1589
+ });
1590
+
1591
+ // with all dimensions calculated, we can compute the
1592
+ // axis bounding boxes, start from the outside
1593
+ // (reverse order)
1594
+ for (i = allocatedAxes.length - 1; i >= 0; --i)
1595
+ allocateAxisBoxFirstPhase(allocatedAxes[i]);
1596
+
1597
+ // make sure we've got enough space for things that
1598
+ // might stick out
1599
+ adjustLayoutForThingsStickingOut();
1600
+
1601
+ $.each(allocatedAxes, function (_, axis) {
1602
+ allocateAxisBoxSecondPhase(axis);
1603
+ });
1604
+ }
1605
+
1606
+ plotWidth = surface.width - plotOffset.left - plotOffset.right;
1607
+ plotHeight = surface.height - plotOffset.bottom - plotOffset.top;
1608
+
1609
+ // now we got the proper plot dimensions, we can compute the scaling
1610
+ $.each(axes, function (_, axis) {
1611
+ setTransformationHelpers(axis);
1612
+ });
1613
+
1614
+ if (showGrid) {
1615
+ drawAxisLabels();
1616
+ }
1617
+
1618
+ insertLegend();
1619
+ }
1620
+
1621
+ function setRange(axis) {
1622
+ var opts = axis.options,
1623
+ min = +(opts.min != null ? opts.min : axis.datamin),
1624
+ max = +(opts.max != null ? opts.max : axis.datamax),
1625
+ delta = max - min;
1626
+
1627
+ if (delta == 0.0) {
1628
+ // degenerate case
1629
+ var widen = max == 0 ? 1 : 0.01;
1630
+
1631
+ if (opts.min == null)
1632
+ min -= widen;
1633
+ // always widen max if we couldn't widen min to ensure we
1634
+ // don't fall into min == max which doesn't work
1635
+ if (opts.max == null || opts.min != null)
1636
+ max += widen;
1637
+ }
1638
+ else {
1639
+ // consider autoscaling
1640
+ var margin = opts.autoscaleMargin;
1641
+ if (margin != null) {
1642
+ if (opts.min == null) {
1643
+ min -= delta * margin;
1644
+ // make sure we don't go below zero if all values
1645
+ // are positive
1646
+ if (min < 0 && axis.datamin != null && axis.datamin >= 0)
1647
+ min = 0;
1648
+ }
1649
+ if (opts.max == null) {
1650
+ max += delta * margin;
1651
+ if (max > 0 && axis.datamax != null && axis.datamax <= 0)
1652
+ max = 0;
1653
+ }
1654
+ }
1655
+ }
1656
+ axis.min = min;
1657
+ axis.max = max;
1658
+ }
1659
+
1660
+ function setupTickGeneration(axis) {
1661
+ var opts = axis.options;
1662
+
1663
+ // estimate number of ticks
1664
+ var noTicks;
1665
+ if (typeof opts.ticks == "number" && opts.ticks > 0)
1666
+ noTicks = opts.ticks;
1667
+ else
1668
+ // heuristic based on the model a*sqrt(x) fitted to
1669
+ // some data points that seemed reasonable
1670
+ noTicks = 0.3 * Math.sqrt(axis.direction == "x" ? surface.width : surface.height);
1671
+
1672
+ var delta = (axis.max - axis.min) / noTicks,
1673
+ dec = -Math.floor(Math.log(delta) / Math.LN10),
1674
+ maxDec = opts.tickDecimals;
1675
+
1676
+ if (maxDec != null && dec > maxDec) {
1677
+ dec = maxDec;
1678
+ }
1679
+
1680
+ var magn = Math.pow(10, -dec),
1681
+ norm = delta / magn, // norm is between 1.0 and 10.0
1682
+ size;
1683
+
1684
+ if (norm < 1.5) {
1685
+ size = 1;
1686
+ } else if (norm < 3) {
1687
+ size = 2;
1688
+ // special case for 2.5, requires an extra decimal
1689
+ if (norm > 2.25 && (maxDec == null || dec + 1 <= maxDec)) {
1690
+ size = 2.5;
1691
+ ++dec;
1692
+ }
1693
+ } else if (norm < 7.5) {
1694
+ size = 5;
1695
+ } else {
1696
+ size = 10;
1697
+ }
1698
+
1699
+ size *= magn;
1700
+
1701
+ if (opts.minTickSize != null && size < opts.minTickSize) {
1702
+ size = opts.minTickSize;
1703
+ }
1704
+
1705
+ axis.delta = delta;
1706
+ axis.tickDecimals = Math.max(0, maxDec != null ? maxDec : dec);
1707
+ axis.tickSize = opts.tickSize || size;
1708
+
1709
+ // Time mode was moved to a plug-in in 0.8, but since so many people use this
1710
+ // we'll add an especially friendly make sure they remembered to include it.
1711
+
1712
+ if (opts.mode == "time" && !axis.tickGenerator) {
1713
+ throw new Error("Time mode requires the flot.time plugin.");
1714
+ }
1715
+
1716
+ // Flot supports base-10 axes; any other mode else is handled by a plug-in,
1717
+ // like flot.time.js.
1718
+
1719
+ if (!axis.tickGenerator) {
1720
+
1721
+ axis.tickGenerator = function (axis) {
1722
+
1723
+ var ticks = [],
1724
+ start = floorInBase(axis.min, axis.tickSize),
1725
+ i = 0,
1726
+ v = Number.NaN,
1727
+ prev;
1728
+
1729
+ do {
1730
+ prev = v;
1731
+ v = start + i * axis.tickSize;
1732
+ ticks.push(v);
1733
+ ++i;
1734
+ } while (v < axis.max && v != prev);
1735
+ return ticks;
1736
+ };
1737
+
1738
+ axis.tickFormatter = function (value, axis) {
1739
+
1740
+ var factor = axis.tickDecimals ? Math.pow(10, axis.tickDecimals) : 1;
1741
+ var formatted = "" + Math.round(value * factor) / factor;
1742
+
1743
+ // If tickDecimals was specified, ensure that we have exactly that
1744
+ // much precision; otherwise default to the value's own precision.
1745
+
1746
+ if (axis.tickDecimals != null) {
1747
+ var decimal = formatted.indexOf(".");
1748
+ var precision = decimal == -1 ? 0 : formatted.length - decimal - 1;
1749
+ if (precision < axis.tickDecimals) {
1750
+ return (precision ? formatted : formatted + ".") + ("" + factor).substr(1, axis.tickDecimals - precision);
1751
+ }
1752
+ }
1753
+
1754
+ return formatted;
1755
+ };
1756
+ }
1757
+
1758
+ if ($.isFunction(opts.tickFormatter))
1759
+ axis.tickFormatter = function (v, axis) { return "" + opts.tickFormatter(v, axis); };
1760
+
1761
+ if (opts.alignTicksWithAxis != null) {
1762
+ var otherAxis = (axis.direction == "x" ? xaxes : yaxes)[opts.alignTicksWithAxis - 1];
1763
+ if (otherAxis && otherAxis.used && otherAxis != axis) {
1764
+ // consider snapping min/max to outermost nice ticks
1765
+ var niceTicks = axis.tickGenerator(axis);
1766
+ if (niceTicks.length > 0) {
1767
+ if (opts.min == null)
1768
+ axis.min = Math.min(axis.min, niceTicks[0]);
1769
+ if (opts.max == null && niceTicks.length > 1)
1770
+ axis.max = Math.max(axis.max, niceTicks[niceTicks.length - 1]);
1771
+ }
1772
+
1773
+ axis.tickGenerator = function (axis) {
1774
+ // copy ticks, scaled to this axis
1775
+ var ticks = [], v, i;
1776
+ for (i = 0; i < otherAxis.ticks.length; ++i) {
1777
+ v = (otherAxis.ticks[i].v - otherAxis.min) / (otherAxis.max - otherAxis.min);
1778
+ v = axis.min + v * (axis.max - axis.min);
1779
+ ticks.push(v);
1780
+ }
1781
+ return ticks;
1782
+ };
1783
+
1784
+ // we might need an extra decimal since forced
1785
+ // ticks don't necessarily fit naturally
1786
+ if (!axis.mode && opts.tickDecimals == null) {
1787
+ var extraDec = Math.max(0, -Math.floor(Math.log(axis.delta) / Math.LN10) + 1),
1788
+ ts = axis.tickGenerator(axis);
1789
+
1790
+ // only proceed if the tick interval rounded
1791
+ // with an extra decimal doesn't give us a
1792
+ // zero at end
1793
+ if (!(ts.length > 1 && /\..*0$/.test((ts[1] - ts[0]).toFixed(extraDec))))
1794
+ axis.tickDecimals = extraDec;
1795
+ }
1796
+ }
1797
+ }
1798
+ }
1799
+
1800
+ function setTicks(axis) {
1801
+ var oticks = axis.options.ticks, ticks = [];
1802
+ if (oticks == null || (typeof oticks == "number" && oticks > 0))
1803
+ ticks = axis.tickGenerator(axis);
1804
+ else if (oticks) {
1805
+ if ($.isFunction(oticks))
1806
+ // generate the ticks
1807
+ ticks = oticks(axis);
1808
+ else
1809
+ ticks = oticks;
1810
+ }
1811
+
1812
+ // clean up/labelify the supplied ticks, copy them over
1813
+ var i, v;
1814
+ axis.ticks = [];
1815
+ for (i = 0; i < ticks.length; ++i) {
1816
+ var label = null;
1817
+ var t = ticks[i];
1818
+ if (typeof t == "object") {
1819
+ v = +t[0];
1820
+ if (t.length > 1)
1821
+ label = t[1];
1822
+ }
1823
+ else
1824
+ v = +t;
1825
+ if (label == null)
1826
+ label = axis.tickFormatter(v, axis);
1827
+ if (!isNaN(v))
1828
+ axis.ticks.push({ v: v, label: label });
1829
+ }
1830
+ }
1831
+
1832
+ function snapRangeToTicks(axis, ticks) {
1833
+ if (axis.options.autoscaleMargin && ticks.length > 0) {
1834
+ // snap to ticks
1835
+ if (axis.options.min == null)
1836
+ axis.min = Math.min(axis.min, ticks[0].v);
1837
+ if (axis.options.max == null && ticks.length > 1)
1838
+ axis.max = Math.max(axis.max, ticks[ticks.length - 1].v);
1839
+ }
1840
+ }
1841
+
1842
+ function draw() {
1843
+
1844
+ surface.clear();
1845
+
1846
+ executeHooks(hooks.drawBackground, [ctx]);
1847
+
1848
+ var grid = options.grid;
1849
+
1850
+ // draw background, if any
1851
+ if (grid.show && grid.backgroundColor)
1852
+ drawBackground();
1853
+
1854
+ if (grid.show && !grid.aboveData) {
1855
+ drawGrid();
1856
+ }
1857
+
1858
+ for (var i = 0; i < series.length; ++i) {
1859
+ executeHooks(hooks.drawSeries, [ctx, series[i]]);
1860
+ drawSeries(series[i]);
1861
+ }
1862
+
1863
+ executeHooks(hooks.draw, [ctx]);
1864
+
1865
+ if (grid.show && grid.aboveData) {
1866
+ drawGrid();
1867
+ }
1868
+
1869
+ surface.render();
1870
+
1871
+ // A draw implies that either the axes or data have changed, so we
1872
+ // should probably update the overlay highlights as well.
1873
+
1874
+ triggerRedrawOverlay();
1875
+ }
1876
+
1877
+ function extractRange(ranges, coord) {
1878
+ var axis, from, to, key, axes = allAxes();
1879
+
1880
+ for (var i = 0; i < axes.length; ++i) {
1881
+ axis = axes[i];
1882
+ if (axis.direction == coord) {
1883
+ key = coord + axis.n + "axis";
1884
+ if (!ranges[key] && axis.n == 1)
1885
+ key = coord + "axis"; // support x1axis as xaxis
1886
+ if (ranges[key]) {
1887
+ from = ranges[key].from;
1888
+ to = ranges[key].to;
1889
+ break;
1890
+ }
1891
+ }
1892
+ }
1893
+
1894
+ // backwards-compat stuff - to be removed in future
1895
+ if (!ranges[key]) {
1896
+ axis = coord == "x" ? xaxes[0] : yaxes[0];
1897
+ from = ranges[coord + "1"];
1898
+ to = ranges[coord + "2"];
1899
+ }
1900
+
1901
+ // auto-reverse as an added bonus
1902
+ if (from != null && to != null && from > to) {
1903
+ var tmp = from;
1904
+ from = to;
1905
+ to = tmp;
1906
+ }
1907
+
1908
+ return { from: from, to: to, axis: axis };
1909
+ }
1910
+
1911
+ function drawBackground() {
1912
+ ctx.save();
1913
+ ctx.translate(plotOffset.left, plotOffset.top);
1914
+
1915
+ ctx.fillStyle = getColorOrGradient(options.grid.backgroundColor, plotHeight, 0, "rgba(255, 255, 255, 0)");
1916
+ ctx.fillRect(0, 0, plotWidth, plotHeight);
1917
+ ctx.restore();
1918
+ }
1919
+
1920
+ function drawGrid() {
1921
+ var i, axes, bw, bc;
1922
+
1923
+ ctx.save();
1924
+ ctx.translate(plotOffset.left, plotOffset.top);
1925
+
1926
+ // draw markings
1927
+ var markings = options.grid.markings;
1928
+ if (markings) {
1929
+ if ($.isFunction(markings)) {
1930
+ axes = plot.getAxes();
1931
+ // xmin etc. is backwards compatibility, to be
1932
+ // removed in the future
1933
+ axes.xmin = axes.xaxis.min;
1934
+ axes.xmax = axes.xaxis.max;
1935
+ axes.ymin = axes.yaxis.min;
1936
+ axes.ymax = axes.yaxis.max;
1937
+
1938
+ markings = markings(axes);
1939
+ }
1940
+
1941
+ for (i = 0; i < markings.length; ++i) {
1942
+ var m = markings[i],
1943
+ xrange = extractRange(m, "x"),
1944
+ yrange = extractRange(m, "y");
1945
+
1946
+ // fill in missing
1947
+ if (xrange.from == null)
1948
+ xrange.from = xrange.axis.min;
1949
+ if (xrange.to == null)
1950
+ xrange.to = xrange.axis.max;
1951
+ if (yrange.from == null)
1952
+ yrange.from = yrange.axis.min;
1953
+ if (yrange.to == null)
1954
+ yrange.to = yrange.axis.max;
1955
+
1956
+ // clip
1957
+ if (xrange.to < xrange.axis.min || xrange.from > xrange.axis.max ||
1958
+ yrange.to < yrange.axis.min || yrange.from > yrange.axis.max)
1959
+ continue;
1960
+
1961
+ xrange.from = Math.max(xrange.from, xrange.axis.min);
1962
+ xrange.to = Math.min(xrange.to, xrange.axis.max);
1963
+ yrange.from = Math.max(yrange.from, yrange.axis.min);
1964
+ yrange.to = Math.min(yrange.to, yrange.axis.max);
1965
+
1966
+ if (xrange.from == xrange.to && yrange.from == yrange.to)
1967
+ continue;
1968
+
1969
+ // then draw
1970
+ xrange.from = xrange.axis.p2c(xrange.from);
1971
+ xrange.to = xrange.axis.p2c(xrange.to);
1972
+ yrange.from = yrange.axis.p2c(yrange.from);
1973
+ yrange.to = yrange.axis.p2c(yrange.to);
1974
+
1975
+ if (xrange.from == xrange.to || yrange.from == yrange.to) {
1976
+ // draw line
1977
+ ctx.beginPath();
1978
+ ctx.strokeStyle = m.color || options.grid.markingsColor;
1979
+ ctx.lineWidth = m.lineWidth || options.grid.markingsLineWidth;
1980
+ ctx.moveTo(xrange.from, yrange.from);
1981
+ ctx.lineTo(xrange.to, yrange.to);
1982
+ ctx.stroke();
1983
+ }
1984
+ else {
1985
+ // fill area
1986
+ ctx.fillStyle = m.color || options.grid.markingsColor;
1987
+ ctx.fillRect(xrange.from, yrange.to,
1988
+ xrange.to - xrange.from,
1989
+ yrange.from - yrange.to);
1990
+ }
1991
+ }
1992
+ }
1993
+
1994
+ // draw the ticks
1995
+ axes = allAxes();
1996
+ bw = options.grid.borderWidth;
1997
+
1998
+ for (var j = 0; j < axes.length; ++j) {
1999
+ var axis = axes[j], box = axis.box,
2000
+ t = axis.tickLength, x, y, xoff, yoff;
2001
+ if (!axis.show || axis.ticks.length == 0)
2002
+ continue;
2003
+
2004
+ ctx.lineWidth = 1;
2005
+
2006
+ // find the edges
2007
+ if (axis.direction == "x") {
2008
+ x = 0;
2009
+ if (t == "full")
2010
+ y = (axis.position == "top" ? 0 : plotHeight);
2011
+ else
2012
+ y = box.top - plotOffset.top + (axis.position == "top" ? box.height : 0);
2013
+ }
2014
+ else {
2015
+ y = 0;
2016
+ if (t == "full")
2017
+ x = (axis.position == "left" ? 0 : plotWidth);
2018
+ else
2019
+ x = box.left - plotOffset.left + (axis.position == "left" ? box.width : 0);
2020
+ }
2021
+
2022
+ // draw tick bar
2023
+ if (!axis.innermost) {
2024
+ ctx.strokeStyle = axis.options.color;
2025
+ ctx.beginPath();
2026
+ xoff = yoff = 0;
2027
+ if (axis.direction == "x")
2028
+ xoff = plotWidth + 1;
2029
+ else
2030
+ yoff = plotHeight + 1;
2031
+
2032
+ if (ctx.lineWidth == 1) {
2033
+ if (axis.direction == "x") {
2034
+ y = Math.floor(y) + 0.5;
2035
+ } else {
2036
+ x = Math.floor(x) + 0.5;
2037
+ }
2038
+ }
2039
+
2040
+ ctx.moveTo(x, y);
2041
+ ctx.lineTo(x + xoff, y + yoff);
2042
+ ctx.stroke();
2043
+ }
2044
+
2045
+ // draw ticks
2046
+
2047
+ ctx.strokeStyle = axis.options.tickColor;
2048
+
2049
+ ctx.beginPath();
2050
+ for (i = 0; i < axis.ticks.length; ++i) {
2051
+ var v = axis.ticks[i].v;
2052
+
2053
+ xoff = yoff = 0;
2054
+
2055
+ if (isNaN(v) || v < axis.min || v > axis.max
2056
+ // skip those lying on the axes if we got a border
2057
+ || (t == "full"
2058
+ && ((typeof bw == "object" && bw[axis.position] > 0) || bw > 0)
2059
+ && (v == axis.min || v == axis.max)))
2060
+ continue;
2061
+
2062
+ if (axis.direction == "x") {
2063
+ x = axis.p2c(v);
2064
+ yoff = t == "full" ? -plotHeight : t;
2065
+
2066
+ if (axis.position == "top")
2067
+ yoff = -yoff;
2068
+ }
2069
+ else {
2070
+ y = axis.p2c(v);
2071
+ xoff = t == "full" ? -plotWidth : t;
2072
+
2073
+ if (axis.position == "left")
2074
+ xoff = -xoff;
2075
+ }
2076
+
2077
+ if (ctx.lineWidth == 1) {
2078
+ if (axis.direction == "x")
2079
+ x = Math.floor(x) + 0.5;
2080
+ else
2081
+ y = Math.floor(y) + 0.5;
2082
+ }
2083
+
2084
+ ctx.moveTo(x, y);
2085
+ ctx.lineTo(x + xoff, y + yoff);
2086
+ }
2087
+
2088
+ ctx.stroke();
2089
+ }
2090
+
2091
+
2092
+ // draw border
2093
+ if (bw) {
2094
+ // If either borderWidth or borderColor is an object, then draw the border
2095
+ // line by line instead of as one rectangle
2096
+ bc = options.grid.borderColor;
2097
+ if(typeof bw == "object" || typeof bc == "object") {
2098
+ if (typeof bw !== "object") {
2099
+ bw = {top: bw, right: bw, bottom: bw, left: bw};
2100
+ }
2101
+ if (typeof bc !== "object") {
2102
+ bc = {top: bc, right: bc, bottom: bc, left: bc};
2103
+ }
2104
+
2105
+ if (bw.top > 0) {
2106
+ ctx.strokeStyle = bc.top;
2107
+ ctx.lineWidth = bw.top;
2108
+ ctx.beginPath();
2109
+ ctx.moveTo(0 - bw.left, 0 - bw.top/2);
2110
+ ctx.lineTo(plotWidth, 0 - bw.top/2);
2111
+ ctx.stroke();
2112
+ }
2113
+
2114
+ if (bw.right > 0) {
2115
+ ctx.strokeStyle = bc.right;
2116
+ ctx.lineWidth = bw.right;
2117
+ ctx.beginPath();
2118
+ ctx.moveTo(plotWidth + bw.right / 2, 0 - bw.top);
2119
+ ctx.lineTo(plotWidth + bw.right / 2, plotHeight);
2120
+ ctx.stroke();
2121
+ }
2122
+
2123
+ if (bw.bottom > 0) {
2124
+ ctx.strokeStyle = bc.bottom;
2125
+ ctx.lineWidth = bw.bottom;
2126
+ ctx.beginPath();
2127
+ ctx.moveTo(plotWidth + bw.right, plotHeight + bw.bottom / 2);
2128
+ ctx.lineTo(0, plotHeight + bw.bottom / 2);
2129
+ ctx.stroke();
2130
+ }
2131
+
2132
+ if (bw.left > 0) {
2133
+ ctx.strokeStyle = bc.left;
2134
+ ctx.lineWidth = bw.left;
2135
+ ctx.beginPath();
2136
+ ctx.moveTo(0 - bw.left/2, plotHeight + bw.bottom);
2137
+ ctx.lineTo(0- bw.left/2, 0);
2138
+ ctx.stroke();
2139
+ }
2140
+ }
2141
+ else {
2142
+ ctx.lineWidth = bw;
2143
+ ctx.strokeStyle = options.grid.borderColor;
2144
+ ctx.strokeRect(-bw/2, -bw/2, plotWidth + bw, plotHeight + bw);
2145
+ }
2146
+ }
2147
+
2148
+ ctx.restore();
2149
+ }
2150
+
2151
+ function drawAxisLabels() {
2152
+
2153
+ $.each(allAxes(), function (_, axis) {
2154
+ var box = axis.box,
2155
+ legacyStyles = axis.direction + "Axis " + axis.direction + axis.n + "Axis",
2156
+ layer = "flot-" + axis.direction + "-axis flot-" + axis.direction + axis.n + "-axis " + legacyStyles,
2157
+ font = axis.options.font || "flot-tick-label tickLabel",
2158
+ tick, x, y, halign, valign;
2159
+
2160
+ // Remove text before checking for axis.show and ticks.length;
2161
+ // otherwise plugins, like flot-tickrotor, that draw their own
2162
+ // tick labels will end up with both theirs and the defaults.
2163
+
2164
+ surface.removeText(layer);
2165
+
2166
+ if (!axis.show || axis.ticks.length == 0)
2167
+ return;
2168
+
2169
+ for (var i = 0; i < axis.ticks.length; ++i) {
2170
+
2171
+ tick = axis.ticks[i];
2172
+ if (!tick.label || tick.v < axis.min || tick.v > axis.max)
2173
+ continue;
2174
+
2175
+ if (axis.direction == "x") {
2176
+ halign = "center";
2177
+ x = plotOffset.left + axis.p2c(tick.v);
2178
+ if (axis.position == "bottom") {
2179
+ y = box.top + box.padding;
2180
+ } else {
2181
+ y = box.top + box.height - box.padding;
2182
+ valign = "bottom";
2183
+ }
2184
+ } else {
2185
+ valign = "middle";
2186
+ y = plotOffset.top + axis.p2c(tick.v);
2187
+ if (axis.position == "left") {
2188
+ x = box.left + box.width - box.padding;
2189
+ halign = "right";
2190
+ } else {
2191
+ x = box.left + box.padding;
2192
+ }
2193
+ }
2194
+
2195
+ surface.addText(layer, x, y, tick.label, font, null, null, halign, valign);
2196
+ }
2197
+ });
2198
+ }
2199
+
2200
+ function drawSeries(series) {
2201
+ if (series.lines.show)
2202
+ drawSeriesLines(series);
2203
+ if (series.bars.show)
2204
+ drawSeriesBars(series);
2205
+ if (series.points.show)
2206
+ drawSeriesPoints(series);
2207
+ }
2208
+
2209
+ function drawSeriesLines(series) {
2210
+ function plotLine(datapoints, xoffset, yoffset, axisx, axisy) {
2211
+ var points = datapoints.points,
2212
+ ps = datapoints.pointsize,
2213
+ prevx = null, prevy = null;
2214
+
2215
+ ctx.beginPath();
2216
+ for (var i = ps; i < points.length; i += ps) {
2217
+ var x1 = points[i - ps], y1 = points[i - ps + 1],
2218
+ x2 = points[i], y2 = points[i + 1];
2219
+
2220
+ if (x1 == null || x2 == null)
2221
+ continue;
2222
+
2223
+ // clip with ymin
2224
+ if (y1 <= y2 && y1 < axisy.min) {
2225
+ if (y2 < axisy.min)
2226
+ continue; // line segment is outside
2227
+ // compute new intersection point
2228
+ x1 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1;
2229
+ y1 = axisy.min;
2230
+ }
2231
+ else if (y2 <= y1 && y2 < axisy.min) {
2232
+ if (y1 < axisy.min)
2233
+ continue;
2234
+ x2 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1;
2235
+ y2 = axisy.min;
2236
+ }
2237
+
2238
+ // clip with ymax
2239
+ if (y1 >= y2 && y1 > axisy.max) {
2240
+ if (y2 > axisy.max)
2241
+ continue;
2242
+ x1 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1;
2243
+ y1 = axisy.max;
2244
+ }
2245
+ else if (y2 >= y1 && y2 > axisy.max) {
2246
+ if (y1 > axisy.max)
2247
+ continue;
2248
+ x2 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1;
2249
+ y2 = axisy.max;
2250
+ }
2251
+
2252
+ // clip with xmin
2253
+ if (x1 <= x2 && x1 < axisx.min) {
2254
+ if (x2 < axisx.min)
2255
+ continue;
2256
+ y1 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1;
2257
+ x1 = axisx.min;
2258
+ }
2259
+ else if (x2 <= x1 && x2 < axisx.min) {
2260
+ if (x1 < axisx.min)
2261
+ continue;
2262
+ y2 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1;
2263
+ x2 = axisx.min;
2264
+ }
2265
+
2266
+ // clip with xmax
2267
+ if (x1 >= x2 && x1 > axisx.max) {
2268
+ if (x2 > axisx.max)
2269
+ continue;
2270
+ y1 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1;
2271
+ x1 = axisx.max;
2272
+ }
2273
+ else if (x2 >= x1 && x2 > axisx.max) {
2274
+ if (x1 > axisx.max)
2275
+ continue;
2276
+ y2 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1;
2277
+ x2 = axisx.max;
2278
+ }
2279
+
2280
+ if (x1 != prevx || y1 != prevy)
2281
+ ctx.moveTo(axisx.p2c(x1) + xoffset, axisy.p2c(y1) + yoffset);
2282
+
2283
+ prevx = x2;
2284
+ prevy = y2;
2285
+ ctx.lineTo(axisx.p2c(x2) + xoffset, axisy.p2c(y2) + yoffset);
2286
+ }
2287
+ ctx.stroke();
2288
+ }
2289
+
2290
+ function plotLineArea(datapoints, axisx, axisy) {
2291
+ var points = datapoints.points,
2292
+ ps = datapoints.pointsize,
2293
+ bottom = Math.min(Math.max(0, axisy.min), axisy.max),
2294
+ i = 0, top, areaOpen = false,
2295
+ ypos = 1, segmentStart = 0, segmentEnd = 0;
2296
+
2297
+ // we process each segment in two turns, first forward
2298
+ // direction to sketch out top, then once we hit the
2299
+ // end we go backwards to sketch the bottom
2300
+ while (true) {
2301
+ if (ps > 0 && i > points.length + ps)
2302
+ break;
2303
+
2304
+ i += ps; // ps is negative if going backwards
2305
+
2306
+ var x1 = points[i - ps],
2307
+ y1 = points[i - ps + ypos],
2308
+ x2 = points[i], y2 = points[i + ypos];
2309
+
2310
+ if (areaOpen) {
2311
+ if (ps > 0 && x1 != null && x2 == null) {
2312
+ // at turning point
2313
+ segmentEnd = i;
2314
+ ps = -ps;
2315
+ ypos = 2;
2316
+ continue;
2317
+ }
2318
+
2319
+ if (ps < 0 && i == segmentStart + ps) {
2320
+ // done with the reverse sweep
2321
+ ctx.fill();
2322
+ areaOpen = false;
2323
+ ps = -ps;
2324
+ ypos = 1;
2325
+ i = segmentStart = segmentEnd + ps;
2326
+ continue;
2327
+ }
2328
+ }
2329
+
2330
+ if (x1 == null || x2 == null)
2331
+ continue;
2332
+
2333
+ // clip x values
2334
+
2335
+ // clip with xmin
2336
+ if (x1 <= x2 && x1 < axisx.min) {
2337
+ if (x2 < axisx.min)
2338
+ continue;
2339
+ y1 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1;
2340
+ x1 = axisx.min;
2341
+ }
2342
+ else if (x2 <= x1 && x2 < axisx.min) {
2343
+ if (x1 < axisx.min)
2344
+ continue;
2345
+ y2 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1;
2346
+ x2 = axisx.min;
2347
+ }
2348
+
2349
+ // clip with xmax
2350
+ if (x1 >= x2 && x1 > axisx.max) {
2351
+ if (x2 > axisx.max)
2352
+ continue;
2353
+ y1 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1;
2354
+ x1 = axisx.max;
2355
+ }
2356
+ else if (x2 >= x1 && x2 > axisx.max) {
2357
+ if (x1 > axisx.max)
2358
+ continue;
2359
+ y2 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1;
2360
+ x2 = axisx.max;
2361
+ }
2362
+
2363
+ if (!areaOpen) {
2364
+ // open area
2365
+ ctx.beginPath();
2366
+ ctx.moveTo(axisx.p2c(x1), axisy.p2c(bottom));
2367
+ areaOpen = true;
2368
+ }
2369
+
2370
+ // now first check the case where both is outside
2371
+ if (y1 >= axisy.max && y2 >= axisy.max) {
2372
+ ctx.lineTo(axisx.p2c(x1), axisy.p2c(axisy.max));
2373
+ ctx.lineTo(axisx.p2c(x2), axisy.p2c(axisy.max));
2374
+ continue;
2375
+ }
2376
+ else if (y1 <= axisy.min && y2 <= axisy.min) {
2377
+ ctx.lineTo(axisx.p2c(x1), axisy.p2c(axisy.min));
2378
+ ctx.lineTo(axisx.p2c(x2), axisy.p2c(axisy.min));
2379
+ continue;
2380
+ }
2381
+
2382
+ // else it's a bit more complicated, there might
2383
+ // be a flat maxed out rectangle first, then a
2384
+ // triangular cutout or reverse; to find these
2385
+ // keep track of the current x values
2386
+ var x1old = x1, x2old = x2;
2387
+
2388
+ // clip the y values, without shortcutting, we
2389
+ // go through all cases in turn
2390
+
2391
+ // clip with ymin
2392
+ if (y1 <= y2 && y1 < axisy.min && y2 >= axisy.min) {
2393
+ x1 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1;
2394
+ y1 = axisy.min;
2395
+ }
2396
+ else if (y2 <= y1 && y2 < axisy.min && y1 >= axisy.min) {
2397
+ x2 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1;
2398
+ y2 = axisy.min;
2399
+ }
2400
+
2401
+ // clip with ymax
2402
+ if (y1 >= y2 && y1 > axisy.max && y2 <= axisy.max) {
2403
+ x1 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1;
2404
+ y1 = axisy.max;
2405
+ }
2406
+ else if (y2 >= y1 && y2 > axisy.max && y1 <= axisy.max) {
2407
+ x2 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1;
2408
+ y2 = axisy.max;
2409
+ }
2410
+
2411
+ // if the x value was changed we got a rectangle
2412
+ // to fill
2413
+ if (x1 != x1old) {
2414
+ ctx.lineTo(axisx.p2c(x1old), axisy.p2c(y1));
2415
+ // it goes to (x1, y1), but we fill that below
2416
+ }
2417
+
2418
+ // fill triangular section, this sometimes result
2419
+ // in redundant points if (x1, y1) hasn't changed
2420
+ // from previous line to, but we just ignore that
2421
+ ctx.lineTo(axisx.p2c(x1), axisy.p2c(y1));
2422
+ ctx.lineTo(axisx.p2c(x2), axisy.p2c(y2));
2423
+
2424
+ // fill the other rectangle if it's there
2425
+ if (x2 != x2old) {
2426
+ ctx.lineTo(axisx.p2c(x2), axisy.p2c(y2));
2427
+ ctx.lineTo(axisx.p2c(x2old), axisy.p2c(y2));
2428
+ }
2429
+ }
2430
+ }
2431
+
2432
+ ctx.save();
2433
+ ctx.translate(plotOffset.left, plotOffset.top);
2434
+ ctx.lineJoin = "round";
2435
+
2436
+ var lw = series.lines.lineWidth,
2437
+ sw = series.shadowSize;
2438
+ // FIXME: consider another form of shadow when filling is turned on
2439
+ if (lw > 0 && sw > 0) {
2440
+ // draw shadow as a thick and thin line with transparency
2441
+ ctx.lineWidth = sw;
2442
+ ctx.strokeStyle = "rgba(0,0,0,0.1)";
2443
+ // position shadow at angle from the mid of line
2444
+ var angle = Math.PI/18;
2445
+ plotLine(series.datapoints, Math.sin(angle) * (lw/2 + sw/2), Math.cos(angle) * (lw/2 + sw/2), series.xaxis, series.yaxis);
2446
+ ctx.lineWidth = sw/2;
2447
+ plotLine(series.datapoints, Math.sin(angle) * (lw/2 + sw/4), Math.cos(angle) * (lw/2 + sw/4), series.xaxis, series.yaxis);
2448
+ }
2449
+
2450
+ ctx.lineWidth = lw;
2451
+ ctx.strokeStyle = series.color;
2452
+ var fillStyle = getFillStyle(series.lines, series.color, 0, plotHeight);
2453
+ if (fillStyle) {
2454
+ ctx.fillStyle = fillStyle;
2455
+ plotLineArea(series.datapoints, series.xaxis, series.yaxis);
2456
+ }
2457
+
2458
+ if (lw > 0)
2459
+ plotLine(series.datapoints, 0, 0, series.xaxis, series.yaxis);
2460
+ ctx.restore();
2461
+ }
2462
+
2463
+ function drawSeriesPoints(series) {
2464
+ function plotPoints(datapoints, radius, fillStyle, offset, shadow, axisx, axisy, symbol) {
2465
+ var points = datapoints.points, ps = datapoints.pointsize;
2466
+
2467
+ for (var i = 0; i < points.length; i += ps) {
2468
+ var x = points[i], y = points[i + 1];
2469
+ if (x == null || x < axisx.min || x > axisx.max || y < axisy.min || y > axisy.max)
2470
+ continue;
2471
+
2472
+ ctx.beginPath();
2473
+ x = axisx.p2c(x);
2474
+ y = axisy.p2c(y) + offset;
2475
+ if (symbol == "circle")
2476
+ ctx.arc(x, y, radius, 0, shadow ? Math.PI : Math.PI * 2, false);
2477
+ else
2478
+ symbol(ctx, x, y, radius, shadow);
2479
+ ctx.closePath();
2480
+
2481
+ if (fillStyle) {
2482
+ ctx.fillStyle = fillStyle;
2483
+ ctx.fill();
2484
+ }
2485
+ ctx.stroke();
2486
+ }
2487
+ }
2488
+
2489
+ ctx.save();
2490
+ ctx.translate(plotOffset.left, plotOffset.top);
2491
+
2492
+ var lw = series.points.lineWidth,
2493
+ sw = series.shadowSize,
2494
+ radius = series.points.radius,
2495
+ symbol = series.points.symbol;
2496
+
2497
+ // If the user sets the line width to 0, we change it to a very
2498
+ // small value. A line width of 0 seems to force the default of 1.
2499
+ // Doing the conditional here allows the shadow setting to still be
2500
+ // optional even with a lineWidth of 0.
2501
+
2502
+ if( lw == 0 )
2503
+ lw = 0.0001;
2504
+
2505
+ if (lw > 0 && sw > 0) {
2506
+ // draw shadow in two steps
2507
+ var w = sw / 2;
2508
+ ctx.lineWidth = w;
2509
+ ctx.strokeStyle = "rgba(0,0,0,0.1)";
2510
+ plotPoints(series.datapoints, radius, null, w + w/2, true,
2511
+ series.xaxis, series.yaxis, symbol);
2512
+
2513
+ ctx.strokeStyle = "rgba(0,0,0,0.2)";
2514
+ plotPoints(series.datapoints, radius, null, w/2, true,
2515
+ series.xaxis, series.yaxis, symbol);
2516
+ }
2517
+
2518
+ ctx.lineWidth = lw;
2519
+ ctx.strokeStyle = series.color;
2520
+ plotPoints(series.datapoints, radius,
2521
+ getFillStyle(series.points, series.color), 0, false,
2522
+ series.xaxis, series.yaxis, symbol);
2523
+ ctx.restore();
2524
+ }
2525
+
2526
+ function drawBar(x, y, b, barLeft, barRight, fillStyleCallback, axisx, axisy, c, horizontal, lineWidth) {
2527
+ var left, right, bottom, top,
2528
+ drawLeft, drawRight, drawTop, drawBottom,
2529
+ tmp;
2530
+
2531
+ // in horizontal mode, we start the bar from the left
2532
+ // instead of from the bottom so it appears to be
2533
+ // horizontal rather than vertical
2534
+ if (horizontal) {
2535
+ drawBottom = drawRight = drawTop = true;
2536
+ drawLeft = false;
2537
+ left = b;
2538
+ right = x;
2539
+ top = y + barLeft;
2540
+ bottom = y + barRight;
2541
+
2542
+ // account for negative bars
2543
+ if (right < left) {
2544
+ tmp = right;
2545
+ right = left;
2546
+ left = tmp;
2547
+ drawLeft = true;
2548
+ drawRight = false;
2549
+ }
2550
+ }
2551
+ else {
2552
+ drawLeft = drawRight = drawTop = true;
2553
+ drawBottom = false;
2554
+ left = x + barLeft;
2555
+ right = x + barRight;
2556
+ bottom = b;
2557
+ top = y;
2558
+
2559
+ // account for negative bars
2560
+ if (top < bottom) {
2561
+ tmp = top;
2562
+ top = bottom;
2563
+ bottom = tmp;
2564
+ drawBottom = true;
2565
+ drawTop = false;
2566
+ }
2567
+ }
2568
+
2569
+ // clip
2570
+ if (right < axisx.min || left > axisx.max ||
2571
+ top < axisy.min || bottom > axisy.max)
2572
+ return;
2573
+
2574
+ if (left < axisx.min) {
2575
+ left = axisx.min;
2576
+ drawLeft = false;
2577
+ }
2578
+
2579
+ if (right > axisx.max) {
2580
+ right = axisx.max;
2581
+ drawRight = false;
2582
+ }
2583
+
2584
+ if (bottom < axisy.min) {
2585
+ bottom = axisy.min;
2586
+ drawBottom = false;
2587
+ }
2588
+
2589
+ if (top > axisy.max) {
2590
+ top = axisy.max;
2591
+ drawTop = false;
2592
+ }
2593
+
2594
+ left = axisx.p2c(left);
2595
+ bottom = axisy.p2c(bottom);
2596
+ right = axisx.p2c(right);
2597
+ top = axisy.p2c(top);
2598
+
2599
+ // fill the bar
2600
+ if (fillStyleCallback) {
2601
+ c.fillStyle = fillStyleCallback(bottom, top);
2602
+ c.fillRect(left, top, right - left, bottom - top)
2603
+ }
2604
+
2605
+ // draw outline
2606
+ if (lineWidth > 0 && (drawLeft || drawRight || drawTop || drawBottom)) {
2607
+ c.beginPath();
2608
+
2609
+ // FIXME: inline moveTo is buggy with excanvas
2610
+ c.moveTo(left, bottom);
2611
+ if (drawLeft)
2612
+ c.lineTo(left, top);
2613
+ else
2614
+ c.moveTo(left, top);
2615
+ if (drawTop)
2616
+ c.lineTo(right, top);
2617
+ else
2618
+ c.moveTo(right, top);
2619
+ if (drawRight)
2620
+ c.lineTo(right, bottom);
2621
+ else
2622
+ c.moveTo(right, bottom);
2623
+ if (drawBottom)
2624
+ c.lineTo(left, bottom);
2625
+ else
2626
+ c.moveTo(left, bottom);
2627
+ c.stroke();
2628
+ }
2629
+ }
2630
+
2631
+ function drawSeriesBars(series) {
2632
+ function plotBars(datapoints, barLeft, barRight, fillStyleCallback, axisx, axisy) {
2633
+ var points = datapoints.points, ps = datapoints.pointsize;
2634
+
2635
+ for (var i = 0; i < points.length; i += ps) {
2636
+ if (points[i] == null)
2637
+ continue;
2638
+ drawBar(points[i], points[i + 1], points[i + 2], barLeft, barRight, fillStyleCallback, axisx, axisy, ctx, series.bars.horizontal, series.bars.lineWidth);
2639
+ }
2640
+ }
2641
+
2642
+ ctx.save();
2643
+ ctx.translate(plotOffset.left, plotOffset.top);
2644
+
2645
+ // FIXME: figure out a way to add shadows (for instance along the right edge)
2646
+ ctx.lineWidth = series.bars.lineWidth;
2647
+ ctx.strokeStyle = series.color;
2648
+
2649
+ var barLeft;
2650
+
2651
+ switch (series.bars.align) {
2652
+ case "left":
2653
+ barLeft = 0;
2654
+ break;
2655
+ case "right":
2656
+ barLeft = -series.bars.barWidth;
2657
+ break;
2658
+ default:
2659
+ barLeft = -series.bars.barWidth / 2;
2660
+ }
2661
+
2662
+ var fillStyleCallback = series.bars.fill ? function (bottom, top) { return getFillStyle(series.bars, series.color, bottom, top); } : null;
2663
+ plotBars(series.datapoints, barLeft, barLeft + series.bars.barWidth, fillStyleCallback, series.xaxis, series.yaxis);
2664
+ ctx.restore();
2665
+ }
2666
+
2667
+ function getFillStyle(filloptions, seriesColor, bottom, top) {
2668
+ var fill = filloptions.fill;
2669
+ if (!fill)
2670
+ return null;
2671
+
2672
+ if (filloptions.fillColor)
2673
+ return getColorOrGradient(filloptions.fillColor, bottom, top, seriesColor);
2674
+
2675
+ var c = $.color.parse(seriesColor);
2676
+ c.a = typeof fill == "number" ? fill : 0.4;
2677
+ c.normalize();
2678
+ return c.toString();
2679
+ }
2680
+
2681
+ function insertLegend() {
2682
+
2683
+ if (options.legend.container != null) {
2684
+ $(options.legend.container).html("");
2685
+ } else {
2686
+ placeholder.find(".legend").remove();
2687
+ }
2688
+
2689
+ if (!options.legend.show) {
2690
+ return;
2691
+ }
2692
+
2693
+ var fragments = [], entries = [], rowStarted = false,
2694
+ lf = options.legend.labelFormatter, s, label;
2695
+
2696
+ // Build a list of legend entries, with each having a label and a color
2697
+
2698
+ for (var i = 0; i < series.length; ++i) {
2699
+ s = series[i];
2700
+ if (s.label) {
2701
+ label = lf ? lf(s.label, s) : s.label;
2702
+ if (label) {
2703
+ entries.push({
2704
+ label: label,
2705
+ color: s.color
2706
+ });
2707
+ }
2708
+ }
2709
+ }
2710
+
2711
+ // Sort the legend using either the default or a custom comparator
2712
+
2713
+ if (options.legend.sorted) {
2714
+ if ($.isFunction(options.legend.sorted)) {
2715
+ entries.sort(options.legend.sorted);
2716
+ } else if (options.legend.sorted == "reverse") {
2717
+ entries.reverse();
2718
+ } else {
2719
+ var ascending = options.legend.sorted != "descending";
2720
+ entries.sort(function(a, b) {
2721
+ return a.label == b.label ? 0 : (
2722
+ (a.label < b.label) != ascending ? 1 : -1 // Logical XOR
2723
+ );
2724
+ });
2725
+ }
2726
+ }
2727
+
2728
+ // Generate markup for the list of entries, in their final order
2729
+
2730
+ for (var i = 0; i < entries.length; ++i) {
2731
+
2732
+ var entry = entries[i];
2733
+
2734
+ if (i % options.legend.noColumns == 0) {
2735
+ if (rowStarted)
2736
+ fragments.push('</tr>');
2737
+ fragments.push('<tr>');
2738
+ rowStarted = true;
2739
+ }
2740
+
2741
+ fragments.push(
2742
+ '<td class="legendColorBox"><div style="border:1px solid ' + options.legend.labelBoxBorderColor + ';padding:1px"><div style="width:4px;height:0;border:5px solid ' + entry.color + ';overflow:hidden"></div></div></td>' +
2743
+ '<td class="legendLabel">' + entry.label + '</td>'
2744
+ );
2745
+ }
2746
+
2747
+ if (rowStarted)
2748
+ fragments.push('</tr>');
2749
+
2750
+ if (fragments.length == 0)
2751
+ return;
2752
+
2753
+ var table = '<table style="font-size:smaller;color:' + options.grid.color + '">' + fragments.join("") + '</table>';
2754
+ if (options.legend.container != null)
2755
+ $(options.legend.container).html(table);
2756
+ else {
2757
+ var pos = "",
2758
+ p = options.legend.position,
2759
+ m = options.legend.margin;
2760
+ if (m[0] == null)
2761
+ m = [m, m];
2762
+ if (p.charAt(0) == "n")
2763
+ pos += 'top:' + (m[1] + plotOffset.top) + 'px;';
2764
+ else if (p.charAt(0) == "s")
2765
+ pos += 'bottom:' + (m[1] + plotOffset.bottom) + 'px;';
2766
+ if (p.charAt(1) == "e")
2767
+ pos += 'right:' + (m[0] + plotOffset.right) + 'px;';
2768
+ else if (p.charAt(1) == "w")
2769
+ pos += 'left:' + (m[0] + plotOffset.left) + 'px;';
2770
+ var legend = $('<div class="legend">' + table.replace('style="', 'style="position:absolute;' + pos +';') + '</div>').appendTo(placeholder);
2771
+ if (options.legend.backgroundOpacity != 0.0) {
2772
+ // put in the transparent background
2773
+ // separately to avoid blended labels and
2774
+ // label boxes
2775
+ var c = options.legend.backgroundColor;
2776
+ if (c == null) {
2777
+ c = options.grid.backgroundColor;
2778
+ if (c && typeof c == "string")
2779
+ c = $.color.parse(c);
2780
+ else
2781
+ c = $.color.extract(legend, 'background-color');
2782
+ c.a = 1;
2783
+ c = c.toString();
2784
+ }
2785
+ var div = legend.children();
2786
+ $('<div style="position:absolute;width:' + div.width() + 'px;height:' + div.height() + 'px;' + pos +'background-color:' + c + ';"> </div>').prependTo(legend).css('opacity', options.legend.backgroundOpacity);
2787
+ }
2788
+ }
2789
+ }
2790
+
2791
+
2792
+ // interactive features
2793
+
2794
+ var highlights = [],
2795
+ redrawTimeout = null;
2796
+
2797
+ // returns the data item the mouse is over, or null if none is found
2798
+ function findNearbyItem(mouseX, mouseY, seriesFilter) {
2799
+ var maxDistance = options.grid.mouseActiveRadius,
2800
+ smallestDistance = maxDistance * maxDistance + 1,
2801
+ item = null, foundPoint = false, i, j, ps;
2802
+
2803
+ for (i = series.length - 1; i >= 0; --i) {
2804
+ if (!seriesFilter(series[i]))
2805
+ continue;
2806
+
2807
+ var s = series[i],
2808
+ axisx = s.xaxis,
2809
+ axisy = s.yaxis,
2810
+ points = s.datapoints.points,
2811
+ mx = axisx.c2p(mouseX), // precompute some stuff to make the loop faster
2812
+ my = axisy.c2p(mouseY),
2813
+ maxx = maxDistance / axisx.scale,
2814
+ maxy = maxDistance / axisy.scale;
2815
+
2816
+ ps = s.datapoints.pointsize;
2817
+ // with inverse transforms, we can't use the maxx/maxy
2818
+ // optimization, sadly
2819
+ if (axisx.options.inverseTransform)
2820
+ maxx = Number.MAX_VALUE;
2821
+ if (axisy.options.inverseTransform)
2822
+ maxy = Number.MAX_VALUE;
2823
+
2824
+ if (s.lines.show || s.points.show) {
2825
+ for (j = 0; j < points.length; j += ps) {
2826
+ var x = points[j], y = points[j + 1];
2827
+ if (x == null)
2828
+ continue;
2829
+
2830
+ // For points and lines, the cursor must be within a
2831
+ // certain distance to the data point
2832
+ if (x - mx > maxx || x - mx < -maxx ||
2833
+ y - my > maxy || y - my < -maxy)
2834
+ continue;
2835
+
2836
+ // We have to calculate distances in pixels, not in
2837
+ // data units, because the scales of the axes may be different
2838
+ var dx = Math.abs(axisx.p2c(x) - mouseX),
2839
+ dy = Math.abs(axisy.p2c(y) - mouseY),
2840
+ dist = dx * dx + dy * dy; // we save the sqrt
2841
+
2842
+ // use <= to ensure last point takes precedence
2843
+ // (last generally means on top of)
2844
+ if (dist < smallestDistance) {
2845
+ smallestDistance = dist;
2846
+ item = [i, j / ps];
2847
+ }
2848
+ }
2849
+ }
2850
+
2851
+ if (s.bars.show && !item) { // no other point can be nearby
2852
+
2853
+ var barLeft, barRight;
2854
+
2855
+ switch (s.bars.align) {
2856
+ case "left":
2857
+ barLeft = 0;
2858
+ break;
2859
+ case "right":
2860
+ barLeft = -s.bars.barWidth;
2861
+ break;
2862
+ default:
2863
+ barLeft = -s.bars.barWidth / 2;
2864
+ }
2865
+
2866
+ barRight = barLeft + s.bars.barWidth;
2867
+
2868
+ for (j = 0; j < points.length; j += ps) {
2869
+ var x = points[j], y = points[j + 1], b = points[j + 2];
2870
+ if (x == null)
2871
+ continue;
2872
+
2873
+ // for a bar graph, the cursor must be inside the bar
2874
+ if (series[i].bars.horizontal ?
2875
+ (mx <= Math.max(b, x) && mx >= Math.min(b, x) &&
2876
+ my >= y + barLeft && my <= y + barRight) :
2877
+ (mx >= x + barLeft && mx <= x + barRight &&
2878
+ my >= Math.min(b, y) && my <= Math.max(b, y)))
2879
+ item = [i, j / ps];
2880
+ }
2881
+ }
2882
+ }
2883
+
2884
+ if (item) {
2885
+ i = item[0];
2886
+ j = item[1];
2887
+ ps = series[i].datapoints.pointsize;
2888
+
2889
+ return { datapoint: series[i].datapoints.points.slice(j * ps, (j + 1) * ps),
2890
+ dataIndex: j,
2891
+ series: series[i],
2892
+ seriesIndex: i };
2893
+ }
2894
+
2895
+ return null;
2896
+ }
2897
+
2898
+ function onMouseMove(e) {
2899
+ if (options.grid.hoverable)
2900
+ triggerClickHoverEvent("plothover", e,
2901
+ function (s) { return s["hoverable"] != false; });
2902
+ }
2903
+
2904
+ function onMouseLeave(e) {
2905
+ if (options.grid.hoverable)
2906
+ triggerClickHoverEvent("plothover", e,
2907
+ function (s) { return false; });
2908
+ }
2909
+
2910
+ function onClick(e) {
2911
+ triggerClickHoverEvent("plotclick", e,
2912
+ function (s) { return s["clickable"] != false; });
2913
+ }
2914
+
2915
+ // trigger click or hover event (they send the same parameters
2916
+ // so we share their code)
2917
+ function triggerClickHoverEvent(eventname, event, seriesFilter) {
2918
+ var offset = eventHolder.offset(),
2919
+ canvasX = event.pageX - offset.left - plotOffset.left,
2920
+ canvasY = event.pageY - offset.top - plotOffset.top,
2921
+ pos = canvasToAxisCoords({ left: canvasX, top: canvasY });
2922
+
2923
+ pos.pageX = event.pageX;
2924
+ pos.pageY = event.pageY;
2925
+
2926
+ var item = findNearbyItem(canvasX, canvasY, seriesFilter);
2927
+
2928
+ if (item) {
2929
+ // fill in mouse pos for any listeners out there
2930
+ item.pageX = parseInt(item.series.xaxis.p2c(item.datapoint[0]) + offset.left + plotOffset.left, 10);
2931
+ item.pageY = parseInt(item.series.yaxis.p2c(item.datapoint[1]) + offset.top + plotOffset.top, 10);
2932
+ }
2933
+
2934
+ if (options.grid.autoHighlight) {
2935
+ // clear auto-highlights
2936
+ for (var i = 0; i < highlights.length; ++i) {
2937
+ var h = highlights[i];
2938
+ if (h.auto == eventname &&
2939
+ !(item && h.series == item.series &&
2940
+ h.point[0] == item.datapoint[0] &&
2941
+ h.point[1] == item.datapoint[1]))
2942
+ unhighlight(h.series, h.point);
2943
+ }
2944
+
2945
+ if (item)
2946
+ highlight(item.series, item.datapoint, eventname);
2947
+ }
2948
+
2949
+ placeholder.trigger(eventname, [ pos, item ]);
2950
+ }
2951
+
2952
+ function triggerRedrawOverlay() {
2953
+ var t = options.interaction.redrawOverlayInterval;
2954
+ if (t == -1) { // skip event queue
2955
+ drawOverlay();
2956
+ return;
2957
+ }
2958
+
2959
+ if (!redrawTimeout)
2960
+ redrawTimeout = setTimeout(drawOverlay, t);
2961
+ }
2962
+
2963
+ function drawOverlay() {
2964
+ redrawTimeout = null;
2965
+
2966
+ // draw highlights
2967
+ octx.save();
2968
+ overlay.clear();
2969
+ octx.translate(plotOffset.left, plotOffset.top);
2970
+
2971
+ var i, hi;
2972
+ for (i = 0; i < highlights.length; ++i) {
2973
+ hi = highlights[i];
2974
+
2975
+ if (hi.series.bars.show)
2976
+ drawBarHighlight(hi.series, hi.point);
2977
+ else
2978
+ drawPointHighlight(hi.series, hi.point);
2979
+ }
2980
+ octx.restore();
2981
+
2982
+ executeHooks(hooks.drawOverlay, [octx]);
2983
+ }
2984
+
2985
+ function highlight(s, point, auto) {
2986
+ if (typeof s == "number")
2987
+ s = series[s];
2988
+
2989
+ if (typeof point == "number") {
2990
+ var ps = s.datapoints.pointsize;
2991
+ point = s.datapoints.points.slice(ps * point, ps * (point + 1));
2992
+ }
2993
+
2994
+ var i = indexOfHighlight(s, point);
2995
+ if (i == -1) {
2996
+ highlights.push({ series: s, point: point, auto: auto });
2997
+
2998
+ triggerRedrawOverlay();
2999
+ }
3000
+ else if (!auto)
3001
+ highlights[i].auto = false;
3002
+ }
3003
+
3004
+ function unhighlight(s, point) {
3005
+ if (s == null && point == null) {
3006
+ highlights = [];
3007
+ triggerRedrawOverlay();
3008
+ return;
3009
+ }
3010
+
3011
+ if (typeof s == "number")
3012
+ s = series[s];
3013
+
3014
+ if (typeof point == "number") {
3015
+ var ps = s.datapoints.pointsize;
3016
+ point = s.datapoints.points.slice(ps * point, ps * (point + 1));
3017
+ }
3018
+
3019
+ var i = indexOfHighlight(s, point);
3020
+ if (i != -1) {
3021
+ highlights.splice(i, 1);
3022
+
3023
+ triggerRedrawOverlay();
3024
+ }
3025
+ }
3026
+
3027
+ function indexOfHighlight(s, p) {
3028
+ for (var i = 0; i < highlights.length; ++i) {
3029
+ var h = highlights[i];
3030
+ if (h.series == s && h.point[0] == p[0]
3031
+ && h.point[1] == p[1])
3032
+ return i;
3033
+ }
3034
+ return -1;
3035
+ }
3036
+
3037
+ function drawPointHighlight(series, point) {
3038
+ var x = point[0], y = point[1],
3039
+ axisx = series.xaxis, axisy = series.yaxis,
3040
+ highlightColor = (typeof series.highlightColor === "string") ? series.highlightColor : $.color.parse(series.color).scale('a', 0.5).toString();
3041
+
3042
+ if (x < axisx.min || x > axisx.max || y < axisy.min || y > axisy.max)
3043
+ return;
3044
+
3045
+ var pointRadius = series.points.radius + series.points.lineWidth / 2;
3046
+ octx.lineWidth = pointRadius;
3047
+ octx.strokeStyle = highlightColor;
3048
+ var radius = 1.5 * pointRadius;
3049
+ x = axisx.p2c(x);
3050
+ y = axisy.p2c(y);
3051
+
3052
+ octx.beginPath();
3053
+ if (series.points.symbol == "circle")
3054
+ octx.arc(x, y, radius, 0, 2 * Math.PI, false);
3055
+ else
3056
+ series.points.symbol(octx, x, y, radius, false);
3057
+ octx.closePath();
3058
+ octx.stroke();
3059
+ }
3060
+
3061
+ function drawBarHighlight(series, point) {
3062
+ var highlightColor = (typeof series.highlightColor === "string") ? series.highlightColor : $.color.parse(series.color).scale('a', 0.5).toString(),
3063
+ fillStyle = highlightColor,
3064
+ barLeft;
3065
+
3066
+ switch (series.bars.align) {
3067
+ case "left":
3068
+ barLeft = 0;
3069
+ break;
3070
+ case "right":
3071
+ barLeft = -series.bars.barWidth;
3072
+ break;
3073
+ default:
3074
+ barLeft = -series.bars.barWidth / 2;
3075
+ }
3076
+
3077
+ octx.lineWidth = series.bars.lineWidth;
3078
+ octx.strokeStyle = highlightColor;
3079
+
3080
+ drawBar(point[0], point[1], point[2] || 0, barLeft, barLeft + series.bars.barWidth,
3081
+ function () { return fillStyle; }, series.xaxis, series.yaxis, octx, series.bars.horizontal, series.bars.lineWidth);
3082
+ }
3083
+
3084
+ function getColorOrGradient(spec, bottom, top, defaultColor) {
3085
+ if (typeof spec == "string")
3086
+ return spec;
3087
+ else {
3088
+ // assume this is a gradient spec; IE currently only
3089
+ // supports a simple vertical gradient properly, so that's
3090
+ // what we support too
3091
+ var gradient = ctx.createLinearGradient(0, top, 0, bottom);
3092
+
3093
+ for (var i = 0, l = spec.colors.length; i < l; ++i) {
3094
+ var c = spec.colors[i];
3095
+ if (typeof c != "string") {
3096
+ var co = $.color.parse(defaultColor);
3097
+ if (c.brightness != null)
3098
+ co = co.scale('rgb', c.brightness);
3099
+ if (c.opacity != null)
3100
+ co.a *= c.opacity;
3101
+ c = co.toString();
3102
+ }
3103
+ gradient.addColorStop(i / (l - 1), c);
3104
+ }
3105
+
3106
+ return gradient;
3107
+ }
3108
+ }
3109
+ }
3110
+
3111
+ // Add the plot function to the top level of the jQuery object
3112
+
3113
+ $.plot = function(placeholder, data, options) {
3114
+ //var t0 = new Date();
3115
+ var plot = new Plot($(placeholder), data, options, $.plot.plugins);
3116
+ //(window.console ? console.log : alert)("time used (msecs): " + ((new Date()).getTime() - t0.getTime()));
3117
+ return plot;
3118
+ };
3119
+
3120
+ $.plot.version = "0.8.2";
3121
+
3122
+ $.plot.plugins = [];
3123
+
3124
+ // Also add the plot function as a chainable property
3125
+
3126
+ $.fn.plot = function(data, options) {
3127
+ return this.each(function() {
3128
+ $.plot(this, data, options);
3129
+ });
3130
+ };
3131
+
3132
+ // round to nearby lower multiple of base
3133
+ function floorInBase(n, base) {
3134
+ return base * Math.floor(n / base);
3135
+ }
3136
+
3137
+ })(jQuery);