/*
 * JJ : James & Jimmy JavaScript Library 2006 :-P
 *
 */
var un = undefined, undef = undefined;

var jj = {

	bind: function (object, func) {
		if (arguments.length > 2) {
			var args = jj.argsSlice (arguments, 2);
			if (typeof (func) == 'string') {
				return function () {
					return object [func].apply (object, jj.join (args, jj.argsSlice (arguments)));
				};
			} else {
				return function () {
					return func.apply (object, jj.join (args, jj.argsSlice (arguments)));
				};
			}
		} else {
			if (typeof (func) == 'string') {
				return function () {
					return object [func].apply (object, arguments);
				};
			} else {
				return function () {
					return func.apply (object, arguments);
				};
			}
		}
	},

	utfLen: function (data) {
		var ret = 0;
		for (var i = 0; i < data.length; i++) {
			var c = data.charCodeAt (i);
			if (c < 0x80) ret += 1;
			else if (c < 0x800) ret += 2;
			else if (c < 0x10000) ret += 3;
			else if (c < 0x200000) ret += 4;
			else if (c < 0x4000000) ret += 5;
			else ret += 6;
		}
		return ret;
	},

	errorSummary: function (error) {
		switch (typeof error) {
		case 'string':
			return error;
		case 'object':
			if ('fileName' in error)
				return error.fileName + ':' + error.lineNumber + ' ' + 
					error.message + ' (' + error.name + ')';
			else return error.message;
		default:
			return jj.pretty (error);
		}
	},

	leadingZero: function (value) {
		return String(value).length < 2 ? '0'+value : String(value);
	},
	returnNumeric: function (input) {
		return input.replace(/[^0-9]+/g,'');
	},

	min: function () {
		var value = undefined;
		for (var i = 0; i < arguments.length; i++)
			if (value == undefined || value > arguments [i])
				value = arguments [i];
		return value;
	},

	max: function () {
		var value = undefined;
		for (var i = 0; i < arguments.length; i++)
			if (value == undefined || value < arguments [i])
				value = arguments [i];
		return value;
	},

	def: function (/* ... */) {
		var name = arguments [0];
		var props = arguments [arguments.length - 1];

		// check it looks alright
		if (! (name in props))
			throw 'Constructor not found: ' + name;

		// create the class object
		var classObj = {
			name: name,
			props: props
		};

		// Create the real constructor/class.
		var ret = function () {
			this [name].apply (this, arguments);
		};
		ret.name = name;

		// import system stuff
		ret.prototype.__class = classObj;

		// import our properties
		jj.mergeIntoNew (ret.prototype, props);

		// import superclass properties
		for (var i = arguments.length - 2; i >= 1; i--)
			jj.mergeIntoNew (ret.prototype, arguments [i].prototype);

		// and return
		return ret;
	},

	/*defMerge: function (dest, src, name) {
		for (var key in src) {
			if (! (src in dest)) {
				dest [key] = src [key];
				if (key != name)
					dest [name + '_' + key] = src [key];
			}
		}
	},*/

	merge: function (/* ... */) {
		var dest = {};
		for (var i = 0; i < arguments.length; i++) {
			jj.mergeInto (dest, arguments [i]);
		}
		return dest;
	},

	mergeInto: function (/* ... */) {
		var dest = arguments [0];
		for (var i = 1; i < arguments.length; i++) {
			var src = arguments [i];
			for (var key in src)
				dest [key] = src [key];
		}
		return dest;
	},

	mergeIntoReal: function (dest, srcs) {
		for (var i = 0; i < srcs.length; i++) {
			var src = srcs [i];
			for (var key in src)
				dest [key] = src [key];
		}
	},

	mergeIntoNew: function (/* ... */) {
		var dest = arguments [0];
		for (var i = 1; i < arguments.length; i++) {
			var src = arguments [i];
			for (var key in src) {
				if (! (key in dest))
					dest [key] = src [key];
			}
		}
	},

	mergeIntoNewReal: function (dest, srcs) {
		for (var i = 0; i < srcs.length; i++) {
			var src = srcs [i];
			for (var key in src) {
				if (! (key in dest))
					dest [key] = src [key];
			}
		}
	},

	callSummary: function (name, args) {
		var s = name + ' (';
		if (jj.isArray (args) || jj.isArguments (args)) {
			var first = true;
			for (var i = 0; i < args.length; i++) {
				if (! first) s += ', ';
				s += jj.pretty (args [i]);
				first = false;
			}
		} else if (jj.isMap (args)) {
			var first = true;
			for (var i in args) {
				if (! first) s += ', ';
				s += i + ': ' + jj.pretty (args [i]);
				first = false;
			}
		} else if (args == undefined) {
			s += 'undefined';
		} else {
			s += '?' + jj.pretty (args) + '?';
		}
		return s + ')';
	},

	handle: function (handlerAny, funcName, args) {
		switch (typeof (handler)) {

		case 'function':
			return handlerAny (funcName, args);

		case 'object':
			return handerAny [funcName].apply (handlerAny, jj.isArray (args)? args : [ args ]);

		default:
			throw 'Eek';
		}
	},

	handlerFunc: function (handlerAny, errorIfNotFound, prefix) {

		if (jj.isUndefined (errorIfNotFound))
			errorIfNotFound = true;

		if (jj.isUndefined (prefix))
			prefix = '';

		switch (typeof (handlerAny)) {

		case 'function':
			return handlerAny;

		case 'object':
			var rest = jj.argsSlice (arguments, 3);
			return function (name, args) {

				// do before if specified
				if (prefix + 'before' in handlerAny) {
					handlerAny [prefix + 'before'].apply (handlerAny, jj.join (rest, [ name, args ]));
				}

				var retVal;
				if (prefix + name in handlerAny) {

					// call the named function
					if (jj.isArray (args)) {
						retVal = handlerAny [prefix + name].apply (handlerAny, args);
					} else if (jj.isMap (args)) {
						var fn = handlerAny [prefix + name];
						if (fn.length == 0) {
							retVal = fn.apply (handlerAny, jj.join (rest, [ ]));
						} else if (fn.length == 1) {
							retVal = fn.apply (handlerAny, jj.join (rest, [ args ]));
						} else if (fn.length == 2) {
							retVal = fn.apply (handlerAny, jj.join (rest, [ name, args ]));
						}
					} else {
						retVal = handlerAny [prefix + name].apply (handlerAny, rest);
					}

				} else if (prefix + 'any' in handlerAny) {

					// call the any function
					return handlerAny [prefix + 'any'].apply (handlerAny, jj.join (rest, [ name, args ]));

				} else if (errorIfNotFound) {

					// throw an error
					throw 'No handler for ' + name;
				}

				// do after if specified
				if (prefix + 'after' in handlerAny) {
					handlerAny [prefix + 'after'].apply (handlerAny, jj.join (rest, [ name, args ]));
				}

				return retVal;
			};

		case 'undefined':
			return function (name, args) {
			};

		default:
			throw 'Eek ' + typeof (handlerAny);
		}
	},
	
	handlerFuncSpecial: function (mainObject, childObject) {
		return function (name, args) {
			if (! (name in childObject))
				throw 'No handler for ' + name;
			return childObject [name].apply (mainObject, args);
		};
	},

	copy: function (data) {

		if (data == undefined) {
			return undefined;
		}

		if (typeof data == 'null') {
			return data;
		}

		if (typeof data == 'number') {
			return data;
		}

		if (typeof data == 'string') {
			return data;
		}

		if (jj.isArray (data)) {
			var ret = [];
			for (var i in data)
				ret.push (jj.copy (data [i]));
			return ret;
		}

		if (jj.isMap (data)) {
			var ret = {};
			for (var i in data)
				ret [i] = data [i];
			return ret;
		}

		if (typeof data == 'boolean') {
			return data;
		}

		throw 'Trying to copy ' + data;
	},

	eq: function () {

		if (arguments.length < 1)
			throw new Exception ('Not enough arguments for jj.eq');

		for (var i = 1; i < arguments.length; i++) {
			if (! jj.eqReal (arguments [0], arguments [i])) {
				return false;
			}
		}

		return true;
	},

	eqReal: function (left, right) {

		if (left == null)
			return right == null;

		if (typeof left == 'undefined')
			return typeof right == 'undefined';

		if (typeof left == 'number')
			return typeof right == 'number' && left == right;

		if (typeof left == 'boolean')
			return typeof right == 'boolean' && left == right;

		if (typeof left == 'string')
			return typeof right == 'string' && left == right;

		if (typeof left == 'object' && left instanceof Array)
			return typeof right == 'object' && right instanceof Array && jj.eqArray (left, right);

		if (jj.isMap (left))
			return jj.isMap (right) && jj.eqMap (left, right);

		throw 'Don\'t know how to handle ' + typeof left;
	},

	eqMap: function (left, right) {

		// check all members from the left
		for (var i in left) {
			if (! (i in right))
				return false;
			if (! jj.eqReal (left [i], right [i]))
				return false;
		}

		// check there's nothing extra on the right
		for (var i in right) {
			if (! (i in left))
				return false;
		}

		// if we get here we are away
		return true;
	},

	eqArray: function (left, right) {

		if (left.length != right.length)
			return false;

		for (var i in left) {
			if (! jj.eqReal (left [i], right [i]))
				return false;
		}
		
		return true;
	},

	propNameRegexp: /^[a-zA-z_][a-zA-Z0-9_]*$/,

	pretty: function (data) {

		if (data == undefined) {
			return 'undefined';
		}

		if (typeof data == 'number') {
			return '' + data;
		}

		if (typeof data == 'string') {
			return '\'' + data
				.replace (/\u005c/g, '\\\\')
				.replace (/\u0027/g, '\\\'')
				.replace (/\u0000/g, '\\u0000')
				.replace (/\u0008/g, '\\b')
				.replace (/\u0009/g, '\\t')
				.replace (/\u000a/g, '\\n')
				.replace (/\u000b/g, '\\v')
				.replace (/\u000c/g, '\\f')
				.replace (/\u000d/g, '\\r')
				+ '\'';
		}

		if (typeof data == 'array' || data.constructor == Array) {
			s = '[ ';
			var first = true;
			for (var i = 0; i < data.length; i++) {
				if (! first) s += ', ';
				s += jj.pretty (data [i]);
				first = false;
			}
			return s + ' ]';
		}

		if (typeof data == 'object') {
			var s = '{ ';
			var first = true;
			for (var key in data) {
				if (! first) s += ', ';
				s += (jj.propNameRegexp.test (key)? key : jj.pretty (key))
					+ ': ' + jj.pretty (data [key]);
				first = false;
			}
			return s + ' }';
		}

		if (typeof data == 'boolean') {
			return data? 'true' : 'false';
		}

		if (typeof data == 'function') {
			return 'function(...){...}';
		}

		throw 'Trying to encode ' + data;
	},

	inx: function (left /*, ... */) {
		for (var i = 1; i < arguments.length; i++) {
			if (left == arguments [i])
				return true;
		}
		return false;
	},

	arrayToSet: function (array) {
		var ret = {};
		for (var i = 0; i < array.length; i++)
			ret [array [i]] = true;
		return ret;
	},

	setToArray: function (set) {
		var ret = [];
		for (var key in set)
			ret.push (key);
		return ret;
	},
	objectToArray: function (obj) {
		var ret = [];
		for (var key in obj)
			ret.push (obj[key]);
		return ret;
	},

	map: function () {
		var ret = {};
		for (var i = 0; i < arguments.length; i += 2) {
			ret [arguments [i]] = arguments [i + 1];
		}
		return ret;
	},

	mapKeys: function (map) {
		var ret = [];
		for (var key in map)
			ret.push (key);
		return ret;
	},

	mapNumberKeys: function (map) {
		var ret = [];
		for (var key in map)
			ret.push (Number (key));
		return ret;
	},

	numberSetToArray: function (set) {
		var ret = [];
		for (var i in set)
			ret.push (Number (i));
		return ret;
	},

	stringSetToArray: function (set) {
		var ret = [];
		for (var i in set)
			ret.push (String (i));
		return ret;
	},

	mapValues: function (map) {
		var ret = [];
		for (var i in map)
			ret.push (map [i]);
		return ret;
	},
	
	randomArray: function (in_array, in_len) {
		var ret = [];
		var present = [];
		for (var i= 0; i<in_len; i++){
			var random = Math.floor (in_array.length * Math.random ());
			if(!this.inArray(random,present)){
				present.push(random);
				ret.push (in_array[random]);
			}
		}
		return ret;
	},
	
	ArrayAminusB: function (in_a, in_b) {
	var ret = [];
	var newValue = false;
	for(var i = 0 ; i<in_a.length; i++){
		if((!this.inArray(in_a[i],in_b))){
			ret.push(in_a[i]);
		}
	}
	return ret;
	},
	
	isArray: function (any) {
		if (typeof any != 'object') return false;
		if (! (any instanceof Array)) return false;
		return true;
	},

	isMap: function (object) {
		if (typeof object != 'object') return false;
		if (object == null) return false;
		if (object.constructor != Object && object instanceof object.constructor) return false;
		return true;
	},

	isRegExp: function (any) {
		if (typeof any != 'function') return false;
		if (any.constructor != RegExp) return false;
		return true;
	},

	isString: function (any) {
		return typeof any == 'string';
	},

	isInt: function (any) {
		return typeof any == 'number' && Math.floor (any) == any;
	},

	isIntArray: function (any) {
		if (! jj.isArray (any)) return false;
		for (var i = 0; i < any.length; i++) {
			if (! jj.isInt (any [i])) return false;
		}
		return true;
	},

	isStringArray: function (any) {
		if (! jj.isArray (any)) return false;
		for (var i = 0; i < any.length; i++) {
			if (! jj.isString (any [i])) return false;
		}
		return true;
	},

	flatten: function (src) {
		var dst = [];
		for (var i = 0; i < arguments.length; i++)
			jj.flattenReal (dst, arguments [i]);
		return dst;
	},

	flattenReal: function (dst, src) {
		if (jj.isArray (src)) {
			for (var i = 0; i < src.length; i++) {
				jj.flattenReal (dst, src [i]);
			}
		} else {
			dst.push (src);
		}
	},

	alert: function () {
		switch (arguments.length) {
		case 1:
			return alert (jj.pretty (arguments [0]));
		case 2:
			return alert (jj.callSummary (arguments [0], arguments [1]));
		default:
			throw 'Invalid number of args for jj.alery: ' + arguments.length;
		}
	},

	isObject: function (any) {
		return typeof any == 'object';
	},

	isBoolean: function (any) {
		return typeof any == 'boolean';
	},

	isFunction: function (any) {
		return typeof any == 'function';
	},

	prettyArgs: function (args) {
		var s = '(';
		for (var i = 0; i < args.length; i++) {
			if (i > 0) s += ', ';
			s += jj.pretty (args [i]);
		}
		return s + ')';
	},

	argsSlice: function (args, offset, length) {
		if (offset == undefined) offset = 0;
		if (length == undefined) length = args.length - offset;
		if (length < 0) length = 0;
		var ret = Array (length);
		for (var i = offset, j = 0; i < offset + length; i++, j++) {
			ret [j] = i < args.length? args [i] : undefined;
		}
		return ret;
	},

	isUndefined: function (any) {
		return any === undefined;
	},

	isNull: function (any) {
		return any === null;
	},

	createNamedElement: function( type, name ) {
		var element;
		try {
			element = document.createElement('<'+type+' name="'+name+'">');
		} catch (e) { }
		if (!element || !element.name) { // Not in IE, then
			element = document.createElement(type);
			element.name = name;
		}
		return element;
	},

	inArray: function (search, array) {
		for (var i = 0; i < array.length; i++) {
			if (array [i] === search) return true;
		}
		return false;
	},

	randomChoice: function (array) {
		return array [Math.floor (array.length * Math.random ())];
	},

	anyInArray: function (searches, array) {
		for (var i = 0; i < searches.length; i++) {
			for (var j in array)
				if (array[j] == searches[i]) return true;
		}
		return false;
	},

	join: function () {
		var ret = [];
		return ret.concat.apply (ret, jj.argsSlice (arguments));
	},

	joinInto: function (arr) {
		for (var i = 1; i < arguments.length; i++) {
			for (var j = 0; j < arguments [i].length; j++) {
				arr.push (arguments [i] [j]);
			}
		}
		return arr;
	},

	coalesce: function () {
		for (var i = 0; i < arguments.length; i++) {
			if (arguments [i] != null && arguments [i] != undefined)
				return arguments [i];
		}
		return undefined;
	},

	arrayDel: function (array, elem) {
		var i = array.indexOf (elem);
		if (i >= 0) {
			array.splice (i, 1);
		}
	},

	firstToUpper: function (str) {
		if (str.length == 0) return '';
		return str.substr (0, 1).toUpperCase () + str.substr (1);
	},

	pluralize: function (num, singular, plural) {
		if (! plural) plural = singular + 's';
		if (num == 1) return '1 ' + singular;
		return String (num) + ' ' + plural;
	},

	toYmd: function (date) {
		return '' +
			jj.zeroPad (date.getFullYear (), 4) + '-' +
			jj.zeroPad (date.getMonth () + 1, 2) + '-' +
			jj.zeroPad (date.getDate (), 2);
	},

	zeroPad: function (num, digits) {
		var s = String (num);
		while (s.length < digits)
			s = '0' + s;
		return s;
	},

	numberFormat: function (num, before, after) {
		var prefix = jj.zeroPad (Math.floor (num), before);
		num = Math.abs (num);
		num = num - Math.floor (num);
		for (var i = 0; i < after; i++) num = num * 10;
		num = Math.round (num);
		var suffix = String (num)
		while (suffix.length < after) suffix = '0' + suffix;
		return prefix + '.' + suffix;
	},

	mapHas: function (map, name) {
		return Object.prototype.propertyIsEnumerable.apply (map, [ name ]);
	},

	mapCount: function (map) {
		var ret = 0;
		for (var key in map) ret++;
		return ret;
	},

	isArguments: function (any) {
		if (typeof any != 'object') return false;
		if (jj.mapCount (any) != 0) return false;
		if (! Object.prototype.hasOwnProperty.apply (any, [ 'length' ])) return false;
		return true;
	},

	mapEmpty: function (map) {
		for (var key in map) { return false; }
		return true;
	},

	arrayMerge: function () {
		var set = {}, ret = [];
		for (var i = 0; i < arguments.length; i++) {
			for (var j = 0; j < arguments [i].length; j++) {
				var val = arguments [i] [j];
				if (set [val]) continue;
				set [val] = true;
				ret.push (val);
			}
		}
		return ret;
	},
	
	arrayMergeInto: function (dst) {

		// initialise set with all elements in dst
		var set = {};
		for (var j = 0; j < dst.length; j++) {
			set [dst [j]] = true;
		}

		// then add any from the other args, marking them in set as we go along
		for (var i = 1; i < arguments.length; i++) {
			for (var j = 0; j < arguments [i].length; j++) {
				var val = arguments [i] [j];
				if (set [val]) continue;
				set [val] = true;
				dst.push (val);
			}
		}

		// and return
		return dst;
	},
	
	arrayDiff: function (plus) {
		var set = {}, ret = [];
		for (var i = 1; i < arguments.length; i++) {
			for (var j = 0; j < arguments [i].length; j++) {
				set [arguments [i] [j]] = true;
			}
		}
		for (var i = 0; i < plus.length; i++) {
			if (! set [plus [i]])
				ret.push (plus [i]);
			set [plus [i]] = true;
		}
		return ret;
	},

	arrayUniq: function (arr) {
		var set = {}, ret = [];
		for (var i = 0; i < arr.length; i++) {
			if (set [arr [i]]) continue;
			set [arr [i]] = true;
			ret.push (arr [i]);
		}
		return ret;
	},

	formatCountry: function(country, region, city){
		var locationString = '';
		locationString = locationString + country;
		if(region){
			locationString = locationString + ', '+ region;
		}
		if(city){
			locationString = locationString + ', '+ city;
		}
		return locationString;
	},

	formatDate: function (dob) {
		splitDob = dob.split('-');
		var dob = new Date(splitDob[0], splitDob[1]-1, splitDob[2]);
		var today = new Date();
		var year = today.getFullYear();
		var roughAge = year - splitDob[0];
		today.setFullYear(splitDob[0]);
		if ((today-dob) < 0) roughAge -= 1;
		return roughAge;
	},

	ifUndef: function () {
		for (var i = 0; i < arguments.length; i++) {
			if (arguments [i] == undefined) continue;
			return arguments [i];
		}
		return undefined;
	},
	
	arrayMap: function (arr, fn) {
		var ret = new Array (arr.length);
		for (var i = 0; i < arr.length; i++) {
			ret [i] = fn (arr [i]);
		}
		return ret;
	},

	spliceArray: function (array, index, count, data) {
		var temp = array.slice (index + count);
		array.splice (index);
		array.concat (data, temp);
	}
};


