1(function(window, document, undefined) { 2 3 /** 4 * Find the absolute position of an element 5 */ 6 var absPos = function(element) { 7 var offsetLeft, offsetTop; 8 offsetLeft = offsetTop = 0; 9 if (element.offsetParent) { 10 do { 11 offsetLeft += element.offsetLeft; 12 offsetTop += element.offsetTop; 13 } while (element = element.offsetParent); 14 } 15 return [offsetLeft, offsetTop]; 16 }; 17 18 /** 19 * @constructor Progress Circle class 20 * @param params.canvas Canvas on which the circles will be drawn. 21 * @param params.minRadius Inner radius of the innermost circle, in px. 22 * @param params.arcWidth Width of each circle(to be more accurate, ring). 23 * @param params.gapWidth Space between each circle. 24 * @param params.centerX X coordinate of the center of circles. 25 * @param params.centerY Y coordinate of the center of circles. 26 * @param params.infoLineBaseAngle Base angle of the info line. 27 * @param params.infoLineAngleInterval Angles between the info lines. 28 */ 29 var ProgressCircle = function(params) { 30 this.canvas = params.canvas; 31 this.minRadius = params.minRadius || 15; 32 this.arcWidth = params.arcWidth || 5; 33 this.gapWidth = params.gapWidth || 3; 34 this.centerX = params.centerX || this.canvas.width / 2; 35 this.centerY = params.centerY || this.canvas.height / 2;
36 this.infoLineLength = params.infoLineLength || 60; 37 this.horizLineLength = params.horizLineLength || 10; 38 this.infoLineAngleInterval = params.infoLineAngleInterval || Math.PI / 8; 39 this.infoLineBaseAngle = params.infoLineBaseAngle || Math.PI / 6; 40 41 this.context = this.canvas.getContext('2d'); 42 43 this.width = this.canvas.width; 44 this.height = this.canvas.height; 45 46 this.circles = []; 47 this.runningCount = 0; 48 }; 49 50 ProgressCircle.prototype = { 51 constructor: ProgressCircle, 52 53 /** 54 * @method Adds an progress monitor entry. 55 * @param params.fillColor Color to fill in the circle. 56 * @param params.outlineColor Color to outline the circle. 57 * @param params.progressListener Callback function to fetch the progress. 58 * @param params.infoListener Callback function to fetch the info. 59 * @returns this 60 */ 61 addEntry: function(params) { 62 this.circles.push(new Circle({ 63 canvas: this.canvas, 64 context: this.context, 65 centerX: this.centerX, 66 centerY: this.centerY, 67 innerRadius: this.minRadius + this.circles.length * 68 (this.gapWidth + this.arcWidth), 69 arcWidth: this.arcWidth,
70 infoLineLength: this.infoLineLength, 71 horizLineLength: this.horizLineLength, 72 73 id: this.circles.length, 74 fillColor: params.fillColor, 75 outlineColor: params.outlineColor, 76 progressListener: params.progressListener, 77 infoListener: params.infoListener, 78 infoLineAngle: this.infoLineBaseAngle + 79 this.circles.length * this.infoLineAngleInterval, 80 })); 81 82 return this; 83 }, 84 85 /** 86 * @method Starts the monitor and updates with the given interval. 87 * @param interval Interval between updates, in millisecond. 88 * @returns this 89 */ 90 start: function(interval) { 91 var self = this; 92 this.timer = setInterval(function() { 93 self._update(); 94 }, interval || 33); 95 96 return this; 97 }, 98 99 /** 100 * @method Manually update all circles 101 */ 102 update: function(value) { 103 this._update(value); 104 }, 105 106 /** 107 * @method Stop the animation. 108 */ 109 stop: function() { 110 clearTimeout(this.timer); 111 }, 112 113 /** 114 * @private 115 * @method Call update on each circle and redraw them. 116 * @param value Value to be passed to circle update method (in case of manual update) 117 * @returns this 118 */ 119 _update: function(value) { 120 this._clear(); 121 this.circles.forEach(function(circle, idx, array) { 122 circle.update(value); 123 }); 124 125 return this; 126 }, 127 128 /** 129 * @private 130 * @method Clear the canvas. 131 * @returns this 132 */ 133 _clear: function() { 134 this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); 135 136 return this; 137 }, 138 139 }; 140 141 /** 142 * @private 143 * @class Individual progress circle. 144 * @param params.canvas Canvas on which the circle will be drawn. 145 * @param params.context Context of the canvas. 146 * @param params.innerRadius Inner radius of the circle, in px. 147 * @param params.arcWidth Width of each arc(circle). 148 * @param params.gapWidth Distance between each arc. 149 * @param params.centerX X coordinate of the center of circles. 150 * @param params.centerY Y coordinate of the center of circles. 151 * @param params.fillColor Color to fill in the circle. 152 * @param params.outlineColor Color to outline the circle. 153 * @param params.progressListener Callback function to fetch the progress. 154 * @param params.infoListener Callback function to fetch the info. 155 * @param params.infoLineAngle Angle of info line. 156 */ 157 var Circle = function(params) { 158 this.id = params.id; 159 this.canvas = params.canvas; 160 this.context = params.context; 161 this.centerX = params.centerX; 162 this.centerY = params.centerY; 163 this.arcWidth = params.arcWidth; 164 this.innerRadius = params.innerRadius || 0; 165 this.fillColor = params.fillColor || '#fff'; 166 this.outlineColor = params.outlineColor || this.fillColor; 167 this.progressListener = params.progressListener;
168 this.infoLineLength = params.infoLineLength || 250; 169 this.horizLineLength = params.horizLineLength || 50; 170 this.infoListener = params.infoListener; 171 this.infoLineAngle = params.infoLineAngle; 172 173 this.outerRadius = this.innerRadius + this.arcWidth; 174 175 // If the info listener is not registered, then don't calculate 176 // the related coordinates 177 if (!this.infoListener) return; 178 179 // calculate the info-line turning points 180 var angle = this.infoLineAngle, 181 arcDistance = (this.innerRadius + this.outerRadius) / 2, 182 183 sinA = Math.sin(angle), 184 cosA = Math.cos(angle); 185 186 this.infoLineStartX = this.centerX + sinA * arcDistance; 187 this.infoLineStartY = this.centerY - cosA * arcDistance; 188 189 this.infoLineMidX = this.centerX + sinA * this.infoLineLength; 190 this.infoLineMidY = this.centerY - cosA * this.infoLineLength; 191 192 this.infoLineEndX = this.infoLineMidX + 193 (sinA < 0 ? -this.horizLineLength : this.horizLineLength); 194 this.infoLineEndY = this.infoLineMidY; 195 196 var infoText = document.createElement('div'), 197 style = infoText.style; 198 199 style.color = this.fillColor; 200 style.position = 'absolute'; 201 style.left = this.infoLineEndX + absPos(this.canvas)[0] + 'px'; 202 // style.top will be calculated in the `drawInfo` method. Since 203 // user may want to change the size of the font, so the top offset 204 // must be updated in each loop. 205 infoText.className = 'ProgressCircleInfo'; // For css styling 206 infoText.id = 'progress_circle_info_' + this.id; 207 document.body.appendChild(infoText); 208 this.infoText = infoText; 209 }; 210 211 Circle.prototype = { 212 constructor: Circle, 213 214 update: function(value) { 215 this.progress = value || this.progressListener(); 216 this._draw(); 217 218 if (this.infoListener) { 219 this.info = this.infoListener(); 220 this._drawInfo(); 221 } 222 }, 223 224 /** 225 * @private 226 * @method Draw the circle on the canvas. 227 * @returns this 228 */ 229 _draw: function() { 230 var ctx = this.context, 231 232 ANGLE_OFFSET = -Math.PI / 2, 233 234 startAngle = 0 + ANGLE_OFFSET, 235 endAngle= startAngle + this.progress * Math.PI * 2, 236 237 x = this.centerX, 238 y = this.centerY, 239 240 innerRadius = this.innerRadius, 241 outerRadius = this.outerRadius; 242 243 ctx.fillStyle = this.fillColor; 244 ctx.strokeStyle = this.outlineColor; 245 246 ctx.beginPath(); 247 ctx.arc(x, y, Math.abs(innerRadius), startAngle, endAngle, false); 248 ctx.arc(x, y, Math.abs(outerRadius), endAngle, startAngle, true); 249 ctx.closePath(); 250 ctx.stroke(); 251 ctx.fill(); 252 253 return this; 254 }, 255 256 /** 257 * @private 258 * @method Draw the info lines and info text. 259 * @returns this 260 */ 261 _drawInfo: function() { 262 263 var pointList, lineHeight; 264 265 pointList = [ 266 [this.infoLineStartX, this.infoLineStartY], 267 [this.infoLineMidX, this.infoLineMidY], 268 [this.infoLineEndX, this.infoLineEndY], 269 ]; 270 this._drawSegments(pointList, false); 271 272 this.infoText.innerHTML = this.info; 273 274 lineHeight = this.infoText.offsetHeight; 275 this.infoText.style.top = this.infoLineEndY + 276 absPos(this.canvas)[1] - lineHeight / 2 + 'px'; 277 278 return this; 279 }, 280 281 /** 282 * @private 283 * @method Helper function to draw the segments 284 * @param pointList An array of points in the form of [x, y]. 285 * @param close Whether to connect the first and last point. 286 */ 287 _drawSegments: function(pointList, close) { 288 var ctx = this.context; 289 290 ctx.beginPath(); 291 ctx.moveTo(pointList[0][0], pointList[0][1]); 292 for (var i = 1; i < pointList.length; ++i) { 293 ctx.lineTo(pointList[i][0], pointList[i][1]); 294 } 295 296 if (close) { 297 ctx.closePath(); 298 } 299 ctx.stroke(); 300 }, 301 }; 302 303 window.ProgressCircle = ProgressCircle; 304 305})(window, document);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.