/*=:project parseSelector 2.0.1 =:description Provides an extensible way of parsing CSS selectors against a DOM in JavaScript. =:file Copyright: 2006-2007 Mark Wubben. Author: Mark Wubben, =:license This software is licensed and provided under the CC-GNU LGPL. See =:support parseSelector supports the following user agents: * Internet Explorer 6 and above * Firefox 1.0 and above, and equivalent Gecko engine versions * Safari 2.0 and above * Opera 8.0 and above * Konqueror 3.5.5 and above It might work in other browsers and versions, but there are no guarantees. There is no verification made when parseSelector is run to ascertain the browser is supported. =:notes The parsing of CSS selectors as streams has been based on Dean Edwards excellent work with cssQuery. See for more info. */ var parseSelector = (function() { var SEPERATOR = /\s*,\s*/ var WHITESPACE = /\s*([\s>+~(),]|^|$)\s*/g; var IMPLIED_ALL = /([\s>+~,]|[^(]\+|^)([#.:@])/g; var STANDARD_SELECT = /^[^\s>+~]/; var STREAM = /[\s#.:>+~()@]|[^\s#.:>+~()@]+/g; function parseSelector(selector, node) { node = node || document.documentElement; var argSelectors = selector.split(SEPERATOR), result = []; for(var i = 0; i < argSelectors.length; i++) { var nodes = [node], stream = toStream(argSelectors[i]); for(var j = 0; j < stream.length;) { var token = stream[j++], filter = stream[j++], args = ''; if(stream[j] == '(') { while(stream[j++] != ')' && j < stream.length) args += stream[j]; args = args.slice(0, -1); } nodes = select(nodes, token, filter, args); } result = result.concat(nodes); } return result; } function toStream(selector) { var stream = selector.replace(WHITESPACE, '$1').replace(IMPLIED_ALL, '$1*$2'); if(STANDARD_SELECT.test(stream)) stream = ' ' + stream; return stream.match(STREAM) || []; } function select(nodes, token, filter, args) { return (parseSelector.selectors[token]) ? parseSelector.selectors[token](nodes, filter, args) : []; } var util = { toArray: function(enumerable) { var a = []; for(var i = 0; i < enumerable.length; i++) a.push(enumerable[i]); return a; } }; var dom = { isTag: function(node, tag) { return (tag == '*') || (tag.toLowerCase() == node.nodeName.toLowerCase()); }, previousSiblingElement: function(node) { do node = node.previousSibling; while(node && node.nodeType != 1); return node; }, nextSiblingElement: function(node) { do node = node.nextSibling; while(node && node.nodeType != 1); return node; }, hasClass: function(name, node) { return (node.className || '').match('(^|\\s)'+name+'(\\s|$)'); }, getByTag: function(tag, node) { return node.getElementsByTagName(tag); } }; var selectors = { '#': function(nodes, filter) { for(var i = 0; i < nodes.length; i++) { if(nodes[i].getAttribute('id') == filter) return [nodes[i]]; } return []; }, ' ': function(nodes, filter) { var result = []; for(var i = 0; i < nodes.length; i++) { result = result.concat(util.toArray(dom.getByTag(filter, nodes[i]))); } return result; }, '>': function(nodes, filter) { var result = []; for(var i = 0, node; i < nodes.length; i++) { node = nodes[i]; for(var j = 0, child; j < node.childNodes.length; j++) { child = node.childNodes[j]; if(child.nodeType == 1 && dom.isTag(child, filter)) result.push(child); } } return result; }, '.': function(nodes, filter) { var result = []; for(var i = 0, node; i < nodes.length; i++) { node = nodes[i]; if(dom.hasClass([filter], node)) result.push(node); } return result; }, ':': function(nodes, filter, args) { return (parseSelector.pseudoClasses[filter]) ? parseSelector.pseudoClasses[filter](nodes, args) : []; } }; parseSelector.selectors = selectors; parseSelector.pseudoClasses = {}; parseSelector.util = util; parseSelector.dom = dom; return parseSelector; })(); /*=:project scalable Inman Flash Replacement (sIFR) version 3. =:file Copyright: 2006 Mark Wubben. Author: Mark Wubben, =:history * IFR: Shaun Inman * sIFR 1: Mike Davidson, Shaun Inman and Tomas Jogin * sIFR 2: Mike Davidson, Shaun Inman, Tomas Jogin and Mark Wubben =:documentation See =:license This software is licensed and provided under the CC-GNU LGPL. See */ var sIFR = new function() { //=:private Constant reference to the Singleton instance var SIFR = this; var CSS_ACTIVE = 'sIFR-active'; var CSS_UNLOADING = 'sIFR-unloading'; var CSS_REPLACED = 'sIFR-replaced'; var CSS_FLASH = 'sIFR-flash'; var CSS_IGNORE = 'sIFR-ignore'; var CSS_ALTERNATE = 'sIFR-alternate'; var CSS_CLASS = 'sIFR-class'; var CSS_LAYOUT = 'sIFR-layout'; var CSS_FIX_FOCUS = 'sIFR-fixfocus' var CSS_DUMMY = 'sIFR-dummy'; var CSS_ZOOM_DETECT = 'sIFR-zoomdetect'; var MIN_FONT_SIZE = 6; var MAX_FONT_SIZE = 126; var MIN_FLASH_VERSION = 8; var PREFETCH_COOKIE = 'SIFR-PREFETCHED'; //= Whitespace string each whitespace character is replaced with, as per `preserveSingleWhitespace`. var DEFAULT_RATIOS = []; var FLASH_PADDING_BOTTOM = 5; var VERSION = '361'; this.isActive = false; this.isEnabled = true; this.preserveSingleWhitespace = false; this.fixWrap = true; this.fixHover = true; this.autoInitialize = true; this.setPrefetchCookie = true; this.cookiePath = '/'; this.domains = []; this.fromLocal = false; this.forceClear = false; this.forceWidth = true; this.fitExactly = false; this.forceTextTransform = true; this.useDomLoaded = true; this.useStyleCheck = false; this.hasFlashClassSet = false; this.repaintOnResize = true; this.callbacks = []; var elementCount = 0; // The number of replaced elements. var hasPrefetched = false, isInitialized = false; var dom = new function() { var XHTML_NS = 'http://www.w3.org/1999/xhtml'; this.getBody = function() { var nodes = document.getElementsByTagName('body'); if(nodes.length == 1) return nodes[0]; return null; }; this.addClass = function(name, node) { if(node) node.className = ((node.className || '') == '' ? '' : node.className + ' ') + name; }; this.removeClass = function(name, node) { if(node) node.className = node.className.replace(new RegExp('(^|\\s)' + name + '(\\s|$)'), '').replace(/^\s+|(\s)\s+/g, '$1'); }; this.hasClass = function(name, node) { return new RegExp('(^|\\s)' + name + '(\\s|$)').test(node.className); }; this.hasOneOfClassses = function(names, node) { for(var i = 0; i < names.length; i++) { if(this.hasClass(names[i], node)) return true; } return false; }; this.create = function(name) { if(document.createElementNS) return document.createElementNS(XHTML_NS, name); return document.createElement(name); }; this.nodeFromHtml = function(html) { var temp = this.create('div'); temp.innerHTML = html; return temp.firstChild; }; this.getComputedStyle = function(node, property) { var result; if(document.defaultView && document.defaultView.getComputedStyle) { result = document.defaultView.getComputedStyle(node, null)[property]; } else if(node.currentStyle) result = node.currentStyle[property]; return result || ''; // Ensuring a string. }; this.getStyleAsInt = function(node, property, requirePx) { var value = this.getComputedStyle(node, property); if(requirePx && !/px$/.test(value)) return 0; value = parseInt(value); return isNaN(value) ? 0 : value; }; this.getWidthFromStyle = function(node) { var width = this.getStyleAsInt(node, 'width', ua.ie); if(width == 0) { var paddingRight = this.getStyleAsInt(node, 'paddingRight', true); var paddingLeft = this.getStyleAsInt(node, 'paddingLeft', true); var borderRight = this.getStyleAsInt(node, 'borderRightWidth', true); var borderLeft = this.getStyleAsInt(node, 'borderLeftWidth', true); width = node.offsetWidth - paddingLeft - paddingRight - borderLeft - borderRight; } return width; }; this.getHeightFromStyle = function(node) { var height = this.getStyleAsInt(node, 'height', ua.ie); if(height == 0) { var paddingTop = this.getStyleAsInt(node, 'paddingTop', true); var paddingBottom = this.getStyleAsInt(node, 'paddingBottom', true); var borderTop = this.getStyleAsInt(node, 'borderTopHeight', true); var borderBottom = this.getStyleAsInt(node, 'borderBottomHeight', true); height = node.offsetHeight - paddingBottom - paddingTop - borderBottom - borderTop; } return height; }; this.blurElement = function(element) { if (ua.gecko) { // This can only be done in Gecko element.blur(); return; } // Move the focus to an input element, and then destroy it. var input = dom.create('input'); input.style.width = '0px'; input.style.height = '0px'; element.parentNode.appendChild(input); input.focus(); input.blur(); input.parentNode.removeChild(input); }; this.getDimensions = function(node) { var width = node.offsetWidth; var height = node.offsetHeight; if(width == 0 || height == 0) { for(var i = 0; i < node.childNodes.length; i++) { var child = node.childNodes[i]; if(child.nodeType != 1) continue; width = Math.max(width, child.offsetWidth); height = Math.max(height, child.offsetHeight); } } return {width: width, height: height}; }; this.contentIsLink = function(node) { var linkFound = false; for(var i = 0; i < node.childNodes.length; i++) { var child = node.childNodes[i]; if(child.nodeType == 3 && !child.nodeValue.match(/^\s*$/)) return false; else if(child.nodeType != 1) continue; var isLink = child.nodeName.toLowerCase() == 'a'; if(!isLink) return false; else linkFound = true; } return linkFound; }; var dom = this; this.swf = { create: function(builder, fixFocus, id, width, height, src, vars, wmode, backgroundColor) { var obj = builder.object(fixFocus, id, src, width, height); return builder.params(obj, 'flashvars', vars, 'wmode', wmode, 'bgcolor', backgroundColor, 'allowScriptAccess', 'always', 'quality', 'best'); }, ie: { // fixFocus is not supported for IE. object: function(fixFocus, id, src, width, height) { return '' + '' + // Load in the callback code. Keep the '); } } function prefetchLight(args) { for(var i = 0; i < args.length; i++) new Image().src = getSource(args[i]); } function clearPrefetch() { if(!ua.ieWin || !hasPrefetched) return; try { var nodes = document.getElementsByTagName('script'); for(var i = nodes.length - 1; i >= 0; i--) { var node = nodes[i]; if(node.type == 'sifr/prefetch') node.parentNode.removeChild(node); } } catch(e) {} } // Gives a font-size to required vertical space ratio function getRatio(size, ratios) { for(var i = 0; i < ratios.length; i += 2) { if(size <= ratios[i]) return ratios[i + 1]; } return ratios[ratios.length - 1] || 1; } function getFilters(obj) { var filters = []; for(var filter in obj) { if(obj[filter] == Object.prototype[filter]) continue; var properties = obj[filter]; filter = [filter.replace(/filter/i, '') + 'Filter']; for(var property in properties) { if(properties[property] == Object.prototype[property]) continue; filter.push(property + ':' + util.escape(util.toJson(util.toHexString(properties[property])))); } filters.push(filter.join(',')); } return util.escape(filters.join(';')); } function calculate(node) { var lineHeight, lines; if(!ua.ie) { //:=todo Only do once for each selector? lineHeight = dom.getStyleAsInt(node, 'lineHeight'); lines = Math.floor(dom.getStyleAsInt(node, 'height') / lineHeight); } else if(ua.ie) { // IE returs computed style in the original units, which is quite useless. // Therefore we'll only use the fontSize if it's in pixel units, otherwise we'll approximate. var fontSize = dom.getComputedStyle(node, 'fontSize'); if(fontSize.indexOf('px') > 0) { lineHeight = parseInt(fontSize); } else { var html = node.innerHTML; // Without these settings, we won't be able to get the rects properly. getClientRects() // won't work on elements having layout or that are hidden. node.style.visibility = 'visible'; node.style.overflow = 'visible'; node.style.position = 'static'; node.style.zoom = 'normal'; node.style.writingMode = 'lr-tb'; node.style.width = node.style.height = 'auto'; node.style.maxWidth = node.style.maxHeight = node.style.styleFloat = 'none'; var rectNode = node; var hasLayout = node.currentStyle.hasLayout; if(hasLayout) { node.innerHTML = '
X
X
X
'; rectNode = node.firstChild; } else node.innerHTML = 'X
X
X'; var rects = rectNode.getClientRects(); lineHeight = rects[1].bottom - rects[1].top; // In IE, the lineHeight is about 1.25 times the height in other browsers. lineHeight = Math.ceil(lineHeight * 0.8); if(hasLayout) { node.innerHTML = '
' + html + '
'; rectNode = node.firstChild; } else node.innerHTML = html; rects = rectNode.getClientRects(); lines = rects.length; if(hasLayout) node.innerHTML = html; // By setting an empty string, the values will fall back to those in the (non-inline) CSS. // When that CSS changes, the changes are reflected here. Setting explicit values would break // that behaviour. node.style.visibility = node.style.width = node.style.height = node.style.maxWidth = node.style.maxHeight = node.style.overflow = node.style.styleFloat = node.style.position = node.style.zoom = node.style.writingMode = ''; } } return {lineHeight: lineHeight, lines: lines}; } this.replace = function(kwargs, mergeKwargs) { if(!ua.supported) return; // This lets you specify to kwarg objects so you don't have to repeat common settings. // The first object will be merged with the second, while properties in the second // object have priority over those in the first. The first object is unmodified // for further use, the resulting second object will be used in the replacement. if(mergeKwargs) kwargs = util.copyProperties(kwargs, mergeKwargs); if(!isInitialized) return replaceKwargsStore.kwargs.push(kwargs); if(SIFR.onReplacementStart) SIFR.onReplacementStart(kwargs); var nodes = kwargs.elements; if(!nodes && parseSelector) nodes = parseSelector(kwargs.selector); if(nodes.length == 0) return; var src = getSource(kwargs.src); var css = util.convertCssArg(kwargs.css); var filters = getFilters(kwargs.filters); var forceClear = (kwargs.forceClear == null) ? SIFR.forceClear : kwargs.forceClear; var fitExactly = (kwargs.fitExactly == null) ? SIFR.fitExactly : kwargs.fitExactly; var forceWidth = fitExactly || (kwargs.forceWidth == null ? SIFR.forceWidth : kwargs.forceWidth); var preventWrap = !!(kwargs.preventWrap && !kwargs.forceSingleLine); var leading = parseInt(util.extractFromCss(css, '.sIFR-root', 'leading')) || 0; var fontSize = util.extractFromCss(css, '.sIFR-root', 'font-size', true) || 0; var backgroundColor = util.extractFromCss(css, '.sIFR-root', 'background-color', true) || '#FFFFFF'; var kerning = util.extractFromCss(css, '.sIFR-root', 'kerning', true) || ''; var gridFitType = kwargs.gridFitType || util.extractFromCss(css, '.sIFR-root', 'text-align') == 'right' ? 'subpixel' : 'pixel'; var textTransform = SIFR.forceTextTransform ? util.extractFromCss(css, '.sIFR-root', 'text-transform', true) || 'none' : 'none'; var opacity = util.extractFromCss(css, '.sIFR-root', 'opacity', true) || '100'; var cursor = util.extractFromCss(css, '.sIFR-root', 'cursor', true) || 'default'; var pixelFont = kwargs.pixelFont || false; var ratios = kwargs.ratios || DEFAULT_RATIOS; var tuneHeight = parseInt(kwargs.tuneHeight) || 0; var events = !!kwargs.onRelease || !!kwargs.onRollOver || !!kwargs.onRollOut; if(parseInt(fontSize).toString() != fontSize && fontSize.indexOf('px') == -1) fontSize = 0; // We only support pixel sizes else fontSize = parseInt(fontSize); if(parseFloat(opacity) < 1) opacity = 100 * parseFloat(opacity); // Make sure to support percentages and decimals var cssText = ''; // Alignment is handled by the browser in this case. if(fitExactly) util.extractFromCss(css, '.sIFR-root', 'text-align', true); if(!kwargs.modifyCss) cssText = util.cssToString(css); var wmode = kwargs.wmode || ''; if(!wmode) { if(kwargs.transparent) wmode = 'transparent'; else if(kwargs.opaque) wmode = 'opaque'; } if(wmode == 'transparent') { if(!ua.transparencySupport) wmode = 'opaque'; else backgroundColor = 'transparent'; } for(var i = 0; i < nodes.length; i++) { var node = nodes[i]; if(dom.hasOneOfClassses([CSS_REPLACED, CSS_IGNORE, CSS_ALTERNATE], node)) continue; // Opera does not allow communication with hidden Flash movies. Visibility is tackled by sIFR itself, but // `display:none` isn't. Additionally, WebKit does not return computed style information for elements with // `display:none`. We'll prevent elements which have `display:none` or are contained in such an element from // being replaced. It's a bit hard to detect this, but we'll check for the dimensions of the element and it's // `display` property. var dimensions = dom.getDimensions(node); var height = dimensions.height; var width = dimensions.width; var display = dom.getComputedStyle(node, 'display'); if(!height || !width || display == null || display == 'none') continue; if(forceClear && ua.gecko) node.style.clear = 'both'; // If the text doesn't wrap nicely, the width becomes too large and Flash // can't adjust for it. By setting the text to just "X" we can be sure // we get the correct width. var html = null; if(SIFR.fixWrap && ua.ie && display == 'block') { html = node.innerHTML; node.innerHTML = 'X'; } // Get the width (again to approximate the final size). The computed width // may not be a pixel unit in IE, in which case we try to calculate using // padding and borders and the offsetWidth. width = dom.getWidthFromStyle(node); if(html && SIFR.fixWrap && ua.ie) node.innerHTML = html; var lineHeight, lines; if(!fontSize) { var calculation = calculate(node); lineHeight = Math.min(MAX_FONT_SIZE, Math.max(MIN_FONT_SIZE, calculation.lineHeight)); if(pixelFont) lineHeight = Math.max(8, 8 * Math.round(lineHeight / 8)); lines = calculation.lines; if(isNaN(lines) || !isFinite(lines) || lines == 0) lines = 1; if(lines > 1 && leading) height += Math.round((lines - 1) * leading); } else { lineHeight = fontSize; lines = 1; } height = Math.round(lines * lineHeight); // We have all the info we need, reset the display setting now. if(forceClear && ua.gecko) node.style.clear = ''; // I wanted to use `noembed` here, but unfortunately FlashBlock only works with `span.sIFR-alternate` var alternate = dom.create('span'); alternate.className = CSS_ALTERNATE; // Clone the original content to the alternate element. var contentNode = node.cloneNode(true); // Temporarily append the contentNode to the document, to get around IE problems with resolved hrefs node.parentNode.appendChild(contentNode); for(var j = 0, l = contentNode.childNodes.length; j < l; j++) { alternate.appendChild(contentNode.childNodes[j].cloneNode(true)); } // Allow the sIFR content to be modified if(kwargs.modifyContent) kwargs.modifyContent(contentNode, kwargs.selector); if(kwargs.modifyCss) cssText = kwargs.modifyCss(css, contentNode, kwargs.selector); var fixHover = SIFR.fixHover && dom.contentIsLink(contentNode); var content = handleContent(contentNode, textTransform, kwargs.uriEncode); // Remove the contentNode again contentNode.parentNode.removeChild(contentNode); if(kwargs.modifyContentString) content.text = kwargs.modifyContentString(content.text, kwargs.selector); if(content.text == '') continue; // Approximate the final height to avoid annoying movements of the page var renderHeight = Math.round(lines * getRatio(lineHeight, ratios) * lineHeight) + FLASH_PADDING_BOTTOM + tuneHeight; var forcedWidth = forceWidth ? width : '100%'; var vars = ['content=' + util.escape(content.text), 'antialiastype=' + (kwargs.antiAliasType || ''), 'width=' + width, 'height=' + height, 'renderheight=' + renderHeight, 'fitexactly=' + fitExactly, 'tunewidth=' + (kwargs.tuneWidth || 0), 'tuneheight=' + tuneHeight, 'offsetleft=' + (kwargs.offsetLeft || ''), 'offsettop=' + (kwargs.offsetTop || ''), 'thickness=' + (kwargs.thickness || ''), 'sharpness=' + (kwargs.sharpness || ''), 'kerning=' + kerning, 'gridfittype=' + gridFitType, 'flashfilters=' + filters, 'opacity=' + opacity, 'blendmode=' + (kwargs.blendMode || ''), 'size=' + lineHeight, 'css=' + util.escape(cssText), 'selectable=' + (kwargs.selectable == null ? 'true' : kwargs.selectable), 'fixhover=' + fixHover, 'preventwrap=' + preventWrap, 'forcesingleline=' + (kwargs.forceSingleLine === true), 'link=' + util.escape(content.primaryLink[0] || ''), 'target=' + util.escape(content.primaryLink[1] || ''), 'events=' + events, 'cursor=' + cursor, 'version=' + VERSION]; var encodedVars = encodeVars(vars); var callbackName = 'sIFR_callback_' + elementCount++; var callbackInfo = new CallbackInfo(callbackName, vars, forceWidth, { onReplacement: kwargs.onReplacement, onRollOver: kwargs.onRollOver, onRollOut: kwargs.onRollOut, onRelease: kwargs.onRelease }); window[callbackName + '_DoFSCommand'] = (function(callbackInfo) { return function(info, arg) { callbackInfo.handle(info, arg); } })(callbackInfo); alternate.setAttribute('id', callbackName + '_alternate'); var builder = ua.ie ? dom.swf.ie : dom.swf.other; var flash = dom.swf.create(builder, ua.fixFocus && kwargs.fixFocus, callbackName, forcedWidth, renderHeight, src, encodedVars, wmode, backgroundColor); builder.insert(node, flash); callbackInfo.html = flash; SIFR.callbacks.push(callbackInfo); if(kwargs.selector) { if(!SIFR.callbacks[kwargs.selector]) SIFR.callbacks[kwargs.selector] = [callbackInfo]; else SIFR.callbacks[kwargs.selector].push(callbackInfo); } node.appendChild(alternate); dom.addClass(CSS_REPLACED, node); } hacks.fragmentIdentifier.restore(); }; this.getCallbackByFlashElement = function(node) { for(var i = 0; i < SIFR.callbacks.length; i++) { if(SIFR.callbacks[i].id == node.getAttribute('id')) return SIFR.callbacks[i]; } }; this.redraw = function() { for(var i = 0; i < SIFR.callbacks.length; i++) SIFR.callbacks[i].resetMovie(); }; function encodeVars(vars) { return vars.join('&').replace(/%/g, '%25'); } /*=:private Walks through the childNodes of `source`. Generates a text representation of these childNodes. Returns: * string: the text representation. Notes: * A number of items are still to do. See the individual comments for that. * This method does not recursion because it'll be necessary to keep a count of all links and their URIs. This is easier without recursion. */ function handleContent(source, textTransform, uriEncode) { uriEncode = uriEncode || util.uriEncode; var stack = [], content = [], primaryLink = []; var nodes = source.childNodes; var i = 0; while(i < nodes.length) { var node = nodes[i]; if(node.nodeType == 3) { var text = util.normalize(node.nodeValue); text = util.textTransform(textTransform, text); // Escape < characters because they'll mess with the HTML rendering inside Flash. text = text.replace(/ -1) className = className.match('(\\s|^)' + CSS_CLASS + '-([^\\s$]*)(\\s|$)')[2]; // or use the first class else className = className.match(/^([^\s]+)/)[1]; } if(className != '') attributes.push('class="' + className + '"'); if(nodeName == 'a') { var href = uriEncode(node.getAttribute('href') || ''); var target = node.getAttribute('target') || ''; attributes.push('href="' + href + '"', 'target="' + target + '"'); if(primaryLink.length == 0) primaryLink = [href, target]; } content.push('<' + nodeName + (attributes.length > 0 ? ' ' : '') + attributes.join(' ') + '>'); if(node.hasChildNodes()) { // Push the current index to the stack and prepare to iterate // over the childNodes. stack.push(i); i = 0; nodes = node.childNodes; continue; } else if(!/^(br|img)$/i.test(node.nodeName)) content.push(''); } if(stack.length > 0 && !node.nextSibling) { // Iterating the childNodes has been completed. Go back to the position // before we started the iteration. If that position was the last child, // go back even further. do { i = stack.pop(); nodes = node.parentNode.parentNode.childNodes; node = nodes[i]; if(node) content.push(''); } while(i == nodes.length - 1 && stack.length > 0); } i++; } return {text: content.join('').replace(/\n|\r/g, ''), primaryLink: primaryLink}; } function CallbackInfo(id, vars, forceWidth, events) { this.id = id; this.vars = vars; this._events = events; this._forceWidth = forceWidth; this._firedReplacementEvent = !(events.onReplacement != null); // Type of value depends on SWF builder. This could use some improvement! this.html = null; } CallbackInfo.prototype.getFlashElement = function() { return document.getElementById(this.id); }; CallbackInfo.prototype.available = function() { var flashNode = this.getFlashElement(); return flashNode && flashNode.parentNode; }; CallbackInfo.prototype.handle = function(info, arg) { if(!this.available()) return; switch(/(FSCommand\:)?(.+)/.exec(info)[2]) { case 'resize': var flashNode = this.getFlashElement(); var $ = arg.split(/\:|,/); flashNode.setAttribute($[0], $[1]); if($.length > 2) flashNode.style[$[2]] = $[3] + 'px'; // Here comes another story! // // Good old Safari (saw this in 2.0.3) does not see the resizing // of the Flash movie as requiring a repaint of the document. Because // of this, the movie won't be resized unless Safari is forced to. // This is done by requesting the `offsetHeight` on the Flash node. // // Just to be sure this hack is applied to all browsers which // implement the KHTML engine. if(ua.khtml) var repaint = flashNode.offsetHeight; if(!this._firedReplacementEvent) { this._events.onReplacement(this); this._firedReplacementEvent = true; } break; case 'resetmovie': this.resetMovie(); break; case 'blur': dom.blurElement(this.getFlashElement()); break; case 'event': if(this._events[arg]) this._events[arg](this); break; default: if(this.debugHandler && /(FSCommand\:)?debug/.test(info)) this.debugHandler(info, arg); } }; CallbackInfo.prototype.call = function(type, value) { if(!this.available()) return false; var flashNode = this.getFlashElement(); try { flashNode.SetVariable('callbackType', type); flashNode.SetVariable('callbackValue', value); flashNode.SetVariable('callbackTrigger', true); } catch(e) { return false; } return true; }; // `content` must not be util.escaped when passed in. // alternate may be an array of nodes to be appended to the alternate content, use this // in XHTML documents. CallbackInfo.prototype.replaceText = function(content, alternate) { var escapedContent = util.escape(content); this.updateVars('content', escapedContent); if(this.call('replacetext', escapedContent)) { var node = this.getAlternate(); if(alternate) { while(node.firstChild) node.removeChild(node.firstChild); for(var i = 0; i < alternate.length; i++) node.appendChild(alternate[i]); } else { try { node.innerHTML = content; } catch(e) {}; } return true; } return false; }; CallbackInfo.prototype.updateVars = function(name, value) { for(var i = 0; i < this.vars.length; i++) { if (this.vars[i].split('=')[0] == name) { this.vars[i] = name + '=' + value; break; } } }; CallbackInfo.prototype.resetMovie = function() { if(!this.available()) return; var flashNode = this.getFlashElement(); var node = flashNode.parentNode; var vars = encodeVars(this.vars); if(ua.ie) { this.html = this.html.replace(/(flashvars(=|\"\svalue=)\")[^\"]+/, '$1' + vars); node.replaceChild(dom.nodeFromHtml(this.html), flashNode); } else { var params = this.html.getElementsByTagName('param'); for(var i = 0; i < params.length; i++) { if(params[i].getAttribute('name') == 'flashvars') { params[i].setAttribute('value', vars); break; } } node.replaceChild(this.html.cloneNode(true), flashNode); } }; CallbackInfo.prototype.resize = function() { if(!this.available()) return; var flashNode = this.getFlashElement(); var ancestor = this.getAncestor(); var minHeight = dom.getHeightFromStyle(ancestor); var currentWidth = flashNode.offsetWidth; var originalWidth = flashNode.getAttribute('width'); var originalHeight = flashNode.getAttribute('height'); // Remove Flash movie from flow flashNode.style.width = '0px'; flashNode.style.height = '0px'; // Set a minimal height on the flashNode's parent, to stop a reflow ancestor.style.minHeight = minHeight + "px"; // Restore original content var nodes = this.getAlternate().childNodes; var clones = []; for(var i = 0; i < nodes.length; i++) { var node = nodes[i].cloneNode(true); clones.push(node); ancestor.appendChild(node); } // Calculate width var width = dom.getWidthFromStyle(ancestor); // Remove original content again for(var i = 0; i < clones.length; i++) ancestor.removeChild(clones[i]); // Reset Flash movie flow flashNode.style.width = flashNode.style.height = ancestor.style.minHeight = ''; flashNode.setAttribute('width', this._forceWidth ? width : originalWidth); flashNode.setAttribute('height', originalHeight); // Resize! if(width != currentWidth) this.call('resize', width); else this.call('scale'); }; CallbackInfo.prototype.changeCSS = function(css) { css = util.escape(util.cssToString(util.convertCssArg(css))); this.updateVars('css', css); return this.call('changecss', css); }; CallbackInfo.prototype.getAlternate = function() { return document.getElementById(this.id + '_alternate'); }; CallbackInfo.prototype.getAncestor = function() { var ancestor = this.getFlashElement().parentNode; return !dom.hasClass(CSS_FIX_FOCUS, ancestor) ? ancestor : ancestor.parentNode; }; };