├── .gitignore ├── site ├── assets │ ├── html.png │ ├── simple.png │ ├── 6charts.png │ ├── effects.js │ └── excanvas.js ├── styles.css └── index.html ├── component.json ├── readme.md ├── samples ├── pie.html ├── doughnut.html ├── bar.html ├── polarArea.html ├── line.html ├── radar.html └── sixup.html ├── LICENSE.md ├── docs ├── prettify.less ├── prettify.css ├── styles.less ├── styles.css ├── prettify.js └── index.html ├── Chart.min.js └── Chart.js /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /site/assets/html.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wikunia/Chart.js/master/site/assets/html.png -------------------------------------------------------------------------------- /site/assets/simple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wikunia/Chart.js/master/site/assets/simple.png -------------------------------------------------------------------------------- /site/assets/6charts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wikunia/Chart.js/master/site/assets/6charts.png -------------------------------------------------------------------------------- /component.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Chart.js", 3 | "version": "0.2.0", 4 | "description": "Simple HTML5 Charts using the canvas element", 5 | "keywords": ["charts"], 6 | "homepage": "https://github.com/nnnick/Chart.js", 7 | "author": "nnnick", 8 | "main": ["Chart.min.js"], 9 | "dependencies": { 10 | } 11 | } -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | Chart.js 2 | ======= 3 | *Simple HTML5 Charts using the canvas element* [chartjs.org](http://www.chartjs.org) 4 | 5 | Competition! 6 | ------- 7 | The guys at ChallengePost are running a competition to design and build a personal dashboard using Chart.js, and are offering some great prizes for winning. Take a look at [chartjs.challengepost.com](http://chartjs.challengepost.com/). 8 | 9 | Documentation 10 | ------- 11 | You can find documentation at [chartjs.org/docs](http://www.chartjs.org/docs). 12 | 13 | License 14 | ------- 15 | Chart.js was taken down on the 19th March. It is now back online for good and IS available under MIT license. 16 | 17 | Chart.js is available under the [MIT license] (http://opensource.org/licenses/MIT). 18 | -------------------------------------------------------------------------------- /samples/pie.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Radar Chart 5 | 6 | 7 | 11 | 12 | 13 | 14 | 15 | 16 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /samples/doughnut.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Doughnut Chart 5 | 6 | 7 | 11 | 12 | 13 | 14 | 15 | 16 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /samples/bar.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Bar Chart 5 | 6 | 7 | 11 | 12 | 13 | 14 | 15 | 16 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /samples/polarArea.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Polar Area Chart 5 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | 14 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Nick Downie 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /samples/line.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Line Chart 5 | 6 | 7 | 11 | 12 | 13 | 14 | 15 | 16 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /samples/radar.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Radar Chart 5 | 6 | 7 | 11 | 12 | 13 | 14 | 15 | 16 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /docs/prettify.less: -------------------------------------------------------------------------------- 1 | /* Pretty printing styles. Used with prettify.js. */ 2 | /* Vim sunburst theme by David Leibovic */ 3 | 4 | pre .str{ color: #65B042; } /* string - green */ 5 | pre .kwd{ color: #E28964; } /* keyword - dark pink */ 6 | pre .com{ color: #AEAEAE; font-style: italic; } /* comment - gray */ 7 | pre .typ{ color: #89bdff; } /* type - light blue */ 8 | pre .lit{ color: #3387CC; } /* literal - blue */ 9 | pre .pun{ color: #fff; } /* punctuation - white */ 10 | pre .pln{ color: #fff; } /* plaintext - white */ 11 | pre .tag{ color: #89bdff; } /* html/xml tag - light blue */ 12 | pre .atn{ color: #bdb76b; } /* html/xml attribute name - khaki */ 13 | pre .atv{ color: #65B042; } /* html/xml attribute value - green */ 14 | pre .dec{ color: #3387CC; } /* decimal - blue */ 15 | 16 | /* Specify class=linenums on a pre to get line numbering */ 17 | ol.linenums { margin-top: 0; margin-bottom: 0; color: #AEAEAE; } /* IE indents via margin-left */ 18 | li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8 { list-style-type: none } 19 | /* Alternate shading for lines */ 20 | li.L1,li.L3,li.L5,li.L7,li.L9 { } 21 | 22 | @media print { 23 | pre .str{ color: #060; } 24 | pre .kwd{ color: #006; font-weight: bold; } 25 | pre .com{ color: #600; font-style: italic; } 26 | pre .typ{ color: #404; font-weight: bold; } 27 | pre .lit{ color: #044; } 28 | pre .pun{ color: #440; } 29 | pre .pln{ color: #000; } 30 | pre .tag{ color: #006; font-weight: bold; } 31 | pre .atn{ color: #404; } 32 | pre .atv{ color: #060; } 33 | } -------------------------------------------------------------------------------- /docs/prettify.css: -------------------------------------------------------------------------------- 1 | /* Pretty printing styles. Used with prettify.js. */ 2 | /* Vim sunburst theme by David Leibovic */ 3 | 4 | pre .str, code .str { color: #65B042; } /* string - green */ 5 | pre .kwd, code .kwd { color: #E28964; } /* keyword - dark pink */ 6 | pre .com, code .com { color: #AEAEAE; font-style: italic; } /* comment - gray */ 7 | pre .typ, code .typ { color: #89bdff; } /* type - light blue */ 8 | pre .lit, code .lit { color: #3387CC; } /* literal - blue */ 9 | pre .pun, code .pun { color: #fff; } /* punctuation - white */ 10 | pre .pln, code .pln { color: #fff; } /* plaintext - white */ 11 | pre .tag, code .tag { color: #89bdff; } /* html/xml tag - light blue */ 12 | pre .atn, code .atn { color: #bdb76b; } /* html/xml attribute name - khaki */ 13 | pre .atv, code .atv { color: #65B042; } /* html/xml attribute value - green */ 14 | pre .dec, code .dec { color: #3387CC; } /* decimal - blue */ 15 | 16 | pre.prettyprint, code.prettyprint { 17 | background-color: #000; 18 | -moz-border-radius: 8px; 19 | -webkit-border-radius: 8px; 20 | -o-border-radius: 8px; 21 | -ms-border-radius: 8px; 22 | -khtml-border-radius: 8px; 23 | border-radius: 8px; 24 | } 25 | 26 | pre.prettyprint { 27 | width: 95%; 28 | margin: 1em auto; 29 | padding: 1em; 30 | white-space: pre-wrap; 31 | } 32 | 33 | 34 | /* Specify class=linenums on a pre to get line numbering */ 35 | ol.linenums { margin-top: 0; margin-bottom: 0; color: #AEAEAE; } /* IE indents via margin-left */ 36 | li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8 { list-style-type: none } 37 | /* Alternate shading for lines */ 38 | li.L1,li.L3,li.L5,li.L7,li.L9 { } 39 | -------------------------------------------------------------------------------- /docs/styles.less: -------------------------------------------------------------------------------- 1 | @import "prettify"; 2 | 3 | @codeBackground : #292B36; 4 | 5 | @primaryFont : "proxima-nova"; 6 | 7 | @textColour : #282B36; 8 | @secondaryTextColour : #767c8d; 9 | 10 | @borderPaleColour : #EBEBEB; 11 | @pageBorderColour : @textColour; 12 | 13 | @red : #F33E6F; 14 | @green : #46BFBD; 15 | @yellow : #FDB45C; 16 | @blue : #2D91EA; 17 | 18 | *{ 19 | padding:0; 20 | margin:0; 21 | box-sizing:border-box; 22 | -moz-box-sizing:border-box; 23 | -webkit-box-sizing:border-box; 24 | color:inherit; 25 | text-rendering: optimizeLegibility; 26 | } 27 | 28 | body{ 29 | color: @textColour; 30 | min-width: 768px; 31 | } 32 | 33 | .redBorder,.greenBorder,.yellowBorder{ 34 | border-top: 8px solid @pageBorderColour; 35 | width: 33.33%; 36 | float: left; 37 | height: 16px; 38 | position: relative; 39 | z-index:50; 40 | } 41 | .redBorder{ 42 | background-color: @red; 43 | } 44 | .greenBorder{ 45 | background-color: @green; 46 | } 47 | .yellowBorder{ 48 | background-color: @yellow; 49 | } 50 | 51 | h1{ 52 | font-family: @primaryFont; 53 | font-weight: 600; 54 | font-size: 32px; 55 | } 56 | h2{ 57 | font-family: @primaryFont; 58 | font-weight: 600; 59 | font-size: 22px; 60 | line-height: 40px; 61 | } 62 | #mainHeader{ 63 | font-size: 55px; 64 | } 65 | #introText{ 66 | font-weight: 400; 67 | margin-top: 20px; 68 | font-size: 26px; 69 | line-height: 40px; 70 | margin-bottom: 40px; 71 | } 72 | #wrapper{ 73 | margin: 0 auto; 74 | position: relative; 75 | min-width: 768px; 76 | nav{ 77 | width: 20%; 78 | padding-right: 20px; 79 | position: fixed; 80 | height: 100%; 81 | overflow-y: scroll; 82 | top: 0; 83 | z-index: 0; 84 | padding: 40px 20px; 85 | font-family: @primaryFont; 86 | background-color: @borderPaleColour; 87 | 88 | dl{ 89 | color: @secondaryTextColour; 90 | dt{ 91 | list-style: none; 92 | margin-top: 10px; 93 | margin-bottom: 5px; 94 | a{ 95 | display: block; 96 | padding: 2px 0; 97 | border-bottom: 1px solid fade(@secondaryTextColour,20%); 98 | text-decoration: none; 99 | } 100 | } 101 | dd{ 102 | margin-bottom: 5px; 103 | padding-left: 5px; 104 | &:before{ 105 | content: "- "; 106 | 107 | } 108 | a{ 109 | text-decoration:none; 110 | font-size: 12px; 111 | border-bottom: 1px solid transparent; 112 | } 113 | } 114 | a{ 115 | -webkit-transition: all 200ms ease-in-out; 116 | -moz-transition: all 200ms ease-in-out; 117 | -o-transition: all 200ms ease-in-out; 118 | -ms-transition: all 200ms ease-in-out; 119 | transition: all 200ms ease-in-out; 120 | &:hover{ 121 | color: @blue; 122 | border-bottom-color:@blue; 123 | } 124 | } 125 | 126 | } 127 | } 128 | #contentWrapper{ 129 | width: 80%; 130 | max-width: 1080px; 131 | margin-left: 20%; 132 | padding: 0px 40px; 133 | padding-top: 72px; 134 | } 135 | } 136 | article{ 137 | border-top: 1px solid @borderPaleColour; 138 | padding: 40px 0; 139 | h2{ 140 | margin-top: 20px; 141 | } 142 | } 143 | 144 | p,ul li{ 145 | font-family: @primaryFont; 146 | line-height: 20px; 147 | font-size: 16px; 148 | margin-top: 10px; 149 | color: @secondaryTextColour; 150 | a{ 151 | text-decoration: none; 152 | border-bottom: 1px solid @blue; 153 | color: @blue; 154 | } 155 | } 156 | 157 | canvas{ 158 | margin-top: 20px; 159 | } 160 | pre{ 161 | background-color: @codeBackground; 162 | padding: 10px; 163 | border-radius: 5px; 164 | position: relative; 165 | -webkit-font-smoothing: antialiased; 166 | margin: 40px 0 20px 0; 167 | code{ 168 | display: block; 169 | } 170 | &:before{ 171 | content: attr(data-type); 172 | position: absolute; 173 | font-size: 12px; 174 | top: -30px; 175 | left: 0; 176 | font-family: @primaryFont; 177 | font-weight: 400; 178 | display: inline-block; 179 | padding: 2px 5px; 180 | border-radius: 5px; 181 | background-color: @borderPaleColour; 182 | } 183 | } 184 | p{ 185 | } -------------------------------------------------------------------------------- /samples/sixup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Doughnut Chart 5 | 6 | 7 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 154 | 155 | 156 | -------------------------------------------------------------------------------- /site/styles.css: -------------------------------------------------------------------------------- 1 | * { 2 | padding: 0; 3 | margin: 0; 4 | color: inherit; 5 | -webkit-font-smoothing: antialiased; 6 | text-rendering: optimizeLegibility; 7 | } 8 | body { 9 | color: #282b36; 10 | border-top: 8px solid #282b36; 11 | } 12 | canvas { 13 | font-family: "proxima-nova", sans-serif sans-serif; 14 | } 15 | .redBorder, 16 | .greenBorder, 17 | .yellowBorder { 18 | width: 33.33%; 19 | float: left; 20 | height: 8px; 21 | } 22 | .redBorder { 23 | background-color: #f33e6f; 24 | } 25 | .greenBorder { 26 | background-color: #46bfbd; 27 | } 28 | .yellowBorder { 29 | background-color: #fdb45c; 30 | } 31 | h1 { 32 | font-family: "proxima-nova", sans-serif; 33 | font-weight: 600; 34 | font-size: 55px; 35 | margin-top: 40px; 36 | } 37 | h2 { 38 | font-family: "proxima-nova", sans-serif; 39 | font-weight: 400; 40 | margin-top: 20px; 41 | font-size: 26px; 42 | line-height: 40px; 43 | } 44 | h3 { 45 | font-family: "proxima-nova", sans-serif; 46 | font-weight: 600; 47 | text-align: center; 48 | margin: 20px 0; 49 | } 50 | h3 a { 51 | color: #2d91ea; 52 | text-decoration: none; 53 | border-bottom: 1px solid #2d91ea; 54 | } 55 | p { 56 | font-family: "proxima-nova", sans-serif; 57 | line-height: 24px; 58 | font-size: 18px; 59 | color: #767c8d; 60 | } 61 | .btn { 62 | display: inline-block; 63 | padding: 20px; 64 | font-family: "proxima-nova", sans-serif; 65 | font-weight: 600; 66 | color: #fff; 67 | text-decoration: none; 68 | border-radius: 5px; 69 | text-align: center; 70 | font-size: 18px; 71 | -webkit-transition-property: background-color box-shadow; 72 | -webkit-transition-duration: 200ms; 73 | -webkit-transition-timing-function: ease-in-out; 74 | -moz-transition-property: background-color box-shadow; 75 | -moz-transition-duration: 200ms; 76 | -moz-transition-timing-function: ease-in-out; 77 | -ms-transition-property: background-color box-shadow; 78 | -ms-transition-duration: 200ms; 79 | -ms-transition-timing-function: ease-in-out; 80 | -o-transition-property: background-color box-shadow; 81 | -o-transition-duration: 200ms; 82 | -o-transition-timing-function: ease-in-out; 83 | transition-property: background-color box-shadow; 84 | transition-duration: 200ms; 85 | transition-timing-function: ease-in-out; 86 | } 87 | .btn:active { 88 | box-shadow: inset 1px 1px 4px rgba(0, 0, 0, 0.25); 89 | } 90 | .btn.red { 91 | background-color: #f33e6f; 92 | } 93 | .btn.red:hover { 94 | background-color: #f2265d; 95 | } 96 | .btn.blue { 97 | background-color: #2d91ea; 98 | } 99 | .btn.blue:hover { 100 | background-color: #1785e6; 101 | } 102 | header { 103 | width: 978px; 104 | margin: 20px auto; 105 | display: block; 106 | position: relative; 107 | } 108 | header hgroup { 109 | width: 50%; 110 | padding: 20px 0; 111 | } 112 | header #introChart { 113 | position: absolute; 114 | top: 60px; 115 | right: 0; 116 | } 117 | header .btn { 118 | margin-right: 10px; 119 | width: 180px; 120 | } 121 | footer { 122 | width: 100%; 123 | text-align: center; 124 | background-color: #ebebeb; 125 | } 126 | footer p { 127 | color: #767c8d; 128 | font-family: "proxima-nova", sans-serif; 129 | font-size: 16px; 130 | padding: 20px 0; 131 | } 132 | section { 133 | width: 978px; 134 | margin: 40px auto; 135 | } 136 | section:before { 137 | content: ''; 138 | width: 600px; 139 | margin: 0 auto; 140 | border-top: 1px solid #ebebeb; 141 | height: 20px; 142 | display: block; 143 | } 144 | section:after { 145 | content: ""; 146 | display: table; 147 | clear: both; 148 | } 149 | *section { 150 | zoom: 1; 151 | } 152 | #features article { 153 | width: 33.33%; 154 | float: left; 155 | } 156 | #features article p { 157 | display: block; 158 | width: 90%; 159 | margin: 0 auto; 160 | text-align: center; 161 | } 162 | #features article img { 163 | width: 250px; 164 | height: 250px; 165 | margin: 0 auto; 166 | display: block; 167 | } 168 | #examples article { 169 | -webkit-transition: opacity 200ms ease-in-out; 170 | -ms-transition: opacity 200ms ease-in-out; 171 | -moz-transition: opacity 200ms ease-in-out; 172 | -o-transition: opacity 200ms ease-in-out; 173 | transition: opacity 200ms ease-in-out; 174 | position: relative; 175 | margin-top: 20px; 176 | clear: both; 177 | } 178 | #examples article:after { 179 | content: ''; 180 | width: 600px; 181 | padding-top: 40px; 182 | margin: 40px auto; 183 | border-bottom: 1px solid #ebebeb; 184 | height: 20px; 185 | display: block; 186 | clear: both; 187 | } 188 | #examples article p { 189 | margin-top: 10px; 190 | } 191 | #examples article .half { 192 | width: 50%; 193 | float: left; 194 | } 195 | #examples article .canvasWrapper { 196 | float: left; 197 | width: 449px; 198 | padding: 0 20px; 199 | } 200 | #examples h3 { 201 | clear: both; 202 | } 203 | #examples .hidden { 204 | opacity: 0; 205 | } 206 | -------------------------------------------------------------------------------- /docs/styles.css: -------------------------------------------------------------------------------- 1 | /* Pretty printing styles. Used with prettify.js. */ 2 | /* Vim sunburst theme by David Leibovic */ 3 | pre .str { 4 | color: #65B042; 5 | } 6 | /* string - green */ 7 | pre .kwd { 8 | color: #E28964; 9 | } 10 | /* keyword - dark pink */ 11 | pre .com { 12 | color: #AEAEAE; 13 | font-style: italic; 14 | } 15 | /* comment - gray */ 16 | pre .typ { 17 | color: #89bdff; 18 | } 19 | /* type - light blue */ 20 | pre .lit { 21 | color: #3387CC; 22 | } 23 | /* literal - blue */ 24 | pre .pun { 25 | color: #fff; 26 | } 27 | /* punctuation - white */ 28 | pre .pln { 29 | color: #fff; 30 | } 31 | /* plaintext - white */ 32 | pre .tag { 33 | color: #89bdff; 34 | } 35 | /* html/xml tag - light blue */ 36 | pre .atn { 37 | color: #bdb76b; 38 | } 39 | /* html/xml attribute name - khaki */ 40 | pre .atv { 41 | color: #65B042; 42 | } 43 | /* html/xml attribute value - green */ 44 | pre .dec { 45 | color: #3387CC; 46 | } 47 | /* decimal - blue */ 48 | /* Specify class=linenums on a pre to get line numbering */ 49 | ol.linenums { 50 | margin-top: 0; 51 | margin-bottom: 0; 52 | color: #AEAEAE; 53 | } 54 | /* IE indents via margin-left */ 55 | li.L0, 56 | li.L1, 57 | li.L2, 58 | li.L3, 59 | li.L5, 60 | li.L6, 61 | li.L7, 62 | li.L8 { 63 | list-style-type: none; 64 | } 65 | /* Alternate shading for lines */ 66 | @media print { 67 | pre .str { 68 | color: #060; 69 | } 70 | pre .kwd { 71 | color: #006; 72 | font-weight: bold; 73 | } 74 | pre .com { 75 | color: #600; 76 | font-style: italic; 77 | } 78 | pre .typ { 79 | color: #404; 80 | font-weight: bold; 81 | } 82 | pre .lit { 83 | color: #044; 84 | } 85 | pre .pun { 86 | color: #440; 87 | } 88 | pre .pln { 89 | color: #000; 90 | } 91 | pre .tag { 92 | color: #006; 93 | font-weight: bold; 94 | } 95 | pre .atn { 96 | color: #404; 97 | } 98 | pre .atv { 99 | color: #060; 100 | } 101 | } 102 | * { 103 | padding: 0; 104 | margin: 0; 105 | box-sizing: border-box; 106 | -moz-box-sizing: border-box; 107 | -webkit-box-sizing: border-box; 108 | color: inherit; 109 | text-rendering: optimizeLegibility; 110 | } 111 | body { 112 | color: #282b36; 113 | min-width: 768px; 114 | } 115 | .redBorder, 116 | .greenBorder, 117 | .yellowBorder { 118 | border-top: 8px solid #282b36; 119 | width: 33.33%; 120 | float: left; 121 | height: 16px; 122 | position: relative; 123 | z-index: 50; 124 | } 125 | .redBorder { 126 | background-color: #f33e6f; 127 | } 128 | .greenBorder { 129 | background-color: #46bfbd; 130 | } 131 | .yellowBorder { 132 | background-color: #fdb45c; 133 | } 134 | h1 { 135 | font-family: "proxima-nova"; 136 | font-weight: 600; 137 | font-size: 32px; 138 | } 139 | h2 { 140 | font-family: "proxima-nova"; 141 | font-weight: 600; 142 | font-size: 22px; 143 | line-height: 40px; 144 | } 145 | #mainHeader { 146 | font-size: 55px; 147 | } 148 | #introText { 149 | font-weight: 400; 150 | margin-top: 20px; 151 | font-size: 26px; 152 | line-height: 40px; 153 | margin-bottom: 40px; 154 | } 155 | #wrapper { 156 | margin: 0 auto; 157 | position: relative; 158 | min-width: 768px; 159 | } 160 | #wrapper nav { 161 | width: 20%; 162 | padding-right: 20px; 163 | position: fixed; 164 | height: 100%; 165 | overflow-y: scroll; 166 | top: 0; 167 | z-index: 0; 168 | padding: 40px 20px; 169 | font-family: "proxima-nova"; 170 | background-color: #ebebeb; 171 | } 172 | #wrapper nav dl { 173 | color: #767c8d; 174 | } 175 | #wrapper nav dl dt { 176 | list-style: none; 177 | margin-top: 10px; 178 | margin-bottom: 5px; 179 | } 180 | #wrapper nav dl dt a { 181 | display: block; 182 | padding: 2px 0; 183 | border-bottom: 1px solid rgba(118, 124, 141, 0.2); 184 | text-decoration: none; 185 | } 186 | #wrapper nav dl dd { 187 | margin-bottom: 5px; 188 | padding-left: 5px; 189 | } 190 | #wrapper nav dl dd:before { 191 | content: "- "; 192 | } 193 | #wrapper nav dl dd a { 194 | text-decoration: none; 195 | font-size: 12px; 196 | border-bottom: 1px solid transparent; 197 | } 198 | #wrapper nav dl a { 199 | -webkit-transition: all 200ms ease-in-out; 200 | -moz-transition: all 200ms ease-in-out; 201 | -o-transition: all 200ms ease-in-out; 202 | -ms-transition: all 200ms ease-in-out; 203 | transition: all 200ms ease-in-out; 204 | } 205 | #wrapper nav dl a:hover { 206 | color: #2d91ea; 207 | border-bottom-color: #2d91ea; 208 | } 209 | #wrapper #contentWrapper { 210 | width: 80%; 211 | max-width: 1080px; 212 | margin-left: 20%; 213 | padding: 0px 40px; 214 | padding-top: 72px; 215 | } 216 | article { 217 | border-top: 1px solid #ebebeb; 218 | padding: 40px 0; 219 | } 220 | article h2 { 221 | margin-top: 20px; 222 | } 223 | p, 224 | ul li { 225 | font-family: "proxima-nova"; 226 | line-height: 20px; 227 | font-size: 16px; 228 | margin-top: 10px; 229 | color: #767c8d; 230 | } 231 | p a, 232 | ul li a { 233 | text-decoration: none; 234 | border-bottom: 1px solid #2d91ea; 235 | color: #2d91ea; 236 | } 237 | canvas { 238 | margin-top: 20px; 239 | } 240 | pre { 241 | background-color: #292b36; 242 | padding: 10px; 243 | border-radius: 5px; 244 | position: relative; 245 | -webkit-font-smoothing: antialiased; 246 | margin: 40px 0 20px 0; 247 | } 248 | pre code { 249 | display: block; 250 | } 251 | pre:before { 252 | content: attr(data-type); 253 | position: absolute; 254 | font-size: 12px; 255 | top: -30px; 256 | left: 0; 257 | font-family: "proxima-nova"; 258 | font-weight: 400; 259 | display: inline-block; 260 | padding: 2px 5px; 261 | border-radius: 5px; 262 | background-color: #ebebeb; 263 | } 264 | -------------------------------------------------------------------------------- /site/assets/effects.js: -------------------------------------------------------------------------------- 1 | $(window).load(function() { 2 | var lineChartData = { 3 | labels : ["January","February","March","April","May","June","July"], 4 | datasets : [ 5 | { 6 | fillColor : "rgba(220,220,220,0.5)", 7 | strokeColor : "rgba(220,220,220,1)", 8 | pointColor : "rgba(220,220,220,1)", 9 | pointStrokeColor : "#fff", 10 | data : [65,59,90,81,56,55,40] 11 | }, 12 | { 13 | fillColor : "rgba(151,187,205,0.5)", 14 | strokeColor : "rgba(151,187,205,1)", 15 | pointColor : "rgba(151,187,205,1)", 16 | pointStrokeColor : "#fff", 17 | data : [28,48,40,19,96,27,100] 18 | } 19 | ] 20 | }; 21 | 22 | var barChartData = { 23 | labels : ["January","February","March","April","May","June","July"], 24 | datasets : [ 25 | { 26 | fillColor : "rgba(220,220,220,0.5)", 27 | strokeColor : "rgba(220,220,220,1)", 28 | data : [65,59,90,81,56,55,40] 29 | }, 30 | { 31 | fillColor : "rgba(151,187,205,0.5)", 32 | strokeColor : "rgba(151,187,205,1)", 33 | data : [28,48,40,19,96,27,100] 34 | } 35 | ] 36 | 37 | }; 38 | 39 | var radarChartData = { 40 | labels : ["A","B","C","D","E","F","G"], 41 | datasets : [ 42 | { 43 | fillColor : "rgba(220,220,220,0.5)", 44 | strokeColor : "rgba(220,220,220,1)", 45 | pointColor : "rgba(220,220,220,1)", 46 | pointStrokeColor : "#fff", 47 | data : [65,59,90,81,56,55,40] 48 | }, 49 | { 50 | fillColor : "rgba(151,187,205,0.5)", 51 | strokeColor : "rgba(151,187,205,1)", 52 | pointColor : "rgba(151,187,205,1)", 53 | pointStrokeColor : "#fff", 54 | data : [28,48,40,19,96,27,100] 55 | } 56 | ] 57 | 58 | }; 59 | var pieChartData = [ 60 | { 61 | value: 30, 62 | color:"#F38630" 63 | }, 64 | { 65 | value : 50, 66 | color : "#E0E4CC" 67 | }, 68 | { 69 | value : 100, 70 | color : "#69D2E7" 71 | } 72 | 73 | ]; 74 | var polarAreaChartData = [ 75 | { 76 | value : 62, 77 | color: "#D97041" 78 | }, 79 | { 80 | value : 70, 81 | color: "#C7604C" 82 | }, 83 | { 84 | value : 41, 85 | color: "#21323D" 86 | }, 87 | { 88 | value : 24, 89 | color: "#9D9B7F" 90 | }, 91 | { 92 | value : 55, 93 | color: "#7D4F6D" 94 | }, 95 | { 96 | value : 18, 97 | color: "#584A5E" 98 | } 99 | ]; 100 | var doughnutChartData = [ 101 | { 102 | value: 30, 103 | color:"#F7464A" 104 | }, 105 | { 106 | value : 50, 107 | color : "#46BFBD" 108 | }, 109 | { 110 | value : 100, 111 | color : "#FDB45C" 112 | }, 113 | { 114 | value : 40, 115 | color : "#949FB1" 116 | }, 117 | { 118 | value : 120, 119 | color : "#4D5360" 120 | } 121 | 122 | ]; 123 | 124 | var globalGraphSettings = {animation : Modernizr.canvas}; 125 | 126 | setIntroChart(); 127 | 128 | function setIntroChart(){ 129 | var ctx = document.getElementById("introChart").getContext("2d"); 130 | 131 | new Chart(ctx).Line(lineChartData,{animation: Modernizr.canvas, scaleShowLabels : false, scaleFontColor : "#767C8D"}); 132 | }; 133 | 134 | function showLineChart(){ 135 | var ctx = document.getElementById("lineChartCanvas").getContext("2d"); 136 | new Chart(ctx).Line(lineChartData,globalGraphSettings); 137 | }; 138 | function showBarChart(){ 139 | var ctx = document.getElementById("barChartCanvas").getContext("2d"); 140 | new Chart(ctx).Bar(barChartData,globalGraphSettings); 141 | }; 142 | function showRadarChart(){ 143 | var ctx = document.getElementById("radarChartCanvas").getContext("2d"); 144 | new Chart(ctx).Radar(radarChartData,globalGraphSettings); 145 | } 146 | function showPolarAreaChart(){ 147 | var ctx = document.getElementById("polarAreaChartCanvas").getContext("2d"); 148 | new Chart(ctx).PolarArea(polarAreaChartData,globalGraphSettings); 149 | } 150 | function showPieChart(){ 151 | var ctx = document.getElementById("pieChartCanvas").getContext("2d"); 152 | new Chart(ctx).Pie(pieChartData,globalGraphSettings); 153 | }; 154 | function showDoughnutChart(){ 155 | var ctx = document.getElementById("doughnutChartCanvas").getContext("2d"); 156 | new Chart(ctx).Doughnut(doughnutChartData,globalGraphSettings); 157 | }; 158 | 159 | var graphInitDelay = 300; 160 | 161 | //Set up each of the inview events here. 162 | $("#lineChart").on("inview",function(){ 163 | var $this = $(this); 164 | $this.removeClass("hidden").off("inview"); 165 | setTimeout(showLineChart,graphInitDelay); 166 | }); 167 | $("#barChart").on("inview",function(){ 168 | var $this = $(this); 169 | $this.removeClass("hidden").off("inview"); 170 | setTimeout(showBarChart,graphInitDelay); 171 | }); 172 | 173 | $("#radarChart").on("inview",function(){ 174 | var $this = $(this); 175 | $this.removeClass("hidden").off("inview"); 176 | setTimeout(showRadarChart,graphInitDelay); 177 | }); 178 | $("#pieChart").on("inview",function(){ 179 | var $this = $(this); 180 | $this.removeClass("hidden").off("inview"); 181 | setTimeout(showPieChart,graphInitDelay); 182 | }); 183 | $("#polarAreaChart").on("inview",function(){ 184 | var $this = $(this); 185 | $this.removeClass("hidden").off("inview"); 186 | setTimeout(showPolarAreaChart,graphInitDelay); 187 | }); 188 | $("#doughnutChart").on("inview",function(){ 189 | var $this = $(this); 190 | $this.removeClass("hidden").off("inview"); 191 | setTimeout(showDoughnutChart,graphInitDelay); 192 | }); 193 | 194 | }); 195 | 196 | /** 197 | * author Christopher Blum 198 | * - based on the idea of Remy Sharp, http://remysharp.com/2009/01/26/element-in-view-event-plugin/ 199 | * - forked from http://github.com/zuk/jquery.inview/ 200 | */ 201 | (function ($) { 202 | var inviewObjects = {}, viewportSize, viewportOffset, 203 | d = document, w = window, documentElement = d.documentElement, expando = $.expando; 204 | 205 | $.event.special.inview = { 206 | add: function(data) { 207 | inviewObjects[data.guid + "-" + this[expando]] = { data: data, $element: $(this) }; 208 | }, 209 | 210 | remove: function(data) { 211 | try { delete inviewObjects[data.guid + "-" + this[expando]]; } catch(e) {} 212 | } 213 | }; 214 | 215 | function getViewportSize() { 216 | var mode, domObject, size = { height: w.innerHeight, width: w.innerWidth }; 217 | 218 | // if this is correct then return it. iPad has compat Mode, so will 219 | // go into check clientHeight/clientWidth (which has the wrong value). 220 | if (!size.height) { 221 | mode = d.compatMode; 222 | if (mode || !$.support.boxModel) { // IE, Gecko 223 | domObject = mode === 'CSS1Compat' ? 224 | documentElement : // Standards 225 | d.body; // Quirks 226 | size = { 227 | height: domObject.clientHeight, 228 | width: domObject.clientWidth 229 | }; 230 | } 231 | } 232 | 233 | return size; 234 | } 235 | 236 | function getViewportOffset() { 237 | return { 238 | top: w.pageYOffset || documentElement.scrollTop || d.body.scrollTop, 239 | left: w.pageXOffset || documentElement.scrollLeft || d.body.scrollLeft 240 | }; 241 | } 242 | 243 | function checkInView() { 244 | var $elements = $(), elementsLength, i = 0; 245 | 246 | $.each(inviewObjects, function(i, inviewObject) { 247 | var selector = inviewObject.data.selector, 248 | $element = inviewObject.$element; 249 | $elements = $elements.add(selector ? $element.find(selector) : $element); 250 | }); 251 | 252 | elementsLength = $elements.length; 253 | if (elementsLength) { 254 | viewportSize = viewportSize || getViewportSize(); 255 | viewportOffset = viewportOffset || getViewportOffset(); 256 | 257 | for (; i viewportOffset.top && 281 | elementOffset.top < viewportOffset.top + viewportSize.height && 282 | elementOffset.left + elementSize.width > viewportOffset.left && 283 | elementOffset.left < viewportOffset.left + viewportSize.width) { 284 | visiblePartX = (viewportOffset.left > elementOffset.left ? 285 | 'right' : (viewportOffset.left + viewportSize.width) < (elementOffset.left + elementSize.width) ? 286 | 'left' : 'both'); 287 | visiblePartY = (viewportOffset.top > elementOffset.top ? 288 | 'bottom' : (viewportOffset.top + viewportSize.height) < (elementOffset.top + elementSize.height) ? 289 | 'top' : 'both'); 290 | visiblePartsMerged = visiblePartX + "-" + visiblePartY; 291 | if (!inView || inView !== visiblePartsMerged) { 292 | $element.data('inview', visiblePartsMerged).trigger('inview', [true, visiblePartX, visiblePartY]); 293 | } 294 | } else if (inView) { 295 | $element.data('inview', false).trigger('inview', [false]); 296 | } 297 | } 298 | } 299 | } 300 | 301 | $(w).bind("scroll resize", function() { 302 | viewportSize = viewportOffset = null; 303 | }); 304 | 305 | // IE < 9 scrolls to focused elements without firing the "scroll" event 306 | if (!documentElement.addEventListener && documentElement.attachEvent) { 307 | documentElement.attachEvent("onfocusin", function() { 308 | viewportOffset = null; 309 | }); 310 | } 311 | 312 | // Use setInterval in order to also make sure this captures elements within 313 | // "overflow:scroll" elements or elements that appeared in the dom tree due to 314 | // dom manipulation and reflow 315 | // old: $(window).scroll(checkInView); 316 | // 317 | // By the way, iOS (iPad, iPhone, ...) seems to not execute, or at least delays 318 | // intervals while the user scrolls. Therefore the inview event might fire a bit late there 319 | setInterval(checkInView, 250); 320 | })(jQuery); -------------------------------------------------------------------------------- /docs/prettify.js: -------------------------------------------------------------------------------- 1 | var q=null;window.PR_SHOULD_USE_CONTINUATION=!0; 2 | (function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a= 3 | [],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;ci[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m), 9 | l=[],p={},d=0,g=e.length;d=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/, 10 | q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/, 11 | q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g, 12 | "");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a), 13 | a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e} 14 | for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], 18 | "catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"], 19 | H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"], 20 | J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+ 21 | I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]), 22 | ["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css", 23 | /^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}), 24 | ["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes", 25 | hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}p 2 | 3 | 4 | Chart.js | HTML5 Charts for your website. 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 18 | 21 | 22 | 23 | 24 |
25 |
26 |
27 |
28 |
29 |

