PageSourceSearch

https://www.radiojeans.net/cust/soundmanager2/360-player/script/berniecode-animator.js

js radiojeans.net collected 2026-09-24 13:14:17 UTC 24,206 bytes, 674 lines download raw bytes

1/** @license
2	Animator.js 1.1.9
3	
4	This library is released under the BSD license:
5
6	Copyright (c) 2006, Bernard Sumption. All rights reserved.
7	
8	Redistribution and use in source and binary forms, with or without
9	modification, are permitted provided that the following conditions are met:
10	
11	Redistributions of source code must retain the above copyright notice, this
12	list of conditions and the following disclaimer. Redistributions in binary
13	form must reproduce the above copyright notice, this list of conditions and
14	the following disclaimer in the documentation and/or other materials
15	provided with the distribution. Neither the name BernieCode nor
16	the names of its contributors may be used to endorse or promote products
17	derived from this software without specific prior written permission. 
18	
19	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20	AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21	IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22	ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
23	ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24	DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25	SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26	CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27	LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28	OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
29	DAMAGE.
30
31*/
32
33// http://www.berniecode.com/writing/animator.html
34
35// Applies a sequence of numbers between 0 and 1 to a number of subjects
36// construct - see setOptions for parameters
37function Animator(options) {
38	this.setOptions(options);
39	var _this = this;
40	this.timerDelegate = function(){_this.onTimerEvent()};
41	this.subjects = [];
42	this.subjectScopes = [];
43	this.target = 0;
44	this.state = 0;
45	this.lastTime = null;
46};
47Animator.prototype = {
48	// apply defaults
49	setOptions: function(options) {
50		this.options = Animator.applyDefaults({
51			interval: 20,  // time between animation frames
52			duration: 400, // length of animation
53			onComplete: function(){},
54			onStep: function(){},
55			transition: Animator.tx.easeInOut
56		}, options);
57	},
58	// animate from the current state to provided value
59	seekTo: function(to) {
60		this.seekFromTo(this.state, to);
61	},
62	// animate from the current state to provided value
63	seekFromTo: function(from, to) {
64		this.target = Math.max(0, Math.min(1, to));
65		this.state = Math.max(0, Math.min(1, from));
66		this.lastTime = new Date().getTime();
67		if (!this.intervalId) {
68			this.intervalId = window.setInterval(this.timerDelegate, this.options.interval);
69		}
70	},
71	// animate from the current state to provided value
72	jumpTo: function(to) {
73		this.target = this.state = Math.max(0, Math.min(1, to));
74		this.propagate();
75	},
76	// seek to the opposite of the current target
77	toggle: function() {
78		this.seekTo(1 - this.target);
79	},
80	// add a function or an object with a method setState(state) that will be called with a number
81	// between 0 and 1 on each frame of the animation
82	addSubject: function(subject,scope) {
83		this.subjects[this.subjects.length] = subject;
84		this.subjectScopes[this.subjectScopes.length] = scope;
85		return this;
86	},
87	// remove all subjects
88	clearSubjects: function() {
89		this.subjects = [];
90		this.subjectScopes = [];
91	},
92	// forward the current state to the animation subjects
93	propagate: function() {
94		var value = this.options.transition(this.state);
95		for (var i=0; i<this.subjects.length; i++) {
96			if (this.subjects[i].setState) {
97				this.subjects[i].setState(value);
98			} else {
99				this.subjects[i].apply(this.subjectScopes[i],[value]);
100			}
101		}
102	},
103	// called once per frame to update the current state
104	onTimerEvent: function() {
105		var now = new Date().getTime();
106		var timePassed = now - this.lastTime;
107		this.lastTime = now;
108		var movement = (timePassed / this.options.duration) * (this.state < this.target ? 1 : -1);
109		if (Math.abs(movement) >= Math.abs(this.state - this.target)) {
110			this.state = this.target;
111		} else {
112			this.state += movement;
113		}
114		
115		try {
116			this.propagate();
117		} finally {
118			this.options.onStep.call(this);
119			if (this.target == this.state) {
120				window.clearInterval(this.intervalId);
121				this.intervalId = null;
122				this.options.onComplete.call(this);
123			}
124		}
125	},
126	// shortcuts
127	play: function() {this.seekFromTo(0, 1)},
128	reverse: function() {this.seekFromTo(1, 0)},
129	// return a string describing this Animator, for debugging
130	inspect: function() {
131		var str = "#<Animator:\n";
132		for (var i=0; i<this.subjects.length; i++) {
133			str += this.subjects[i].inspect();
134		}
135		str += ">";
136		return str;
137	}
138}
139// merge the properties of two objects
140Animator.applyDefaults = function(defaults, prefs) {
141	prefs = prefs || {};
142	var prop, result = {};
143	for (prop in defaults) result[prop] = prefs[prop] !== undefined ? prefs[prop] : defaults[prop];
144	return result;
145}
146// make an array from any object
147Animator.makeArray = function(o) {
148	if (o == null) return [];
149	if (!o.length) return [o];
150	var result = [];
151	for (var i=0; i<o.length; i++) result[i] = o[i];
152	return result;
153}
154// convert a dash-delimited-property to a camelCaseProperty (c/o Prototype, thanks Sam!)
155Animator.camelize = function(string) {
156	var oStringList = string.split('-');
157	if (oStringList.length == 1) return oStringList[0];
158	
159	var camelizedString = string.indexOf('-') == 0
160		? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1)
161		: oStringList[0];
162	
163	for (var i = 1, len = oStringList.length; i < len; i++) {
164		var s = oStringList[i];
165		camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
166	}
167	return camelizedString;
168}
169// syntactic sugar for creating CSSStyleSubjects
170Animator.apply = function(el, style, options) {
171	if (style instanceof Array) {
172		return new Animator(options).addSubject(new CSSStyleSubject(el, style[0], style[1]));
173	}
174	return new Animator(options).addSubject(new CSSStyleSubject(el, style));
175}
176// make a transition function that gradually accelerates. pass a=1 for smooth
177// gravitational acceleration, higher values for an exaggerated effect
178Animator.makeEaseIn = function(a) {
179	return function(state) {
180		return Math.pow(state, a*2); 
181	}
182}
183// as makeEaseIn but for deceleration
184Animator.makeEaseOut = function(a) {
185	return function(state) {
186		return 1 - Math.pow(1 - state, a*2); 
187	}
188}
189// make a transition function that, like an object with momentum being attracted to a point,
190// goes past the target then returns
191Animator.makeElastic = function(bounces) {
192	return function(state) {
193		state = Animator.tx.easeInOut(state);
194		return ((1-Math.cos(state * Math.PI * bounces)) * (1 - state)) + state; 
195	}
196}
197// make an Attack Decay Sustain Release envelope that starts and finishes on the same level
198// 
199Animator.makeADSR = function(attackEnd, decayEnd, sustainEnd, sustainLevel) {
200	if (sustainLevel == null) sustainLevel = 0.5;
201	return function(state) {
202		if (state < attackEnd) {
203			return state / attackEnd;
204		}
205		if (state < decayEnd) {
206			return 1 - ((state - attackEnd) / (decayEnd - attackEnd) * (1 - sustainLevel));
207		}
208		if (state < sustainEnd) {
209			return sustainLevel;
210		}
211		return sustainLevel * (1 - ((state - sustainEnd) / (1 - sustainEnd)));
212	}
213}
214// make a transition function that, like a ball falling to floor, reaches the target and/
215// bounces back again
216Animator.makeBounce = function(bounces) {
217	var fn = Animator.makeElastic(bounces);
218	return function(state) {
219		state = fn(state); 
220		return state <= 1 ? state : 2-state;
221	}
222}
223 
224// pre-made transition functions to use with the 'transition' option
225Animator.tx = {
226	easeInOut: function(pos){
227		return ((-Math.cos(pos*Math.PI)/2) + 0.5);
228	},
229	linear: function(x) {
230		return x;
231	},
232	easeIn: Animator.makeEaseIn(1.5),
233	easeOut: Animator.makeEaseOut(1.5),
234	strongEaseIn: Animator.makeEaseIn(2.5),
235	strongEaseOut: Animator.makeEaseOut(2.5),
236	elastic: Animator.makeElastic(1),
237	veryElastic: Animator.makeElastic(3),
238	bouncy: Animator.makeBounce(1),
239	veryBouncy: Animator.makeBounce(3)
240}
241
242// animates a pixel-based style property between two integer values
243function NumericalStyleSubject(els, property, from, to, units) {
244	this.els = Animator.makeArray(els);
245	if (property == 'opacity' && window.ActiveXObject) {
246		this.property = 'filter';
247	} else {
248		this.property = Animator.camelize(property);
249	}
250	this.from = parseFloat(from);
251	this.to = parseFloat(to);
252	this.units = units != null ? units : 'px';
253}
254NumericalStyleSubject.prototype = {
255	setState: function(state) {
256		var style = this.getStyle(state);
257		var visibility = (this.property == 'opacity' && state == 0) ? 'hidden' : '';
258		var j=0;
259		for (var i=0; i<this.els.length; i++) {
260			try {
261				this.els[i].style[this.property] = style;
262			} catch (e) {
263				// ignore fontWeight - intermediate numerical values cause exeptions in firefox
264				if (this.property != 'fontWeight') throw e;
265			}
266			if (j++ > 20) return;
267		}
268	},
269	getStyle: function(state) {
270		state = this.from + ((this.to - this.from) * state);
271		if (this.property == 'filter') return "alpha(opacity=" + Math.round(state*100) + ")";
272		if (this.property == 'opacity') return state;
273		return Math.round(state) + this.units;
274	},
275	inspect: function() {
276		return "\t" + this.property + "(" + this.from + this.units + " to " + this.to + this.units + ")\n";
277	}
278}
279
280// animates a colour based style property between two hex values
281function ColorStyleSubject(els, property, from, to) {
282	this.els = Animator.makeArray(els);
283	this.property = Animator.camelize(property);
284	this.to = this.expandColor(to);
285	this.from = this.expandColor(from);
286	this.origFrom = from;
287	this.origTo = to;
288}
289
290ColorStyleSubject.prototype = {
291	// parse "#FFFF00" to [256, 256, 0]
292	expandColor: function(color) {
293		var hexColor, red, green, blue;
294		hexColor = ColorStyleSubject.parseColor(color);
295		if (hexColor) {
296			red = parseInt(hexColor.slice(1, 3), 16);
297			green = parseInt(hexColor.slice(3, 5), 16);
298			blue = parseInt(hexColor.slice(5, 7), 16);
299			return [red,green,blue]
300		}
301		if (window.DEBUG) {
302			alert("Invalid colour: '" + color + "'");
303		}
304	},
305	getValueForState: function(color, state) {
306		return Math.round(this.from[color] + ((this.to[color] - this.from[color]) * state));
307	},
308	setState: function(state) {
309		var color = '#'
310				+ ColorStyleSubject.toColorPart(this.getValueForState(0, state))
311				+ ColorStyleSubject.toColorPart(this.getValueForState(1, state))
312				+ ColorStyleSubject.toColorPart(this.getValueForState(2, state));
313		for (var i=0; i<this.els.length; i++) {
314			this.els[i].style[this.property] = color;
315		}
316	},
317	inspect: function() {
318		return "\t" + this.property + "(" + this.origFrom + " to " + this.origTo + ")\n";
319	}
320}
321
322// return a properly formatted 6-digit hex colour spec, or false
323ColorStyleSubject.parseColor = function(string) {
324	var color = '#', match;
325	if(match = ColorStyleSubject.parseColor.rgbRe.exec(string)) {
326		var part;
327		for (var i=1; i<=3; i++) {
328			part = Math.max(0, Math.min(255, parseInt(match[i])));
329			color += ColorStyleSubject.toColorPart(part);
330		}
331		return color;
332	}
333	if (match = ColorStyleSubject.parseColor.hexRe.exec(string)) {
334		if(match[1].length == 3) {
335			for (var i=0; i<3; i++) {
336				color += match[1].charAt(i) + match[1].charAt(i);
337			}
338			return color;
339		}
340		return '#' + match[1];
341	}
342	return false;
343}
344// convert a number to a 2 digit hex string
345ColorStyleSubject.toColorPart = function(number) {
346	if (number > 255) number = 255;
347	var digits = number.toString(16);
348	if (number < 16) return '0' + digits;
349	return digits;
350}
351ColorStyleSubject.parseColor.rgbRe = /^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/i;
352ColorStyleSubject.parseColor.hexRe = /^\#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/;
353
354// Animates discrete styles, i.e. ones that do not scale but have discrete values
355// that can't be interpolated
356function DiscreteStyleSubject(els, property, from, to, threshold) {
357	this.els = Animator.makeArray(els);
358	this.property = Animator.camelize(property);
359	this.from = from;
360	this.to = to;
361	this.threshold = threshold || 0.5;
362}
363
364DiscreteStyleSubject.prototype = {
365	setState: function(state) {
366		var j=0;
367		for (var i=0; i<this.els.length; i++) {
368			this.els[i].style[this.property] = state <= this.threshold ? this.from : this.to; 
369		}
370	},
371	inspect: function() {
372		return "\t" + this.property + "(" + this.from + " to " + this.to + " @ " + this.threshold + ")\n";
373	}
374}
375
376// animates between two styles defined using CSS.
377// if style1 and style2 are present, animate between them, if only style1
378// is present, animate between the element's current style and style1
379function CSSStyleSubject(els, style1, style2) {
380	els = Animator.makeArray(els);
381	this.subjects = [];
382	if (els.length == 0) return;
383	var prop, toStyle, fromStyle;
384	if (style2) {
385		fromStyle = this.parseStyle(style1, els[0]);
386		toStyle = this.parseStyle(style2, els[0]);
387	} else {
388		toStyle = this.parseStyle(style1, els[0]);
389		fromStyle = {};
390		for (prop in toStyle) {
391			fromStyle[prop] = CSSStyleSubject.getStyle(els[0], prop);
392		}
393	}
394	// remove unchanging properties
395	var prop;
396	for (prop in fromStyle) {
397		if (fromStyle[prop] == toStyle[prop]) {
398			delete fromStyle[prop];
399			delete toStyle[prop];
400		}
401	}
402	// discover the type (numerical or colour) of each style
403	var prop, units, match, type, from, to;
404	for (prop in fromStyle) {
405		var fromProp = String(fromStyle[prop]);
406		var toProp = String(toStyle[prop]);
407		if (toStyle[prop] == null) {
408			if (window.DEBUG) alert("No to style provided for '" + prop + '"');
409			continue;
410		}
411		
412		if (from = ColorStyleSubject.parseColor(fromProp)) {
413			to = ColorStyleSubject.parseColor(toProp);
414			type = ColorStyleSubject;
415		} else if (fromProp.match(CSSStyleSubject.numericalRe)
416				&& toProp.match(CSSStyleSubject.numericalRe)) {
417			from = parseFloat(fromProp);
418			to = parseFloat(toProp);
419			type = NumericalStyleSubject;
420			match = CSSStyleSubject.numericalRe.exec(fromProp);
421			var reResult = CSSStyleSubject.numericalRe.exec(toProp);
422			if (match[1] != null) {
423				units = match[1];
424			} else if (reResult[1] != null) {
425				units = reResult[1];
426			} else {
427				units = reResult;
428			}
429		} else if (fromProp.match(CSSStyleSubject.discreteRe)
430				&& toProp.match(CSSStyleSubject.discreteRe)) {
431			from = fromProp;
432			to = toProp;
433			type = DiscreteStyleSubject;
434			units = 0;   // hack - how to get an animator option down to here
435		} else {
436			if (window.DEBUG) {
437				alert("Unrecognised format for value of "
438					+ prop + ": '" + fromStyle[prop] + "'");
439			}
440			continue;
441		}
442		this.subjects[this.subjects.length] = new type(els, prop, from, to, units);
443	}
444}
445
446CSSStyleSubject.prototype = {
447	// parses "width: 400px; color: #FFBB2E" to {width: "400px", color: "#FFBB2E"}
448	parseStyle: function(style, el) {
449		var rtn = {};
450		// if style is a rule set
451		if (style.indexOf(":") != -1) {
452			var styles = style.split(";");
453			for (var i=0; i<styles.length; i++) {
454				var parts = CSSStyleSubject.ruleRe.exec(styles[i]);
455				if (parts) {
456					rtn[parts[1]] = parts[2];
457				}
458			}
459		}
460		// else assume style is a class name
461		else {
462			var prop, value, oldClass;
463			oldClass = el.className;
464			el.className = style;
465			for (var i=0; i<CSSStyleSubject.cssProperties.length; i++) {
466				prop = CSSStyleSubject.cssProperties[i];
467				value = CSSStyleSubject.getStyle(el, prop);
468				if (value != null) {
469					rtn[prop] = value;
470				}
471			}
472			el.className = oldClass;
473		}
474		return rtn;
475		
476	},
477	setState: function(state) {
478		for (var i=0; i<this.subjects.length; i++) {
479			this.subjects[i].setState(state);
480		}
481	},
482	inspect: function() {
483		var str = "";
484		for (var i=0; i<this.subjects.length; i++) {
485			str += this.subjects[i].inspect();
486		}
487		return str;
488	}
489}
490// get the current value of a css property, 
491CSSStyleSubject.getStyle = function(el, property){
492	var style;
493	if(document.defaultView && document.defaultView.getComputedStyle){
494		style = document.defaultView.getComputedStyle(el, "").getPropertyValue(property);
495		if (style) {
496			return style;
497		}
498	}
499	property = Animator.camelize(property);
500	if(el.currentStyle){
501		style = el.currentStyle[property];
502	}
503	return style || el.style[property]
504}
505
506
507CSSStyleSubject.ruleRe = /^\s*([a-zA-Z\-]+)\s*:\s*(\S(.+\S)?)\s*$/;
508CSSStyleSubject.numericalRe = /^-?\d+(?:\.\d+)?(%|[a-zA-Z]{2})?$/;
509CSSStyleSubject.discreteRe = /^\w+$/;
510
511// required because the style object of elements isn't enumerable in Safari
512/*
513CSSStyleSubject.cssProperties = ['background-color','border','border-color','border-spacing',
514'border-style','border-top','border-right','border-bottom','border-left','border-top-color',
515'border-right-color','border-bottom-color','border-left-color','border-top-width','border-right-width',
516'border-bottom-width','border-left-width','border-width','bottom','color','font-size','font-size-adjust',
517'font-stretch','font-style','height','left','letter-spacing','line-height','margin','margin-top',
518'margin-right','margin-bottom','margin-left','marker-offset','max-height','max-width','min-height',
519'min-width','orphans','outline','outline-color','outline-style','outline-width','overflow','padding',
520'padding-top','padding-right','padding-bottom','padding-left','quotes','right','size','text-indent',
521'top','width','word-spacing','z-index','opacity','outline-offset'];*/
522
523
524CSSStyleSubject.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','o
524pacity','outline-offset','overflow-x','overflow-y'];
525
526
527// chains several Animator objects together
528function AnimatorChain(animators, options) {
529	this.animators = animators;
530	this.setOptions(options);
531	for (var i=0; i<this.animators.length; i++) {
532		this.listenTo(this.animators[i]);
533	}
534	this.forwards = false;
535	this.current = 0;
536}
537
538AnimatorChain.prototype = {
539	// apply defaults
540	setOptions: function(options) {
541		this.options = Animator.applyDefaults({
542			// by default, each call to AnimatorChain.play() calls jumpTo(0) of each animator
543			// before playing, which can cause flickering if you have multiple animators all
544			// targeting the same element. Set this to false to avoid this.
545			resetOnPlay: true
546		}, options);
547	},
548	// play each animator in turn
549	play: function() {
550		this.forwards = true;
551		this.current = -1;
552		if (this.options.resetOnPlay) {
553			for (var i=0; i<this.animators.length; i++) {
554				this.animators[i].jumpTo(0);
555			}
556		}
557		this.advance();
558	},
559	// play all animators backwards
560	reverse: function() {
561		this.forwards = false;
562		this.current = this.animators.length;
563		if (this.options.resetOnPlay) {
564			for (var i=0; i<this.animators.length; i++) {
565				this.animators[i].jumpTo(1);
566			}
567		}
568		this.advance();
569	},
570	// if we have just play()'d, then call reverse(), and vice versa
571	toggle: function() {
572		if (this.forwards) {
573			this.seekTo(0);
574		} else {
575			this.seekTo(1);
576		}
577	},
578	// internal: install an event listener on an animator's onComplete option
579	// to trigger the next animator
580	listenTo: function(animator) {
581		var oldOnComplete = animator.options.onComplete;
582		var _this = this;
583		animator.options.onComplete = function() {
584			if (oldOnComplete) oldOnComplete.call(animator);
585			_this.advance();
586		}
587	},
588	// play the next animator
589	advance: function() {
590		if (this.forwards) {
591			if (this.animators[this.current + 1] == null) return;
592			this.current++;
593			this.animators[this.current].play();
594		} else {
595			if (this.animators[this.current - 1] == null) return;
596			this.current--;
597			this.animators[this.current].reverse();
598		}
599	},
600	// this function is provided for drop-in compatibility with Animator objects,
601	// but only accepts 0 and 1 as target values
602	seekTo: function(target) {
603		if (target <= 0) {
604			this.forwards = false;
605			this.animators[this.current].seekTo(0);
606		} else {
607			this.forwards = true;
608			this.animators[this.current].seekTo(1);
609		}
610	}
611}
612
613// an Accordion is a class that creates and controls a number of Animators. An array of elements is passed in,
614// and for each element an Animator and a activator button is created. When an Animator's activator button is
615// clicked, the Animator and all before it seek to 0, and all Animators after it seek to 1. This can be used to
616// create the classic Accordion effect, hence the name.
617// see setOptions for arguments
618function Accordion(options) {
619	this.setOptions(options);
620	var selected = this.options.initialSection, current;
621	if (this.options.rememberance) {
622		current = document.location.hash.substring(1);
623	}
624	this.rememberanceTexts = [];
625	this.ans = [];
626	var _this = this;
627	for (var i=0; i<this.options.sections.length; i++) {
628		var el = this.options.sections[i];
629		var an = new Animator(this.options.animatorOptions);
630		var from = this.options.from + (this.options.shift * i);
631		var to = this.options.to + (this.options.shift * i);
632		an.addSubject(new NumericalStyleSubject(el, this.options.property, from, to, this.options.units));
633		an.jumpTo(0);
634		var activator = this.options.getActivator(el);
635		activator.index = i;
636		activator.onclick = function(){_this.show(this.index)};
637		this.ans[this.ans.length] = an;
638		this.rememberanceTexts[i] = activator.innerHTML.replace(/\s/g, "");
639		if (this.rememberanceTexts[i] === current) {
640			selected = i;
641		}
642	}
643	this.show(selected);
644}
645
646Accordion.prototype = {
647	// apply defaults
648	setOptions: function(options) {
649		this.options = Object.extend({
650			// REQUIRED: an array of elements to use as the accordion sections
651			sections: null,
652			// a function that locates an activator button element given a section element.
653			// by default it takes a button id from the section's "activator" attibute
654			getActivator: function(el) {return document.getElementById(el.getAttribute("activator"))},
655			// shifts each animator's range, for example with options {from:0,to:100,shift:20}
656			// the animators' ranges will be 0-100, 20-120, 40-140 etc.
657			shift: 0,
658			// the first page to show
659			initialSection: 0,
660			// if set to true, document.location.hash will be used to preserve the open section across page reloads 
661			rememberance: true,
662			// constructor arguments to the Animator objects
663			animatorOptions: {}
664		}, options || {});
665	},
666	show: function(section) {
667		for (var i=0; i<this.ans.length; i++) {
668			this.ans[i].seekTo(i > section ? 1 : 0);
669		}
670		if (this.options.rememberance) {
671			document.location.hash = this.rememberanceTexts[section];
672		}
673	}
674}

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.