application/js/progress.js
// 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 (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);