Chart.js

30 |

Easy, object oriented client side graphs for designers and developers

31 |
32 | 33 | 34 | 35 | DocumentationDownload 36 |
37 |
38 |
39 | 40 |

6 Chart types

41 |

Visualise your data in different ways. Each of them animated, fully customisable and look great, even on retina displays.

42 |
43 |
44 | 45 |

HTML5 Based

46 |

Chart.js uses the HTML5 canvas element. It supports all modern browsers, and polyfills provide support for IE7/8.

47 |
48 |
49 | 50 |

Simple and flexible

51 |

Chart.js is dependency free, lightweight (4.5k when minified and gzipped) and offers loads of customisation options.

52 |
53 |
54 |
55 | 65 | 66 | 76 | 77 | 87 | 88 | 98 | 99 | 109 | 110 | 120 |

Like what you see? Download Chart.js on Github or read detailed documentation

121 |
122 | 126 | 127 | 140 | 141 | 142 | -------------------------------------------------------------------------------- /Chart.min.js: -------------------------------------------------------------------------------- 1 | var Chart=function(s){function v(a,c,b){a=A((a-c.graphMin)/(c.steps*c.stepValue),1,0);return b*c.steps*a}function x(a,c,b,e){function h(){g+=f;var k=a.animation?A(d(g),null,0):1;e.clearRect(0,0,q,u);a.scaleOverlay?(b(k),c()):(c(),b(k));if(1>=g)D(h);else if("function"==typeof a.onAnimationComplete)a.onAnimationComplete()}var f=a.animation?1/A(a.animationSteps,Number.MAX_VALUE,1):1,d=B[a.animationEasing],g=a.animation?0:1;"function"!==typeof c&&(c=function(){});D(h)}function C(a,c,b,e,h,f){var d;a= 2 | Math.floor(Math.log(e-h)/Math.LN10);h=Math.floor(h/(1*Math.pow(10,a)))*Math.pow(10,a);e=Math.ceil(e/(1*Math.pow(10,a)))*Math.pow(10,a)-h;a=Math.pow(10,a);for(d=Math.round(e/a);dc;)a=dc?c:!isNaN(parseFloat(b))&& 3 | isFinite(b)&&a)[^\t]*)'/g,"$1\r").replace(/\t=(.*?)%>/g,"',$1,'").split("\t").join("');").split("%>").join("p.push('").split("\r").join("\\'")+"');}return p.join('');");return c? 4 | b(c):b}var r=this,B={linear:function(a){return a},easeInQuad:function(a){return a*a},easeOutQuad:function(a){return-1*a*(a-2)},easeInOutQuad:function(a){return 1>(a/=0.5)?0.5*a*a:-0.5*(--a*(a-2)-1)},easeInCubic:function(a){return a*a*a},easeOutCubic:function(a){return 1*((a=a/1-1)*a*a+1)},easeInOutCubic:function(a){return 1>(a/=0.5)?0.5*a*a*a:0.5*((a-=2)*a*a+2)},easeInQuart:function(a){return a*a*a*a},easeOutQuart:function(a){return-1*((a=a/1-1)*a*a*a-1)},easeInOutQuart:function(a){return 1>(a/=0.5)? 5 | 0.5*a*a*a*a:-0.5*((a-=2)*a*a*a-2)},easeInQuint:function(a){return 1*(a/=1)*a*a*a*a},easeOutQuint:function(a){return 1*((a=a/1-1)*a*a*a*a+1)},easeInOutQuint:function(a){return 1>(a/=0.5)?0.5*a*a*a*a*a:0.5*((a-=2)*a*a*a*a+2)},easeInSine:function(a){return-1*Math.cos(a/1*(Math.PI/2))+1},easeOutSine:function(a){return 1*Math.sin(a/1*(Math.PI/2))},easeInOutSine:function(a){return-0.5*(Math.cos(Math.PI*a/1)-1)},easeInExpo:function(a){return 0==a?1:1*Math.pow(2,10*(a/1-1))},easeOutExpo:function(a){return 1== 6 | a?1:1*(-Math.pow(2,-10*a/1)+1)},easeInOutExpo:function(a){return 0==a?0:1==a?1:1>(a/=0.5)?0.5*Math.pow(2,10*(a-1)):0.5*(-Math.pow(2,-10*--a)+2)},easeInCirc:function(a){return 1<=a?a:-1*(Math.sqrt(1-(a/=1)*a)-1)},easeOutCirc:function(a){return 1*Math.sqrt(1-(a=a/1-1)*a)},easeInOutCirc:function(a){return 1>(a/=0.5)?-0.5*(Math.sqrt(1-a*a)-1):0.5*(Math.sqrt(1-(a-=2)*a)+1)},easeInElastic:function(a){var c=1.70158,b=0,e=1;if(0==a)return 0;if(1==(a/=1))return 1;b||(b=0.3);ea?-0.5*e*Math.pow(2,10* 8 | (a-=1))*Math.sin((1*a-c)*2*Math.PI/b):0.5*e*Math.pow(2,-10*(a-=1))*Math.sin((1*a-c)*2*Math.PI/b)+1},easeInBack:function(a){return 1*(a/=1)*a*(2.70158*a-1.70158)},easeOutBack:function(a){return 1*((a=a/1-1)*a*(2.70158*a+1.70158)+1)},easeInOutBack:function(a){var c=1.70158;return 1>(a/=0.5)?0.5*a*a*(((c*=1.525)+1)*a-c):0.5*((a-=2)*a*(((c*=1.525)+1)*a+c)+2)},easeInBounce:function(a){return 1-B.easeOutBounce(1-a)},easeOutBounce:function(a){return(a/=1)<1/2.75?1*7.5625*a*a:a<2/2.75?1*(7.5625*(a-=1.5/2.75)* 9 | a+0.75):a<2.5/2.75?1*(7.5625*(a-=2.25/2.75)*a+0.9375):1*(7.5625*(a-=2.625/2.75)*a+0.984375)},easeInOutBounce:function(a){return 0.5>a?0.5*B.easeInBounce(2*a):0.5*B.easeOutBounce(2*a-1)+0.5}},q=s.canvas.width,u=s.canvas.height;window.devicePixelRatio&&(s.canvas.style.width=q+"px",s.canvas.style.height=u+"px",s.canvas.height=u*window.devicePixelRatio,s.canvas.width=q*window.devicePixelRatio,s.scale(window.devicePixelRatio,window.devicePixelRatio));this.PolarArea=function(a,c){r.PolarArea.defaults={scaleOverlay:!0, 10 | scaleOverride:!1,scaleSteps:null,scaleStepWidth:null,scaleStartValue:null,scaleShowLine:!0,scaleLineColor:"rgba(0,0,0,.1)",scaleLineWidth:1,scaleShowLabels:!0,scaleLabel:"<%=value%>",scaleFontFamily:"'Arial'",scaleFontSize:12,scaleFontStyle:"normal",scaleFontColor:"#666",scaleShowLabelBackdrop:!0,scaleBackdropColor:"rgba(255,255,255,0.75)",scaleBackdropPaddingY:2,scaleBackdropPaddingX:2,segmentShowStroke:!0,segmentStrokeColor:"#fff",segmentStrokeWidth:2,animation:!0,animationSteps:100,animationEasing:"easeOutBounce", 11 | animateRotate:!0,animateScale:!1,onAnimationComplete:null};var b=c?y(r.PolarArea.defaults,c):r.PolarArea.defaults;return new G(a,b,s)};this.Radar=function(a,c){r.Radar.defaults={scaleOverlay:!1,scaleOverride:!1,scaleSteps:null,scaleStepWidth:null,scaleStartValue:null,scaleShowLine:!0,scaleLineColor:"rgba(0,0,0,.1)",scaleLineWidth:1,scaleShowLabels:!1,scaleLabel:"<%=value%>",scaleFontFamily:"'Arial'",scaleFontSize:12,scaleFontStyle:"normal",scaleFontColor:"#666",scaleShowLabelBackdrop:!0,scaleBackdropColor:"rgba(255,255,255,0.75)", 12 | scaleBackdropPaddingY:2,scaleBackdropPaddingX:2,angleShowLineOut:!0,angleLineColor:"rgba(0,0,0,.1)",angleLineWidth:1,pointLabelFontFamily:"'Arial'",pointLabelFontStyle:"normal",pointLabelFontSize:12,pointLabelFontColor:"#666",pointDot:!0,pointDotRadius:3,pointDotStrokeWidth:1,datasetStroke:!0,datasetStrokeWidth:2,datasetFill:!0,animation:!0,animationSteps:60,animationEasing:"easeOutQuart",onAnimationComplete:null};var b=c?y(r.Radar.defaults,c):r.Radar.defaults;return new H(a,b,s)};this.Pie=function(a, 13 | c){r.Pie.defaults={segmentShowStroke:!0,segmentStrokeColor:"#fff",segmentStrokeWidth:2,animation:!0,animationSteps:100,animationEasing:"easeOutBounce",animateRotate:!0,animateScale:!1,onAnimationComplete:null};var b=c?y(r.Pie.defaults,c):r.Pie.defaults;return new I(a,b,s)};this.Doughnut=function(a,c){r.Doughnut.defaults={segmentShowStroke:!0,segmentStrokeColor:"#fff",segmentStrokeWidth:2,percentageInnerCutout:50,animation:!0,animationSteps:100,animationEasing:"easeOutBounce",animateRotate:!0,animateScale:!1, 14 | onAnimationComplete:null};var b=c?y(r.Doughnut.defaults,c):r.Doughnut.defaults;return new J(a,b,s)};this.Line=function(a,c){r.Line.defaults={scaleOverlay:!1,scaleOverride:!1,scaleSteps:null,scaleStepWidth:null,scaleStartValue:null,scaleLineColor:"rgba(0,0,0,.1)",scaleLineWidth:1,scaleShowLabels:!0,scaleLabel:"<%=value%>",scaleFontFamily:"'Arial'",scaleFontSize:12,scaleFontStyle:"normal",scaleFontColor:"#666",scaleShowGridLines:!0,scaleGridLineColor:"rgba(0,0,0,.05)",scaleGridLineWidth:1,bezierCurve:!0, 15 | pointDot:!0,pointDotRadius:4,pointDotStrokeWidth:2,datasetStroke:!0,datasetStrokeWidth:2,datasetFill:!0,animation:!0,animationSteps:60,animationEasing:"easeOutQuart",onAnimationComplete:null};var b=c?y(r.Line.defaults,c):r.Line.defaults;return new K(a,b,s)};this.Bar=function(a,c){r.Bar.defaults={scaleOverlay:!1,scaleOverride:!1,scaleSteps:null,scaleStepWidth:null,scaleStartValue:null,scaleLineColor:"rgba(0,0,0,.1)",scaleLineWidth:1,scaleShowLabels:!0,scaleLabel:"<%=value%>",scaleFontFamily:"'Arial'", 16 | scaleFontSize:12,scaleFontStyle:"normal",scaleFontColor:"#666",scaleShowGridLines:!0,scaleGridLineColor:"rgba(0,0,0,.05)",scaleGridLineWidth:1,barShowStroke:!0,barStrokeWidth:2,barValueSpacing:5,barDatasetSpacing:1,animation:!0,animationSteps:60,animationEasing:"easeOutQuart",onAnimationComplete:null};var b=c?y(r.Bar.defaults,c):r.Bar.defaults;return new L(a,b,s)};var G=function(a,c,b){var e,h,f,d,g,k,j,l,m;g=Math.min.apply(Math,[q,u])/2;g-=Math.max.apply(Math,[0.5*c.scaleFontSize,0.5*c.scaleLineWidth]); 17 | d=2*c.scaleFontSize;c.scaleShowLabelBackdrop&&(d+=2*c.scaleBackdropPaddingY,g-=1.5*c.scaleBackdropPaddingY);l=g;d=d?d:5;e=Number.MIN_VALUE;h=Number.MAX_VALUE;for(f=0;fe&&(e=a[f].value),a[f].valuel&&(l=h);g-=Math.max.apply(Math,[l,1.5*(c.pointLabelFontSize/2)]);g-=c.pointLabelFontSize;l=g=A(g,null,0);d=d?d:5;e=Number.MIN_VALUE; 21 | h=Number.MAX_VALUE;for(f=0;fe&&(e=a.datasets[f].data[m]),a.datasets[f].data[m]Math.PI?"right":"left";b.textBaseline="middle";b.fillText(a.labels[d],f,-h)}b.restore()},function(d){var e=2*Math.PI/a.datasets[0].data.length;b.save();b.translate(q/2,u/2);for(var g=0;gt?e:t;q/a.labels.lengthe&&(e=a.datasets[f].data[l]),a.datasets[f].data[l]d?h:d;d+=10}r=q-d-t;m=Math.floor(r/(a.labels.length-1));n=q-t/2-r;p=g+c.scaleFontSize/2;x(c,function(){b.lineWidth=c.scaleLineWidth;b.strokeStyle=c.scaleLineColor;b.beginPath();b.moveTo(q-t/2+5,p);b.lineTo(q-t/2-r-5,p);b.stroke();0t?e:t;q/a.labels.lengthe&&(e=a.datasets[f].data[l]),a.datasets[f].data[l]< 35 | h&&(h=a.datasets[f].data[l]);f=Math.floor(g/(0.66*d));d=Math.floor(0.5*(g/d));l=c.scaleShowLabels?c.scaleLabel:"";c.scaleOverride?(j={steps:c.scaleSteps,stepValue:c.scaleStepWidth,graphMin:c.scaleStartValue,labels:[]},z(l,j.labels,j.steps,c.scaleStartValue,c.scaleStepWidth)):j=C(g,f,d,e,h,l);k=Math.floor(g/j.steps);d=1;if(c.scaleShowLabels){b.font=c.scaleFontStyle+" "+c.scaleFontSize+"px "+c.scaleFontFamily;for(e=0;ed?h:d;d+=10}r=q-d-t;m= 36 | Math.floor(r/a.labels.length);s=(m-2*c.scaleGridLineWidth-2*c.barValueSpacing-(c.barDatasetSpacing*a.datasets.length-1)-(c.barStrokeWidth/2*a.datasets.length-1))/a.datasets.length;n=q-t/2-r;p=g+c.scaleFontSize/2;x(c,function(){b.lineWidth=c.scaleLineWidth;b.strokeStyle=c.scaleLineColor;b.beginPath();b.moveTo(q-t/2+5,p);b.lineTo(q-t/2-r-5,p);b.stroke();0 2 | 3 | 4 | Chart.js Documentation 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 |
16 |
17 | 21 | 22 |
23 |

Chart.js Documentation

24 |

Everything you need to know to create great looking charts using Chart.js

25 |
26 |

Getting started

27 |

Include Chart.js

28 |

First we need to include the Chart.js library on the page. The library occupies a global variable of Chart.

29 |
<script src="Chart.js"></script>
30 |

Creating a chart

31 |

To create a chart, we need to instantiate the Chart class. To do this, we need to pass in the 2d context of where we want to draw the chart. Here's an example.

32 |
<canvas id="myChart" width="400" height="400"></canvas>
33 |
//Get the context of the canvas element we want to select
 34 | var ctx = document.getElementById("myChart").getContext("2d");
 35 | var myNewChart = new Chart(ctx).PolarArea(data);
36 |

We can also get the context of our canvas with jQuery. To do this, we need to get the DOM node out of the jQuery collection, and call the getContext("2d") method on that.

37 |
//Get context with jQuery - using jQuery's .get() method.
 38 | var ctx = $("#myChart").get(0).getContext("2d");
 39 | //This will get the first returned node in the jQuery collection.
 40 | var myNewChart = new Chart(ctx);
41 |

After we've instantiated the Chart class on the canvas we want to draw on, Chart.js will handle the scaling for retina displays.

42 |

With the Chart class set up, we can go on to create one of the charts Chart.js has available. In the example below, we would be drawing a Polar area chart.

43 |
new Chart(ctx).PolarArea(data,options);
44 |

We call a method of the name of the chart we want to create. We pass in the data for that chart type, and the options for that chart as parameters. Chart.js will merge the options you pass in with the default options for that chart type.

45 |
46 | 47 |
48 |

Line chart

49 |

Introduction

50 |

A line chart is a way of plotting data points on a line.

51 |

Often, it is used to show trend data, and the comparison of two data sets.

52 |

Example usage

53 | 54 |
new Chart(ctx).Line(data,options);
55 |

Data structure

56 |
var data = {
 57 | 	labels : ["January","February","March","April","May","June","July"],
 58 | 	datasets : [
 59 | 		{
 60 | 			fillColor : "rgba(220,220,220,0.5)",
 61 | 			strokeColor : "rgba(220,220,220,1)",
 62 | 			pointColor : "rgba(220,220,220,1)",
 63 | 			pointStrokeColor : "#fff",
 64 | 			data : [65,59,90,81,56,55,40]
 65 | 		},
 66 | 		{
 67 | 			fillColor : "rgba(151,187,205,0.5)",
 68 | 			strokeColor : "rgba(151,187,205,1)",
 69 | 			pointColor : "rgba(151,187,205,1)",
 70 | 			pointStrokeColor : "#fff",
 71 | 			data : [28,48,40,19,96,27,100]
 72 | 		}
 73 | 	]
 74 | }
75 |

The line chart requires an array of labels for each of the data points. This is show on the X axis.

76 |

The data for line charts is broken up into an array of datasets. Each dataset has a colour for the fill, a colour for the line and colours for the points and strokes of the points. These colours are strings just like CSS. You can use RGBA, RGB, HEX or HSL notation.

77 | 78 |

Chart options

79 |
Line.defaults = {
 80 | 				
 81 | 	//Boolean - If we show the scale above the chart data			
 82 | 	scaleOverlay : false,
 83 | 	
 84 | 	//Boolean - If we want to override with a hard coded scale
 85 | 	scaleOverride : false,
 86 | 	
 87 | 	//** Required if scaleOverride is true **
 88 | 	//Number - The number of steps in a hard coded scale
 89 | 	scaleSteps : null,
 90 | 	//Number - The value jump in the hard coded scale
 91 | 	scaleStepWidth : null,
 92 | 	//Number - The scale starting value
 93 | 	scaleStartValue : null,
 94 | 
 95 | 	//String - Colour of the scale line	
 96 | 	scaleLineColor : "rgba(0,0,0,.1)",
 97 | 	
 98 | 	//Number - Pixel width of the scale line	
 99 | 	scaleLineWidth : 1,
100 | 
101 | 	//Boolean - Whether to show labels on the scale	
102 | 	scaleShowLabels : true,
103 | 	
104 | 	//Interpolated JS string - can access value
105 | 	scaleLabel : "<%=value%>",
106 | 	
107 | 	//String - Scale label font declaration for the scale label
108 | 	scaleFontFamily : "'Arial'",
109 | 	
110 | 	//Number - Scale label font size in pixels	
111 | 	scaleFontSize : 12,
112 | 	
113 | 	//String - Scale label font weight style	
114 | 	scaleFontStyle : "normal",
115 | 	
116 | 	//String - Scale label font colour	
117 | 	scaleFontColor : "#666",	
118 | 	
119 | 	///Boolean - Whether grid lines are shown across the chart
120 | 	scaleShowGridLines : true,
121 | 	
122 | 	//String - Colour of the grid lines
123 | 	scaleGridLineColor : "rgba(0,0,0,.05)",
124 | 	
125 | 	//Number - Width of the grid lines
126 | 	scaleGridLineWidth : 1,	
127 | 	
128 | 	//Boolean - Whether the line is curved between points
129 | 	bezierCurve : true,
130 | 	
131 | 	//Boolean - Whether to show a dot for each point
132 | 	pointDot : true,
133 | 	
134 | 	//Number - Radius of each point dot in pixels
135 | 	pointDotRadius : 3,
136 | 	
137 | 	//Number - Pixel width of point dot stroke
138 | 	pointDotStrokeWidth : 1,
139 | 	
140 | 	//Boolean - Whether to show a stroke for datasets
141 | 	datasetStroke : true,
142 | 	
143 | 	//Number - Pixel width of dataset stroke
144 | 	datasetStrokeWidth : 2,
145 | 	
146 | 	//Boolean - Whether to fill the dataset with a colour
147 | 	datasetFill : true,
148 | 	
149 | 	//Boolean - Whether to animate the chart
150 | 	animation : true,
151 | 
152 | 	//Number - Number of animation steps
153 | 	animationSteps : 60,
154 | 	
155 | 	//String - Animation easing effect
156 | 	animationEasing : "easeOutQuart",
157 | 
158 | 	//Function - Fires when the animation is complete
159 | 	onAnimationComplete : null
160 | 	
161 | }
162 |
163 | 164 |
165 |

Bar chart

166 |

Introduction

167 |

A bar chart is a way of showing data as bars.

168 |

It is sometimes used to show trend data, and the comparison of multiple data sets side by side.

169 |

Example usage

170 | 171 |
new Chart(ctx).Bar(data,options);
172 |

Data structure

173 |
var data = {
174 | 	labels : ["January","February","March","April","May","June","July"],
175 | 	datasets : [
176 | 		{
177 | 			fillColor : "rgba(220,220,220,0.5)",
178 | 			strokeColor : "rgba(220,220,220,1)",
179 | 			data : [65,59,90,81,56,55,40]
180 | 		},
181 | 		{
182 | 			fillColor : "rgba(151,187,205,0.5)",
183 | 			strokeColor : "rgba(151,187,205,1)",
184 | 			data : [28,48,40,19,96,27,100]
185 | 		}
186 | 	]
187 | }
188 |

The bar chart has the a very similar data structure to the line chart, and has an array of datasets, each with colours and an array of data. Again, colours are in CSS format.

189 |

We have an array of labels too for display. In the example, we are showing the same data as the previous line chart example.

190 | 191 |

Chart options

192 |
Bar.defaults = {
193 | 				
194 | 	//Boolean - If we show the scale above the chart data			
195 | 	scaleOverlay : false,
196 | 	
197 | 	//Boolean - If we want to override with a hard coded scale
198 | 	scaleOverride : false,
199 | 	
200 | 	//** Required if scaleOverride is true **
201 | 	//Number - The number of steps in a hard coded scale
202 | 	scaleSteps : null,
203 | 	//Number - The value jump in the hard coded scale
204 | 	scaleStepWidth : null,
205 | 	//Number - The scale starting value
206 | 	scaleStartValue : null,
207 | 
208 | 	//String - Colour of the scale line	
209 | 	scaleLineColor : "rgba(0,0,0,.1)",
210 | 	
211 | 	//Number - Pixel width of the scale line	
212 | 	scaleLineWidth : 1,
213 | 
214 | 	//Boolean - Whether to show labels on the scale	
215 | 	scaleShowLabels : true,
216 | 	
217 | 	//Interpolated JS string - can access value
218 | 	scaleLabel : "<%=value%>",
219 | 	
220 | 	//String - Scale label font declaration for the scale label
221 | 	scaleFontFamily : "'Arial'",
222 | 	
223 | 	//Number - Scale label font size in pixels	
224 | 	scaleFontSize : 12,
225 | 	
226 | 	//String - Scale label font weight style	
227 | 	scaleFontStyle : "normal",
228 | 	
229 | 	//String - Scale label font colour	
230 | 	scaleFontColor : "#666",	
231 | 	
232 | 	///Boolean - Whether grid lines are shown across the chart
233 | 	scaleShowGridLines : true,
234 | 	
235 | 	//String - Colour of the grid lines
236 | 	scaleGridLineColor : "rgba(0,0,0,.05)",
237 | 	
238 | 	//Number - Width of the grid lines
239 | 	scaleGridLineWidth : 1,	
240 | 
241 | 	//Boolean - If there is a stroke on each bar	
242 | 	barShowStroke : true,
243 | 	
244 | 	//Number - Pixel width of the bar stroke	
245 | 	barStrokeWidth : 2,
246 | 	
247 | 	//Number - Spacing between each of the X value sets
248 | 	barValueSpacing : 5,
249 | 	
250 | 	//Number - Spacing between data sets within X values
251 | 	barDatasetSpacing : 1,
252 | 	
253 | 	//Boolean - Whether to animate the chart
254 | 	animation : true,
255 | 
256 | 	//Number - Number of animation steps
257 | 	animationSteps : 60,
258 | 	
259 | 	//String - Animation easing effect
260 | 	animationEasing : "easeOutQuart",
261 | 
262 | 	//Function - Fires when the animation is complete
263 | 	onAnimationComplete : null
264 | 	
265 | }
266 |
267 | 268 |
269 |

Radar chart

270 |

Introduction

271 |

A radar chart is a way of showing multiple data points and the variation between them.

272 |

They are often useful for comparing the points of two or more different data sets

273 |

Example usage

274 | 275 |
new Chart(ctx).Radar(data,options);
276 |

Data structure

277 |
var data = {
278 | 	labels : ["Eating","Drinking","Sleeping","Designing","Coding","Partying","Running"],
279 | 	datasets : [
280 | 		{
281 | 			fillColor : "rgba(220,220,220,0.5)",
282 | 			strokeColor : "rgba(220,220,220,1)",
283 | 			pointColor : "rgba(220,220,220,1)",
284 | 			pointStrokeColor : "#fff",
285 | 			data : [65,59,90,81,56,55,40]
286 | 		},
287 | 		{
288 | 			fillColor : "rgba(151,187,205,0.5)",
289 | 			strokeColor : "rgba(151,187,205,1)",
290 | 			pointColor : "rgba(151,187,205,1)",
291 | 			pointStrokeColor : "#fff",
292 | 			data : [28,48,40,19,96,27,100]
293 | 		}
294 | 	]
295 | }
296 |

For a radar chart, usually you will want to show a label on each point of the chart, so we include an array of strings that we show around each point in the chart. If you do not want this, you can either not include the array of labels, or choose to hide them in the chart options.

297 |

For the radar chart data, we have an array of datasets. Each of these is an object, with a fill colour, a stroke colour, a colour for the fill of each point, and a colour for the stroke of each point. We also have an array of data values.

298 | 299 |

Chart options

300 |
Radar.defaults = {
301 | 				
302 | 	//Boolean - If we show the scale above the chart data			
303 | 	scaleOverlay : false,
304 | 	
305 | 	//Boolean - If we want to override with a hard coded scale
306 | 	scaleOverride : false,
307 | 	
308 | 	//** Required if scaleOverride is true **
309 | 	//Number - The number of steps in a hard coded scale
310 | 	scaleSteps : null,
311 | 	//Number - The value jump in the hard coded scale
312 | 	scaleStepWidth : null,
313 | 	//Number - The centre starting value
314 | 	scaleStartValue : null,
315 | 	
316 | 	//Boolean - Whether to show lines for each scale point
317 | 	scaleShowLine : true,
318 | 
319 | 	//String - Colour of the scale line	
320 | 	scaleLineColor : "rgba(0,0,0,.1)",
321 | 	
322 | 	//Number - Pixel width of the scale line	
323 | 	scaleLineWidth : 1,
324 | 
325 | 	//Boolean - Whether to show labels on the scale	
326 | 	scaleShowLabels : false,
327 | 	
328 | 	//Interpolated JS string - can access value
329 | 	scaleLabel : "<%=value%>",
330 | 	
331 | 	//String - Scale label font declaration for the scale label
332 | 	scaleFontFamily : "'Arial'",
333 | 	
334 | 	//Number - Scale label font size in pixels	
335 | 	scaleFontSize : 12,
336 | 	
337 | 	//String - Scale label font weight style	
338 | 	scaleFontStyle : "normal",
339 | 	
340 | 	//String - Scale label font colour	
341 | 	scaleFontColor : "#666",
342 | 	
343 | 	//Boolean - Show a backdrop to the scale label
344 | 	scaleShowLabelBackdrop : true,
345 | 	
346 | 	//String - The colour of the label backdrop	
347 | 	scaleBackdropColor : "rgba(255,255,255,0.75)",
348 | 	
349 | 	//Number - The backdrop padding above & below the label in pixels
350 | 	scaleBackdropPaddingY : 2,
351 | 	
352 | 	//Number - The backdrop padding to the side of the label in pixels	
353 | 	scaleBackdropPaddingX : 2,
354 | 	
355 | 	//Boolean - Whether we show the angle lines out of the radar
356 | 	angleShowLineOut : true,
357 | 	
358 | 	//String - Colour of the angle line
359 | 	angleLineColor : "rgba(0,0,0,.1)",
360 | 	
361 | 	//Number - Pixel width of the angle line
362 | 	angleLineWidth : 1,			
363 | 	
364 | 	//String - Point label font declaration
365 | 	pointLabelFontFamily : "'Arial'",
366 | 	
367 | 	//String - Point label font weight
368 | 	pointLabelFontStyle : "normal",
369 | 	
370 | 	//Number - Point label font size in pixels	
371 | 	pointLabelFontSize : 12,
372 | 	
373 | 	//String - Point label font colour	
374 | 	pointLabelFontColor : "#666",
375 | 	
376 | 	//Boolean - Whether to show a dot for each point
377 | 	pointDot : true,
378 | 	
379 | 	//Number - Radius of each point dot in pixels
380 | 	pointDotRadius : 3,
381 | 	
382 | 	//Number - Pixel width of point dot stroke
383 | 	pointDotStrokeWidth : 1,
384 | 	
385 | 	//Boolean - Whether to show a stroke for datasets
386 | 	datasetStroke : true,
387 | 	
388 | 	//Number - Pixel width of dataset stroke
389 | 	datasetStrokeWidth : 2,
390 | 	
391 | 	//Boolean - Whether to fill the dataset with a colour
392 | 	datasetFill : true,
393 | 	
394 | 	//Boolean - Whether to animate the chart
395 | 	animation : true,
396 | 
397 | 	//Number - Number of animation steps
398 | 	animationSteps : 60,
399 | 	
400 | 	//String - Animation easing effect
401 | 	animationEasing : "easeOutQuart",
402 | 
403 | 	//Function - Fires when the animation is complete
404 | 	onAnimationComplete : null
405 | 	
406 | }
407 |
408 | 409 |
410 |

Polar area chart

411 |

Introduction

412 |

Polar area charts are similar to pie charts, but each segment has the same angle - the radius of the segment differs depending on the value.

413 |

This type of chart is often useful when we want to show a comparison data similar to a pie chart, but also show a scale of values for context.

414 |

Example usage

415 | 416 |
new Chart(ctx).PolarArea(data,options);
417 |

Data structure

418 |
var data = [
419 | 	{
420 | 		value : 30,
421 | 		color: "#D97041"
422 | 	},
423 | 	{
424 | 		value : 90,
425 | 		color: "#C7604C"
426 | 	},
427 | 	{
428 | 		value : 24,
429 | 		color: "#21323D"
430 | 	},
431 | 	{
432 | 		value : 58,
433 | 		color: "#9D9B7F"
434 | 	},
435 | 	{
436 | 		value : 82,
437 | 		color: "#7D4F6D"
438 | 	},
439 | 	{
440 | 		value : 8,
441 | 		color: "#584A5E"
442 | 	}
443 | ]
444 |

As you can see, for the chart data you pass in an array of objects, with a value and a colour. The value attribute should be a number, while the color attribute should be a string. Similar to CSS, for this string you can use HEX notation, RGB, RGBA or HSL.

445 |

Chart options

446 |

These are the default chart options. By passing in an object with any of these attributes, Chart.js will merge these objects and the graph accordingly. Explanations of each option are commented in the code below.

447 |
PolarArea.defaults = {
448 | 				
449 | 	//Boolean - Whether we show the scale above or below the chart segments
450 | 	scaleOverlay : true,
451 | 	
452 | 	//Boolean - If we want to override with a hard coded scale
453 | 	scaleOverride : false,
454 | 	
455 | 	//** Required if scaleOverride is true **
456 | 	//Number - The number of steps in a hard coded scale
457 | 	scaleSteps : null,
458 | 	//Number - The value jump in the hard coded scale
459 | 	scaleStepWidth : null,
460 | 	//Number - The centre starting value
461 | 	scaleStartValue : null,
462 | 	
463 | 	//Boolean - Show line for each value in the scale
464 | 	scaleShowLine : true,
465 | 	
466 | 	//String - The colour of the scale line
467 | 	scaleLineColor : "rgba(0,0,0,.1)",
468 | 	
469 | 	//Number - The width of the line - in pixels
470 | 	scaleLineWidth : 1,
471 | 	
472 | 	//Boolean - whether we should show text labels
473 | 	scaleShowLabels : true,
474 | 	
475 | 	//Interpolated JS string - can access value
476 | 	scaleLabel : "<%=value%>",
477 | 	
478 | 	//String - Scale label font declaration for the scale label
479 | 	scaleFontFamily : "'Arial'",
480 | 	
481 | 	//Number - Scale label font size in pixels	
482 | 	scaleFontSize : 12,
483 | 	
484 | 	//String - Scale label font weight style	
485 | 	scaleFontStyle : "normal",
486 | 	
487 | 	//String - Scale label font colour	
488 | 	scaleFontColor : "#666",
489 | 	
490 | 	//Boolean - Show a backdrop to the scale label
491 | 	scaleShowLabelBackdrop : true,
492 | 	
493 | 	//String - The colour of the label backdrop	
494 | 	scaleBackdropColor : "rgba(255,255,255,0.75)",
495 | 	
496 | 	//Number - The backdrop padding above & below the label in pixels
497 | 	scaleBackdropPaddingY : 2,
498 | 	
499 | 	//Number - The backdrop padding to the side of the label in pixels	
500 | 	scaleBackdropPaddingX : 2,
501 | 
502 | 	//Boolean - Stroke a line around each segment in the chart
503 | 	segmentShowStroke : true,
504 | 	
505 | 	//String - The colour of the stroke on each segement.
506 | 	segmentStrokeColor : "#fff",
507 | 	
508 | 	//Number - The width of the stroke value in pixels	
509 | 	segmentStrokeWidth : 2,
510 | 	
511 | 	//Boolean - Whether to animate the chart or not
512 | 	animation : true,
513 | 	
514 | 	//Number - Amount of animation steps
515 | 	animationSteps : 100,
516 | 	
517 | 	//String - Animation easing effect.
518 | 	animationEasing : "easeOutBounce",
519 | 
520 | 	//Boolean - Whether to animate the rotation of the chart
521 | 	animateRotate : true,
522 | 	
523 | 	//Boolean - Whether to animate scaling the chart from the centre
524 | 	animateScale : false,
525 | 
526 | 	//Function - This will fire when the animation of the chart is complete.
527 | 	onAnimationComplete : null
528 | }
529 |
530 |
531 |

Pie chart

532 |

Introduction

533 |

Pie charts are probably the most commonly used chart there are. They are divided into segments, the arc of each segment shows a the proportional value of each piece of data.

534 |

They are excellent at showing the relational proportions between data.

535 |

Example usage

536 | 537 |
new Chart(ctx).Pie(data,options);
538 |

Data structure

539 |
var data = [
540 | 	{
541 | 		value: 30,
542 | 		color:"#F38630"
543 | 	},
544 | 	{
545 | 		value : 50,
546 | 		color : "#E0E4CC"
547 | 	},
548 | 	{
549 | 		value : 100,
550 | 		color : "#69D2E7"
551 | 	}			
552 | ]
553 |

For a pie chart, you must pass in an array of objects with a value and a color property. The value attribute should be a number, Chart.js will total all of the numbers and calculate the relative proportion of each. The color attribute should be a string. Similar to CSS, for this string you can use HEX notation, RGB, RGBA or HSL.

554 |

Chart options

555 |

These are the default options for the Pie chart. Pass in an object with any of these attributes to override them. 556 |

Pie.defaults = {
557 | 	//Boolean - Whether we should show a stroke on each segment
558 | 	segmentShowStroke : true,
559 | 	
560 | 	//String - The colour of each segment stroke
561 | 	segmentStrokeColor : "#fff",
562 | 	
563 | 	//Number - The width of each segment stroke
564 | 	segmentStrokeWidth : 2,
565 | 	
566 | 	//Boolean - Whether we should animate the chart	
567 | 	animation : true,
568 | 	
569 | 	//Number - Amount of animation steps
570 | 	animationSteps : 100,
571 | 	
572 | 	//String - Animation easing effect
573 | 	animationEasing : "easeOutBounce",
574 | 	
575 | 	//Boolean - Whether we animate the rotation of the Pie
576 | 	animateRotate : true,
577 | 
578 | 	//Boolean - Whether we animate scaling the Pie from the centre
579 | 	animateScale : false,
580 | 	
581 | 	//Function - Will fire on animation completion.
582 | 	onAnimationComplete : null
583 | }
584 |
585 |
586 |

Doughnut chart

587 |

Introduction

588 |

Doughnut charts are similar to pie charts, however they have the centre cut out, and are therefore shaped more like a doughnut than a pie!

589 |

They are aso excellent at showing the relational proportions between data.

590 |

Example usage

591 | 592 |
new Chart(ctx).Doughnut(data,options);
593 |

Data structure

594 |
var data = [
595 | 	{
596 | 		value: 30,
597 | 		color:"#F7464A"
598 | 	},
599 | 	{
600 | 		value : 50,
601 | 		color : "#E2EAE9"
602 | 	},
603 | 	{
604 | 		value : 100,
605 | 		color : "#D4CCC5"
606 | 	},
607 | 	{
608 | 		value : 40,
609 | 		color : "#949FB1"
610 | 	},
611 | 	{
612 | 		value : 120,
613 | 		color : "#4D5360"
614 | 	}
615 | 
616 | ]
617 |

For a doughnut chart, you must pass in an array of objects with a value and a color property. The value attribute should be a number, Chart.js will total all of the numbers and calculate the relative proportion of each. The color attribute should be a string. Similar to CSS, for this string you can use HEX notation, RGB, RGBA or HSL.

618 |

Chart options

619 |

These are the default options for the doughnut chart. Pass in an object with any of these attributes to override them. 620 |

Doughnut.defaults = {
621 | 	//Boolean - Whether we should show a stroke on each segment
622 | 	segmentShowStroke : true,
623 | 	
624 | 	//String - The colour of each segment stroke
625 | 	segmentStrokeColor : "#fff",
626 | 	
627 | 	//Number - The width of each segment stroke
628 | 	segmentStrokeWidth : 2,
629 | 	
630 | 	//The percentage of the chart that we cut out of the middle.
631 | 	percentageInnerCutout : 50,
632 | 	
633 | 	//Boolean - Whether we should animate the chart	
634 | 	animation : true,
635 | 	
636 | 	//Number - Amount of animation steps
637 | 	animationSteps : 100,
638 | 	
639 | 	//String - Animation easing effect
640 | 	animationEasing : "easeOutBounce",
641 | 	
642 | 	//Boolean - Whether we animate the rotation of the Doughnut
643 | 	animateRotate : true,
644 | 
645 | 	//Boolean - Whether we animate scaling the Doughnut from the centre
646 | 	animateScale : false,
647 | 	
648 | 	//Function - Will fire on animation completion.
649 | 	onAnimationComplete : null
650 | }
651 |
652 |
653 |

General issues

654 |

Chart interactivity

655 |

If you are looking to add interaction as a layer to charts, Chart.js is not the library for you. A better option would be using SVG, as this will let you attach event listeners to any of the elements in the chart, as these are all DOM nodes.

656 |

Chart.js uses the canvas element, which is a single DOM node, similar in characteristics to a static image. This does mean that it has a wider scope for compatibility, and less memory implications than SVG based charting solutions. The canvas element also allows for saving the contents as a base 64 string, allowing saving the chart as an image.

657 |

In SVG, all of the lines, data points and everything you see is a DOM node. As a result of this, complex charts with a lot of intricacies, or many charts on the page will often see dips in performance when scrolling or generating the chart, especially when there are multiple on the page. SVG also has relatively poor mobile support, with Android not supporting SVG at all before version 3.0, and iOS before 5.0. (caniuse.com/svg-html5).

658 |

Browser support

659 |

Browser support for the canvas element is available in all modern & major mobile browsers (caniuse.com/canvas).

660 |

For IE8 & below, I would recommend using the polyfill ExplorerCanvas - available at https://code.google.com/p/explorercanvas/. It falls back to Internet explorer's format VML when canvas support is not available. Example use:

661 |
<head>
662 | 	<!--[if lte IE 8]>
663 | 		<script src="excanvas.js"></script>
664 | 	<![endif]-->
665 | </head>
666 |

Usually I would recommend feature detection to choose whether or not to load a polyfill, rather than IE conditional comments, however in this case, VML is a Microsoft proprietary format, so it will only work in IE.

667 |

Some important points to note in my experience using ExplorerCanvas as a fallback.

668 |
    669 |
  • Initialise charts on load rather than DOMContentReady when using the library, as sometimes a race condition will occur, and it will result in an error when trying to get the 2d context of a canvas.
  • 670 |
  • New VML DOM elements are being created for each animation frame and there is no hardware acceleration. As a result animation is usually slow and jerky, with flashing text. It is a good idea to dynamically turn off animation based on canvas support. I recommend using the excellent Modernizr to do this.
  • 671 |
  • When declaring fonts, the library explorercanvas requires the font name to be in single quotes inside the string. For example, instead of your scaleFontFamily property being simply "Arial", explorercanvas support, use "'Arial'" instead. Chart.js does this for default values.
  • 672 | 673 |
674 |

Bugs & issues

675 |

Please report these on the Github page - at github.com/nnnick/Chart.js.

676 |

New contributions to the library are welcome.

677 |

License

678 |

Chart.js is open source and available under the MIT license.

679 |
680 |
681 |
682 | 683 | 753 | 766 | 767 | -------------------------------------------------------------------------------- /site/assets/excanvas.js: -------------------------------------------------------------------------------- 1 | // Copyright 2006 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | 16 | // Known Issues: 17 | // 18 | // * Patterns only support repeat. 19 | // * Radial gradient are not implemented. The VML version of these look very 20 | // different from the canvas one. 21 | // * Clipping paths are not implemented. 22 | // * Coordsize. The width and height attribute have higher priority than the 23 | // width and height style values which isn't correct. 24 | // * Painting mode isn't implemented. 25 | // * Canvas width/height should is using content-box by default. IE in 26 | // Quirks mode will draw the canvas using border-box. Either change your 27 | // doctype to HTML5 28 | // (http://www.whatwg.org/specs/web-apps/current-work/#the-doctype) 29 | // or use Box Sizing Behavior from WebFX 30 | // (http://webfx.eae.net/dhtml/boxsizing/boxsizing.html) 31 | // * Non uniform scaling does not correctly scale strokes. 32 | // * Optimize. There is always room for speed improvements. 33 | 34 | // Only add this code if we do not already have a canvas implementation 35 | if (!document.createElement('canvas').getContext) { 36 | 37 | (function() { 38 | 39 | // alias some functions to make (compiled) code shorter 40 | var m = Math; 41 | var mr = m.round; 42 | var ms = m.sin; 43 | var mc = m.cos; 44 | var abs = m.abs; 45 | var sqrt = m.sqrt; 46 | 47 | // this is used for sub pixel precision 48 | var Z = 10; 49 | var Z2 = Z / 2; 50 | 51 | var IE_VERSION = +navigator.userAgent.match(/MSIE ([\d.]+)?/)[1]; 52 | 53 | /** 54 | * This funtion is assigned to the elements as element.getContext(). 55 | * @this {HTMLElement} 56 | * @return {CanvasRenderingContext2D_} 57 | */ 58 | function getContext() { 59 | return this.context_ || 60 | (this.context_ = new CanvasRenderingContext2D_(this)); 61 | } 62 | 63 | var slice = Array.prototype.slice; 64 | 65 | /** 66 | * Binds a function to an object. The returned function will always use the 67 | * passed in {@code obj} as {@code this}. 68 | * 69 | * Example: 70 | * 71 | * g = bind(f, obj, a, b) 72 | * g(c, d) // will do f.call(obj, a, b, c, d) 73 | * 74 | * @param {Function} f The function to bind the object to 75 | * @param {Object} obj The object that should act as this when the function 76 | * is called 77 | * @param {*} var_args Rest arguments that will be used as the initial 78 | * arguments when the function is called 79 | * @return {Function} A new function that has bound this 80 | */ 81 | function bind(f, obj, var_args) { 82 | var a = slice.call(arguments, 2); 83 | return function() { 84 | return f.apply(obj, a.concat(slice.call(arguments))); 85 | }; 86 | } 87 | 88 | function encodeHtmlAttribute(s) { 89 | return String(s).replace(/&/g, '&').replace(/"/g, '"'); 90 | } 91 | 92 | function addNamespace(doc, prefix, urn) { 93 | if (!doc.namespaces[prefix]) { 94 | doc.namespaces.add(prefix, urn, '#default#VML'); 95 | } 96 | } 97 | 98 | function addNamespacesAndStylesheet(doc) { 99 | addNamespace(doc, 'g_vml_', 'urn:schemas-microsoft-com:vml'); 100 | addNamespace(doc, 'g_o_', 'urn:schemas-microsoft-com:office:office'); 101 | 102 | // Setup default CSS. Only add one style sheet per document 103 | if (!doc.styleSheets['ex_canvas_']) { 104 | var ss = doc.createStyleSheet(); 105 | ss.owningElement.id = 'ex_canvas_'; 106 | ss.cssText = 'canvas{display:inline-block;overflow:hidden;' + 107 | // default size is 300x150 in Gecko and Opera 108 | 'text-align:left;width:300px;height:150px}'; 109 | } 110 | } 111 | 112 | // Add namespaces and stylesheet at startup. 113 | addNamespacesAndStylesheet(document); 114 | 115 | var G_vmlCanvasManager_ = { 116 | init: function(opt_doc) { 117 | var doc = opt_doc || document; 118 | // Create a dummy element so that IE will allow canvas elements to be 119 | // recognized. 120 | doc.createElement('canvas'); 121 | doc.attachEvent('onreadystatechange', bind(this.init_, this, doc)); 122 | }, 123 | 124 | init_: function(doc) { 125 | // find all canvas elements 126 | var els = doc.getElementsByTagName('canvas'); 127 | for (var i = 0; i < els.length; i++) { 128 | this.initElement(els[i]); 129 | } 130 | }, 131 | 132 | /** 133 | * Public initializes a canvas element so that it can be used as canvas 134 | * element from now on. This is called automatically before the page is 135 | * loaded but if you are creating elements using createElement you need to 136 | * make sure this is called on the element. 137 | * @param {HTMLElement} el The canvas element to initialize. 138 | * @return {HTMLElement} the element that was created. 139 | */ 140 | initElement: function(el) { 141 | if (!el.getContext) { 142 | el.getContext = getContext; 143 | 144 | // Add namespaces and stylesheet to document of the element. 145 | addNamespacesAndStylesheet(el.ownerDocument); 146 | 147 | // Remove fallback content. There is no way to hide text nodes so we 148 | // just remove all childNodes. We could hide all elements and remove 149 | // text nodes but who really cares about the fallback content. 150 | el.innerHTML = ''; 151 | 152 | // do not use inline function because that will leak memory 153 | el.attachEvent('onpropertychange', onPropertyChange); 154 | el.attachEvent('onresize', onResize); 155 | 156 | var attrs = el.attributes; 157 | if (attrs.width && attrs.width.specified) { 158 | // TODO: use runtimeStyle and coordsize 159 | // el.getContext().setWidth_(attrs.width.nodeValue); 160 | el.style.width = attrs.width.nodeValue + 'px'; 161 | } else { 162 | el.width = el.clientWidth; 163 | } 164 | if (attrs.height && attrs.height.specified) { 165 | // TODO: use runtimeStyle and coordsize 166 | // el.getContext().setHeight_(attrs.height.nodeValue); 167 | el.style.height = attrs.height.nodeValue + 'px'; 168 | } else { 169 | el.height = el.clientHeight; 170 | } 171 | //el.getContext().setCoordsize_() 172 | } 173 | return el; 174 | } 175 | }; 176 | 177 | function onPropertyChange(e) { 178 | var el = e.srcElement; 179 | 180 | switch (e.propertyName) { 181 | case 'width': 182 | el.getContext().clearRect(); 183 | el.style.width = el.attributes.width.nodeValue + 'px'; 184 | // In IE8 this does not trigger onresize. 185 | el.firstChild.style.width = el.clientWidth + 'px'; 186 | break; 187 | case 'height': 188 | el.getContext().clearRect(); 189 | el.style.height = el.attributes.height.nodeValue + 'px'; 190 | el.firstChild.style.height = el.clientHeight + 'px'; 191 | break; 192 | } 193 | } 194 | 195 | function onResize(e) { 196 | var el = e.srcElement; 197 | if (el.firstChild) { 198 | el.firstChild.style.width = el.clientWidth + 'px'; 199 | el.firstChild.style.height = el.clientHeight + 'px'; 200 | } 201 | } 202 | 203 | G_vmlCanvasManager_.init(); 204 | 205 | // precompute "00" to "FF" 206 | var decToHex = []; 207 | for (var i = 0; i < 16; i++) { 208 | for (var j = 0; j < 16; j++) { 209 | decToHex[i * 16 + j] = i.toString(16) + j.toString(16); 210 | } 211 | } 212 | 213 | function createMatrixIdentity() { 214 | return [ 215 | [1, 0, 0], 216 | [0, 1, 0], 217 | [0, 0, 1] 218 | ]; 219 | } 220 | 221 | function matrixMultiply(m1, m2) { 222 | var result = createMatrixIdentity(); 223 | 224 | for (var x = 0; x < 3; x++) { 225 | for (var y = 0; y < 3; y++) { 226 | var sum = 0; 227 | 228 | for (var z = 0; z < 3; z++) { 229 | sum += m1[x][z] * m2[z][y]; 230 | } 231 | 232 | result[x][y] = sum; 233 | } 234 | } 235 | return result; 236 | } 237 | 238 | function copyState(o1, o2) { 239 | o2.fillStyle = o1.fillStyle; 240 | o2.lineCap = o1.lineCap; 241 | o2.lineJoin = o1.lineJoin; 242 | o2.lineWidth = o1.lineWidth; 243 | o2.miterLimit = o1.miterLimit; 244 | o2.shadowBlur = o1.shadowBlur; 245 | o2.shadowColor = o1.shadowColor; 246 | o2.shadowOffsetX = o1.shadowOffsetX; 247 | o2.shadowOffsetY = o1.shadowOffsetY; 248 | o2.strokeStyle = o1.strokeStyle; 249 | o2.globalAlpha = o1.globalAlpha; 250 | o2.font = o1.font; 251 | o2.textAlign = o1.textAlign; 252 | o2.textBaseline = o1.textBaseline; 253 | o2.arcScaleX_ = o1.arcScaleX_; 254 | o2.arcScaleY_ = o1.arcScaleY_; 255 | o2.lineScale_ = o1.lineScale_; 256 | } 257 | 258 | var colorData = { 259 | aliceblue: '#F0F8FF', 260 | antiquewhite: '#FAEBD7', 261 | aquamarine: '#7FFFD4', 262 | azure: '#F0FFFF', 263 | beige: '#F5F5DC', 264 | bisque: '#FFE4C4', 265 | black: '#000000', 266 | blanchedalmond: '#FFEBCD', 267 | blueviolet: '#8A2BE2', 268 | brown: '#A52A2A', 269 | burlywood: '#DEB887', 270 | cadetblue: '#5F9EA0', 271 | chartreuse: '#7FFF00', 272 | chocolate: '#D2691E', 273 | coral: '#FF7F50', 274 | cornflowerblue: '#6495ED', 275 | cornsilk: '#FFF8DC', 276 | crimson: '#DC143C', 277 | cyan: '#00FFFF', 278 | darkblue: '#00008B', 279 | darkcyan: '#008B8B', 280 | darkgoldenrod: '#B8860B', 281 | darkgray: '#A9A9A9', 282 | darkgreen: '#006400', 283 | darkgrey: '#A9A9A9', 284 | darkkhaki: '#BDB76B', 285 | darkmagenta: '#8B008B', 286 | darkolivegreen: '#556B2F', 287 | darkorange: '#FF8C00', 288 | darkorchid: '#9932CC', 289 | darkred: '#8B0000', 290 | darksalmon: '#E9967A', 291 | darkseagreen: '#8FBC8F', 292 | darkslateblue: '#483D8B', 293 | darkslategray: '#2F4F4F', 294 | darkslategrey: '#2F4F4F', 295 | darkturquoise: '#00CED1', 296 | darkviolet: '#9400D3', 297 | deeppink: '#FF1493', 298 | deepskyblue: '#00BFFF', 299 | dimgray: '#696969', 300 | dimgrey: '#696969', 301 | dodgerblue: '#1E90FF', 302 | firebrick: '#B22222', 303 | floralwhite: '#FFFAF0', 304 | forestgreen: '#228B22', 305 | gainsboro: '#DCDCDC', 306 | ghostwhite: '#F8F8FF', 307 | gold: '#FFD700', 308 | goldenrod: '#DAA520', 309 | grey: '#808080', 310 | greenyellow: '#ADFF2F', 311 | honeydew: '#F0FFF0', 312 | hotpink: '#FF69B4', 313 | indianred: '#CD5C5C', 314 | indigo: '#4B0082', 315 | ivory: '#FFFFF0', 316 | khaki: '#F0E68C', 317 | lavender: '#E6E6FA', 318 | lavenderblush: '#FFF0F5', 319 | lawngreen: '#7CFC00', 320 | lemonchiffon: '#FFFACD', 321 | lightblue: '#ADD8E6', 322 | lightcoral: '#F08080', 323 | lightcyan: '#E0FFFF', 324 | lightgoldenrodyellow: '#FAFAD2', 325 | lightgreen: '#90EE90', 326 | lightgrey: '#D3D3D3', 327 | lightpink: '#FFB6C1', 328 | lightsalmon: '#FFA07A', 329 | lightseagreen: '#20B2AA', 330 | lightskyblue: '#87CEFA', 331 | lightslategray: '#778899', 332 | lightslategrey: '#778899', 333 | lightsteelblue: '#B0C4DE', 334 | lightyellow: '#FFFFE0', 335 | limegreen: '#32CD32', 336 | linen: '#FAF0E6', 337 | magenta: '#FF00FF', 338 | mediumaquamarine: '#66CDAA', 339 | mediumblue: '#0000CD', 340 | mediumorchid: '#BA55D3', 341 | mediumpurple: '#9370DB', 342 | mediumseagreen: '#3CB371', 343 | mediumslateblue: '#7B68EE', 344 | mediumspringgreen: '#00FA9A', 345 | mediumturquoise: '#48D1CC', 346 | mediumvioletred: '#C71585', 347 | midnightblue: '#191970', 348 | mintcream: '#F5FFFA', 349 | mistyrose: '#FFE4E1', 350 | moccasin: '#FFE4B5', 351 | navajowhite: '#FFDEAD', 352 | oldlace: '#FDF5E6', 353 | olivedrab: '#6B8E23', 354 | orange: '#FFA500', 355 | orangered: '#FF4500', 356 | orchid: '#DA70D6', 357 | palegoldenrod: '#EEE8AA', 358 | palegreen: '#98FB98', 359 | paleturquoise: '#AFEEEE', 360 | palevioletred: '#DB7093', 361 | papayawhip: '#FFEFD5', 362 | peachpuff: '#FFDAB9', 363 | peru: '#CD853F', 364 | pink: '#FFC0CB', 365 | plum: '#DDA0DD', 366 | powderblue: '#B0E0E6', 367 | rosybrown: '#BC8F8F', 368 | royalblue: '#4169E1', 369 | saddlebrown: '#8B4513', 370 | salmon: '#FA8072', 371 | sandybrown: '#F4A460', 372 | seagreen: '#2E8B57', 373 | seashell: '#FFF5EE', 374 | sienna: '#A0522D', 375 | skyblue: '#87CEEB', 376 | slateblue: '#6A5ACD', 377 | slategray: '#708090', 378 | slategrey: '#708090', 379 | snow: '#FFFAFA', 380 | springgreen: '#00FF7F', 381 | steelblue: '#4682B4', 382 | tan: '#D2B48C', 383 | thistle: '#D8BFD8', 384 | tomato: '#FF6347', 385 | turquoise: '#40E0D0', 386 | violet: '#EE82EE', 387 | wheat: '#F5DEB3', 388 | whitesmoke: '#F5F5F5', 389 | yellowgreen: '#9ACD32' 390 | }; 391 | 392 | 393 | function getRgbHslContent(styleString) { 394 | var start = styleString.indexOf('(', 3); 395 | var end = styleString.indexOf(')', start + 1); 396 | var parts = styleString.substring(start + 1, end).split(','); 397 | // add alpha if needed 398 | if (parts.length != 4 || styleString.charAt(3) != 'a') { 399 | parts[3] = 1; 400 | } 401 | return parts; 402 | } 403 | 404 | function percent(s) { 405 | return parseFloat(s) / 100; 406 | } 407 | 408 | function clamp(v, min, max) { 409 | return Math.min(max, Math.max(min, v)); 410 | } 411 | 412 | function hslToRgb(parts){ 413 | var r, g, b, h, s, l; 414 | h = parseFloat(parts[0]) / 360 % 360; 415 | if (h < 0) 416 | h++; 417 | s = clamp(percent(parts[1]), 0, 1); 418 | l = clamp(percent(parts[2]), 0, 1); 419 | if (s == 0) { 420 | r = g = b = l; // achromatic 421 | } else { 422 | var q = l < 0.5 ? l * (1 + s) : l + s - l * s; 423 | var p = 2 * l - q; 424 | r = hueToRgb(p, q, h + 1 / 3); 425 | g = hueToRgb(p, q, h); 426 | b = hueToRgb(p, q, h - 1 / 3); 427 | } 428 | 429 | return '#' + decToHex[Math.floor(r * 255)] + 430 | decToHex[Math.floor(g * 255)] + 431 | decToHex[Math.floor(b * 255)]; 432 | } 433 | 434 | function hueToRgb(m1, m2, h) { 435 | if (h < 0) 436 | h++; 437 | if (h > 1) 438 | h--; 439 | 440 | if (6 * h < 1) 441 | return m1 + (m2 - m1) * 6 * h; 442 | else if (2 * h < 1) 443 | return m2; 444 | else if (3 * h < 2) 445 | return m1 + (m2 - m1) * (2 / 3 - h) * 6; 446 | else 447 | return m1; 448 | } 449 | 450 | var processStyleCache = {}; 451 | 452 | function processStyle(styleString) { 453 | if (styleString in processStyleCache) { 454 | return processStyleCache[styleString]; 455 | } 456 | 457 | var str, alpha = 1; 458 | 459 | styleString = String(styleString); 460 | if (styleString.charAt(0) == '#') { 461 | str = styleString; 462 | } else if (/^rgb/.test(styleString)) { 463 | var parts = getRgbHslContent(styleString); 464 | var str = '#', n; 465 | for (var i = 0; i < 3; i++) { 466 | if (parts[i].indexOf('%') != -1) { 467 | n = Math.floor(percent(parts[i]) * 255); 468 | } else { 469 | n = +parts[i]; 470 | } 471 | str += decToHex[clamp(n, 0, 255)]; 472 | } 473 | alpha = +parts[3]; 474 | } else if (/^hsl/.test(styleString)) { 475 | var parts = getRgbHslContent(styleString); 476 | str = hslToRgb(parts); 477 | alpha = parts[3]; 478 | } else { 479 | str = colorData[styleString] || styleString; 480 | } 481 | return processStyleCache[styleString] = {color: str, alpha: alpha}; 482 | } 483 | 484 | var DEFAULT_STYLE = { 485 | style: 'normal', 486 | variant: 'normal', 487 | weight: 'normal', 488 | size: 10, 489 | family: 'sans-serif' 490 | }; 491 | 492 | // Internal text style cache 493 | var fontStyleCache = {}; 494 | 495 | function processFontStyle(styleString) { 496 | if (fontStyleCache[styleString]) { 497 | return fontStyleCache[styleString]; 498 | } 499 | 500 | var el = document.createElement('div'); 501 | var style = el.style; 502 | try { 503 | style.font = styleString; 504 | } catch (ex) { 505 | // Ignore failures to set to invalid font. 506 | } 507 | 508 | return fontStyleCache[styleString] = { 509 | style: style.fontStyle || DEFAULT_STYLE.style, 510 | variant: style.fontVariant || DEFAULT_STYLE.variant, 511 | weight: style.fontWeight || DEFAULT_STYLE.weight, 512 | size: style.fontSize || DEFAULT_STYLE.size, 513 | family: style.fontFamily || DEFAULT_STYLE.family 514 | }; 515 | } 516 | 517 | function getComputedStyle(style, element) { 518 | var computedStyle = {}; 519 | 520 | for (var p in style) { 521 | computedStyle[p] = style[p]; 522 | } 523 | 524 | // Compute the size 525 | var canvasFontSize = parseFloat(element.currentStyle.fontSize), 526 | fontSize = parseFloat(style.size); 527 | 528 | if (typeof style.size == 'number') { 529 | computedStyle.size = style.size; 530 | } else if (style.size.indexOf('px') != -1) { 531 | computedStyle.size = fontSize; 532 | } else if (style.size.indexOf('em') != -1) { 533 | computedStyle.size = canvasFontSize * fontSize; 534 | } else if(style.size.indexOf('%') != -1) { 535 | computedStyle.size = (canvasFontSize / 100) * fontSize; 536 | } else if (style.size.indexOf('pt') != -1) { 537 | computedStyle.size = fontSize / .75; 538 | } else { 539 | computedStyle.size = canvasFontSize; 540 | } 541 | 542 | // Different scaling between normal text and VML text. This was found using 543 | // trial and error to get the same size as non VML text. 544 | computedStyle.size *= 0.981; 545 | 546 | return computedStyle; 547 | } 548 | 549 | function buildStyle(style) { 550 | return style.style + ' ' + style.variant + ' ' + style.weight + ' ' + 551 | style.size + 'px ' + style.family; 552 | } 553 | 554 | var lineCapMap = { 555 | 'butt': 'flat', 556 | 'round': 'round' 557 | }; 558 | 559 | function processLineCap(lineCap) { 560 | return lineCapMap[lineCap] || 'square'; 561 | } 562 | 563 | /** 564 | * This class implements CanvasRenderingContext2D interface as described by 565 | * the WHATWG. 566 | * @param {HTMLElement} canvasElement The element that the 2D context should 567 | * be associated with 568 | */ 569 | function CanvasRenderingContext2D_(canvasElement) { 570 | this.m_ = createMatrixIdentity(); 571 | 572 | this.mStack_ = []; 573 | this.aStack_ = []; 574 | this.currentPath_ = []; 575 | 576 | // Canvas context properties 577 | this.strokeStyle = '#000'; 578 | this.fillStyle = '#000'; 579 | 580 | this.lineWidth = 1; 581 | this.lineJoin = 'miter'; 582 | this.lineCap = 'butt'; 583 | this.miterLimit = Z * 1; 584 | this.globalAlpha = 1; 585 | this.font = '10px sans-serif'; 586 | this.textAlign = 'left'; 587 | this.textBaseline = 'alphabetic'; 588 | this.canvas = canvasElement; 589 | 590 | var cssText = 'width:' + canvasElement.clientWidth + 'px;height:' + 591 | canvasElement.clientHeight + 'px;overflow:hidden;position:absolute'; 592 | var el = canvasElement.ownerDocument.createElement('div'); 593 | el.style.cssText = cssText; 594 | canvasElement.appendChild(el); 595 | 596 | var overlayEl = el.cloneNode(false); 597 | // Use a non transparent background. 598 | overlayEl.style.backgroundColor = 'red'; 599 | overlayEl.style.filter = 'alpha(opacity=0)'; 600 | canvasElement.appendChild(overlayEl); 601 | 602 | this.element_ = el; 603 | this.arcScaleX_ = 1; 604 | this.arcScaleY_ = 1; 605 | this.lineScale_ = 1; 606 | } 607 | 608 | var contextPrototype = CanvasRenderingContext2D_.prototype; 609 | contextPrototype.clearRect = function() { 610 | if (this.textMeasureEl_) { 611 | this.textMeasureEl_.removeNode(true); 612 | this.textMeasureEl_ = null; 613 | } 614 | this.element_.innerHTML = ''; 615 | }; 616 | 617 | contextPrototype.beginPath = function() { 618 | // TODO: Branch current matrix so that save/restore has no effect 619 | // as per safari docs. 620 | this.currentPath_ = []; 621 | }; 622 | 623 | contextPrototype.moveTo = function(aX, aY) { 624 | var p = getCoords(this, aX, aY); 625 | this.currentPath_.push({type: 'moveTo', x: p.x, y: p.y}); 626 | this.currentX_ = p.x; 627 | this.currentY_ = p.y; 628 | }; 629 | 630 | contextPrototype.lineTo = function(aX, aY) { 631 | var p = getCoords(this, aX, aY); 632 | this.currentPath_.push({type: 'lineTo', x: p.x, y: p.y}); 633 | 634 | this.currentX_ = p.x; 635 | this.currentY_ = p.y; 636 | }; 637 | 638 | contextPrototype.bezierCurveTo = function(aCP1x, aCP1y, 639 | aCP2x, aCP2y, 640 | aX, aY) { 641 | var p = getCoords(this, aX, aY); 642 | var cp1 = getCoords(this, aCP1x, aCP1y); 643 | var cp2 = getCoords(this, aCP2x, aCP2y); 644 | bezierCurveTo(this, cp1, cp2, p); 645 | }; 646 | 647 | // Helper function that takes the already fixed cordinates. 648 | function bezierCurveTo(self, cp1, cp2, p) { 649 | self.currentPath_.push({ 650 | type: 'bezierCurveTo', 651 | cp1x: cp1.x, 652 | cp1y: cp1.y, 653 | cp2x: cp2.x, 654 | cp2y: cp2.y, 655 | x: p.x, 656 | y: p.y 657 | }); 658 | self.currentX_ = p.x; 659 | self.currentY_ = p.y; 660 | } 661 | 662 | contextPrototype.quadraticCurveTo = function(aCPx, aCPy, aX, aY) { 663 | // the following is lifted almost directly from 664 | // http://developer.mozilla.org/en/docs/Canvas_tutorial:Drawing_shapes 665 | 666 | var cp = getCoords(this, aCPx, aCPy); 667 | var p = getCoords(this, aX, aY); 668 | 669 | var cp1 = { 670 | x: this.currentX_ + 2.0 / 3.0 * (cp.x - this.currentX_), 671 | y: this.currentY_ + 2.0 / 3.0 * (cp.y - this.currentY_) 672 | }; 673 | var cp2 = { 674 | x: cp1.x + (p.x - this.currentX_) / 3.0, 675 | y: cp1.y + (p.y - this.currentY_) / 3.0 676 | }; 677 | 678 | bezierCurveTo(this, cp1, cp2, p); 679 | }; 680 | 681 | contextPrototype.arc = function(aX, aY, aRadius, 682 | aStartAngle, aEndAngle, aClockwise) { 683 | aRadius *= Z; 684 | var arcType = aClockwise ? 'at' : 'wa'; 685 | 686 | var xStart = aX + mc(aStartAngle) * aRadius - Z2; 687 | var yStart = aY + ms(aStartAngle) * aRadius - Z2; 688 | 689 | var xEnd = aX + mc(aEndAngle) * aRadius - Z2; 690 | var yEnd = aY + ms(aEndAngle) * aRadius - Z2; 691 | 692 | // IE won't render arches drawn counter clockwise if xStart == xEnd. 693 | if (xStart == xEnd && !aClockwise) { 694 | xStart += 0.125; // Offset xStart by 1/80 of a pixel. Use something 695 | // that can be represented in binary 696 | } 697 | 698 | var p = getCoords(this, aX, aY); 699 | var pStart = getCoords(this, xStart, yStart); 700 | var pEnd = getCoords(this, xEnd, yEnd); 701 | 702 | this.currentPath_.push({type: arcType, 703 | x: p.x, 704 | y: p.y, 705 | radius: aRadius, 706 | xStart: pStart.x, 707 | yStart: pStart.y, 708 | xEnd: pEnd.x, 709 | yEnd: pEnd.y}); 710 | 711 | }; 712 | 713 | contextPrototype.rect = function(aX, aY, aWidth, aHeight) { 714 | this.moveTo(aX, aY); 715 | this.lineTo(aX + aWidth, aY); 716 | this.lineTo(aX + aWidth, aY + aHeight); 717 | this.lineTo(aX, aY + aHeight); 718 | this.closePath(); 719 | }; 720 | 721 | contextPrototype.strokeRect = function(aX, aY, aWidth, aHeight) { 722 | var oldPath = this.currentPath_; 723 | this.beginPath(); 724 | 725 | this.moveTo(aX, aY); 726 | this.lineTo(aX + aWidth, aY); 727 | this.lineTo(aX + aWidth, aY + aHeight); 728 | this.lineTo(aX, aY + aHeight); 729 | this.closePath(); 730 | this.stroke(); 731 | 732 | this.currentPath_ = oldPath; 733 | }; 734 | 735 | contextPrototype.fillRect = function(aX, aY, aWidth, aHeight) { 736 | var oldPath = this.currentPath_; 737 | this.beginPath(); 738 | 739 | this.moveTo(aX, aY); 740 | this.lineTo(aX + aWidth, aY); 741 | this.lineTo(aX + aWidth, aY + aHeight); 742 | this.lineTo(aX, aY + aHeight); 743 | this.closePath(); 744 | this.fill(); 745 | 746 | this.currentPath_ = oldPath; 747 | }; 748 | 749 | contextPrototype.createLinearGradient = function(aX0, aY0, aX1, aY1) { 750 | var gradient = new CanvasGradient_('gradient'); 751 | gradient.x0_ = aX0; 752 | gradient.y0_ = aY0; 753 | gradient.x1_ = aX1; 754 | gradient.y1_ = aY1; 755 | return gradient; 756 | }; 757 | 758 | contextPrototype.createRadialGradient = function(aX0, aY0, aR0, 759 | aX1, aY1, aR1) { 760 | var gradient = new CanvasGradient_('gradientradial'); 761 | gradient.x0_ = aX0; 762 | gradient.y0_ = aY0; 763 | gradient.r0_ = aR0; 764 | gradient.x1_ = aX1; 765 | gradient.y1_ = aY1; 766 | gradient.r1_ = aR1; 767 | return gradient; 768 | }; 769 | 770 | contextPrototype.drawImage = function(image, var_args) { 771 | var dx, dy, dw, dh, sx, sy, sw, sh; 772 | 773 | // to find the original width we overide the width and height 774 | var oldRuntimeWidth = image.runtimeStyle.width; 775 | var oldRuntimeHeight = image.runtimeStyle.height; 776 | image.runtimeStyle.width = 'auto'; 777 | image.runtimeStyle.height = 'auto'; 778 | 779 | // get the original size 780 | var w = image.width; 781 | var h = image.height; 782 | 783 | // and remove overides 784 | image.runtimeStyle.width = oldRuntimeWidth; 785 | image.runtimeStyle.height = oldRuntimeHeight; 786 | 787 | if (arguments.length == 3) { 788 | dx = arguments[1]; 789 | dy = arguments[2]; 790 | sx = sy = 0; 791 | sw = dw = w; 792 | sh = dh = h; 793 | } else if (arguments.length == 5) { 794 | dx = arguments[1]; 795 | dy = arguments[2]; 796 | dw = arguments[3]; 797 | dh = arguments[4]; 798 | sx = sy = 0; 799 | sw = w; 800 | sh = h; 801 | } else if (arguments.length == 9) { 802 | sx = arguments[1]; 803 | sy = arguments[2]; 804 | sw = arguments[3]; 805 | sh = arguments[4]; 806 | dx = arguments[5]; 807 | dy = arguments[6]; 808 | dw = arguments[7]; 809 | dh = arguments[8]; 810 | } else { 811 | throw Error('Invalid number of arguments'); 812 | } 813 | 814 | var d = getCoords(this, dx, dy); 815 | 816 | var w2 = sw / 2; 817 | var h2 = sh / 2; 818 | 819 | var vmlStr = []; 820 | 821 | var W = 10; 822 | var H = 10; 823 | 824 | // For some reason that I've now forgotten, using divs didn't work 825 | vmlStr.push(' ' , 866 | '', 874 | ''); 875 | 876 | this.element_.insertAdjacentHTML('BeforeEnd', vmlStr.join('')); 877 | }; 878 | 879 | contextPrototype.stroke = function(aFill) { 880 | var lineStr = []; 881 | var lineOpen = false; 882 | 883 | var W = 10; 884 | var H = 10; 885 | 886 | lineStr.push(''); 954 | 955 | if (!aFill) { 956 | appendStroke(this, lineStr); 957 | } else { 958 | appendFill(this, lineStr, min, max); 959 | } 960 | 961 | lineStr.push(''); 962 | 963 | this.element_.insertAdjacentHTML('beforeEnd', lineStr.join('')); 964 | }; 965 | 966 | function appendStroke(ctx, lineStr) { 967 | var a = processStyle(ctx.strokeStyle); 968 | var color = a.color; 969 | var opacity = a.alpha * ctx.globalAlpha; 970 | var lineWidth = ctx.lineScale_ * ctx.lineWidth; 971 | 972 | // VML cannot correctly render a line if the width is less than 1px. 973 | // In that case, we dilute the color to make the line look thinner. 974 | if (lineWidth < 1) { 975 | opacity *= lineWidth; 976 | } 977 | 978 | lineStr.push( 979 | '' 986 | ); 987 | } 988 | 989 | function appendFill(ctx, lineStr, min, max) { 990 | var fillStyle = ctx.fillStyle; 991 | var arcScaleX = ctx.arcScaleX_; 992 | var arcScaleY = ctx.arcScaleY_; 993 | var width = max.x - min.x; 994 | var height = max.y - min.y; 995 | if (fillStyle instanceof CanvasGradient_) { 996 | // TODO: Gradients transformed with the transformation matrix. 997 | var angle = 0; 998 | var focus = {x: 0, y: 0}; 999 | 1000 | // additional offset 1001 | var shift = 0; 1002 | // scale factor for offset 1003 | var expansion = 1; 1004 | 1005 | if (fillStyle.type_ == 'gradient') { 1006 | var x0 = fillStyle.x0_ / arcScaleX; 1007 | var y0 = fillStyle.y0_ / arcScaleY; 1008 | var x1 = fillStyle.x1_ / arcScaleX; 1009 | var y1 = fillStyle.y1_ / arcScaleY; 1010 | var p0 = getCoords(ctx, x0, y0); 1011 | var p1 = getCoords(ctx, x1, y1); 1012 | var dx = p1.x - p0.x; 1013 | var dy = p1.y - p0.y; 1014 | angle = Math.atan2(dx, dy) * 180 / Math.PI; 1015 | 1016 | // The angle should be a non-negative number. 1017 | if (angle < 0) { 1018 | angle += 360; 1019 | } 1020 | 1021 | // Very small angles produce an unexpected result because they are 1022 | // converted to a scientific notation string. 1023 | if (angle < 1e-6) { 1024 | angle = 0; 1025 | } 1026 | } else { 1027 | var p0 = getCoords(ctx, fillStyle.x0_, fillStyle.y0_); 1028 | focus = { 1029 | x: (p0.x - min.x) / width, 1030 | y: (p0.y - min.y) / height 1031 | }; 1032 | 1033 | width /= arcScaleX * Z; 1034 | height /= arcScaleY * Z; 1035 | var dimension = m.max(width, height); 1036 | shift = 2 * fillStyle.r0_ / dimension; 1037 | expansion = 2 * fillStyle.r1_ / dimension - shift; 1038 | } 1039 | 1040 | // We need to sort the color stops in ascending order by offset, 1041 | // otherwise IE won't interpret it correctly. 1042 | var stops = fillStyle.colors_; 1043 | stops.sort(function(cs1, cs2) { 1044 | return cs1.offset - cs2.offset; 1045 | }); 1046 | 1047 | var length = stops.length; 1048 | var color1 = stops[0].color; 1049 | var color2 = stops[length - 1].color; 1050 | var opacity1 = stops[0].alpha * ctx.globalAlpha; 1051 | var opacity2 = stops[length - 1].alpha * ctx.globalAlpha; 1052 | 1053 | var colors = []; 1054 | for (var i = 0; i < length; i++) { 1055 | var stop = stops[i]; 1056 | colors.push(stop.offset * expansion + shift + ' ' + stop.color); 1057 | } 1058 | 1059 | // When colors attribute is used, the meanings of opacity and o:opacity2 1060 | // are reversed. 1061 | lineStr.push(''); 1070 | } else if (fillStyle instanceof CanvasPattern_) { 1071 | if (width && height) { 1072 | var deltaLeft = -min.x; 1073 | var deltaTop = -min.y; 1074 | lineStr.push(''); 1082 | } 1083 | } else { 1084 | var a = processStyle(ctx.fillStyle); 1085 | var color = a.color; 1086 | var opacity = a.alpha * ctx.globalAlpha; 1087 | lineStr.push(''); 1089 | } 1090 | } 1091 | 1092 | contextPrototype.fill = function() { 1093 | this.stroke(true); 1094 | }; 1095 | 1096 | contextPrototype.closePath = function() { 1097 | this.currentPath_.push({type: 'close'}); 1098 | }; 1099 | 1100 | function getCoords(ctx, aX, aY) { 1101 | var m = ctx.m_; 1102 | return { 1103 | x: Z * (aX * m[0][0] + aY * m[1][0] + m[2][0]) - Z2, 1104 | y: Z * (aX * m[0][1] + aY * m[1][1] + m[2][1]) - Z2 1105 | }; 1106 | }; 1107 | 1108 | contextPrototype.save = function() { 1109 | var o = {}; 1110 | copyState(this, o); 1111 | this.aStack_.push(o); 1112 | this.mStack_.push(this.m_); 1113 | this.m_ = matrixMultiply(createMatrixIdentity(), this.m_); 1114 | }; 1115 | 1116 | contextPrototype.restore = function() { 1117 | if (this.aStack_.length) { 1118 | copyState(this.aStack_.pop(), this); 1119 | this.m_ = this.mStack_.pop(); 1120 | } 1121 | }; 1122 | 1123 | function matrixIsFinite(m) { 1124 | return isFinite(m[0][0]) && isFinite(m[0][1]) && 1125 | isFinite(m[1][0]) && isFinite(m[1][1]) && 1126 | isFinite(m[2][0]) && isFinite(m[2][1]); 1127 | } 1128 | 1129 | function setM(ctx, m, updateLineScale) { 1130 | if (!matrixIsFinite(m)) { 1131 | return; 1132 | } 1133 | ctx.m_ = m; 1134 | 1135 | if (updateLineScale) { 1136 | // Get the line scale. 1137 | // Determinant of this.m_ means how much the area is enlarged by the 1138 | // transformation. So its square root can be used as a scale factor 1139 | // for width. 1140 | var det = m[0][0] * m[1][1] - m[0][1] * m[1][0]; 1141 | ctx.lineScale_ = sqrt(abs(det)); 1142 | } 1143 | } 1144 | 1145 | contextPrototype.translate = function(aX, aY) { 1146 | var m1 = [ 1147 | [1, 0, 0], 1148 | [0, 1, 0], 1149 | [aX, aY, 1] 1150 | ]; 1151 | 1152 | setM(this, matrixMultiply(m1, this.m_), false); 1153 | }; 1154 | 1155 | contextPrototype.rotate = function(aRot) { 1156 | var c = mc(aRot); 1157 | var s = ms(aRot); 1158 | 1159 | var m1 = [ 1160 | [c, s, 0], 1161 | [-s, c, 0], 1162 | [0, 0, 1] 1163 | ]; 1164 | 1165 | setM(this, matrixMultiply(m1, this.m_), false); 1166 | }; 1167 | 1168 | contextPrototype.scale = function(aX, aY) { 1169 | this.arcScaleX_ *= aX; 1170 | this.arcScaleY_ *= aY; 1171 | var m1 = [ 1172 | [aX, 0, 0], 1173 | [0, aY, 0], 1174 | [0, 0, 1] 1175 | ]; 1176 | 1177 | setM(this, matrixMultiply(m1, this.m_), true); 1178 | }; 1179 | 1180 | contextPrototype.transform = function(m11, m12, m21, m22, dx, dy) { 1181 | var m1 = [ 1182 | [m11, m12, 0], 1183 | [m21, m22, 0], 1184 | [dx, dy, 1] 1185 | ]; 1186 | 1187 | setM(this, matrixMultiply(m1, this.m_), true); 1188 | }; 1189 | 1190 | contextPrototype.setTransform = function(m11, m12, m21, m22, dx, dy) { 1191 | var m = [ 1192 | [m11, m12, 0], 1193 | [m21, m22, 0], 1194 | [dx, dy, 1] 1195 | ]; 1196 | 1197 | setM(this, m, true); 1198 | }; 1199 | 1200 | /** 1201 | * The text drawing function. 1202 | * The maxWidth argument isn't taken in account, since no browser supports 1203 | * it yet. 1204 | */ 1205 | contextPrototype.drawText_ = function(text, x, y, maxWidth, stroke) { 1206 | var m = this.m_, 1207 | delta = 1000, 1208 | left = 0, 1209 | right = delta, 1210 | offset = {x: 0, y: 0}, 1211 | lineStr = []; 1212 | 1213 | var fontStyle = getComputedStyle(processFontStyle(this.font), 1214 | this.element_); 1215 | 1216 | var fontStyleString = buildStyle(fontStyle); 1217 | 1218 | var elementStyle = this.element_.currentStyle; 1219 | var textAlign = this.textAlign.toLowerCase(); 1220 | switch (textAlign) { 1221 | case 'left': 1222 | case 'center': 1223 | case 'right': 1224 | break; 1225 | case 'end': 1226 | textAlign = elementStyle.direction == 'ltr' ? 'right' : 'left'; 1227 | break; 1228 | case 'start': 1229 | textAlign = elementStyle.direction == 'rtl' ? 'right' : 'left'; 1230 | break; 1231 | default: 1232 | textAlign = 'left'; 1233 | } 1234 | 1235 | // 1.75 is an arbitrary number, as there is no info about the text baseline 1236 | switch (this.textBaseline) { 1237 | case 'hanging': 1238 | case 'top': 1239 | offset.y = fontStyle.size / 1.75; 1240 | break; 1241 | case 'middle': 1242 | break; 1243 | default: 1244 | case null: 1245 | case 'alphabetic': 1246 | case 'ideographic': 1247 | case 'bottom': 1248 | offset.y = -fontStyle.size / 2.25; 1249 | break; 1250 | } 1251 | 1252 | switch(textAlign) { 1253 | case 'right': 1254 | left = delta; 1255 | right = 0.05; 1256 | break; 1257 | case 'center': 1258 | left = right = delta / 2; 1259 | break; 1260 | } 1261 | 1262 | var d = getCoords(this, x + offset.x, y + offset.y); 1263 | 1264 | lineStr.push(''); 1268 | 1269 | if (stroke) { 1270 | appendStroke(this, lineStr); 1271 | } else { 1272 | // TODO: Fix the min and max params. 1273 | appendFill(this, lineStr, {x: -left, y: 0}, 1274 | {x: right, y: fontStyle.size}); 1275 | } 1276 | 1277 | var skewM = m[0][0].toFixed(3) + ',' + m[1][0].toFixed(3) + ',' + 1278 | m[0][1].toFixed(3) + ',' + m[1][1].toFixed(3) + ',0,0'; 1279 | 1280 | var skewOffset = mr(d.x / Z) + ',' + mr(d.y / Z); 1281 | 1282 | lineStr.push('', 1284 | '', 1285 | ''); 1290 | 1291 | this.element_.insertAdjacentHTML('beforeEnd', lineStr.join('')); 1292 | }; 1293 | 1294 | contextPrototype.fillText = function(text, x, y, maxWidth) { 1295 | this.drawText_(text, x, y, maxWidth, false); 1296 | }; 1297 | 1298 | contextPrototype.strokeText = function(text, x, y, maxWidth) { 1299 | this.drawText_(text, x, y, maxWidth, true); 1300 | }; 1301 | 1302 | contextPrototype.measureText = function(text) { 1303 | if (!this.textMeasureEl_) { 1304 | var s = ''; 1307 | this.element_.insertAdjacentHTML('beforeEnd', s); 1308 | this.textMeasureEl_ = this.element_.lastChild; 1309 | } 1310 | var doc = this.element_.ownerDocument; 1311 | this.textMeasureEl_.innerHTML = ''; 1312 | this.textMeasureEl_.style.font = this.font; 1313 | // Don't use innerHTML or innerText because they allow markup/whitespace. 1314 | this.textMeasureEl_.appendChild(doc.createTextNode(text)); 1315 | return {width: this.textMeasureEl_.offsetWidth}; 1316 | }; 1317 | 1318 | /******** STUBS ********/ 1319 | contextPrototype.clip = function() { 1320 | // TODO: Implement 1321 | }; 1322 | 1323 | contextPrototype.arcTo = function() { 1324 | // TODO: Implement 1325 | }; 1326 | 1327 | contextPrototype.createPattern = function(image, repetition) { 1328 | return new CanvasPattern_(image, repetition); 1329 | }; 1330 | 1331 | // Gradient / Pattern Stubs 1332 | function CanvasGradient_(aType) { 1333 | this.type_ = aType; 1334 | this.x0_ = 0; 1335 | this.y0_ = 0; 1336 | this.r0_ = 0; 1337 | this.x1_ = 0; 1338 | this.y1_ = 0; 1339 | this.r1_ = 0; 1340 | this.colors_ = []; 1341 | } 1342 | 1343 | CanvasGradient_.prototype.addColorStop = function(aOffset, aColor) { 1344 | aColor = processStyle(aColor); 1345 | this.colors_.push({offset: aOffset, 1346 | color: aColor.color, 1347 | alpha: aColor.alpha}); 1348 | }; 1349 | 1350 | function CanvasPattern_(image, repetition) { 1351 | assertImageIsValid(image); 1352 | switch (repetition) { 1353 | case 'repeat': 1354 | case null: 1355 | case '': 1356 | this.repetition_ = 'repeat'; 1357 | break 1358 | case 'repeat-x': 1359 | case 'repeat-y': 1360 | case 'no-repeat': 1361 | this.repetition_ = repetition; 1362 | break; 1363 | default: 1364 | throwException('SYNTAX_ERR'); 1365 | } 1366 | 1367 | this.src_ = image.src; 1368 | this.width_ = image.width; 1369 | this.height_ = image.height; 1370 | } 1371 | 1372 | function throwException(s) { 1373 | throw new DOMException_(s); 1374 | } 1375 | 1376 | function assertImageIsValid(img) { 1377 | if (!img || img.nodeType != 1 || img.tagName != 'IMG') { 1378 | throwException('TYPE_MISMATCH_ERR'); 1379 | } 1380 | if (img.readyState != 'complete') { 1381 | throwException('INVALID_STATE_ERR'); 1382 | } 1383 | } 1384 | 1385 | function DOMException_(s) { 1386 | this.code = this[s]; 1387 | this.message = s +': DOM Exception ' + this.code; 1388 | } 1389 | var p = DOMException_.prototype = new Error; 1390 | p.INDEX_SIZE_ERR = 1; 1391 | p.DOMSTRING_SIZE_ERR = 2; 1392 | p.HIERARCHY_REQUEST_ERR = 3; 1393 | p.WRONG_DOCUMENT_ERR = 4; 1394 | p.INVALID_CHARACTER_ERR = 5; 1395 | p.NO_DATA_ALLOWED_ERR = 6; 1396 | p.NO_MODIFICATION_ALLOWED_ERR = 7; 1397 | p.NOT_FOUND_ERR = 8; 1398 | p.NOT_SUPPORTED_ERR = 9; 1399 | p.INUSE_ATTRIBUTE_ERR = 10; 1400 | p.INVALID_STATE_ERR = 11; 1401 | p.SYNTAX_ERR = 12; 1402 | p.INVALID_MODIFICATION_ERR = 13; 1403 | p.NAMESPACE_ERR = 14; 1404 | p.INVALID_ACCESS_ERR = 15; 1405 | p.VALIDATION_ERR = 16; 1406 | p.TYPE_MISMATCH_ERR = 17; 1407 | 1408 | // set up externs 1409 | G_vmlCanvasManager = G_vmlCanvasManager_; 1410 | CanvasRenderingContext2D = CanvasRenderingContext2D_; 1411 | CanvasGradient = CanvasGradient_; 1412 | CanvasPattern = CanvasPattern_; 1413 | DOMException = DOMException_; 1414 | })(); 1415 | 1416 | } // if -------------------------------------------------------------------------------- /Chart.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Chart.js 3 | * http://chartjs.org/ 4 | * 5 | * Copyright 2013 Nick Downie 6 | * Released under the MIT license 7 | * https://github.com/nnnick/Chart.js/blob/master/LICENSE.md 8 | */ 9 | 10 | //Define the global Chart Variable as a class. 11 | window.Chart = function(context){ 12 | 13 | var chart = this; 14 | var int_language = "en-EN"; 15 | 16 | //Easing functions adapted from Robert Penner's easing equations 17 | //http://www.robertpenner.com/easing/ 18 | 19 | var animationOptions = { 20 | linear : function (t){ 21 | return t; 22 | }, 23 | easeInQuad: function (t) { 24 | return t*t; 25 | }, 26 | easeOutQuad: function (t) { 27 | return -1 *t*(t-2); 28 | }, 29 | easeInOutQuad: function (t) { 30 | if ((t/=1/2) < 1) return 1/2*t*t; 31 | return -1/2 * ((--t)*(t-2) - 1); 32 | }, 33 | easeInCubic: function (t) { 34 | return t*t*t; 35 | }, 36 | easeOutCubic: function (t) { 37 | return 1*((t=t/1-1)*t*t + 1); 38 | }, 39 | easeInOutCubic: function (t) { 40 | if ((t/=1/2) < 1) return 1/2*t*t*t; 41 | return 1/2*((t-=2)*t*t + 2); 42 | }, 43 | easeInQuart: function (t) { 44 | return t*t*t*t; 45 | }, 46 | easeOutQuart: function (t) { 47 | return -1 * ((t=t/1-1)*t*t*t - 1); 48 | }, 49 | easeInOutQuart: function (t) { 50 | if ((t/=1/2) < 1) return 1/2*t*t*t*t; 51 | return -1/2 * ((t-=2)*t*t*t - 2); 52 | }, 53 | easeInQuint: function (t) { 54 | return 1*(t/=1)*t*t*t*t; 55 | }, 56 | easeOutQuint: function (t) { 57 | return 1*((t=t/1-1)*t*t*t*t + 1); 58 | }, 59 | easeInOutQuint: function (t) { 60 | if ((t/=1/2) < 1) return 1/2*t*t*t*t*t; 61 | return 1/2*((t-=2)*t*t*t*t + 2); 62 | }, 63 | easeInSine: function (t) { 64 | return -1 * Math.cos(t/1 * (Math.PI/2)) + 1; 65 | }, 66 | easeOutSine: function (t) { 67 | return 1 * Math.sin(t/1 * (Math.PI/2)); 68 | }, 69 | easeInOutSine: function (t) { 70 | return -1/2 * (Math.cos(Math.PI*t/1) - 1); 71 | }, 72 | easeInExpo: function (t) { 73 | return (t==0) ? 1 : 1 * Math.pow(2, 10 * (t/1 - 1)); 74 | }, 75 | easeOutExpo: function (t) { 76 | return (t==1) ? 1 : 1 * (-Math.pow(2, -10 * t/1) + 1); 77 | }, 78 | easeInOutExpo: function (t) { 79 | if (t==0) return 0; 80 | if (t==1) return 1; 81 | if ((t/=1/2) < 1) return 1/2 * Math.pow(2, 10 * (t - 1)); 82 | return 1/2 * (-Math.pow(2, -10 * --t) + 2); 83 | }, 84 | easeInCirc: function (t) { 85 | if (t>=1) return t; 86 | return -1 * (Math.sqrt(1 - (t/=1)*t) - 1); 87 | }, 88 | easeOutCirc: function (t) { 89 | return 1 * Math.sqrt(1 - (t=t/1-1)*t); 90 | }, 91 | easeInOutCirc: function (t) { 92 | if ((t/=1/2) < 1) return -1/2 * (Math.sqrt(1 - t*t) - 1); 93 | return 1/2 * (Math.sqrt(1 - (t-=2)*t) + 1); 94 | }, 95 | easeInElastic: function (t) { 96 | var s=1.70158;var p=0;var a=1; 97 | if (t==0) return 0; if ((t/=1)==1) return 1; if (!p) p=1*.3; 98 | if (a < Math.abs(1)) { a=1; var s=p/4; } 99 | else var s = p/(2*Math.PI) * Math.asin (1/a); 100 | return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*1-s)*(2*Math.PI)/p )); 101 | }, 102 | easeOutElastic: function (t) { 103 | var s=1.70158;var p=0;var a=1; 104 | if (t==0) return 0; if ((t/=1)==1) return 1; if (!p) p=1*.3; 105 | if (a < Math.abs(1)) { a=1; var s=p/4; } 106 | else var s = p/(2*Math.PI) * Math.asin (1/a); 107 | return a*Math.pow(2,-10*t) * Math.sin( (t*1-s)*(2*Math.PI)/p ) + 1; 108 | }, 109 | easeInOutElastic: function (t) { 110 | var s=1.70158;var p=0;var a=1; 111 | if (t==0) return 0; if ((t/=1/2)==2) return 1; if (!p) p=1*(.3*1.5); 112 | if (a < Math.abs(1)) { a=1; var s=p/4; } 113 | else var s = p/(2*Math.PI) * Math.asin (1/a); 114 | if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*1-s)*(2*Math.PI)/p )); 115 | return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*1-s)*(2*Math.PI)/p )*.5 + 1; 116 | }, 117 | easeInBack: function (t) { 118 | var s = 1.70158; 119 | return 1*(t/=1)*t*((s+1)*t - s); 120 | }, 121 | easeOutBack: function (t) { 122 | var s = 1.70158; 123 | return 1*((t=t/1-1)*t*((s+1)*t + s) + 1); 124 | }, 125 | easeInOutBack: function (t) { 126 | var s = 1.70158; 127 | if ((t/=1/2) < 1) return 1/2*(t*t*(((s*=(1.525))+1)*t - s)); 128 | return 1/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2); 129 | }, 130 | easeInBounce: function (t) { 131 | return 1 - animationOptions.easeOutBounce (1-t); 132 | }, 133 | easeOutBounce: function (t) { 134 | if ((t/=1) < (1/2.75)) { 135 | return 1*(7.5625*t*t); 136 | } else if (t < (2/2.75)) { 137 | return 1*(7.5625*(t-=(1.5/2.75))*t + .75); 138 | } else if (t < (2.5/2.75)) { 139 | return 1*(7.5625*(t-=(2.25/2.75))*t + .9375); 140 | } else { 141 | return 1*(7.5625*(t-=(2.625/2.75))*t + .984375); 142 | } 143 | }, 144 | easeInOutBounce: function (t) { 145 | if (t < 1/2) return animationOptions.easeInBounce (t*2) * .5; 146 | return animationOptions.easeOutBounce (t*2-1) * .5 + 1*.5; 147 | } 148 | }; 149 | 150 | //Variables global to the chart 151 | var width = context.canvas.width; 152 | var height = context.canvas.height; 153 | 154 | 155 | //High pixel density displays - multiply the size of the canvas height/width by the device pixel ratio, then scale. 156 | if (window.devicePixelRatio) { 157 | context.canvas.style.width = width + "px"; 158 | context.canvas.style.height = height + "px"; 159 | context.canvas.height = height * window.devicePixelRatio; 160 | context.canvas.width = width * window.devicePixelRatio; 161 | context.scale(window.devicePixelRatio, window.devicePixelRatio); 162 | } 163 | 164 | this.PolarArea = function(data,options){ 165 | 166 | chart.PolarArea.defaults = { 167 | scaleOverlay : true, 168 | scaleOverride : false, 169 | scaleSteps : null, 170 | scaleStepWidth : null, 171 | scaleStartValue : null, 172 | scaleShowLine : true, 173 | scaleLineColor : "rgba(0,0,0,.1)", 174 | scaleLineWidth : 1, 175 | scaleShowLabels : true, 176 | scaleLabel : "<%=value%>", 177 | scaleFontFamily : "'Arial'", 178 | scaleFontSize : 12, 179 | scaleFontStyle : "normal", 180 | scaleFontColor : "#666", 181 | scaleShowLabelBackdrop : true, 182 | scaleBackdropColor : "rgba(255,255,255,0.75)", 183 | scaleBackdropPaddingY : 2, 184 | scaleBackdropPaddingX : 2, 185 | segmentShowStroke : true, 186 | segmentStrokeColor : "#fff", 187 | segmentStrokeWidth : 2, 188 | animation : true, 189 | animationSteps : 100, 190 | animationEasing : "easeOutBounce", 191 | animateRotate : true, 192 | animateScale : false, 193 | onAnimationComplete : null 194 | }; 195 | 196 | var config = (options)? mergeChartConfig(chart.PolarArea.defaults,options) : chart.PolarArea.defaults; 197 | 198 | return new PolarArea(data,config,context); 199 | }; 200 | 201 | this.Radar = function(data,options){ 202 | 203 | chart.Radar.defaults = { 204 | scaleOverlay : false, 205 | scaleOverride : false, 206 | scaleSteps : null, 207 | scaleStepWidth : null, 208 | scaleStartValue : null, 209 | scaleShowLine : true, 210 | scaleLineColor : "rgba(0,0,0,.1)", 211 | scaleLineWidth : 1, 212 | scaleShowLabels : false, 213 | scaleLabel : "<%=value%>", 214 | scaleFontFamily : "'Arial'", 215 | scaleFontSize : 12, 216 | scaleFontStyle : "normal", 217 | scaleFontColor : "#666", 218 | scaleShowLabelBackdrop : true, 219 | scaleBackdropColor : "rgba(255,255,255,0.75)", 220 | scaleBackdropPaddingY : 2, 221 | scaleBackdropPaddingX : 2, 222 | angleShowLineOut : true, 223 | angleLineColor : "rgba(0,0,0,.1)", 224 | angleLineWidth : 1, 225 | pointLabelFontFamily : "'Arial'", 226 | pointLabelFontStyle : "normal", 227 | pointLabelFontSize : 12, 228 | pointLabelFontColor : "#666", 229 | pointDot : true, 230 | pointDotRadius : 3, 231 | pointDotStrokeWidth : 1, 232 | datasetStroke : true, 233 | datasetStrokeWidth : 2, 234 | datasetFill : true, 235 | animation : true, 236 | animationSteps : 60, 237 | animationEasing : "easeOutQuart", 238 | onAnimationComplete : null 239 | }; 240 | 241 | var config = (options)? mergeChartConfig(chart.Radar.defaults,options) : chart.Radar.defaults; 242 | 243 | return new Radar(data,config,context); 244 | }; 245 | 246 | this.Pie = function(data,options){ 247 | chart.Pie.defaults = { 248 | segmentShowStroke : true, 249 | segmentStrokeColor : "#fff", 250 | segmentStrokeWidth : 2, 251 | animation : true, 252 | animationSteps : 100, 253 | animationEasing : "easeOutBounce", 254 | animateRotate : true, 255 | animateScale : false, 256 | onAnimationComplete : null 257 | }; 258 | 259 | var config = (options)? mergeChartConfig(chart.Pie.defaults,options) : chart.Pie.defaults; 260 | 261 | return new Pie(data,config,context); 262 | }; 263 | 264 | this.Doughnut = function(data,options){ 265 | 266 | chart.Doughnut.defaults = { 267 | segmentShowStroke : true, 268 | segmentStrokeColor : "#fff", 269 | segmentStrokeWidth : 2, 270 | percentageInnerCutout : 50, 271 | animation : true, 272 | animationSteps : 100, 273 | animationEasing : "easeOutBounce", 274 | animateRotate : true, 275 | animateScale : false, 276 | onAnimationComplete : null 277 | }; 278 | 279 | var config = (options)? mergeChartConfig(chart.Doughnut.defaults,options) : chart.Doughnut.defaults; 280 | 281 | return new Doughnut(data,config,context); 282 | 283 | }; 284 | 285 | this.Line = function(data,options){ 286 | 287 | chart.Line.defaults = { 288 | scaleOverlay : false, 289 | scaleOverride : false, 290 | scaleSteps : null, 291 | scaleStepWidth : null, 292 | scaleStartValue : null, 293 | scaleLineColor : "rgba(0,0,0,.1)", 294 | scaleLineWidth : 1, 295 | scaleShowLabels : true, 296 | scaleLabel : "<%=value%>", 297 | scaleFontFamily : "'Arial'", 298 | scaleFontSize : 12, 299 | scaleFontStyle : "normal", 300 | scaleFontColor : "#666", 301 | scaleShowGridLines : true, 302 | scaleGridLineColor : "rgba(0,0,0,.05)", 303 | scaleGridLineWidth : 1, 304 | bezierCurve : true, 305 | pointDot : true, 306 | pointDotRadius : 4, 307 | pointDotStrokeWidth : 2, 308 | datasetStroke : true, 309 | datasetStrokeWidth : 2, 310 | datasetFill : true, 311 | animation : true, 312 | animationSteps : 60, 313 | animationEasing : "easeOutQuart", 314 | onAnimationComplete : null 315 | }; 316 | var config = (options) ? mergeChartConfig(chart.Line.defaults,options) : chart.Line.defaults; 317 | 318 | return new Line(data,config,context); 319 | } 320 | 321 | this.Bar = function(data,options){ 322 | chart.Bar.defaults = { 323 | scaleOverlay : false, 324 | scaleOverride : false, 325 | scaleSteps : null, 326 | scaleStepWidth : null, 327 | scaleStartValue : null, 328 | scaleLineColor : "rgba(0,0,0,.1)", 329 | scaleLineWidth : 1, 330 | scaleShowLabels : true, 331 | scaleLabel : "<%=value%>", 332 | scaleFontFamily : "'Arial'", 333 | scaleFontSize : 12, 334 | scaleFontStyle : "normal", 335 | scaleFontColor : "#666", 336 | scaleShowGridLines : true, 337 | scaleGridLineColor : "rgba(0,0,0,.05)", 338 | scaleGridLineWidth : 1, 339 | barShowStroke : true, 340 | barStrokeWidth : 2, 341 | barValueSpacing : 5, 342 | barDatasetSpacing : 1, 343 | animation : true, 344 | animationSteps : 60, 345 | animationEasing : "easeOutQuart", 346 | onAnimationComplete : null 347 | }; 348 | var config = (options) ? mergeChartConfig(chart.Bar.defaults,options) : chart.Bar.defaults; 349 | 350 | return new Bar(data,config,context); 351 | } 352 | 353 | var clear = function(c){ 354 | c.clearRect(0, 0, width, height); 355 | }; 356 | 357 | var PolarArea = function(data,config,ctx){ 358 | var maxSize, scaleHop, calculatedScale, labelHeight, scaleHeight, valueBounds, labelTemplateString; 359 | 360 | 361 | calculateDrawingSizes(); 362 | 363 | valueBounds = getValueBounds(); 364 | 365 | labelTemplateString = (config.scaleShowLabels)? config.scaleLabel : null; 366 | 367 | //Check and set the scale 368 | if (!config.scaleOverride){ 369 | 370 | calculatedScale = calculateScale(scaleHeight,valueBounds.maxSteps,valueBounds.minSteps,valueBounds.maxValue,valueBounds.minValue,labelTemplateString); 371 | } 372 | else { 373 | calculatedScale = { 374 | steps : config.scaleSteps, 375 | stepValue : config.scaleStepWidth, 376 | graphMin : config.scaleStartValue, 377 | labels : [] 378 | } 379 | populateLabels(labelTemplateString, calculatedScale.labels,calculatedScale.steps,config.scaleStartValue,config.scaleStepWidth); 380 | } 381 | 382 | scaleHop = maxSize/(calculatedScale.steps); 383 | 384 | //Wrap in an animation loop wrapper 385 | animationLoop(config,drawScale,drawAllSegments,ctx); 386 | 387 | function calculateDrawingSizes(){ 388 | maxSize = (Min([width,height])/2); 389 | //Remove whatever is larger - the font size or line width. 390 | 391 | maxSize -= Max([config.scaleFontSize*0.5,config.scaleLineWidth*0.5]); 392 | 393 | labelHeight = config.scaleFontSize*2; 394 | //If we're drawing the backdrop - add the Y padding to the label height and remove from drawing region. 395 | if (config.scaleShowLabelBackdrop){ 396 | labelHeight += (2 * config.scaleBackdropPaddingY); 397 | maxSize -= config.scaleBackdropPaddingY*1.5; 398 | } 399 | 400 | scaleHeight = maxSize; 401 | //If the label height is less than 5, set it to 5 so we don't have lines on top of each other. 402 | labelHeight = Default(labelHeight,5); 403 | } 404 | function drawScale(){ 405 | for (var i=0; i upperValue) {upperValue = data[i].value;} 474 | if (data[i].value < lowerValue) {lowerValue = data[i].value;} 475 | }; 476 | 477 | var maxSteps = Math.floor((scaleHeight / (labelHeight*0.66))); 478 | var minSteps = Math.floor((scaleHeight / labelHeight*0.5)); 479 | 480 | return { 481 | maxValue : upperValue, 482 | minValue : lowerValue, 483 | maxSteps : maxSteps, 484 | minSteps : minSteps 485 | }; 486 | 487 | 488 | } 489 | } 490 | 491 | var Radar = function (data,config,ctx) { 492 | var maxSize, scaleHop, calculatedScale, labelHeight, scaleHeight, valueBounds, labelTemplateString; 493 | 494 | //If no labels are defined set to an empty array, so referencing length for looping doesn't blow up. 495 | if (!data.labels) data.labels = []; 496 | 497 | calculateDrawingSizes(); 498 | 499 | var valueBounds = getValueBounds(); 500 | 501 | labelTemplateString = (config.scaleShowLabels)? config.scaleLabel : null; 502 | 503 | //Check and set the scale 504 | if (!config.scaleOverride){ 505 | 506 | calculatedScale = calculateScale(scaleHeight,valueBounds.maxSteps,valueBounds.minSteps,valueBounds.maxValue,valueBounds.minValue,labelTemplateString); 507 | } 508 | else { 509 | calculatedScale = { 510 | steps : config.scaleSteps, 511 | stepValue : config.scaleStepWidth, 512 | graphMin : config.scaleStartValue, 513 | labels : [] 514 | } 515 | populateLabels(labelTemplateString, calculatedScale.labels,calculatedScale.steps,config.scaleStartValue,config.scaleStepWidth); 516 | } 517 | 518 | scaleHop = maxSize/(calculatedScale.steps); 519 | 520 | animationLoop(config,drawScale,drawAllDataPoints,ctx); 521 | 522 | //Radar specific functions. 523 | function drawAllDataPoints(animationDecimal){ 524 | var rotationDegree = (2*Math.PI)/data.datasets[0].data.length; 525 | 526 | ctx.save(); 527 | //translate to the centre of the canvas. 528 | ctx.translate(width/2,height/2); 529 | 530 | //We accept multiple data sets for radar charts, so show loop through each set 531 | for (var i=0; i Math.PI){ 636 | ctx.textAlign = "right"; 637 | } 638 | else{ 639 | ctx.textAlign = "left"; 640 | } 641 | 642 | ctx.textBaseline = "middle"; 643 | 644 | ctx.fillText(data.labels[k],opposite,-adjacent); 645 | 646 | } 647 | ctx.restore(); 648 | }; 649 | function calculateDrawingSizes(){ 650 | maxSize = (Min([width,height])/2); 651 | 652 | labelHeight = config.scaleFontSize*2; 653 | 654 | var labelLength = 0; 655 | for (var i=0; ilabelLength) labelLength = textMeasurement; 659 | } 660 | 661 | //Figure out whats the largest - the height of the text or the width of what's there, and minus it from the maximum usable size. 662 | maxSize -= Max([labelLength,((config.pointLabelFontSize/2)*1.5)]); 663 | 664 | maxSize -= config.pointLabelFontSize; 665 | maxSize = CapValue(maxSize, null, 0); 666 | scaleHeight = maxSize; 667 | //If the label height is less than 5, set it to 5 so we don't have lines on top of each other. 668 | labelHeight = Default(labelHeight,5); 669 | }; 670 | function getValueBounds() { 671 | var upperValue = Number.MIN_VALUE; 672 | var lowerValue = Number.MAX_VALUE; 673 | 674 | for (var i=0; i upperValue){upperValue = data.datasets[i].data[j]} 677 | if (data.datasets[i].data[j] < lowerValue){lowerValue = data.datasets[i].data[j]} 678 | } 679 | } 680 | 681 | var maxSteps = Math.floor((scaleHeight / (labelHeight*0.66))); 682 | var minSteps = Math.floor((scaleHeight / labelHeight*0.5)); 683 | 684 | return { 685 | maxValue : upperValue, 686 | minValue : lowerValue, 687 | maxSteps : maxSteps, 688 | minSteps : minSteps 689 | }; 690 | 691 | 692 | } 693 | } 694 | 695 | var Pie = function(data,config,ctx){ 696 | var segmentTotal = 0; 697 | 698 | //In case we have a canvas that is not a square. Minus 5 pixels as padding round the edge. 699 | var pieRadius = Min([height/2,width/2]) - 5; 700 | 701 | for (var i=0; i 0){ 872 | ctx.save(); 873 | ctx.textAlign = "right"; 874 | } 875 | else{ 876 | ctx.textAlign = "center"; 877 | } 878 | ctx.fillStyle = config.scaleFontColor; 879 | for (var i=0; i 0){ 882 | ctx.translate(yAxisPosX + i*valueHop,xAxisPosY + config.scaleFontSize); 883 | ctx.rotate(-(rotateLabels * (Math.PI/180))); 884 | ctx.fillText(data.labels[i], 0,0); 885 | ctx.restore(); 886 | } 887 | 888 | else{ 889 | ctx.fillText(data.labels[i], yAxisPosX + i*valueHop,xAxisPosY + config.scaleFontSize+3); 890 | } 891 | 892 | ctx.beginPath(); 893 | ctx.moveTo(yAxisPosX + i * valueHop, xAxisPosY+3); 894 | 895 | //Check i isnt 0, so we dont go over the Y axis twice. 896 | if(config.scaleShowGridLines && i>0){ 897 | ctx.lineWidth = config.scaleGridLineWidth; 898 | ctx.strokeStyle = config.scaleGridLineColor; 899 | ctx.lineTo(yAxisPosX + i * valueHop, 5); 900 | } 901 | else{ 902 | ctx.lineTo(yAxisPosX + i * valueHop, xAxisPosY+3); 903 | } 904 | ctx.stroke(); 905 | } 906 | 907 | //Y axis 908 | ctx.lineWidth = config.scaleLineWidth; 909 | ctx.strokeStyle = config.scaleLineColor; 910 | ctx.beginPath(); 911 | ctx.moveTo(yAxisPosX,xAxisPosY+5); 912 | ctx.lineTo(yAxisPosX,5); 913 | ctx.stroke(); 914 | 915 | ctx.textAlign = "right"; 916 | ctx.textBaseline = "middle"; 917 | for (var j=0; j longestText)? measuredText : longestText; 946 | } 947 | //Add a little extra padding from the y axis 948 | longestText +=10; 949 | } 950 | xAxisLength = width - longestText - widestXLabel; 951 | valueHop = Math.floor(xAxisLength/(data.labels.length-1)); 952 | 953 | yAxisPosX = width-widestXLabel/2-xAxisLength; 954 | xAxisPosY = scaleHeight + config.scaleFontSize/2; 955 | } 956 | function calculateDrawingSizes(){ 957 | maxSize = height; 958 | 959 | //Need to check the X axis first - measure the length of each text metric, and figure out if we need to rotate by 45 degrees. 960 | ctx.font = config.scaleFontStyle + " " + config.scaleFontSize+"px " + config.scaleFontFamily; 961 | widestXLabel = 1; 962 | for (var i=0; i widestXLabel)? textLength : widestXLabel; 966 | } 967 | if (width/data.labels.length < widestXLabel){ 968 | rotateLabels = 45; 969 | if (width/data.labels.length < Math.cos(rotateLabels) * widestXLabel){ 970 | rotateLabels = 90; 971 | maxSize -= widestXLabel; 972 | } 973 | else{ 974 | maxSize -= Math.sin(rotateLabels) * widestXLabel; 975 | } 976 | } 977 | else{ 978 | maxSize -= config.scaleFontSize; 979 | } 980 | 981 | //Add a little padding between the x line and the text 982 | maxSize -= 5; 983 | 984 | 985 | labelHeight = config.scaleFontSize; 986 | 987 | maxSize -= labelHeight; 988 | //Set 5 pixels greater than the font size to allow for a little padding from the X axis. 989 | 990 | scaleHeight = maxSize; 991 | 992 | //Then get the area above we can safely draw on. 993 | 994 | } 995 | function getValueBounds() { 996 | var upperValue = Number.MIN_VALUE; 997 | var lowerValue = Number.MAX_VALUE; 998 | for (var i=0; i upperValue) { upperValue = data.datasets[i].data[j] }; 1001 | if ( data.datasets[i].data[j] < lowerValue) { lowerValue = data.datasets[i].data[j] }; 1002 | } 1003 | }; 1004 | 1005 | var maxSteps = Math.floor((scaleHeight / (labelHeight*0.66))); 1006 | var minSteps = Math.floor((scaleHeight / labelHeight*0.5)); 1007 | 1008 | return { 1009 | maxValue : upperValue, 1010 | minValue : lowerValue, 1011 | maxSteps : maxSteps, 1012 | minSteps : minSteps 1013 | }; 1014 | 1015 | 1016 | } 1017 | 1018 | 1019 | } 1020 | 1021 | var Bar = function(data,config,ctx){ 1022 | var maxSize, scaleHop, calculatedScale, labelHeight, scaleHeight, valueBounds, labelTemplateString, valueHop,widestXLabel, xAxisLength,yAxisPosX,xAxisPosY,barWidth, rotateLabels = 0; 1023 | 1024 | calculateDrawingSizes(); 1025 | 1026 | valueBounds = getValueBounds(); 1027 | //Check and set the scale 1028 | labelTemplateString = (config.scaleShowLabels)? config.scaleLabel : ""; 1029 | if (!config.scaleOverride){ 1030 | 1031 | calculatedScale = calculateScale(scaleHeight,valueBounds.maxSteps,valueBounds.minSteps,valueBounds.maxValue,valueBounds.minValue,labelTemplateString); 1032 | } 1033 | else { 1034 | calculatedScale = { 1035 | steps : config.scaleSteps, 1036 | stepValue : config.scaleStepWidth, 1037 | graphMin : config.scaleStartValue, 1038 | labels : [] 1039 | } 1040 | populateLabels(labelTemplateString, calculatedScale.labels,calculatedScale.steps,config.scaleStartValue,config.scaleStepWidth); 1041 | } 1042 | 1043 | scaleHop = Math.floor(scaleHeight/calculatedScale.steps); 1044 | calculateXAxisSize(); 1045 | animationLoop(config,drawScale,drawBars,ctx); 1046 | 1047 | function drawBars(animPc){ 1048 | ctx.lineWidth = config.barStrokeWidth; 1049 | for (var i=0; i 0){ 1080 | ctx.save(); 1081 | ctx.textAlign = "right"; 1082 | } 1083 | else{ 1084 | ctx.textAlign = "center"; 1085 | } 1086 | ctx.fillStyle = config.scaleFontColor; 1087 | for (var i=0; i 0){ 1090 | ctx.translate(yAxisPosX + i*valueHop,xAxisPosY + config.scaleFontSize); 1091 | ctx.rotate(-(rotateLabels * (Math.PI/180))); 1092 | ctx.fillText(data.labels[i], 0,0); 1093 | ctx.restore(); 1094 | } 1095 | 1096 | else{ 1097 | ctx.fillText(data.labels[i], yAxisPosX + i*valueHop + valueHop/2,xAxisPosY + config.scaleFontSize+3); 1098 | } 1099 | 1100 | ctx.beginPath(); 1101 | ctx.moveTo(yAxisPosX + (i+1) * valueHop, xAxisPosY+3); 1102 | 1103 | //Check i isnt 0, so we dont go over the Y axis twice. 1104 | ctx.lineWidth = config.scaleGridLineWidth; 1105 | ctx.strokeStyle = config.scaleGridLineColor; 1106 | ctx.lineTo(yAxisPosX + (i+1) * valueHop, 5); 1107 | ctx.stroke(); 1108 | } 1109 | 1110 | //Y axis 1111 | ctx.lineWidth = config.scaleLineWidth; 1112 | ctx.strokeStyle = config.scaleLineColor; 1113 | ctx.beginPath(); 1114 | ctx.moveTo(yAxisPosX,xAxisPosY+5); 1115 | ctx.lineTo(yAxisPosX,5); 1116 | ctx.stroke(); 1117 | 1118 | ctx.textAlign = "right"; 1119 | ctx.textBaseline = "middle"; 1120 | for (var j=0; j longestText)? measuredText : longestText; 1148 | } 1149 | //Add a little extra padding from the y axis 1150 | longestText +=10; 1151 | } 1152 | xAxisLength = width - longestText - widestXLabel; 1153 | valueHop = Math.floor(xAxisLength/(data.labels.length)); 1154 | 1155 | barWidth = (valueHop - config.scaleGridLineWidth*2 - (config.barValueSpacing*2) - (config.barDatasetSpacing*data.datasets.length-1) - ((config.barStrokeWidth/2)*data.datasets.length-1))/data.datasets.length; 1156 | 1157 | yAxisPosX = width-widestXLabel/2-xAxisLength; 1158 | xAxisPosY = scaleHeight + config.scaleFontSize/2; 1159 | } 1160 | function calculateDrawingSizes(){ 1161 | maxSize = height; 1162 | 1163 | //Need to check the X axis first - measure the length of each text metric, and figure out if we need to rotate by 45 degrees. 1164 | ctx.font = config.scaleFontStyle + " " + config.scaleFontSize+"px " + config.scaleFontFamily; 1165 | widestXLabel = 1; 1166 | for (var i=0; i widestXLabel)? textLength : widestXLabel; 1170 | } 1171 | if (width/data.labels.length < widestXLabel){ 1172 | rotateLabels = 45; 1173 | if (width/data.labels.length < Math.cos(rotateLabels) * widestXLabel){ 1174 | rotateLabels = 90; 1175 | maxSize -= widestXLabel; 1176 | } 1177 | else{ 1178 | maxSize -= Math.sin(rotateLabels) * widestXLabel; 1179 | } 1180 | } 1181 | else{ 1182 | maxSize -= config.scaleFontSize; 1183 | } 1184 | 1185 | //Add a little padding between the x line and the text 1186 | maxSize -= 5; 1187 | 1188 | 1189 | labelHeight = config.scaleFontSize; 1190 | 1191 | maxSize -= labelHeight; 1192 | //Set 5 pixels greater than the font size to allow for a little padding from the X axis. 1193 | 1194 | scaleHeight = maxSize; 1195 | 1196 | //Then get the area above we can safely draw on. 1197 | 1198 | } 1199 | function getValueBounds() { 1200 | var upperValue = Number.MIN_VALUE; 1201 | var lowerValue = Number.MAX_VALUE; 1202 | for (var i=0; i upperValue) { upperValue = data.datasets[i].data[j] }; 1205 | if ( data.datasets[i].data[j] < lowerValue) { lowerValue = data.datasets[i].data[j] }; 1206 | } 1207 | }; 1208 | 1209 | var maxSteps = Math.floor((scaleHeight / (labelHeight*0.66))); 1210 | var minSteps = Math.floor((scaleHeight / labelHeight*0.5)); 1211 | 1212 | return { 1213 | maxValue : upperValue, 1214 | minValue : lowerValue, 1215 | maxSteps : maxSteps, 1216 | minSteps : minSteps 1217 | }; 1218 | 1219 | 1220 | } 1221 | } 1222 | 1223 | function calculateOffset(val,calculatedScale,scaleHop){ 1224 | var outerValue = calculatedScale.steps * calculatedScale.stepValue; 1225 | var adjustedValue = val - calculatedScale.graphMin; 1226 | var scalingFactor = CapValue(adjustedValue/outerValue,1,0); 1227 | return (scaleHop*calculatedScale.steps) * scalingFactor; 1228 | } 1229 | 1230 | function animationLoop(config,drawScale,drawData,ctx){ 1231 | var animFrameAmount = (config.animation)? 1/CapValue(config.animationSteps,Number.MAX_VALUE,1) : 1, 1232 | easingFunction = animationOptions[config.animationEasing], 1233 | percentAnimComplete =(config.animation)? 0 : 1; 1234 | 1235 | 1236 | 1237 | if (typeof drawScale !== "function") drawScale = function(){}; 1238 | 1239 | requestAnimFrame(animLoop); 1240 | 1241 | function animateFrame(){ 1242 | var easeAdjustedAnimationPercent =(config.animation)? CapValue(easingFunction(percentAnimComplete),null,0) : 1; 1243 | clear(ctx); 1244 | if(config.scaleOverlay){ 1245 | drawData(easeAdjustedAnimationPercent); 1246 | drawScale(); 1247 | } else { 1248 | drawScale(); 1249 | drawData(easeAdjustedAnimationPercent); 1250 | } 1251 | } 1252 | function animLoop(){ 1253 | //We need to check if the animation is incomplete (less than 1), or complete (1). 1254 | percentAnimComplete += animFrameAmount; 1255 | animateFrame(); 1256 | //Stop the loop continuing forever 1257 | if (percentAnimComplete <= 1){ 1258 | requestAnimFrame(animLoop); 1259 | } 1260 | else{ 1261 | if (typeof config.onAnimationComplete == "function") config.onAnimationComplete(); 1262 | } 1263 | 1264 | } 1265 | 1266 | } 1267 | 1268 | //Declare global functions to be called within this namespace here. 1269 | 1270 | 1271 | // shim layer with setTimeout fallback 1272 | var requestAnimFrame = (function(){ 1273 | return window.requestAnimationFrame || 1274 | window.webkitRequestAnimationFrame || 1275 | window.mozRequestAnimationFrame || 1276 | window.oRequestAnimationFrame || 1277 | window.msRequestAnimationFrame || 1278 | function(callback) { 1279 | window.setTimeout(callback, 1000 / 60); 1280 | }; 1281 | })(); 1282 | 1283 | function calculateScale(drawingHeight,maxSteps,minSteps,maxValue,minValue,labelTemplateString){ 1284 | var graphMin,graphMax,graphRange,stepValue,numberOfSteps,valueRange,rangeOrderOfMagnitude,decimalNum; 1285 | 1286 | valueRange = maxValue - minValue; 1287 | 1288 | rangeOrderOfMagnitude = calculateOrderOfMagnitude(valueRange); 1289 | 1290 | graphMin = Math.floor(minValue / (1 * Math.pow(10, rangeOrderOfMagnitude))) * Math.pow(10, rangeOrderOfMagnitude); 1291 | 1292 | graphMax = Math.ceil(maxValue / (1 * Math.pow(10, rangeOrderOfMagnitude))) * Math.pow(10, rangeOrderOfMagnitude); 1293 | 1294 | graphRange = graphMax - graphMin; 1295 | 1296 | stepValue = Math.pow(10, rangeOrderOfMagnitude); 1297 | 1298 | numberOfSteps = Math.round(graphRange / stepValue); 1299 | 1300 | //Compare number of steps to the max and min for that size graph, and add in half steps if need be. 1301 | while(numberOfSteps < minSteps || numberOfSteps > maxSteps) { 1302 | if (numberOfSteps < minSteps){ 1303 | stepValue /= 2; 1304 | numberOfSteps = Math.round(graphRange/stepValue); 1305 | } 1306 | else{ 1307 | stepValue *=2; 1308 | numberOfSteps = Math.round(graphRange/stepValue); 1309 | } 1310 | }; 1311 | 1312 | var labels = []; 1313 | populateLabels(labelTemplateString, labels, numberOfSteps, graphMin, stepValue); 1314 | 1315 | return { 1316 | steps : numberOfSteps, 1317 | stepValue : stepValue, 1318 | graphMin : graphMin, 1319 | labels : labels 1320 | 1321 | } 1322 | 1323 | function calculateOrderOfMagnitude(val){ 1324 | return Math.floor(Math.log(val) / Math.LN10); 1325 | } 1326 | 1327 | 1328 | } 1329 | 1330 | //Populate an array of all the labels by interpolating the string. 1331 | function populateLabels(labelTemplateString, labels, numberOfSteps, graphMin, stepValue) { 1332 | if (labelTemplateString) { 1333 | //Fix floating point errors by setting to fixed the on the same decimal as the stepValue. 1334 | for (var i = 1; i < numberOfSteps + 1; i++) { 1335 | labels.push(tmpl(labelTemplateString, {value: (graphMin + (stepValue * i)).toFixed(getDecimalPlaces(stepValue))})); 1336 | } 1337 | } 1338 | } 1339 | 1340 | //Max value from array 1341 | function Max( array ){ 1342 | return Math.max.apply( Math, array ); 1343 | }; 1344 | //Min value from array 1345 | function Min( array ){ 1346 | return Math.min.apply( Math, array ); 1347 | }; 1348 | //Default if undefined 1349 | function Default(userDeclared,valueIfFalse){ 1350 | if(!userDeclared){ 1351 | return valueIfFalse; 1352 | } else { 1353 | return userDeclared; 1354 | } 1355 | }; 1356 | //Is a number function 1357 | function isNumber(n) { 1358 | return !isNaN(parseFloat(n)) && isFinite(n); 1359 | } 1360 | //Apply cap a value at a high or low number 1361 | function CapValue(valueToCap, maxValue, minValue){ 1362 | if(isNumber(maxValue)) { 1363 | if( valueToCap > maxValue ) { 1364 | return maxValue; 1365 | } 1366 | } 1367 | if(isNumber(minValue)){ 1368 | if ( valueToCap < minValue ){ 1369 | return minValue; 1370 | } 1371 | } 1372 | return valueToCap; 1373 | } 1374 | function getDecimalPlaces (num){ 1375 | var numberOfDecimalPlaces; 1376 | if (num%1!=0){ 1377 | return num.toString().split(".")[1].length 1378 | } 1379 | else{ 1380 | return 0; 1381 | } 1382 | 1383 | } 1384 | 1385 | function mergeChartConfig(defaults,userDefined){ 1386 | var returnObj = {}; 1387 | for (var attrname in defaults) { returnObj[attrname] = defaults[attrname]; } 1388 | for (var attrname in userDefined) { returnObj[attrname] = userDefined[attrname]; } 1389 | return returnObj; 1390 | } 1391 | 1392 | //Javascript micro templating by John Resig - source at http://ejohn.org/blog/javascript-micro-templating/ 1393 | var cache = {}; 1394 | 1395 | function tmpl(str, data){ 1396 | // Figure out if we're getting a template, or if we need to 1397 | // load the template - and be sure to cache the result. 1398 | var fn = !/\W/.test(str) ? 1399 | cache[str] = cache[str] || 1400 | tmpl(document.getElementById(str).innerHTML) : 1401 | 1402 | // Generate a reusable function that will serve as a template 1403 | // generator (and which will be cached). 1404 | new Function("obj", 1405 | "var p=[],print=function(){p.push.apply(p,arguments);};" + 1406 | 1407 | // Introduce the data as local variables using with(){} 1408 | "with(obj){p.push('" + 1409 | 1410 | // Convert the template into pure JavaScript 1411 | str 1412 | .replace(/[\r\t\n]/g, " ") 1413 | .split("<%").join("\t") 1414 | .replace(/((^|%>)[^\t]*)'/g, "$1\r") 1415 | .replace(/\t=(.*?)%>/g, "',$1,'") 1416 | .split("\t").join("');") 1417 | .split("%>").join("p.push('") 1418 | .split("\r").join("\\'") 1419 | + "');}return p.join('');"); 1420 | 1421 | // Provide some basic currying to the user 1422 | return data ? fn( data ) : fn; 1423 | }; 1424 | 1425 | function thousand_separator(input) { 1426 | return parseFloat(input).toLocaleString(int_language); 1427 | } 1428 | } 1429 | 1430 | 1431 | --------------------------------------------------------------------------------