//fgnass.github.com/spin.js#v1.3
(function(root, factory){
if(typeof exports=='object')  module.exports=factory()
else if(typeof define=='function'&&define.amd) define(factory)
else root.Spinner=factory()
}
(this, function(){
"use strict";
var prefixes=['webkit', 'Moz', 'ms', 'O'] 
, animations={} 
, useCssAnimations 
function createEl(tag, prop){
var el=document.createElement(tag||'div')
, n
for(n in prop) el[n]=prop[n]
return el
}
function ins(parent ){
for (var i=1, n=arguments.length; i<n; i++)
parent.appendChild(arguments[i])
return parent
}
var sheet=(function(){
var el=createEl('style', {type:'text/css'})
ins(document.getElementsByTagName('head')[0], el)
return el.sheet||el.styleSheet
}())
function addAnimation(alpha, trail, i, lines){
var name=['opacity', trail, ~~(alpha*100), i, lines].join('-')
, start=0.01 + i/lines * 100
, z=Math.max(1 - (1-alpha) / trail * (100-start), alpha)
, prefix=useCssAnimations.substring(0, useCssAnimations.indexOf('Animation')).toLowerCase()
, pre=prefix&&'-' + prefix + '-'||''
if(!animations[name]){
sheet.insertRule('@' + pre + 'keyframes ' + name + '{' +
'0%{opacity:' + z + '}' +
start + '%{opacity:' + alpha + '}' +
(start+0.01) + '%{opacity:1}' +
(start+trail) % 100 + '%{opacity:' + alpha + '}' +
'100%{opacity:' + z + '}' +
'}', sheet.cssRules.length)
animations[name]=1
}
return name
}
function vendor(el, prop){
var s=el.style
, pp
, i
if(s[prop]!==undefined) return prop
prop=prop.charAt(0).toUpperCase() + prop.slice(1)
for(i=0; i<prefixes.length; i++){
pp=prefixes[i]+prop
if(s[pp]!==undefined) return pp
}}
function css(el, prop){
for (var n in prop)
el.style[vendor(el, n)||n]=prop[n]
return el
}
function merge(obj){
for (var i=1; i < arguments.length; i++){
var def=arguments[i]
for (var n in def)
if(obj[n]===undefined) obj[n]=def[n]
}
return obj
}
function pos(el){
var o={ x:el.offsetLeft, y:el.offsetTop }
while((el=el.offsetParent))
o.x+=el.offsetLeft, o.y+=el.offsetTop
return o
}
var defaults={
lines: 12,
length: 7,
width: 5,
radius: 10,
rotate: 0,
corners: 1,
color: '#000',
direction: 1,
speed: 1,
trail: 100,
opacity: 1/4,
fps: 20,
zIndex: 2e9,
className: 'spinner',
top: 'auto',
left: 'auto',
position: 'relative'
}
function Spinner(o){
if(typeof this=='undefined') return new Spinner(o)
this.opts=merge(o||{}, Spinner.defaults, defaults)
}
Spinner.defaults={}
merge(Spinner.prototype, {
spin: function(target){
this.stop()
var self=this
, o=self.opts
, el=self.el=css(createEl(0, {className: o.className}), {position: o.position, width: 0, zIndex: o.zIndex})
, mid=o.radius+o.length+o.width
, ep
, tp
if(target){
target.insertBefore(el, target.firstChild||null)
tp=pos(target)
ep=pos(el)
css(el, {
left: (o.left=='auto' ? tp.x-ep.x + (target.offsetWidth >> 1):parseInt(o.left, 10) + mid) + 'px',
top: (o.top=='auto' ? tp.y-ep.y + (target.offsetHeight >> 1):parseInt(o.top, 10) + mid)  + 'px'
})
}
el.setAttribute('role', 'progressbar')
self.lines(el, self.opts)
if(!useCssAnimations){
var i=0
, start=(o.lines - 1) * (1 - o.direction) / 2
, alpha
, fps=o.fps
, f=fps/o.speed
, ostep=(1-o.opacity) / (f*o.trail / 100)
, astep=f/o.lines
;(function anim(){
i++;
for (var j=0; j < o.lines; j++){
alpha=Math.max(1 - (i + (o.lines - j) * astep) % f * ostep, o.opacity)
self.opacity(el, j * o.direction + start, alpha, o)
}
self.timeout=self.el&&setTimeout(anim, ~~(1000/fps))
})()
}
return self
},
stop: function(){
var el=this.el
if(el){
clearTimeout(this.timeout)
if(el.parentNode) el.parentNode.removeChild(el)
this.el=undefined
}
return this
},
lines: function(el, o){
var i=0
, start=(o.lines - 1) * (1 - o.direction) / 2
, seg
function fill(color, shadow){
return css(createEl(), {
position: 'absolute',
width: (o.length+o.width) + 'px',
height: o.width + 'px',
background: color,
boxShadow: shadow,
transformOrigin: 'left',
transform: 'rotate(' + ~~(360/o.lines*i+o.rotate) + 'deg) translate(' + o.radius+'px' +',0)',
borderRadius: (o.corners * o.width>>1) + 'px'
})
}
for (; i < o.lines; i++){
seg=css(createEl(), {
position: 'absolute',
top: 1+~(o.width/2) + 'px',
transform: o.hwaccel ? 'translate3d(0,0,0)':'',
opacity: o.opacity,
animation: useCssAnimations&&addAnimation(o.opacity, o.trail, start + i * o.direction, o.lines) + ' ' + 1/o.speed + 's linear infinite'
})
if(o.shadow) ins(seg, css(fill('#000', '0 0 4px ' + '#000'), {top: 2+'px'}))
ins(el, ins(seg, fill(o.color, '0 0 1px rgba(0,0,0,.1)')))
}
return el
},
opacity: function(el, i, val){
if(i < el.childNodes.length) el.childNodes[i].style.opacity=val
}})
function initVML(){
function vml(tag, attr){
return createEl('<' + tag + ' xmlns="urn:schemas-microsoft.com:vml" class="spin-vml">', attr)
}
sheet.addRule('.spin-vml', 'behavior:url(#default#VML)')
Spinner.prototype.lines=function(el, o){
var r=o.length+o.width
, s=2*r
function grp(){
return css(
vml('group', {
coordsize: s + ' ' + s,
coordorigin: -r + ' ' + -r
}),
{ width: s, height: s }
)
}
var margin=-(o.width+o.length)*2 + 'px'
, g=css(grp(), {position: 'absolute', top: margin, left: margin})
, i
function seg(i, dx, filter){
ins(g,
ins(css(grp(), {rotation: 360 / o.lines * i + 'deg', left: ~~dx}),
ins(css(vml('roundrect', {arcsize: o.corners}), {
width: r,
height: o.width,
left: o.radius,
top: -o.width>>1,
filter: filter
}),
vml('fill', {color: o.color, opacity: o.opacity}),
vml('stroke', {opacity: 0})
)
)
)
}
if(o.shadow)
for (i=1; i <=o.lines; i++)
seg(i, -2, 'progid:DXImageTransform.Microsoft.Blur(pixelradius=2,makeshadow=1,shadowopacity=.3)')
for (i=1; i <=o.lines; i++) seg(i)
return ins(el, g)
}
Spinner.prototype.opacity=function(el, i, val, o){
var c=el.firstChild
o=o.shadow&&o.lines||0
if(c&&i+o < c.childNodes.length){
c=c.childNodes[i+o]; c=c&&c.firstChild; c=c&&c.firstChild
if(c) c.opacity=val
}}
}
var probe=css(createEl('group'), {behavior: 'url(#default#VML)'})
if(!vendor(probe, 'transform')&&probe.adj) initVML()
else useCssAnimations=vendor(probe, 'animation')
return Spinner
}));