/errai-bus/src/main/java/org/jboss/errai/bus/public/js/pagebus_2.0.js
JavaScript | 6701 lines | 3952 code | 540 blank | 2209 comment | 561 complexity | 2712716bc6bad39a2d961a61505ccfb5 MD5 | raw file
Possible License(s): Apache-2.0
- /*
- * Copyright 2010 JBoss, a divison Red Hat, Inc
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- // prevent re-definition of the OpenAjax object
- if (!window["OpenAjax"]) {
- OpenAjax = new function() {
- this.hub = {};
- h = this.hub;
- h.implementer = "http://openajax.org";
- h.implVersion = "2.0";
- h.specVersion = "2.0";
- h.implExtraData = {};
- var libs = {};
- h.libraries = libs;
- var ooh = "org.openajax.hub.";
- h.registerLibrary = function(prefix, nsURL, version, extra) {
- libs[prefix] = {
- prefix: prefix,
- namespaceURI: nsURL,
- version: version,
- extraData: extra
- };
- this.publish(ooh + "registerLibrary", libs[prefix]);
- }
- h.unregisterLibrary = function(prefix) {
- this.publish(ooh + "unregisterLibrary", libs[prefix]);
- delete libs[prefix];
- }
- }
- /**
- * Error
- *
- * Standard Error names used when the standard functions need to throw Errors.
- */
- OpenAjax.hub.Error = {
- // Either a required argument is missing or an invalid argument was provided
- BadParameters: "OpenAjax.hub.Error.BadParameters",
- // The specified hub has been disconnected and cannot perform the requested
- // operation:
- Disconnected: "OpenAjax.hub.Error.Disconnected",
- // Container with specified ID already exists:
- Duplicate: "OpenAjax.hub.Error.Duplicate",
- // The specified ManagedHub has no such Container (or it has been removed)
- NoContainer: "OpenAjax.hub.Error.NoContainer",
- // The specified ManagedHub or Container has no such subscription
- NoSubscription: "OpenAjax.hub.Error.NoSubscription",
- // Permission denied by manager's security policy
- NotAllowed: "OpenAjax.hub.Error.NotAllowed",
- // Wrong communications protocol identifier provided by Container or HubClient
- WrongProtocol: "OpenAjax.hub.Error.WrongProtocol"
- };
- /**
- * SecurityAlert
- *
- * Standard codes used when attempted security violations are detected. Unlike
- * Errors, these codes are not thrown as exceptions but rather passed into the
- * SecurityAlertHandler function registered with the Hub instance.
- */
- OpenAjax.hub.SecurityAlert = {
- // Container did not load (possible frame phishing attack)
- LoadTimeout: "OpenAjax.hub.SecurityAlert.LoadTimeout",
- // Hub suspects a frame phishing attack against the specified container
- FramePhish: "OpenAjax.hub.SecurityAlert.FramePhish",
- // Hub detected a message forgery that purports to come to a specifed
- // container
- ForgedMsg: "OpenAjax.hub.SecurityAlert.ForgedMsg"
- };
- /**
- * Debugging Help
- *
- * OpenAjax.hub.enableDebug
- *
- * If OpenAjax.hub.enableDebug is set to true, then the "debugger" keyword
- * will get hit whenever a user callback throws an exception, thereby
- * bringing up the JavaScript debugger.
- */
- OpenAjax.hub._debugger = function() {
- if (OpenAjax.hub.enableDebug) debugger; // REMOVE ON BUILD
- }
- ////////////////////////////////////////////////////////////////////////////////
- /**
- * Hub interface
- *
- * Hub is implemented on the manager side by ManagedHub and on the client side
- * by ClientHub.
- */
- //OpenAjax.hub.Hub = function() {}
- /**
- * Subscribe to a topic.
- *
- * @param {String} topic
- * A valid topic string. MAY include wildcards.
- * @param {Function} onData
- * Callback function that is invoked whenever an event is
- * published on the topic
- * @param {Object} [scope]
- * When onData callback or onComplete callback is invoked,
- * the JavaScript "this" keyword refers to this scope object.
- * If no scope is provided, default is window.
- * @param {Function} [onComplete]
- * Invoked to tell the client application whether the
- * subscribe operation succeeded or failed.
- * @param {*} [subscriberData]
- * Client application provides this data, which is handed
- * back to the client application in the subscriberData
- * parameter of the onData callback function.
- *
- * @returns subscriptionID
- * Identifier representing the subscription. This identifier is an
- * arbitrary ID string that is unique within this Hub instance
- * @type {String}
- *
- * @throws {OpenAjax.hub.Error.Disconnected} if this Hub instance is not in CONNECTED state
- * @throws {OpenAjax.hub.Error.BadParameters} if the topic is invalid (e.g. contains an empty token)
- */
- //OpenAjax.hub.Hub.prototype.subscribe = function( topic, onData, scope, onComplete, subscriberData ) {}
- /**
- * Publish an event on a topic
- *
- * @param {String} topic
- * A valid topic string. MUST NOT include wildcards.
- * @param {*} data
- * Valid publishable data. To be portable across different
- * Container implementations, this value SHOULD be serializable
- * as JSON.
- *
- * @throws {OpenAjax.hub.Error.Disconnected} if this Hub instance is not in CONNECTED state
- * @throws {OpenAjax.hub.Error.BadParameters} if the topic cannot be published (e.g. contains
- * wildcards or empty tokens) or if the data cannot be published (e.g. cannot be serialized as JSON)
- */
- //OpenAjax.hub.Hub.prototype.publish = function( topic, data ) {}
- /**
- * Unsubscribe from a subscription
- *
- * @param {String} subscriptionID
- * A subscriptionID returned by Hub.subscribe()
- * @param {Function} [onComplete]
- * Callback function invoked when unsubscribe completes
- * @param {Object} [scope]
- * When onComplete callback function is invoked, the JavaScript "this"
- * keyword refers to this scope object.
- * If no scope is provided, default is window.
- *
- * @throws {OpenAjax.hub.Error.Disconnected} if this Hub instance is not in CONNECTED state
- * @throws {OpenAjax.hub.Error.NoSubscription} if no such subscription is found
- */
- //OpenAjax.hub.Hub.prototype.unsubscribe = function( subscriptionID, onComplete, scope ) {}
- /**
- * Return true if this Hub instance is in the Connected state.
- * Else returns false.
- *
- * This function can be called even if the Hub is not in a CONNECTED state.
- *
- * @returns Boolean
- * @type {Boolean}
- */
- //OpenAjax.hub.Hub.prototype.isConnected = function() {}
- /**
- * Returns the scope associated with this Hub instance and which will be used
- * with callback functions.
- *
- * This function can be called even if the Hub is not in a CONNECTED state.
- *
- * @returns scope object
- * @type {Object}
- */
- //OpenAjax.hub.Hub.prototype.getScope = function() {}
- /**
- * Returns the subscriberData parameter that was provided when
- * Hub.subscribe was called.
- *
- * @param {String} subscriberID
- * The subscriberID of a subscription
- *
- * @returns subscriberData
- * @type {*}
- *
- * @throws {OpenAjax.hub.Error.Disconnected} if this Hub instance is not in CONNECTED state
- * @throws {OpenAjax.hub.Error.NoSubscription} if there is no such subscription
- */
- //OpenAjax.hub.Hub.prototype.getSubscriberData = function(subscriberID) {}
- /**
- * Returns the scope associated with a specified subscription. This scope will
- * be used when invoking the 'onData' callback supplied to Hub.subscribe().
- *
- * @param {String} subscriberID
- * The subscriberID of a subscription
- *
- * @returns scope
- * @type {*}
- *
- * @throws {OpenAjax.hub.Error.Disconnected} if this Hub instance is not in CONNECTED state
- * @throws {OpenAjax.hub.Error.NoSubscription} if there is no such subscription
- */
- //OpenAjax.hub.Hub.prototype.getSubscriberScope = function(subscriberID) {}
- /**
- * Returns the params object associated with this Hub instance.
- * Allows mix-in code to access parameters passed into constructor that created
- * this Hub instance.
- *
- * @returns params the params object associated with this Hub instance
- * @type {Object}
- */
- //OpenAjax.hub.Hub.prototype.getParameters = function() {}
- ////////////////////////////////////////////////////////////////////////////////
- /**
- * HubClient interface
- *
- * Extends Hub interface.
- *
- * A HubClient implementation is typically specific to a particular
- * implementation of Container.
- */
- /**
- * Create a new HubClient. All HubClient constructors MUST have this
- * signature.
- * @constructor
- *
- * @param {Object} params
- * Parameters used to instantiate the HubClient.
- * Once the constructor is called, the params object belongs to the
- * HubClient. The caller MUST not modify it.
- * Implementations of HubClient may specify additional properties
- * for the params object, besides those identified below.
- *
- * @param {Function} params.HubClient.onSecurityAlert
- * Called when an attempted security breach is thwarted
- * @param {Object} [params.HubClient.scope]
- * Whenever one of the HubClient's callback functions is called,
- * references to "this" in the callback will refer to the scope object.
- * If not provided, the default is window.
- *
- * @throws {OpenAjax.hub.Error.BadParameters} if any of the required
- * parameters is missing, or if a parameter value is invalid in
- * some way.
- */
- //OpenAjax.hub.HubClient = function( params ) {}
- /**
- * Requests a connection to the ManagedHub, via the Container
- * associated with this HubClient.
- *
- * If the Container accepts the connection request, the HubClient's
- * state is set to CONNECTED and the HubClient invokes the
- * onComplete callback function.
- *
- * If the Container refuses the connection request, the HubClient
- * invokes the onComplete callback function with an error code.
- * The error code might, for example, indicate that the Container
- * is being destroyed.
- *
- * In most implementations, this function operates asynchronously,
- * so the onComplete callback function is the only reliable way to
- * determine when this function completes and whether it has succeeded
- * or failed.
- *
- * A client application may call HubClient.disconnect and then call
- * HubClient.connect.
- *
- * @param {Function} [onComplete]
- * Callback function to call when this operation completes.
- * @param {Object} [scope]
- * When the onComplete function is invoked, the JavaScript "this"
- * keyword refers to this scope object.
- * If no scope is provided, default is window.
- *
- * @throws {OpenAjax.hub.Error.Duplicate} if the HubClient is already connected
- */
- //OpenAjax.hub.HubClient.prototype.connect = function( onComplete, scope ) {}
- /**
- * Disconnect from the ManagedHub
- *
- * Disconnect immediately:
- *
- * 1. Sets the HubClient's state to DISCONNECTED.
- * 2. Causes the HubClient to send a Disconnect request to the
- * associated Container.
- * 3. Ensures that the client application will receive no more
- * onData or onComplete callbacks associated with this
- * connection, except for the disconnect function's own
- * onComplete callback.
- * 4. Automatically destroys all of the HubClient's subscriptions.
- *
- * In most implementations, this function operates asynchronously,
- * so the onComplete callback function is the only reliable way to
- * determine when this function completes and whether it has succeeded
- * or failed.
- *
- * A client application is allowed to call HubClient.disconnect and
- * then call HubClient.connect.
- *
- * @param {Function} [onComplete]
- * Callback function to call when this operation completes.
- * @param {Object} [scope]
- * When the onComplete function is invoked, the JavaScript "this"
- * keyword refers to the scope object.
- * If no scope is provided, default is window.
- *
- * @throws {OpenAjax.hub.Error.Disconnected} if the HubClient is already
- * disconnected
- */
- //OpenAjax.hub.HubClient.prototype.disconnect = function( onComplete, scope ) {}
- /**
- * If DISCONNECTED: Returns null
- * If CONNECTED: Returns the origin associated with the window containing the
- * Container associated with this HubClient instance. The origin has the format
- *
- * [protocol]://[host]
- *
- * where:
- *
- * [protocol] is "http" or "https"
- * [host] is the hostname of the partner page.
- *
- * @returns Partner's origin
- * @type {String}
- */
- //OpenAjax.hub.HubClient.prototype.getPartnerOrigin = function() {}
- /**
- * Returns the client ID of this HubClient
- *
- * @returns clientID
- * @type {String}
- */
- //OpenAjax.hub.HubClient.prototype.getClientID = function() {}
- ////////////////////////////////////////////////////////////////////////////////
- /**
- * OpenAjax.hub.ManagedHub
- *
- * Managed hub API for the manager application and for Containers.
- *
- * Implements OpenAjax.hub.Hub.
- */
- /**
- * Create a new ManagedHub instance
- * @constructor
- *
- * This constructor automatically sets the ManagedHub's state to
- * CONNECTED.
- *
- * @param {Object} params
- * Parameters used to instantiate the ManagedHub.
- * Once the constructor is called, the params object belongs exclusively to
- * the ManagedHub. The caller MUST not modify it.
- *
- * The params object may contain the following properties:
- *
- * @param {Function} params.onPublish
- * Callback function that is invoked whenever a
- * data value published by a Container is about
- * to be delivered to some (possibly the same) Container.
- * This callback function implements a security policy;
- * it returns true if the delivery of the data is
- * permitted and false if permission is denied.
- * @param {Function} params.onSubscribe
- * Called whenever a Container tries to subscribe
- * on behalf of its client.
- * This callback function implements a security policy;
- * it returns true if the subscription is permitted
- * and false if permission is denied.
- * @param {Function} [params.onUnsubscribe]
- * Called whenever a Container unsubscribes on behalf of its client.
- * Unlike the other callbacks, onUnsubscribe is intended only for
- * informative purposes, and is not used to implement a security
- * policy.
- * @param {Object} [params.scope]
- * Whenever one of the ManagedHub's callback functions is called,
- * references to the JavaScript "this" keyword in the callback
- * function refer to this scope object
- * If no scope is provided, default is window.
- * @param {Function} [params.log] Optional logger function. Would
- * be used to log to console.log or equivalent.
- *
- * @throws {OpenAjax.hub.Error.BadParameters} if any of the required
- * parameters are missing
- */
- OpenAjax.hub.ManagedHub = function(params) {
- if (! params || ! params.onPublish || ! params.onSubscribe)
- throw new Error(OpenAjax.hub.Error.BadParameters);
- this._p = params;
- this._onUnsubscribe = params.onUnsubscribe ? params.onUnsubscribe : null;
- this._scope = params.scope || window;
- if (params.log) {
- var scope = this._scope;
- var logfunc = params.log;
- this._log = function(msg) {
- logfunc.call(scope, "ManagedHub: " + msg);
- };
- } else {
- this._log = function() {
- };
- }
- this._subscriptions = { c:{}, s:null };
- this._containers = {};
- // Sequence # used to create IDs that are unique within this hub
- this._seq = 0;
- this._active = true;
- this._isPublishing = false;
- this._pubQ = [];
- }
- /**
- * Subscribe to a topic on behalf of a Container. Called only by
- * Container implementations, NOT by manager applications.
- *
- * This function:
- * 1. Checks with the ManagedHub's onSubscribe security policy
- * to determine whether this Container is allowed to subscribe
- * to this topic.
- * 2. If the subscribe operation is permitted, subscribes to the
- * topic and returns the ManagedHub's subscription ID for this
- * subscription.
- * 3. If the subscribe operation is not permitted, throws
- * OpenAjax.hub.Error.NotAllowed.
- *
- * When data is published on the topic, the ManagedHub's
- * onPublish security policy will be invoked to ensure that
- * this Container is permitted to receive the published data.
- * If the Container is allowed to receive the data, then the
- * Container's sendToClient function will be invoked.
- *
- * When a Container needs to create a subscription on behalf of
- * its client, the Container MUST use this function to create
- * the subscription.
- *
- * @param {OpenAjax.hub.Container} container
- * A Container
- * @param {String} topic
- * A valid topic
- * @param {String} containerSubID
- * Arbitrary string ID that the Container uses to
- * represent the subscription. Must be unique within the
- * context of the Container
- *
- * @returns managerSubID
- * Arbitrary string ID that this ManagedHub uses to
- * represent the subscription. Will be unique within the
- * context of this ManagedHub
- * @type {String}
- *
- * @throws {OpenAjax.hub.Error.Disconnected} if this.isConnected() returns false
- * @throws {OpenAjax.hub.Error.NotAllowed} if subscription request is denied by the onSubscribe security policy
- * @throws {OpenAjax.hub.Error.BadParameters} if one of the parameters, e.g. the topic, is invalid
- */
- OpenAjax.hub.ManagedHub.prototype.subscribeForClient = function(container, topic, containerSubID) {
- this._assertConn();
- // check subscribe permission
- if (this._invokeOnSubscribe(topic, container)) {
- // return ManagedHub's subscriptionID for this subscription
- return this._subscribe(topic, this._sendToClient, this, { c: container, sid: containerSubID });
- }
- throw new Error(OpenAjax.hub.Error.NotAllowed);
- }
- /**
- * Unsubscribe from a subscription on behalf of a Container. Called only by
- * Container implementations, NOT by manager application code.
- *
- * This function:
- * 1. Destroys the specified subscription
- * 2. Calls the ManagedHub's onUnsubscribe callback function
- *
- * This function can be called even if the ManagedHub is not in a CONNECTED state.
- *
- * @param {OpenAjax.hub.Container} container
- * container instance that is unsubscribing
- * @param {String} managerSubID
- * opaque ID of a subscription, returned by previous call to subscribeForClient()
- *
- * @throws {OpenAjax.hub.Error.NoSubscription} if subscriptionID does not refer to a valid subscription
- */
- OpenAjax.hub.ManagedHub.prototype.unsubscribeForClient = function(container, managerSubID) {
- this._unsubscribe(managerSubID);
- this._invokeOnUnsubscribe(container, managerSubID);
- }
- /**
- * Publish data on a topic on behalf of a Container. Called only by
- * Container implementations, NOT by manager application code.
- *
- * @param {OpenAjax.hub.Container} container
- * Container on whose behalf data should be published
- * @param {String} topic
- * Valid topic string. Must NOT contain wildcards.
- * @param {*} data
- * Valid publishable data. To be portable across different
- * Container implementations, this value SHOULD be serializable
- * as JSON.
- *
- * @throws {OpenAjax.hub.Error.Disconnected} if this.isConnected() returns false
- * @throws {OpenAjax.hub.Error.BadParameters} if one of the parameters, e.g. the topic, is invalid
- */
- OpenAjax.hub.ManagedHub.prototype.publishForClient = function(container, topic, data) {
- this._assertConn();
- this._publish(topic, data, container);
- }
- /**
- * Destroy this ManagedHub
- *
- * 1. Sets state to DISCONNECTED. All subsequent attempts to add containers,
- * publish or subscribe will throw the Disconnected error. We will
- * continue to allow "cleanup" operations such as removeContainer
- * and unsubscribe, as well as read-only operations such as
- * isConnected
- * 2. Remove all Containers associated with this ManagedHub
- */
- OpenAjax.hub.ManagedHub.prototype.disconnect = function() {
- this._active = false;
- for (var c in this._containers) {
- this.removeContainer(this._containers[c]);
- }
- }
- /**
- * Get a container belonging to this ManagedHub by its clientID, or null
- * if this ManagedHub has no such container
- *
- * This function can be called even if the ManagedHub is not in a CONNECTED state.
- *
- * @param {String} containerId
- * Arbitrary string ID associated with the container
- *
- * @returns container associated with given ID
- * @type {OpenAjax.hub.Container}
- */
- OpenAjax.hub.ManagedHub.prototype.getContainer = function(containerId) {
- var container = this._containers[containerId];
- return container ? container : null;
- }
- /**
- * Returns an array listing all containers belonging to this ManagedHub.
- * The order of the Containers in this array is arbitrary.
- *
- * This function can be called even if the ManagedHub is not in a CONNECTED state.
- *
- * @returns container array
- * @type {OpenAjax.hub.Container[]}
- */
- OpenAjax.hub.ManagedHub.prototype.listContainers = function() {
- var res = [];
- for (var c in this._containers) {
- res.push(this._containers[c]);
- }
- return res;
- }
- /**
- * Add a container to this ManagedHub.
- *
- * This function should only be called by a Container constructor.
- *
- * @param {OpenAjax.hub.Container} container
- * A Container to be added to this ManagedHub
- *
- * @throws {OpenAjax.hub.Error.Duplicate} if there is already a Container
- * in this ManagedHub whose clientId is the same as that of container
- * @throws {OpenAjax.hub.Error.Disconnected} if this.isConnected() returns false
- */
- OpenAjax.hub.ManagedHub.prototype.addContainer = function(container) {
- this._assertConn();
- var containerId = container.getClientID();
- if (this._containers[containerId]) {
- throw new Error(OpenAjax.hub.Error.Duplicate);
- }
- this._containers[containerId] = container;
- }
- /**
- * Remove a container from this ManagedHub immediately
- *
- * This function can be called even if the ManagedHub is not in a CONNECTED state.
- *
- * @param {OpenAjax.hub.Container} container
- * A Container to be removed from this ManagedHub
- *
- * @throws {OpenAjax.hub.Error.NoContainer} if no such container is found
- */
- OpenAjax.hub.ManagedHub.prototype.removeContainer = function(container) {
- var containerId = container.getClientID();
- if (! this._containers[ containerId ]) {
- throw new Error(OpenAjax.hub.Error.NoContainer);
- }
- container.remove();
- delete this._containers[ containerId ];
- }
- /*** OpenAjax.hub.Hub interface implementation ***/
- /**
- * Subscribe to a topic.
- *
- * This implementation of Hub.subscribe is synchronous. When subscribe
- * is called:
- *
- * 1. The ManagedHub's onSubscribe callback is invoked. The
- * container parameter is null, because the manager application,
- * rather than a container, is subscribing.
- * 2. If onSubscribe returns true, then the subscription is created.
- * 3. The onComplete callback is invoked.
- * 4. Then this function returns.
- *
- * @param {String} topic
- * A valid topic string. MAY include wildcards.
- * @param {Function} onData
- * Callback function that is invoked whenever an event is
- * published on the topic
- * @param {Object} [scope]
- * When onData callback or onComplete callback is invoked,
- * the JavaScript "this" keyword refers to this scope object.
- * If no scope is provided, default is window.
- * @param {Function} [onComplete]
- * Invoked to tell the client application whether the
- * subscribe operation succeeded or failed.
- * @param {*} [subscriberData]
- * Client application provides this data, which is handed
- * back to the client application in the subscriberData
- * parameter of the onData and onComplete callback functions.
- *
- * @returns subscriptionID
- * Identifier representing the subscription. This identifier is an
- * arbitrary ID string that is unique within this Hub instance
- * @type {String}
- *
- * @throws {OpenAjax.hub.Error.Disconnected} if this Hub instance is not in CONNECTED state
- * @throws {OpenAjax.hub.Error.BadParameters} if the topic is invalid (e.g. contains an empty token)
- */
- OpenAjax.hub.ManagedHub.prototype.subscribe = function(topic, onData, scope, onComplete, subscriberData) {
- this._assertConn();
- this._assertSubTopic(topic);
- if (! onData) {
- throw new Error(OpenAjax.hub.Error.BadParameters);
- }
- // check subscribe permission
- if (! this._invokeOnSubscribe(topic, null)) {
- this._invokeOnComplete(onComplete, scope, null, false, OpenAjax.hub.Error.NotAllowed);
- return;
- }
- // on publish event, check publish permissions
- scope = scope || window;
- var that = this;
- function publishCB(topic, data, sd, pcont) {
- if (that._invokeOnPublish(topic, data, pcont, null)) {
- try {
- onData.call(scope, topic, data, subscriberData);
- } catch(e) {
- OpenAjax.hub._debugger();
- that._log("caught error from onData callback to Hub.subscribe(): " + e.message);
- }
- }
- }
- var subID = this._subscribe(topic, publishCB, scope, subscriberData);
- this._invokeOnComplete(onComplete, scope, subID, true);
- return subID;
- }
- /**
- * Publish an event on a topic
- *
- * This implementation of Hub.publish is synchronous. When publish
- * is called:
- *
- * 1. The target subscriptions are identified.
- * 2. For each target subscription, the ManagedHub's onPublish
- * callback is invoked. Data is only delivered to a target
- * subscription if the onPublish callback returns true.
- * The pcont parameter of the onPublish callback is null.
- * This is because the ManagedHub, rather than a container,
- * is publishing the data.
- *
- * @param {String} topic
- * A valid topic string. MUST NOT include wildcards.
- * @param {*} data
- * Valid publishable data. To be portable across different
- * Container implementations, this value SHOULD be serializable
- * as JSON.
- *
- * @throws {OpenAjax.hub.Error.Disconnected} if this Hub instance is not in CONNECTED state
- * @throws {OpenAjax.hub.Error.BadParameters} if the topic cannot be published (e.g. contains
- * wildcards or empty tokens) or if the data cannot be published (e.g. cannot be serialized as JSON)
- */
- OpenAjax.hub.ManagedHub.prototype.publish = function(topic, data) {
- this._assertConn();
- this._assertPubTopic(topic);
- this._publish(topic, data, null);
- }
- /**
- * Unsubscribe from a subscription
- *
- * This implementation of Hub.unsubscribe is synchronous. When unsubscribe
- * is called:
- *
- * 1. The subscription is destroyed.
- * 2. The ManagedHub's onUnsubscribe callback is invoked, if there is one.
- * 3. The onComplete callback is invoked.
- * 4. Then this function returns.
- *
- * @param {String} subscriptionID
- * A subscriptionID returned by Hub.subscribe()
- * @param {Function} [onComplete]
- * Callback function invoked when unsubscribe completes
- * @param {Object} [scope]
- * When onComplete callback function is invoked, the JavaScript "this"
- * keyword refers to this scope object.
- * If no scope is provided, default is window.
- *
- * @throws {OpenAjax.hub.Error.Disconnected} if this Hub instance is not in CONNECTED state
- * @throws {OpenAjax.hub.Error.NoSubscription} if no such subscription is found
- */
- OpenAjax.hub.ManagedHub.prototype.unsubscribe = function(subscriptionID, onComplete, scope) {
- this._assertConn();
- if (typeof subscriptionID === "undefined" || subscriptionID == null) {
- throw new Error(OpenAjax.hub.Error.BadParameters);
- }
- this._unsubscribe(subscriptionID);
- this._invokeOnUnsubscribe(null, subscriptionID);
- this._invokeOnComplete(onComplete, scope, subscriptionID, true);
- }
- /**
- * Returns true if disconnect() has NOT been called on this ManagedHub,
- * else returns false
- *
- * @returns Boolean
- * @type {Boolean}
- */
- OpenAjax.hub.ManagedHub.prototype.isConnected = function() {
- return this._active;
- }
- /**
- * Returns the scope associated with this Hub instance and which will be used
- * with callback functions.
- *
- * This function can be called even if the Hub is not in a CONNECTED state.
- *
- * @returns scope object
- * @type {Object}
- */
- OpenAjax.hub.ManagedHub.prototype.getScope = function() {
- return this._scope;
- }
- /**
- * Returns the subscriberData parameter that was provided when
- * Hub.subscribe was called.
- *
- * @param subscriberID
- * The subscriberID of a subscription
- *
- * @returns subscriberData
- * @type {*}
- *
- * @throws {OpenAjax.hub.Error.Disconnected} if this Hub instance is not in CONNECTED state
- * @throws {OpenAjax.hub.Error.NoSubscription} if there is no such subscription
- */
- OpenAjax.hub.ManagedHub.prototype.getSubscriberData = function(subscriberID) {
- this._assertConn();
- var path = subscriberID.split(".");
- var sid = path.pop();
- var sub = this._getSubscriptionObject(this._subscriptions, path, 0, sid);
- if (sub)
- return sub.data;
- throw new Error(OpenAjax.hub.Error.NoSubscription);
- }
- /**
- * Returns the scope associated with a specified subscription. This scope will
- * be used when invoking the 'onData' callback supplied to Hub.subscribe().
- *
- * @param subscriberID
- * The subscriberID of a subscription
- *
- * @returns scope
- * @type {*}
- *
- * @throws {OpenAjax.hub.Error.Disconnected} if this Hub instance is not in CONNECTED state
- * @throws {OpenAjax.hub.Error.NoSubscription} if there is no such subscription
- */
- OpenAjax.hub.ManagedHub.prototype.getSubscriberScope = function(subscriberID) {
- this._assertConn();
- var path = subscriberID.split(".");
- var sid = path.pop();
- var sub = this._getSubscriptionObject(this._subscriptions, path, 0, sid);
- if (sub)
- return sub.scope;
- throw new Error(OpenAjax.hub.Error.NoSubscription);
- }
- /**
- * Returns the params object associated with this Hub instance.
- * Allows mix-in code to access parameters passed into constructor that created
- * this Hub instance.
- *
- * @returns params the params object associated with this Hub instance
- * @type {Object}
- */
- OpenAjax.hub.ManagedHub.prototype.getParameters = function() {
- return this._p;
- }
- /* PRIVATE FUNCTIONS */
- /**
- * Send a message to a container's client.
- * This is an OAH subscriber's data callback. It is private to ManagedHub
- * and serves as an adapter between the OAH 1.0 API and Container.sendToClient.
- *
- * @param {String} topic Topic on which data was published
- * @param {Object} data Data to be delivered to the client
- * @param {Object} sd Object containing properties
- * c: container to which data must be sent
- * sid: subscription ID within that container
- * @param {Object} pcont Publishing container, or null if this data was
- * published by the manager
- */
- OpenAjax.hub.ManagedHub.prototype._sendToClient = function(topic, data, sd, pcont) {
- if (!this.isConnected()) {
- return;
- }
- if (this._invokeOnPublish(topic, data, pcont, sd.c)) {
- sd.c.sendToClient(topic, data, sd.sid);
- }
- }
- OpenAjax.hub.ManagedHub.prototype._assertConn = function() {
- if (!this.isConnected()) {
- throw new Error(OpenAjax.hub.Error.Disconnected);
- }
- }
- OpenAjax.hub.ManagedHub.prototype._assertPubTopic = function(topic) {
- if ((topic == null) || (topic == "") || (topic.indexOf("*") != -1) ||
- (topic.indexOf("..") != -1) || (topic.charAt(0) == ".") ||
- (topic.charAt(topic.length - 1) == ".")) {
- throw new Error(OpenAjax.hub.Error.BadParameters);
- }
- }
- OpenAjax.hub.ManagedHub.prototype._assertSubTopic = function(topic) {
- if (! topic) {
- throw new Error(OpenAjax.hub.Error.BadParameters);
- }
- var path = topic.split(".");
- var len = path.length;
- for (var i = 0; i < len; i++) {
- var p = path[i];
- if ((p == "") ||
- ((p.indexOf("*") != -1) && (p != "*") && (p != "**"))) {
- throw new Error(OpenAjax.hub.Error.BadParameters);
- }
- if ((p == "**") && (i < len - 1)) {
- throw new Error(OpenAjax.hub.Error.BadParameters);
- }
- }
- }
- OpenAjax.hub.ManagedHub.prototype._invokeOnComplete = function(func, scope, item, success, errorCode) {
- if (func) { // onComplete is optional
- try {
- scope = scope || window;
- func.call(scope, item, success, errorCode);
- } catch(e) {
- OpenAjax.hub._debugger();
- this._log("caught error from onComplete callback: " + e.message);
- }
- }
- }
- OpenAjax.hub.ManagedHub.prototype._invokeOnPublish = function(topic, data, pcont, scont) {
- try {
- return this._p.onPublish.call(this._scope, topic, data, pcont, scont);
- } catch(e) {
- OpenAjax.hub._debugger();
- this._log("caught error from onPublish callback to constructor: " + e.message);
- }
- return false;
- }
- OpenAjax.hub.ManagedHub.prototype._invokeOnSubscribe = function(topic, container) {
- try {
- return this._p.onSubscribe.call(this._scope, topic, container);
- } catch(e) {
- OpenAjax.hub._debugger();
- this._log("caught error from onSubscribe callback to constructor: " + e.message);
- }
- return false;
- }
- OpenAjax.hub.ManagedHub.prototype._invokeOnUnsubscribe = function(container, managerSubID) {
- if (this._onUnsubscribe) {
- var topic = managerSubID.slice(0, managerSubID.lastIndexOf("."));
- try {
- this._onUnsubscribe.call(this._scope, topic, container);
- } catch(e) {
- OpenAjax.hub._debugger();
- this._log("caught error from onUnsubscribe callback to constructor: " + e.message);
- }
- }
- }
- OpenAjax.hub.ManagedHub.prototype._subscribe = function(topic, onData, scope, subscriberData) {
- var handle = topic + "." + this._seq;
- var sub = { scope: scope, cb: onData, data: subscriberData, sid: this._seq++ };
- var path = topic.split(".");
- this._recursiveSubscribe(this._subscriptions, path, 0, sub);
- return handle;
- }
- OpenAjax.hub.ManagedHub.prototype._recursiveSubscribe = function(tree, path, index, sub) {
- var token = path[index];
- if (index == path.length) {
- sub.next = tree.s;
- tree.s = sub;
- } else {
- if (typeof tree.c == "undefined") {
- tree.c = {};
- }
- if (typeof tree.c[token] == "undefined") {
- tree.c[token] = { c: {}, s: null };
- this._recursiveSubscribe(tree.c[token], path, index + 1, sub);
- } else {
- this._recursiveSubscribe(tree.c[token], path, index + 1, sub);
- }
- }
- }
- OpenAjax.hub.ManagedHub.prototype._publish = function(topic, data, pcont) {
- // if we are currently handling a publish event, then queue this request
- // and handle later, one by one
- if (this._isPublishing) {
- this._pubQ.push({ t: topic, d: data, p: pcont });
- return;
- }
- this._safePublish(topic, data, pcont);
- while (this._pubQ.length > 0) {
- var pub = this._pubQ.shift();
- this._safePublish(pub.t, pub.d, pub.p);
- }
- }
- OpenAjax.hub.ManagedHub.prototype._safePublish = function(topic, data, pcont) {
- this._isPublishing = true;
- var path = topic.split(".");
- this._recursivePublish(this._subscriptions, path, 0, topic, data, pcont);
- this._isPublishing = false;
- }
- OpenAjax.hub.ManagedHub.prototype._recursivePublish = function(tree, path, index, name, msg, pcont) {
- if (typeof tree != "undefined") {
- var node;
- if (index == path.length) {
- node = tree;
- } else {
- this._recursivePublish(tree.c[path[index]], path, index + 1, name, msg, pcont);
- this._recursivePublish(tree.c["*"], path, index + 1, name, msg, pcont);
- node = tree.c["**"];
- }
- if (typeof node != "undefined") {
- var sub = node.s;
- while (sub) {
- var sc = sub.scope;
- var cb = sub.cb;
- var d = sub.data;
- var sid = sub.sid;
- if (typeof cb == "string") {
- // get a function object
- cb = sc[cb];
- }
- cb.call(sc, name, msg, d, pcont);
- sub = sub.next;
- }
- }
- }
- }
- OpenAjax.hub.ManagedHub.prototype._unsubscribe = function(subscriptionID) {
- var path = subscriptionID.split(".");
- var sid = path.pop();
- if (! this._recursiveUnsubscribe(this._subscriptions, path, 0, sid)) {
- throw new Error(OpenAjax.hub.Error.NoSubscription);
- }
- }
- /**
- * @returns 'true' if properly unsubscribed; 'false' otherwise
- */
- OpenAjax.hub.ManagedHub.prototype._recursiveUnsubscribe = function(tree, path, index, sid) {
- if (typeof tree == "undefined") {
- return false;
- }
- if (index < path.length) {
- var childNode = tree.c[path[index]];
- if (! childNode) {
- return false;
- }
- this._recursiveUnsubscribe(childNode, path, index + 1, sid);
- if (childNode.s == null) {
- for (var x in childNode.c) {
- return true;
- }
- delete tree.c[path[index]];
- }
- } else {
- var sub = tree.s;
- var sub_prev = null;
- var found = false;
- while (sub) {
- if (sid == sub.sid) {
- found = true;
- if (sub == tree.s) {
- tree.s = sub.next;
- } else {
- sub_prev.next = sub.next;
- }
- break;
- }
- sub_prev = sub;
- sub = sub.next;
- }
- if (! found) {
- return false;
- }
- }
- return true;
- }
- OpenAjax.hub.ManagedHub.prototype._getSubscriptionObject = function(tree, path, index, sid) {
- if (typeof tree != "undefined") {
- if (index < path.length) {
- var childNode = tree.c[path[index]];
- return this._getSubscriptionObject(childNode, path, index + 1, sid);
- }
- var sub = tree.s;
- while (sub) {
- if (sid == sub.sid) {
- return sub;
- }
- sub = sub.next;
- }
- }
- return null;
- }
- ////////////////////////////////////////////////////////////////////////////////
- /**
- * Container
- * @constructor
- *
- * Container represents an instance of a manager-side object that contains and
- * communicates with a single client of the hub. The container might be an inline
- * container, an iframe FIM container, or an iframe PostMessage container, or
- * it might be an instance of some other implementation.
- *
- * @param {OpenAjax.hub.ManagedHub} hub
- * Managed Hub instance
- * @param {String} clientID
- * A string ID that identifies a particular client of a Managed Hub. Unique
- * within the context of the ManagedHub.
- * @param {Object} params
- * Parameters used to instantiate the Container.
- * Once the constructor is called, the params object belongs exclusively to
- * the Container. The caller MUST not modify it.
- * Implementations of Container may specify additional properties
- * for the params object, besides those identified below.
- * The following params properties MUST be supported by all Container
- * implementations:
- * @param {Function} params.Container.onSecurityAlert
- * Called when an attempted security breach is thwarted. Function is defined
- * as follows: function(container, securityAlert)
- * @param {Function} [params.Container.onConnect]
- * Called when the client connects to the Managed Hub. Function is defined
- * as follows: function(container)
- * @param {Function} [params.Container.onDisconnect]
- * Called when the client disconnects from the Managed Hub. Function is
- * defined as follows: function(container)
- * @param {Object} [params.Container.scope]
- * Whenever one of the Container's callback functions is called, references
- * to "this" in the callback will refer to the scope object. If no scope is
- * provided, default is window.
- * @param {Function} [params.Container.log]
- * Optional logger function. Would be used to log to console.log or
- * equivalent.
- *
- * @throws {OpenAjax.hub.Error.BadParameters} if required params are not
- * present or null
- * @throws {OpenAjax.hub.Error.Duplicate} if a Container with this clientID
- * already exists in the given Managed Hub
- * @throws {OpenAjax.hub.Error.Disconnected} if ManagedHub is not connected
- */
- //OpenAjax.hub.Container = function( hub, clientID, params ) {}
- /**
- * Send a message to the client inside this container. This function MUST only
- * be called by ManagedHub.
- *
- * @param {String} topic
- * The topic name for the published message
- * @param {*} data
- * The payload. Can be any JSON-serializable value.
- * @param {String} containerSubscriptionId
- * Container's ID for a subscription, from previous call to
- * subscribeForClient()
- */
- //OpenAjax.hub.Container.prototype.sendToClient = function( topic, data, containerSubscriptionId ) {}
- /**
- * Shut down a container. remove does all of the following:
- * - disconnects container from HubClient
- * - unsubscribes from all of its existing subscriptions in the ManagedHub
- *
- * This function is only called by ManagedHub.removeContainer
- * Calling this function does NOT cause the container's onDisconnect callback to
- * be invoked.
- */
- //OpenAjax.hub.Container.prototype.remove = function() {}
- /**
- * Returns true if the given client is connected to the managed hub.
- * Else returns false.
- *
- * @returns true if the client is connected to the managed hub
- * @type boolean
- */
- //OpenAjax.hub.Container.prototype.isConnected = function() {}
- /**
- * Returns the clientID passed in when this Container was instantiated.
- *
- * @returns The clientID
- * @type {String}
- */
- //OpenAjax.hub.Container.prototype.getClientID = function() {}
- /**
- * If DISCONNECTED:
- * Returns null
- * If CONNECTED:
- * Returns the origin associated with the window containing the HubClient
- * associated with this Container instance. The origin has the format
- *
- * [protocol]://[host]
- *
- * where:
- *
- * [protocol] is "http" or "https"
- * [host] is the hostname of the partner page.
- *
- * @returns Partner's origin
- * @type {String}
- */
- //OpenAjax.hub.Container.prototype.getPartnerOrigin = function() {}
- /**
- * Returns the params object associated with this Container instance.
- * Allows mix-in code to access parameters passed into constructor that created
- * this Container instance.
- *
- * @returns params
- * The params object associated with this Container instance
- * @type {Object}
- */
- //OpenAjax.hub.Container.prototype.getParameters = function() {}
- /**
- * Returns the ManagedHub to which this Container belongs.
- *
- * @returns ManagedHub
- * The ManagedHub object associated with this Container instance
- * @type {OpenAjax.hub.ManagedHub}
- */
- //OpenAjax.hub.Container.prototype.getHub = function() {}
- ////////////////////////////////////////////////////////////////////////////////
- /*
- * Unmanaged Hub
- */
- /**
- * OpenAjax.hub._hub is the default ManagedHub instance that we use to
- * provide OAH 1.0 behavior.
- */
- OpenAjax.hub._hub = new OpenAjax.hub.ManagedHub({
- onSubscribe: function(topic, ctnr) {
- return true;
- },
- onPublish: function(topic, data, pcont, scont) {
- return true;
- }
- });
- /**
- * Subscribe to a topic.
- *
- * @param {String} topic
- * A valid topic string. MAY include wildcards.
- * @param {Function|String} onData
- * Callback function that is invoked whenever an event is published on the
- * topic. If 'onData' is a string, then it represents the name of a
- * function on the 'scope' object.
- * @param {Object} [scope]
- * When onData callback is invoked,
- * the JavaScript "this" keyword refers to this scope object.
- * If no scope is provided, default is window.
- * @param {*} [subscriberData]
- * Client application provides this data, which is handed
- * back to the client application in the subscriberData
- * parameter of the onData callback function.
- *
- * @returns {String} Identifier representing the subscription.
- *
- * @throws {OpenAjax.hub.Error.BadParameters} if the topic is invalid
- * (e.g.contains an empty token)
- */
- OpenAjax.hub.subscribe = function(topic, onData, scope, subscriberData) {
- // resolve the 'onData' function if it is a string
- if (typeof onData === "string") {
- scope = scope || window;
- onData = scope[ onData ] || null;
- }
- return OpenAjax.hub._hub.subscribe(topic, onData, scope, null, subscriberData);
- }
- /**
- * Unsubscribe from a subscription.
- *
- * @param {String} subscriptionID
- * Subscription identifier returned by subscribe()
- *
- * @throws {OpenAjax.hub.Error.NoSubscription} if no such subscription is found
- */
- OpenAjax.hub.unsubscribe = function(subscriptionID) {
- return OpenAjax.hub._hub.unsubscribe(subscriptionID);
- }
- /**
- * Publish an event on a topic.
- *
- * @param {String} topic
- * A valid topic string. MUST NOT include wildcards.
- * @param {*} data
- * Valid publishable data.
- *
- * @throws {OpenAjax.hub.Error.BadParameters} if the topic cannot be published
- * (e.g. contains wildcards or empty tokens)
- */
- OpenAjax.hub.publish = function(topic, data) {
- OpenAjax.hub._hub.publish(topic, data);
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Register the OpenAjax Hub itself as a library.
- OpenAjax.hub.registerLibrary("OpenAjax", "http://openajax.org/hub", "2.0", {});
- } // !window["OpenAjax"]
- ////////////////////////////////////////////////////////////////////////////////
- /*
- Copyright 2006-2009 OpenAjax Alliance
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- /**
- * Create a new Inline Container.
- * @constructor
- * @extends OpenAjax.hub.Container
- *
- * InlineContainer implements the Container interface to provide a container
- * that places components within the same browser frame as the main mashup
- * application. As such, this container does not isolate client components into
- * secure sandboxes.
- *
- * @param {OpenAjax.hub.ManagedHub} hub
- * Managed Hub instance to which this Container belongs
- * @param {String} clientID
- * A string ID that identifies a particular client of a Managed Hub. Unique
- * within the context of the ManagedHub.
- * @param {Object} params
- * Parameters used to instantiate the InlineContainer.
- * Once the constructor is called, the params object belongs exclusively to
- * the InlineContainer. The caller MUST not modify it.
- * The following are the pre-defined properties on params:
- * @param {Function} params.Container.onSecurityAlert
- * Called when an attempted security breach is thwarted. Function is defined
- * as follows: function(container, securityAlert)
- * @param {Function} [params.Container.onConnect]
- * Called when the client connects to the Managed Hub. Function is defined
- * as follows: function(container)
- * @param {Function} [params.Container.onDisconnect]
- * Called when the client disconnects from the Managed Hub. Function is
- * defined as follows: function(container)
- * @param {Object} [params.Container.scope]
- * Whenever one of the Container's callback functions is called, references
- * to "this" in the callback will refer to the scope object. If no scope is
- * provided, default is window.
- * @param {Function} [params.Container.log]
- * Optional logger function. Would be used to log to console.log or
- * equivalent.
- *
- * @throws {OpenAjax.hub.Error.BadParameters} if required params are not
- * present or null
- * @throws {OpenAjax.hub.Error.Duplicate} if a Container with this clientID
- * already exists in the given Managed Hub
- * @throws {OpenAjax.hub.Error.Disconnected} if ManagedHub is not connected
- */
- OpenAjax.hub.InlineContainer = function(hub, clientID, params) {
- if (! hub || ! clientID || ! params ||
- ! params.Container || ! params.Container.onSecurityAlert) {
- throw new Error(OpenAjax.hub.Error.BadParameters);
- }
- this._params = params;
- this._hub = hub;
- this._id = clientID;
- this._onSecurityAlert = params.Container.onSecurityAlert;
- this._onConnect = params.Container.onConnect ? params.Container.onConnect : null;
- this._onDisconnect = params.Container.onDisconnect ? params.Container.onDisconnect : null;
- this._scope = params.Container.scope || window;
- if (params.Container.log) {
- var scope = this._scope;
- var logfunc = params.Container.log;
- this._log = function(msg) {
- logfunc.call(scope, "InlineContainer::" + clientID + ": " + msg);
- };
- this._doLog = true; // HW Optimization
- } else {
- this._log = function() {
- };
- }
- this._connected = false;
- this._subs = [];
- this._subIndex = 1; // HW FIX
- hub.addContainer(this);
- }
- /*** OpenAjax.hub.Container interface implementation ***/
- OpenAjax.hub.InlineContainer.prototype.getHub = function() {
- return this._hub;
- };
- OpenAjax.hub.InlineContainer.prototype.sendToClient = function(topic, data, subscriptionID) {
- if (this.isConnected()) {
- var sub = this._subs[ subscriptionID ];
- try {
- sub.cb.call(sub.sc, topic, data, sub.d);
- } catch(e) {
- OpenAjax.hub._debugger();
- this._client._log("caught error from onData callback to HubClient.subscribe(): " + e.message);
- }
- }
- }
- OpenAjax.hub.InlineContainer.prototype.remove = function() {
- if (this.isConnected()) {
- this._disconnect();
- }
- }
- OpenAjax.hub.InlineContainer.prototype.isConnected = function() {
- return this._connected;
- }
- OpenAjax.hub.InlineContainer.prototype.getClientID = function() {
- return this._id;
- }
- OpenAjax.hub.InlineContainer.prototype.getPartnerOrigin = function() {
- if (this._connected) {
- // HW Optimization
- if (!this._cacheOrig)
- this._cacheOrig = window.location.protocol + "//" + window.location.hostname;
- return this._cacheOrig;
- }
- return null;
- }
- OpenAjax.hub.InlineContainer.prototype.getParameters = function() {
- return this._params;
- }
- /*** OpenAjax.hub.HubClient interface implementation ***/
- OpenAjax.hub.InlineContainer.prototype.connect = function(client, onComplete, scope) {
- if (this._connected) {
- throw new Error(OpenAjax.hub.Error.Duplicate);
- }
- this._connected = true;
- this._client = client;
- if (this._onConnect) {
- try {
- this._onConnect.call(this._scope, this);
- } catch(e) {
- OpenAjax.hub._debugger();
- this._log("caught error from onConnect callback to constructor: " + e.message);
- }
- }
- this._invokeOnComplete(onComplete, scope, client, true);
- }
- OpenAjax.hub.InlineContainer.prototype.disconnect = function(client, onComplete, scope) {
- if (!this._connected) {
- throw new Error(OpenAjax.hub.Error.Disconnected);
- }
- this._disconnect();
- if (this._onDisconnect) {
- try {
- this._onDisconnect.call(this._scope, this);
- } catch(e) {
- OpenAjax.hub._debugger();
- this._log("caught error from onDisconnect callback to constructor: " + e.message);
- }
- }
- this._invokeOnComplete(onComplete, scope, client, true);
- }
- /*** OpenAjax.hub.Hub interface implementation ***/
- OpenAjax.hub.InlineContainer.prototype.subscribe = function(topic, onData, scope, onComplete, subscriberData) {
- this._assertConn();
- this._assertSubTopic(topic);
- if (! onData) {
- throw new Error(OpenAjax.hub.Error.BadParameters);
- }
- var subID = "" + this._subIndex++;
- var success = false;
- var msg = null;
- try {
- var handle = this._hub.subscribeForClient(this, topic, subID);
- success = true;
- } catch(e) {
- // failure
- subID = null;
- msg = e.message;
- }
- scope = scope || window;
- if (success) {
- this._subs[ subID ] = { h: handle, cb: onData, sc: scope, d: subscriberData };
- }
- this._invokeOnComplete(onComplete, scope, subID, success, msg);
- return subID;
- }
- OpenAjax.hub.InlineContainer.prototype.publish = function(topic, data) {
- this._assertConn();
- this._assertPubTopic(topic);
- this._hub.publishForClient(this, topic, data);
- }
- OpenAjax.hub.InlineContainer.prototype.unsubscribe = function(subscriptionID, onComplete, scope) {
- this._assertConn();
- if (typeof subscriptionID === "undefined" || subscriptionID == null) {
- throw new Error(OpenAjax.hub.Error.BadParameters);
- }
- var sub = this._subs[ subscriptionID ];
- if (! sub)
- throw new Error(OpenAjax.hub.Error.NoSubscription);
- this._hub.unsubscribeForClient(this, sub.h);
- delete this._subs[ subscriptionID ];
- this._invokeOnComplete(onComplete, scope, subscriptionID, true);
- }
- OpenAjax.hub.InlineContainer.prototype.getSubscriberData = function(subID) {
- this._assertConn();
- return this._getSubscription(subID).d;
- }
- OpenAjax.hub.InlineContainer.prototype.getSubscriberScope = function(subID) {
- this._assertConn();
- return this._getSubscription(subID).sc;
- }
- /*** PRIVATE FUNCTIONS ***/
- OpenAjax.hub.InlineContainer.prototype._invokeOnComplete = function(func, scope, item, success, errorCode) {
- if (func) { // onComplete is optional
- try {
- scope = scope || window;
- func.call(scope, item, success, errorCode);
- } catch(e) {
- OpenAjax.hub._debugger();
- // _invokeOnComplete is only called for client interfaces (Hub and HubClient)
- this._client._log("caught error from onComplete callback: " + e.message);
- }
- }
- }
- OpenAjax.hub.InlineContainer.prototype._disconnect = function() {
- for (var subID in this._subs) {
- this._hub.unsubscribeForClient(this, this._subs[subID].h);
- }
- this._subs = [];
- this._subIndex = 1; // HW FIX
- this._connected = false;
- }
- OpenAjax.hub.InlineContainer.prototype._assertConn = function() {
- if (! this._connected) {
- throw new Error(OpenAjax.hub.Error.Disconnected);
- }
- }
- OpenAjax.hub.InlineContainer.prototype._assertPubTopic = function(topic) {
- if ((topic == null) || (topic == "") || (topic.indexOf("*") != -1) ||
- (topic.indexOf("..") != -1) || (topic.charAt(0) == ".") ||
- (topic.charAt(topic.length - 1) == ".")) {
- throw new Error(OpenAjax.hub.Error.BadParameters);
- }
- }
- OpenAjax.hub.InlineContainer.prototype._assertSubTopic = function(topic) {
- if (! topic) {
- throw new Error(OpenAjax.hub.Error.BadParameters);
- }
- var path = topic.split(".");
- var len = path.length;
- for (var i = 0; i < len; i++) {
- var p = path[i];
- if ((p == "") ||
- ((p.indexOf("*") != -1) && (p != "*") && (p != "**"))) {
- throw new Error(OpenAjax.hub.Error.BadParameters);
- }
- if ((p == "**") && (i < len - 1)) {
- throw new Error(OpenAjax.hub.Error.BadParameters);
- }
- }
- }
- OpenAjax.hub.InlineContainer.prototype._getSubscription = function(subID) {
- var sub = this._subs[ subID ];
- if (sub) {
- return sub;
- }
- throw new Error(OpenAjax.hub.Error.NoSubscription);
- }
- ////////////////////////////////////////////////////////////////////////////////
- /**
- * Create a new InlineHubClient.
- * @constructor
- * @extends OpenAjax.hub.HubClient
- *
- * @param {Object} params
- * Parameters used to instantiate the HubClient.
- * Once the constructor is called, the params object belongs to the
- * HubClient. The caller MUST not modify it.
- * The following are the pre-defined properties on params:
- * @param {Function} params.HubClient.onSecurityAlert
- * Called when an attempted security breach is thwarted
- * @param {Object} [params.HubClient.scope]
- * Whenever one of the HubClient's callback functions is called,
- * references to "this" in the callback will refer to the scope object.
- * If not provided, the default is window.
- * @param {OpenAjax.hub.InlineContainer} params.InlineHubClient.container
- * Specifies the InlineContainer to which this HubClient will connect
- *
- * @throws {OpenAjax.hub.Error.BadParameters} if any of the required
- * parameters are missing
- */
- OpenAjax.hub.InlineHubClient = function(params) {
- if (! params || ! params.HubClient || ! params.HubClient.onSecurityAlert ||
- ! params.InlineHubClient || ! params.InlineHubClient.container) {
- throw new Error(OpenAjax.hub.Error.BadParameters);
- }
- this._params = params;
- this._onSecurityAlert = params.HubClient.onSecurityAlert;
- this._scope = params.HubClient.scope || window;
- this._container = params.InlineHubClient.container;
- if (params.HubClient.log) {
- var id = this._container.getClientID();
- var scope = this._scope;
- var logfunc = params.HubClient.log;
- this._log = function(msg) {
- logfunc.call(scope, "InlineHubClient::" + id + ": " + msg);
- };
- this._doLog = true; // HW Optimization
- } else {
- this._log = function() {
- };
- }
- }
- /*** OpenAjax.hub.HubClient interface implementation ***/
- /**
- * Requests a connection to the ManagedHub, via the InlineContainer
- * associated with this InlineHubClient.
- *
- * If the Container accepts the connection request, this HubClient's
- * state is set to CONNECTED and the HubClient invokes the
- * onComplete callback function.
- *
- * If the Container refuses the connection request, the HubClient
- * invokes the onComplete callback function with an error code.
- * The error code might, for example, indicate that the Container
- * is being destroyed.
- *
- * If the HubClient is already connected, calling connect will cause
- * the HubClient to immediately invoke the onComplete callback with
- * the error code OpenAjax.hub.Error.Duplicate.
- *
- * @param {Function} [onComplete]
- * Callback function to call when this operation completes.
- * @param {Object} [scope]
- * When the onComplete function is invoked, the JavaScript "this"
- * keyword refers to this scope object.
- * If no scope is provided, default is window.
- *
- * In this implementation of InlineHubClient, this function operates
- * SYNCHRONOUSLY, so the onComplete callback function is invoked before
- * this connect function returns. Developers are cautioned that in
- * IframeHubClient implementations, this is not the case.
- *
- * A client application may call InlineHubClient.disconnect and then call
- * InlineHubClient.connect to reconnect to the Managed Hub.
- */
- OpenAjax.hub.InlineHubClient.prototype.connect = function(onComplete, scope) {
- this._container.connect(this, onComplete, scope);
- }
- /**
- * Disconnect from the ManagedHub
- *
- * Disconnect immediately:
- *
- * 1. Sets the HubClient's state to DISCONNECTED.
- * 2. Causes the HubClient to send a Disconnect request to the
- * associated Container.
- * 3. Ensures that the client application will receive no more
- * onData or onComplete callbacks associated with this
- * connection, except for the disconnect function's own
- * onComplete callback.
- * 4. Automatically destroys all of the HubClient's subscriptions.
- *
- * @param {Function} [onComplete]
- * Callback function to call when this operation completes.
- * @param {Object} [scope]
- * When the onComplete function is invoked, the JavaScript "this"
- * keyword refers to the scope object.
- * If no scope is provided, default is window.
- *
- * In this implementation of InlineHubClient, the disconnect function operates
- * SYNCHRONOUSLY, so the onComplete callback function is invoked before
- * this function returns. Developers are cautioned that in IframeHubClient
- * implementations, this is not the case.
- *
- * A client application is allowed to call HubClient.disconnect and
- * then call HubClient.connect in order to reconnect.
- */
- OpenAjax.hub.InlineHubClient.prototype.disconnect = function(onComplete, scope) {
- this._container.disconnect(this, onComplete, scope);
- }
- OpenAjax.hub.InlineHubClient.prototype.getPartnerOrigin = function() {
- return this._container.getPartnerOrigin();
- }
- OpenAjax.hub.InlineHubClient.prototype.getClientID = function() {
- return this._container.getClientID();
- }
- /*** OpenAjax.hub.Hub interface implementation ***/
- /**
- * Subscribe to a topic.
- *
- * @param {String} topic
- * A valid topic string. MAY include wildcards.
- * @param {Function} onData
- * Callback function that is invoked whenever an event is
- * published on the topic
- * @param {Object} [scope]
- * When onData callback or onComplete callback is invoked,
- * the JavaScript "this" keyword refers to this scope object.
- * If no scope is provided, default is window.
- * @param {Function} [onComplete]
- * Invoked to tell the client application whether the
- * subscribe operation succeeded or failed.
- * @param {*} [subscriberData]
- * Client application provides this data, which is handed
- * back to the client application in the subscriberData
- * parameter of the onData and onComplete callback functions.
- *
- * @returns subscriptionID
- * Identifier representing the subscription. This identifier is an
- * arbitrary ID string that is unique within this Hub instance
- * @type {String}
- *
- * @throws {OpenAjax.hub.Error.Disconnected} if this Hub instance is not in CONNECTED state
- * @throws {OpenAjax.hub.Error.BadParameters} if the topic is invalid (e.g. contains an empty token)
- *
- * In this implementation of InlineHubClient, the subscribe function operates
- * Thus, onComplete is invoked before this function returns. Developers are
- * cautioned that in most implementations of HubClient, onComplete is invoked
- * after this function returns.
- *
- * If unsubscribe is called before subscribe completes, the subscription is
- * immediately terminated, and onComplete is never invoked.
- */
- OpenAjax.hub.InlineHubClient.prototype.subscribe = function(topic, onData, scope, onComplete, subscriberData) {
- return this._container.subscribe(topic, onData, scope, onComplete, subscriberData);
- }
- /**
- * Publish an event on 'topic' with the given data.
- *
- * @param {String} topic
- * A valid topic string. MUST NOT include wildcards.
- * @param {*} data
- * Valid publishable data. To be portable across different
- * Container implementations, this value SHOULD be serializable
- * as JSON.
- *
- * @throws {OpenAjax.hub.Error.Disconnected} if this Hub instance
- * is not in CONNECTED state
- *
- * In this implementation, publish operates SYNCHRONOUSLY.
- * Data will be delivered to subscribers after this function returns.
- * In most implementations, publish operates synchronously,
- * delivering its data to the clients before this function returns.
- */
- OpenAjax.hub.InlineHubClient.prototype.publish = function(topic, data) {
- this._container.publish(topic, data);
- }
- /**
- * Unsubscribe from a subscription
- *
- * @param {String} subscriptionID
- * A subscriptionID returned by InlineHubClient.prototype.subscribe()
- * @param {Function} [onComplete]
- * Callback function invoked when unsubscribe completes
- * @param {Object} [scope]
- * When onComplete callback function is invoked, the JavaScript "this"
- * keyword refers to this scope object.
- *
- * @throws {OpenAjax.hub.Error.NoSubscription} if no such subscription is found
- *
- * To facilitate cleanup, it is possible to call unsubscribe even
- * when the HubClient is in a DISCONNECTED state.
- *
- * In this implementation of HubClient, this function operates SYNCHRONOUSLY.
- * Thus, onComplete is invoked before this function returns. Developers are
- * cautioned that in most implementations of HubClient, onComplete is invoked
- * after this function returns.
- */
- OpenAjax.hub.InlineHubClient.prototype.unsubscribe = function(subscriptionID, onComplete, scope) {
- this._container.unsubscribe(subscriptionID, onComplete, scope);
- }
- OpenAjax.hub.InlineHubClient.prototype.isConnected = function() {
- return this._container.isConnected();
- }
- OpenAjax.hub.InlineHubClient.prototype.getScope = function() {
- return this._scope;
- }
- OpenAjax.hub.InlineHubClient.prototype.getSubscriberData = function(subID) {
- return this._container.getSubscriberData(subID);
- }
- OpenAjax.hub.InlineHubClient.prototype.getSubscriberScope = function(subID) {
- return this._container.getSubscriberScope(subID);
- }
- /**
- * Returns the params object associated with this Hub instance.
- * Allows mix-in code to access parameters passed into constructor that created
- * this Hub instance.
- *
- * @returns params the params object associated with this Hub instance
- * @type {Object}
- */
- OpenAjax.hub.InlineHubClient.prototype.getParameters = function() {
- return this._params;
- }
- ////////////////////////////////////////////////////////////////////////////////
- /*
- Copyright 2006-2009 OpenAjax Alliance
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- if (typeof OpenAjax === "undefined") {
- OpenAjax = { hub: {} };
- }
- /**
- * Create a new Iframe Container.
- * @constructor
- * @extends OpenAjax.hub.Container
- *
- * IframeContainer implements the Container interface to provide a container
- * that isolates client components into secure sandboxes by leveraging the
- * isolation features provided by browser iframes.
- *
- * @param {OpenAjax.hub.ManagedHub} hub
- * Managed Hub instance to which this Container belongs
- * @param {String} clientID
- * A string ID that identifies a particular client of a Managed Hub. Unique
- * within the context of the ManagedHub.
- * @param {Object} params
- * Parameters used to instantiate the IframeContainer.
- * Once the constructor is called, the params object belongs exclusively to
- * the IframeContainer. The caller MUST not modify it.
- * The following are the pre-defined properties on params:
- * @param {Function} params.Container.onSecurityAlert
- * Called when an attempted security breach is thwarted. Function is defined
- * as follows: function(container, securityAlert)
- * @param {Function} [params.Container.onConnect]
- * Called when the client connects to the Managed Hub. Function is defined
- * as follows: function(container)
- * @param {Function} [params.Container.onDisconnect]
- * Called when the client disconnects from the Managed Hub. Function is
- * defined as follows: function(container)
- * @param {Object} [params.Container.scope]
- * Whenever one of the Container's callback functions is called, references
- * to "this" in the callback will refer to the scope object. If no scope is
- * provided, default is window.
- * @param {Function} [params.Container.log]
- * Optional logger function. Would be used to log to console.log or
- * equivalent.
- * @param {Object} params.IframeContainer.parent
- * Element ID of DOM element that is to be parent of iframe
- * @param {String} params.IframeContainer.uri
- * Initial Iframe URI (Container will add parameters to this URI)
- * @param {String} params.IframeContainer.tunnelURI
- * URI of the tunnel iframe. Must be from the same origin as the page which
- * instantiates the IframeContainer.
- * @param {Object} [params.IframeContainer.iframeAttrs]
- * Attributes to add to IFRAME DOM entity. For example:
- * { style: { width: "100%",
- * height: "100%" },
- * className: "some_class" }
- * @param {Number} [params.IframeContainer.timeout]
- * Load timeout in milliseconds. If not specified, defaults to 15000. If
- * the client at params.IframeContainer.uri does not establish a connection
- * with this container in the given time, the onSecurityAlert callback is
- * called with a LoadTimeout error code.
- * @param {Function} [params.IframeContainer.seed]
- * A function that returns a string that will be used to seed the
- * pseudo-random number generator, which is used to create the security
- * tokens. An implementation of IframeContainer may choose to ignore this
- * value.
- * @param {Number} [params.IframeContainer.tokenLength]
- * Length of the security tokens used when transmitting messages. If not
- * specified, defaults to 6. An implementation of IframeContainer may choose
- * to ignore this value.
- *
- * @throws {OpenAjax.hub.Error.BadParameters} if required params are not
- * present or null
- * @throws {OpenAjax.hub.Error.Duplicate} if a Container with this clientID
- * already exists in the given Managed Hub
- * @throws {OpenAjax.hub.Error.Disconnected} if hub is not connected
- */
- OpenAjax.hub.IframeContainer = function(hub, clientID, params) {
- if (! hub || ! clientID || ! params ||
- ! params.Container || ! params.Container.onSecurityAlert ||
- ! params.IframeContainer || ! params.IframeContainer.parent ||
- ! params.IframeContainer.uri || ! params.IframeContainer.tunnelURI) {
- throw new Error(OpenAjax.hub.Error.BadParameters);
- }
- this._params = params;
- this._id = clientID;
- if (window.postMessage) {
- this._delegate = new OpenAjax.hub.IframePMContainer(this, hub, clientID, params);
- } else {
- this._delegate = new OpenAjax.hub.IframeFIMContainer(this, hub, clientID, params);
- }
- // Create IFRAME to hold the client
- this._iframe = this._createIframe(params.IframeContainer.parent, this._delegate.getURI(),
- params.IframeContainer.iframeAttrs);
- hub.addContainer(this);
- }
- /*** OpenAjax.hub.Container interface implementation ***/
- OpenAjax.hub.IframeContainer.prototype.getHub = function() {
- return this._delegate.getHub();
- }
- OpenAjax.hub.IframeContainer.prototype.sendToClient = function(topic, data, subscriptionID) {
- this._delegate.sendToClient(topic, data, subscriptionID);
- }
- OpenAjax.hub.IframeContainer.prototype.remove = function() {
- this._delegate.remove();
- this._iframe.parentNode.removeChild(this._iframe);
- }
- OpenAjax.hub.IframeContainer.prototype.isConnected = function() {
- return this._delegate.isConnected();
- }
- OpenAjax.hub.IframeContainer.prototype.getClientID = function() {
- return this._id;
- }
- OpenAjax.hub.IframeContainer.prototype.getPartnerOrigin = function() {
- return this._delegate.getPartnerOrigin();
- }
- OpenAjax.hub.IframeContainer.prototype.getParameters = function() {
- return this._params;
- }
- /**
- * Get the iframe associated with this iframe container
- *
- * This function returns the iframe associated with an IframeContainer,
- * allowing the Manager Application to change its size, styles, scrollbars, etc.
- *
- * CAUTION: The iframe is owned exclusively by the IframeContainer. The Manager
- * Application MUST NOT destroy the iframe directly. Also, if the iframe is
- * hidden and disconnected, the Manager Application SHOULD NOT attempt to make
- * it visible. The Container SHOULD automatically hide the iframe when it is
- * disconnected; to make it visible would introduce security risks.
- *
- * @returns iframeElement
- * @type {Object}
- */
- OpenAjax.hub.IframeContainer.prototype.getIframe = function() {
- return this._iframe;
- }
- /*** Helper Functions ***/
- /**
- * Return function that runs in given scope.
- *
- * @param {Object} toWhom scope in which to run given function
- * @param {Function} callback function to run in given scope
- * @returns {Function}
- */
- OpenAjax.hub.IframeContainer.bind = function(toWhom, callback) {
- var __method = callback;
- return function() {
- return __method.apply(toWhom, arguments);
- }
- }
- /*** Private Functions ***/
- OpenAjax.hub.IframeContainer.prototype._createIframe = function(parent, src, attrs) {
- var iframe = document.createElement("iframe");
- // Add iframe attributes
- if (attrs) {
- for (var attr in attrs) {
- if (attr == "style") {
- for (var style in attrs.style) {
- iframe.style[ style ] = attrs.style[ style ];
- }
- } else {
- iframe[ attr ] = attrs[ attr ];
- }
- }
- }
- // initially hide IFRAME content, in order to lessen frame phishing impact
- iframe.style.visibility = "hidden";
- // (1) Setting the iframe src after it has been added to the DOM can cause
- // problems in IE6/7. Specifically, if the code is being executed on a page
- // that was served through HTTPS, then IE6/7 will see an iframe with a blank
- // src as a non-secure item and display a dialog warning the user that "this
- // page contains both secure and nonsecure items." To prevent that, we
- // first set the src to a dummy value, then add the iframe to the DOM, then
- // set the real src value.
- // (2) Trying to fix the above issue by setting the real src before adding
- // the iframe to the DOM breaks Firefox 3.x. For some reason, when
- // reloading a page that has instantiated an IframeContainer, Firefox will
- // load a previously cached version of the iframe content, whose source
- // contains stale URL query params or hash. This results in errors in the
- // Hub code, which is expected different values.
- iframe.src = 'javascript:"<html></html>"';
- parent.appendChild(iframe);
- iframe.src = src;
- return iframe;
- }
- //------------------------------------------------------------------------------
- /**
- * Create a new IframeHubClient.
- * @constructor
- * @extends OpenAjax.hub.HubClient
- *
- * @param {Object} params
- * Once the constructor is called, the params object belongs to the
- * HubClient. The caller MUST not modify it.
- * The following are the pre-defined properties on params:
- * @param {Function} params.HubClient.onSecurityAlert
- * Called when an attempted security breach is thwarted
- * @param {Object} [params.HubClient.scope]
- * Whenever one of the HubClient's callback functions is called,
- * references to "this" in the callback will refer to the scope object.
- * If not provided, the default is window.
- * @param {Function} [params.IframeHubClient.seed]
- * A function that returns a string that will be used to seed the
- * pseudo-random number generator, which is used to create the security
- * tokens. An implementation of IframeHubClient may choose to ignore
- * this value.
- * @param {Number} [params.IframeHubClient.tokenLength]
- * Length of the security tokens used when transmitting messages. If
- * not specified, defaults to 6. An implementation of IframeHubClient
- * may choose to ignore this value.
- *
- * @throws {OpenAjax.hub.Error.BadParameters} if any of the required
- * parameters is missing, or if a parameter value is invalid in
- * some way.
- */
- OpenAjax.hub.IframeHubClient = function(params) {
- if (! params || ! params.HubClient || ! params.HubClient.onSecurityAlert) {
- throw new Error(OpenAjax.hub.Error.BadParameters);
- }
- this._params = params;
- if (window.postMessage) {
- this._delegate = new OpenAjax.hub.IframePMHubClient(this, params);
- } else {
- this._delegate = new OpenAjax.hub.IframeFIMHubClient(this, params);
- }
- }
- /*** OpenAjax.hub.HubClient interface implementation ***/
- OpenAjax.hub.IframeHubClient.prototype.connect = function(onComplete, scope) {
- scope = scope || window;
- if (this.isConnected()) {
- throw new Error(OpenAjax.hub.Error.Duplicate);
- }
- this._delegate.connect(onComplete, scope);
- }
- OpenAjax.hub.IframeHubClient.prototype.disconnect = function(onComplete, scope) {
- scope = scope || window;
- if (! this.isConnected()) {
- throw new Error(OpenAjax.hub.Error.Disconnected);
- }
- this._delegate.disconnect(onComplete, scope);
- }
- OpenAjax.hub.IframeHubClient.prototype.getPartnerOrigin = function() {
- return this._delegate.getPartnerOrigin();
- }
- OpenAjax.hub.IframeHubClient.prototype.getClientID = function() {
- return this._delegate.getClientID();
- }
- /*** OpenAjax.hub.Hub interface implementation ***/
- OpenAjax.hub.IframeHubClient.prototype.subscribe = function(topic, onData, scope, onComplete, subscriberData) {
- this._assertConn();
- this._assertSubTopic(topic);
- if (! onData) {
- throw new Error(OpenAjax.hub.Error.BadParameters);
- }
- scope = scope || window;
- return this._delegate.subscribe(topic, onData, scope, onComplete, subscriberData);
- }
- OpenAjax.hub.IframeHubClient.prototype.publish = function(topic, data) {
- this._assertConn();
- this._assertPubTopic(topic);
- this._delegate.publish(topic, data);
- }
- OpenAjax.hub.IframeHubClient.prototype.unsubscribe = function(subscriptionID, onComplete, scope) {
- this._assertConn();
- if (typeof subscriptionID === "undefined" || subscriptionID == null) {
- throw new Error(OpenAjax.hub.Error.BadParameters);
- }
- scope = scope || window;
- this._delegate.unsubscribe(subscriptionID, onComplete, scope);
- }
- OpenAjax.hub.IframeHubClient.prototype.isConnected = function() {
- return this._delegate.isConnected();
- }
- OpenAjax.hub.IframeHubClient.prototype.getScope = function() {
- return this._delegate.getScope();
- }
- OpenAjax.hub.IframeHubClient.prototype.getSubscriberData = function(subscriptionID) {
- this._assertConn();
- return this._delegate.getSubscriberData(subscriptionID);
- }
- OpenAjax.hub.IframeHubClient.prototype.getSubscriberScope = function(subscriptionID) {
- this._assertConn();
- return this._delegate.getSubscriberScope(subscriptionID);
- }
- OpenAjax.hub.IframeHubClient.prototype.getParameters = function() {
- return this._params;
- }
- /*** Private Functions ***/
- OpenAjax.hub.IframeHubClient.prototype._assertConn = function() {
- if (! this.isConnected()) {
- throw new Error(OpenAjax.hub.Error.Disconnected);
- }
- }
- OpenAjax.hub.IframeHubClient.prototype._assertSubTopic = function(topic) {
- if (! topic) {
- throw new Error(OpenAjax.hub.Error.BadParameters);
- }
- var path = topic.split(".");
- var len = path.length;
- for (var i = 0; i < len; i++) {
- var p = path[i];
- if ((p == "") ||
- ((p.indexOf("*") != -1) && (p != "*") && (p != "**"))) {
- throw new Error(OpenAjax.hub.Error.BadParameters);
- }
- if ((p == "**") && (i < len - 1)) {
- throw new Error(OpenAjax.hub.Error.BadParameters);
- }
- }
- }
- OpenAjax.hub.IframeHubClient.prototype._assertPubTopic = function(topic) {
- if ((topic == null) || (topic == "") || (topic.indexOf("*") != -1) ||
- (topic.indexOf("..") != -1) || (topic.charAt(0) == ".") ||
- (topic.charAt(topic.length - 1) == ".")) {
- throw new Error(OpenAjax.hub.Error.BadParameters);
- }
- }
- /******************************************************************************
- * PostMessage Iframe Container
- *
- * Implementation of the Iframe Container which uses window.postMessage()
- * for communicating between an iframe and its parent.
- ******************************************************************************/
- OpenAjax.hub.IframePMContainer = function(container, hub, clientID, params) {
- this._container = container;
- this._hub = hub;
- this._id = clientID;
- this._onSecurityAlert = params.Container.onSecurityAlert;
- this._onConnect = params.Container.onConnect ? params.Container.onConnect : null;
- this._onDisconnect = params.Container.onDisconnect ? params.Container.onDisconnect : null;
- this._scope = params.Container.scope || window;
- this._uri = params.IframeContainer.uri;
- this._tunnelURI = params.IframeContainer.tunnelURI;
- this._timeout = params.IframeContainer.timeout || 15000;
- if (params.Container.log) {
- var scope = this._scope;
- var logfunc = params.Container.log;
- this._log = function(msg) {
- logfunc.call(scope, "IframeContainer::" + clientID + ": " + msg);
- };
- this._doLog = true; // HW Optimization
- } else {
- this._log = function() {
- };
- }
- this._securityToken = this._generateSecurityToken(params);
- this._connected = false;
- this._subs = {};
- // test if the postMessage impl of this browser is synchronous
- if (typeof OpenAjax.hub.IframePMContainer._pmCapabilities === "undefined") {
- this._testPostMessage();
- }
- // if postMessage is synchronous, wrap in a setTimeout
- if (OpenAjax.hub.IframePMContainer._pmCapabilities.indexOf("s") == -1) {
- this._postMessage = function(win, msg, origin) {
- win.postMessage(msg, origin);
- }
- } else {
- this._postMessage = function(win, msg, origin) {
- setTimeout(
- function() {
- win.postMessage(msg, origin);
- },
- 0
- );
- }
- }
- // register this container with the singleton message listener
- if (! OpenAjax.hub.IframePMContainer._pmListener) {
- OpenAjax.hub.IframePMContainer._pmListener =
- new OpenAjax.hub.IframePMContainer.PMListener();
- }
- // the 'internal ID' is guaranteed to be unique within the page, not just
- // the ManagedHub instance
- this._internalID = OpenAjax.hub.IframePMContainer._pmListener.addContainer(this);
- this._startLoadTimer();
- }
- //communications protocol identifier
- OpenAjax.hub.IframePMContainer.protocolID = "openajax-2.0";
- //Singleton message listener
- OpenAjax.hub.IframePMContainer._pmListener = null;
- OpenAjax.hub.IframePMContainer.prototype.getHub = function() {
- return this._hub;
- };
- OpenAjax.hub.IframePMContainer.prototype.sendToClient = function(topic, data, subscriptionID) {
- this._sendMessage("pub", { t: topic, d: data, s: subscriptionID });
- }
- OpenAjax.hub.IframePMContainer.prototype.remove = function() {
- this._disconnect();
- OpenAjax.hub.IframePMContainer._pmListener.removeContainer(this._internalID);
- clearTimeout(this._loadTimer);
- }
- OpenAjax.hub.IframePMContainer.prototype.isConnected = function() {
- return this._connected;
- }
- OpenAjax.hub.IframePMContainer.prototype.getPartnerOrigin = function() {
- if (this._connected) {
- // remove port, if it is present
- // HW Optimization
- return this._partnerOriginNoPort;
- // return new RegExp( "^([a-zA-Z]+://[^:]+).*" ).exec( this._partnerOrigin )[1];
- }
- return null;
- }
- OpenAjax.hub.IframePMContainer.prototype.receiveMessage = function(event, msg) {
- // check that security token and client window origin for incoming message
- // are what we expect
- if (msg.t != this._securityToken ||
- ( typeof this._partnerOrigin != "undefined" &&
- ! OpenAjax.hub.IframePMContainer.originMatches(this, event))) {
- // security error -- incoming message is not valid; ignore
- this._invokeSecurityAlert(OpenAjax.hub.SecurityAlert.ForgedMsg);
- return;
- }
- if (this._doLog) { // HW Optimization
- this._log("received message: [" + event.data + "]");
- }
- switch (msg.m) {
- // subscribe
- case "sub":
- var errCode = ""; // empty string is success
- try {
- this._subs[ msg.p.s ] = this._hub.subscribeForClient(this._container, msg.p.t, msg.p.s);
- } catch(e) {
- errCode = e.message;
- }
- this._sendMessage("sub_ack", { s: msg.p.s, e: errCode });
- break;
- // publish
- case "pub":
- this._hub.publishForClient(this._container, msg.p.t, msg.p.d);
- break;
- // unsubscribe
- case "uns":
- var handle = this._subs[ msg.p.s ];
- this._hub.unsubscribeForClient(this._container, handle);
- delete this._subs[ msg.p.s ];
- this._sendMessage("uns_ack", msg.p.s);
- break;
- // connect is handled elsewhere -- see IframePMContainer.prototype.connect
- // disconnect
- case "dis":
- this._startLoadTimer();
- this._disconnect();
- this._sendMessage("dis_ack", null);
- if (this._onDisconnect) {
- try {
- this._onDisconnect.call(this._scope, this._container);
- } catch(e) {
- OpenAjax.hub._debugger();
- this._log("caught error from onDisconnect callback to constructor: " + e.message);
- }
- }
- break;
- }
- }
- /**
- * Complete connection from HubClient to this Container.
- *
- * @param {String} origin IframePMHubClient's window's origin
- * @param {String} securityToken Security token originally sent by Container
- * @param {Object} tunnelWindow window object reference of tunnel window
- */
- OpenAjax.hub.IframePMContainer.prototype.connect = function(origin, securityToken, tunnelWindow) {
- this._log("client connecting to container " + this._id +
- " :: origin = " + origin + " :: securityToken = " + securityToken);
- // check that security token is what we expect
- if (securityToken != this._securityToken) {
- // security error -- incoming message is not valid
- this._invokeSecurityAlert(OpenAjax.hub.SecurityAlert.ForgedMsg);
- return;
- }
- // set unload handler on tunnel window
- var that = this;
- tunnelWindow.onunload = function() {
- if (that.isConnected()) {
- // Use a timer to delay the phishing message. This makes sure that
- // page navigation does not cause phishing errors.
- // Setting it to 1 ms is enough for it not to be triggered on
- // regular page navigations.
- setTimeout(
- function() {
- that._invokeSecurityAlert(OpenAjax.hub.SecurityAlert.FramePhish);
- }, 1
- );
- }
- };
- clearTimeout(this._loadTimer);
- this._iframe = this._container.getIframe();
- this._iframe.style.visibility = "visible";
- this._partnerOrigin = origin;
- // HW Optimization
- this._partnerOriginNoPort = new RegExp("^([a-zA-Z]+://[^:]+).*").exec(this._partnerOrigin)[1]; // HW Optimization
- // if "message" event doesn't support "origin" property, then save hostname
- // (domain) also
- if (OpenAjax.hub.IframePMContainer._pmCapabilities.indexOf("d") != -1) {
- this._partnerDomain = new RegExp("^.+://([^:]+).*").exec(this._partnerOrigin)[1];
- }
- this._sendMessage("con_ack", null);
- this._connected = true;
- if (this._onConnect) {
- try {
- this._onConnect.call(this._scope, this._container);
- } catch(e) {
- OpenAjax.hub._debugger();
- this._log("caught error from onConnect callback to constructor: " + e.message);
- }
- }
- }
- OpenAjax.hub.IframePMContainer.prototype.getURI = function() {
- // add the client ID and a security token as URL query params when loading
- // the client iframe
- var paramStr =
- "oahpv=" + encodeURIComponent(OpenAjax.hub.IframePMContainer.protocolID) +
- "&oahi=" + encodeURIComponent(this._internalID) +
- "&oaht=" + this._securityToken +
- "&oahu=" + encodeURIComponent(this._tunnelURI) +
- "&oahpm=" + OpenAjax.hub.IframePMContainer._pmCapabilities;
- if (this._id !== this._internalID) {
- paramStr += "&oahj=" + this._internalID;
- }
- paramStr += OpenAjax.hub.enableDebug ? "&oahd=true" : ""; // REMOVE ON BUILD
- var parts = this._uri.split("#");
- parts[0] = parts[0] + ((parts[0].indexOf("?") != -1) ? "&" : "?") + paramStr;
- if (parts.length == 1) {
- return parts[0];
- }
- return parts[0] + "#" + parts[1];
- }
- /*** Helper Functions ***/
- OpenAjax.hub.IframePMContainer.originMatches = function(obj, event) {
- if (event.origin) {
- return event.origin == obj._partnerOrigin;
- } else {
- return event.domain == obj._partnerDomain;
- }
- }
- /*** Private Function ***/
- OpenAjax.hub.IframePMContainer.prototype._generateSecurityToken = function(params) {
- if (! OpenAjax.hub.IframePMContainer._prng) {
- // create pseudo-random number generator with a default seed
- var seed = new Date().getTime() + Math.random() + document.cookie;
- OpenAjax.hub.IframePMContainer._prng = smash.crypto.newPRNG(seed);
- }
- if (params.IframeContainer.seed) {
- try {
- var extraSeed = params.IframeContainer.seed.call(this._scope);
- OpenAjax.hub.IframePMContainer._prng.addSeed(extraSeed);
- } catch(e) {
- OpenAjax.hub._debugger();
- this._log("caught error from 'seed' callback: " + e.message);
- }
- }
- var tokenLength = params.IframeContainer.tokenLength || 6;
- return OpenAjax.hub.IframePMContainer._prng.nextRandomB64Str(tokenLength);
- }
- /**
- * Some browsers (IE, Opera) have an implementation of postMessage that is
- * synchronous, although HTML5 specifies that it should be asynchronous. In
- * order to make all browsers behave consistently, we run a small test to detect
- * if postMessage is asynchronous or not. If not, we wrap calls to postMessage
- * in a setTimeout with a timeout of 0.
- * Also, Opera's "message" event does not have an "origin" property (at least,
- * it doesn't in version 9.64; presumably, it will in version 10). If
- * event.origin does not exist, use event.domain. The other difference is that
- * while event.origin looks like <scheme>://<hostname>:<port>, event.domain
- * consists only of <hostname>.
- */
- OpenAjax.hub.IframePMContainer.prototype._testPostMessage = function() {
- // String identifier that specifies whether this browser's postMessage
- // implementation differs from the spec:
- // contains "s" - postMessage is synchronous
- // contains "d" - "message" event does not have an "origin" property;
- // the code looks for the "domain" property instead
- OpenAjax.hub.IframePMContainer._pmCapabilities = "";
- var hit = false;
- function receiveMsg(event) {
- if (event.data == "postmessage.test") {
- hit = true;
- if (typeof event.origin === "undefined") {
- OpenAjax.hub.IframePMContainer._pmCapabilities += "d";
- }
- }
- }
- if (window.addEventListener) {
- window.addEventListener("message", receiveMsg, false);
- } else if (window.attachEvent) {
- window.attachEvent("onmessage", receiveMsg);
- }
- window.postMessage("postmessage.test", "*");
- // if 'hit' is true here, then postMessage is synchronous
- if (hit) {
- OpenAjax.hub.IframePMContainer._pmCapabilities += "s";
- }
- if (window.removeEventListener) {
- window.removeEventListener("message", receiveMsg, false);
- } else {
- window.detachEvent("onmessage", receiveMsg);
- }
- }
- OpenAjax.hub.IframePMContainer.prototype._startLoadTimer = function() {
- var that = this;
- this._loadTimer = setTimeout(
- function() {
- // don't accept any messages from client
- OpenAjax.hub.IframePMContainer._pmListener.removeContainer(that._internalID);
- // alert the security alert callback
- that._invokeSecurityAlert(OpenAjax.hub.SecurityAlert.LoadTimeout);
- },
- this._timeout
- );
- }
- /**
- * Send a string message to the associated hub client.
- *
- * The message is a JSON representation of the following object:
- * {
- * m: message type,
- * i: client id,
- * t: security token,
- * p: payload (depends on message type)
- * }
- *
- * The payload for each message type is as follows:
- * TYPE DESCRIPTION PAYLOAD
- * "con_ack" connect acknowledgment N/A
- * "dis_ack" disconnect acknowledgment N/A
- * "sub_ack" subscribe acknowledgment { s: subscription id, e: error code (empty string if no error) }
- * "uns_ack" unsubscribe acknowledgment { s: subscription id }
- * "pub" publish (i.e. sendToClient()) { t: topic, d: data, s: subscription id }
- */
- OpenAjax.hub.IframePMContainer.prototype._sendMessage = function(type, payload) {
- var msg = JSON.stringify({
- m: type,
- i: this._internalID,
- t: this._securityToken,
- p: payload
- });
- this._postMessage(this._iframe.contentWindow, msg, this._partnerOrigin);
- }
- OpenAjax.hub.IframePMContainer.prototype._disconnect = function() {
- if (this._connected) {
- this._connected = false;
- this._iframe.style.visibility = "hidden";
- // unsubscribe from all subs
- for (var sub in this._subs) {
- this._hub.unsubscribeForClient(this._container, this._subs[ sub ]);
- }
- this._subs = {};
- }
- }
- OpenAjax.hub.IframePMContainer.prototype._invokeSecurityAlert = function(errorMsg) {
- try {
- this._onSecurityAlert.call(this._scope, this._container, errorMsg);
- } catch(e) {
- OpenAjax.hub._debugger();
- this._log("caught error from onSecurityAlert callback to constructor: " + e.message);
- }
- }
- //------------------------------------------------------------------------------
- OpenAjax.hub.IframePMContainer.PMListener = function() {
- this._containers = {};
- if (window.addEventListener) {
- window.addEventListener("message",
- OpenAjax.hub.IframeContainer.bind(this, this._receiveMessage), false);
- } else if (window.attachEvent) {
- window.attachEvent("onmessage",
- OpenAjax.hub.IframeContainer.bind(this, this._receiveMessage));
- }
- }
- /**
- * Add an IframePMContainer to listen for messages. Returns an ID for the given
- * container that is unique within the PAGE, not just the ManagedHub instance.
- */
- OpenAjax.hub.IframePMContainer.PMListener.prototype.addContainer = function(container) {
- var id = container._id;
- while (this._containers[ id ]) {
- // a client with the specified ID already exists on this page;
- // create a unique ID
- id = ((0x7fff * Math.random()) | 0).toString(16) + "_" + id;
- }
- this._containers[ id ] = container;
- return id;
- }
- OpenAjax.hub.IframePMContainer.PMListener.prototype.removeContainer = function(internalID) {
- delete this._containers[ internalID ];
- // XXX TODO If no more postMessage containers, remove listener?
- }
- /**
- * Complete connection between HubClient and Container identified by "id". This
- * function is only called by the tunnel window.
- */
- OpenAjax.hub.IframePMContainer.PMListener.prototype.connectFromTunnel = function(internalID, origin, securityToken, tunnelWindow) {
- if (this._containers[ internalID ]) {
- this._containers[ internalID ].connect(origin, securityToken, tunnelWindow);
- }
- }
- OpenAjax.hub.IframePMContainer.PMListener.prototype._receiveMessage = function(event) {
- // If the received message isn't JSON parseable or if the resulting
- // object doesn't have the structure we expect, then just return.
- try {
- var msg = JSON.parse(event.data);
- } catch(e) {
- return;
- }
- if (! this._verifyMsg(msg)) {
- return;
- }
- if (this._containers[ msg.i ]) {
- var container = this._containers[ msg.i ].receiveMessage(event, msg);
- }
- }
- OpenAjax.hub.IframePMContainer.PMListener.prototype._verifyMsg = function(msg) {
- return typeof msg.m == "string" && typeof msg.i == "string" &&
- "t" in msg && "p" in msg;
- }
- //------------------------------------------------------------------------------
- OpenAjax.hub.IframePMHubClient = function(client, params) {
- // check communications protocol ID
- this._checkProtocolID();
- this._client = client;
- this._onSecurityAlert = params.HubClient.onSecurityAlert;
- this._scope = params.HubClient.scope || window;
- this._id = OpenAjax.hub.IframePMHubClient.queryURLParam("oahi");
- this._internalID = OpenAjax.hub.IframePMHubClient.queryURLParam("oahj") || this._id;
- this._securityToken = OpenAjax.hub.IframePMHubClient.queryURLParam("oaht");
- this._tunnelURI = OpenAjax.hub.IframePMHubClient.queryURLParam("oahu");
- OpenAjax.hub.IframePMContainer._pmCapabilities = OpenAjax.hub.IframePMHubClient.queryURLParam("oahpm");
- // if any of the URL params are missing, throw WrongProtocol error
- if (! this._id || ! this._securityToken || ! this._tunnelURI) {
- throw new Error(OpenAjax.hub.Error.WrongProtocol);
- }
- if (OpenAjax.hub.IframePMHubClient.queryURLParam("oahd")) OpenAjax.hub.enableDebug = true; // REMOVE ON BUILD
- this._partnerOrigin = new RegExp("^([a-zA-Z]+://[^/?#]+).*").exec(this._tunnelURI)[1];
- this._partnerOriginNoPort = new RegExp("^([a-zA-Z]+://[^:]+).*").exec(this._partnerOrigin)[1]; // HW Optimization
- // if "message" event doesn't support "origin" property, then save hostname
- // (domain) also
- if (OpenAjax.hub.IframePMContainer._pmCapabilities.indexOf("d") != -1) {
- this._partnerDomain = new RegExp("^.+://([^:]+).*").exec(this._partnerOrigin)[1];
- }
- if (params.HubClient.log) {
- var id = this._id;
- var scope = this._scope;
- var logfunc = params.HubClient.log;
- this._log = function(msg) {
- logfunc.call(scope, "IframeHubClient::" + id + ": " + msg);
- };
- this._doLog = true; // HW Optimization
- } else {
- this._log = function() {
- };
- }
- this._connected = false;
- this._subs = {};
- this._subIndex = 1; // HW FIX
- // if postMessage is synchronous, wrap in a setTimeout
- if (OpenAjax.hub.IframePMContainer._pmCapabilities.indexOf("s") == -1) {
- this._postMessage = function(win, msg, origin) {
- win.postMessage(msg, origin);
- }
- } else {
- this._postMessage = function(win, msg, origin) {
- setTimeout(
- function() {
- win.postMessage(msg, origin);
- },
- 0
- );
- }
- }
- }
- //communications protocol identifier
- OpenAjax.hub.IframePMHubClient.protocolID = "openajax-2.0";
- /*** OpenAjax.hub.HubClient interface implementation ***/
- OpenAjax.hub.IframePMHubClient.prototype.connect = function(onComplete, scope) {
- if (onComplete) {
- this._connectOnComplete = { cb: onComplete, sc: scope };
- }
- // start listening for messages
- this._msgListener = OpenAjax.hub.IframeContainer.bind(this, this._receiveMessage);
- if (window.addEventListener) {
- window.addEventListener("message", this._msgListener, false);
- } else if (window.attachEvent) {
- window.attachEvent("onmessage", this._msgListener);
- }
- // create tunnel iframe, which will finish connection to container
- var origin = window.location.protocol + "//" + window.location.host;
- var iframe = document.createElement("iframe");
- document.body.appendChild(iframe);
- iframe.src = this._tunnelURI +
- (this._tunnelURI.indexOf("?") == -1 ? "?" : "&") +
- "oahj=" + encodeURIComponent(this._internalID) +
- "&oaht=" + this._securityToken +
- "&oaho=" + encodeURIComponent(origin);
- iframe.style.position = "absolute";
- iframe.style.left = iframe.style.top = "-10px";
- iframe.style.height = iframe.style.width = "1px";
- iframe.style.visibility = "hidden";
- this._tunnelIframe = iframe;
- }
- OpenAjax.hub.IframePMHubClient.prototype.disconnect = function(onComplete, scope) {
- this._connected = false;
- if (onComplete) {
- this._disconnectOnComplete = { cb: onComplete, sc: scope };
- }
- this._sendMessage("dis", null);
- }
- OpenAjax.hub.IframePMHubClient.prototype.getPartnerOrigin = function() {
- if (this._connected) {
- // remove port, if it is present
- return new RegExp("^([a-zA-Z]+://[^:]+).*").exec(this._partnerOrigin)[1];
- }
- return null;
- }
- OpenAjax.hub.IframePMHubClient.prototype.getClientID = function() {
- return this._id;
- }
- /*** OpenAjax.hub.Hub interface implementation ***/
- OpenAjax.hub.IframePMHubClient.prototype.subscribe = function(topic, onData, scope, onComplete, subscriberData) {
- var subID = "" + this._subIndex++;
- this._subs[ subID ] = { cb: onData, sc: scope, d: subscriberData, oc: onComplete };
- this._sendMessage("sub", { t: topic, s: subID });
- return subID;
- }
- OpenAjax.hub.IframePMHubClient.prototype.publish = function(topic, data) {
- this._sendMessage("pub", { t: topic, d: data });
- }
- OpenAjax.hub.IframePMHubClient.prototype.unsubscribe = function(subID, onComplete, scope) {
- // if no such subID, or in process of unsubscribing given ID, throw error
- if (! this._subs[ subID ] || this._subs[ subID ].uns) {
- throw new Error(OpenAjax.hub.Error.NoSubscription);
- }
- this._subs[ subID ].uns = { cb: onComplete, sc: scope };
- this._sendMessage("uns", { s: subID });
- }
- OpenAjax.hub.IframePMHubClient.prototype.isConnected = function() {
- return this._connected;
- }
- OpenAjax.hub.IframePMHubClient.prototype.getScope = function() {
- return this._scope;
- }
- OpenAjax.hub.IframePMHubClient.prototype.getSubscriberData = function(subID) {
- var sub = this._subs[ subID ];
- if (sub) {
- return sub.d;
- }
- throw new Error(OpenAjax.hub.Error.NoSubscription);
- }
- OpenAjax.hub.IframePMHubClient.prototype.getSubscriberScope = function(subID) {
- var sub = this._subs[ subID ];
- if (sub) {
- return sub.sc;
- }
- throw new Error(OpenAjax.hub.Error.NoSubscription);
- }
- /*** Helper Functions ***/
- OpenAjax.hub.IframePMHubClient.queryURLParam = function(param) {
- var result = new RegExp("[\\?&]" + param + "=([^&#]*)").exec(window.location.search);
- if (result) {
- return decodeURIComponent(result[1].replace(/\+/g, "%20"));
- }
- return null;
- };
- /*** Private Functions ***/
- OpenAjax.hub.IframePMHubClient.prototype._checkProtocolID = function() {
- var partnerProtocolID = OpenAjax.hub.IframePMHubClient.queryURLParam("oahpv");
- if (partnerProtocolID != OpenAjax.hub.IframePMHubClient.protocolID) {
- throw new Error(OpenAjax.hub.Error.WrongProtocol);
- }
- }
- OpenAjax.hub.IframePMHubClient.prototype._receiveMessage = function(event) {
- // If the received message isn't JSON parseable or if the resulting
- // object doesn't have the structure we expect, then just return. This
- // message might belong to some other code on the page that is also using
- // postMessage for communication.
- try {
- var msg = JSON.parse(event.data);
- } catch(e) {
- return;
- }
- if (! this._verifyMsg(msg)) {
- return;
- }
- // check that security token and window source for incoming message
- // are what we expect
- if (msg.i != this._internalID) {
- // this message might belong to an IframeContainer on this page
- return;
- } else if (! OpenAjax.hub.IframePMContainer.originMatches(this, event) ||
- msg.t != this._securityToken) {
- // security error -- incoming message is not valid
- try {
- this._onSecurityAlert.call(this._scope, this._client,
- OpenAjax.hub.SecurityAlert.ForgedMsg);
- } catch(e) {
- OpenAjax.hub._debugger();
- this._log("caught error from onSecurityAlert callback to constructor: " + e.message);
- }
- return;
- }
- if (this._doLog) { // HW Optimization
- this._log("received message: [" + event.data + "]");
- }
- switch (msg.m) {
- // subscribe acknowledgement
- case "sub_ack":
- var subID = msg.p.s;
- var onComplete = this._subs[ subID ].oc;
- if (onComplete) {
- try {
- delete this._subs[ subID ].oc;
- var scope = this._subs[ subID ].sc;
- onComplete.call(scope, msg.p.s, msg.p.e == "", msg.p.e);
- } catch(e) {
- OpenAjax.hub._debugger();
- this._log("caught error from onComplete callback to HubClient.subscribe(): " + e.message);
- }
- }
- break;
- // publish event
- case "pub":
- var subID = msg.p.s;
- // if subscription exists and we are not in process of unsubscribing...
- if (this._subs[ subID ] && ! this._subs[ subID ].uns) {
- var onData = this._subs[ subID ].cb;
- var scope = this._subs[ subID ].sc;
- var subscriberData = this._subs[ subID ].d;
- try {
- onData.call(scope, msg.p.t, msg.p.d, subscriberData);
- } catch(e) {
- OpenAjax.hub._debugger();
- this._log("caught error from onData callback to HubClient.subscribe(): " + e.message);
- }
- }
- break;
- // unsubscribe acknowledgement
- case "uns_ack":
- var subID = msg.p;
- if (this._subs[ subID ]) {
- var onComplete = this._subs[ subID ].uns.cb;
- if (onComplete) {
- try {
- var scope = this._subs[ subID ].uns.sc;
- onComplete.call(scope, subID, true);
- } catch(e) {
- OpenAjax.hub._debugger();
- this._log("caught error from onComplete callback to HubClient.unsubscribe(): " + e.message);
- }
- }
- delete this._subs[ subID ];
- }
- break;
- // connect acknowledgement
- case "con_ack":
- this._connected = true;
- if (this._connectOnComplete) {
- var onComplete = this._connectOnComplete.cb;
- var scope = this._connectOnComplete.sc;
- try {
- onComplete.call(scope, this._client, true);
- } catch(e) {
- OpenAjax.hub._debugger();
- this._log("caught error from onComplete callback to HubClient.connect(): " + e.message);
- }
- delete this._connectOnComplete;
- }
- break;
- // disconnect acknowledgment
- case "dis_ack":
- // stop listening for messages
- if (window.removeEventListener) {
- window.removeEventListener("message", this._msgListener, false);
- } else {
- window.detachEvent("onmessage", this._msgListener);
- }
- delete this._msgListener;
- this._tunnelIframe.parentNode.removeChild(this._tunnelIframe);
- delete this._tunnelIframe;
- if (this._disconnectOnComplete) {
- try {
- var onComplete = this._disconnectOnComplete.cb;
- var scope = this._disconnectOnComplete.sc;
- onComplete.call(scope, this._client, true);
- } catch(e) {
- OpenAjax.hub._debugger();
- this._log("caught error from onComplete callback to HubClient.disconnect(): " + e.message);
- }
- delete this._disconnectOnComplete;
- }
- break;
- }
- }
- OpenAjax.hub.IframePMHubClient.prototype._verifyMsg = function(msg) {
- return typeof msg.m == "string" && "t" in msg && "p" in msg;
- }
- /**
- * Send a string message to the associated container.
- *
- * The message is a JSON representation of the following object:
- * {
- * m: message type,
- * i: client id,
- * t: security token,
- * p: payload (depends on message type)
- * }
- *
- * The payload for each message type is as follows:
- * TYPE DESCRIPTION PAYLOAD
- * "con" connect N/A
- * "dis" disconnect N/A
- * "sub" subscribe { t: topic, s: subscription id }
- * "uns" unsubscribe { s: subscription id }
- * "pub" publish { t: topic, d: data }
- */
- OpenAjax.hub.IframePMHubClient.prototype._sendMessage = function(type, payload) {
- var msg = JSON.stringify({
- m: type,
- i: this._internalID,
- t: this._securityToken,
- p: payload
- });
- this._postMessage(window.parent, msg, this._partnerOrigin);
- }
- ////////////////////////////////////////////////////////////////////////////////
- /*
- Copyright 2006-2009 OpenAjax Alliance
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- //XXX revert r231 - Revision 231 added support for having the client pass back
- //both the initial URI and the current URI, which are different in the case
- //or redirection. However, in order for this to work, the final client code
- //must set smash._initialClientURI to the initial URI (the URI for the page
- //that did the redirection). There isn't a clean way to do this with the
- //current Hub 2.0 APIs, so I'm disabling this feature for now. Search the code
- //for "XXX revert r231".
- if (typeof OpenAjax === "undefined") {
- OpenAjax = { hub: {} };
- }
- OpenAjax.hub.IframeFIMContainer = function(container, hub, clientID, params) {
- this._container = container;
- this._hub = hub;
- this._onSecurityAlert = params.Container.onSecurityAlert;
- this._onConnect = params.Container.onConnect ? params.Container.onConnect : null;
- this._onDisconnect = params.Container.onDisconnect ? params.Container.onDisconnect : null;
- this._scope = params.Container.scope || window;
- // XXX Need to make sure URI is absolute, or change the "clientURI!=componentURI"
- // comparison in SEComm.initializationFinished (where 'clientURI' is always
- // absolute, but 'componentURI' is based on params.IframeContainer.uri and
- // may be relative, which makes the comparison fail)
- this._clientURI = params.IframeContainer.uri;
- smash.SEComm.tunnelURI = params.IframeContainer.tunnelURI;
- smash._loadTimeout = params.IframeContainer.timeout || 15000;
- if (params.Container.log) {
- var scope = this._scope;
- var logfunc = params.Container.log;
- this._log = function(msg) {
- logfunc.call(scope, "IframeContainer::" + clientID + ": " + msg);
- };
- this._doLog = true; // HW Optimization
- } else {
- this._log = function() {
- };
- }
- // configurable goodbyeMessage: protects against malicious unloading of the mashup application
- //if (params.goodbyeMessage != null) {
- //smash._goodbyeMessage = params.goodbyeMessage;
- //}
- // configurable securityTokenLength
- //if (params.securityTokenLength != null) {
- //smash._securityTokenLength = params.securityTokenLength;
- //smash._computeOtherTokenConstants();
- //}
- // create and configure the pseudo-random number generator, used to create
- // security tokens
- smash._createPRNG(this, params);
- smash._ensureSingletonManager();
- // the 'internal ID' is guaranteed to be unique within the page, not just
- // the ManagedHub instance
- this._internalID = smash._singletonManager.generateUniqueClientName(clientID);
- }
- OpenAjax.hub.IframeFIMContainer.prototype.getHub = function() {
- return this._hub;
- };
- OpenAjax.hub.IframeFIMContainer.prototype.sendToClient = function(topic, data, subscriptionID) {
- smash._singletonManager.sendToClient(this._internalID, topic, data, [ subscriptionID ]);
- }
- OpenAjax.hub.IframeFIMContainer.prototype.remove = function() {
- /**
- * Cleans up data-strucrures for communication with the given client. Needs to be called prior to unloading of the
- * client to prevent false positives about 'frame phishing' attacks.
- * smash.prepareForUnload(clientName: string)
- */
- return smash._singletonManager.prepareForUnload(this._internalID);
- }
- OpenAjax.hub.IframeFIMContainer.prototype.isConnected = function() {
- return smash._singletonManager.isConnected(this._internalID);
- }
- OpenAjax.hub.IframeFIMContainer.prototype.getPartnerOrigin = function() {
- return smash._singletonManager.getPartnerOrigin(this._internalID);
- }
- OpenAjax.hub.IframeFIMContainer.prototype.getURI = function() {
- /**
- * Prepares for loading of a client in a separate iframe. In addition to setting up internal data-structures,
- * it updates the URI (potentially adding a fragment identifier and URI parameters).
- * The updates are necessary to pass values needed to bootstrap communication.
- *
- * string smash.prepareForLoad({clientName: string, uri: string,
- * [commErrorCallback:function(clientName:string, error:string)]})
- * return value of null indicates failure, a non-null return value is the updated URI
- */
- var that = this;
- function errorCallback(clientID, error) {
- var alertType = null;
- switch (error) {
- case smash.SecurityErrors.INVALID_TOKEN:
- case smash.SecurityErrors.TOKEN_VERIFICATION_FAILED:
- alertType = OpenAjax.hub.SecurityAlert.ForgedMsg;
- break;
- case smash.SecurityErrors.TUNNEL_UNLOAD:
- alertType = OpenAjax.hub.SecurityAlert.FramePhish;
- break;
- case smash.SecurityErrors.COMPONENT_LOAD:
- alertType = OpenAjax.hub.SecurityAlert.LoadTimeout;
- break;
- }
- try {
- that._onSecurityAlert.call(that._scope, that._container, alertType);
- } catch(e) {
- OpenAjax.hub._debugger();
- that._log("caught error from onSecurityAlert callback to constructor: " + e.message);
- }
- }
- var newURI = smash._singletonManager.prepareForLoad({ clientName: this._internalID,
- uri: this._clientURI, commErrorCallback: errorCallback,
- oaaContainer: this, log: this._log });
- if (newURI && OpenAjax.hub.enableDebug) newURI += ":debug"; // REMOVE ON BUILD
- return newURI;
- }
- //------------------------------------------------------------------------------
- OpenAjax.hub.IframeFIMHubClient = function(client, params) {
- // XXX Since server redirection breaks hash communication (the server does
- // not receive the fragment value, therefore the final URL does not contain
- // this information), the initial message is transmitted as a URL param.
- // The SMash code, though, expects messages after the hash. So we copy
- // the initial message value into the fragment.
- var initialMsg = new RegExp("[\\?&]oahm=([^&#]*)").exec(window.location.search);
- if (! initialMsg) {
- throw new Error(OpenAjax.hub.Error.WrongProtocol);
- }
- initialMsg = initialMsg[1];
- // check communications protocol ID
- var partnerProtocolID = initialMsg.split(":", 1);
- if (partnerProtocolID[0] != smash._protocolID) {
- throw new Error(OpenAjax.hub.Error.WrongProtocol);
- }
- // remove protocol ID from initialMsg, since decodeMessage() doesn't
- // expect it
- initialMsg = initialMsg.substring(partnerProtocolID[0].length + 1);
- // copy initial message into URL fragment
- var url = window.location.href + "#" + initialMsg;
- window.location.replace(url);
- this._client = client;
- this._onSecurityAlert = params.HubClient.onSecurityAlert;
- this._scope = params.HubClient.scope || window;
- // pull out client id from initial message
- var re = new RegExp("\\d{3}.{" + smash._securityTokenLength + "}.{" + smash._securityTokenLength + "}\\d{3}(.*)");
- var payload = re.exec(initialMsg)[1];
- var parts = payload.split(":");
- var internalID = decodeURIComponent(parts[0]);
- this._id = internalID.substring(internalID.indexOf("_") + 1);
- if (parts[2] && parts[2] == "debug") OpenAjax.hub.enableDebug = true; // REMOVE ON BUILD
- if (params.HubClient.log) {
- var id = this._id;
- var scope = this._scope;
- var logfunc = params.HubClient.log;
- this._log = function(msg) {
- logfunc.call(scope, "IframeHubClient::" + id + ": " + msg);
- };
- this._doLog = true; // HW Optimization
- } else {
- this._log = function() {
- };
- }
- this._connected = false;
- this._subs = {};
- this._subIndex = 1; // HW FIX
- // create and configure the pseudo-random number generator, used to create
- // security tokens
- smash._createPRNG(this, params);
- // configurable initialClientURI: only for those clients which perform URI redirection
- // at client load time
- //XXX revert r231
- //if (params.initialClientURI) {
- //smash._initialClientURI = params.initialClientURI;
- //}
- }
- /*** OpenAjax.hub.HubClient interface implementation ***/
- OpenAjax.hub.IframeFIMHubClient.prototype.connect = function(onComplete, scope) {
- if (smash._singletonClientHub == null) {
- // allow a null clientName since the SMash provider can find it in the fragment.
- smash._singletonClientHub = new smash.SEHubClient(null, this._log);
- // set to be notified of security errors
- var that = this;
- smash._singletonClientHub.setSecurityErrorCallback(function(errorcode) {
- if (errorcode != smash.SecurityErrors.INVALID_TOKEN) {
- that._log("unknown smash security error: " + errorcode);
- }
- try {
- that._onSecurityAlert.call(that._scope, that._client, OpenAjax.hub.SecurityAlert.ForgedMsg);
- } catch(e) {
- OpenAjax.hub._debugger();
- that._log("caught error from onSecurityAlert callback to constructor: " + e.message);
- }
- });
- }
- var that = this;
- function cb(success, seHubClient) {
- if (success) {
- that._connected = true;
- }
- if (onComplete) {
- try {
- onComplete.call(scope, that._client, success); // XXX which error to return when success == false?
- } catch(e) {
- OpenAjax.hub._debugger();
- that._log("caught error from onComplete callback to HubClient.connect(): " + e.message);
- }
- }
- }
- smash._singletonClientHub.connect(cb);
- }
- OpenAjax.hub.IframeFIMHubClient.prototype.disconnect = function(onComplete, scope) {
- this._connected = false;
- var that = this;
- function cb(success, seHubClient) {
- // XXX what happens if success == false
- if (onComplete) {
- try {
- onComplete.call(scope, that._client, success); // XXX which error to return when success == false?
- } catch(e) {
- OpenAjax.hub._debugger();
- that._log("caught error from onComplete callback to HubClient.disconnect(): " + e.message);
- }
- }
- }
- smash._singletonClientHub.disconnect(cb);
- }
- OpenAjax.hub.IframeFIMHubClient.prototype.getPartnerOrigin = function() {
- return smash._singletonClientHub ? smash._singletonClientHub.getPartnerOrigin() : null;
- }
- OpenAjax.hub.IframeFIMHubClient.prototype.getClientID = function() {
- return this._id;
- }
- /*** OpenAjax.hub.Hub interface implementation ***/
- OpenAjax.hub.IframeFIMHubClient.prototype.subscribe = function(topic, onData, scope, onComplete, subscriberData) {
- var subID = "" + this._subIndex++;
- var that = this;
- var completeCallback = ! onComplete ? null :
- function (success, subHandle, error) {
- try {
- onComplete.call(scope, subID, success, error);
- } catch(e) {
- OpenAjax.hub._debugger();
- that._log("caught error from onComplete callback to HubClient.subscribe(): " + e.message);
- }
- };
- function dataCallback(subHandle, topic, data) {
- try {
- onData.call(scope, topic, data, subscriberData);
- } catch(e) {
- OpenAjax.hub._debugger();
- that._log("caught error from onData callback to HubClient.subscribe(): " + e.message);
- }
- }
- this._subs[ subID ] = smash._singletonClientHub.subscribe(topic, completeCallback, dataCallback, scope, subscriberData);
- return subID;
- }
- OpenAjax.hub.IframeFIMHubClient.prototype.publish = function(topic, data) {
- smash._singletonClientHub.publish(topic, data);
- }
- OpenAjax.hub.IframeFIMHubClient.prototype.unsubscribe = function(subID, onComplete, scope) {
- if (! this._subs[ subID ]) {
- throw new Error(OpenAjax.hub.Error.NoSubscription);
- }
- var that = this;
- function cb(success, subHandle) {
- delete that._subs[ subID ];
- if (onComplete) {
- try {
- onComplete.call(scope, subID, success/*, error*/);
- } catch(e) {
- OpenAjax.hub._debugger();
- that._log("caught error from onComplete callback to HubClient.unsubscribe(): " + e.message);
- }
- }
- }
- ;
- this._subs[ subID ].unsubscribe(cb);
- }
- OpenAjax.hub.IframeFIMHubClient.prototype.isConnected = function() {
- return this._connected;
- }
- OpenAjax.hub.IframeFIMHubClient.prototype.getScope = function() {
- return this._scope;
- }
- OpenAjax.hub.IframeFIMHubClient.prototype.getSubscriberData = function(subID) {
- var sub = this._subs[ subID ];
- if (sub) {
- return sub.getSubscriberData();
- }
- throw new Error(OpenAjax.hub.Error.NoSubscription);
- }
- OpenAjax.hub.IframeFIMHubClient.prototype.getSubscriberScope = function(subID) {
- var sub = this._subs[ subID ];
- if (sub) {
- return sub.getSubscriberScope();
- }
- throw new Error(OpenAjax.hub.Error.NoSubscription);
- }
- ////////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////////
- if (typeof(smash) == 'undefined') {
- var smash = {};
- }
- //Ideally, should use a closure for private (and public) data and functions,
- //but this was easier for the initial SMash refactoring.
- smash._singletonManager = undefined; // the singleton that implements all the manager-side SPI
- smash._singletonClientHub = undefined; // the singleton that implements all the client-side SPI
- smash._protocolID = "openajax-2.0";
- //smash._goodbyeMessage = undefined; // The goodbye message sent when unloading the mashup page. Protects against malicious unloading of the mashup application. If undefined, no message is displayed
- //smash._loadTimeout = 20000; // The default timeout time during loading of a component. The lower the value the higher the security against frame-phishing but also the higer the chance of false detections.
- //XXX revert r231
- //smash._initialClientURI = undefined; // For use by the smash provider loaded by a client. Should only be changed from the default value if the client does URI redirection at load time. Otherwise, we will assume that the current URI was also the initial URI
- //--- security token stuff ---
- //configurable pseudo random number generator (prng) to use for generating the security token.
- //If not set, we use Math.random.
- //If set, the provided random number generator must support a function nextRandomB64Str(strlength:integer)
- //that returns a string of length strlength, where each character is a "modified Base64 for URL" character.
- //This includes A-Z, a-z, and 0-9 for the first 62 digits, like standard Base64 encoding, but
- //no padding '='. And the '+', '/' characters of standard Base64 are replaced by '-', '_'.
- smash._prng = undefined;
- smash._securityTokenLength = 6; // configurable security token length. If default value is not used, both manager and clients have to change it to the same value.
- smash._securityTokenOverhead = null; // the number of characters in a serialized message consumed by the security tokens
- smash._computeOtherTokenConstants = function() {
- smash._securityTokenOverhead = 2 * smash._securityTokenLength;
- smash._multiplier = Math.pow(10, smash._securityTokenLength - 1);
- }
- smash._computeOtherTokenConstants();
- smash._createPRNG = function(container, params) {
- if (! smash._prng) {
- // create pseudo-random number generator with a default seed
- var seed = new Date().getTime() + Math.random() + document.cookie;
- smash._prng = smash.crypto.newPRNG(seed);
- }
- var p = params.IframeContainer || params.IframeHubClient;
- if (p && p.seed) {
- try {
- var extraSeed = p.seed.call(container._scope);
- smash._prng.addSeed(extraSeed);
- } catch(e) {
- OpenAjax.hub._debugger();
- container._log("caught error from 'seed' callback: " + e.message);
- }
- }
- }
- /**
- * Randomly generates the security token which will be used to ensure message integrity.
- */
- smash._keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
- smash._generateSecurityToken = function() {
- var r;
- if (smash._prng)
- r = smash._prng.nextRandomB64Str(smash._securityTokenLength);
- else {
- var r1 = Math.random(); // value in (0,1)
- r = "";
- // assuming one Math.random() value has enough bits for smash._securityTokenLenght
- for (var i = 0; i < smash._securityTokenLength; i++) {
- var r2 = r1 * 64; // get the most significant base-64 value
- var c = Math.floor(r2);
- r1 = (r2 - c); // the remaining fractional value
- r = r + smash._keyStr.charAt(c);
- }
- }
- return r;
- }
- //------------------------- manager-side implementation ------------------------
- /**
- * lazy creation of the manager-side singleton
- */
- smash._ensureSingletonManager = function() {
- if (smash._singletonManager == null)
- smash._singletonManager = new smash.SEHub();
- }
- /**
- * Constructor.
- * The name SEHub is legacy. The provider on the manager-side does not implement any of the hub functionality
- * other than communication.
- */
- smash.SEHub = function() {
- // This is used to make the object available to the private methods. This is a workaround for an error in the ECMAScript Language Specification which causes this to be set incorrectly for inner functions. See http://www.crockford.com/javascript/private.html
- var that = this;
- // associative array indexed by componentId. Each element is a ComponentInfo object.
- // Component is synonymous with client. componentId is the same as clientName
- this.componentInfo = [];
- this._subs = [];
- /**
- * Constructor for ComponentInfo objects
- */
- function ComponentInfo(uri, eCallback) {
- this.uri = uri;
- //this.state = smash.SEHubConstants.START;
- this.connected = false;
- this.errorCallback = eCallback;
- }
- // create an ID that is unique within the page
- this.generateUniqueClientName = function(clientName) {
- do {
- clientName = ((0x7fff * Math.random()) | 0).toString(16) + "_" + clientName;
- } while (that.componentInfo[ clientName ]);
- return clientName;
- }
- // securityListener function registered for each component's security events
- function securityListener(errorType, clientName) {
- //var errorString = that.getSecurityErrorString(errorType); // get the error as a string
- var ci = that.componentInfo[clientName];
- if (ci != null) {
- var errorCallback = ci.errorCallback; // the errorCallback registered by the application
- if (errorCallback != null) { // if one was registered
- // errorCallback(clientName, errorString);
- errorCallback(clientName, errorType);
- }
- }
- }
- /**
- * string prepareForLoad({clientName: string, uri: string,
- * [commErrorCallback:function(clientName:string, error:string)]})
- * return value of null indicates failure, a non-null return value is the updated URI
- */
- this.prepareForLoad = function(params) {
- var clientName = params.clientName; // componentId and clientName are the same thing in this code
- var componentURI = params.uri;
- if ((clientName == null) || (componentURI == null))
- return null;
- if (that.componentInfo[clientName] != null) {
- return null;
- }
- that.componentInfo[clientName] = new ComponentInfo(componentURI, params.commErrorCallback);
- that.componentInfo[clientName].seComm = new smash.SEComm(); //The SEComm library used for this component
- that.componentInfo[clientName].seComm.setSecurityListener(securityListener);
- that.componentInfo[clientName].oaaContainer = params.oaaContainer;
- return that.componentInfo[clientName].seComm.prepareForLoad(clientName, componentURI, that, smash._loadTimeout, params.log);
- }
- /**
- * boolean prepareForUnload(clientName: string)
- */
- this.prepareForUnload = function(clientName) {
- if (!that.componentInfo[clientName]) {
- // component does not exist.
- return true;
- }
- //// change state. pretty useless, since going to delete anyway
- //that.componentInfo[clientName].state = smash.SEHubConstants.UNLOADED;
- that._disconnect(clientName);
- that.componentInfo[clientName].seComm.prepareForUnload();
- // remove the relevant objects
- delete that.componentInfo[clientName];
- return true;
- }
- /**
- * boolean isConnected(clientName:string)
- */
- this.isConnected = function(clientName) {
- //if ( that.componentInfo[clientName] && that.componentInfo[clientName].state == smash.SEHubConstants.LOADED )
- if (that.componentInfo[clientName] && that.componentInfo[clientName].connected) {
- return true;
- }
- return false;
- }
- /**
- * sendToClient(clientName:string, topic: string, data:JSON|string, matchingSubs:array of string)
- */
- this.sendToClient = function(clientName, topic, data, matchingSubs) {
- // send to the component
- if (that.isConnected(clientName)) {
- var comms = that.componentInfo[clientName].seComm;
- if (comms) {
- comms.distribute(topic, matchingSubs, data);
- }
- }
- }
- /** Callback when component loaded */
- this.componentLoaded = function(clientName, partnerURL) {
- if (that.componentInfo[clientName]) {
- // that.componentInfo[clientName].state = smash.SEHubConstants.LOADED;
- that.componentInfo[clientName].connected = true;
- that.componentInfo[clientName].partnerOrigin = new RegExp("^([a-zA-Z]+://[^:/?#]+).*").exec(partnerURL)[1];
- var oaaContainer = that.componentInfo[ clientName ].oaaContainer;
- oaaContainer._container.getIframe().style.visibility = "visible";
- if (oaaContainer._onConnect) {
- try {
- oaaContainer._onConnect.call(oaaContainer._scope, oaaContainer._container);
- } catch(e) {
- OpenAjax.hub._debugger();
- oaaContainer._log("caught error from onConnect callback to constructor: " + e.message);
- }
- }
- }
- }
- /**
- * A message received from a component
- * @param componentId The component that sent the message
- * @param topic
- * @param message The payload of the message (JSON|string)
- */
- this.publishInternal = function(componentId, topic, message) {
- if (that.componentInfo[componentId]) {
- // component exists
- var oaaContainer = that.componentInfo[ componentId ].oaaContainer;
- oaaContainer._hub.publishForClient(oaaContainer._container, topic, message);
- }
- }
- /**
- * A subscribe message received from a component
- * @param componentId The component that sent the message
- * @param subId The subscription id
- * @param topic
- */
- this.subscribeInternal = function(componentId, subId, topic) {
- var oaaContainer = that.componentInfo[ componentId ].oaaContainer;
- that._subs[ subId ] = oaaContainer._hub.subscribeForClient(oaaContainer._container, topic, subId);
- }
- /**
- * A unsubscribe message received from a component
- * @param componentId The component that sent the message
- * @param subId
- * @returns true if unsubscribe was accepted else false
- */
- this.unsubscribeInternal = function(componentId, subId) {
- try {
- var handle = that._subs[ subId ];
- var oaaContainer = that.componentInfo[ componentId ].oaaContainer;
- oaaContainer._hub.unsubscribeForClient(oaaContainer._container, handle);
- return true;
- } catch(e) {
- }
- return false;
- }
- this.disconnect = function(componentId) {
- that._disconnect(componentId);
- var oaaContainer = that.componentInfo[ componentId ].oaaContainer;
- if (oaaContainer._onDisconnect) {
- try {
- oaaContainer._onDisconnect.call(oaaContainer._scope, oaaContainer._container);
- } catch(e) {
- OpenAjax.hub._debugger();
- oaaContainer._log("caught error from onDisconnect callback to constructor: " + e.message);
- }
- }
- }
- this._disconnect = function(componentId) {
- if (that.componentInfo[ componentId ].connected) {
- that.componentInfo[ componentId ].connected = false;
- // hide component iframe
- var oaaContainer = that.componentInfo[ componentId ].oaaContainer;
- oaaContainer._container.getIframe().style.visibility = "hidden";
- // unsubscribe from all subs
- for (var sub in that._subs) {
- oaaContainer._hub.unsubscribeForClient(oaaContainer._container, that._subs[ sub ]);
- }
- that._subs = [];
- }
- }
- this.getPartnerOrigin = function(componentId) {
- if (that.componentInfo[ componentId ].connected) {
- return that.componentInfo[ componentId ].partnerOrigin;
- }
- return null;
- }
- /**
- * Converts a security error code into a readable error message.
- * @param error The error code.
- */
- //this.getSecurityErrorString = function(error) {
- //switch (error) {
- // case smash.SecurityErrors.INVALID_TOKEN: return smash.SecurityErrors.INVALID_TOKEN_MSG;
- // case smash.SecurityErrors.TOKEN_VERIFICATION_FAILED: return smash.SecurityErrors.TOKEN_VERIFICATION_FAILED_MSG;
- // case smash.SecurityErrors.TUNNEL_UNLOAD: return smash.SecurityErrors.TUNNEL_UNLOAD_MSG;
- // case smash.SecurityErrors.COMPONENT_LOAD: return smash.SecurityErrors.COMPONENT_LOAD_MSG;
- // default: return "UNKNOWN";
- //}
- //}
- /**
- * Sets the unload function which shows the goodbye message.
- */
- //window.onunload=function(){
- //if (smash._goodbyeMessage != undefined)
- // alert(smash._goodbyeMessage);
- //}
- }
- //---------- client-side implementation ----------------------------------------
- /**
- * SEHubClient implementation linking the SECommClient together with the component side logic.
- */
- smash.SEHubClient = function(clientName, logfunc) {
- //-------- interface implemented by connHandle in Hub 1.1. We use the SEHub instance itself
- //-------- as the connHandle object for the "manager".
- this.equals = function(anotherConn) {
- return that === anotherConn;
- }
- this.isConnected = function() {
- return connected;
- }
- this.getClientName = function() {
- return clientName;
- }
- this.connect = function(callback) {
- connectCallback = function(success) {
- if (success) {
- connected = true;
- }
- callback(success, that);
- };
- seCommClient.connect(connectCallback);
- }
- this.disconnect = function(callback) {
- disconnectCallback = function(success) {
- if (success) {
- connected = false;
- subHandles = []; // delete all existing subscriptions
- }
- callback(success, that);
- };
- seCommClient.disconnect();
- return;
- }
- /**
- * connHandle.subscribe(topic:string, callback:function, eventCallback:function)
- * returns a subHandle object, or null if it fails immediately.
- */
- this.subscribe = function(topic, callback, eventCallback, scope, subscriberData) {
- // keep track of the callback so that the incomming message can be distributed correctly
- var subId = (subCount + ''); // assign the subscription id - making it a string
- subCount++;
- subHandles[subId] = new SubHandle(subId, topic, callback, eventCallback, that, scope, subscriberData);
- seCommClient.subscribe(subId, topic);
- return subHandles[subId];
- }
- /**
- * connHandle.publish(topic:string, data:JSON|string)
- */
- this.publish = function(topic, data) {
- seCommClient.publish(topic, data);
- return true;
- }
- function SubHandle(subId, topic, callback, eventCallback, sehubClient, scope, subscriberData) {
- var _isSubscribed = false;
- var _data = subscriberData;
- var _scope = scope;
- var that = this;
- this.getTopic = function() {
- return topic;
- }
- this.getConnHandle = function() {
- return sehubClient;
- }
- this.equals = function(anotherSubHandle) {
- if ((anotherSubHandle._getSubId != null) && (typeof anotherSubHandle._getSubId == "function")
- && (anotherSubHandle.getConnHandle != null) && (typeof anotherSubHandle.getConnHandle == "function")) {
- if ((subId === anotherSubHandle._getSubId()) && (sehubClient === anotherSubHandle.getConnHandle()))
- return true;
- }
- return false;
- }
- this.isSubscribed = function() {
- return _isSubscribed;
- }
- this.unsubscribe = function(callback) {
- return sehubClient._unsubscribe(that, callback);
- }
- this.getSubscriberData = function() {
- return _data;
- }
- this.getSubscriberScope = function() {
- return _scope;
- }
- this._getSubId = function() {
- return subId;
- }
- this._setIsSubscribed = function(value) {
- _isSubscribed = value;
- }
- this._getCallback = function() {
- return callback;
- }
- this._getEventCallback = function() {
- return eventCallback;
- }
- }
- this.getPartnerOrigin = function() {
- if (connected && seCommClient != null) {
- var ptu = seCommClient.getParsedTunnelUrl();
- if (ptu != null) {
- return ptu.scheme + "://" + ptu.host;
- }
- }
- return null;
- }
- //-------- end of interface implemented by connHandle in Hub 1.1.
- //------- addition public interfaces not part of Hub 1.1 -----
- /**
- * Set a callback to find out about security errors.
- * Not part of the OpenAjax Hub 1.1 standard
- */
- this.setSecurityErrorCallback = function(errorcallback) {
- securityErrorCallback = errorcallback;
- }
- //this.getManagerDomain = function() {
- //if (seCommClient != null) {
- // var ptu = seCommClient.getParsedTunnelUrl();
- // if (ptu != null) return ptu.host;
- //}
- //return null;
- //}
- //------- private stuff ------
- /**
- * _unsubscribe(subHandle:object, callback:function)
- * returns a subHandle object, or null if it fails immediately.
- */
- this._unsubscribe = function(subHandle, callback) {
- var subId = subHandle._getSubId();
- if (! subHandles[ subId ]) {
- throw new Error(OpenAjax.hub.Error.NoSubscription);
- }
- subHandles[subId] = undefined;
- seCommClient.unsubscribe(subId);
- // no async callback as no confirmation message from manager
- if (callback != null) {
- callback(true, subHandle); // function(success:boolean, subHandle:object).
- }
- return subHandle;
- }
- var securityErrorCallback = undefined; // securityErrorCallback registered by the application in this component/frame
- // subscriptions: each subscription is assigned an integer id that is unique to this client
- var subCount = 0;
- // mapping the subscription ids to the SubHandles
- var subHandles = [];
- // SECommClient serving the communication between the SEHub and the SEHub client
- var seCommClient = new smash.SECommClient(clientName, logfunc);
- //var state = smash.SEHubConstants.LOADED; // initialize my state to LOADED.
- var connected = false;
- // This is used to make the object available to the private methods. This is a workaround for an error in the ECMAScript Language Specification which causes this to be set incorrectly for inner functions. See http://www.crockford.com/javascript/private.html
- var that = this;
- var connectCallback = null;
- var disconnectCallback = null;
- /**
- * Processes messages received by the SECommClient
- * @param message The actual message.
- */
- function handleIncomingMessage(message) {
- if (! connected && message.type != smash.SECommMessage.CONNECT_ACK) {
- return;
- }
- switch (message.type) {
- case smash.SECommMessage.DISTRIBUTE:
- if ((message.additionalHeader != null) && (message.additionalHeader.s != null)) {
- var subs = message.additionalHeader.s;
- for (var i = 0; i < subs.length; i++) {
- var subId = subs[i];
- if ((subId != null) && (subHandles[subId] != null)) {
- var eventCallback = subHandles[subId]._getEventCallback();
- if (eventCallback != null)
- eventCallback(subHandles[subId], message.topic, message.payload);
- }
- }
- }
- break;
- case smash.SECommMessage.SUBSCRIBE_ACK:
- if (message.additionalHeader != null) {
- var subId = message.additionalHeader.subId;
- var isOk = message.additionalHeader.isOk;
- var err = message.additionalHeader.err;
- if ((subId != null) && (isOk != null)) {
- if (subHandles[subId] != null) {
- var callback = subHandles[subId]._getCallback();
- if (isOk) {
- subHandles[subId]._setIsSubscribed(true);
- if (callback != null)
- callback(true, subHandles[subId]);
- }
- else {
- if (callback != null)
- callback(false, subHandles[subId], err);
- subHandles[subId] = undefined; // unsubscribe
- }
- }
- }
- }
- // else ignore the message
- break;
- case smash.SECommMessage.CONNECT_ACK:
- connectCallback(true);
- break;
- case smash.SECommMessage.DISCONNECT_ACK:
- disconnectCallback(true);
- break;
- }
- }
- function securityListenerCallback(errorcode) {
- //var errorString = getSecurityErrorString(errorcode);
- if (securityErrorCallback != null) {
- // securityErrorCallback(errorString);
- securityErrorCallback(errorcode);
- }
- else {
- throw new Error(errorString);
- }
- }
- //function getSecurityErrorString(error) {
- //switch (error) {
- // case smash.SecurityErrors.INVALID_TOKEN: return smash.SecurityErrors.INVALID_TOKEN_MSG;
- // default: return "UNKNOWN";
- //}
- //}
- // Override the SECommClient's received method with our own implementation
- seCommClient.handleIncomingMessage = handleIncomingMessage;
- seCommClient.setSecurityListener(securityListenerCallback);
- }
- //-----------------------------------------------------------------------------------------------
- //smash.SEHubConstants = {
- //
- //// Constants representing states of a component.
- //// Component State Machine: START -> LOADED -> UNLOADED
- //
- //START: 0,
- //LOADED: 1,
- //UNLOADED: 2
- //
- //};
- //-----------------------------------------------------------------------------------------------
- /**
- * Constants representing the different types of attacks that can be detected and prevented by the library.
- */
- smash.SecurityErrors = {
- // This error occurs when the CommLib detects a message with a different security token than the one with wich it was initialized.
- INVALID_TOKEN: 0,
- //INVALID_TOKEN_MSG: "The sender of the received message could not be verified because the received security token was not correct.",
- // This error occurs when the SEComm receives a different security token than the one that was sent by the SEComm during the loading of the component.
- TOKEN_VERIFICATION_FAILED: 1,
- //TOKEN_VERIFICATION_FAILED_MSG: "The security token could not be verified. A different security token than the one that was sent during the loading of the component was received after loading.",
- // Phishing error
- TUNNEL_UNLOAD: 2,
- //TUNNEL_UNLOAD_MSG: "The tunnel was unloaded without the component being unloaded by the mashup application. Frame-phishing may have occured after the component was loaded successfully.",
- // Phishing error before successfull load
- COMPONENT_LOAD: 3
- //COMPONENT_LOAD_MSG: "A timeout occured before the communication channel between the component and the mashup application was set up correctly. Frame-phishing may have occured during the loading of the component."
- };
- //-----------------------------------------------------------------------------------------------
- /**
- * The object implementing the message serializer and deserializer for use in SEComm.
- * The topic and payload are typically under application control and may contain URI reserved characters.
- * These will be percent-encoded and decoded, and the application has to deal with the composition issues
- * if it is passing in data or topics that are already percent-encoded.
- */
- smash.SECommMessage = function() {
- // The type of the message. A string
- this.type = null;
- // The topic of the message. A string
- this.topic = null;
- // The remaining header information. A JSON object
- this.additionalHeader = null;
- // The payload of the message. A string
- this.payload = null;
- // The name used in the name value pair transmission. one character for efficiency. only use a letter or number
- var typeName = "y";
- var topicName = "t";
- var additionalHeaderName = "h"; // other header information that is not handled by typeName and topicName
- var payloadName = "p";
- /**
- * Serializes the message into a string which can be transmitted over a communication channel.
- * URI-encodes the topic and payload and uses "=", "&" as separators. The communication channel
- * must not perform any URI-encoding as "=", "&" are not reserved for fragments.
- * If using something other than fragment messaging at the communication channel, the serialization
- * may need to change.
- * @returns The serialized message.
- */
- this.serialize = function() {
- var returnValue = typeName + "=" + this.type;
- if (this.topic != null) {
- var topicString = encodeURIComponent(this.topic);
- var topicSer = "&" + topicName + "=" + topicString;
- returnValue += topicSer;
- }
- if (this.additionalHeader != null) {
- var headerString = encodeURIComponent(JSON.stringify(this.additionalHeader));
- var headerSer = "&" + additionalHeaderName + "=" + headerString;
- returnValue += headerSer;
- }
- if (this.payload != null) {
- var payloadString = encodeURIComponent(this.payload);
- var payloadSer = "&" + payloadName + "=" + payloadString;
- returnValue += payloadSer;
- }
- return returnValue;
- }
- /**
- * Deserializes a serialized message and initializes the objects parameters.
- */
- this.deserialize = function(serializedMessage) {
- var messageParts = serializedMessage.split("&");
- for (var i = 0; i < messageParts.length; i++) {
- var nameValue = messageParts[i].split("=");
- switch (nameValue[0]) {
- case typeName:
- this.type = nameValue[1];
- break;
- case topicName:
- this.topic = decodeURIComponent(nameValue[1]);
- break;
- case additionalHeaderName:
- var headerString = decodeURIComponent(nameValue[1]);
- this.additionalHeader = JSON.parse(headerString);
- break;
- case payloadName:
- this.payload = decodeURIComponent(nameValue[1]);
- break;
- }
- }
- }
- }
- //only use letters or numbers as characters
- //CONNECT message
- smash.SECommMessage.CONNECT = "con";
- smash.SECommMessage.CONNECT_ACK = "cac";
- //DISCONNECT message
- smash.SECommMessage.DISCONNECT = "xcon";
- smash.SECommMessage.DISCONNECT_ACK = "xac";
- //PUBLISH message: additionalHeader is {f:"S"} or {f:"J"} representing that the payload is a string or JSON,
- //topic and payload are topic, payload of message
- smash.SECommMessage.PUBLISH = "pub";
- //DISTRIBUTE message: additionalHeader is {f: string, s:[string, ...]} where f is defined as in the PUBLISH message,
- //and s representing subIds that should receive this message; topic and payload are as in PUBLISH message
- smash.SECommMessage.DISTRIBUTE = "dis";
- //SUSCRIBE message: additionalHeader is {subId: string}, payload==null, topic is subscription topic
- smash.SECommMessage.SUBSCRIBE = "sub";
- //UNSUBSCRIBE message: additionalHeader is {subId: string}, topic==null, payload==null
- smash.SECommMessage.UNSUBSCRIBE = "uns";
- //SUBCRIBE_ACK message: additionalHeader is {subId: string, isOk: boolean, err: string}, topic==null, payload == null
- smash.SECommMessage.SUBSCRIBE_ACK = "sac";
- smash.SECommMessage.ERROR = "err"; // TBD
- //-----------------------------------------------------------------------------------------------
- /**
- * Definitions of exceptions used by SECom
- */
- smash.SECommErrors = {};
- smash.SECommErrors.tunnelNotSetError = new Error("The tunnel URI was not set. Please set the tunnel URI.");
- //smash.SECommErrors.componentNotFoundError = new Error ("The component could not be identified. Please declare the component correctly.");
- //smash.SECommErrors.securityTokenNotVerifiedError = new Error (smash.SecurityErrors.TOKEN_VERIFICATION_FAILED_MSG);
- //smash.SECommErrors.tunnelUnloadError = new Error (smash.SecurityErrors.TUNNEL_UNLOAD_MSG);
- //smash.SECommErrors.componentLoadError = new Error (smash.SecurityErrors.COMPONENT_LOAD_MSG);
- /**
- * Links the SEHub and the SEHubClient together over the communication implemented by CommLib bridge
- *
- * TODO: Check if the component loading allows valid HTML.
- * TODO: Propagate the style of the enclosing tag into the iFrame
- * TODO: Check if there is a better way than polling to see if the tunnel's commLib has been registered
- */
- smash.SEComm = function() {
- // The timer used to delay the phishing message. This makes sure that a page navigation does not cause phishing errors.
- // Setting it to 1 ms is enough for it not to be triggered on regular page navigations.
- var unloadTimer = 1;
- // Variable storing the identifier for the setInterval if processing a registrationTimer
- var registrationTimerProcess = null;
- var loadTimeout = 0;
- var reconnectTimerProcess = null;
- // The URI of the component being manages by this SEComm.
- var componentURI = null;
- // The commLib of the tunnel
- var commLib = null;
- // Variable storing the identifier to clear when the setInterval is called
- var commLibPoll = null;
- // The HTML id of the component for which this is a SEComm
- var componentID = null;
- // A queue for outgoing messages. This queue is used when new send requests are done while we are still sending or receiving a message.
- var queueOut = [];
- // Variable storing the identifier for the setInterval if processing an output queue
- var queueOutProcess = null;
- // Variable storing a reference to the SEHub which is managing this SEComm
- var seHUB = null;
- // The iframe in which the component is loaded
- var myIframe = null;
- // The security token used for this component
- var securityTokenParent = null;
- // Variable storing the callback to the security listener function
- var securityListener = null;
- // This is used to make the object available to the private methods. This is a workaround for an error in the ECMAScript Language Specification which causes this to be set incorrectly for inner functions. See http://www.crockford.com/javascript/private.html
- var that = this;
- // keeps track of the initialization
- var initialized = false;
- // logging function
- var logfunc = null;
- /**
- * Sets the callback for security errors.
- *
- * @param The callback for security errors.
- */
- this.setSecurityListener = function(callback) {
- securityListener = callback;
- }
- function insertURLParams(uri, params) {
- var parts = uri.split("?");
- if (parts.length > 1) {
- return parts[0] + "?" + params + "&" + parts[1];
- }
- parts = uri.split("#");
- if (parts.length > 1) {
- return parts[0] + "?" + params + parts[1];
- }
- return uri + "?" + params;
- }
- /**
- * Prepares for loading a component into an iframe.
- * @returns The modified URI
- */
- this.prepareForLoad = function(componentId, frameURI, seHub, loadtimeout, logFunc) {
- logfunc = logFunc;
- this.log("Parent connecting to : " + componentId);
- // Store the SEHub
- seHUB = seHub;
- // Store the component Id
- componentID = componentId;
- loadTimeout = loadtimeout;
- // Check if the tunnel is set
- if (smash.SEComm.tunnelURI == null)throw smash.SECommErrors.tunnelNotSetError;
- // modify the URI
- securityTokenParent = smash._generateSecurityToken();
- // include the token twice since the child token value does not matter yet
- //XXX revert r231
- //componentURI = insertURLParams( frameURI, "id=" + encodeURIComponent(componentId) );
- //var modifiedURIWithFragment = componentURI + "#100" + securityTokenParent + securityTokenParent + "000" + encodeURIComponent(componentId) + ":" + encodeURIComponent(smash.SEComm.tunnelURI);
- // Since a server redirect does not take into account the fragment value
- // (it is not transmitted by the browser to the server), the initial
- // message must be sent as a URL param.
- componentURI = insertURLParams(frameURI, "oahm=" + smash._protocolID + ":100" + securityTokenParent + securityTokenParent + "000" + encodeURIComponent(componentId) + ":" + encodeURIComponent(smash.SEComm.tunnelURI));
- // Make the instance available for the tunnel.
- smash.SEComm.instances[componentId] = that;
- // Set a timer which detects if the component loaded successfully
- // We are using an interval not to lose our evaluation context.
- registrationTimerProcess = setInterval(pollForIncomingCommLibTimeout, loadTimeout);
- return componentURI;
- }
- function pollForIncomingCommLibTimeout() {
- clearInterval(registrationTimerProcess);
- registrationTimerProcess = null;
- //No CommLib has been registered.
- if (! commLib) {
- that.handleSecurityError(smash.SecurityErrors.COMPONENT_LOAD);
- }
- }
- function reconnectTimeout() {
- clearInterval(reconnectTimerProcess);
- that.handleSecurityError(smash.SecurityErrors.COMPONENT_LOAD);
- }
- /**
- * Gets the scope. Should only be used by the tunnel during INIT.
- * @returns scope (object) the scope in which the callback needs to be called.
- **/
- this.getScope = function() {
- return this;
- }
- /**
- * Gets the callback. Should only be used by the tunnel during INIT.
- * @param c (string) the name of the callback method
- **/
- this.getCallback = function() {
- return "messageReceived";
- }
- /**
- * Called when the initialisaiton of the library is done and processes all messages in the queue
- */
- this.initializationFinished = function(tunnelCommLib, token, currentClientURI, initialClientURI, tunnelWindow) {
- this.log("Tunnel commLib initialization finished. Processing outgoing queue. Security token: " + token);
- //XXX revert r231
- //// verify the security token and currentClientURI
- //if ((securityTokenParent!=token) || (initialClientURI!=componentURI)) {
- // verify the security token
- if (securityTokenParent != token) {
- that.handleSecurityError(smash.SecurityErrors.TOKEN_VERIFICATION_FAILED);
- return false;
- }
- else {
- commLib = tunnelCommLib;
- initialized = true;
- this.log("Token verified.");
- // register the onunload handler
- tunnelWindow.onunload = tunnelUnloadHandler;
- // switch the state to loaded in the seHUB.
- seHUB.componentLoaded(componentID, currentClientURI);
- // process the current outgoing queue.
- while (queueOut.length > 0)commLib.send(queueOut.shift());
- return true;
- }
- }
- this.prepareForUnload = function() {
- // stop all timers
- if (registrationTimerProcess != null) {
- clearInterval(registrationTimerProcess);
- registrationTimerProcess = null;
- }
- }
- function securityListenerClosure(error, componentId) {
- return function() {
- securityListener(error, componentId);
- }
- }
- this.handleSecurityError = function(error) {
- // if we have a timeout error, then overwrite initializationFinished()
- // to return false by default, in order to prevent client connection
- if (error == smash.SecurityErrors.COMPONENT_LOAD) {
- this.initializationFinished = function() {
- return false;
- }
- }
- if (securityListener == null) {
- throw new Error(error);
- }
- else {
- securityListener(error, componentID);
- }
- return;
- }
- /**
- *
- */
- function tunnelUnloadHandler() {
- if (securityListener == null) {
- setTimeout("throw tunnelUnloadError;", unloadTimer);
- }
- else {
- setTimeout(securityListenerClosure(smash.SecurityErrors.TUNNEL_UNLOAD, componentID), unloadTimer);
- }
- }
- /**
- * Function processing the incomming data from commLib
- *
- * @param message The message containing the incomming data
- */
- this.messageReceived = function (message) {
- var msg = new smash.SECommMessage();
- msg.deserialize(message);
- switch (msg.type) {
- case smash.SECommMessage.PUBLISH:
- if (msg.additionalHeader != null) {
- var payload = msg.payload;
- if (msg.additionalHeader.f == "J")
- payload = JSON.parse(msg.payload);
- seHUB.publishInternal(componentID, msg.topic, payload);
- } // else no additionalHeader defining the payload format. hence ignore the message
- break;
- case smash.SECommMessage.SUBSCRIBE:
- if (msg.additionalHeader != null) {
- var isOk = true;
- var errMsg = "";
- try {
- seHUB.subscribeInternal(componentID, msg.additionalHeader.subId, msg.topic);
- } catch(e) {
- isOk = false;
- errMsg = e.message;
- }
- var msgack = new smash.SECommMessage();
- msgack.type = smash.SECommMessage.SUBSCRIBE_ACK;
- msgack.additionalHeader = {subId: msg.additionalHeader.subId, isOk: isOk, err: errMsg};
- send(msgack.serialize());
- }
- break;
- case smash.SECommMessage.UNSUBSCRIBE:
- if (msg.additionalHeader != null)
- seHUB.unsubscribeInternal(componentID, msg.additionalHeader.subId);
- break;
- case smash.SECommMessage.CONNECT:
- clearInterval(reconnectTimerProcess);
- // switch the state to loaded in the seHUB.
- seHUB.componentLoaded(componentID, msg.payload);
- // send acknowledgement
- var msg = new smash.SECommMessage();
- msg.type = smash.SECommMessage.CONNECT_ACK;
- send(msg.serialize());
- break;
- case smash.SECommMessage.DISCONNECT:
- seHUB.disconnect(componentID);
- // Set a timer which detects if the component reloaded
- // We are using an interval not to lose our evaluation context.
- reconnectTimerProcess = setInterval(reconnectTimeout, loadTimeout);
- // send acknowledgement
- var msg = new smash.SECommMessage();
- msg.type = smash.SECommMessage.DISCONNECT_ACK;
- send(msg.serialize());
- break;
- }
- }
- /**
- * Sends a published message to the partner component
- */
- this.distribute = function(topic, matchingSubs, payload) {
- var msg = new smash.SECommMessage();
- msg.type = smash.SECommMessage.DISTRIBUTE;
- msg.topic = topic;
- msg.additionalHeader = {s: matchingSubs};
- if ((typeof payload) == "string") {
- msg.additionalHeader.f = "S";
- msg.payload = payload;
- }
- else {
- msg.additionalHeader.f = "J";
- msg.payload = JSON.stringify(payload);
- }
- send(msg.serialize());
- }
- function send(message) {
- // Queue the message if sending or if there is no communication partner yet
- if (initialized == false) {
- queueOut.push(message);
- }
- else {
- commLib.send(message);
- }
- }
- this.log = function(msg) {
- logfunc(msg);
- }
- }
- //Static array which contains the list of the currently loaded instances. The array is indexed by the url of the child component.
- smash.SEComm.instances = [];
- //-----------------------------------------------------------------------------------------------
- /**
- * SEHubClient implementation linking the SEComm together with the component side logic.
- */
- smash.SECommClient = function(clientName, logfunc) {
- // Storing the CommLib used for communicating
- var controllers = [];
- controllers["child"] = this;
- var commLib = new smash.CommLib(true, controllers, clientName);
- // This is used to make the object available to the private methods. This is a workaround for an error in the ECMAScript Language Specification which causes this to be set incorrectly for inner functions. See http://www.crockford.com/javascript/private.html
- var that = this;
- // A queue for outgoing messages. This queue is used when new send requests are done while we are still sending or receiving a message.
- var queueOut = [];
- // keeps track of the initialization
- var initialized = false;
- var securityListener = null;
- var jsonPayloadHeader = {f: "J"};
- var stringPayloadHeader = {f: "S"};
- var parsedTunnelUrl = null;
- /**
- * Publishes a message to a certain topic
- * @param topic string
- * @param data JSON|string
- */
- this.publish = function(topic, data) {
- var msg = new smash.SECommMessage();
- msg.type = smash.SECommMessage.PUBLISH;
- msg.topic = topic;
- if ((typeof data) == "string") {
- msg.additionalHeader = stringPayloadHeader;
- msg.payload = data;
- }
- else {
- msg.additionalHeader = jsonPayloadHeader;
- msg.payload = JSON.stringify(data);
- }
- send(msg.serialize());
- }
- /**
- * subscribes to a certain topic
- */
- this.subscribe = function(subId, topic) {
- var msg = new smash.SECommMessage();
- msg.type = smash.SECommMessage.SUBSCRIBE;
- msg.topic = topic;
- msg.additionalHeader = {subId: subId};
- send(msg.serialize());
- }
- this.connect = function(callback) {
- if (initialized) {
- var msg = new smash.SECommMessage();
- msg.type = smash.SECommMessage.CONNECT;
- msg.payload = window.location.href.split("#")[0];
- send(msg.serialize());
- return;
- }
- connectCallback = callback;
- }
- this.disconnect = function() {
- var msg = new smash.SECommMessage();
- msg.type = smash.SECommMessage.DISCONNECT;
- send(msg.serialize());
- }
- /**
- * Called when the initialisaiton of the library is done and processes all messages in the queue
- */
- this.initializationFinished = function(tunnelUrl) {
- this.log("Initialization finished. Processing outgoing queue.");
- parsedTunnelUrl = new ParsedUrl(tunnelUrl);
- initialized = true;
- connectCallback(true);
- while (queueOut.length > 0)commLib.send(queueOut.shift());
- }
- this.getParsedTunnelUrl = function() {
- return parsedTunnelUrl;
- }
- var _regex = new RegExp("^((http|https):)?(//([^/?#:]*))?(:([0-9]*))?([^?#]*)(\\?([^#]*))?");
- function ParsedUrl(url) {
- var matchedurl = url.match(_regex);
- this.scheme = (matchedurl[2] == "") ? null : matchedurl[2];
- this.host = (matchedurl[4] == "") ? null : matchedurl[4];
- this.port = (matchedurl[6] == "") ? null : matchedurl[6];
- this.path = (matchedurl[7] == "") ? null : matchedurl[7];
- this.query = (matchedurl[8] == "") ? null : matchedurl[8];
- }
- /**
- * unsubscribes
- */
- this.unsubscribe = function(subId) {
- var msg = new smash.SECommMessage();
- msg.type = smash.SECommMessage.UNSUBSCRIBE;
- msg.additionalHeader = {subId: subId};
- send(msg.serialize());
- }
- function send(message) {
- // Queue the message if sending or if there is no communication partner yet
- if (initialized == false) {
- queueOut.push(message);
- }
- else {
- commLib.send(message);
- }
- }
- /**
- * Function processing the incomming data from commLib
- *
- * @param message The message containing the incomming data
- */
- this.messageReceived = function (message) {
- var msg = new smash.SECommMessage();
- msg.deserialize(message);
- // parse the JSON payload
- if (msg.type == smash.SECommMessage.DISTRIBUTE) {
- var header = msg.additionalHeader;
- if ((header != null) && (header.f == "J"))
- msg.payload = JSON.parse(msg.payload);
- }
- //For now, pass all messages to handleIncomingMessage()
- //if ((msg.type == smash.SECommMessage.DISTRIBUTE) || (msg.type == smash.SECommMessage.SUBSCRIBE_ACK))
- that.handleIncomingMessage(msg);
- }
- this.handleSecurityError = function (error) {
- if (securityListener == null) {
- throw new Error(error);
- }
- else {
- securityListener(error, clientName);
- }
- return;
- }
- /**
- * Sets the callback for security errors.
- *
- * @param The callback for security errors.
- */
- this.setSecurityListener = function(callback) {
- securityListener = callback;
- }
- /**
- * This method is the location for the callback to the SECommClient library.
- * The application using this library overrides this method with its own implementation.
- * HACK: this is terrible from a layering perspective. Ideally all message formatting details, such
- * as header formats should be handled at this layer alone.
- * The default behavior is to alert a message.
- *
- * @param message The actual message.
- */
- this.handleIncomingMessage = function(message) {
- alert("SECommClient\n\nTopic: " + message.topic + "\n\nPayload: " + message.payload);
- }
- this.log = function(msg) {
- logfunc(msg);
- }
- }
- /**
- * Provides the low level communication layer.
- * @param child (boolean) indicating if this is a child iframe or not.
- * @param controllers (object []) an array indexed by the clientName of objects implementing the controller interface.
- * @param clientName - only explicitly passed for the child iframe
- *
- * controller.messageReceived - called when the commlib recieves an incomming message.
- * controller.initializationFinished - called when the commlib finished its initialzation.
- * controller.handleSecurityError - called when a security error occurs.
- *
- */
- smash.CommLib = function(child, controllers, clientName) {
- /**BEGIN of communcation protocol **/
- /*
- Message format:
- | Message Type | Message Sequence Number | Security Token Parent | Security Token Child | ACK | ACK Message Sequence Number | Payload |
- | 1 character | 2 characters | x characters | x characters | 1 character | 2 characters | varable length |
- */
- // Init message payload=communication partner url
- var INIT = "1";
- // An ack message without any payload. The reciever is not supposed to ack this message therefore the message sequence number will be 00.
- var ACK = "2";
- // The part message indicates that this is a message that needed to be split up. It will contain the payload of a part of the total message.
- var PART = "3";
- // The end message indicates that this is the last part of a split up message. The full message has arrived after processing this message.
- var END = "4";
- /** END of communcation protocol **/
- // This is used to make the object available to the private methods. This is a workaround for an error in the ECMAScript Language Specification which causes this to be set incorrectly for inner functions. See http://www.crockford.com/javascript/private.html
- var that = this;
- // polling and queue processing interval
- var interval = 100;
- // The maximul length of a URL. If the message is longer it will be split into different parts.
- var urlLimit = 4000;
- // Protocol overhead excluding security token overhead
- var protocolOverhead = 6;
- // Need to do an acknowledgement
- var ack = 0;
- // Raw incoming data
- var currentHash = null;
- // The newly decoded incoming message
- var messageIn = null;
- // The last decoded incoming message
- var previousIn = null;
- // The currently transmitted message
- var messageOut = null;
- // The previously transmitted message
- var previousOut = null;
- // The url of the partner
- var partnerURL = null;
- // The window object of the partner
- var partnerWindow = null;
- // A queue for outgoing messages. This queue is used when new send requests are done while we are still sending or recieving a message.
- var queueOut = [];
- // Storing the last sent message number
- var msn = 00;
- // Buffer for storing the incoming message parts
- var messageBuffer = "";
- // Variable storing the timerId of the message timer.
- var timerId = null;
- // Two security tokens - One created by the parent frame (the manager) and one by the child frame (the client)
- var securityTokenParent = null;
- var securityTokenChild = null;
- //
- var controller = null;
- var logQ = [];
- /**
- * Sends a message to the communication partner
- * @param message (string) the message that needs to be delivered to the communication partner
- */
- this.send = function(message) {
- // check if we are properly initialized
- if (partnerURL == null) {
- log("Trying to send without proper initialization. Message will be discarded. " + message);
- return;
- }
- log("Sending: " + message);
- // URL encode the message
- // var encodedMessage=encodeURIComponent(message);
- var encodedMessage = message;
- // determine the payload size
- var payloadLength = urlLimit - protocolOverhead - smash._securityTokenOverhead - partnerURL.length;
- // DEBUG LARGE MESSAGES
- //if(oah_ifr_debug)payloadLength=1;
- // Split up into separate messages if necessary
- var currentMessage = encodedMessage;
- while (currentMessage.length > 0) {
- // split up and put in output queue
- var part = currentMessage.substr(0, payloadLength);
- currentMessage = currentMessage.substr(payloadLength);
- if (currentMessage == 0) {
- queueOut.push({type: END, payload: part});
- }
- else {
- queueOut.push({type: PART, payload: part});
- }
- }
- }
- /**
- * The timer triggering the flow of messages through the system.
- */
- function messageTimer() {
- // check if there is a new message
- if (checkMessage()) {
- // check if it can be decoded properly
- if (decodeMessage()) {
- // check if it is conform the security requirements
- if (checkSecurity()) {
- // process it
- processMessage();
- }
- }
- }
- // Only sent if an ack was received for the last transmitted message.
- if (checkAck()) {
- // send anything that might be in the out queue
- sendMessage();
- }
- }
- /**
- * Returns true if the previously transmitted message was acknowledged.
- *
- * Possible exception situations to take into account:
- * - One of the parties takes two turns in a row.
- * p p c
- * c p1 -
- * p p1'
- *
- * c p p c
- * c ac1 p1
- * p c1 p1'
- *
- */
- function checkAck() {
- // No ack is expected for an ack.
- if (previousOut.type == ACK)return true;
- // Ack is received.
- if ((previousOut.msn == messageIn.ackMsn) && (messageIn.ack == 1)) return true;
- // Wait for the ack to arrive.
- log("Waiting for ACK : " + previousOut.msn);
- return false;
- }
- /**
- * Helper method providing a new message sequence number
- * @returns (string) the new sequence number
- */
- function getNewMsn() {
- msn++;
- if (msn == 100) msn = 0;
- if (msn < 10) return "0" + msn;
- return "" + msn;
- }
- /**
- * Checks the information after the hash to see if there is a new incomming message.
- */
- function checkMessage() {
- //Can't use location.hash because at least Firefox does a decodeURIComponent on it.
- var urlParts = window.location.href.split("#");
- if (urlParts.length == 2) {
- var newHash = urlParts[1];
- if (newHash != "" && newHash != currentHash) {
- currentHash = newHash;
- return true;
- }
- }
- return false;
- }
- /**
- * Decodes an incomming message and checks to see if it is syntactially valid.
- */
- function decodeMessage() {
- //new RegExp( "(\\d)(\\d{2})(.{" + smash._securityTokenLength + "})(.{" + smash._securityTokenLength + "})(\\d)(\\d{2})(.*)" )
- var type = currentHash.substr(0, 1);
- var msn = currentHash.substr(1, 2);
- var nextStart = 3;
- var tokenParent = currentHash.substr(nextStart, smash._securityTokenLength);
- nextStart += smash._securityTokenLength;
- var tokenChild = currentHash.substr(nextStart, smash._securityTokenLength);
- nextStart += smash._securityTokenLength;
- var ack = currentHash.substr(nextStart, 1);
- nextStart += 1;
- var ackMsn = currentHash.substr(nextStart, 2);
- nextStart += 2;
- // The payload needs to stay raw since the uri decoding needs to happen on the concatenated data in case of a large message
- var payload = currentHash.substr(nextStart);
- log("In : Type: " + type + " msn: " + msn + " tokenParent: " + tokenParent + " tokenChild: " + tokenChild + " ack: " + ack + " msn: " + ackMsn + " payload: " + payload);
- messageIn = {type: type, msn: msn, tokenParent: tokenParent, tokenChild: tokenChild, ack: ack, ackMsn: ackMsn, payload: payload};
- return true;
- }
- /**
- * Check if there have been any security breaches in the message.
- */
- function checkSecurity() {
- // Check the security tokens
- if (messageIn.type != INIT && (messageIn.tokenParent != securityTokenParent || messageIn.tokenChild != securityTokenChild)) {
- log("Security token error: Invalid security token received. The message will be discarded.");
- handleSecurityError(smash.SecurityErrors.INVALID_TOKEN);
- return false;
- }
- // Attacks should never pass the security check. Code below is to debug the implementation.
- //if(oah_ifr_debug){
- // if (messageIn.type!=INIT && messageIn.type!=ACK && messageIn.type!=PART && messageIn.type!=END){
- // if(oah_ifr_debug)debug("Syntax error: Message Type. The message will be discarded.");
- // return false;
- // }
- // if (!(messageIn.msn>=0 && messageIn.msn<=99)){
- // if(oah_ifr_debug)debug("Syntax error: Message Sequence Number. The message will be discarded.");
- // return false;
- // }
- // if (!(messageIn.ack==0 || messageIn.ack==1)){
- // if(oah_ifr_debug)debug("Syntax error: ACK. The message will be discarded.");
- // return false;
- // }
- // if (!(messageIn.ackMsn>=0 && messageIn.ackMsn<=99)){
- // if(oah_ifr_debug)debug("Syntax error: ACK Message Sequence Number. The message will be discarded.");
- // return false;
- // }
- //}
- return true;
- }
- /**
- * Process the incoming message.
- */
- function processMessage() {
- ack = 1;
- // The child is initialized as soon as there is an ack for the init message sent by the child.
- if (messageIn.type != INIT && child && previousOut.type == INIT && messageIn.ack == "1" && previousOut.msn == messageIn.ackMsn) {
- controller.initializationFinished(partnerURL);
- }
- // Call the actual processing functions
- switch (messageIn.type) {
- case INIT:
- processInit();
- break;
- case ACK:
- processAck();
- break;
- case PART:
- processPart();
- break;
- case END:
- processEnd();
- break;
- }
- // Set the processed message as the previousIn message
- previousIn = messageIn;
- }
- /**
- * Implementation of the INIT message type
- **/
- function processInit() {
- var parts = messageIn.payload.split(":");
- var cname = decodeURIComponent(parts[0]);
- partnerURL = decodeURIComponent(parts[1]);
- securityTokenParent = messageIn.tokenParent;
- securityTokenChild = messageIn.tokenChild;
- // Initialize a component
- if (child) {
- if (clientName != null) cname = clientName; // override what is read from the URL
- // generate a real security token for the child
- securityTokenChild = smash._generateSecurityToken();
- // GUI which will be used to name the iFrame tunnel.
- var tunnelGUID = "3827816c-f3b1-11db-8314-0800200c9a66";
- // Generate the hidden iframe for communicating
- var iframe = document.createElement("iframe");
- var currentClientURI = encodeURIComponent(window.location.href.split("#")[0]);
- var initialClientURI = currentClientURI;
- // if (smash._initialClientURI) {
- // initialClientURI = encodeURIComponent(smash._initialClientURI);
- // }
- var initpayload = encodeURIComponent(cname) + ":" + currentClientURI + ":" + initialClientURI;
- // sending an ack for msn "00" to the tunnel, since have processed the INIT message,
- // and so that the INIT message the component is sending to the tunnel will result
- // in an ack to be sent back.
- // XXX Since server redirection breaks hash communication (the server does
- // not receive the fragment value, therefore the final URL does not contain
- // this information), the initial message is transmitted as a URL param.
- partnerURL += (partnerURL.indexOf("?") != -1 ? "&" : "?") + "oahm=100" + securityTokenParent + securityTokenChild + "100" + initpayload;
- iframe.src = partnerURL;
- iframe.name = tunnelGUID;
- iframe.id = tunnelGUID;
- document.body.appendChild(iframe);
- iframe.style.position = "absolute";
- iframe.style.left = iframe.style.top = "-10px";
- iframe.style.height = iframe.style.width = "1px";
- iframe.style.visibility = "hidden";
- // We do not send an ack directly to the parent frame since it is impossible to directly communicate with it in IE7
- // The ack is done indirectly when the registerTunnelCommLib is done
- ack = 0;
- // set up the partner window
- partnerWindow = window.frames[tunnelGUID];
- // store the last sent message - will be used to detect intialization and for detecting security breaches
- previousOut = {type: INIT, msn: "00", tokenParent: securityTokenParent, tokenChild: securityTokenChild, ack: "0", ackMsn: "00", payload: initpayload}; // only using type and msn of previousOut. presumably the rest is for FDK's retransmit stuff? should get rid of this complexity
- // set the controller for this component
- controller = controllers["child"];
- }
- // Initialize a tunnel
- else {
- var initialClientURI = decodeURIComponent(parts[2]);
- // set up the partner window
- partnerWindow = window.parent;
- // set the controller for this component
- controller = controllers[cname];
- var success = controller.initializationFinished(that, securityTokenParent, partnerURL, initialClientURI, window);
- if (!success) ack = 0; // don't send an ack signalling the completion of connection setup.
- // store the last sent message - will be used to detect intialization and for detecting security breaches
- previousOut = {type: INIT, msn: "00", tokenParent: securityTokenParent, tokenChild: securityTokenChild, ack: "0", ackMsn: "00", payload: (encodeURIComponent(cname) + ":" + encodeURIComponent(window.location.href.split("#")[0]))}; // only using type and msn of previousOut. presumably the rest is for FDK's retransmit stuff? should get rid of this complexity
- }
- if (partnerWindow == null) {
- log("Init failed.");
- }
- }
- /**
- * Implementation of the ACK message type
- **/
- function processAck() {
- // do not ack an ack
- ack = 0;
- }
- /**
- * Implementation of the PART message type
- **/
- function processPart() {
- // Process message
- messageBuffer += messageIn.payload;
- }
- /**
- * Implementation the END message type
- **/
- function processEnd() {
- // Process message
- messageBuffer += messageIn.payload;
- // messageBuffer=decodeURIComponent(messageBuffer);
- log("Received: " + messageBuffer);
- controller.messageReceived(messageBuffer);
- messageBuffer = "";
- }
- /**
- * Send a reply to the incoming message.
- */
- function sendMessage() {
- // If there is nothing in the queue and an ack needs to be sent put the ack on the queue;
- if (queueOut.length == 0 && ack == 1) {
- // The correct values will be filled in later. Just push a clean ack message
- queueOut.push({type: ACK, payload: ""});
- }
- // Process the output queue
- if (queueOut.length != 0) {
- messageOut = queueOut.shift();
- // Fill in the security token
- messageOut.tokenParent = securityTokenParent;
- messageOut.tokenChild = securityTokenChild;
- // Get a new sequence number
- messageOut.msn = getNewMsn();
- // Fill in the right ack values
- // The protocol keeps acking the last received message to ensure that there are no
- // problems with overwriting a pure ack message. Which could happen because there is
- // no waiting for an ack of an ack.
- messageOut.ack = "1";
- messageOut.ackMsn = previousIn.msn;
- // turn of the ack
- ack = 0;
- writeToPartnerWindow();
- }
- }
- /**
- * Writes the message to the partner window's fragment id
- **/
- function writeToPartnerWindow() {
- var url = partnerURL + "#" + messageOut.type + messageOut.msn + messageOut.tokenParent + messageOut.tokenChild + messageOut.ack + messageOut.ackMsn + messageOut.payload;
- partnerWindow.location.replace(url);
- previousOut = messageOut;
- log("Out: Type: " + messageOut.type + " msn: " + messageOut.msn + " tokenParent: " + messageOut.tokenParent + " tokenChild: " + messageOut.tokenChild + " ack: " + messageOut.ack + " msn: " + messageOut.ackMsn + " payload: " + messageOut.payload);
- }
- /**
- * Default handler of the security listener. If a security error occurs, the CommLib is switched off. And communication is no longer possible.
- *
- */
- function handleSecurityError(error) {
- // Stop the communication
- clearInterval(timerId);
- // If there is a securityListener inform the controller of what happened.
- controller.handleSecurityError(error);
- }
- function log(msg) {
- if (controller) {
- while (logQ.length > 0) {
- controller.log(logQ.shift());
- }
- controller.log(msg);
- } else {
- logQ.push(msg);
- }
- }
- // Start listening for incoming messages
- timerId = setInterval(messageTimer, interval);
- };
- ////////////////////////////////////////////////////////////////////////////////
- /*
- Copyright 2006-2009 OpenAjax Alliance
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- //SMASH.CRYPTO
- //
- //Small library containing some minimal crypto functionality for a
- //- a hash-function: SHA-1 (see FIPS PUB 180-2 for definition)
- //BigEndianWord[5] <- smash.crypto.sha1( BigEndianWord[*] dataWA, int lenInBits)
- //
- //- a message authentication code (MAC): HMAC-SHA-1 (RFC2104/2202)
- //BigEndianWord[5] <- smash.crypto.hmac_sha1(
- // BigEndianWord[3-16] keyWA,
- // Ascii or Unicode string dataS,
- // int chrsz (8 for Asci/16 for Unicode)
- //
- //- pseudo-random number generator (PRNG): HMAC-SHA-1 in counter mode, following
- //Barak & Halevi, An architecture for robust pseudo-random generation and applications to /dev/random, CCS 2005
- //rngObj <- smash.crypto.newPRNG( String[>=12] seedS)
- //where rngObj has methods
- //addSeed(String seed)
- //BigEndianWord[len] <- nextRandomOctets(int len)
- //Base64-String[len] <- nextRandomB64Str(int len)
- //Note: HMAC-SHA1 in counter-mode does not provide forward-security on corruption.
- // However, the PRNG state is kept inside a closure. So if somebody can break the closure, he probably could
- // break a whole lot more and forward-security of the prng is not the highest of concerns anymore :-)
- if (typeof(smash) == 'undefined') {
- var smash = {};
- }
- smash.crypto = {
- // Some utilities
- // convert a string to an array of big-endian words
- 'strToWA': function (/* Ascii or Unicode string */ str, /* int 8 for Asci/16 for Unicode */ chrsz) {
- var bin = Array();
- var mask = (1 << chrsz) - 1;
- for (var i = 0; i < str.length * chrsz; i += chrsz)
- bin[i >> 5] |= (str.charCodeAt(i / chrsz) & mask) << (32 - chrsz - i % 32);
- return bin;
- },
- // MAC
- 'hmac_sha1' : function(
- /* BigEndianWord[3-16]*/ keyWA,
- /* Ascii or Unicode string */ dataS,
- /* int 8 for Asci/16 for Unicode */ chrsz) {
- // write our own hmac derived from paj's so we do not have to do constant key conversions and length checking ...
- var ipad = Array(16), opad = Array(16);
- for (var i = 0; i < 16; i++) {
- ipad[i] = keyWA[i] ^ 0x36363636;
- opad[i] = keyWA[i] ^ 0x5C5C5C5C;
- }
- var hash = this.sha1(ipad.concat(this.strToWA(dataS, chrsz)), 512 + dataS.length * chrsz);
- return this.sha1(opad.concat(hash), 512 + 160);
- },
- // PRNG factory method
- // see below 'addSeed', 'nextRandomOctets' & 'nextRandomB64Octets' for public methods of returnd prng object
- 'newPRNG' : function (/* String[>=12] */ seedS) {
- that = this;
- // parameter checking
- // We cannot really verify entropy but obviously the string must have at least a minimal length to have enough entropy
- // However, a 2^80 security seems ok, so we check only that at least 12 chars assuming somewhat random ASCII
- if ((typeof seedS != 'string') || (seedS.length < 12)) {
- alert("WARNING: Seed length too short ...");
- }
- // constants
- var __refresh_keyWA = [ 0xA999, 0x3E36, 0x4706, 0x816A,
- 0x2571, 0x7850, 0xC26C, 0x9CD0,
- 0xBA3E, 0xD89D, 0x1233, 0x9525,
- 0xff3C, 0x1A83, 0xD491, 0xFF15 ]; // some random key for refresh ...
- // internal state
- var _keyWA = []; // BigEndianWord[5]
- var _cnt = 0; // int
- function extract(seedS) {
- return that.hmac_sha1(__refresh_keyWA, seedS, 8);
- }
- function refresh(seedS) {
- // HMAC-SHA1 is not ideal, Rijndal 256bit block/key in CBC mode with fixed key might be better
- // but to limit the primitives and given that we anyway have only limited entropy in practise
- // this seems good enough
- var uniformSeedWA = extract(seedS);
- for (var i = 0; i < 5; i++) {
- _keyWA[i] ^= uniformSeedWA[i];
- }
- }
- // inital state seeding
- refresh(seedS);
- // public methods
- return {
- // Mix some additional seed into the PRNG state
- 'addSeed' : function (/* String */ seed) {
- // no parameter checking. Any added entropy should be fine ...
- refresh(seed);
- },
- // Get an array of len random octets
- 'nextRandomOctets' : /* BigEndianWord[len] <- */ function (/* int */ len) {
- var randOctets = [];
- while (len > 0) {
- _cnt += 1;
- var nextBlock = that.hmac_sha1(_keyWA, (_cnt).toString(16), 8);
- for (i = 0; (i < 20) & (len > 0); i++,len--) {
- randOctets.push((nextBlock[i >> 2] >> (i % 4) ) % 256);
- }
- // Note: if len was not a multiple 20, some random octets are ignored here but who cares ..
- }
- return randOctets;
- },
- // Get a random string of Base64-like (see below) chars of length len
- // Note: there is a slightly non-standard Base64 with no padding and '-' and '_' for '+' and '/', respectively
- 'nextRandomB64Str' : /* Base64-String <- */ function (/* int */ len) {
- var b64StrMap = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
- var randOctets = this.nextRandomOctets(len);
- var randB64Str = '';
- for (var i = 0; i < len; i++) {
- randB64Str += b64StrMap.charAt(randOctets[i] & 0x3F);
- }
- return randB64Str;
- }
- }
- },
- // Digest function:
- // BigEndianWord[5] <- sha1( BigEndianWord[*] dataWA, int lenInBits)
- 'sha1' : function() {
- // Note: all Section references below refer to FIPS 180-2.
- // private utility functions
- // - 32bit addition with wrap-around
- var add_wa = function (x, y) {
- var lsw = (x & 0xFFFF) + (y & 0xFFFF);
- var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
- return (msw << 16) | (lsw & 0xFFFF);
- }
- // - 32bit rotatate left
- var rol = function(num, cnt) {
- return (num << cnt) | (num >>> (32 - cnt));
- }
- // - round-dependent function f_t from Section 4.1.1
- function sha1_ft(t, b, c, d) {
- if (t < 20) return (b & c) | ((~b) & d);
- if (t < 40) return b ^ c ^ d;
- if (t < 60) return (b & c) | (b & d) | (c & d);
- return b ^ c ^ d;
- }
- // - round-dependent SHA-1 constants from Section 4.2.1
- function sha1_kt(t) {
- return (t < 20) ? 1518500249 :
- (t < 40) ? 1859775393 :
- (t < 60) ? -1894007588 :
- /* (t < 80) */ -899497514;
- }
- // main algorithm.
- return function(/* BigEndianWord[*] */ dataWA, /* int */ lenInBits) {
- // Section 6.1.1: Preprocessing
- //-----------------------------
- // 1. padding: (see also Section 5.1.1)
- // - append one 1 followed by 0 bits filling up 448 bits of last (512bit) block
- dataWA[lenInBits >> 5] |= 0x80 << (24 - lenInBits % 32);
- // - encode length in bits in last 64 bits
- // Note: we rely on javascript to zero file elements which are beyond last (partial) data-block
- // but before this length encoding!
- dataWA[((lenInBits + 64 >> 9) << 4) + 15] = lenInBits;
- // 2. 512bit blocks (actual split done ondemand later)
- var W = Array(80);
- // 3. initial hash using SHA-1 constants on page 13
- var H0 = 1732584193;
- var H1 = -271733879;
- var H2 = -1732584194;
- var H3 = 271733878;
- var H4 = -1009589776;
- // 6.1.2 SHA-1 Hash Computation
- for (var i = 0; i < dataWA.length; i += 16) {
- // 1. Message schedule, done below
- // 2. init working variables
- var a = H0;
- var b = H1;
- var c = H2;
- var d = H3;
- var e = H4;
- // 3. round-functions
- for (var j = 0; j < 80; j++) {
- // postponed step 2
- W[j] = ( (j < 16) ? dataWA[i + j] : rol(W[j - 3] ^ W[j - 8] ^ W[j - 14] ^ W[j - 16], 1));
- var T = add_wa(add_wa(rol(a, 5), sha1_ft(j, b, c, d)),
- add_wa(add_wa(e, W[j]), sha1_kt(j)));
- e = d;
- d = c;
- c = rol(b, 30);
- b = a;
- a = T;
- }
- // 4. intermediate hash
- H0 = add_wa(a, H0);
- H1 = add_wa(b, H1);
- H2 = add_wa(c, H2);
- H3 = add_wa(d, H3);
- H4 = add_wa(e, H4);
- }
- return Array(H0, H1, H2, H3, H4);
- }
- }()
- };
- ////////////////////////////////////////////////////////////////////////////////
- /*
- http://www.JSON.org/json2.js
- 2008-11-19
- Public Domain.
- NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
- See http://www.JSON.org/js.html
- This file creates a global JSON object containing two methods: stringify
- and parse.
- JSON.stringify(value, replacer, space)
- value any JavaScript value, usually an object or array.
- replacer an optional parameter that determines how object
- values are stringified for objects. It can be a
- function or an array of strings.
- space an optional parameter that specifies the indentation
- of nested structures. If it is omitted, the text will
- be packed without extra whitespace. If it is a number,
- it will specify the number of spaces to indent at each
- level. If it is a string (such as '\t' or ' '),
- it contains the characters used to indent at each level.
- This method produces a JSON text from a JavaScript value.
- When an object value is found, if the object contains a toJSON
- method, its toJSON method will be called and the result will be
- stringified. A toJSON method does not serialize: it returns the
- value represented by the name/value pair that should be serialized,
- or undefined if nothing should be serialized. The toJSON method
- will be passed the key associated with the value, and this will be
- bound to the object holding the key.
- For example, this would serialize Dates as ISO strings.
- Date.prototype.toJSON = function (key) {
- function f(n) {
- // Format integers to have at least two digits.
- return n < 10 ? '0' + n : n;
- }
- return this.getUTCFullYear() + '-' +
- f(this.getUTCMonth() + 1) + '-' +
- f(this.getUTCDate()) + 'T' +
- f(this.getUTCHours()) + ':' +
- f(this.getUTCMinutes()) + ':' +
- f(this.getUTCSeconds()) + 'Z';
- };
- You can provide an optional replacer method. It will be passed the
- key and value of each member, with this bound to the containing
- object. The value that is returned from your method will be
- serialized. If your method returns undefined, then the member will
- be excluded from the serialization.
- If the replacer parameter is an array of strings, then it will be
- used to select the members to be serialized. It filters the results
- such that only members with keys listed in the replacer array are
- stringified.
- Values that do not have JSON representations, such as undefined or
- functions, will not be serialized. Such values in objects will be
- dropped; in arrays they will be replaced with null. You can use
- a replacer function to replace those with JSON values.
- JSON.stringify(undefined) returns undefined.
- The optional space parameter produces a stringification of the
- value that is filled with line breaks and indentation to make it
- easier to read.
- If the space parameter is a non-empty string, then that string will
- be used for indentation. If the space parameter is a number, then
- the indentation will be that many spaces.
- Example:
- text = JSON.stringify(['e', {pluribus: 'unum'}]);
- // text is '["e",{"pluribus":"unum"}]'
- text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
- // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
- text = JSON.stringify([new Date()], function (key, value) {
- return this[key] instanceof Date ?
- 'Date(' + this[key] + ')' : value;
- });
- // text is '["Date(---current time---)"]'
- JSON.parse(text, reviver)
- This method parses a JSON text to produce an object or array.
- It can throw a SyntaxError exception.
- The optional reviver parameter is a function that can filter and
- transform the results. It receives each of the keys and values,
- and its return value is used instead of the original value.
- If it returns what it received, then the structure is not modified.
- If it returns undefined then the member is deleted.
- Example:
- // Parse the text. Values that look like ISO date strings will
- // be converted to Date objects.
- myData = JSON.parse(text, function (key, value) {
- var a;
- if (typeof value === 'string') {
- a =
- /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
- if (a) {
- return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
- +a[5], +a[6]));
- }
- }
- return value;
- });
- myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
- var d;
- if (typeof value === 'string' &&
- value.slice(0, 5) === 'Date(' &&
- value.slice(-1) === ')') {
- d = new Date(value.slice(5, -1));
- if (d) {
- return d;
- }
- }
- return value;
- });
- This is a reference implementation. You are free to copy, modify, or
- redistribute.
- This code should be minified before deployment.
- See http://javascript.crockford.com/jsmin.html
- USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
- NOT CONTROL.
- */
- /*jslint evil: true */
- /*global JSON */
- /*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
- call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
- getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
- lastIndex, length, parse, prototype, push, replace, slice, stringify,
- test, toJSON, toString, valueOf
- */
- //Create a JSON object only if one does not already exist. We create the
- //methods in a closure to avoid creating global variables.
- if (!this.JSON) {
- JSON = {};
- }
- (function () {
- function f(n) {
- // Format integers to have at least two digits.
- return n < 10 ? '0' + n : n;
- }
- if (typeof Date.prototype.toJSON !== 'function') {
- Date.prototype.toJSON = function (key) {
- return this.getUTCFullYear() + '-' +
- f(this.getUTCMonth() + 1) + '-' +
- f(this.getUTCDate()) + 'T' +
- f(this.getUTCHours()) + ':' +
- f(this.getUTCMinutes()) + ':' +
- f(this.getUTCSeconds()) + 'Z';
- };
- String.prototype.toJSON =
- Number.prototype.toJSON =
- Boolean.prototype.toJSON = function (key) {
- return this.valueOf();
- };
- }
- var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
- escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
- gap,
- indent,
- meta = { // table of character substitutions
- '\b': '\\b',
- '\t': '\\t',
- '\n': '\\n',
- '\f': '\\f',
- '\r': '\\r',
- '"' : '\\"',
- '\\': '\\\\'
- },
- rep;
- function quote(string) {
- //If the string contains no control characters, no quote characters, and no
- //backslash characters, then we can safely slap some quotes around it.
- //Otherwise we must also replace the offending characters with safe escape
- //sequences.
- escapable.lastIndex = 0;
- return escapable.test(string) ?
- '"' + string.replace(escapable, function (a) {
- var c = meta[a];
- return typeof c === 'string' ? c :
- '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
- }) + '"' :
- '"' + string + '"';
- }
- function str(key, holder) {
- //Produce a string from holder[key].
- var i, // The loop counter.
- k, // The member key.
- v, // The member value.
- length,
- mind = gap,
- partial,
- value = holder[key];
- //If the value has a toJSON method, call it to obtain a replacement value.
- if (value && typeof value === 'object' &&
- typeof value.toJSON === 'function') {
- value = value.toJSON(key);
- }
- //If we were called with a replacer function, then call the replacer to
- //obtain a replacement value.
- if (typeof rep === 'function') {
- value = rep.call(holder, key, value);
- }
- //What happens next depends on the value's type.
- switch (typeof value) {
- case 'string':
- return quote(value);
- case 'number':
- //JSON numbers must be finite. Encode non-finite numbers as null.
- return isFinite(value) ? String(value) : 'null';
- case 'boolean':
- case 'null':
- //If the value is a boolean or null, convert it to a string. Note:
- //typeof null does not produce 'null'. The case is included here in
- //the remote chance that this gets fixed someday.
- return String(value);
- //If the type is 'object', we might be dealing with an object or an array or
- //null.
- case 'object':
- //Due to a specification blunder in ECMAScript, typeof null is 'object',
- //so watch out for that case.
- if (!value) {
- return 'null';
- }
- //Make an array to hold the partial results of stringifying this object value.
- gap += indent;
- partial = [];
- //Is the value an array?
- if (Object.prototype.toString.apply(value) === '[object Array]') {
- //The value is an array. Stringify every element. Use null as a placeholder
- //for non-JSON values.
- length = value.length;
- for (i = 0; i < length; i += 1) {
- partial[i] = str(i, value) || 'null';
- }
- //Join all of the elements together, separated with commas, and wrap them in
- //brackets.
- v = partial.length === 0 ? '[]' :
- gap ? '[\n' + gap +
- partial.join(',\n' + gap) + '\n' +
- mind + ']' :
- '[' + partial.join(',') + ']';
- gap = mind;
- return v;
- }
- //If the replacer is an array, use it to select the members to be stringified.
- if (rep && typeof rep === 'object') {
- length = rep.length;
- for (i = 0; i < length; i += 1) {
- k = rep[i];
- if (typeof k === 'string') {
- v = str(k, value);
- if (v) {
- partial.push(quote(k) + (gap ? ': ' : ':') + v);
- }
- }
- }
- } else {
- //Otherwise, iterate through all of the keys in the object.
- for (k in value) {
- if (Object.hasOwnProperty.call(value, k)) {
- v = str(k, value);
- if (v) {
- partial.push(quote(k) + (gap ? ': ' : ':') + v);
- }
- }
- }
- }
- //Join all of the member texts together, separated with commas,
- //and wrap them in braces.
- v = partial.length === 0 ? '{}' :
- gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
- mind + '}' : '{' + partial.join(',') + '}';
- gap = mind;
- return v;
- }
- }
- //If the JSON object does not yet have a stringify method, give it one.
- if (typeof JSON.stringify !== 'function') {
- JSON.stringify = function (value, replacer, space) {
- //The stringify method takes a value and an optional replacer, and an optional
- //space parameter, and returns a JSON text. The replacer can be a function
- //that can replace values, or an array of strings that will select the keys.
- //A default replacer method can be provided. Use of the space parameter can
- //produce text that is more easily readable.
- var i;
- gap = '';
- indent = '';
- //If the space parameter is a number, make an indent string containing that
- //many spaces.
- if (typeof space === 'number') {
- for (i = 0; i < space; i += 1) {
- indent += ' ';
- }
- //If the space parameter is a string, it will be used as the indent string.
- } else if (typeof space === 'string') {
- indent = space;
- }
- //If there is a replacer, it must be a function or an array.
- //Otherwise, throw an error.
- rep = replacer;
- if (replacer && typeof replacer !== 'function' &&
- (typeof replacer !== 'object' ||
- typeof replacer.length !== 'number')) {
- throw new Error('JSON.stringify');
- }
- //Make a fake root object containing our value under the key of ''.
- //Return the result of stringifying the value.
- return str('', {'': value});
- };
- }
- //If the JSON object does not yet have a parse method, give it one.
- if (typeof JSON.parse !== 'function') {
- JSON.parse = function (text, reviver) {
- //The parse method takes a text and an optional reviver function, and returns
- //a JavaScript value if the text is a valid JSON text.
- var j;
- function walk(holder, key) {
- //The walk method is used to recursively walk the resulting structure so
- //that modifications can be made.
- var k, v, value = holder[key];
- if (value && typeof value === 'object') {
- for (k in value) {
- if (Object.hasOwnProperty.call(value, k)) {
- v = walk(value, k);
- if (v !== undefined) {
- value[k] = v;
- } else {
- delete value[k];
- }
- }
- }
- }
- return reviver.call(holder, key, value);
- }
- //Parsing happens in four stages. In the first stage, we replace certain
- //Unicode characters with escape sequences. JavaScript handles many characters
- //incorrectly, either silently deleting them, or treating them as line endings.
- cx.lastIndex = 0;
- if (cx.test(text)) {
- text = text.replace(cx, function (a) {
- return '\\u' +
- ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
- });
- }
- //In the second stage, we run the text against regular expressions that look
- //for non-JSON patterns. We are especially concerned with '()' and 'new'
- //because they can cause invocation, and '=' because it can cause mutation.
- //But just to be safe, we want to reject all unexpected forms.
- //We split the second stage into 4 regexp operations in order to work around
- //crippling inefficiencies in IE's and Safari's regexp engines. First we
- //replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
- //replace all simple value tokens with ']' characters. Third, we delete all
- //open brackets that follow a colon or comma or that begin the text. Finally,
- //we look to see that the remaining characters are only whitespace or ']' or
- //',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
- if (/^[\],:{}\s]*$/.
- test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
- replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
- replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
- //In the third stage we use the eval function to compile the text into a
- //JavaScript structure. The '{' operator is subject to a syntactic ambiguity
- //in JavaScript: it can begin a block or an object literal. We wrap the text
- //in parens to eliminate the ambiguity.
- j = eval('(' + text + ')');
- //In the optional fourth stage, we recursively walk the new structure, passing
- //each name/value pair to a reviver function for possible transformation.
- return typeof reviver === 'function' ?
- walk({'': j}, '') : j;
- }
- //If the text is not JSON parseable, then a SyntaxError is thrown.
- throw new SyntaxError('JSON.parse');
- };
- }
- })();
- ////////////////////////////////////////////////////////////////////////////////
- if (!window["PageBus"])
- window.PageBus = {};
- // Insert a debugger breakpoint in Dev builds of PageBus only. The debugger line should be removed in production builds.
- PageBus._debug = function() {
- // debugger; // REMOVE ON BUILD
- };
- PageBus._esc = function(s) {
- return s.replace(/\./g, "!");
- };
- PageBus._assertPubTopic = function(topic) {
- if ((topic == null) || (topic == "") || (topic.indexOf("*") != -1) || (topic.indexOf("..") != -1) ||
- (topic.charAt(0) == ".") || (topic.charAt(topic.length - 1) == ".")) {
- throw new Error(OpenAjax.hub.Error.BadParameters);
- }
- };
- PageBus._assertSubTopic = function(topic) {
- if ((topic == null) || (topic == ""))
- throw new Error(OpenAjax.hub.Error.BadParameters);
- var path = topic.split(".");
- var len = path.length;
- for (var i = 0; i < len; i++) {
- var p = path[i];
- if ((p == "") ||
- ((p.indexOf("*") != -1) && (p != "*") && (p != "**"))) {
- throw new Error(OpenAjax.hub.Error.BadParameters);
- }
- if ((p == "**") && (i < len - 1)) {
- throw new Error(OpenAjax.hub.Error.BadParameters);
- }
- }
- };
- PageBus._copy = function(obj) {
- var c;
- if (typeof(obj) == "object") {
- if (obj == null)
- return null;
- else if (obj.constructor == Array) {
- c = [];
- for (var i = 0; i < obj.length; i++)
- c[i] = PageBus._copy(obj[i]);
- return c;
- }
- else if (obj.constructor == Date) {
- c = new Date();
- c.setDate(obj.getDate());
- return c;
- }
- c = {};
- for (var p in obj)
- c[p] = PageBus._copy(obj[p]);
- return c;
- }
- else {
- return obj;
- }
- };
- PageBus._TopicMatcher = function() {
- this._items = {};
- };
- PageBus._TopicMatcher.prototype.store = function(topic, val) {
- var path = topic.split(".");
- var len = path.length;
- _recurse = function(tree, index) {
- if (index == len)
- tree["."] = { topic: topic, value: val };
- else {
- var token = path[index];
- if (!tree[token])
- tree[token] = {};
- _recurse(tree[token], index + 1);
- }
- };
- _recurse(this._items, 0);
- };
- PageBus._TopicMatcher.prototype.match = function(topic, exactMatch) {
- var path = topic.split(".");
- var len = path.length;
- var res = [];
- _recurse = function(tree, index) {
- if (!tree)
- return;
- var node;
- if (index == len)
- node = tree;
- else {
- _recurse(tree[path[index]], index + 1);
- if (exactMatch)
- return;
- if (path[index] != "**")
- _recurse(tree["*"], index + 1);
- node = tree["**"];
- }
- if ((!node) || (!node["."]))
- return;
- res.push(node["."]);
- };
- _recurse(this._items, 0);
- return res;
- };
- PageBus._TopicMatcher.prototype.exists = function(topic, exactMatch) {
- var path = topic.split(".");
- var len = path.length;
- var res = false;
- _recurse = function(tree, index) {
- if (!tree)
- return;
- var node;
- if (index == len)
- node = tree;
- else {
- _recurse(tree[path[index]], index + 1);
- if (res || exactMatch)
- return;
- if (path[index] != "**") {
- _recurse(tree["*"], index + 1);
- if (res)
- return;
- }
- node = tree["**"];
- }
- if ((!node) || (!node["."]))
- return;
- res = true;
- };
- _recurse(this._items, 0);
- return res;
- };
- PageBus._TopicMatcher.prototype.clear = function(topic) {
- var path = topic.split(".");
- var len = path.length;
- _recurse = function(tree, index) {
- if (!tree)
- return;
- if (index == len) {
- if (tree["."])
- delete tree["."];
- }
- else {
- _recurse(tree[path[index]], index + 1);
- for (var x in tree[path[index]]) {
- return;
- }
- delete tree[path[index]];
- }
- };
- _recurse(this._items, 0);
- };
- PageBus._TopicMatcher.prototype.wildcardClear = function(topic) {
- var path = topic.split(".");
- var len = path.length;
- _clean = function(node, tok) {
- for (m in node[tok])
- return;
- delete node[tok];
- };
- _recurse = function(tree, index) {
- if (!tree)
- return;
- if (index == len) {
- if (tree["."])
- delete tree["."];
- return;
- }
- else {
- var tok = path[index];
- var n;
- if (tree[tok]) {
- _recurse(tree[tok], index + 1);
- _clean(tree, tok);
- }
- if (tok == "*") {
- for (n in tree) {
- if (( n != "**" ) && (n != ".")) {
- _recurse(tree[n], index + 1);
- _clean(tree, n);
- }
- }
- }
- else if (tok == "**") {
- for (n in tree) {
- delete tree[n];
- }
- }
- }
- return;
- };
- _recurse(this._items, 0);
- };
- PageBus._TopicMatcher.prototype.wildcardMatch = function(topic) {
- var path = topic.split(".");
- var len = path.length;
- var res = [];
- _recurse = function(tree, index) {
- var tok = path[index];
- var node;
- if ((!tree) || (index == len))
- return;
- if (tok == "**") {
- for (var n in tree) {
- if (n != ".") {
- node = tree[n];
- if (node["."])
- res.push(node["."]);
- _recurse(node, index);
- }
- }
- }
- else if (tok == "*") {
- for (var n in tree) {
- if ((n != ".") && (n != "**")) {
- node = tree[n];
- if (index == len - 1) {
- if (node["."])
- res.push(node["."]);
- }
- else
- _recurse(node, index + 1);
- }
- }
- }
- else {
- node = tree[tok];
- if (!node)
- return;
- if (index == len - 1) {
- if (node["."])
- res.push(node["."]);
- }
- else
- _recurse(node, index + 1);
- }
- };
- _recurse(this._items, 0);
- return res;
- };
- ////////////////////////////////////////////////////////////////////////////////////
- PageBus.policy = {
- Ops: {
- Publish: "p",
- Subscribe: "s"
- },
- Error: {
- BadParameters: "PageBus.policy.Error.BadParameters"
- },
- _assertName: function(topic) {
- if ((topic == null) || (topic == ""))
- throw new Error(OpenAjax.hub.Error.BadParameters);
- if (PageBus.policy._tops[topic])
- return;
- var path = topic.split(".");
- var len = path.length;
- for (var i = 0; i < len; i++) {
- var p = path[i];
- if ((p == "") ||
- ((p.indexOf("*") != -1) && (p != "*") && (p != "**"))) {
- throw new Error(PageBus.policy.Error.BadParameters);
- }
- if ((p == "**") && (i < len - 1)) {
- throw new Error(PageBus.policy.Error.BadParameters);
- }
- }
- PageBus.policy._tops[topic] = true;
- },
- _tops: {}
- };
- PageBus.policy.HubPolicy = function(params) {
- if (!params)
- params = {};
- this._cfg = params;
- this._log = params["log"];
- this._topicMgr = new PageBus._TopicMatcher();
- };
- PageBus.policy.HubPolicy.prototype.onPublish = function(topic, data, pc, sc) {
- var res = true;
- var origin;
- if (sc != null) {
- origin = sc.getPartnerOrigin();
- if (!origin)
- return false;
- res = this.isAllowed.call(this, origin, PageBus.policy.Ops.Subscribe, topic);
- }
- if (res && (pc != null)) {
- origin = pc.getPartnerOrigin();
- if (!origin)
- return false;
- res = this.isAllowed.call(this, origin, PageBus.policy.Ops.Publish, topic);
- }
- if (this._log) {
- var sid = sc ? sc.getClientID() : "(Mgr)";
- var pid = pc ? pc.getClientID() : "(Mgr)";
- this._log("(PageBus.policy) [" + pid + ", " + sid + "] onPublish: " +
- (res ? "ALLOWED " : "DENIED ") + topic);
- }
- return res;
- };
- PageBus.policy.HubPolicy.prototype.onSubscribe = function(topic, sc) {
- var res = true;
- var origin;
- if (sc != null) {
- origin = sc.getPartnerOrigin();
- if (!origin)
- return false;
- res = this.isAllowed.call(this, origin, PageBus.policy.Ops.Subscribe, topic);
- }
- if (this._log) {
- var cid = sc ? sc.getClientID() : "(Mgr)";
- this._log("(PageBus.policy) [" + cid + "] onSubscribe: " +
- (res ? "ALLOWED " : "DENIED ") + topic);
- }
- return res;
- };
- PageBus.policy.HubPolicy.prototype.onUnsubscribe = function(topic, sc) {
- };
- PageBus.policy.HubPolicy.prototype.onSend = function(topic, data, origin) {
- var res = this.isAllowed(origin, PageBus.policy.Ops.Subscribe, topic);
- if (this._log)
- this._log("(PageBus.policy) [" + origin + "] onSend: " + (res ? "ALLOWED " : "DENIED ") + topic);
- return res;
- };
- PageBus.policy.HubPolicy.prototype.onReceive = function(topic, data, origin) {
- var res = this.isAllowed(origin, PageBus.policy.Ops.Publish, topic);
- if (this._log)
- this._log("(PageBus.policy) [" + origin + "] onReceive: " + (res ? "ALLOWED " : "DENIED ") + topic);
- return res;
- };
- PageBus.policy.HubPolicy.prototype._getMyOrigin = function() {
- var o = window.location.href.match(/[^:]*:\/\/[^:\/\?#]*/);
- return o[0];
- };
- PageBus.policy.HubPolicy.prototype.grant = function(origin, op, name) {
- if ((!origin) || (!op) || (!name))
- throw new Error(PageBus.policy.Error.BadParameters);
- var t = PageBus._esc(origin) + "." + op + "." + name;
- PageBus.policy._assertName(t);
- this._topicMgr.store(t, { dm: origin, op: op, tp: name });
- if (this._log)
- this._log("(PageBus.policy) [" + origin + "] grant: " + op + " on " + name);
- var cacheName = "_pagebus.cache.s." + name;
- this._topicMgr.store(PageBus._esc(origin) + "." + op + "." + cacheName, { dm: origin, op: op, tp: cacheName });
- if (this._log)
- this._log("(PageBus.policy) [" + origin + "] implicit grant: " + op + " on " + cacheName);
- };
- PageBus.policy.HubPolicy.prototype.revoke = function(origin, op, name) {
- if ((!origin) || (!op) || (!name))
- throw new Error(PageBus.policy.Error.BadParameters);
- var t = PageBus._esc(origin) + "." + op + "." + name;
- PageBus.policy._assertName(t);
- this._topicMgr.clear(t);
- if (this._log)
- this._log("(PageBus.policy) [" + origin + "] revoke: " + op + " on " + name);
- var cacheName = "_pagebus.cache.s." + name;
- this._topicMgr.clear(PageBus._esc(origin) + "." + op + "." + cacheName);
- if (this._log)
- this._log("(PageBus.policy) [" + origin + "] implicit revoke: " + op + " on " + cacheName);
- };
- PageBus.policy.HubPolicy.prototype.revokeAll = function(origin) {
- if ((!origin))
- throw new Error(PageBus.policy.Error.BadParameters);
- this._topicMgr.wildcardClear(PageBus._esc(origin) + ".**");
- if (this._log)
- this._log("(PageBus.policy) [" + origin + "] revokeAll");
- };
- PageBus.policy.HubPolicy.prototype.isAllowed = function(origin, op, name) {
- if ((!origin) || (origin == "") || (!op) || (op == "") || (!name) || (name == ""))
- throw new Error(PageBus.policy.Error.BadParameters);
- var t = PageBus._esc(origin) + "." + op + "." + name;
- return this._topicMgr.exists(t, false);
- };
- PageBus.policy.HubPolicy.prototype.listAllowed = function(origin, op) {
- if ((!origin) || (!op))
- throw new Error(PageBus.policy.Error.BadParameters);
- var qr = this._topicMgr.wildcardMatch(PageBus._esc(origin) + "." + op + ".**");
- var res = [];
- for (var r in qr) {
- if (qr[r].value.tp.substring(0, 9) != "_pagebus.")
- res.push(qr[r].value.tp);
- }
- return res;
- };
- PageBus.cache = {};
- PageBus.cache.Error = {
- // This topic is not being cached by the local hub instance
- NoCache: "PageBus.cache.Error.NoCache"
- };
- PageBus._cache = {};
- PageBus._cache.isCacheable = function(subData) {
- return ( (subData) && (typeof subData == "object") && (subData["PageBus"]) && (subData.PageBus["cache"]) );
- };
- PageBus._cache.Cache = function() {
- this._refs = {};
- this._doCache = new PageBus._TopicMatcher();
- this._caches = new PageBus._TopicMatcher();
- };
- PageBus._cache.Cache.prototype.add = function(topic, subID) {
- var dc;
- var dca = this._doCache.match(topic, true);
- if (dca.length > 0)
- dc = dca[0].value;
- else {
- dc = { rc: 0 };
- this._doCache.store(topic, dc);
- }
- dc.rc++;
- this._refs[subID] = topic;
- };
- PageBus._cache.Cache.prototype.remove = function(subID) {
- var topic = this._refs[subID];
- if (!topic)
- return;
- delete this._refs[subID];
- var dca = this._doCache.match(topic, true);
- if (dca.length == 0)
- return;
- dca[0].value.rc--;
- if (dca[0].value.rc == 0) {
- this._doCache.clear(topic);
- var caches = this._caches.wildcardMatch(topic);
- for (var i = 0; i < caches.length; i++) {
- if (!(this._doCache.exists(caches[i].topic, false)))
- this._caches.clear(caches[i].topic);
- }
- }
- };
- PageBus._cache.Cache.prototype.storeCopy = function(topic, value) {
- PageBus._assertPubTopic(topic);
- var copy = PageBus._copy(value);
- this._caches.store(topic, copy);
- };
- PageBus._cache.Cache.prototype.clear = function(topic, value) {
- PageBus._assertPubTopic(topic);
- this._caches.clear(topic);
- };
- PageBus._cache.Cache.prototype.query = function(topic) {
- PageBus._assertSubTopic(topic);
- return this._caches.wildcardMatch(topic);
- };
- PageBus._cache.Cache.prototype.isCaching = function(topic) {
- for (var r in this._refs)
- false;
- return this._doCache.exists(topic, false);
- };
- PageBus._enableMH = function() {
- var MHClass = OpenAjax.hub.ManagedHub;
- OpenAjax.hub.ManagedHub = function(params) {
- if (!params) {
- throw new Error(OpenAjax.hub.Error.BadParameters);
- }
- if ((!params.onPublish) || (!params.onSubscribe)) {
- throw new Error(OpenAjax.hub.Error.BadParameters);
- }
- var defaultParams = {};
- var defaultPolicy = null;
- if (!params["PageBus"])
- params.PageBus = {};
- if (!params.PageBus["policy"])
- params.PageBus.policy = defaultPolicy;
- if (!params["scope"])
- params.scope = window;
- this._pagebus = {
- _params: {},
- _getActualParameters: function() {
- return this._pagebus._params;
- },
- _policy: params.PageBus.policy,
- _hub: this
- };
- // newly added
- this.pagebus = { _hub: this };
- var pb = this._pagebus;
- pb._cache = new PageBus._cache.Cache();
- this.pagebus.query = function(topic) {
- return this._hub._pagebus._cache.query(topic);
- };
- this.pagebus.store = function(topic, data) {
- if (this._hub._pagebus._cache.isCaching(topic))
- this._hub.publish(topic, data);
- };
- this.pagebus.clear = function(topic) {
- if (this._hub._pagebus._cache.isCaching(topic))
- this._hub.publish(topic, null);
- };
- var appScope = params.scope ? params.scope : window;
- var params2 = this._pagebus._params;
- for (var pn in params) {
- params2[pn] = params[pn];
- }
- params2.scope = params.scope ? params.scope : window;
- params2.onPublish = function(topic, data, pcont, scont) {
- try {
- var pol = params2.PageBus.policy;
- if (pol) {
- var res = pol.onPublish.call(pol, topic, data, pcont, scont);
- if (!res)
- return false;
- }
- return params.onPublish.call(appScope, topic, data, pcont, scont);
- }
- catch(e) {
- return false;
- }
- };
- params2.onSubscribe = function(topic, scont) {
- try {
- var pol = params2.PageBus.policy;
- if (pol) {
- var res = pol.onSubscribe.call(pol, topic, scont);
- if (!res)
- return false;
- }
- res = params.onSubscribe.call(appScope, topic, scont);
- return res;
- }
- catch(e) {
- return false;
- }
- };
- params2.onUnsubscribe = function(topic, scont) {
- try {
- var pol = params2.PageBus.policy;
- if (pol) {
- pol.onUnsubscribe.call(pol, params.scope, topic, scont);
- }
- params.onUnsubscribe.call(appScope, topic, scont);
- }
- catch(e) {
- return;
- }
- };
- if (!pb._policy) {
- pb._policy = null; // new PageBus.policy.HubPolicy(params);
- }
- MHClass.call(this, params2);
- this.getParameters = function() {
- return params;
- };
- };
- OpenAjax.hub.ManagedHub.prototype = MHClass.prototype;
- var p = OpenAjax.hub.ManagedHub.prototype.publish;
- var s = OpenAjax.hub.ManagedHub.prototype.subscribe;
- var u = OpenAjax.hub.ManagedHub.prototype.unsubscribe;
- var p4c = OpenAjax.hub.ManagedHub.prototype.publishForClient;
- var s4c = OpenAjax.hub.ManagedHub.prototype.subscribeForClient;
- var u4c = OpenAjax.hub.ManagedHub.prototype.unsubscribeForClient;
- var gs = OpenAjax.hub.ManagedHub.prototype.getScope;
- OpenAjax.hub.ManagedHub.prototype.publish = function(topic, data) {
- PageBus._assertPubTopic(topic);
- if (this._pagebus._cache.isCaching(topic)) {
- if (data == null)
- this._pagebus._cache.clear(topic);
- else
- this._pagebus._cache.storeCopy(topic, data);
- }
- p.call(this, topic, data);
- };
- OpenAjax.hub.ManagedHub.prototype.subscribe = function(topic, onData, scope, onComplete, subscriberData) {
- PageBus._assertSubTopic(topic);
- var sid = s.call(this, topic, onData, scope, onComplete, subscriberData);
- if (PageBus._cache.isCacheable(subscriberData)) {
- var cache = this._pagebus._cache;
- cache.add(topic, sid);
- var vals = cache.query(topic);
- for (var i = 0; i < vals.length; i++) {
- try {
- onData.call(scope ? scope : window, vals[i].topic, vals[i].value, subscriberData);
- }
- catch(e) {
- PageBus._debug();
- }
- }
- }
- return sid;
- };
- OpenAjax.hub.ManagedHub.prototype.unsubscribe = function(subID, onComplete, scope) {
- var cache = this._pagebus._cache;
- cache.remove(subID);
- u.call(this, subID, onComplete, scope);
- };
- OpenAjax.hub.ManagedHub.prototype.getScope = function() {
- return gs.call(this);
- };
- OpenAjax.hub.ManagedHub.prototype.publishForClient = function(container, topic, data) {
- PageBus._assertPubTopic(topic);
- if ((!this._pagebus._policy) ||
- ( this._pagebus._policy.isAllowed(container.getPartnerOrigin(), PageBus.policy.Ops.Publish, topic) )) {
- if (this._pagebus._cache.isCaching(topic)) {
- if (data == null)
- this._pagebus._cache.clear(topic);
- else
- this._pagebus._cache.storeCopy(topic, data);
- }
- }
- p4c.call(this, container, topic, data);
- };
- OpenAjax.hub.ManagedHub.prototype.subscribeForClient = function(container, topic, containerSubID) {
- PageBus._assertSubTopic(topic);
- var mgrSubID = s4c.call(this, container, topic, containerSubID);
- if (topic.substring(0, 17) == "_pagebus.cache.s.") {
- var t = topic.substring(17);
- this._pagebus._cache.add(t, mgrSubID);
- var vals = this._pagebus._cache.query(t);
- function _sendValues() {
- for (var i = 0; i < vals.length; i++) {
- container.sendToClient(vals[i].topic, vals[i].value, containerSubID);
- }
- }
- setTimeout(_sendValues, 0);
- }
- return mgrSubID;
- };
- OpenAjax.hub.ManagedHub.prototype.unsubscribeForClient = function(container, managerSubID) {
- this._pagebus._cache.remove(managerSubID);
- try {
- var sdata = this.getSubscriberData(managerSubID);
- if (PageBus._cache.isCacheable(sdata)) {
- if (this._pagebus._cacheSids[managerSubID]) {
- this.unsubscribe(this._pagebus._cacheSids[managerSubID], null, null);
- delete this._pagebus._cacheSids[managerSubID];
- this._pagebus._cache.remove(managerSubID);
- }
- }
- u4c.call(this, container, managerSubID);
- }
- catch(e) {
- PageBus._debug();
- }
- };
- };
- PageBus._enableMH();
- PageBus.HubClientExtender = function(hub, params) {
- var that = this;
- this._hub = hub;
- // Set up default parameters
- this._params = params;
- if (!params["PageBus"])
- this._params.PageBus = { log: params.log };
- if (!params.PageBus["policy"]) {
- params.PageBus.policy = null;
- }
- // Initialize this HubClientExtender
- this._policy = params.PageBus.policy;
- this._cache = new PageBus._cache.Cache();
- this._cacheSids = {};
- // Store references to the hub's sub, unsub and publish functions:
- this._wrappedSubscribe = hub.subscribe;
- this._wrappedUnsubscribe = hub.unsubscribe;
- this._wrappedPublish = hub.publish;
- this._wrappedDisconnect = hub.disconnect;
- // Replace the hub's pub, sub and uns functions with wrappers:
- hub.publish = function(topic, data) {
- that._publishWrapper(topic, data);
- };
- hub.subscribe = function(topic, onData, scope, onComplete, subData) {
- return that._subscribeWrapper(topic, onData, scope, onComplete, subData);
- };
- hub.unsubscribe = function(subscriptionID, onComplete, scope) {
- that._unsubscribeWrapper(subscriptionID, onComplete, scope);
- };
- hub.disconnect = function(onComplete, scope) {
- that._disconnectWrapper(onComplete, scope);
- }
- };
- PageBus.HubClientExtender.prototype._publishWrapper = function(topic, data) {
- if (!this._hub.isConnected())
- throw new Error(OpenAjax.hub.Error.Disconnected);
- PageBus._assertPubTopic(topic);
- if (this._cache.isCaching(topic)) {
- if (data == null)
- this._cache.clear(topic);
- else
- this._cache.storeCopy(topic, data);
- }
- var origin = this._hub.getPartnerOrigin();
- if (!origin)
- throw new Error(OpenAjax.hub.Error.Disconnected);
- if (this._policy && (! this._policy.onSend(topic, data, origin)))
- return;
- this._wrappedPublish.call(this._hub, topic, data);
- };
- PageBus.HubClientExtender.prototype._subscribeWrapper = function(topic, onData, scope, onComplete, subData) {
- var that = this;
- if (!this._hub.isConnected())
- throw new Error(OpenAjax.hub.Error.Disconnected);
- PageBus._assertSubTopic(topic);
- if (!onData)
- throw new Error(OpenAjax.hub.Error.BadParameters);
- var policy = this._policy;
- var origin = this._hub.getPartnerOrigin();
- if (!origin)
- throw new Error(OpenAjax.hub.Error.Disconnected);
- if (policy && (! policy.onReceive.call(policy, topic, null, origin)))
- throw new Error(OpenAjax.hub.Error.NotAllowed);
- dataHook = function(t, d, sd) {
- var policy = that._policy;
- var origin = that._hub.getPartnerOrigin();
- if (policy && (! policy.onReceive.call(policy, t, d, origin)))
- return;
- if (PageBus._cache.isCacheable(sd)) {
- if (that._cache.isCaching(t)) {
- if (d == null)
- that._cache.clear(t);
- else
- that._cache.storeCopy(t, d);
- }
- }
- try {
- var s = scope ? scope : window;
- onData.call(s, t, d, sd);
- }
- catch(e) {
- PageBus._debug();
- }
- };
- completeHook = function(item, suc, err) {
- if (!suc) {
- if (that._cacheSids[item]) {
- that._hub.unsubscribe(that._cacheSids[item], null, null);
- delete that._cacheSids[item];
- that._cache.remove(item);
- }
- }
- try {
- var s = scope ? scope : window;
- onComplete.call(s, item, suc, err);
- }
- catch(e) {
- PageBus._debug();
- }
- };
- var sid = this._wrappedSubscribe.call(this._hub, topic, dataHook, scope, completeHook, subData);
- try {
- this._hub.getSubscriberData(sid);
- } catch(e) {
- if (e.message == OpenAjax.hub.Error.NoSubscription) {
- // unsubscribe was synchronously called within completeHook
- return sid;
- }
- }
- if (PageBus._cache.isCacheable(subData)) {
- this._cache.add(topic, sid);
- this._cacheSids[sid] = this._hub.subscribe(
- "_pagebus.cache.s." + topic,
- function(t, d, sd) {
- var policy = this._policy;
- var origin = this._hub.getPartnerOrigin();
- if (policy && (! this._policy.onReceive(t, d, origin)))
- return;
- if (PageBus._cache.isCacheable(subData)) {
- if (this._cache.isCaching(t)) {
- if (d == null)
- this._cache.clear(t);
- else
- this._cache.storeCopy(t, d);
- }
- }
- try {
- var s = scope ? scope : window;
- onData.call(s, t, d, subData);
- }
- catch(e) {
- PageBus._debug();
- }
- },
- this,
- function(item, suc, err) {
- },
- null);
- }
- return sid;
- };
- PageBus.HubClientExtender.prototype._unsubscribeWrapper = function(subscriptionID, onComplete, scope) {
- if (!this._hub.isConnected())
- throw new Error(OpenAjax.hub.Error.Disconnected);
- if ((subscriptionID == null) || (subscriptionID == ""))
- throw new Error(OpenAjax.hub.Error.BadParameters);
- var sdata = this._hub.getSubscriberData(subscriptionID);
- if (PageBus._cache.isCacheable(sdata)) {
- if (this._cacheSids[subscriptionID]) {
- this._hub.unsubscribe(this._cacheSids[subscriptionID], null, null);
- delete this._cacheSids[subscriptionID];
- this._cache.remove(subscriptionID);
- }
- }
- this._wrappedUnsubscribe.call(this._hub, subscriptionID, onComplete, scope);
- };
- PageBus.HubClientExtender.prototype._disconnectWrapper = function(onComplete, scope) {
- this._cache._caches.wildcardClear("**");
- this._cacheSids = {};
- this._wrappedDisconnect.call(this._hub, onComplete, scope);
- };
- PageBus.HubClientExtender.prototype.query = function(topic) {
- return this._cache.query(topic); // do not throw NoCache; can use query across broad ranges that are partly cached.
- };
- PageBus.HubClientExtender.prototype.store = function(topic, data) {
- if (this._cache.isCaching(topic))
- this._hub.publish(topic, data);
- else
- throw new Error(PageBus.cache.Error.NoCache);
- };
- PageBus.HubClientExtender.prototype.clear = function(topic) {
- if (this._cache.isCaching(topic))
- this._hub.publish(topic, null);
- else
- throw new Error(PageBus.cache.Error.NoCache);
- };
- /**
- * enableHubClientClass
- * Global function that prepares a HubClient class so that when it is
- * instantiated via the "new" operator, the resulting instance is
- * PageBus-enabled.
- */
- PageBus.enableHubClientClass = function(hubClass) {
- hubClass.prototype._pagebusWrappedConnect = hubClass.prototype.connect;
- hubClass.prototype.connect = function(onComplete, scope) {
- var hub = this;
- if (!hub.pagebus) {
- hub.pagebus = new PageBus.HubClientExtender(hub, hub.getParameters());
- }
- hub._pagebusWrappedConnect.call(hub, onComplete, scope);
- };
- };
- //////////////////////////////////////////////////////////////////////
- //PageBus-enable the reference implementation:
- if (!OpenAjax.hub) {
- debugger;
- }
- if (OpenAjax.hub.InlineHubClient)
- PageBus.enableHubClientClass(OpenAjax.hub.InlineHubClient);
- if (OpenAjax.hub.IframeHubClient)
- PageBus.enableHubClientClass(OpenAjax.hub.IframeHubClient);
- OpenAjax.hub._hub = new OpenAjax.hub.ManagedHub({
- onSubscribe: function(topic, ctnr) {
- return true;
- },
- onPublish: function(topic, data, pcont, scont) {
- return true;
- }
- });
- OpenAjax.hub.subscribe = function(topic, onData, scope, subscriberData) {
- if (typeof onData === "string") {
- scope = scope || window;
- onData = scope[ onData ] || null;
- }
- return OpenAjax.hub._hub.subscribe(topic, onData, scope, null, subscriberData);
- }
- OpenAjax.hub.unsubscribe = function(subscriptionID) {
- return OpenAjax.hub._hub.unsubscribe(subscriptionID);
- }
- OpenAjax.hub.publish = function(topic, data) {
- OpenAjax.hub._hub.publish(topic, data);
- }
- //////////////////////////////////////////////////////////////////////
- PageBus.publish = function(topic, data) {
- OpenAjax.hub.publish(topic, data);
- };
- PageBus.subscribe = function(topic, scope, onData, subscriberData) {
- return OpenAjax.hub.subscribe(topic, onData, scope, subscriberData);
- };
- PageBus.unsubscribe = function(sub) {
- OpenAjax.hub.unsubscribe(sub);
- };
- PageBus.store = function(topic, data) {
- OpenAjax.hub._hub.pagebus.store(topic, data);
- };
- PageBus.query = function(topic) {
- return OpenAjax.hub._hub.pagebus.query(topic);
- };
- OpenAjax.hub.registerLibrary("PageBus", "http://pagebus.org/pagebus", "2.0", {});