/** * 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; } } } }