var MediaPlayer = jj.def ('MediaPlayer', {

	MediaPlayer: function () {

},
init: function () {
	// checks if we are ready to initialise
	// TODO: should check playState here too
	if (window.parent.frames['music'] == undefined || window.parent.frames['music'].soundManager == undefined || document.getElementById('jsMediaPlayer_author') == undefined) {
	//	alert("in init" + window.parent.frames['music']);
	//	alert("in init" + window.parent.frames['music'].document);
		window.setTimeout(jj.bind(this,'init'), 1000);
		return;
	}
	this.domStatus = document.getElementById('jsMediaPlayer_status');
	this.domControl = document.getElementById('jsMediaPlayer_control');
	
	this.domAuthor = document.getElementById('jsMediaPlayer_author');
	this.domTrack = document.getElementById('jsMediaPlayer_track');
	this.domDescription = document.getElementById('jsMediaPlayer_description');
	
	this.player = window.parent.frames['music'].soundManager;

	
	this.playIfRequired();
	this.updateStatus();
	this.updateDetails();
},
playIfRequired: function () {
	if ((this.readCookie("pause_gonetoosoon_music") == null || this.readCookie("pause_gonetoosoon_music") == false) && this.player.sounds[String(window.parent.frames['music'].currentTrack)] != undefined && this.player.sounds[String(window.parent.frames['music'].currentTrack)].playState != 1 && !this.player.sounds[String(window.parent.frames['music'].currentTrack)].paused)
		this.play();
	else if ((this.readCookie("pause_gonetoosoon_music") && this.readCookie("pause_gonetoosoon_music") == true)) 
		this.pause();
	else 
		this.updateStatus();
	
},
updateMessage: function (messageIn) {
	if (this.domStatus == undefined)
		this.domStatus = document.getElementById('jsMediaPlayer_status');

	if (this.domStatus != undefined)
		this.domStatus.innerHTML = messageIn;
},
updateDetails: function (messageIn) {
	window.parent.frames['music'].updateMediaGui(String(window.parent.frames['music'].currentTrack));
},
updateInfo: function (track, artist, comment) {

	if (this.domAuthor == undefined)
	this.domAuthor = document.getElementById('jsMediaPlayer_author');
	if (this.domTrack == undefined)
	this.domTrack = document.getElementById('jsMediaPlayer_track');
	if (this.domDescription == undefined)
	this.domDescription = document.getElementById('jsMediaPlayer_description');
	
	this.domAuthor.innerHTML = artist;
	this.domTrack.innerHTML =  track == '' ? 'Unknown Track' : track;
	this.domDescription.innerHTML = comment;
	
},
noMusic: function () {
	this.domStatus.innerHTML = 'No music to play!';

},
updateStatus: function () {
	if (this.player.sounds[String(window.parent.frames['music'].currentTrack)] != undefined && this.player.sounds[String(window.parent.frames['music'].currentTrack)].playState == 1 && !this.player.sounds[String(window.parent.frames['music'].currentTrack)].paused) {

		this.domStatus.innerHTML = 'Now Playing...';
		this.domControl.onclick = jj.bind(this, 'pause');
		this.domControl.innerHTML = 'Click here to Pause';
	} else if (this.player.sounds[String(window.parent.frames['music'].currentTrack)] != undefined && (this.player.sounds[String(window.parent.frames['music'].currentTrack)].playState == 0 || this.player.sounds[String(window.parent.frames['music'].currentTrack)].paused)) {
		this.domStatus.innerHTML = 'Paused';
		this.domControl.onclick = jj.bind(this,'play');
		this.domControl.innerHTML = 'Click here to Play';
	} else {
		this.domStatus.innerHTML = 'Player could not load!<br /><br />Check you have <a href="http://www.adobe.com/products/flashplayer/">Flash Player</a> installed';
	}
	
},
play: function () {
	if (this.readCookie("pause_gonetoosoon_music"))
		this.createCookie("pause_gonetoosoon_music",false,-1);
	
	if (!this.player.sounds[String(window.parent.frames['music'].currentTrack)].paused) {
		this.player.play(window.parent.frames['music'].currentTrack);
	} else {
		this.player.togglePause(String(window.parent.frames['music'].currentTrack));
	}
	this.updateStatus();
	

},
pause: function () {

	if (!this.readCookie("pause_gonetoosoon_music"))
		this.createCookie("pause_gonetoosoon_music", true, 365);

	this.player.togglePause(String(window.parent.frames['music'].currentTrack));
	this.updateStatus();
},
createCookie: function (name,value,days) {
	if (days) {
		var date = new Date();
		date.setTime(date.getTime()+(days*24*60*60*1000));
		var expires = "; expires="+date.toGMTString();
	}
	else var expires = "";
	document.cookie = name+"="+value+expires+"; path=/";
},
readCookie: function (name) {
	var nameEQ = name + "=";
	var ca = document.cookie.split(';');
	for(var i=0;i < ca.length;i++) {
		var c = ca[i];
		while (c.charAt(0)==' ') c = c.substring(1,c.length);
		if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
	}
	return null;
}
});

