UNPKG

blackbaud-skyux

Version:

Sky UX provides an HTML, CSS and JavaScript framework to implement Blackbaud's design patterns.

1,294 lines (1,215 loc) 102 kB
/** * easyXDM * http://easyxdm.net/ * Copyright(c) 2009-2011, Øyvind Sean Kinsey, oyvind@kinsey.no. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ (function (window, document, location, setTimeout, decodeURIComponent, encodeURIComponent) { /*jslint evil: true, browser: true, immed: true, passfail: true, undef: true, newcap: true*/ /*global JSON, XMLHttpRequest, window, escape, unescape, ActiveXObject */ // // easyXDM // http://easyxdm.net/ // Copyright(c) 2009-2011, Øyvind Sean Kinsey, oyvind@kinsey.no. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // var global = this; var channelId = Math.floor(Math.random() * 10000); // randomize the initial id in case of multiple closures loaded var emptyFn = Function.prototype; var reURI = /^((http.?:)\/\/([^:\/\s]+)(:\d+)*)/; // returns groups for protocol (2), domain (3) and port (4) var reParent = /[\-\w]+\/\.\.\//; // matches a foo/../ expression var reDoubleSlash = /([^:])\/\//g; // matches // anywhere but in the protocol var namespace = ""; // stores namespace under which easyXDM object is stored on the page (empty if object is global) var easyXDM = {}; var _easyXDM = window.easyXDM; // map over global easyXDM in case of overwrite var IFRAME_PREFIX = "easyXDM_"; var HAS_NAME_PROPERTY_BUG; var useHash = false; // whether to use the hash over the query var flashVersion; // will be set if using flash var HAS_FLASH_THROTTLED_BUG; // http://peter.michaux.ca/articles/feature-detection-state-of-the-art-browser-scripting function isHostMethod(object, property){ var t = typeof object[property]; return t == 'function' || (!!(t == 'object' && object[property])) || t == 'unknown'; } function isHostObject(object, property){ return !!(typeof(object[property]) == 'object' && object[property]); } // end // http://perfectionkills.com/instanceof-considered-harmful-or-how-to-write-a-robust-isarray/ function isArray(o){ return Object.prototype.toString.call(o) === '[object Array]'; } // end function hasFlash(){ var name = "Shockwave Flash", mimeType = "application/x-shockwave-flash"; if (!undef(navigator.plugins) && typeof navigator.plugins[name] == "object") { // adapted from the swfobject code var description = navigator.plugins[name].description; if (description && !undef(navigator.mimeTypes) && navigator.mimeTypes[mimeType] && navigator.mimeTypes[mimeType].enabledPlugin) { flashVersion = description.match(/\d+/g); } } if (!flashVersion) { var flash; try { flash = new ActiveXObject("ShockwaveFlash.ShockwaveFlash"); flashVersion = Array.prototype.slice.call(flash.GetVariable("$version").match(/(\d+),(\d+),(\d+),(\d+)/), 1); flash = null; } catch (notSupportedException) { } } if (!flashVersion) { return false; } var major = parseInt(flashVersion[0], 10), minor = parseInt(flashVersion[1], 10); HAS_FLASH_THROTTLED_BUG = major > 9 && minor > 0; return true; } /* * Cross Browser implementation for adding and removing event listeners. */ var on, un; if (isHostMethod(window, "addEventListener")) { on = function(target, type, listener){ target.addEventListener(type, listener, false); }; un = function(target, type, listener){ target.removeEventListener(type, listener, false); }; } else if (isHostMethod(window, "attachEvent")) { on = function(object, sEvent, fpNotify){ object.attachEvent("on" + sEvent, fpNotify); }; un = function(object, sEvent, fpNotify){ object.detachEvent("on" + sEvent, fpNotify); }; } else { throw new Error("Browser not supported"); } /* * Cross Browser implementation of DOMContentLoaded. */ var domIsReady = false, domReadyQueue = [], readyState; if ("readyState" in document) { // If browser is WebKit-powered, check for both 'loaded' (legacy browsers) and // 'interactive' (HTML5 specs, recent WebKit builds) states. // https://bugs.webkit.org/show_bug.cgi?id=45119 readyState = document.readyState; domIsReady = readyState == "complete" || (~ navigator.userAgent.indexOf('AppleWebKit/') && (readyState == "loaded" || readyState == "interactive")); } else { // If readyState is not supported in the browser, then in order to be able to fire whenReady functions apropriately // when added dynamically _after_ DOM load, we have to deduce wether the DOM is ready or not. // We only need a body to add elements to, so the existence of document.body is enough for us. domIsReady = !!document.body; } function dom_onReady(){ if (domIsReady) { return; } domIsReady = true; for (var i = 0; i < domReadyQueue.length; i++) { domReadyQueue[i](); } domReadyQueue.length = 0; } if (!domIsReady) { if (isHostMethod(window, "addEventListener")) { on(document, "DOMContentLoaded", dom_onReady); } else { on(document, "readystatechange", function(){ if (document.readyState == "complete") { dom_onReady(); } }); if (document.documentElement.doScroll && window === top) { var doScrollCheck = function(){ if (domIsReady) { return; } // http://javascript.nwbox.com/IEContentLoaded/ try { document.documentElement.doScroll("left"); } catch (e) { setTimeout(doScrollCheck, 1); return; } dom_onReady(); }; doScrollCheck(); } } // A fallback to window.onload, that will always work on(window, "load", dom_onReady); } /** * This will add a function to the queue of functions to be run once the DOM reaches a ready state. * If functions are added after this event then they will be executed immediately. * @param {function} fn The function to add * @param {Object} scope An optional scope for the function to be called with. */ function whenReady(fn, scope){ if (domIsReady) { fn.call(scope); return; } domReadyQueue.push(function(){ fn.call(scope); }); } /** * Returns an instance of easyXDM from the parent window with * respect to the namespace. * * @return An instance of easyXDM (in the parent window) */ function getParentObject(){ var obj = parent; if (namespace !== "") { for (var i = 0, ii = namespace.split("."); i < ii.length; i++) { obj = obj[ii[i]]; } } return obj.easyXDM; } /** * Removes easyXDM variable from the global scope. It also returns control * of the easyXDM variable to whatever code used it before. * * @param {String} ns A string representation of an object that will hold * an instance of easyXDM. * @return An instance of easyXDM */ function noConflict(ns){ window.easyXDM = _easyXDM; namespace = ns; if (namespace) { IFRAME_PREFIX = "easyXDM_" + namespace.replace(".", "_") + "_"; } return easyXDM; } /* * Methods for working with URLs */ /** * Get the domain name from a url. * @param {String} url The url to extract the domain from. * @return The domain part of the url. * @type {String} */ function getDomainName(url){ return url.match(reURI)[3]; } /** * Get the port for a given URL, or "" if none * @param {String} url The url to extract the port from. * @return The port part of the url. * @type {String} */ function getPort(url){ return url.match(reURI)[4] || ""; } /** * Returns a string containing the schema, domain and if present the port * @param {String} url The url to extract the location from * @return {String} The location part of the url */ function getLocation(url){ var m = url.toLowerCase().match(reURI); var proto = m[2], domain = m[3], port = m[4] || ""; if ((proto == "http:" && port == ":80") || (proto == "https:" && port == ":443")) { port = ""; } return proto + "//" + domain + port; } /** * Resolves a relative url into an absolute one. * @param {String} url The path to resolve. * @return {String} The resolved url. */ function resolveUrl(url){ // replace all // except the one in proto with / url = url.replace(reDoubleSlash, "$1/"); // If the url is a valid url we do nothing if (!url.match(/^(http||https):\/\//)) { // If this is a relative path var path = (url.substring(0, 1) === "/") ? "" : location.pathname; if (path.substring(path.length - 1) !== "/") { path = path.substring(0, path.lastIndexOf("/") + 1); } url = location.protocol + "//" + location.host + path + url; } // reduce all 'xyz/../' to just '' while (reParent.test(url)) { url = url.replace(reParent, ""); } return url; } /** * Appends the parameters to the given url.<br/> * The base url can contain existing query parameters. * @param {String} url The base url. * @param {Object} parameters The parameters to add. * @return {String} A new valid url with the parameters appended. */ function appendQueryParameters(url, parameters){ var hash = "", indexOf = url.indexOf("#"); if (indexOf !== -1) { hash = url.substring(indexOf); url = url.substring(0, indexOf); } var q = []; for (var key in parameters) { if (parameters.hasOwnProperty(key)) { q.push(key + "=" + encodeURIComponent(parameters[key])); } } return url + (useHash ? "#" : (url.indexOf("?") == -1 ? "?" : "&")) + q.join("&") + hash; } // build the query object either from location.query, if it contains the xdm_e argument, or from location.hash var query = (function(input){ input = input.substring(1).split("&"); var data = {}, pair, i = input.length; while (i--) { pair = input[i].split("="); data[pair[0]] = decodeURIComponent(pair[1]); } return data; }(/xdm_e=/.test(location.search) ? location.search : location.hash)); /* * Helper methods */ /** * Helper for checking if a variable/property is undefined * @param {Object} v The variable to test * @return {Boolean} True if the passed variable is undefined */ function undef(v){ return typeof v === "undefined"; } /** * A safe implementation of HTML5 JSON. Feature testing is used to make sure the implementation works. * @return {JSON} A valid JSON conforming object, or null if not found. */ var getJSON = function(){ var cached = {}; var obj = { a: [1, 2, 3] }, json = "{\"a\":[1,2,3]}"; if (typeof JSON != "undefined" && typeof JSON.stringify === "function" && JSON.stringify(obj).replace((/\s/g), "") === json) { // this is a working JSON instance return JSON; } if (Object.toJSON) { if (Object.toJSON(obj).replace((/\s/g), "") === json) { // this is a working stringify method cached.stringify = Object.toJSON; } } if (typeof String.prototype.evalJSON === "function") { obj = json.evalJSON(); if (obj.a && obj.a.length === 3 && obj.a[2] === 3) { // this is a working parse method cached.parse = function(str){ return str.evalJSON(); }; } } if (cached.stringify && cached.parse) { // Only memoize the result if we have valid instance getJSON = function(){ return cached; }; return cached; } return null; }; /** * Applies properties from the source object to the target object.<br/> * @param {Object} target The target of the properties. * @param {Object} source The source of the properties. * @param {Boolean} noOverwrite Set to True to only set non-existing properties. */ function apply(destination, source, noOverwrite){ var member; for (var prop in source) { if (source.hasOwnProperty(prop)) { if (prop in destination) { member = source[prop]; if (typeof member === "object") { apply(destination[prop], member, noOverwrite); } else if (!noOverwrite) { destination[prop] = source[prop]; } } else { destination[prop] = source[prop]; } } } return destination; } // This tests for the bug in IE where setting the [name] property using javascript causes the value to be redirected into [submitName]. function testForNamePropertyBug(){ var form = document.body.appendChild(document.createElement("form")), input = form.appendChild(document.createElement("input")); input.name = IFRAME_PREFIX + "TEST" + channelId; // append channelId in order to avoid caching issues HAS_NAME_PROPERTY_BUG = input !== form.elements[input.name]; document.body.removeChild(form); } /** * Creates a frame and appends it to the DOM. * @param config {object} This object can have the following properties * <ul> * <li> {object} prop The properties that should be set on the frame. This should include the 'src' property.</li> * <li> {object} attr The attributes that should be set on the frame.</li> * <li> {DOMElement} container Its parent element (Optional).</li> * <li> {function} onLoad A method that should be called with the frames contentWindow as argument when the frame is fully loaded. (Optional)</li> * </ul> * @return The frames DOMElement * @type DOMElement */ function createFrame(config){ if (undef(HAS_NAME_PROPERTY_BUG)) { testForNamePropertyBug(); } var frame; // This is to work around the problems in IE6/7 with setting the name property. // Internally this is set as 'submitName' instead when using 'iframe.name = ...' // This is not required by easyXDM itself, but is to facilitate other use cases if (HAS_NAME_PROPERTY_BUG) { frame = document.createElement("<iframe name=\"" + config.props.name + "\"/>"); } else { frame = document.createElement("IFRAME"); frame.name = config.props.name; } frame.id = frame.name = config.props.name; delete config.props.name; if (typeof config.container == "string") { config.container = document.getElementById(config.container); } if (!config.container) { // This needs to be hidden like this, simply setting display:none and the like will cause failures in some browsers. apply(frame.style, { position: "absolute", top: "-2000px", // Avoid potential horizontal scrollbar left: "0px" }); config.container = document.body; } // HACK: IE cannot have the src attribute set when the frame is appended // into the container, so we set it to "javascript:false" as a // placeholder for now. If we left the src undefined, it would // instead default to "about:blank", which causes SSL mixed-content // warnings in IE6 when on an SSL parent page. var src = config.props.src; config.props.src = "javascript:false"; // transfer properties to the frame apply(frame, config.props); frame.border = frame.frameBorder = 0; frame.allowTransparency = true; config.container.appendChild(frame); if (config.onLoad) { on(frame, "load", config.onLoad); } // set the frame URL to the proper value (we previously set it to // "javascript:false" to work around the IE issue mentioned above) if(config.usePost) { var form = config.container.appendChild(document.createElement('form')), input; form.target = frame.name; form.action = src; form.method = 'POST'; if (typeof(config.usePost) === 'object') { for (var i in config.usePost) { if (config.usePost.hasOwnProperty(i)) { if (HAS_NAME_PROPERTY_BUG) { input = document.createElement('<input name="' + i + '"/>'); } else { input = document.createElement("INPUT"); input.name = i; } input.value = config.usePost[i]; form.appendChild(input); } } } form.submit(); form.parentNode.removeChild(form); } else { frame.src = src; } config.props.src = src; return frame; } /** * Check whether a domain is allowed using an Access Control List. * The ACL can contain * and ? as wildcards, or can be regular expressions. * If regular expressions they need to begin with ^ and end with $. * @param {Array/String} acl The list of allowed domains * @param {String} domain The domain to test. * @return {Boolean} True if the domain is allowed, false if not. */ function checkAcl(acl, domain){ // normalize into an array if (typeof acl == "string") { acl = [acl]; } var re, i = acl.length; while (i--) { re = acl[i]; re = new RegExp(re.substr(0, 1) == "^" ? re : ("^" + re.replace(/(\*)/g, ".$1").replace(/\?/g, ".") + "$")); if (re.test(domain)) { return true; } } return false; } /* * Functions related to stacks */ /** * Prepares an array of stack-elements suitable for the current configuration * @param {Object} config The Transports configuration. See easyXDM.Socket for more. * @return {Array} An array of stack-elements with the TransportElement at index 0. */ function prepareTransportStack(config){ var protocol = config.protocol, stackEls; config.isHost = config.isHost || undef(query.xdm_p); useHash = config.hash || false; if (!config.props) { config.props = {}; } if (!config.isHost) { config.channel = query.xdm_c.replace(/["'<>\\]/g, ""); config.secret = query.xdm_s; config.remote = query.xdm_e.replace(/["'<>\\]/g, ""); ; protocol = query.xdm_p; if (config.acl && !checkAcl(config.acl, config.remote)) { throw new Error("Access denied for " + config.remote); } } else { config.remote = resolveUrl(config.remote); config.channel = config.channel || "default" + channelId++; config.secret = Math.random().toString(16).substring(2); if (undef(protocol)) { if (getLocation(location.href) == getLocation(config.remote)) { /* * Both documents has the same origin, lets use direct access. */ protocol = "4"; } else if (isHostMethod(window, "postMessage") || isHostMethod(document, "postMessage")) { /* * This is supported in IE8+, Firefox 3+, Opera 9+, Chrome 2+ and Safari 4+ */ protocol = "1"; } else if (config.swf && isHostMethod(window, "ActiveXObject") && hasFlash()) { /* * The Flash transport superseedes the NixTransport as the NixTransport has been blocked by MS */ protocol = "6"; } else if (navigator.product === "Gecko" && "frameElement" in window && navigator.userAgent.indexOf('WebKit') == -1) { /* * This is supported in Gecko (Firefox 1+) */ protocol = "5"; } else if (config.remoteHelper) { /* * This is supported in all browsers that retains the value of window.name when * navigating from one domain to another, and where parent.frames[foo] can be used * to get access to a frame from the same domain */ protocol = "2"; } else { /* * This is supported in all browsers where [window].location is writable for all * The resize event will be used if resize is supported and the iframe is not put * into a container, else polling will be used. */ protocol = "0"; } } } config.protocol = protocol; // for conditional branching switch (protocol) { case "0":// 0 = HashTransport apply(config, { interval: 100, delay: 2000, useResize: true, useParent: false, usePolling: false }, true); if (config.isHost) { if (!config.local) { // If no local is set then we need to find an image hosted on the current domain var domain = location.protocol + "//" + location.host, images = document.body.getElementsByTagName("img"), image; var i = images.length; while (i--) { image = images[i]; if (image.src.substring(0, domain.length) === domain) { config.local = image.src; break; } } if (!config.local) { // If no local was set, and we are unable to find a suitable file, then we resort to using the current window config.local = window; } } var parameters = { xdm_c: config.channel, xdm_p: 0 }; if (config.local === window) { // We are using the current window to listen to config.usePolling = true; config.useParent = true; config.local = location.protocol + "//" + location.host + location.pathname + location.search; parameters.xdm_e = config.local; parameters.xdm_pa = 1; // use parent } else { parameters.xdm_e = resolveUrl(config.local); } if (config.container) { config.useResize = false; parameters.xdm_po = 1; // use polling } config.remote = appendQueryParameters(config.remote, parameters); } else { apply(config, { channel: query.xdm_c, remote: query.xdm_e, useParent: !undef(query.xdm_pa), usePolling: !undef(query.xdm_po), useResize: config.useParent ? false : config.useResize }); } stackEls = [new easyXDM.stack.HashTransport(config), new easyXDM.stack.ReliableBehavior({}), new easyXDM.stack.QueueBehavior({ encode: true, maxLength: 4000 - config.remote.length }), new easyXDM.stack.VerifyBehavior({ initiate: config.isHost })]; break; case "1": stackEls = [new easyXDM.stack.PostMessageTransport(config)]; break; case "2": if (config.isHost) { config.remoteHelper = resolveUrl(config.remoteHelper); } stackEls = [new easyXDM.stack.NameTransport(config), new easyXDM.stack.QueueBehavior(), new easyXDM.stack.VerifyBehavior({ initiate: config.isHost })]; break; case "3": stackEls = [new easyXDM.stack.NixTransport(config)]; break; case "4": stackEls = [new easyXDM.stack.SameOriginTransport(config)]; break; case "5": stackEls = [new easyXDM.stack.FrameElementTransport(config)]; break; case "6": if (!flashVersion) { hasFlash(); } stackEls = [new easyXDM.stack.FlashTransport(config)]; break; } // this behavior is responsible for buffering outgoing messages, and for performing lazy initialization stackEls.push(new easyXDM.stack.QueueBehavior({ lazy: config.lazy, remove: true })); return stackEls; } /** * Chains all the separate stack elements into a single usable stack.<br/> * If an element is missing a necessary method then it will have a pass-through method applied. * @param {Array} stackElements An array of stack elements to be linked. * @return {easyXDM.stack.StackElement} The last element in the chain. */ function chainStack(stackElements){ var stackEl, defaults = { incoming: function(message, origin){ this.up.incoming(message, origin); }, outgoing: function(message, recipient){ this.down.outgoing(message, recipient); }, callback: function(success){ this.up.callback(success); }, init: function(){ this.down.init(); }, destroy: function(){ this.down.destroy(); } }; for (var i = 0, len = stackElements.length; i < len; i++) { stackEl = stackElements[i]; apply(stackEl, defaults, true); if (i !== 0) { stackEl.down = stackElements[i - 1]; } if (i !== len - 1) { stackEl.up = stackElements[i + 1]; } } return stackEl; } /** * This will remove a stackelement from its stack while leaving the stack functional. * @param {Object} element The elment to remove from the stack. */ function removeFromStack(element){ element.up.down = element.down; element.down.up = element.up; element.up = element.down = null; } /* * Export the main object and any other methods applicable */ /** * @class easyXDM * A javascript library providing cross-browser, cross-domain messaging/RPC. * @version 2.4.19.3 * @singleton */ apply(easyXDM, { /** * The version of the library * @type {string} */ version: "2.4.19.3", /** * This is a map containing all the query parameters passed to the document. * All the values has been decoded using decodeURIComponent. * @type {object} */ query: query, /** * @private */ stack: {}, /** * Applies properties from the source object to the target object.<br/> * @param {object} target The target of the properties. * @param {object} source The source of the properties. * @param {boolean} noOverwrite Set to True to only set non-existing properties. */ apply: apply, /** * A safe implementation of HTML5 JSON. Feature testing is used to make sure the implementation works. * @return {JSON} A valid JSON conforming object, or null if not found. */ getJSONObject: getJSON, /** * This will add a function to the queue of functions to be run once the DOM reaches a ready state. * If functions are added after this event then they will be executed immediately. * @param {function} fn The function to add * @param {object} scope An optional scope for the function to be called with. */ whenReady: whenReady, /** * Removes easyXDM variable from the global scope. It also returns control * of the easyXDM variable to whatever code used it before. * * @param {String} ns A string representation of an object that will hold * an instance of easyXDM. * @return An instance of easyXDM */ noConflict: noConflict }); /*jslint evil: true, browser: true, immed: true, passfail: true, undef: true, newcap: true*/ /*global console, _FirebugCommandLine, easyXDM, window, escape, unescape, isHostObject, undef, _trace, domIsReady, emptyFn, namespace */ // // easyXDM // http://easyxdm.net/ // Copyright(c) 2009-2011, Øyvind Sean Kinsey, oyvind@kinsey.no. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // /*jslint evil: true, browser: true, immed: true, passfail: true, undef: true, newcap: true*/ /*global easyXDM, window, escape, unescape, isHostObject, isHostMethod, un, on, createFrame, debug */ // // easyXDM // http://easyxdm.net/ // Copyright(c) 2009-2011, Øyvind Sean Kinsey, oyvind@kinsey.no. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // /** * @class easyXDM.DomHelper * Contains methods for dealing with the DOM * @singleton */ easyXDM.DomHelper = { /** * Provides a consistent interface for adding eventhandlers * @param {Object} target The target to add the event to * @param {String} type The name of the event * @param {Function} listener The listener */ on: on, /** * Provides a consistent interface for removing eventhandlers * @param {Object} target The target to remove the event from * @param {String} type The name of the event * @param {Function} listener The listener */ un: un, /** * Checks for the presence of the JSON object. * If it is not present it will use the supplied path to load the JSON2 library. * This should be called in the documents head right after the easyXDM script tag. * http://json.org/json2.js * @param {String} path A valid path to json2.js */ requiresJSON: function(path){ if (!isHostObject(window, "JSON")) { // we need to encode the < in order to avoid an illegal token error // when the script is inlined in a document. document.write('<' + 'script type="text/javascript" src="' + path + '"><' + '/script>'); } } }; /*jslint evil: true, browser: true, immed: true, passfail: true, undef: true, newcap: true*/ /*global easyXDM, window, escape, unescape, debug */ // // easyXDM // http://easyxdm.net/ // Copyright(c) 2009-2011, Øyvind Sean Kinsey, oyvind@kinsey.no. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // (function(){ // The map containing the stored functions var _map = {}; /** * @class easyXDM.Fn * This contains methods related to function handling, such as storing callbacks. * @singleton * @namespace easyXDM */ easyXDM.Fn = { /** * Stores a function using the given name for reference * @param {String} name The name that the function should be referred by * @param {Function} fn The function to store * @namespace easyXDM.fn */ set: function(name, fn){ _map[name] = fn; }, /** * Retrieves the function referred to by the given name * @param {String} name The name of the function to retrieve * @param {Boolean} del If the function should be deleted after retrieval * @return {Function} The stored function * @namespace easyXDM.fn */ get: function(name, del){ if (!_map.hasOwnProperty(name)) { return; } var fn = _map[name]; if (del) { delete _map[name]; } return fn; } }; }()); /*jslint evil: true, browser: true, immed: true, passfail: true, undef: true, newcap: true*/ /*global easyXDM, window, escape, unescape, chainStack, prepareTransportStack, getLocation, debug */ // // easyXDM // http://easyxdm.net/ // Copyright(c) 2009-2011, Øyvind Sean Kinsey, oyvind@kinsey.no. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // /** * @class easyXDM.Socket * This class creates a transport channel between two domains that is usable for sending and receiving string-based messages.<br/> * The channel is reliable, supports queueing, and ensures that the message originates from the expected domain.<br/> * Internally different stacks will be used depending on the browsers features and the available parameters. * <h2>How to set up</h2> * Setting up the provider: * <pre><code> * var socket = new easyXDM.Socket({ * &nbsp; local: "name.html", * &nbsp; onReady: function(){ * &nbsp; &nbsp; &#47;&#47; you need to wait for the onReady callback before using the socket * &nbsp; &nbsp; socket.postMessage("foo-message"); * &nbsp; }, * &nbsp; onMessage: function(message, origin) { * &nbsp;&nbsp; alert("received " + message + " from " + origin); * &nbsp; } * }); * </code></pre> * Setting up the consumer: * <pre><code> * var socket = new easyXDM.Socket({ * &nbsp; remote: "http:&#47;&#47;remotedomain/page.html", * &nbsp; remoteHelper: "http:&#47;&#47;remotedomain/name.html", * &nbsp; onReady: function(){ * &nbsp; &nbsp; &#47;&#47; you need to wait for the onReady callback before using the socket * &nbsp; &nbsp; socket.postMessage("foo-message"); * &nbsp; }, * &nbsp; onMessage: function(message, origin) { * &nbsp;&nbsp; alert("received " + message + " from " + origin); * &nbsp; } * }); * </code></pre> * If you are unable to upload the <code>name.html</code> file to the consumers domain then remove the <code>remoteHelper</code> property * and easyXDM will fall back to using the HashTransport instead of the NameTransport when not able to use any of the primary transports. * @namespace easyXDM * @constructor * @cfg {String/Window} local The url to the local name.html document, a local static file, or a reference to the local window. * @cfg {Boolean} lazy (Consumer only) Set this to true if you want easyXDM to defer creating the transport until really needed. * @cfg {String} remote (Consumer only) The url to the providers document. * @cfg {String} remoteHelper (Consumer only) The url to the remote name.html file. This is to support NameTransport as a fallback. Optional. * @cfg {Number} delay The number of milliseconds easyXDM should try to get a reference to the local window. Optional, defaults to 2000. * @cfg {Number} interval The interval used when polling for messages. Optional, defaults to 300. * @cfg {String} channel (Consumer only) The name of the channel to use. Can be used to set consistent iframe names. Must be unique. Optional. * @cfg {Function} onMessage The method that should handle incoming messages.<br/> This method should accept two arguments, the message as a string, and the origin as a string. Optional. * @cfg {Function} onReady A method that should be called when the transport is ready. Optional. * @cfg {DOMElement|String} container (Consumer only) The element, or the id of the element that the primary iframe should be inserted into. If not set then the iframe will be positioned off-screen. Optional. * @cfg {Array/String} acl (Provider only) Here you can specify which '[protocol]://[domain]' patterns that should be allowed to act as the consumer towards this provider.<br/> * This can contain the wildcards ? and *. Examples are 'http://example.com', '*.foo.com' and '*dom?.com'. If you want to use reqular expressions then you pattern needs to start with ^ and end with $. * If none of the patterns match an Error will be thrown. * @cfg {Object} props (Consumer only) Additional properties that should be applied to the iframe. This can also contain nested objects e.g: <code>{style:{width:"100px", height:"100px"}}</code>. * Properties such as 'name' and 'src' will be overrided. Optional. */ easyXDM.Socket = function(config){ // create the stack var stack = chainStack(prepareTransportStack(config).concat([{ incoming: function(message, origin){ config.onMessage(message, origin); }, callback: function(success){ if (config.onReady) { config.onReady(success); } } }])), recipient = getLocation(config.remote); // set the origin this.origin = getLocation(config.remote); /** * Initiates the destruction of the stack. */ this.destroy = function(){ stack.destroy(); }; /** * Posts a message to the remote end of the channel * @param {String} message The message to send */ this.postMessage = function(message){ stack.outgoing(message, recipient); }; stack.init(); }; /*jslint evil: true, browser: true, immed: true, passfail: true, undef: true, newcap: true*/ /*global easyXDM, window, escape, unescape, undef,, chainStack, prepareTransportStack, debug, getLocation */ // // easyXDM // http://easyxdm.net/ // Copyright(c) 2009-2011, Øyvind Sean Kinsey, oyvind@kinsey.no. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // /** * @class easyXDM.Rpc * Creates a proxy object that can be used to call methods implemented on the remote end of the channel, and also to provide the implementation * of methods to be called from the remote end.<br/> * The instantiated object will have methods matching those specified in <code>config.remote</code>.<br/> * This requires the JSON object present in the document, either natively, using json.org's json2 or as a wrapper around library spesific methods. * <h2>How to set up</h2> * <pre><code> * var rpc = new easyXDM.Rpc({ * &nbsp; &#47;&#47; this configuration is equal to that used by the Socket. * &nbsp; remote: "http:&#47;&#47;remotedomain/...", * &nbsp; onReady: function(){ * &nbsp; &nbsp; &#47;&#47; you need to wait for the onReady callback before using the proxy * &nbsp; &nbsp; rpc.foo(... * &nbsp; } * },{ * &nbsp; local: {..}, * &nbsp; remote: {..} * }); * </code></pre> * * <h2>Exposing functions (procedures)</h2> * <pre><code> * var rpc = new easyXDM.Rpc({ * &nbsp; ... * },{ * &nbsp; local: { * &nbsp; &nbsp; nameOfMethod: { * &nbsp; &nbsp; &nbsp; method: function(arg1, arg2, success, error){ * &nbsp; &nbsp; &nbsp; &nbsp; ... * &nbsp; &nbsp; &nbsp; } * &nbsp; &nbsp; }, * &nbsp; &nbsp; &#47;&#47; with shorthand notation * &nbsp; &nbsp; nameOfAnotherMethod: function(arg1, arg2, success, error){ * &nbsp; &nbsp; } * &nbsp; }, * &nbsp; remote: {...} * }); * </code></pre> * The function referenced by [method] will receive the passed arguments followed by the callback functions <code>success</code> and <code>error</code>.<br/> * To send a successfull result back you can use * <pre><code> * return foo; * </pre></code> * or * <pre><code> * success(foo); * </pre></code> * To return an error you can use * <pre><code> * throw new Error("foo error"); * </code></pre> * or * <pre><code> * error("foo error"); * </code></pre> * * <h2>Defining remotely exposed methods (procedures/notifications)</h2> * The definition of the remote end is quite similar: * <pre><code> * var rpc = new easyXDM.Rpc({ * &nbsp; ... * },{ * &nbsp; local: {...}, * &nbsp; remote: { * &nbsp; &nbsp; nameOfMethod: {} * &nbsp; } * }); * </code></pre> * To call a remote method use * <pre><code> * rpc.nameOfMethod("arg1", "arg2", function(value) { * &nbsp; alert("success: " + value); * }, function(message) { * &nbsp; alert("error: " + message + ); * }); * </code></pre> * Both the <code>success</code> and <code>errror</code> callbacks are optional.<br/> * When called with no callback a JSON-RPC 2.0 notification will be executed. * Be aware that you will not be notified of any errors with this method. * <br/> * <h2>Specifying a custom serializer</h2> * If you do not want to use the JSON2 library for non-native JSON support, but instead capabilities provided by some other library * then you can specify a custom serializer using <code>serializer: foo</code> * <pre><code> * var rpc = new easyXDM.Rpc({ * &nbsp; ... * },{ * &nbsp; local: {...}, * &nbsp; remote: {...}, * &nbsp; serializer : { * &nbsp; &nbsp; parse: function(string){ ... }, * &nbsp; &nbsp; stringify: function(object) {...} * &nbsp; } * }); * </code></pre> * If <code>serializer</code> is set then the class will not attempt to use the native implementation. * @namespace easyXDM * @constructor * @param {Object} config The underlying transports configuration. See easyXDM.Socket for available parameters. * @param {Object} jsonRpcConfig The description of the interface to implement. */ easyXDM.Rpc = function(config, jsonRpcConfig){ // expand shorthand notation if (jsonRpcConfig.local) { for (var method in jsonRpcConfig.local) { if (jsonRpcConfig.local.hasOwnProperty(method)) { var member = jsonRpcConfig.local[method]; if (typeof member === "function") { jsonRpcConfig.local[method] = { method: member }; } } } } // create the stack var stack = chainStack(prepareTransportStack(config).concat([new easyXDM.stack.RpcBehavior(this, jsonRpcConfig), { callback: function(success){ if (config.onReady) { config.onReady(success); } } }])); // set the origin this.origin = getLocation(config.remote); /** * Initiates the destruction of the stack. */ this.destroy = function(){ stack.destroy(); }; stack.init(); }; /*jslint evil: true, browser: true, immed: true, passfail: true, undef: true, newcap: true*/ /*global easyXDM, window, escape, unescape, getLocation, appendQueryParameters, createFrame, debug, un, on, apply, whenReady, getParentObject, IFRAME_PREFIX*/ // // easyXDM // http://easyxdm.net/ // Copyright(c) 2009-2011, Øyvind Sean Kinsey, oyvind@kinsey.no. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,