// console.log('Loading Progress...'); Spontaneous.Progress = (function($, S) { var Progress = function(parent, size, options) { var defaults = { spinner_fg_color: "#000000", // colour spinner_bg_color: null, // null or false for transparent progress_fg_color: "#000000", // colour progress_bg_color: false, // null or false for transparent spinner_alpha: 1, progress_alpha: 1, period: 550, // (ms) time taken for spinner to complete one revolution segments: 12, // number of bars in spinner segment_length: 0.47, // length of spinner bar as fraction of total radius segment_width: 0.6, // spinner bar thickness. 1 means inner edges form unbroken circle trail_length: 1.1, // trail length of 1 means shaded bars go all the way around rounded: true, shaded: false }; if (typeof options == 'undefined') options = {}; var settings = {}; for (var k in defaults) { settings[k] = defaults[k]; } for (var k in options) { settings[k] = options[k]; } return { container: parent, size: size, _canvas: false, defaults: defaults, options: settings, _color: {}, // forces the addition of the canvas. normally this is lazily added // only when the progress indicator is actually used init: function() { var c = this.canvas(); }, context: function() { return this.canvas().getContext('2d'); }, colour: function(t, l, alpha) { var c = t + '_' + l + '_color'; if (typeof alpha == 'undefined') alpha = this.options[t+'_alpha']; if (!this._color[c]) { this._color[c] = this._parse_color(this.options[c]); } return "rgba("+this._color[c][0]+", "+this._color[c][1]+", "+this._color[c][2]+", " +alpha+ ")"; }, canvas: function() { var c; if (this.container && !this._canvas) { c = document.createElement('canvas'); c.width = this.size; c.height = this.size; // _log(typeof this.container) var e = null; if (typeof this.container == 'string') { this.container = document.getElementById(this.container); } this.container.appendChild(c); this._canvas = c; } return this._canvas; }, _percent: false, // thanks to Stoyan Stefanov // for original pie drawing code: // http://www.phpied.com/canvas-pie/ update: function(percent) { this._indeterminate = false; percent = Math.max(0.1, parseFloat(percent)); // always show something... this._percent = percent; var ctx = this.context(); var radius = this.size / 2; var center = this.size / 2; ctx.clearRect(0,0,this.size,this.size); var colour; if (this.options.progress_bg_color) { colour = this.colour('progress', 'bg'); ctx.beginPath(); ctx.moveTo(center, center); ctx.arc( // draw next arc center, // x center, // y radius, // radius Math.PI * -0.5, // -0.5 sets set the start to be top Math.PI * 2, false // clockwise? ); ctx.closePath(); ctx.fillStyle = colour; // color ctx.fill(); } colour = this.colour('progress', 'fg'); var a = (parseFloat(percent)/100.0); ctx.beginPath(); ctx.moveTo(center, center); // center of the pie ctx.arc( // draw next arc center, // x center, // y radius, // radius Math.PI * -0.5, // -0.5 sets set the start to be top Math.PI * (- 0.5 + 2 * a), false // clockwise? ); ctx.lineTo(center, center); // line back to the center ctx.closePath(); ctx.fillStyle = colour; // color ctx.fill(); return this; }, _redraw: function() { if (this._percent) this.update(this._percent); }, _indeterminate: false, _indeterminate_interval: false, indeterminate: function() { if (this._indeterminate) return; this._indeterminate = true; var __spinner = this; var __spinning = function() { __spinner._update_indeterminate(); }; this._draw_indeterminate(); this._indeterminate_interval = setInterval(__spinning, parseFloat(this.options.period) / this.options.segments); }, spin: function() { this.indeterminate(); }, start: function() { this.indeterminate(); }, _spin_angle: 0, _spin_increment: function() { return 2 / this.options.segments; }, _update_indeterminate: function() { if (!this._indeterminate) { this.pause(); return; } this._spin_angle += this._spin_increment(); this._draw_indeterminate(); }, _draw_indeterminate: function() { var ctx = this.context(); var radius = this._radius(); var center = this._center(); var inc = this._spin_increment(); ctx.clearRect(0,0,this.size,this.size); var r1 = radius - (radius * this.options.segment_length); var p = ((2.0 * Math.PI * r1) / this.options.segments) * this.options.segment_width ; var r2 = radius; if (this.options.rounded) r2 -= p/2; for (var i = 0; i < this.options.segments; i++) { var offset = (inc * i); var a = Math.PI * (1 -this._spin_angle + offset); ctx.beginPath(); if (this.options.rounded) { this._draw_rounded(ctx, center, r1, r2, a, p); } else { this._draw_square(ctx, center, r1, r2, a, p); } ctx.closePath(); ctx.fillStyle = this._fill(ctx, i); ctx.fill(); } }, _draw_rounded: function(ctx, c, r1, r2, a, p) { var sin = Math.sin(a); var cos = Math.cos(a); var a1 = Math.PI - a; var a2 = 2 * Math.PI - a; ctx.arc( c + r1 * sin, // x c + r1 * cos, // y p/2,// radius a1, a2, false ); ctx.arc( c + r2 * sin, // x c + r2 * cos, // y p/2, // radius a2, a1, false ); }, _draw_square: function(ctx, c, r1, r2, a, p) { var sin = Math.sin(a); var cos = Math.cos(a); var dx = (p/2.0) * cos; var dy = (p/2.0) * sin; var x0 = c + r1 * sin; var y0 = c + r1 * cos; var x1 = c + r1 * sin + dx; var y1 = c + r1 * cos - dy; var x2 = c + r2 * sin + dx; var y2 = c + r2 * cos - dy; var x3 = c + r2 * sin - dx; var y3 = c + r2 * cos + dy; var x4 = c + r1 * sin - dx; var y4 = c + r1 * cos + dy; ctx.moveTo(x1, y1); ctx.lineTo(x2, y2); ctx.lineTo(x3, y3); ctx.lineTo(x4, y4); }, _fill: function(ctx, i) { if (this.options.shaded) { return this._shaded_fill(ctx, i); } else { return this._solid_fill(ctx, i); } }, _solid_fill: function(ctx, i) { return this.colour('spinner','fg', this._alpha_for_segment(i)); }, _shaded_fill: function(ctx, i) { var c = this._center(); var gradient = ctx.createRadialGradient(c, c, 0, c, c, this._radius()); gradient.addColorStop(0.3, this.colour('spinner', 'fg', 0)); gradient.addColorStop(1, this.colour('spinner', 'fg', this._alpha_for_segment(i))); return gradient; }, _alpha_for_segment: function(n) { var x = (n / this.options.segments); // gotta be a smart way to figure out these constants var a = Math.max(0, this.options.spinner_alpha * ((1.0 / (5.0 * ((x / this.options.trail_length) + (1.0/5.8)))) - 0.168)); return a; }, _radius: function() { return this.size/2; }, _center: function() { return this._radius(); }, _parse_color: function(css_colour) { if (css_colour.charAt(0) == '#') { css_colour = css_colour.substr(1, 6); } css_colour = css_colour.replace(/ /g,''); css_colour = css_colour.toLowerCase(); var i, hex = []; if (css_colour.length == 3) { for (i = 0; i < 3; i++) { hex[hex.length] = css_colour.charAt(i) + css_colour.charAt(i); } } else { for (i = 0; i < 3; i++) { hex[hex.length] = css_colour.substr(i*2, 2); } } var rgb = []; for (i = 0; i < hex.length; i++) { rgb[i] = parseInt(hex[i], 16); } return rgb; }, pause: function() { // pauses indeterminate spinner this._indeterminate = false; // this._spin_angle = 0; clearInterval(this._indeterminate_interval); }, stop: function() { this.pause(); this.clear(); }, clear: function() { this.context().clearRect(0,0,this.size,this.size); }, _disappear_interval: null, disappear: function(duration) { if (typeof duration === 'undefined') duration = 1000; var disappearing = this; var orig_alpha = this.options.spinner_alpha; var finish_time = (new Date()).valueOf() + duration; var disappear = function() { if (disappearing.options.spinner_alpha <= 0) { clearInterval(disappearing._disappear_interval); disappearing.stop(); disappearing.options.spinner_alpha = orig_alpha; } else { var now = (new Date()).valueOf(), remaining = (finish_time - now)/duration; disappearing.options.spinner_alpha = orig_alpha * remaining; } }; this._disappear_interval = setInterval(disappear, 50); }, set_options: function(o) { this.options = o; this._color = {}; this._spin_angle = 0; if (this._indeterminate) { this.stop(); this.indeterminate(); } else { this._redraw(); } } }; }; return Progress; })(jQuery, Spontaneous);