﻿function Animator(options) { this.setOptions(options); var _this = this; this.timerDelegate = function(){_this.onTimerEvent()}; this.subjects = []; this.target = 0; this.state = 0; this.lastTime = null;}; Animator.prototype = { setOptions: function(options) { this.options = Animator.applyDefaults({ interval: 20, duration: 400, onComplete: function(){}, onStep: function(){}, transition: Animator.tx.easeInOut
}, options);}, seekTo: function(to) { this.seekFromTo(this.state, to);}, seekFromTo: function(from, to) { this.target = Math.max(0, Math.min(1, to)); this.state = Math.max(0, Math.min(1, from)); this.lastTime = new Date().getTime(); if (!this.intervalId) { this.intervalId = window.setInterval(this.timerDelegate, this.options.interval);}
}, jumpTo: function(to) { this.target = this.state = Math.max(0, Math.min(1, to)); this.propagate();}, toggle: function() { this.seekTo(1 - this.target);}, addSubject: function(subject) { this.subjects[this.subjects.length] = subject; return this;}, clearSubjects: function() { this.subjects = [];}, propagate: function() { var value = this.options.transition(this.state); for (var i=0; i<this.subjects.length; i++) { if (this.subjects[i].setState) { this.subjects[i].setState(value);} else { this.subjects[i](value);}
}
}, onTimerEvent: function() { var now = new Date().getTime(); var timePassed = now - this.lastTime; this.lastTime = now; var movement = (timePassed / this.options.duration) * (this.state < this.target ? 1 : -1); if (Math.abs(movement) >= Math.abs(this.state - this.target)) { this.state = this.target;} else { this.state += movement;}
try { this.propagate();} finally { this.options.onStep.call(this); if (this.target == this.state) { window.clearInterval(this.intervalId); this.intervalId = null; this.options.onComplete.call(this);}
}
}, play: function() {this.seekFromTo(0, 1)}, reverse: function() {this.seekFromTo(1, 0)}, inspect: function() { var str = "#<Animator:\n"; for (var i=0; i<this.subjects.length; i++) { str += this.subjects[i].inspect();}
str += ">"; return str;}
}
Animator.applyDefaults = function(defaults, prefs) { prefs = prefs || {}; var prop, result = {}; for (prop in defaults) result[prop] = prefs[prop] !== undefined ? prefs[prop] : defaults[prop]; return result;}
Animator.makeArray = function(o) { if (o == null) return []; if (!o.length) return [o]; var result = []; for (var i=0; i<o.length; i++) result[i] = o[i]; return result;}
Animator.camelize = function(string) { var oStringList = string.split('-'); if (oStringList.length == 1) return oStringList[0]; var camelizedString = string.indexOf('-') == 0
? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1)
: oStringList[0]; for (var i = 1, len = oStringList.length; i < len; i++) { var s = oStringList[i]; camelizedString += s.charAt(0).toUpperCase() + s.substring(1);}
return camelizedString;}
Animator.apply = function(el, style, options) { if (style instanceof Array) { return new Animator(options).addSubject(new CSSStyleSubject(el, style[0], style[1]));}
return new Animator(options).addSubject(new CSSStyleSubject(el, style));}
Animator.makeEaseIn = function(a) { return function(state) { return Math.pow(state, a*2);}
}
Animator.makeEaseOut = function(a) { return function(state) { return 1 - Math.pow(1 - state, a*2);}
}
Animator.makeElastic = function(bounces) { return function(state) { state = Animator.tx.easeInOut(state); return ((1-Math.cos(state * Math.PI * bounces)) * (1 - state)) + state;}
}
Animator.makeADSR = function(attackEnd, decayEnd, sustainEnd, sustainLevel) { if (sustainLevel == null) sustainLevel = 0.5; return function(state) { if (state < attackEnd) { return state / attackEnd;}
if (state < decayEnd) { return 1 - ((state - attackEnd) / (decayEnd - attackEnd) * (1 - sustainLevel));}
if (state < sustainEnd) { return sustainLevel;}
return sustainLevel * (1 - ((state - sustainEnd) / (1 - sustainEnd)));}
}
Animator.makeBounce = function(bounces) { var fn = Animator.makeElastic(bounces); return function(state) { state = fn(state); return state <= 1 ? state : 2-state;}
}
Animator.tx = { easeInOut: function(pos){ return ((-Math.cos(pos*Math.PI)/2) + 0.5);}, linear: function(x) { return x;}, easeIn: Animator.makeEaseIn(1.5), easeOut: Animator.makeEaseOut(1.5), strongEaseIn: Animator.makeEaseIn(2.5), strongEaseOut: Animator.makeEaseOut(2.5), elastic: Animator.makeElastic(1), veryElastic: Animator.makeElastic(3), bouncy: Animator.makeBounce(1), veryBouncy: Animator.makeBounce(3)
}
function NumericalStyleSubject(els, property, from, to, units) { this.els = Animator.makeArray(els); if (property == 'opacity' && window.ActiveXObject) { this.property = 'filter';} else { this.property = Animator.camelize(property);}
this.from = parseFloat(from); this.to = parseFloat(to); this.units = units != null ? units : 'px';}
NumericalStyleSubject.prototype = { setState: function(state) { var style = this.getStyle(state); var visibility = (this.property == 'opacity' && state == 0) ? 'hidden' : ''; var j=0; for (var i=0; i<this.els.length; i++) { try { this.els[i].style[this.property] = style;} catch (e) { if (this.property != 'fontWeight') throw e;}
if (j++ > 20) return;}
}, getStyle: function(state) { state = this.from + ((this.to - this.from) * state); if (this.property == 'filter') return "alpha(opacity=" + Math.round(state*100) + ")"; if (this.property == 'opacity') return state; return Math.round(state) + this.units;}, inspect: function() { return "\t" + this.property + "(" + this.from + this.units + " to " + this.to + this.units + ")\n";}
}
function ColorStyleSubject(els, property, from, to) { this.els = Animator.makeArray(els); this.property = Animator.camelize(property); this.to = this.expandColor(to); this.from = this.expandColor(from); this.origFrom = from; this.origTo = to;}
ColorStyleSubject.prototype = { expandColor: function(color) { var hexColor, red, green, blue; hexColor = ColorStyleSubject.parseColor(color); if (hexColor) { red = parseInt(hexColor.slice(1, 3), 16); green = parseInt(hexColor.slice(3, 5), 16); blue = parseInt(hexColor.slice(5, 7), 16); return [red,green,blue]
}
if (window.DEBUG) { alert("Invalid colour: '" + color + "'");}
}, getValueForState: function(color, state) { return Math.round(this.from[color] + ((this.to[color] - this.from[color]) * state));}, setState: function(state) { var color = '#' + ColorStyleSubject.toColorPart(this.getValueForState(0, state)) + ColorStyleSubject.toColorPart(this.getValueForState(1, state)) + ColorStyleSubject.toColorPart(this.getValueForState(2, state)); for (var i=0; i<this.els.length; i++) { this.els[i].style[this.property] = color;}
}, inspect: function() { return "\t" + this.property + "(" + this.origFrom + " to " + this.origTo + ")\n";}
}
ColorStyleSubject.parseColor = function(string) { var color = '#', match; if(match = ColorStyleSubject.parseColor.rgbRe.exec(string)) { var part; for (var i=1; i<=3; i++) { part = Math.max(0, Math.min(255, parseInt(match[i]))); color += ColorStyleSubject.toColorPart(part);}
return color;}
if (match = ColorStyleSubject.parseColor.hexRe.exec(string)) { if(match[1].length == 3) { for (var i=0; i<3; i++) { color += match[1].charAt(i) + match[1].charAt(i);}
return color;}
return '#' + match[1];}
return false;}
ColorStyleSubject.toColorPart = function(number) { if (number > 255) number = 255; var digits = number.toString(16); if (number < 16) return '0' + digits; return digits;}
ColorStyleSubject.parseColor.rgbRe = /^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/i; ColorStyleSubject.parseColor.hexRe = /^\#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/; function DiscreteStyleSubject(els, property, from, to, threshold) { this.els = Animator.makeArray(els); this.property = Animator.camelize(property); this.from = from; this.to = to; this.threshold = threshold || 0.5;}
DiscreteStyleSubject.prototype = { setState: function(state) { var j=0; for (var i=0; i<this.els.length; i++) { this.els[i].style[this.property] = state <= this.threshold ? this.from : this.to;}
}, inspect: function() { return "\t" + this.property + "(" + this.from + " to " + this.to + " @ " + this.threshold + ")\n";}
}
function CSSStyleSubject(els, style1, style2) { els = Animator.makeArray(els); this.subjects = []; if (els.length == 0) return; var prop, toStyle, fromStyle; if (style2) { fromStyle = this.parseStyle(style1, els[0]); toStyle = this.parseStyle(style2, els[0]);} else { toStyle = this.parseStyle(style1, els[0]); fromStyle = {}; for (prop in toStyle) { fromStyle[prop] = CSSStyleSubject.getStyle(els[0], prop);}
}
var prop; for (prop in fromStyle) { if (fromStyle[prop] == toStyle[prop]) { delete fromStyle[prop]; delete toStyle[prop];}
}
var prop, units, match, type, from, to; for (prop in fromStyle) { var fromProp = String(fromStyle[prop]); var toProp = String(toStyle[prop]); if (toStyle[prop] == null) { if (window.DEBUG) alert("No to style provided for '" + prop + '"'); continue;}
if (from = ColorStyleSubject.parseColor(fromProp)) { to = ColorStyleSubject.parseColor(toProp); type = ColorStyleSubject;} else if (fromProp.match(CSSStyleSubject.numericalRe)
&& toProp.match(CSSStyleSubject.numericalRe)) { from = parseFloat(fromProp); to = parseFloat(toProp); type = NumericalStyleSubject; match = CSSStyleSubject.numericalRe.exec(fromProp); var reResult = CSSStyleSubject.numericalRe.exec(toProp); if (match[1] != null) { units = match[1];} else if (reResult[1] != null) { units = reResult[1];} else { units = reResult;}
} else if (fromProp.match(CSSStyleSubject.discreteRe)
&& toProp.match(CSSStyleSubject.discreteRe)) { from = fromProp; to = toProp; type = DiscreteStyleSubject; units = 0;} else { if (window.DEBUG) { alert("Unrecognised format for value of " + prop + ": '" + fromStyle[prop] + "'");}
continue;}
this.subjects[this.subjects.length] = new type(els, prop, from, to, units);}
}
CSSStyleSubject.prototype = { parseStyle: function(style, el) { var rtn = {}; if (style.indexOf(":") != -1) { var styles = style.split(";"); for (var i=0; i<styles.length; i++) { var parts = CSSStyleSubject.ruleRe.exec(styles[i]); if (parts) { rtn[parts[1]] = parts[2];}
}
}
else { var prop, value, oldClass; oldClass = el.className; el.className = style; for (var i=0; i<CSSStyleSubject.cssProperties.length; i++) { prop = CSSStyleSubject.cssProperties[i]; value = CSSStyleSubject.getStyle(el, prop); if (value != null) { rtn[prop] = value;}
}
el.className = oldClass;}
return rtn;}, setState: function(state) { for (var i=0; i<this.subjects.length; i++) { this.subjects[i].setState(state);}
}, inspect: function() { var str = ""; for (var i=0; i<this.subjects.length; i++) { str += this.subjects[i].inspect();}
return str;}
}
CSSStyleSubject.getStyle = function(el, property){ var style; if(document.defaultView && document.defaultView.getComputedStyle){ style = document.defaultView.getComputedStyle(el, "").getPropertyValue(property); if (style) { return style;}
}
property = Animator.camelize(property); if(el.currentStyle){ style = el.currentStyle[property];}
return style || el.style[property]
}
CSSStyleSubject.ruleRe = /^\s*([a-zA-Z\-]+)\s*:\s*(\S(.+\S)?)\s*$/; CSSStyleSubject.numericalRe = /^-?\d+(?:\.\d+)?(%|[a-zA-Z]{2})?$/; CSSStyleSubject.discreteRe = /^\w+$/; CSSStyleSubject.cssProperties = ['azimuth','background','background-attachment','background-color','background-image','background-position','background-repeat','border-collapse','border-color','border-spacing','border-style','border-top','border-top-color','border-right-color','border-bottom-color','border-left-color','border-top-style','border-right-style','border-bottom-style','border-left-style','border-top-width','border-right-width','border-bottom-width','border-left-width','border-width','bottom','clear','clip','color','content','cursor','direction','display','elevation','empty-cells','css-float','font','font-family','font-size','font-size-adjust','font-stretch','font-style','font-variant','font-weight','height','left','letter-spacing','line-height','list-style','list-style-image','list-style-position','list-style-type','margin','margin-top','margin-right','margin-bottom','margin-left','max-height','max-width','min-height','min-width','orphans','outline','outline-color','outline-style','outline-width','overflow','padding','padding-top','padding-right','padding-bottom','padding-left','pause','position','right','size','table-layout','text-align','text-decoration','text-indent','text-shadow','text-transform','top','vertical-align','visibility','white-space','width','word-spacing','z-index','opacity','outline-offset','overflow-x','overflow-y']; function AnimatorChain(animators, options) { this.animators = animators; this.setOptions(options); for (var i=0; i<this.animators.length; i++) { this.listenTo(this.animators[i]);}
this.forwards = false; this.current = 0;}
AnimatorChain.prototype = { setOptions: function(options) { this.options = Animator.applyDefaults({ resetOnPlay: true
}, options);}, play: function() { this.forwards = true; this.current = -1; if (this.options.resetOnPlay) { for (var i=0; i<this.animators.length; i++) { this.animators[i].jumpTo(0);}
}
this.advance();}, reverse: function() { this.forwards = false; this.current = this.animators.length; if (this.options.resetOnPlay) { for (var i=0; i<this.animators.length; i++) { this.animators[i].jumpTo(1);}
}
this.advance();}, toggle: function() { if (this.forwards) { this.seekTo(0);} else { this.seekTo(1);}
}, listenTo: function(animator) { var oldOnComplete = animator.options.onComplete; var _this = this; animator.options.onComplete = function() { if (oldOnComplete) oldOnComplete.call(animator); _this.advance();}
}, advance: function() { if (this.forwards) { if (this.animators[this.current + 1] == null) return; this.current++; this.animators[this.current].play();} else { if (this.animators[this.current - 1] == null) return; this.current--; this.animators[this.current].reverse();}
}, seekTo: function(target) { if (target <= 0) { this.forwards = false; this.animators[this.current].seekTo(0);} else { this.forwards = true; this.animators[this.current].seekTo(1);}
}
}
function Accordion(options) { this.setOptions(options); var selected = this.options.initialSection, current; if (this.options.rememberance) { current = document.location.hash.substring(1);}
this.rememberanceTexts = []; this.ans = []; var _this = this; for (var i=0; i<this.options.sections.length; i++) { var el = this.options.sections[i]; var an = new Animator(this.options.animatorOptions); var from = this.options.from + (this.options.shift * i); var to = this.options.to + (this.options.shift * i); an.addSubject(new NumericalStyleSubject(el, this.options.property, from, to, this.options.units)); an.jumpTo(0); var activator = this.options.getActivator(el); activator.index = i; activator.onclick = function(){_this.show(this.index)}; this.ans[this.ans.length] = an; this.rememberanceTexts[i] = activator.innerHTML.replace(/\s/g, ""); if (this.rememberanceTexts[i] === current) { selected = i;}
}
this.show(selected);}
Accordion.prototype = { setOptions: function(options) { this.options = Object.extend({ sections: null, getActivator: function(el) {return document.getElementById(el.getAttribute("activator"))}, shift: 0, initialSection: 0, rememberance: true, animatorOptions: {}
}, options || {});}, show: function(section) { for (var i=0; i<this.ans.length; i++) { this.ans[i].seekTo(i > section ? 1 : 0);}
if (this.options.rememberance) { document.location.hash = this.rememberanceTexts[section];}
}
}
