/**
* SWFAddress 2.0: Deep linking for Flash and Ajax - http://www.asual.com/swfaddress/
*
* SWFAddress is (c) 2006-2007 Rostislav Hristov and is released under the MIT License:
* http://www.opensource.org/licenses/mit-license.php
*
*/
if (typeof com == "undefined") var com = {};
if (typeof com.asual == "undefined") com.asual = {};
if (typeof com.asual.util == "undefined") com.asual.util = {};
/**
* @class Utility class that provides detailed browser information.
* @static
* @ignore
*/
com.asual.util.Browser = new function() {
var _supported = false;
var _version = -1;
var _agent = navigator.userAgent;
var _ie = false;
var _camino = false;
var _safari = false;
var _opera = false;
var _mozilla = false;
if (/MSIE/.test(_agent)) {
_ie = true;
_version = parseFloat(_agent.substring(_agent.indexOf('MSIE') + 4));
_supported = _version >= 6;
} else if (/AppleWebKit/.test(_agent)) {
_safari = true;
_version = parseFloat(_agent.substring(_agent.indexOf('Safari') + 7));
_supported = _version >= 312;
} else if (/Opera/.test(_agent)) {
_opera = true;
_version = parseFloat(navigator.appVersion);
_supported = _version >= 9.02;
} else if (/Camino/.test(_agent)) {
_camino = true;
_version = parseFloat(_agent.substring(_agent.indexOf('Camino') + 7));
_supported = _version >= 1;
} else if (/Firefox/.test(_agent)) {
_mozilla = true;
_version = parseFloat(_agent.substring(_agent.indexOf('Firefox') + 8));
_supported = _version >= 1;
} else if (/Netscape/.test(_agent)) {
_mozilla = true;
_version = parseFloat(_agent.substring(_agent.indexOf('Netscape') + 9));
_supported = _version >= 8;
} else if (/Mozilla/.test(_agent) && /rv:/.test(_agent)) {
_mozilla = true;
_version = parseFloat(_agent.substring(_agent.indexOf('rv:') + 3));
_supported = _version >= 1.8;
}
/**
* Detects if the browser is supported.
* @return {Boolean}
* @static
*/
this.isSupported = function() {
return _supported;
}
/**
* Detects the version of the browser.
* @return {Number}
* @static
*/
this.getVersion = function() {
return _version;
}
/**
* Detects if the browser is Internet Explorer.
* @return {Boolean}
* @static
*/
this.isIE = function() {
return _ie;
}
/**
* Detects if the browser is Safari.
* @return {Boolean}
* @static
*/
this.isSafari = function() {
return _safari;
}
/**
* Detects if the browser is Opera.
* @return {Boolean}
* @static
*/
this.isOpera = function() {
return _opera;
}
/**
* Detects if the browser is Camino.
* @return {Boolean}
* @static
*/
this.isCamino = function() {
return _camino;
}
/**
* Detects if the browser is Mozilla.
* @return {Boolean}
* @static
*/
this.isMozilla = function() {
return _mozilla;
}
}
/**
* @class Utility class that provides event helpers.
* @static
* @ignore
*/
com.asual.util.Events = new function() {
var _cache = [];
var _browser = com.asual.util.Browser;
var _dcl = 'DOMContentLoaded';
if (_browser.isIE() || _browser.isSafari()) {
(function(){
try {
if (_browser.isIE() || !/loaded|complete/.test(document.readyState))
document.documentElement.doScroll('left');
} catch(e) {
return setTimeout(arguments.callee, 0);
}
for (var i = 0, e; e = _cache[i]; i++) {
if (e.t == _dcl) e.l.call(null);
}
})();
}
/**
* Adds an event listener to an object.
* @param {Object} obj The object that provides events.
* @param {String} type The type of the event.
* @param {Function} listener The event listener function.
* @return {void}
* @static
*/
this.addListener = function(obj, type, listener) {
_cache.push({o: obj, t: type, l: listener});
if (type == _dcl && (_browser.isIE() || _browser.isSafari()))
return;
if (obj.addEventListener){
obj.addEventListener(type, listener, false);
} else if (obj.attachEvent){
obj.attachEvent('on' + type, listener);
}
}
/**
* Removes an event listener from an object.
* @param {Object} obj The object that provides events.
* @param {String} type The type of the event.
* @param {Function} listener The event listener function.
* @return {void}
* @static
*/
this.removeListener = function(obj, type, listener) {
for (var i = 0, e; e = _cache[i]; i++) {
if (e.o == obj && e.t == type && e.l == listener) {
_cache.splice(i, 1);
break;
}
}
if (type == _dcl && (_browser.isIE() || _browser.isSafari()))
return;
if (obj.removeEventListener){
obj.removeEventListener(type, listener, false);
} else if (obj.detachEvent){
obj.detachEvent('on' + type, listener);
}
}
var _unload = function() {
for (var i = 0, evt; evt = _cache[i]; i++) {
if (evt.t != _dcl)
com.asual.util.Events.removeListener(evt.o, evt.t, evt.l);
}
}
this.addListener(window, 'unload', _unload);
}
/**
* Creates a new SWFAddress event.
* @class Event class for SWFAddress.
* @param {String} type Type of the event.
*/
SWFAddressEvent = function(type) {
/**
* String representation of this object.
* @ignore
*/
this.toString = function() {
return '[object SWFAddressEvent]';
}
/**
* The type of this event.
* @type String
*/
this.type = type;
/**
* The target of this event.
* @type Function
*/
this.target = [SWFAddress][0];
/**
* The value of this event.
* @type String
*/
this.value = SWFAddress.getValue();
/**
* The path of this event.
* @type String
*/
this.path = SWFAddress.getPath();
/**
* The parameters of this event.
* @type Object
*/
this.parameters = {};
var _names = SWFAddress.getParameterNames();
for (var i = 0, n; n = _names[i]; i++) {
this.parameters[n] = SWFAddress.getParameter(n);
}
}
/**
* Init event.
* @type String
* @memberOf SWFAddressEvent
* @static
*/
SWFAddressEvent.INIT = 'init';
/**
* Change event.
* @type String
* @memberOf SWFAddressEvent
* @static
*/
SWFAddressEvent.CHANGE = 'change';
/**
* @class The SWFAddress class can be configured with query parameters using the following format:
* swfaddress.js?html=false&history=1&tracker=pageTracker._trackPageview&strict=1.
* The list of supported options include:
* history:Boolean
- Enables or disables the creation of history entries.
* html:Boolean
- Enables or disables the usage of swfaddress.html.
* strict:Boolean
- Enables or disables the strict mode.
* tracker:String
- Sets a function for page view tracking.
* @static
*/
SWFAddress = new function() {
var _browser = com.asual.util.Browser;
var _supported = _browser.isSupported();
var _d = top.document;
var _h = top.history;
var _l = top.location;
var _iframe, _form, _url, _js = 'swfaddress.js';
var _title = _d.title;
var _length = _h.length;
var _silent = false;
var _listeners = {};
var _stack = [];
var _ids = [];
var _opts = [];
_opts['history'] = true;
_opts['html'] = false;
_opts['strict'] = true;
_opts['tracker'] = 'urchinTracker';
if ((!_supported && _l.href.indexOf('#') != -1) ||
(_browser.isSafari() && _browser.getVersion() < 412 && _l.href.indexOf('#') != -1 && _l.search != '')){
_d.open();
_d.write('
');
_d.close();
}
var _getHash = function() {
var index = _l.href.indexOf('#');
if (index != -1) {
return _l.href.substring(index).replace(/^#/g, '');
}
return '';
}
var _value = _getHash();
var _strictCheck = function(value, force) {
if (_opts['strict']) {
if (force) {
if (value.substr(0, 1) != '/') value = '/' + value;
value = value.replace(/^([^\?.]*[^\/])(\?|$)/, '$1/$2').replace(/\/\//, '/');
} else {
if (value == '') value = '/';
}
}
return value;
}
var _titleCheck = function() {
if (_browser.isIE() && _d.title != _title) {
SWFAddress.setTitle(_title);
}
}
var _listen = function() {
if (!_silent) {
if (_browser.isIE()) {
if (_value != _getHash()) {
if (_browser.getVersion() < 7) {
_l.reload();
} else {
SWFAddress.setValue(_getHash());
}
}
} else if (_browser.isSafari()) {
if (_length != _h.length) {
_length = _h.length;
if (typeof _stack[_length - 1] != 'undefined') {
_value = _stack[_length - 1];
}
_update();
}
} else if (_value != _getHash()) {
_value = _getHash();
_update();
}
_titleCheck();
}
}
var _jsDispatch = function(type) {
if (SWFAddress.hasEventListener(type)) {
SWFAddress.dispatchEvent(new SWFAddressEvent(type));
}
type = type.substr(0, 1).toUpperCase() + type.substring(1);
if(typeof SWFAddress['on' + type] == 'function') {
SWFAddress['on' + type]();
}
}
var _jsInit = function() {
_jsDispatch('init');
}
var _jsChange = function() {
_jsDispatch('change');
}
var _swfChange = function() {
for (var i = 0, id; id = _ids[i]; i++) {
var obj = document.getElementById(id);
if (obj) {
if (obj.parentNode && typeof obj.parentNode.so != 'undefined') {
obj.parentNode.so.call('setSWFAddressValue', SWFAddress.getValue());
} else {
obj = (obj && typeof obj.setSWFAddressValue != 'undefined') ?
obj : ((obj.getElementsByTagName('object')[0] &&
typeof obj.getElementsByTagName('object')[0].setSWFAddressValue != 'undefined') ?
obj.getElementsByTagName('object')[0] : ((obj.getElementsByTagName('embed')[0] &&
typeof obj.getElementsByTagName('embed')[0].setSWFAddressValue != 'undefined') ?
obj.getElementsByTagName('embed')[0] : null));
if (obj) {
obj.setSWFAddressValue(SWFAddress.getValue());
}
}
}
}
}
var _update = function() {
_swfChange();
_jsChange();
}
var _track = function() {
if (typeof _opts['tracker'] != 'undefined' && eval('typeof ' + _opts['tracker'] + ' != "undefined"')){
var fn = eval(_opts['tracker']);
if (typeof fn == 'function') {
fn((_l.pathname + SWFAddress.getValue()).replace(/\/\//, '/').replace(/^\/$/, ''));
}
}
}
var _htmlWrite = function() {
var doc = _iframe.contentWindow.document;
doc.open();
doc.write('');
doc.close();
}
var _htmlLoad = function() {
if (_opts['html']) {
var src = _iframe.contentWindow.location.href;
_value = (src.indexOf('?') > -1) ?
src.substring(src.indexOf('?') + 1) : '';
} else {
_value = (typeof _iframe.contentWindow.swfaddress != 'undefined') ?
_iframe.contentWindow.swfaddress : '';
}
if (_value != _getHash()) {
_update();
_l.hash = _value;
}
}
var _load = function() {
var attr = 'id="swfaddress" style="position:absolute;top:-9999px;"';
if (_browser.isIE()) {
document.body.appendChild(document.createElement('div')).innerHTML = '';
_iframe = document.getElementById('swfaddress');
setTimeout(function() {
if (!_opts['html'] && typeof _iframe.contentWindow.swfaddress == 'undefined') _htmlWrite();
com.asual.util.Events.addListener(_iframe, 'load', _htmlLoad);
}, 10);
} else if (_browser.isSafari()) {
if (_browser.getVersion() < 412) {
document.body.innerHTML += '';
_form = document.getElementById('swfaddress');
}
if (typeof _l.swfaddress == 'undefined') _l.swfaddress = {};
if (typeof _l.swfaddress[_l.pathname] != 'undefined') _stack = _l.swfaddress[_l.pathname].split(',');
} else if (_browser.isOpera() && _ids.length == 0) {
document.body.innerHTML += '';
}
setTimeout(_jsInit, 1);
setTimeout(_jsChange, 2);
setTimeout(_track, 10);
setInterval(_listen, 50);
}
/**
* Init event.
* @type Function
* @event
* @static
*/
this.onInit = null;
/**
* Change event.
* @type Function
* @event
* @static
*/
this.onChange = null;
/**
* String representation of this class.
* @ignore
*/
this.toString = function() {
return '[class SWFAddress]';
}
/**
* Loads the previous URL in the history list.
* @return {void}
* @static
*/
this.back = function() {
_h.back();
}
/**
* Loads the next URL in the history list.
* @return {void}
* @static
*/
this.forward = function() {
_h.forward();
}
/**
* Loads a URL from the history list.
* @param {Number} delta An integer representing a relative position in the history list.
* @return {void}
* @static
*/
this.go = function(delta) {
_h.go(delta);
}
/**
* Opens a new URL in the browser.
* @param {String} url The resource to be opened.
* @param {String} target Target window.
* @return {void}
* @static
*/
this.href = function(url, target) {
target = typeof target != 'undefined' ? target : '_self';
switch(target) {
case '_self':
self.location.href = url;
break;
case '_top':
_l.href = url;
break;
case '_blank':
window.open(url);
break;
default:
top.frames[target].location.href = url;
break;
}
}
/**
* Opens a browser popup window.
* @param {String} url Resource location.
* @param {String} name Name of the popup window.
* @param {String} options Options which get evaluted and passed to the window.open() method.
* @param {String} handler Optional JavaScript code for popup handling.
* @return {void}
* @static
*/
this.popup = function(url, name, options, handler) {
var popup = window.open(url, name, eval(options));
eval(handler);
}
/**
* Registers an event listener..
* @param {String} type Event type.
* @param {Function} listener Event listener.
* @return {void}
* @static
*/
this.addEventListener = function (type, listener) {
if (typeof _listeners[type] == 'undefined') {
_listeners[type] = [];
}
_listeners[type].push(listener);
}
/**
* Removes an event listener.
* @param {String} type Event type.
* @param {Function} listener Event listener.
* @return {void}
* @static
*/
this.removeEventListener = function (type, listener) {
if (typeof _listeners[type] != 'undefined') {
for (var i = 0, l; l = _listeners[type][i]; i++) {
if (l == listener) break;
}
_listeners[type].splice(i, 1);
}
}
/**
* Dispatches an event to all the registered listeners.
* @param {Object} event Event object.
* @return {Boolean}
* @static
*/
this.dispatchEvent = function (event) {
if (typeof _listeners[event.type] != 'undefined' && _listeners[event.type].length) {
event.target = this;
for (var i = 0, l; l = _listeners[event.type][i]; i++) {
l(event);
}
return true;
}
return false;
}
/**
* Checks the existance of any listeners registered for a specific type of event.
* @param {String} event Event type.
* @return {Boolean}
* @static
*/
this.hasEventListener = function (type) {
return (typeof _listeners[type] != 'undefined' && _listeners[type].length > 0);
}
/**
* Provides the state of the strict mode setting.
* @return {Boolean}
* @static
*/
this.getStrict = function() {
return _opts['strict'];
}
/**
* Enables or disables the strict mode.
* @param {Boolean} strict Strict mode state.
* @return {void}
* @static
*/
this.setStrict = function(strict) {
_opts['strict'] = enabled;
}
/**
* Provides the state of the history setting.
* @return {Boolean}
* @static
*/
this.getHistory = function() {
return _opts['history'];
}
/**
* Enables or disables the creation of history entries.
* @param {Boolean} history History state.
* @return {void}
* @static
*/
this.setHistory = function(history) {
_opts['history'] = history;
}
/**
* Provides the tracker function.
* @return {String}
* @static
*/
this.getTracker = function() {
return _opts['tracker'];
}
/**
* Sets a function for page view tracking. The default value is 'urchinTracker'.
* @param {String} tracker Tracker function.
* @return {void}
* @static
*/
this.setTracker = function(tracker) {
_opts['tracker'] = tracker;
}
/**
* Provides a list of all the Flash objects registered.
* @return {Array}
* @static
*/
this.getIds = function() {
return _ids;
}
/**
* Provides the id the first and probably the only Flash object registered.
* @return {String}
* @static
*/
this.getId = function(index) {
return _ids[0];
}
/**
* Sets the id of a single Flash object which will be registered for deep linking.
* @param {String} id ID of the object.
* @return {void}
* @static
*/
this.setId = function(id) {
_ids[0] = id;
}
/**
* Adds an id to the list of Flash object registered for deep linking.
* @param {String} id ID of the object.
* @return {void}
* @static
*/
this.addId = function(id) {
this.removeId(id);
_ids.push(id);
}
/**
* Removes an id from the list of Flash object registered for deep linking.
* @param {String} id ID of the object.
* @return {void}
* @static
*/
this.removeId = function(id) {
for (var i = 0, swfid; swfid = _ids[i]; i++) {
if (id == swfid) {
_ids.splice(i, 1);
break;
}
}
}
/**
* Provides the title of the HTML document.
* @return {String}
* @static
*/
this.getTitle = function() {
return _d.title;
}
/**
* Sets the title of the HTML document.
* @param {String} title Title value.
* @return {void}
* @static
*/
this.setTitle = function(title) {
if (!_supported) return null;
if (typeof title == 'undefined') return;
if (title == 'null') title = '';
_title = _d.title = title;
if (_iframe && _iframe.contentWindow)
_iframe.contentWindow.document.title = title;
}
/**
* Provides the status of the browser window.
* @return {String}
* @static
*/
this.getStatus = function() {
return top.status;
}
/**
* Sets the status of the browser window.
* @param {String} status Status value.
* @return {void}
* @static
*/
this.setStatus = function(status) {
if (!_supported) return null;
if (typeof status == 'undefined') return;
if (!_browser.isSafari()) {
if (status == 'null') status = '';
status = _strictCheck(status, true);
if (status == '/') status = '';
if (!(/http(s)?:\/\//.test(status))) {
var index = _l.href.indexOf('#');
status = (index == -1 ? _l.href : _l.href.substr(0, index)) + '#' + status;
}
top.status = status;
}
}
/**
* Resets the status of the browser window.
* @return {void}
* @static
*/
this.resetStatus = function() {
top.status = '';
}
/**
* Provides the current deep linking value.
* @return {String}
* @static
*/
this.getValue = function() {
if (!_supported) return null;
return _strictCheck(_value, false);
}
/**
* Sets the current deep linking value.
* @param {String} value A value which will be appended to the base link of the HTML document.
* @return {void}
* @static
*/
this.setValue = function(value) {
if (!_supported) return null;
if (typeof value == 'undefined') return;
if (value == 'null') value = ''
value = _strictCheck(value, true);
if (value == '/') value = '';
if (_value == value) return;
_value = value;
_silent = true;
_update();
_stack[_h.length] = _value;
if (_browser.isSafari()) {
if (_opts['history']) {
_l.swfaddress[_l.pathname] = _stack.toString();
_length = _h.length + 1;
if (_browser.getVersion() < 412) {
if (_l.search == '') {
_form.action = '#' + _value;
_form.submit();
}
} else {
var evt = document.createEvent('MouseEvents');
evt.initEvent('click', true, true);
var anchor = document.createElement('a');
anchor.href = '#' + _value;
anchor.dispatchEvent(evt);
}
} else {
_l.replace('#' + _value);
}
} else if (_value != _getHash()) {
if (_opts['history']) {
_l.hash = '#' + _value;
} else {
_l.replace('#' + _value);
}
}
if (_browser.isIE() && _opts['history']) {
if (_opts['html']) {
_iframe.contentWindow.location.assign(_iframe.contentWindow.location.pathname +
'?' + _getHash());
} else {
_htmlWrite();
}
}
setTimeout(_track, 10);
_silent = false;
}
/**
* Provides the deep linking value without the query string.
* @return {String}
* @static
*/
this.getPath = function() {
var value = this.getValue();
if (value.indexOf('?') != -1) {
return value.split('?')[0];
} else {
return value;
}
}
/**
* Provides the query string part of the deep linking value.
* @return {String}
* @static
*/
this.getQueryString = function() {
var value = this.getValue();
var index = value.indexOf('?');
if (index != -1 && index < value.length) {
return value.substr(index + 1);
}
return '';
}
/**
* Provides the value of a specific query parameter.
* @param {String} param Parameter name.
* @return {String}
* @static
*/
this.getParameter = function(param) {
var value = this.getValue();
var index = value.indexOf('?');
if (index != -1) {
value = value.substr(index + 1);
var params = value.split('&');
var p, i = params.length;
while(i--) {
p = params[i].split('=');
if (p[0] == param) {
return p[1];
}
}
}
return '';
}
/**
* Provides a list of all the query parameter names.
* @return {Array}
* @static
*/
this.getParameterNames = function() {
var value = this.getValue();
var index = value.indexOf('?');
var names = [];
if (index != -1) {
value = value.substr(index + 1);
if (value != '' && value.indexOf('=') != -1) {
var params = value.split('&');
var i = 0;
while(i < params.length) {
names.push(params[i].split('=')[0]);
i++;
}
}
}
return names;
}
if (!_supported) return;
for (var i = 1; i < _length; i++) {
_stack.push('');
}
_stack.push(_l.hash.replace(/^#/g, ''));
if (_browser.isIE() && _l.hash != _getHash()) {
_l.hash = '#' + _getHash();
}
var scripts = document.getElementsByTagName('script');
for (var i = 0, s; s = scripts[i]; i++) {
if (s.src.indexOf(_js) > -1) {
_url = String(s.src);
break;
}
}
if ((qi = _url.indexOf('?')) > -1) {
var param, params = _url.substr(qi + 1).split('&');
for (var j = 0, p; p = params[j]; j++) {
param = p.split('=');
if (/^(history|html|strict)$/.test(param[0])) {
_opts[param[0]] = (isNaN(param[1]) ? eval(param[1]) : (parseFloat(param[1]) > 0));
}
if (/^tracker$/.test(param[0])) {
_opts[param[0]] = param[1];
}
}
}
if (/file:\/\//.test(_l.href)) _opts['html'] = false;
_titleCheck();
com.asual.util.Events.addListener(document, 'DOMContentLoaded', _load);
}
/* Flash embedding hooks */
if (typeof swfobject != 'undefined') SWFObject = swfobject;
if (typeof FlashObject != 'undefined') SWFObject = FlashObject;
if (typeof SWFObject != 'undefined') {
if (SWFObject.prototype && SWFObject.prototype.write) {
com.asual.SWFObjectWrite = SWFObject.prototype.write;
/**
* @ignore
*/
SWFObject.prototype.write = function() {
if (this.getAttribute('version').major < 8) {
this.addVariable('$swfaddress', SWFAddress.getValue());
((typeof arguments[0] == 'string') ?
document.getElementById(arguments[0]) : arguments[0]).so = this;
}
if (success = com.asual.SWFObjectWrite.apply(this, arguments))
SWFAddress.addId(this.getAttribute('id'));
return success;
}
} else {
com.asual.SWFObjectRegisterObject = SWFObject.registerObject;
SWFObject.registerObject = function() {
com.asual.SWFObjectRegisterObject.apply(this, arguments);
SWFAddress.addId(arguments[0]);
}
com.asual.SWFObjectCreateSWF = SWFObject.createSWF;
SWFObject.createSWF = function() {
com.asual.SWFObjectCreateSWF.apply(this, arguments);
SWFAddress.addId(arguments[0].id);
}
com.asual.SWFObjectEmbedSWF = SWFObject.embedSWF;
SWFObject.embedSWF = function() {
com.asual.SWFObjectEmbedSWF.apply(this, arguments);
SWFAddress.addId(arguments[8].id);
}
}
}
if (typeof UFO != 'undefined') {
com.asual.UFOCreate = UFO.create;
UFO.create = function() {
com.asual.UFOCreate.apply(this, arguments);
SWFAddress.addId(arguments[0].id);
}
}
if (typeof AC_FL_RunContent != 'undefined') {
com.asual.AC_FL_RunContent = AC_FL_RunContent;
AC_FL_RunContent = function() {
com.asual.AC_FL_RunContent.apply(this, arguments);
for (var i = 0, a; a = arguments[i]; i++) {
if (a == 'id') {
SWFAddress.addId(arguments[i+1]);
break;
}
}
}
}