/*
* easyXDM
* http://easyxdm.net/
* Copyright(c) 2009, Øyvind Sean Kinsey, oyvind@kinsey.no.
*
* MIT Licensed - http://easyxdm.net/license/mit.txt
*
*/
/*jslint evil: true, browser: true, immed: true, passfail: true, undef: true, newcap: true*/
/*global easyXDM: true, window, escape, unescape */
/**
* @class easyXDM
* A javascript library providing cross-browser, cross-site messaging/method invocation.
* easyXDM.Debug and the easyXDM.configuration namespace is only available in the debug version.
* @version 1.5.3.32
* @singleton
*/
easyXDM = {
/**
* The version of the library
* @type {String}
*/
version: "1.5.3.32",
/**
* Applies properties from the source object to the target object
* @param {Object} target The target of the properties
* @param {Object} source The source of the properties
*/
apply: function(target, source){
if (!source) {
return;
}
for (var key in source) {
if (source.hasOwnProperty(key)) {
target[key] = source[key];
}
}
},
/**
* @class easyXDM.Interface
* Creates an interface 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.
* @constructor
* @param {easyXDM.configuration.ChannelConfiguration} channelConfig The underlying channels configuration.
* @param {easyXDM.configuration.InterfaceConfiguration} config The description of the interface to implement
* @param {Function} onReady A method that should be called when the interface is ready
* @namespace easyXDM
*/
Interface: function(channelConfig, config, onReady){
easyXDM.Debug.trace("creating new interface");
var _channel;
var _callbackCounter = 0, _callbacks = {};
/**
* Creates a method that implements the given definition
* @private
* @param {easyXDM.configuration.Methods.Method} The method configuration
* @param {String} name The name of the method
*/
function _createMethod(definition, name){
// Add the scope so that calling the methods will work as expected
if (typeof definition.scope === "undefined") {
definition.scope = window;
}
if (definition.isVoid) {
easyXDM.Debug.trace("creating void method " + name);
// No need to register a callback
return function(){
easyXDM.Debug.trace("executing void method " + name);
var params = Array.prototype.slice.call(arguments, 0);
// Send the method request
window.setTimeout(function(){
_channel.sendData({
name: name,
params: params
});
}, 0);
};
}
else {
easyXDM.Debug.trace("creating method " + name);
// We need to extract and register the callback
return function(){
easyXDM.Debug.trace("executing method " + name);
_callbacks["" + (++_callbackCounter)] = arguments[arguments.length - 1];
var request = {
name: name,
id: (_callbackCounter),
params: Array.prototype.slice.call(arguments, 0, arguments.length - 1)
};
// Send the method request
window.setTimeout(function(){
_channel.sendData(request);
}, 0);
};
}
}
/**
* Executes the exposed method
* @private
* @param {String} name The name of the method
* @param {Number} id The callback id to use
* @param {Function} method The exposed implementation
* @param {Array} params The parameters supplied by the remote end
*/
function _executeMethod(name, id, method, params){
if (!method) {
throw new Error("The method " + name + " is not implemented.");
}
if (method.isAsync) {
easyXDM.Debug.trace("requested to execute async method " + name);
// The method is async, we need to add a callback
params.push(function(result){
// Send back the result
_channel.sendData({
id: id,
response: result
});
});
// Call local method
method.method.apply(method.scope, params);
}
else {
if (method.isVoid) {
easyXDM.Debug.trace("requested to execute void method " + name);
// Call local method
method.method.apply(method.scope, params);
}
else {
easyXDM.Debug.trace("requested to execute method " + name);
// Call local method and send back the response
_channel.sendData({
id: id,
response: method.method.apply(method.scope, params)
});
}
}
}
channelConfig.converter = JSON;
/**
* Handles incoming data.
* This can be either a request a method invocation, the response to one.
* @private
* @param {Object} data The JSON data object
* @param {String} origin The origin of the message
*/
channelConfig.onData = function(data, origin){
if (data.name) {
easyXDM.Debug.trace("received request to execute method " + data.name + (data.id ? (" using callback id " + data.id) : ""));
// A method call from the remote end
_executeMethod(data.name, data.id, config.local[data.name], data.params);
}
else {
easyXDM.Debug.trace("received return value destined to callback with id " + data.id);
// A method response from the other end
_callbacks[data.id](data.response);
delete _callbacks[data.id];
}
};
/**
* Tries to destroy the underlying channel and to remove all traces of the interface.
*/
this.destroy = function(){
_channel.destroy();
for (var x in this) {
if (this.hasOwnProperty(x)) {
delete this[x];
}
}
};
if (config.remote) {
easyXDM.Debug.trace("creating concrete implementations");
// Implement the remote sides exposed methods
for (var name in config.remote) {
if (config.remote.hasOwnProperty(name)) {
this[name] = _createMethod(config.remote[name], name);
}
}
}
// Delay setting up the channel until the interface has been returned
window.setTimeout(function(){
_channel = new easyXDM.Channel(channelConfig, onReady);
}, 5);
},
/**
* @class easyXDM.Channel
* A channel wrapping an underlying transport.
* @constructor
* @param {easyXDM.ChannelConfiguration} config The channels configuration
* @param {Function} onReady A method that should be called when the channel is ready
* @namespace easyXDM
*/
Channel: function(config, onReady){
easyXDM.Debug.trace("easyXDM.Channel.constructor");
if (!config.converter) {
throw new Error("No converter present. You should use the easyXDM.transport classes directly.");
}
/**
* Wraps the transports onMessage method using the supplied serializer to convert.
* @param {Object} data
* @private
*/
config.onMessage = function(message, origin){
this.onData(this.converter.parse(message), origin);
};
/**
* The underlying transport used by this channel
* @type easyXDM.transport.ITransport
*/
this.transport = null;
/**
* Tries to destroy the underlying transport
*/
this.destroy = function(){
easyXDM.Debug.trace("easyXDM.Channel.destroy");
this.transport.destroy();
};
/**
* Send data using the underlying transport
* If a serializer is specified then this will be used to serialize the data first.
* @param {Object} data the data to send
*/
this.sendData = function(data){
this.transport.postMessage(config.converter.stringify(data));
};
var that = this;
// Delay setting up the transport until the Channel is returned
window.setTimeout(function(){
that.transport = new easyXDM.transport.BestAvailableTransport(config, onReady);
}, 5);
}
};
/**
* @class easyXDM.Debug
* Utilities for debugging. This class is only precent in the debug version.
* @namespace easyXDM
*/
easyXDM.Debug = {
/**
* Logs the message to console.log if available
* @param {String} msg The message to log
*/
log: function(msg){
// Uses memoizing to cache the implementation
var log;
if (typeof console === "undefined" || typeof console.log === "undefined") {
/**
* Sets log to be an empty function since we have no output available
* @ignore
*/
log = function(){
};
}
else {
/**
* Sets log to be a wrapper around console.log
* @ignore
* @param {String} msg
*/
log = function(msg){
//console.log(location.host + ":" + msg);
};
}
log(msg);
easyXDM.Debug.log = log;
},
/**
* Will try to trace the given message either to a DOMElement with the id "log",
* or by using console.info.
* @param {String} msg The message to trace
*/
trace: function(msg){
// Uses memoizing to cache the implementation
var trace;
var el = document.getElementById("log");
if (el) {
/**
* Sets trace to be a function that outputs the messages to the DOMElement with id "log"
* @ignore
* @param {String} msg
*/
trace = function(msg){
el.appendChild(document.createElement("div")).appendChild(document.createTextNode(location.host + "-" + new Date().valueOf() + ":" + msg));
el.scrollTop = el.scrollHeight;
};
}
else {
if (typeof console === "undefined" || typeof console.info === "undefined") {
/**
* Sets trace to be an empty function
* @ignore
*/
trace = function(){
};
}
else {
/**
* Sets trace to be a wrapper around console.info
* @ignore
* @param {String} msg
*/
trace = function(msg){
//console.info(location.host + ":" + msg);
};
}
}
easyXDM.Debug.trace = trace;
easyXDM.Debug.trace(msg);
}
};
/**
* @class easyXDM.DomHelper
* Contains methods for dealing with the DOM
* @singleton
*/
easyXDM.DomHelper = {
/**
* Creates a frame and appends it to the DOM.
* @param {String} url The url the frame should be set to
* @param {String} name The id/name the frame should get
* @param {DOMElement} container
* @param {Function} onLoad A method that should be called with the frames contentWindow as argument when the frame is fully loaded.
* @return The frames DOMElement
* @type DOMElement
*/
createFrame: function(url, container, onLoad){
easyXDM.Debug.trace("creating frame pointing to " + url);
var frame;
var framesets = document.getElementsByTagName("FRAMESET");
if (!container && framesets && framesets.length > 0) {
frame = document.createElement("FRAME");
frame.src = url;
if (onLoad) {
this.addEventListener(frame, "load", function(){
onLoad(frame.contentWindow);
});
}
framesets[0].appendChild(frame);
}
else {
frame = document.createElement("IFRAME");
frame.setAttribute('frameBorder', '0');
frame.setAttribute('border', '0');
frame.setAttribute('scrolling', 'auto');
frame.src = url;
if (onLoad) {
this.addEventListener(frame, "load", function(){
onLoad(frame.contentWindow);
});
}
if (container) {
container.appendChild(frame);
}
else {
frame.style.position = "absolute";
frame.style.left = "-2000px";
document.body.appendChild(frame);
}
}
return frame;
},
/**
* Gives 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
*/
addEventListener: function(target, type, listener, useCapture){
// Uses memoizing to cache the implementation
if (window.addEventListener) {
/**
* Set addEventListener to use the DOM level 2 addEventListener
* https://developer.mozilla.org/en/DOM/element.addEventListener
* @ignore
* @param {Object} target
* @param {String} type
* @param {Function} listener
*/
easyXDM.DomHelper.addEventListener = function(target, type, listener, useCapture){
easyXDM.Debug.trace("adding listener " + type);
target.addEventListener(type, listener, useCapture);
};
}
else {
/**
* Set addEventlistener to a wrapper around the IE spesific attachEvent
* http://msdn.microsoft.com/en-us/library/ms536343%28VS.85%29.aspx
* @ignore
* @param {Object} object
* @param {String} sEvent
* @param {Function} fpNotify
*/
easyXDM.DomHelper.addEventListener = function(object, sEvent, fpNotify){
easyXDM.Debug.trace("adding listener " + sEvent);
object.attachEvent("on" + sEvent, fpNotify);
};
}
easyXDM.DomHelper.addEventListener(target, type, listener);
},
/**
* Gives 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
*/
removeEventListener: function(target, type, listener, useCapture){
// Uses memoizing to cache the implementation
var removeEventListener;
if (window.removeEventListener) {
/**
* Set removeEventListener to use the DOM level 2 removeEventListener
* https://developer.mozilla.org/en/DOM/element.removeEventListener
* @ignore
* @param {Object} target
* @param {String} type
* @param {Function} listener
*/
removeEventListener = function(target, type, listener, useCapture){
target.removeEventListener(type, listener, useCapture);
};
}
else {
/**
* Set removeEventlistener to a wrapper around the IE spesific detachEvent
* http://msdn.microsoft.com/en-us/library/ms536411%28VS.85%29.aspx
* @ignore
* @param {Object} object
* @param {String} sEvent
* @param {Function} fpNotify
*/
removeEventListener = function(object, sEvent, fpNotify){
object.detachEvent("on" + sEvent, fpNotify);
};
}
removeEventListener(target, type, listener);
easyXDM.DomHelper.removeEventListener = removeEventListener;
},
/**
* Checks for the precense of the JSON object.
* If it is not precent 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 (typeof JSON == "undefined" || !JSON) {
easyXDM.Debug.log("loading external JSON");
document.write('');
}
else {
easyXDM.Debug.log("native JSON found");
}
}
};
easyXDM.transport = {
/**
* @class easyXDM.transport.ITransport
* The interface implemented by all transport classes.
* Only available in debug mode.
* @namespace easyXDM.transport
*/
ITransport: {
/**
* Sends the message
* @param {String} message The message to send
*/
postMessage: function(message){
},
/**
* Breaks down the connection and tries to clean up the dom.
*/
destroy: function(){
}
},
/**
* @class easyXDM.transport.BestAvailableTransport
* @extends easyXDM.transport.ITransport
* BestAvailableTransport is a transport class that uses the best transport available.
* Currently it will select among PostMessageTransport and HashTransport.
* @constructor
* @param {easyXDM.transport.TransportConfiguration} config The transports configuration.
* @param {Function} onReady A method that should be called when the transport is ready
* @namespace easyXDM.transport
*/
BestAvailableTransport: function(config, onReady){
easyXDM.Debug.trace("easyXDM.transport.BestAvailableTransport.constructor");
if (config.local) {
config.channel = (config.channel) ? config.channel : "default";
}
else {
var query = easyXDM.Url.Query();
config.channel = query.channel;
config.remote = query.endpoint;
}
var type = "HashTransport";
if (window.postMessage) {
type = "PostMessageTransport";
}
return new easyXDM.transport[type](config, onReady);
},
/**
* @class easyXDM.transport.PostMessageTransport
* @extends easyXDM.transport.ITransport
* PostMessageTransport is a transport class that uses HTML5 postMessage for communication
* http://msdn.microsoft.com/en-us/library/ms644944(VS.85).aspx
* https://developer.mozilla.org/en/DOM/window.postMessage
* @constructor
* @param {easyXDM.transport.TransportConfiguration} config The transports configuration.
* @param {Function} onReady A method that should be called when the transport is ready
* @namespace easyXDM.transport
*/
PostMessageTransport: function(config, onReady){
if (!window.postMessage) {
throw "This browser does not support window.postMessage";
}
easyXDM.Debug.trace("easyXDM.transport.PostMessageTransport.constructor");
var _callerWindow, _targetOrigin = easyXDM.Url.getLocation(config.remote), _window_onMessageImplementation;
/**
* Resolves the origin from the event object
* @private
* @param {Object} event The messageevent
* @return {String} The scheme, host and port of the origin
*/
function _getOrigin(event){
if (event.origin) {
// This is the HTML5 property
return event.origin;
}
if (event.uri) {
// From earlier implementations
return easyXDM.Url.getLocation(event.uri);
}
if (event.domain) {
// This is the last option and will fail if the
// origin is not using the same schema as we are
return location.protocol + "//" + event.domain;
}
throw "Unable to retrieve the origin of the event";
}
/**
* Delays calling onReady until the class has been returned
* @private
*/
function _onReady(){
if (onReady) {
window.setTimeout(onReady, 5);
}
}
/**
* The main onMessage handler. This will pass on the event to the real implementation
* @private
* @param {Object} event The messageevent
*/
function _window_onMessage(event){
easyXDM.Debug.trace("onMessage");
_window_onMessageImplementation(event);
}
easyXDM.DomHelper.addEventListener(window, "message", _window_onMessage);
/**
* This is the main implementation for the onMessage event.
* It checks the validity of the origin and passes the message on if appropriate.
* @private
* @param {Object} event The messageevent
*/
function _handleMessage(event){
var origin = _getOrigin(event);
easyXDM.Debug.trace("received message '" + event.data + "' from " + origin);
if (origin == _targetOrigin && event.data.substring(0, config.channel.length + 1) == config.channel + " ") {
config.onMessage(event.data.substring(config.channel.length + 1), origin);
}
}
/**
* Used by local to fire the onReady method.
* After being notified by the remote, this method will replace the
* onMessage handler with _handleMessage and fire onReady
* @private
* @param {Object} event The messageevent
*/
function _waitForReady(event){
if (event.data == config.channel + "-ready") {
easyXDM.Debug.trace("firing onReady");
// We use memoization to avoid having to run this check each time
_window_onMessageImplementation = _handleMessage;
_onReady();
}
else {
easyXDM.Debug.trace("received unexpected message: " + event.data + ", expected " + config.channel + "-ready");
}
}
/**
* Destroy all that we can destroy :)
*/
this.destroy = function(){
easyXDM.Debug.trace("destroying transport");
easyXDM.DomHelper.removeEventListener(window, "message", _window_onMessage);
if (config.local) {
_callerWindow.parentNode.removeChild(_callerWindow);
_callerWindow = null;
}
};
/**
* Sends the message using the postMethod method available on the window object
* @param {String} message The message to send
*/
this.postMessage = (function(){
// Set up the messaging differently dependin on being local or remote
if (config.local) {
_window_onMessageImplementation = _waitForReady;
_callerWindow = easyXDM.DomHelper.createFrame(easyXDM.Url.appendQueryParameters(config.remote, {
endpoint: easyXDM.Url.resolveUrl(config.local),
channel: config.channel
}), config.container);
return function(message){
easyXDM.Debug.trace("sending message '" + message + "' to iframe " + _targetOrigin);
_callerWindow.contentWindow.postMessage(config.channel + " " + message, _targetOrigin);
};
}
else {
_window_onMessageImplementation = _handleMessage;
easyXDM.Debug.trace("firing onReady");
window.parent.postMessage(config.channel + "-ready", _targetOrigin);
_onReady();
return function(message){
easyXDM.Debug.trace("sending message '" + message + "' to parent " + _targetOrigin);
window.parent.postMessage(config.channel + " " + message, _targetOrigin);
};
}
}());
},
/**
* @class easyXDM.transport.HashTransport
* @extends easyXDM.transport.ITransport
* HashTransport is a transport class that uses the IFrame URL Technique for communication
* http://msdn.microsoft.com/en-us/library/bb735305.aspx
* @constructor
* @param {easyXDM.transport.TransportConfiguration} config The transports configuration.
* @param {Function} onReady A method that should be called when the transport is ready
* @namespace easyXDM.transport
*/
HashTransport: function(config, onReady){
easyXDM.Debug.trace("easyXDM.transport.HashTransport.constructor");
var _timer, _pollInterval = config.interval || 300, _poll;
var _lastMsg = "#" + config.channel, _msgNr = 0, _listenerWindow, _callerWindow;
var _remoteUrl, _remoteOrigin = easyXDM.Url.getLocation(config.remote);
if (config.local) {
var parameters = {
endpoint: easyXDM.Url.resolveUrl(config.local),
channel: config.channel
};
_poll = (typeof config.container !== "undefined");
if (_poll) {
parameters.poll = 1;
easyXDM.Debug.trace("using polling");
}
_remoteUrl = easyXDM.Url.appendQueryParameters(config.remote, parameters);
}
else {
_listenerWindow = window;
_poll = (typeof easyXDM.Url.Query().poll !== "undefined");
if (_poll) {
easyXDM.Debug.trace("using polling");
}
_remoteUrl = config.remote + "#" + config.channel;
}
/**
* Checks location.hash for a new message and relays this to the receiver.
* @private
*/
function _checkForMessage(){
try {
if (_listenerWindow.location.hash && _listenerWindow.location.hash != _lastMsg) {
_lastMsg = _listenerWindow.location.hash;
easyXDM.Debug.trace("received message '" + _lastMsg + "' from " + _remoteOrigin);
config.onMessage(decodeURIComponent(_lastMsg.substring(_lastMsg.indexOf("_") + 1)), _remoteOrigin);
}
}
catch (ex) {
}
}
/**
* Calls the supplied onReady method
* We delay this so that the the call to createChannel or createTransport will have completed.
* @private
*/
function _onReady(){
if (config.local) {
_listenerWindow = easyXDM.transport.HashTransport.getWindow(config.channel);
}
if (_poll) {
easyXDM.Debug.trace("starting polling");
_timer = window.setInterval(function(){
_checkForMessage();
}, _pollInterval);
}
else {
easyXDM.DomHelper.addEventListener(_listenerWindow, "resize", _checkForMessage);
}
if (onReady) {
window.setTimeout(onReady, 10);
}
}
/**
* Sends a message by encoding and placing it in the hash part of _callerWindows url.
* We include a message number so that identical messages will be read as separate messages.
* @param {String} message The message to send
*/
this.postMessage = function(message){
easyXDM.Debug.trace("sending message '" + message + "' to " + _remoteOrigin);
_callerWindow.src = _remoteUrl + "#" + (_msgNr++) + "_" + encodeURIComponent(message);
if (!_poll) {
_callerWindow.width = _callerWindow.width > 75 ? 50 : 100;
}
};
/**
* Tries to clean up the DOM
*/
this.destroy = function(){
easyXDM.Debug.trace("destroying transport");
if (_poll) {
window.clearInterval(_timer);
}
else {
easyXDM.DomHelper.removeEventListener(_listenerWindow, "resize", _checkForMessage);
}
_callerWindow.parentNode.removeChild(_callerWindow);
_callerWindow = null;
};
if (config.local) {
// Register onReady callback in the library so that
// it can be called when hash.html has loaded.
easyXDM.transport.HashTransport.registerOnReady(config.channel, _onReady);
}
_callerWindow = easyXDM.DomHelper.createFrame(_remoteUrl, config.container, (config.local) ? null : _onReady);
}
};
/**
* Contains the callbacks used to notify local that the remote end is ready
*/
easyXDM.transport.HashTransport.callbacks = {};
/**
* Contains the proxy windows used to read messages from remote when
* using HashTransport.
*/
easyXDM.transport.HashTransport.windows = {};
/**
* Register a callback that should be called when the remote end of a channel is ready
* @param {String} channel
* @param {Function} callback
*/
easyXDM.transport.HashTransport.registerOnReady = function(channel, callback){
easyXDM.Debug.trace("registering onReady callback for channel " + channel);
easyXDM.transport.HashTransport.callbacks[channel] = callback;
};
/**
* Notify that a channel is ready and register a window to be used for reading messages
* for on the channel.
* @param {String} channel
* @param {Window} contentWindow
*/
easyXDM.transport.HashTransport.channelReady = function(channel, contentWindow){
easyXDM.transport.HashTransport.windows[channel] = contentWindow;
easyXDM.Debug.trace("executing onReady callback for channel " + channel);
var fn = easyXDM.transport.HashTransport.callbacks[channel];
if (fn) {
fn();
delete easyXDM.transport.HashTransport.callbacks[channel];
}
};
/**
* Returns the window associated with a channel
* @param {String} channel
* @return {Window} The window
*/
easyXDM.transport.HashTransport.getWindow = function(channel){
return easyXDM.transport.HashTransport.windows[channel];
};
easyXDM.configuration = {
/**
* @class easyXDM.configuration.TransportConfiguration
* The configuration for transport classes.
* @namespace easyXDM.configuration
*/
TransportConfiguration: {
/**
* The url of the remote endpoint
*/
remote: "",
/**
* The url of the local copy of hash.html
*/
local: "",
/**
* The method that should handle incoming messages
* @param {String} message The message
* @param {String} origin The origin of the message
*/
onMessage: function(message, origin){
}
},
/**
* @class easyXDM.configuration.ChannelConfiguration
* The channels configuration
* @extends easyXDM.configuration.TransportConfiguration
* @namespace easyXDM.configuration
*/
ChannelConfiguration: {
/**
* The serializer to use
* @type easyXDM.serializing.ISerializer
*/
converter: {}
},
/**
* @class easyXDM.configuration.InterfaceConfiguration
* The interface configuration
* @namespace easyXDM.configuration
*/
InterfaceConfiguration: {
/**
* The local property is of type {@link easyXDM.configuration.LocalConfiguration}
* @link {easyXDM.configuration.LocalConfiguration}
* @type easyXDM.configuration.LocalConfiguration
*/
local: {},
/**
* The remote property contains a list of method-definitions in the form of methodname:{description}
* @type easyXDM.configuration.RemoteConfiguration
*/
remote: {}
},
/**
* @class easyXDM.configuration.LocalConfiguration
* The configuration for the local property
* @namespace easyXDM.configuration
*/
LocalConfiguration: {
/**
* A method returning data
* @type easyXDM.configuration.Methods.LocalMethod
*/
methodName: {},
/**
* A method not returning any data
* @type easyXDM.configuration.Methods.LocalVoidMethod
*/
voidMethodName: {},
/**
* An asynchronous method that is unable to return data immediately
* This can for instance be a method using an xmlHttpRequest object to retrieve data
* @type easyXDM.configuration.Methods.LocalAsyncMethod
*/
asyncMethodName: {}
},
/**
* @class easyXDM.configuration.RemoteConfiguration
* The configuration for the remote property
* @namespace easyXDM.configuration
*/
RemoteConfiguration: {
/**
* Methods are by default expected to return data
* @type easyXDM.configuration.Methods.RemoteMethod
*/
methodName: {},
/**
* We do not expect any data back from this method
* @type easyXDM.configuration.Methods.RemoteVoidMethod
*/
voidMethodName: {},
/**
* We do not need to know that the remote method is implemented asynchronous
* @type easyXDM.configuration.Methods.RemoteAsyncMethod
*/
asyncMethodName: {}
},
/**
* Contains description on the various method descriptions
*/
Methods: {
/**
* @class easyXDM.configuration.Methods.Method
* The base method implementation
* @namespace easyXDM.configuration.Methods
*/
Method: {},
/**
* @class easyXDM.configuration.Methods.LocalMethod
* @extends easyXDM.configuration.Methods.Method
* A method returning data
* @namespace easyXDM.configuration.Methods
*/
LocalMethod: {
/**
* The implementation
* @param {Object} arg1
* @param {Object} arg2
* @param {Object} argN
* @return The methods return value
*/
method: function(arg1, arg2, argN){
}
},
/**
* @class easyXDM.configuration.Methods.LocalVoidMethod
* @extends easyXDM.configuration.Methods.Method
* A method not returning any data
* @namespace easyXDM.configuration.Methods
*/
LocalVoidMethod: {
/**
* If the method does not return anything then we mark it as void
* @property
*/
isVoid: true,
/**
* The implementation
* @param {Object} arg1
* @param {Object} arg2
* @param {Object} argN
*/
method: function(arg1, arg2, argN){
}
},
/**
* @class easyXDM.configuration.Methods.LocalAsyncMethod
* @extends easyXDM.configuration.Methods.Method
* An asynchronous method that is unable to return data immediately
* This can for instance be a method using an xmlHttpRequest object to retrieve data
* @namespace easyXDM.configuration.Methods
*/
LocalAsyncMethod: {
/**
* If the method is asyncronous we mark it as async
* This is so that the framework will know that it expects a callback function
*/
isAsync: true,
/**
* The implementation
* @param {Object} arg1
* @param {Object} arg2
* @param {Object} argN
* @param {Function} callback
*/
method: function(arg1, arg2, argN, callback){
}
},
/**
* @class easyXDM.configuration.Methods.RemoteMethod
* Methods are by default expected to return data
* @namespace easyXDM.configuration.Methods
*/
RemoteMethod: {},
/**
* @class easyXDM.configuration.Methods.RemoteVoidMethod
* @extends easyXDM.configuration.Methods.Method
* We do not expect any data back from this method
* @namespace easyXDM.configuration.Methods
*/
RemoteVoidMethod: {
/**
* We mark the method as void so that the framework will not wait for any response, and will not expect a callback method
*/
isVoid: true
},
/**
* @class easyXDM.configuration.Methods.RemoteAsyncMethod
* @extends easyXDM.configuration.Methods.Method
* We do not need to know that the remote method is implemented asynchronous
* @namespace easyXDM.configuration.Methods
*/
RemoteAsyncMethod: {}
}
};
/**
* @class easyXDM.Url
* Contains methods for dealing with url's
* @singleton
*/
easyXDM.Url = {
/**
* A hashtable that gives access to the documents query string
* The hashtable is cached internally
* @returns A hashtable populated with keys and values from the querystring
* @type {Object}
*/
Query: function(){
if (this._query) {
return this._query;
}
this._query = {};
var pair, key, value, search = location.search.substring(1).split("&");
for (var i = 0, len = search.length; i < len; i++) {
pair = search[i];
key = pair.substring(0, pair.indexOf("="));
value = pair.substring(key.length + 1);
this._query[key] = value;
}
return this._query;
},
/**
* Get the domain name from a url
* @param {String} url The url to extract the domain from
* @returns The domain part of the url
* @type {String}
*/
getDomainName: function(url){
var domain = url.substring(url.indexOf("//") + 2);
domain = domain.substring(0, domain.indexOf("/"));
var _indexOf = domain.indexOf(":");
if (_indexOf != -1) {
domain = domain.substring(0, _indexOf);
}
return domain;
},
/**
* 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
*/
getLocation: function(url){
var indexOf = url.indexOf("//");
var loc = url.substring(indexOf + 2);
loc = loc.substring(0, loc.indexOf("/"));
return url.substring(0, indexOf + 2) + loc;
},
/**
* Resolves a path to a complete url
* @param {String} url The path to resolve
* @return {String} The resolved url
*/
resolveUrl: function(url){
// If the url is a valid url we do nothing
if (url.match(/^(http||https):\/\//)) {
return url;
}
// If the url is relative to the root
if (url.substring(0, 1) == "/") {
return location.protocol + "//" + location.host + url;
}
// If the url is relative to the current directory
return location.href.substring(0, location.href.lastIndexOf("/") + 1) + url;
},
/**
* Appends the parameters to the given url.
* The base url can contain existing query parameters.
* @param {String} url The base url
* @param {Object} parameters The parameters to add
*/
appendQueryParameters: function(url, parameters){
var q = "";
for (var key in parameters) {
if (parameters.hasOwnProperty(key)) {
q += key + "=" + parameters[key] + "&";
}
}
return url + ((url.indexOf("?") == -1) ? "?" : "&") + q.substring(0, q.length - 1);
}
};
easyXDM.serializing = {
/**
* @class easyXDM.serializing.ISerializer
* The Interface implemented by all serializers.
* Only available in debug mode.
* @namespace easyXDM.serializing
*/
ISerializer: {
/**
* Serializes an object and returns it as a string
* @param {Object} data The data to serialize
* @returns The serialized string
* @type {String}
*/
stringify: function(data){
},
/**
* Deserializes a string and returns an object
* @param {String} message The string to deserialize
* @returns An object
* @type {Object}
*/
parse: function(message){
}
},
/**
* @class easyXDM.serializing.hashTableSerializer
* A serializer that can convert to and from hashtables
* It uses the same format as the query string for its serialized data
* @namespace easyXDM.serializing
*/
hashTableSerializer: {
/**
* Serializes a hashtable and returns it as a string
* @param {Object} data The data to serialize
* @returns The serialized string
* @type {String}
*/
stringify: function(data){
var message = "";
for (var key in data) {
if (data.hasOwnProperty(key)) {
message += key + "=" + escape(data[key]) + "&";
}
}
return message.substring(0, message.length - 1);
},
/**
* Deserializes a string and returns a hashtable
* @param {String} message The string to deserialize
* @returns An hashtable populated with key-value pairs
* @type {Object}
*/
parse: function(message){
var data = {};
var d = message.split("&");
var pair, key, value;
for (var i = 0, len = d.length; i < len; i++) {
pair = d[i];
key = pair.substring(0, pair.indexOf("="));
value = pair.substring(key.length + 1);
data[key] = unescape(value);
}
return data;
}
}
};