var GtsLoader = jj.def ('GtsLoader', {

	GtsLoader: function () {	
		this.loadEvents = [];	
	},
	addEvent: function (event) {
		this.loadEvents.push(event)
	},
	go : function () {
	
		for (var i in this.loadEvents)  {
			this.loadEvents[i]();
			}
	}
	
});

 		var gtsLoader = new GtsLoader();
 		
 		
 		
 		
var relationSelectChanged = function (domObject) {
		var row = document.getElementById("bookmarkRow"); 
	if(domObject.value!=0) { 
		row.style.display = 'none';
	} else {
		row.style.display = 'table-row';
	}
}
var swfobject=function(){var b="undefined",Q="object",n="Shockwave Flash",p="ShockwaveFlash.ShockwaveFlash",P="application/x-shockwave-flash",m="SWFObjectExprInst",j=window,K=document,T=navigator,o=[],N=[],i=[],d=[],J,Z=null,M=null,l=null,e=false,A=false;var h=function(){var v=typeof K.getElementById!=b&&typeof K.getElementsByTagName!=b&&typeof K.createElement!=b,AC=[0,0,0],x=null;if(typeof T.plugins!=b&&typeof T.plugins[n]==Q){x=T.plugins[n].description;if(x&&!(typeof T.mimeTypes!=b&&T.mimeTypes[P]&&!T.mimeTypes[P].enabledPlugin)){x=x.replace(/^.*\s+(\S+\s+\S+$)/,"$1");AC[0]=parseInt(x.replace(/^(.*)\..*$/,"$1"),10);AC[1]=parseInt(x.replace(/^.*\.(.*)\s.*$/,"$1"),10);AC[2]=/r/.test(x)?parseInt(x.replace(/^.*r(.*)$/,"$1"),10):0}}else{if(typeof j.ActiveXObject!=b){var y=null,AB=false;try{y=new ActiveXObject(p+".7")}catch(t){try{y=new ActiveXObject(p+".6");AC=[6,0,21];y.AllowScriptAccess="always"}catch(t){if(AC[0]==6){AB=true}}if(!AB){try{y=new ActiveXObject(p)}catch(t){}}}if(!AB&&y){try{x=y.GetVariable("$version");if(x){x=x.split(" ")[1].split(",");AC=[parseInt(x[0],10),parseInt(x[1],10),parseInt(x[2],10)]}}catch(t){}}}}var AD=T.userAgent.toLowerCase(),r=T.platform.toLowerCase(),AA=/webkit/.test(AD)?parseFloat(AD.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,q=false,z=r?/win/.test(r):/win/.test(AD),w=r?/mac/.test(r):/mac/.test(AD);
/*@cc_on q=true;@if(@_win32)z=true;@elif(@_mac)w=true;@end@*/
return{w3cdom:v,pv:AC,webkit:AA,ie:q,win:z,mac:w}}();var L=function(){if(!h.w3cdom){return }f(H);if(h.ie&&h.win){try{K.write("<script id=__ie_ondomload defer=true src=//:><\/script>");J=C("__ie_ondomload");if(J){I(J,"onreadystatechange",S)}}catch(q){}}if(h.webkit&&typeof K.readyState!=b){Z=setInterval(function(){if(/loaded|complete/.test(K.readyState)){E()}},10)}if(typeof K.addEventListener!=b){K.addEventListener("DOMContentLoaded",E,null)}R(E)}();function S(){if(J.readyState=="complete"){J.parentNode.removeChild(J);E()}}function E(){if(e){return }if(h.ie&&h.win){var v=a("span");try{var u=K.getElementsByTagName("body")[0].appendChild(v);u.parentNode.removeChild(u)}catch(w){return }}e=true;if(Z){clearInterval(Z);Z=null}var q=o.length;for(var r=0;r<q;r++){o[r]()}}function f(q){if(e){q()}else{o[o.length]=q}}function R(r){if(typeof j.addEventListener!=b){j.addEventListener("load",r,false)}else{if(typeof K.addEventListener!=b){K.addEventListener("load",r,false)}else{if(typeof j.attachEvent!=b){I(j,"onload",r)}else{if(typeof j.onload=="function"){var q=j.onload;j.onload=function(){q();r()}}else{j.onload=r}}}}}function H(){var t=N.length;for(var q=0;q<t;q++){var u=N[q].id;if(h.pv[0]>0){var r=C(u);if(r){N[q].width=r.getAttribute("width")?r.getAttribute("width"):"0";N[q].height=r.getAttribute("height")?r.getAttribute("height"):"0";if(c(N[q].swfVersion)){if(h.webkit&&h.webkit<312){Y(r)}W(u,true)}else{if(N[q].expressInstall&&!A&&c("6.0.65")&&(h.win||h.mac)){k(N[q])}else{O(r)}}}}else{W(u,true)}}}function Y(t){var q=t.getElementsByTagName(Q)[0];if(q){var w=a("embed"),y=q.attributes;if(y){var v=y.length;for(var u=0;u<v;u++){if(y[u].nodeName=="DATA"){w.setAttribute("src",y[u].nodeValue)}else{w.setAttribute(y[u].nodeName,y[u].nodeValue)}}}var x=q.childNodes;if(x){var z=x.length;for(var r=0;r<z;r++){if(x[r].nodeType==1&&x[r].nodeName=="PARAM"){w.setAttribute(x[r].getAttribute("name"),x[r].getAttribute("value"))}}}t.parentNode.replaceChild(w,t)}}function k(w){A=true;var u=C(w.id);if(u){if(w.altContentId){var y=C(w.altContentId);if(y){M=y;l=w.altContentId}}else{M=G(u)}if(!(/%$/.test(w.width))&&parseInt(w.width,10)<310){w.width="310"}if(!(/%$/.test(w.height))&&parseInt(w.height,10)<137){w.height="137"}K.title=K.title.slice(0,47)+" - Flash Player Installation";var z=h.ie&&h.win?"ActiveX":"PlugIn",q=K.title,r="MMredirectURL="+j.location+"&MMplayerType="+z+"&MMdoctitle="+q,x=w.id;if(h.ie&&h.win&&u.readyState!=4){var t=a("div");x+="SWFObjectNew";t.setAttribute("id",x);u.parentNode.insertBefore(t,u);u.style.display="none";var v=function(){u.parentNode.removeChild(u)};I(j,"onload",v)}U({data:w.expressInstall,id:m,width:w.width,height:w.height},{flashvars:r},x)}}function O(t){if(h.ie&&h.win&&t.readyState!=4){var r=a("div");t.parentNode.insertBefore(r,t);r.parentNode.replaceChild(G(t),r);t.style.display="none";var q=function(){t.parentNode.removeChild(t)};I(j,"onload",q)}else{t.parentNode.replaceChild(G(t),t)}}function G(v){var u=a("div");if(h.win&&h.ie){u.innerHTML=v.innerHTML}else{var r=v.getElementsByTagName(Q)[0];if(r){var w=r.childNodes;if(w){var q=w.length;for(var t=0;t<q;t++){if(!(w[t].nodeType==1&&w[t].nodeName=="PARAM")&&!(w[t].nodeType==8)){u.appendChild(w[t].cloneNode(true))}}}}}return u}function U(AG,AE,t){var q,v=C(t);if(v){if(typeof AG.id==b){AG.id=t}if(h.ie&&h.win){var AF="";for(var AB in AG){if(AG[AB]!=Object.prototype[AB]){if(AB.toLowerCase()=="data"){AE.movie=AG[AB]}else{if(AB.toLowerCase()=="styleclass"){AF+=' class="'+AG[AB]+'"'}else{if(AB.toLowerCase()!="classid"){AF+=" "+AB+'="'+AG[AB]+'"'}}}}}var AD="";for(var AA in AE){if(AE[AA]!=Object.prototype[AA]){AD+='<param name="'+AA+'" value="'+AE[AA]+'" />'}}v.outerHTML='<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"'+AF+">"+AD+"</object>";i[i.length]=AG.id;q=C(AG.id)}else{if(h.webkit&&h.webkit<312){var AC=a("embed");AC.setAttribute("type",P);for(var z in AG){if(AG[z]!=Object.prototype[z]){if(z.toLowerCase()=="data"){AC.setAttribute("src",AG[z])}else{if(z.toLowerCase()=="styleclass"){AC.setAttribute("class",AG[z])}else{if(z.toLowerCase()!="classid"){AC.setAttribute(z,AG[z])}}}}}for(var y in AE){if(AE[y]!=Object.prototype[y]){if(y.toLowerCase()!="movie"){AC.setAttribute(y,AE[y])}}}v.parentNode.replaceChild(AC,v);q=AC}else{var u=a(Q);u.setAttribute("type",P);for(var x in AG){if(AG[x]!=Object.prototype[x]){if(x.toLowerCase()=="styleclass"){u.setAttribute("class",AG[x])}else{if(x.toLowerCase()!="classid"){u.setAttribute(x,AG[x])}}}}for(var w in AE){if(AE[w]!=Object.prototype[w]&&w.toLowerCase()!="movie"){F(u,w,AE[w])}}v.parentNode.replaceChild(u,v);q=u}}}return q}function F(t,q,r){var u=a("param");u.setAttribute("name",q);u.setAttribute("value",r);t.appendChild(u)}function X(r){var q=C(r);if(q&&(q.nodeName=="OBJECT"||q.nodeName=="EMBED")){if(h.ie&&h.win){if(q.readyState==4){B(r)}else{j.attachEvent("onload",function(){B(r)})}}else{q.parentNode.removeChild(q)}}}function B(t){var r=C(t);if(r){for(var q in r){if(typeof r[q]=="function"){r[q]=null}}r.parentNode.removeChild(r)}}function C(t){var q=null;try{q=K.getElementById(t)}catch(r){}return q}function a(q){return K.createElement(q)}function I(t,q,r){t.attachEvent(q,r);d[d.length]=[t,q,r]}function c(t){var r=h.pv,q=t.split(".");q[0]=parseInt(q[0],10);q[1]=parseInt(q[1],10)||0;q[2]=parseInt(q[2],10)||0;return(r[0]>q[0]||(r[0]==q[0]&&r[1]>q[1])||(r[0]==q[0]&&r[1]==q[1]&&r[2]>=q[2]))?true:false}function V(v,r){if(h.ie&&h.mac){return }var u=K.getElementsByTagName("head")[0],t=a("style");t.setAttribute("type","text/css");t.setAttribute("media","screen");if(!(h.ie&&h.win)&&typeof K.createTextNode!=b){t.appendChild(K.createTextNode(v+" {"+r+"}"))}u.appendChild(t);if(h.ie&&h.win&&typeof K.styleSheets!=b&&K.styleSheets.length>0){var q=K.styleSheets[K.styleSheets.length-1];if(typeof q.addRule==Q){q.addRule(v,r)}}}function W(t,q){var r=q?"visible":"hidden";if(e&&C(t)){C(t).style.visibility=r}else{V("#"+t,"visibility:"+r)}}function g(s){var r=/[\\\"<>\.;]/;var q=r.exec(s)!=null;return q?encodeURIComponent(s):s}var D=function(){if(h.ie&&h.win){window.attachEvent("onunload",function(){var w=d.length;for(var v=0;v<w;v++){d[v][0].detachEvent(d[v][1],d[v][2])}var t=i.length;for(var u=0;u<t;u++){X(i[u])}for(var r in h){h[r]=null}h=null;for(var q in swfobject){swfobject[q]=null}swfobject=null})}}();return{registerObject:function(u,q,t){if(!h.w3cdom||!u||!q){return }var r={};r.id=u;r.swfVersion=q;r.expressInstall=t?t:false;N[N.length]=r;W(u,false)},getObjectById:function(v){var q=null;if(h.w3cdom){var t=C(v);if(t){var u=t.getElementsByTagName(Q)[0];if(!u||(u&&typeof t.SetVariable!=b)){q=t}else{if(typeof u.SetVariable!=b){q=u}}}}return q},embedSWF:function(x,AE,AB,AD,q,w,r,z,AC){if(!h.w3cdom||!x||!AE||!AB||!AD||!q){return }AB+="";AD+="";if(c(q)){W(AE,false);var AA={};if(AC&&typeof AC===Q){for(var v in AC){if(AC[v]!=Object.prototype[v]){AA[v]=AC[v]}}}AA.data=x;AA.width=AB;AA.height=AD;var y={};if(z&&typeof z===Q){for(var u in z){if(z[u]!=Object.prototype[u]){y[u]=z[u]}}}if(r&&typeof r===Q){for(var t in r){if(r[t]!=Object.prototype[t]){if(typeof y.flashvars!=b){y.flashvars+="&"+t+"="+r[t]}else{y.flashvars=t+"="+r[t]}}}}f(function(){U(AA,y,AE);if(AA.id==AE){W(AE,true)}})}else{if(w&&!A&&c("6.0.65")&&(h.win||h.mac)){A=true;W(AE,false);f(function(){var AF={};AF.id=AF.altContentId=AE;AF.width=AB;AF.height=AD;AF.expressInstall=w;k(AF)})}}},getFlashPlayerVersion:function(){return{major:h.pv[0],minor:h.pv[1],release:h.pv[2]}},hasFlashPlayerVersion:c,createSWF:function(t,r,q){if(h.w3cdom){return U(t,r,q)}else{return undefined}},removeSWF:function(q){if(h.w3cdom){X(q)}},createCSS:function(r,q){if(h.w3cdom){V(r,q)}},addDomLoadEvent:f,addLoadEvent:R,getQueryParamValue:function(v){var u=K.location.search||K.location.hash;if(v==null){return g(u)}if(u){var t=u.substring(1).split("&");for(var r=0;r<t.length;r++){if(t[r].substring(0,t[r].indexOf("="))==v){return g(t[r].substring((t[r].indexOf("=")+1)))}}}return""},expressInstallCallback:function(){if(A&&M){var q=C(m);if(q){q.parentNode.replaceChild(M,q);if(l){W(l,true);if(h.ie&&h.win){M.style.display="block"}}M=null;l=null;A=false}}}}}();



/*
SoundManager 2: Javascript Sound for the Web
--------------------------------------------
http://schillmania.com/projects/soundmanager2/

Copyright (c) 2008, Scott Schiller. All rights reserved.
Code licensed under the BSD License:
http://schillmania.com/projects/soundmanager2/license.txt

V2.77a.20080901
*/

function SoundManager(smURL,smID) {

this.flashVersion = 8;           // Version of flash to require, either 8 or 9. Some API features require Flash 9.
this.debugMode = false;           // enable debugging output (div#soundmanager-debug, OR console if available + configured)
this.useConsole = true;          // use firebug/safari console.log()-type debug console if available
this.consoleOnly = false;        // if console is being used, do not create/write to #soundmanager-debug
this.waitForWindowLoad = false;  // force SM2 to wait for window.onload() before trying to call soundManager.onload()
this.nullURL = 'data/null.mp3';  // path to "null" (empty) MP3 file, used to unload sounds (Flash 8 only)
this.allowPolling = true;        // allow flash to poll for status update (required for "while playing", peak, sound spectrum functions to work.)

this.defaultOptions = {
 'autoLoad': false,             // enable automatic loading (otherwise .load() will be called on demand with .play(), the latter being nicer on bandwidth - if you want to .load yourself, you also can)
 'stream': true,                // allows playing before entire file has loaded (recommended)
 'autoPlay': false,             // enable playing of file as soon as possible (much faster if "stream" is true)
 'onid3': null,                 // callback function for "ID3 data is added/available"
 'onload': null,                // callback function for "load finished"
 'whileloading': null,          // callback function for "download progress update" (X of Y bytes received)
 'onplay': null,                // callback for "play" start
 'onpause': null,               // callback for "pause"
 'onresume': null,              // callback for "resume" (pause toggle)
 'whileplaying': null,          // callback during play (position update)
 'onstop': null,                // callback for "user stop"
 'onfinish': null,              // callback function for "sound finished playing"
 'onbeforefinish': null,        // callback for "before sound finished playing (at [time])"
 'onbeforefinishtime': 5000,    // offset (milliseconds) before end of sound to trigger beforefinish (eg. 1000 msec = 1 second)
 'onbeforefinishcomplete':null, // function to call when said sound finishes playing
 'onjustbeforefinish':null,     // callback for [n] msec before end of current sound
 'onjustbeforefinishtime':200,  // [n] - if not using, set to 0 (or null handler) and event will not fire.
 'multiShot': true,             // let sounds "restart" or layer on top of each other when played multiple times, rather than one-shot/one at a time
 'position': null,              // offset (milliseconds) to seek to within loaded sound data.
 'pan': 0,                      // "pan" settings, left-to-right, -100 to 100
 'volume': 100                  // self-explanatory. 0-100, the latter being the max.
};

this.flash9Options = {           // Flash 9-only options, merged into defaultOptions if applicable
 usePeakData: false,            // enable left/right channel peak (level) data
 useWaveformData: false,        // enable sound spectrum (raw waveform data) - WARNING: CPU-INTENSIVE: may set CPUs on fire.
 useEQData: false               // enable sound EQ (frequency spectrum data) - WARNING: Also CPU-intensive.
};

this.flashBlockHelper = {
 enabled: false,                // try to help mozilla/firefox users if flashBlock is suspected to be breaking SM2 (preventing start-up)
 message: [                     // "nag bar" to show when messaging the user, if SM2 fails on firefox etc.
  '<div id="sm2-flashblock" style="position:fixed;left:0px;top:0px;width:100%;min-height:24px;z-index:9999;background:#666;color:#fff;font-family:helvetica,verdana,arial;font-size:11px;border-bottom:1px solid #333;opacity:0.95">',
  '<div style="float:right;display:inline;margin-right:0.5em;color:#999;line-height:24px">[<a href="#noflashblock" onclick="document.getElementById(\'sm2-flashblock\').style.display=\'none\'" title="Go away! :)" style="color:#fff;text-decoration:none">x</a>]</div>',
  '<div id="sm2-flashmovie" style="float:left;display:inline;margin-left:0.5em;margin-right:0.5em"><!-- [flash] --></div>',
  '<div style="padding-left:0.5em;padding-right:0.5em;line-height:24px">Using Flashblock? Please right-click the icon and "<b>allow flash from this site</b>" to enable sound/audio features, and then reload this page.</div>',
  '</div>'
 ]
};

var self = this; 
this.version = null;
this.versionNumber = 'V2.77a.20080901';
this.movieURL = null;
this.url = null;
this.swfLoaded = false;
this.enabled = false;
this.o = null;
this.id = (smID||'sm2movie');
this.oMC = null;
this.sounds = [];
this.soundIDs = [];
this.muted = false;
this.isIE = (navigator.userAgent.match(/MSIE/i));
this.isSafari = (navigator.userAgent.match(/safari/i));
this.isGecko = (navigator.userAgent.match(/gecko/i));
this.debugID = 'soundmanager-debug';
this._debugOpen = true;
this._didAppend = false;
this._appendSuccess = false;
this._didInit = false;
this._disabled = false;
this._windowLoaded = false;
this._hasConsole = (typeof console != 'undefined' && typeof console.log != 'undefined');
this._debugLevels = ['log','info','warn','error'];
this._defaultFlashVersion = 8;
this.features = {
 peakData: false,
 waveformData: false,
 eqData: false
};
this.sandbox = {
 'type': null,
 'types': {
   'remote': 'remote (domain-based) rules',
   'localWithFile': 'local with file access (no internet access)',
   'localWithNetwork': 'local with network (internet access only, no local access)',
   'localTrusted': 'local, trusted (local + internet access)'
 },
 'description': null,
 'noRemote': null,
 'noLocal': null
};
this._setVersionInfo = function() {
 if (self.flashVersion != 8 && self.flashVersion != 9) {
   alert('soundManager.flashVersion must be 8 or 9. "'+self.flashVersion+'" is invalid. Reverting to '+self._defaultFlashVersion+'.');
   self.flashVersion = self._defaultFlashVersion;
 }
 self.version = self.versionNumber+(self.flashVersion==9?' (AS3/Flash 9)':' (AS2/Flash 8)');
 self.movieURL = (self.flashVersion==8?'soundmanager2.swf':'soundmanager2_flash9.swf');
 self.features.peakData = self.features.waveformData = self.features.eqData = (self.flashVersion==9);
}
this._overHTTP = (document.location?document.location.protocol.match(/http/i):null);
this._waitingforEI = false;
this._initPending = false;
this._tryInitOnFocus = (this.isSafari && typeof document.hasFocus == 'undefined');
this._isFocused = (typeof document.hasFocus != 'undefined'?document.hasFocus():null);
this._okToDisable = !this._tryInitOnFocus;
var flashCPLink = 'http://www.macromedia.com/support/documentation/en/flashplayer/help/settings_manager04.html';

// --- public methods ---

this.supported = function() {
 return (self._didInit && !self._disabled);
};

this.getMovie = function(smID) {
 return self.isIE?window[smID]:(self.isSafari?document.getElementById(smID)||document[smID]:document.getElementById(smID));
};

this.loadFromXML = function(sXmlUrl) {
 try {
   self.o._loadFromXML(sXmlUrl);
 } catch(e) {
   self._failSafely();
   return true;
 };
};

this.createSound = function(oOptions) {
 if (!self._didInit) throw new Error('soundManager.createSound(): Not loaded yet - wait for soundManager.onload() before calling sound-related methods');
 if (arguments.length==2) {
   // function overloading in JS! :) ..assume simple createSound(id,url) use case
   oOptions = {'id':arguments[0],'url':arguments[1]};
 };
 var thisOptions = self._mergeObjects(oOptions); // inherit SM2 defaults
 self._writeDebug('soundManager.createSound(): '+thisOptions.id+' ('+thisOptions.url+')',1);
 if (self._idCheck(thisOptions.id,true)) {
   self._writeDebug('soundManager.createSound(): '+thisOptions.id+' exists',1);
   return self.sounds[thisOptions.id];
 };
 self.sounds[thisOptions.id] = new SMSound(self,thisOptions);
 self.soundIDs[self.soundIDs.length] = thisOptions.id;
 // AS2:
 if (self.flashVersion==8) {
   self.o._createSound(thisOptions.id,thisOptions.onjustbeforefinishtime);
 } else {
   self.o._createSound(thisOptions.id,thisOptions.url,thisOptions.onjustbeforefinishtime,thisOptions.usePeakData,thisOptions.useWaveformData,thisOptions.useEQData);
 };
 if (thisOptions.autoLoad || thisOptions.autoPlay) window.setTimeout(function(){self.sounds[thisOptions.id].load(thisOptions);},20);
 if (thisOptions.autoPlay) {
	  if (self.flashVersion == 8) {
	    self.sounds[thisOptions.id].playState = 1; // we can only assume this sound will be playing soon.
	  } else {
	    self.sounds[thisOptions.id].play();	
	  }
	}
 return self.sounds[thisOptions.id];
};

this.destroySound = function(sID,bFromSound) {
 // explicitly destroy a sound before normal page unload, etc.
 if (!self._idCheck(sID)) return false;
 for (var i=0; i<self.soundIDs.length; i++) {
   if (self.soundIDs[i] == sID) {
	    self.soundIDs.splice(i,1);
     continue;
   };
 };
 self.sounds[sID].unload();
 if (!bFromSound) {
   // ignore if being called from SMSound instance
   self.sounds[sID].destruct();
 };
 delete self.sounds[sID];
};

this.load = function(sID,oOptions) {
 if (!self._idCheck(sID)) return false;
 self.sounds[sID].load(oOptions);
};

this.unload = function(sID) {
 if (!self._idCheck(sID)) return false;
 self.sounds[sID].unload();
};

this.play = function(sID,oOptions) {
 if (!self._idCheck(sID)) {
   if (typeof oOptions != 'Object') oOptions = {url:oOptions}; // overloading use case: play('mySound','/path/to/some.mp3');
   if (oOptions && oOptions.url) {
     // overloading use case, creation + playing of sound: .play('someID',{url:'/path/to.mp3'});
     self._writeDebug('soundController.play(): attempting to create "'+sID+'"',1);
     oOptions.id = sID;
     self.createSound(oOptions);
   } else {
     return false;
   };
 };
 self.sounds[sID].play(oOptions);
};

this.start = this.play; // just for convenience

this.setPosition = function(sID,nMsecOffset) {
 if (!self._idCheck(sID)) return false;
 self.sounds[sID].setPosition(nMsecOffset);
};

this.stop = function(sID) {
 if (!self._idCheck(sID)) return false;
 self._writeDebug('soundManager.stop('+sID+')',1);
 self.sounds[sID].stop(); 
};

this.stopAll = function() {
 self._writeDebug('soundManager.stopAll()',1);
 for (var oSound in self.sounds) {
   if (self.sounds[oSound] instanceof SMSound) self.sounds[oSound].stop(); // apply only to sound objects
 };
};

this.pause = function(sID) {
 if (!self._idCheck(sID)) return false;
 self.sounds[sID].pause();
};

this.resume = function(sID) {
 if (!self._idCheck(sID)) return false;
 self.sounds[sID].resume();
};

this.togglePause = function(sID) {
 if (!self._idCheck(sID)) return false;
 self.sounds[sID].togglePause();
};

this.setPan = function(sID,nPan) {
 if (!self._idCheck(sID)) return false;
 self.sounds[sID].setPan(nPan);
};

this.setVolume = function(sID,nVol) {
 if (!self._idCheck(sID)) return false;
 self.sounds[sID].setVolume(nVol);
};

this.mute = function(sID) {
	if (typeof sID != 'string') sID = null;
 if (!sID) {
   var o = null;
   self._writeDebug('soundManager.mute(): Muting all sounds');
   for (var i=self.soundIDs.length; i--;) {
     self.sounds[self.soundIDs[i]].mute();
   }
   self.muted = true;
 } else {
   if (!self._idCheck(sID)) return false;
   self._writeDebug('soundManager.mute(): Muting "'+sID+'"');
   self.sounds[sID].mute();
 }
};

this.unmute = function(sID) {
 if (typeof sID != 'string') sID = null;
 if (!sID) {
   var o = null;
   self._writeDebug('soundManager.unmute(): Unmuting all sounds');
   for (var i=self.soundIDs.length; i--;) {
     self.sounds[self.soundIDs[i]].unmute();
   }
   self.muted = false;
 } else {
   if (!self._idCheck(sID)) return false;
   self._writeDebug('soundManager.unmute(): Unmuting "'+sID+'"');
   self.sounds[sID].unmute();
 }
};

this.setPolling = function(bPolling) {
 if (!self.o || !self.allowPolling) return false;
 // self._writeDebug('soundManager.setPolling('+bPolling+')');
 self.o._setPolling(bPolling);
};

this.disable = function(bUnload) {
 // destroy all functions
 if (self._disabled) return false;
 if (!bUnload && self.flashBlockHelper.enabled) {
   self.handleFlashBlock();
 }
 self._disabled = true;
 self._writeDebug('soundManager.disable(): Disabling all functions - future calls will return false.',1);
 for (var i=self.soundIDs.length; i--;) {
   self._disableObject(self.sounds[self.soundIDs[i]]);
 };
 self.initComplete(); // fire "complete", despite fail
 self._disableObject(self);
};

this.handleFlashBlock = function(bForce) {
	// Mozilla "flashblock" extension may prevent SM2 from starting up - try to notify user.
	function showNagbar() {
	  self._writeDebug('soundManager.handleFlashBlock(): Showing info bar');
	  var o = document.getElementById('sm2-flashblock');
	  if (!o) {
		try {
		  // var o = document.createElement('div');
		  var oC = document.getElementById('sm2-container');
		  if (oC) {
			// existing movie container
			oC.parentNode.removeChild(oC);
		  }
		  var oBar = document.createElement('div');
		  oBar.innerHTML = self.flashBlockHelper.message.join('').replace('<!-- [flash] -->',self._html);
		  self._getDocument().appendChild(oBar);
		  window.setTimeout(function(){
		    var oIco = document.getElementById('sm2-flashmovie').getElementsByTagName('div')[0];
		    oIco.style.background = 'url(chrome://flashblock/skin/flash-disabled-16.png) 0px 0px no-repeat';
		    oIco.style.border = 'none';
		    oIco.style.minWidth = '';
	        oIco.style.minHeight = '';
		    oIco.style.width = '16px';
		    oIco.style.height = '16px';
		    oIco.style.marginTop = '4px';
		    oIco.onmouseover = null; // holy crap, that works? damn.
		    oIco.onmouseout = null;
		    oIco.onclick = null; // only make right-click work. Unreal. ;)
		    document.getElementById('sm2-flashmovie').onclick = oIco.onclick;
		  },1);
		} catch(e) {
		  // oh noes, DOM fail!
		  self._writeDebug('soundManager.handleFlashblock: DOM append failed - may be XHTML-related.');
		  return false;
		}
	  } else {
     // maybe you made your own nag bar, including the flash movie?
     o.style.display = 'block';
	  };
	  this.onload = null;
	};
 if (bForce) {
	  showNagbar(); // for dev/testing
	  return false;
	};
	if (!self.isGecko) return false; // try to target only extension-supporting UAs
	if (window.location.toString().match(/\#noflashblock/i)) {
	  self._writeDebug('flashBlock nagbar disabled by URL - exiting');
	  return false; // if user already clicked "go away", and reloaded
	}
	// ok, now for something insanely hackish/brittle: flashblock detect via loading from chrome
	var chromeURL = 'chrome://flashblock/skin/flash-disabled-16.png';
	var img = new Image();
	img.style.position = 'absolute';
	img.style.left = '-256px';
	img.style.top = '-256px';
	img.onload = showNagbar;
	img.onerror = function() {this.onerror = null;} // no flashblock, or image URL changed etc. (d'oh)
	img.src = chromeURL;
 self._getDocument().appendChild(img);
};

this.getSoundById = function(sID,suppressDebug) {
 if (!sID) throw new Error('SoundManager.getSoundById(): sID is null/undefined');
 var result = self.sounds[sID];
 if (!result && !suppressDebug) {
   self._writeDebug('"'+sID+'" is an invalid sound ID.',2);
   // soundManager._writeDebug('trace: '+arguments.callee.caller);
 };
 return result;
};

this.onload = function() {
 // window.onload() equivalent for SM2, ready to create sounds etc.
 // this is a stub - you can override this in your own external script, eg. soundManager.onload = function() {}
 soundManager._writeDebug('<em>Warning</em>: soundManager.onload() is undefined.',2);
};

this.onerror = function() {
 // stub for user handler, called when SM2 fails to load/init
};

// --- "private" methods ---

this._idCheck = this.getSoundById;

this._disableObject = function(o) {
 for (var oProp in o) {
   if (typeof o[oProp] == 'function' && typeof o[oProp]._protected == 'undefined') o[oProp] = function(){return false;};
 };
 oProp = null;
};

this._failSafely = function() {
 // exception handler for "object doesn't support this property or method" or general failure
 var fpgssTitle = 'You may need to whitelist this location/domain eg. file:///C:/ or C:/ or mysite.com, or set ALWAYS ALLOW under the Flash Player Global Security Settings page. The latter is probably less-secure.';
 var flashCPL = '<a href="'+flashCPLink+'" title="'+fpgssTitle+'">view/edit</a>';
 var FPGSS = '<a href="'+flashCPLink+'" title="Flash Player Global Security Settings">FPGSS</a>';
 if (!self._disabled) {
   self._writeDebug('soundManager: Failed to initialise.',2);
   self.disable();
 };
};

this._normalizeMovieURL = function(smURL) {
 if (smURL) {
   if (smURL.match(/\.swf/)) {
     smURL = smURL.substr(0,smURL.lastIndexOf('.swf'));
   }
   if (smURL.lastIndexOf('/') != smURL.length-1) {
     smURL = smURL+'/';
   }
 }
 return(smURL && smURL.lastIndexOf('/')!=-1?smURL.substr(0,smURL.lastIndexOf('/')+1):'./')+self.movieURL;
};

this._getDocument = function() {
 return (document.body?document.body:(document.documentElement?document.documentElement:document.getElementsByTagName('div')[0]));
};

this._getDocument._protected = true;

this._createMovie = function(smID,smURL) {
 if (self._didAppend && self._appendSuccess) return false; // ignore if already succeeded
 if (window.location.href.indexOf('debug=1')+1) self.debugMode = true; // allow force of debug mode via URL
 self._didAppend = true;
	
 // safety check for legacy (change to Flash 9 URL)
 self._setVersionInfo();
 self.url = self._normalizeMovieURL(smURL?smURL:self.url);
 smURL = self.url;

 var htmlEmbed = '<embed name="'+smID+'" id="'+smID+'" src="'+smURL+'" width="1" height="1" quality="high" allowScriptAccess="always" pluginspage="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash"></embed>';
 var htmlObject = '<object id="'+smID+'" data="'+smURL+'" type="application/x-shockwave-flash" width="1" height="1"><param name="movie" value="'+smURL+'" /><param name="AllowScriptAccess" value="always" /><!-- --></object>';
 html = (!self.isIE?htmlEmbed:htmlObject);
 self._html = html; // flashblock reference

 var toggleElement = '<div id="'+self.debugID+'-toggle" style="position:fixed;_position:absolute;right:0px;bottom:0px;_top:0px;width:1.2em;height:1.2em;line-height:1.2em;margin:2px;padding:0px;text-align:center;border:1px solid #999;cursor:pointer;background:#fff;color:#333;z-index:706" title="Toggle SM2 debug console" onclick="soundManager._toggleDebug()">-</div>';
 var debugHTML = '<div id="'+self.debugID+'" style="display:'+(self.debugMode && ((!self._hasConsole||!self.useConsole)||(self.useConsole && self._hasConsole && !self.consoleOnly))?'block':'none')+';opacity:0.85"></div>';
 var appXHTML = 'soundManager._createMovie(): appendChild/innerHTML set failed. May be app/xhtml+xml DOM-related.';
 var sHTML = '<div id="sm2-container" style="position:absolute;left:-256px;top:-256px;width:1px;height:1px" class="movieContainer">'+html+'</div>'+(self.debugMode && ((!self._hasConsole||!self.useConsole)||(self.useConsole && self._hasConsole && !self.consoleOnly)) && !document.getElementById(self.debugID)?'x'+debugHTML+toggleElement:'');

 var oTarget = self._getDocument();
 if (oTarget) {
   self.oMC = document.createElement('div');
   self.oMC.id = 'sm2-container';
   self.oMC.className = 'movieContainer';
   // "hide" flash movie
   self.oMC.style.position = 'absolute';
   self.oMC.style.left = '-256px';
   self.oMC.style.width = '1px';
   self.oMC.style.height = '1px';
   try {
     oTarget.appendChild(self.oMC);
     self.oMC.innerHTML = html;
     self._appendSuccess = true;
   } catch(e) {
     throw new Error(appXHTML);
   };
   if (!document.getElementById(self.debugID) && ((!self._hasConsole||!self.useConsole)||(self.useConsole && self._hasConsole && !self.consoleOnly))) {
     var oDebug = document.createElement('div');
     oDebug.id = self.debugID;
     oDebug.style.display = (self.debugMode?'block':'none');
     if (self.debugMode) {
       try {
         var oD = document.createElement('div');
         oTarget.appendChild(oD);
         oD.innerHTML = toggleElement;
       } catch(e) {
         throw new Error(appXHTML);
       };
     };
     oTarget.appendChild(oDebug);
   };
   oTarget = null;
 };
 self._writeDebug('-- SoundManager 2 '+self.version+' --',1);
 self._writeDebug('soundManager._createMovie(): Trying to load '+smURL,1);
};

this._writeDebug = function(sText,sType,bTimestamp) {
 if (!self.debugMode) return false;
 if (typeof bTimestamp != 'undefined' && bTimestamp) {
   sText = sText + ' | '+new Date().getTime();
 };
 if (self._hasConsole && self.useConsole) {
   var sMethod = self._debugLevels[sType];
   if (typeof console[sMethod] != 'undefined') {
     console[sMethod](sText);
   } else {
     console.log(sText);
   };
   if (self.useConsoleOnly) return true;
 };
 var sDID = 'soundmanager-debug';
 try {
   var o = document.getElementById(sDID);
   if (!o) return false;
   var oItem = document.createElement('div');
   sText = sText.replace(/\n/g,'<br />');
   if (typeof sType == 'undefined') {
     var sType = 0;
   } else {
     sType = parseInt(sType);
   };
   oItem.innerHTML = sText;
   if (sType) {
     if (sType >= 2) oItem.style.fontWeight = 'bold';
     if (sType == 3) oItem.style.color = '#ff3333';
   };
   // o.appendChild(oItem); // top-to-bottom
   o.insertBefore(oItem,o.firstChild); // bottom-to-top
 } catch(e) {
   // oh well
 };
 o = null;
};
this._writeDebug._protected = true;

this._writeDebugAlert = function(sText) { alert(sText); };

if (window.location.href.indexOf('debug=alert')+1 && self.debugMode) {
 self._writeDebug = self._writeDebugAlert;
};

this._toggleDebug = function() {
 var o = document.getElementById(self.debugID);
 var oT = document.getElementById(self.debugID+'-toggle');
 if (!o) return false;
 if (self._debugOpen) {
   // minimize
   oT.innerHTML = '+';
   o.style.display = 'none';
 } else {
   oT.innerHTML = '-';
   o.style.display = 'block';
 };
 self._debugOpen = !self._debugOpen;
};

this._toggleDebug._protected = true;

this._debug = function() {
 self._writeDebug('--- soundManager._debug(): Current sound objects ---',1);
 for (var i=0,j=self.soundIDs.length; i<j; i++) {
   // self._writeDebug(self.sounds[self.soundIDs[i]].sID+' | '+self.sounds[self.soundIDs[i]].url,0);
   self.sounds[self.soundIDs[i]]._debug();
 };
};

this._mergeObjects = function(oMain,oAdd) {
 // non-destructive merge
 var o1 = {}; // clone o1
 for (var i in oMain) {
   o1[i] = oMain[i];
 }
 var o2 = (typeof oAdd == 'undefined'?self.defaultOptions:oAdd);
 for (var o in o2) {
   if (typeof o1[o] == 'undefined') o1[o] = o2[o];
 };
 return o1;
};

this.createMovie = function(sURL) {
 if (sURL) self.url = sURL;
 self._initMovie();
};

this.go = this.createMovie; // nice alias

this._initMovie = function() {
 // attempt to get, or create, movie
 if (self.o) return false; // pre-init may have fired this function before window.onload(), may already exist
 self.o = self.getMovie(self.id); // try to get flash movie (inline markup)
 if (!self.o) {
   // try to create
   self._createMovie(self.id,self.url);
   self.o = self.getMovie(self.id);
 };
 if (self.o) {
   self._writeDebug('soundManager._initMovie(): Got '+self.o.nodeName+' element ('+(self._didAppend?'created via JS':'static HTML')+')',1);
   self._writeDebug('soundManager._initMovie(): Waiting for ExternalInterface call from Flash..');
 };
};

this.waitForExternalInterface = function() {
 if (self._waitingForEI) return false;
 self._waitingForEI = true;
 if (self._tryInitOnFocus && !self._isFocused) {
   self._writeDebug('soundManager: Special case: Flash may not have started due to non-focused tab (Safari is lame), and/or focus cannot be detected. Waiting for focus-related event..');
   return false;
 };
 if (!self._didInit) {
   self._writeDebug('soundManager: Getting impatient, still waiting for Flash.. ;)');
 };
 setTimeout(function() {
   if (!self._didInit) {
     self._writeDebug('soundManager: No Flash response within reasonable time after document load.\nPossible causes: Flash version under 8, no support, or Flash security denying JS-Flash communication.',2);
     if (!self._overHTTP) {
       self._writeDebug('soundManager: Loading this page from local/network file system (not over HTTP?) Flash security likely restricting JS-Flash access. Consider adding current URL to "trusted locations" in the Flash player security settings manager at '+flashCPLink+', or simply serve this content over HTTP.',2);
     };
   };
   // if still not initialized and no other options, give up
   if (!self._didInit && self._okToDisable) self._failSafely();
 },750);
};

this.handleFocus = function() {
 if (self._isFocused || !self._tryInitOnFocus) return true;
 self._okToDisable = true;
 self._isFocused = true;
 self._writeDebug('soundManager.handleFocus()');
 if (self._tryInitOnFocus) {
   // giant Safari 3.1 hack - assume window in focus if mouse is moving, since document.hasFocus() not currently implemented.
   window.removeEventListener('mousemove',self.handleFocus,false);
 };
 // allow init to restart
 self._waitingForEI = false;
 setTimeout(self.waitForExternalInterface,500);
 // detach event
 if (window.removeEventListener) {
   window.removeEventListener('focus',self.handleFocus,false);
 } else if (window.detachEvent) {
   window.detachEvent('onfocus',self.handleFocus);
 };
};

this.initComplete = function() {
 if (self._didInit) return false;
 self._didInit = true;
 self._writeDebug('-- SoundManager 2 '+(self._disabled?'failed to load':'loaded')+' ('+(self._disabled?'security/load error':'OK')+') --',1);
 if (self._disabled) {
   self._writeDebug('soundManager.initComplete(): calling soundManager.onerror()',1);
   self.onerror.apply(window);
   return false;
 };
 if (self.waitForWindowLoad && !self._windowLoaded) {
   self._writeDebug('soundManager: Waiting for window.onload()');
   if (window.addEventListener) {
     window.addEventListener('load',self.initUserOnload,false);
   } else if (window.attachEvent) {
     window.attachEvent('onload',self.initUserOnload);
   };
   return false;
 } else {
   if (self.waitForWindowLoad && self._windowLoaded) {
     self._writeDebug('soundManager: Document already loaded');
   };
   self.initUserOnload();
 };
};

this.initUserOnload = function() {
 self._writeDebug('soundManager.initComplete(): calling soundManager.onload()',1);
 // call user-defined "onload", scoped to window
 try {
   self.onload.apply(window);
 } catch(e) {
   // something broke (likely JS error in user function)
   self._writeDebug('soundManager.onload() threw an exception: '+e.message,2);
   setTimeout(function(){throw new Error(e)},20);
   return false;
 };
 self._writeDebug('soundManager.onload() complete',1);
};

this.init = function() {
 self._writeDebug('-- soundManager.init() --');
 // called after onload()
 self._initMovie();
 if (self._didInit) {
   self._writeDebug('soundManager.init(): Already called?');
   return false;
 };
 // event cleanup
 if (window.removeEventListener) {
   window.removeEventListener('load',self.beginDelayedInit,false);
 } else if (window.detachEvent) {
   window.detachEvent('onload',self.beginDelayedInit);
 };
 try {
   self._writeDebug('Attempting to call JS -&gt; Flash..');
   self.o._externalInterfaceTest(false); // attempt to talk to Flash
   // self._writeDebug('Flash ExternalInterface call (JS-Flash) OK',1);
   if (!self.allowPolling) self._writeDebug('Polling (whileloading/whileplaying support) is disabled.',1);
   self.setPolling(true);
	  if (!self.debugMode) self.o._disableDebug();
   self.enabled = true;
 } catch(e) {
   self._failSafely();
   self.initComplete();
   return false;
 };
 self.initComplete();
};

this.beginDelayedInit = function() {
 self._writeDebug('soundManager.beginDelayedInit(): Document loaded');
 self._windowLoaded = true;
 setTimeout(self.waitForExternalInterface,500);
 setTimeout(self.beginInit,20);
};

this.beginInit = function() {
 if (self._initPending) return false;
 self.createMovie(); // ensure creation if not already done
 self._initMovie();
 self._initPending = true;
 return true;
};

this.domContentLoaded = function() {
 self._writeDebug('soundManager.domContentLoaded()');
 if (document.removeEventListener) document.removeEventListener('DOMContentLoaded',self.domContentLoaded,false);
 self.go();
};

this._externalInterfaceOK = function() {
 // callback from flash for confirming that movie loaded, EI is working etc.
 if (self.swfLoaded) return false;
 self._writeDebug('soundManager._externalInterfaceOK()');
 self.swfLoaded = true;
 self._tryInitOnFocus = false;
 if (self.isIE) {
   // IE needs a timeout OR delay until window.onload - may need TODO: investigating
   setTimeout(self.init,100);
 } else {
   self.init();
 };
};

this._setSandboxType = function(sandboxType) {
 var sb = self.sandbox;
 sb.type = sandboxType;
 sb.description = sb.types[(typeof sb.types[sandboxType] != 'undefined'?sandboxType:'unknown')];
 self._writeDebug('Flash security sandbox type: '+sb.type);
 if (sb.type == 'localWithFile') {
   sb.noRemote = true;
   sb.noLocal = false;
   self._writeDebug('Flash security note: Network/internet URLs will not load due to security restrictions. Access can be configured via Flash Player Global Security Settings Page: http://www.macromedia.com/support/documentation/en/flashplayer/help/settings_manager04.html',2);
 } else if (sb.type == 'localWithNetwork') {
   sb.noRemote = false;
   sb.noLocal = true;
 } else if (sb.type == 'localTrusted') {
   sb.noRemote = false;
   sb.noLocal = false;
 };
};

this.destruct = function() {
 self._writeDebug('soundManager.destruct()');
 self.disable(true);
};

// SMSound (sound object)

function SMSound(oSM,oOptions) {
var self = this;
var sm = oSM;
this.sID = oOptions.id;
this.url = oOptions.url;
this.options = sm._mergeObjects(oOptions);
this.instanceOptions = this.options; // per-play-instance-specific options

this._debug = function() {
 if (sm.debugMode) {
 var stuff = null;
 var msg = [];
 var sF = null;
 var sfBracket = null;
 var maxLength = 64; // # of characters of function code to show before truncating
 for (stuff in self.options) {
   if (self.options[stuff] != null) {
     if (self.options[stuff] instanceof Function) {
	  // handle functions specially
	  sF = self.options[stuff].toString();
	  sF = sF.replace(/\s\s+/g,' '); // normalize spaces
	  sfBracket = sF.indexOf('{');
	  msg[msg.length] = ' '+stuff+': {'+sF.substr(sfBracket+1,(Math.min(Math.max(sF.indexOf('\n')-1,maxLength),maxLength))).replace(/\n/g,'')+'... }';
	} else {
	  msg[msg.length] = ' '+stuff+': '+self.options[stuff];
	};
   };
 };
 sm._writeDebug('SMSound() merged options: {\n'+msg.join(', \n')+'\n}');
 };
};

this._debug();

this.id3 = {
/* 
 Name/value pairs set via Flash when available - see reference for names:
 http://livedocs.macromedia.com/flash/8/main/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_Parts&file=00001567.html
 (eg., this.id3.songname or this.id3['songname'])
*/
};

self.resetProperties = function(bLoaded) {
 self.bytesLoaded = null;
 self.bytesTotal = null;
 self.position = null;
 self.duration = null;
 self.durationEstimate = null;
 self.loaded = false;
 self.loadSuccess = null;
 self.playState = 0;
 self.paused = false;
 self.readyState = 0; // 0 = uninitialised, 1 = loading, 2 = failed/error, 3 = loaded/success
 self.muted = false;
 self.didBeforeFinish = false;
 self.didJustBeforeFinish = false;
 self.instanceOptions = {};
 self.instanceCount = 0;
 self.peakData = {
   left: 0,
   right: 0
 };
 self.waveformData = [];
 self.eqData = [];
};

self.resetProperties();

// --- public methods ---

this.load = function(oOptions) {
 self.instanceOptions = sm._mergeObjects(oOptions);
 if (typeof self.instanceOptions.url == 'undefined') self.instanceOptions.url = self.url;
 sm._writeDebug('soundManager.load(): '+self.instanceOptions.url,1);
 if (self.instanceOptions.url == self.url && self.readyState != 0 && self.readyState != 2) {
   sm._writeDebug('soundManager.load(): current URL already assigned.',1);
   return false;
 }
 self.loaded = false;
 self.loadSuccess = null;
 self.readyState = 1;
 self.playState = (oOptions.autoPlay?1:0); // if autoPlay, assume "playing" is true (no way to detect when it actually starts in Flash unless onPlay is watched?)
 try {
	  if (sm.flashVersion==8) {
	    sm.o._load(self.sID,self.instanceOptions.url,self.instanceOptions.stream,self.instanceOptions.autoPlay,(self.instanceOptions.whileloading?1:0));
	  } else {
     sm.o._load(self.sID,self.instanceOptions.url,self.instanceOptions.stream?true:false,self.instanceOptions.autoPlay?true:false); // ,(thisOptions.whileloading?true:false)
	  };
 } catch(e) {
   sm._writeDebug('SMSound.load(): JS-Flash communication failed.',2);
   sm.onerror();
   sm.disable();
 };
};

this.unload = function() {
 // Flash 8/AS2 can't "close" a stream - fake it by loading an empty MP3
 // Flash 9/AS3: Close stream, preventing further load
 if (self.readyState != 0) {
   sm._writeDebug('SMSound.unload(): "'+self.sID+'"');
   self.setPosition(0); // reset current sound positioning
   sm.o._unload(self.sID,sm.nullURL);
   // reset load/status flags
   self.resetProperties();
 }
};

this.destruct = function() {
 // kill sound within Flash
 sm._writeDebug('SMSound.destruct(): "'+self.sID+'"');
 sm.o._destroySound(self.sID);
 sm.destroySound(self.sID,true); // ensure deletion from controller
}

this.play = function(oOptions) {
 if (!oOptions) oOptions = {};
 self.instanceOptions = sm._mergeObjects(oOptions,self.instanceOptions);
 self.instanceOptions = sm._mergeObjects(self.instanceOptions,self.options);
 if (self.playState == 1) {
   var allowMulti = self.instanceOptions.multiShot;
   if (!allowMulti) {
     sm._writeDebug('SMSound.play(): "'+self.sID+'" already playing (one-shot)',1);
     return false;
   } else {
     sm._writeDebug('SMSound.play(): "'+self.sID+'" already playing (multi-shot)',1);
   };
 };
 if (!self.loaded) {
   if (self.readyState == 0) {
     sm._writeDebug('SMSound.play(): Attempting to load "'+self.sID+'"',1);
     // try to get this sound playing ASAP
     self.instanceOptions.stream = true;
     self.instanceOptions.autoPlay = true;
     // TODO: need to investigate when false, double-playing
     // if (typeof oOptions.autoPlay=='undefined') thisOptions.autoPlay = true; // only set autoPlay if unspecified here
     self.load(self.instanceOptions); // try to get this sound playing ASAP
   } else if (self.readyState == 2) {
     sm._writeDebug('SMSound.play(): Could not load "'+self.sID+'" - exiting',2);
     return false;
   } else {
     sm._writeDebug('SMSound.play(): "'+self.sID+'" is loading - attempting to play..',1);
   };
 } else {
   sm._writeDebug('SMSound.play(): "'+self.sID+'"');
 };
 if (self.paused) {
   self.resume();
 } else {
   self.playState = 1;
   if (!self.instanceCount || sm.flashVersion == 9) self.instanceCount++;
   self.position = (typeof self.instanceOptions.position != 'undefined' && !isNaN(self.instanceOptions.position)?self.instanceOptions.position:0);
   if (self.instanceOptions.onplay) self.instanceOptions.onplay.apply(self);
   self.setVolume(self.instanceOptions.volume);
   self.setPan(self.instanceOptions.pan);
   sm.o._start(self.sID,self.instanceOptions.loop||1,(sm.flashVersion==9?self.position:self.position/1000));
 };
};

this.start = this.play; // just for convenience

this.stop = function(bAll) {
 if (self.playState == 1) {
   self.playState = 0;
   self.paused = false;
   // if (sm.defaultOptions.onstop) sm.defaultOptions.onstop.apply(self);
   if (self.instanceOptions.onstop) self.instanceOptions.onstop.apply(self);
   sm.o._stop(self.sID,bAll);
   self.instanceCount = 0;
   self.instanceOptions = {};
 };
};

this.setPosition = function(nMsecOffset) {
 self.instanceOptions.position = nMsecOffset;
 sm.o._setPosition(self.sID,(sm.flashVersion==9?self.instanceOptions.position:self.instanceOptions.position/1000),(self.paused||!self.playState)); // if paused or not playing, will not resume (by playing)
};

this.pause = function() {
 if (self.paused) return false;
 sm._writeDebug('SMSound.pause()');
 self.paused = true;
 sm.o._pause(self.sID);
 if (self.instanceOptions.onpause) self.instanceOptions.onpause.apply(self);
};

this.resume = function() {
 if (!self.paused) return false;
 sm._writeDebug('SMSound.resume()');
 self.paused = false;
 sm.o._pause(self.sID); // flash method is toggle-based (pause/resume)
 if (self.instanceOptions.onresume) self.instanceOptions.onresume.apply(self);
};

this.togglePause = function() {
 sm._writeDebug('SMSound.togglePause()');
 if (!self.playState) {
   self.play({position:(sm.flashVersion==9?self.position:self.position/1000)});
   return false;
 };
 if (self.paused) {
   self.resume();
 } else {
   self.pause();
 };
};

this.setPan = function(nPan) {
 if (typeof nPan == 'undefined') nPan = 0;
 sm.o._setPan(self.sID,nPan);
 self.instanceOptions.pan = nPan;
};

this.setVolume = function(nVol) {
 if (typeof nVol == 'undefined') nVol = 100;
 sm.o._setVolume(self.sID,(sm.muted&&!self.muted)||self.muted?0:nVol);
 self.instanceOptions.volume = nVol;
};

this.mute = function() {
	self.muted = true;
 sm.o._setVolume(self.sID,0);
};

this.unmute = function() {
	self.muted = false;
 sm.o._setVolume(self.sID,typeof self.instanceOptions.volume != 'undefined'?self.instanceOptions.volume:self.options.volume);
};

// --- "private" methods called by Flash ---

this._whileloading = function(nBytesLoaded,nBytesTotal,nDuration) {
 self.bytesLoaded = nBytesLoaded;
 self.bytesTotal = nBytesTotal;
 self.duration = Math.floor(nDuration);
 self.durationEstimate = parseInt((self.bytesTotal/self.bytesLoaded)*self.duration); // estimate total time (will only be accurate with CBR MP3s.)
 if (self.readyState != 3 && self.instanceOptions.whileloading) self.instanceOptions.whileloading.apply(self);
};

this._onid3 = function(oID3PropNames,oID3Data) {
 // oID3PropNames: string array (names)
 // ID3Data: string array (data)
 sm._writeDebug('SMSound._onid3(): "'+this.sID+'" ID3 data received.');
 var oData = [];
 for (var i=0,j=oID3PropNames.length; i<j; i++) {
   oData[oID3PropNames[i]] = oID3Data[i];
   // sm._writeDebug(oID3PropNames[i]+': '+oID3Data[i]);
 };
 self.id3 = sm._mergeObjects(self.id3,oData);
 if (self.instanceOptions.onid3) self.instanceOptions.onid3.apply(self);
};

this._whileplaying = function(nPosition,oPeakData,oWaveformData,oEQData) {
 if (isNaN(nPosition) || nPosition == null) return false; // Flash may return NaN at times
 self.position = nPosition;
	if (self.instanceOptions.usePeakData && typeof oPeakData != 'undefined' && oPeakData) {
	  self.peakData = {
	   left: oPeakData.leftPeak,
	   right: oPeakData.rightPeak
	  };
	};
	if (self.instanceOptions.useWaveformData && typeof oWaveformData != 'undefined' && oWaveformData) {
	  self.waveformData = oWaveformData;
	  /*
	  self.spectrumData = {
	   left: oSpectrumData.left.split(','),
	   right: oSpectrumData.right.split(',')
	  }
	  */
	};
	if (self.instanceOptions.useEQData && typeof oEQData != 'undefined' && oEQData) {
	  self.eqData = oEQData;
	};
 if (self.playState == 1) {
   if (self.instanceOptions.whileplaying) self.instanceOptions.whileplaying.apply(self); // flash may call after actual finish
   if (self.loaded && self.instanceOptions.onbeforefinish && self.instanceOptions.onbeforefinishtime && !self.didBeforeFinish && self.duration-self.position <= self.instanceOptions.onbeforefinishtime) {
     sm._writeDebug('duration-position &lt;= onbeforefinishtime: '+self.duration+' - '+self.position+' &lt= '+self.instanceOptions.onbeforefinishtime+' ('+(self.duration-self.position)+')');
     self._onbeforefinish();
   };
 };
};

this._onload = function(bSuccess) {
 bSuccess = (bSuccess==1?true:false);
 sm._writeDebug('SMSound._onload(): "'+self.sID+'"'+(bSuccess?' loaded.':' failed to load? - '+self.url));
 if (!bSuccess) {
   if (sm.sandbox.noRemote == true) {
     sm._writeDebug('SMSound._onload(): Reminder: Flash security is denying network/internet access',1);
   };
   if (sm.sandbox.noLocal == true) {
     sm._writeDebug('SMSound._onload(): Reminder: Flash security is denying local access',1);
   };
 };
 self.loaded = bSuccess;
 self.loadSuccess = bSuccess;
 self.readyState = bSuccess?3:2;
 if (self.instanceOptions.onload) {
   self.instanceOptions.onload.apply(self);
 };
};

this._onbeforefinish = function() {
 if (!self.didBeforeFinish) {
   self.didBeforeFinish = true;
   if (self.instanceOptions.onbeforefinish) {
     sm._writeDebug('SMSound._onbeforefinish(): "'+self.sID+'"');
     self.instanceOptions.onbeforefinish.apply(self);
   }
 };
};

this._onjustbeforefinish = function(msOffset) {
 // msOffset: "end of sound" delay actual value (eg. 200 msec, value at event fire time was 187)
 if (!self.didJustBeforeFinish) {
   self.didJustBeforeFinish = true;
   if (self.instanceOptions.onjustbeforefinish) {
     sm._writeDebug('SMSound._onjustbeforefinish(): "'+self.sID+'"');
     self.instanceOptions.onjustbeforefinish.apply(self);
   }
 };
};

this._onfinish = function() {
 // sound has finished playing
 sm._writeDebug('SMSound._onfinish(): "'+self.sID+'"');
 self.playState = 0;
 self.paused = false;
 if (self.instanceOptions.onfinish) self.instanceOptions.onfinish.apply(self);
 if (self.instanceOptions.onbeforefinishcomplete) self.instanceOptions.onbeforefinishcomplete.apply(self);
 // reset some state items
 // self.setPosition(0);
 self.didBeforeFinish = false;
 self.didJustBeforeFinish = false;
 if (self.instanceCount) {
   self.instanceCount--;
   if (!self.instanceCount) {
     // reset instance options
     self.instanceCount = 0;
     self.instanceOptions = {};
   }
 }
};

}; // SMSound()

// set up default options
if (this.flashVersion == 9) {
 this.defaultOptions = this._mergeObjects(this.defaultOptions,this.flash9Options);
}

// register a few event handlers
if (window.addEventListener) {
 window.addEventListener('focus',self.handleFocus,false);
 window.addEventListener('load',self.beginDelayedInit,false);
 window.addEventListener('beforeunload',self.destruct,false);
 if (self._tryInitOnFocus) window.addEventListener('mousemove',self.handleFocus,false); // massive Safari focus hack
} else if (window.attachEvent) {
 window.attachEvent('onfocus',self.handleFocus);
 window.attachEvent('onload',self.beginDelayedInit);
 window.attachEvent('beforeunload',self.destruct);
} else {
 // no add/attachevent support - safe to assume no JS -> Flash either.
 soundManager.onerror();
 soundManager.disable();
};

if (document.addEventListener) document.addEventListener('DOMContentLoaded',self.domContentLoaded,false);

var SM2_COPYRIGHT = [
 'SoundManager 2: Javascript Sound for the Web',
 'http://schillmania.com/projects/soundmanager2/',
 'Copyright (c) 2008, Scott Schiller. All rights reserved.',
 'Code provided under the BSD License: http://schillmania.com/projects/soundmanager2/license.txt',
];

}; // SoundManager()

var soundManager = new SoundManager();
