/** * SWFAddress 2.1: 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 _version = -1; var _agent = navigator.userAgent; var _ie = false, _camino = false, _safari = false, _opera = false, _firefox = false, _netscape = false, _mozilla = false; var _getVersion = function(s, i) { return parseFloat(_agent.substr(_agent.indexOf(s) + i)); } if (_ie = /MSIE/.test(_agent)) _version = _getVersion('MSIE', 4); if (_safari = /AppleWebKit/.test(_agent)) _version = _getVersion('Safari', 7); if (_opera = /Opera/.test(_agent)) _version = parseFloat(navigator.appVersion); if (_camino = /Camino/.test(_agent)) _version = _getVersion('Camino', 7); if (_firefox = /Firefox/.test(_agent)) _version = _getVersion('Firefox', 8); if (_netscape = /Netscape/.test(_agent)) _version = _getVersion('Netscape', 9); if (_mozilla = /Mozilla/.test(_agent) && /rv:/.test(_agent)) _version = _getVersion('rv:', 3); /** * 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 Firefox. * @return {Boolean} * @static */ this.isFirefox = function() { return _firefox; } /** * Detects if the browser is Netscape. * @return {Boolean} * @static */ this.isNetscape = function() { return _netscape; } /** * 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()))) { 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()))) { 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 folders in the deep linking path of this event. * @type Array */ this.pathNames = SWFAddress.getPathNames(); /** * The parameters of this event. * @type Object */ this.parameters = {}; var _parametersNames = SWFAddress.getParameterNames(); for (var i = 0, l = _parametersNames.length; i < l; i++) this.parameters[_parametersNames[i]] = SWFAddress.getParameter(_parametersNames[i]); /** * The parameters names of this event. * @type Array */ this.parametersNames = _parametersNames; } /** * 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 _events = com.asual.util.Events; var _version = _browser.getVersion(); var _supported = false; if (_browser.isIE()) _supported = _version >= 6; if (_browser.isSafari()) _supported = _version >= 312; if (_browser.isOpera()) _supported = _version >= 9.02; if (_browser.isCamino()) _supported = _version >= 1; if (_browser.isFirefox()) _supported = _version >= 1; if (_browser.isNetscape()) _supported = _version >= 8; if (_browser.isMozilla()) _supported = _version >= 1.8; var _t = top; var _d = _t.document; var _h = _t.history; var _l = _t.location; var _st = setTimeout; var FUNCTION = 'function'; var UNDEFINED = 'undefined'; var SWFADDRESS = 'swfaddress'; var _iframe, _form, _url; 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 = '_trackDefault'; if ((!_supported && _l.href.indexOf('#') != -1) || (_browser.isSafari() && _version < 412 && _l.href.indexOf('#') != -1 && _l.search != '')){ _d.open(); _d.write(''); _d.close(); } var _getHash = function() { var index = _l.href.indexOf('#'); if (index != -1) { var hash = unescape(_l.href.substr(index + 1)); if (/^[a-z0-9 ,=_&\-\.\?\+\/]*$/i.test(hash)) return hash; else _l.replace(_l.href.substr(0, index)); } return ''; } var _value = _getHash(); var _strictCheck = function(value, force) { if (_opts.strict) value = force ? (value.substr(0, 1) != '/' ? '/' + value : value) : (value == '' ? '/' : value); return value; } var _ieLocal = function(value) { return (_browser.isIE() && _l.protocol == 'file:') ? _value.replace(/\?/, '%3F') : value; } var _searchScript = function(el) { if (el.src && /swfaddress\.js(\?.*)?$/.test(el.src)) return el; for (var i = 0, l = el.childNodes.length, s; i < l; i++) if (s = _searchScript(el.childNodes[i])) return s; } var _titleCheck = function() { if (_browser.isIE() && _d.title != _title) SWFAddress.setTitle(_title); } var _listen = function() { if (!_silent) { var hash = _getHash(); if (_browser.isIE()) { if (_value != hash) { if (_version < 7) _l.reload(); else SWFAddress.setValue(hash); } } else if (_browser.isSafari() && _version < 523) { if (_length != _h.length) { _length = _h.length; if (typeof _stack[_length - 1] != UNDEFINED) _value = _stack[_length - 1]; _update(); } } else if (_value != hash) { _value = hash; _update(); } _titleCheck(); } } var _jsDispatch = function(type) { if (SWFAddress.hasEventListener(type)) SWFAddress.dispatchEvent(new SWFAddressEvent(type)); type = type.substr(0, 1).toUpperCase() + type.substr(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, value = SWFAddress.getValue(), setter = 'setSWFAddressValue'; id = _ids[i]; i++) { var obj = document.getElementById(id); if (obj) { if (obj.parentNode && typeof obj.parentNode.so != UNDEFINED) { obj.parentNode.so.call(setter, value); } else { if (!(obj && typeof obj[setter] != UNDEFINED)) { var objects = obj.getElementsByTagName('object'); var embeds = obj.getElementsByTagName('embed'); obj = ((objects[0] && typeof objects[0][setter] != UNDEFINED) ? objects : ((embeds[0] && typeof embeds[0][setter] != UNDEFINED) ? embeds[0] : null)); } if (obj) obj[setter](value); } } else if (obj = document[id]) { if (typeof obj[setter] != UNDEFINED) obj[setter](value); } } } var _update = function() { _swfChange(); _jsChange(); _st(_track, 10); } var _trackDefault = function(value) { if (typeof urchinTracker == FUNCTION) urchinTracker(value); /** * if (typeof tracker1 != UNDEFINED && typeof tracker1._trackPageview == FUNCTION) * tracker1._trackPageview(value); * if (typeof tracker2 != UNDEFINED && typeof tracker2._trackPageview == FUNCTION) * tracker2._trackPageview(value); * if (typeof pageTracker != UNDEFINED && typeof pageTracker._trackPageview == FUNCTION) * pageTracker._trackPageview(value); */ } eval('var _trackDefault = ' + _trackDefault + ';'); 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 + (/\/$/.test(_l.pathname) ? '' : '/') + SWFAddress.getValue()).replace(/\/\//, '/').replace(/^\/$/, '')); } } var _htmlWrite = function() { var doc = _iframe.contentWindow.document; doc.open(); doc.write(''); doc.close(); } var _htmlLoad = function() { var win = _iframe.contentWindow; if (_opts.html) { var src = win.location.href; _value = (src.indexOf('?') > -1) ? src.substr(src.indexOf('?') + 1) : ''; } else { _value = (typeof win[SWFADDRESS] != UNDEFINED) ? win[SWFADDRESS] : ''; } win.document.title = _d.title; if (_value != _getHash()) { _update(); _l.hash = _ieLocal(_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); _st(function() { _events.addListener(_iframe, 'load', _htmlLoad); if (!_opts.html && typeof _iframe.contentWindow[SWFADDRESS] == UNDEFINED) _htmlWrite(); }, 10); } else if (_browser.isSafari()) { if (_version < 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 && typeof navigator.plugins['Shockwave Flash'] == 'object') { document.body.innerHTML += ''; } _st(_jsInit, 1); _st(_jsChange, 2); _st(_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'; if (target == '_self') self.location.href = url; if (target == '_top') _l.href = url; if (target == '_blank') window.open(url); else _t.frames[target].location.href = url; } /** * 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, 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 base address of the document. * @return {String} * @static */ this.getBaseURL = function() { var url = _l.href; if (url.indexOf('#') != -1) url = url.substr(0, url.indexOf('#')); if (url.substr(url.length - 1) == '/') url = url.substr(0, url.length - 1); return url; } /** * 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 = strict; } /** * 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; i < _ids.length; i++) { if (id == _ids[i]) { _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; _st(function() { if (_iframe && _iframe.contentWindow && _iframe.contentWindow.document && _iframe.contentWindow.title) _iframe.contentWindow.document.title = _title; }, 1000); } /** * Provides the status of the browser window. * @return {String} * @static */ this.getStatus = function() { return _t.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()) { status = _strictCheck((status != 'null') ? 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; } _t.status = status; } } /** * Resets the status of the browser window. * @return {void} * @static */ this.resetStatus = function() { _t.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 (_version < 412) { if (_l.search == '') { _form.action = '#' + _value; _form.submit(); } } else if (_version < 523){ var evt = document.createEvent('MouseEvents'); evt.initEvent('click', true, true); var anchor = document.createElement('a'); anchor.href = '#' + _value; anchor.dispatchEvent(evt); } else { _l.hash = '#' + _value; } } else { _l.replace('#' + _value); } } else if (_value != _getHash()) { if (_opts.history) _l.hash = '#' + _ieLocal(_value); else _l.replace('#' + _value); } if (_browser.isIE() && _opts.history) { if (_opts.html) { var loc = _iframe.contentWindow.location; loc.assign(loc.pathname + '?' + _getHash()); } else { _htmlWrite(); } } if (_browser.isSafari()) _st(function(){ _silent = false; }, 1); else _silent = false; } /** * Provides the deep linking value without the query string. * @return {String} * @static */ this.getPath = function() { var value = this.getValue(); return (value.indexOf('?') != -1) ? value.split('?')[0] : value; } /** * Provides a list of all the folders in the deep linking path. * @return {Array} * @static */ this.getPathNames = function() { var path = SWFAddress.getPath(); var names = path.split('/'); if (path.substr(0, 1) == '/') names.splice(0, 1); if (path.substr(path.length - 1, 1) == '/') names.splice(names.length - 1, 1); return names; } /** * Provides the query string part of the deep linking value. * @return {String} * @static */ this.getQueryString = function() { var value = this.getValue(); var index = value.indexOf('?'); return (index != -1 && index < value.length) ? value.substr(index + 1) : ''; } /** * 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) { for (var i = 1; i < _length; i++) _stack.push(''); _stack.push(_getHash()); if (_browser.isIE() && _l.hash != _getHash()) _l.hash = '#' + _ieLocal(_getHash()); try { _url = String(_searchScript(document).src); var qi = _url.indexOf('?'); if (_url && qi > -1) { var param, params = _url.substr(qi + 1).split('&'); for (var i = 0, p; p = params[i]; i++) { 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]; } } } catch(e) {} if (/file:\/\//.test(_l.href)) _opts.html = false; _titleCheck(); _events.addListener(document, 'DOMContentLoaded', _load); } else { _track(); } } /** * SWFAddress embed hooks. * @ignore */ new function() { var _func, _args; var UNDEFINED = 'undefined'; if (typeof swfobject != UNDEFINED) SWFObject = swfobject; if (typeof FlashObject != UNDEFINED) SWFObject = FlashObject; if (typeof SWFObject != UNDEFINED) { if (SWFObject.prototype && SWFObject.prototype.write) { _func = SWFObject.prototype.write; SWFObject.prototype.write = function() { _args = arguments; if (this.getAttribute('version').major < 8) { this.addVariable('$swfaddress', SWFAddress.getValue()); ((typeof _args[0] == 'string') ? document.getElementById(_args[0]) : _args[0]).so = this; } var success; if (success = _func.apply(this, _args)) SWFAddress.addId(this.getAttribute('id')); return success; } } else { _func = SWFObject.registerObject; SWFObject.registerObject = function() { _args = arguments; _func.apply(this, _args); SWFAddress.addId(_args[0]); } _func = SWFObject.createSWF; SWFObject.createSWF = function() { _args = arguments; _func.apply(this, _args); SWFAddress.addId(_args[0].id); } _func = SWFObject.embedSWF; SWFObject.embedSWF = function() { _args = arguments; _func.apply(this, _args); SWFAddress.addId(_args[8].id); } } } if (typeof UFO != UNDEFINED) { _func = UFO.create; UFO.create = function() { _args = arguments; _func.apply(this, _args); SWFAddress.addId(_args[0].id); } } if (typeof AC_FL_RunContent != UNDEFINED) { _func = AC_FL_RunContent; AC_FL_RunContent = function() { _args = arguments; _func.apply(this, _args); for (var i = 0, l = _args.length; i < l; i++) if (_args[i]== 'id') SWFAddress.addId(_args[i+1]); } } }