PageRenderTime 120ms CodeModel.GetById 8ms RepoModel.GetById 0ms app.codeStats 1ms

/errai-bus/src/main/java/org/jboss/errai/bus/public/js/pagebus_2.0.js

https://github.com/bpmc/errai
JavaScript | 6701 lines | 3952 code | 540 blank | 2209 comment | 561 complexity | 2712716bc6bad39a2d961a61505ccfb5 MD5 | raw file
Possible License(s): Apache-2.0
  1. /*
  2. * Copyright 2010 JBoss, a divison Red Hat, Inc
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. // prevent re-definition of the OpenAjax object
  17. if (!window["OpenAjax"]) {
  18. OpenAjax = new function() {
  19. this.hub = {};
  20. h = this.hub;
  21. h.implementer = "http://openajax.org";
  22. h.implVersion = "2.0";
  23. h.specVersion = "2.0";
  24. h.implExtraData = {};
  25. var libs = {};
  26. h.libraries = libs;
  27. var ooh = "org.openajax.hub.";
  28. h.registerLibrary = function(prefix, nsURL, version, extra) {
  29. libs[prefix] = {
  30. prefix: prefix,
  31. namespaceURI: nsURL,
  32. version: version,
  33. extraData: extra
  34. };
  35. this.publish(ooh + "registerLibrary", libs[prefix]);
  36. }
  37. h.unregisterLibrary = function(prefix) {
  38. this.publish(ooh + "unregisterLibrary", libs[prefix]);
  39. delete libs[prefix];
  40. }
  41. }
  42. /**
  43. * Error
  44. *
  45. * Standard Error names used when the standard functions need to throw Errors.
  46. */
  47. OpenAjax.hub.Error = {
  48. // Either a required argument is missing or an invalid argument was provided
  49. BadParameters: "OpenAjax.hub.Error.BadParameters",
  50. // The specified hub has been disconnected and cannot perform the requested
  51. // operation:
  52. Disconnected: "OpenAjax.hub.Error.Disconnected",
  53. // Container with specified ID already exists:
  54. Duplicate: "OpenAjax.hub.Error.Duplicate",
  55. // The specified ManagedHub has no such Container (or it has been removed)
  56. NoContainer: "OpenAjax.hub.Error.NoContainer",
  57. // The specified ManagedHub or Container has no such subscription
  58. NoSubscription: "OpenAjax.hub.Error.NoSubscription",
  59. // Permission denied by manager's security policy
  60. NotAllowed: "OpenAjax.hub.Error.NotAllowed",
  61. // Wrong communications protocol identifier provided by Container or HubClient
  62. WrongProtocol: "OpenAjax.hub.Error.WrongProtocol"
  63. };
  64. /**
  65. * SecurityAlert
  66. *
  67. * Standard codes used when attempted security violations are detected. Unlike
  68. * Errors, these codes are not thrown as exceptions but rather passed into the
  69. * SecurityAlertHandler function registered with the Hub instance.
  70. */
  71. OpenAjax.hub.SecurityAlert = {
  72. // Container did not load (possible frame phishing attack)
  73. LoadTimeout: "OpenAjax.hub.SecurityAlert.LoadTimeout",
  74. // Hub suspects a frame phishing attack against the specified container
  75. FramePhish: "OpenAjax.hub.SecurityAlert.FramePhish",
  76. // Hub detected a message forgery that purports to come to a specifed
  77. // container
  78. ForgedMsg: "OpenAjax.hub.SecurityAlert.ForgedMsg"
  79. };
  80. /**
  81. * Debugging Help
  82. *
  83. * OpenAjax.hub.enableDebug
  84. *
  85. * If OpenAjax.hub.enableDebug is set to true, then the "debugger" keyword
  86. * will get hit whenever a user callback throws an exception, thereby
  87. * bringing up the JavaScript debugger.
  88. */
  89. OpenAjax.hub._debugger = function() {
  90. if (OpenAjax.hub.enableDebug) debugger; // REMOVE ON BUILD
  91. }
  92. ////////////////////////////////////////////////////////////////////////////////
  93. /**
  94. * Hub interface
  95. *
  96. * Hub is implemented on the manager side by ManagedHub and on the client side
  97. * by ClientHub.
  98. */
  99. //OpenAjax.hub.Hub = function() {}
  100. /**
  101. * Subscribe to a topic.
  102. *
  103. * @param {String} topic
  104. * A valid topic string. MAY include wildcards.
  105. * @param {Function} onData
  106. * Callback function that is invoked whenever an event is
  107. * published on the topic
  108. * @param {Object} [scope]
  109. * When onData callback or onComplete callback is invoked,
  110. * the JavaScript "this" keyword refers to this scope object.
  111. * If no scope is provided, default is window.
  112. * @param {Function} [onComplete]
  113. * Invoked to tell the client application whether the
  114. * subscribe operation succeeded or failed.
  115. * @param {*} [subscriberData]
  116. * Client application provides this data, which is handed
  117. * back to the client application in the subscriberData
  118. * parameter of the onData callback function.
  119. *
  120. * @returns subscriptionID
  121. * Identifier representing the subscription. This identifier is an
  122. * arbitrary ID string that is unique within this Hub instance
  123. * @type {String}
  124. *
  125. * @throws {OpenAjax.hub.Error.Disconnected} if this Hub instance is not in CONNECTED state
  126. * @throws {OpenAjax.hub.Error.BadParameters} if the topic is invalid (e.g. contains an empty token)
  127. */
  128. //OpenAjax.hub.Hub.prototype.subscribe = function( topic, onData, scope, onComplete, subscriberData ) {}
  129. /**
  130. * Publish an event on a topic
  131. *
  132. * @param {String} topic
  133. * A valid topic string. MUST NOT include wildcards.
  134. * @param {*} data
  135. * Valid publishable data. To be portable across different
  136. * Container implementations, this value SHOULD be serializable
  137. * as JSON.
  138. *
  139. * @throws {OpenAjax.hub.Error.Disconnected} if this Hub instance is not in CONNECTED state
  140. * @throws {OpenAjax.hub.Error.BadParameters} if the topic cannot be published (e.g. contains
  141. * wildcards or empty tokens) or if the data cannot be published (e.g. cannot be serialized as JSON)
  142. */
  143. //OpenAjax.hub.Hub.prototype.publish = function( topic, data ) {}
  144. /**
  145. * Unsubscribe from a subscription
  146. *
  147. * @param {String} subscriptionID
  148. * A subscriptionID returned by Hub.subscribe()
  149. * @param {Function} [onComplete]
  150. * Callback function invoked when unsubscribe completes
  151. * @param {Object} [scope]
  152. * When onComplete callback function is invoked, the JavaScript "this"
  153. * keyword refers to this scope object.
  154. * If no scope is provided, default is window.
  155. *
  156. * @throws {OpenAjax.hub.Error.Disconnected} if this Hub instance is not in CONNECTED state
  157. * @throws {OpenAjax.hub.Error.NoSubscription} if no such subscription is found
  158. */
  159. //OpenAjax.hub.Hub.prototype.unsubscribe = function( subscriptionID, onComplete, scope ) {}
  160. /**
  161. * Return true if this Hub instance is in the Connected state.
  162. * Else returns false.
  163. *
  164. * This function can be called even if the Hub is not in a CONNECTED state.
  165. *
  166. * @returns Boolean
  167. * @type {Boolean}
  168. */
  169. //OpenAjax.hub.Hub.prototype.isConnected = function() {}
  170. /**
  171. * Returns the scope associated with this Hub instance and which will be used
  172. * with callback functions.
  173. *
  174. * This function can be called even if the Hub is not in a CONNECTED state.
  175. *
  176. * @returns scope object
  177. * @type {Object}
  178. */
  179. //OpenAjax.hub.Hub.prototype.getScope = function() {}
  180. /**
  181. * Returns the subscriberData parameter that was provided when
  182. * Hub.subscribe was called.
  183. *
  184. * @param {String} subscriberID
  185. * The subscriberID of a subscription
  186. *
  187. * @returns subscriberData
  188. * @type {*}
  189. *
  190. * @throws {OpenAjax.hub.Error.Disconnected} if this Hub instance is not in CONNECTED state
  191. * @throws {OpenAjax.hub.Error.NoSubscription} if there is no such subscription
  192. */
  193. //OpenAjax.hub.Hub.prototype.getSubscriberData = function(subscriberID) {}
  194. /**
  195. * Returns the scope associated with a specified subscription. This scope will
  196. * be used when invoking the 'onData' callback supplied to Hub.subscribe().
  197. *
  198. * @param {String} subscriberID
  199. * The subscriberID of a subscription
  200. *
  201. * @returns scope
  202. * @type {*}
  203. *
  204. * @throws {OpenAjax.hub.Error.Disconnected} if this Hub instance is not in CONNECTED state
  205. * @throws {OpenAjax.hub.Error.NoSubscription} if there is no such subscription
  206. */
  207. //OpenAjax.hub.Hub.prototype.getSubscriberScope = function(subscriberID) {}
  208. /**
  209. * Returns the params object associated with this Hub instance.
  210. * Allows mix-in code to access parameters passed into constructor that created
  211. * this Hub instance.
  212. *
  213. * @returns params the params object associated with this Hub instance
  214. * @type {Object}
  215. */
  216. //OpenAjax.hub.Hub.prototype.getParameters = function() {}
  217. ////////////////////////////////////////////////////////////////////////////////
  218. /**
  219. * HubClient interface
  220. *
  221. * Extends Hub interface.
  222. *
  223. * A HubClient implementation is typically specific to a particular
  224. * implementation of Container.
  225. */
  226. /**
  227. * Create a new HubClient. All HubClient constructors MUST have this
  228. * signature.
  229. * @constructor
  230. *
  231. * @param {Object} params
  232. * Parameters used to instantiate the HubClient.
  233. * Once the constructor is called, the params object belongs to the
  234. * HubClient. The caller MUST not modify it.
  235. * Implementations of HubClient may specify additional properties
  236. * for the params object, besides those identified below.
  237. *
  238. * @param {Function} params.HubClient.onSecurityAlert
  239. * Called when an attempted security breach is thwarted
  240. * @param {Object} [params.HubClient.scope]
  241. * Whenever one of the HubClient's callback functions is called,
  242. * references to "this" in the callback will refer to the scope object.
  243. * If not provided, the default is window.
  244. *
  245. * @throws {OpenAjax.hub.Error.BadParameters} if any of the required
  246. * parameters is missing, or if a parameter value is invalid in
  247. * some way.
  248. */
  249. //OpenAjax.hub.HubClient = function( params ) {}
  250. /**
  251. * Requests a connection to the ManagedHub, via the Container
  252. * associated with this HubClient.
  253. *
  254. * If the Container accepts the connection request, the HubClient's
  255. * state is set to CONNECTED and the HubClient invokes the
  256. * onComplete callback function.
  257. *
  258. * If the Container refuses the connection request, the HubClient
  259. * invokes the onComplete callback function with an error code.
  260. * The error code might, for example, indicate that the Container
  261. * is being destroyed.
  262. *
  263. * In most implementations, this function operates asynchronously,
  264. * so the onComplete callback function is the only reliable way to
  265. * determine when this function completes and whether it has succeeded
  266. * or failed.
  267. *
  268. * A client application may call HubClient.disconnect and then call
  269. * HubClient.connect.
  270. *
  271. * @param {Function} [onComplete]
  272. * Callback function to call when this operation completes.
  273. * @param {Object} [scope]
  274. * When the onComplete function is invoked, the JavaScript "this"
  275. * keyword refers to this scope object.
  276. * If no scope is provided, default is window.
  277. *
  278. * @throws {OpenAjax.hub.Error.Duplicate} if the HubClient is already connected
  279. */
  280. //OpenAjax.hub.HubClient.prototype.connect = function( onComplete, scope ) {}
  281. /**
  282. * Disconnect from the ManagedHub
  283. *
  284. * Disconnect immediately:
  285. *
  286. * 1. Sets the HubClient's state to DISCONNECTED.
  287. * 2. Causes the HubClient to send a Disconnect request to the
  288. * associated Container.
  289. * 3. Ensures that the client application will receive no more
  290. * onData or onComplete callbacks associated with this
  291. * connection, except for the disconnect function's own
  292. * onComplete callback.
  293. * 4. Automatically destroys all of the HubClient's subscriptions.
  294. *
  295. * In most implementations, this function operates asynchronously,
  296. * so the onComplete callback function is the only reliable way to
  297. * determine when this function completes and whether it has succeeded
  298. * or failed.
  299. *
  300. * A client application is allowed to call HubClient.disconnect and
  301. * then call HubClient.connect.
  302. *
  303. * @param {Function} [onComplete]
  304. * Callback function to call when this operation completes.
  305. * @param {Object} [scope]
  306. * When the onComplete function is invoked, the JavaScript "this"
  307. * keyword refers to the scope object.
  308. * If no scope is provided, default is window.
  309. *
  310. * @throws {OpenAjax.hub.Error.Disconnected} if the HubClient is already
  311. * disconnected
  312. */
  313. //OpenAjax.hub.HubClient.prototype.disconnect = function( onComplete, scope ) {}
  314. /**
  315. * If DISCONNECTED: Returns null
  316. * If CONNECTED: Returns the origin associated with the window containing the
  317. * Container associated with this HubClient instance. The origin has the format
  318. *
  319. * [protocol]://[host]
  320. *
  321. * where:
  322. *
  323. * [protocol] is "http" or "https"
  324. * [host] is the hostname of the partner page.
  325. *
  326. * @returns Partner's origin
  327. * @type {String}
  328. */
  329. //OpenAjax.hub.HubClient.prototype.getPartnerOrigin = function() {}
  330. /**
  331. * Returns the client ID of this HubClient
  332. *
  333. * @returns clientID
  334. * @type {String}
  335. */
  336. //OpenAjax.hub.HubClient.prototype.getClientID = function() {}
  337. ////////////////////////////////////////////////////////////////////////////////
  338. /**
  339. * OpenAjax.hub.ManagedHub
  340. *
  341. * Managed hub API for the manager application and for Containers.
  342. *
  343. * Implements OpenAjax.hub.Hub.
  344. */
  345. /**
  346. * Create a new ManagedHub instance
  347. * @constructor
  348. *
  349. * This constructor automatically sets the ManagedHub's state to
  350. * CONNECTED.
  351. *
  352. * @param {Object} params
  353. * Parameters used to instantiate the ManagedHub.
  354. * Once the constructor is called, the params object belongs exclusively to
  355. * the ManagedHub. The caller MUST not modify it.
  356. *
  357. * The params object may contain the following properties:
  358. *
  359. * @param {Function} params.onPublish
  360. * Callback function that is invoked whenever a
  361. * data value published by a Container is about
  362. * to be delivered to some (possibly the same) Container.
  363. * This callback function implements a security policy;
  364. * it returns true if the delivery of the data is
  365. * permitted and false if permission is denied.
  366. * @param {Function} params.onSubscribe
  367. * Called whenever a Container tries to subscribe
  368. * on behalf of its client.
  369. * This callback function implements a security policy;
  370. * it returns true if the subscription is permitted
  371. * and false if permission is denied.
  372. * @param {Function} [params.onUnsubscribe]
  373. * Called whenever a Container unsubscribes on behalf of its client.
  374. * Unlike the other callbacks, onUnsubscribe is intended only for
  375. * informative purposes, and is not used to implement a security
  376. * policy.
  377. * @param {Object} [params.scope]
  378. * Whenever one of the ManagedHub's callback functions is called,
  379. * references to the JavaScript "this" keyword in the callback
  380. * function refer to this scope object
  381. * If no scope is provided, default is window.
  382. * @param {Function} [params.log] Optional logger function. Would
  383. * be used to log to console.log or equivalent.
  384. *
  385. * @throws {OpenAjax.hub.Error.BadParameters} if any of the required
  386. * parameters are missing
  387. */
  388. OpenAjax.hub.ManagedHub = function(params) {
  389. if (! params || ! params.onPublish || ! params.onSubscribe)
  390. throw new Error(OpenAjax.hub.Error.BadParameters);
  391. this._p = params;
  392. this._onUnsubscribe = params.onUnsubscribe ? params.onUnsubscribe : null;
  393. this._scope = params.scope || window;
  394. if (params.log) {
  395. var scope = this._scope;
  396. var logfunc = params.log;
  397. this._log = function(msg) {
  398. logfunc.call(scope, "ManagedHub: " + msg);
  399. };
  400. } else {
  401. this._log = function() {
  402. };
  403. }
  404. this._subscriptions = { c:{}, s:null };
  405. this._containers = {};
  406. // Sequence # used to create IDs that are unique within this hub
  407. this._seq = 0;
  408. this._active = true;
  409. this._isPublishing = false;
  410. this._pubQ = [];
  411. }
  412. /**
  413. * Subscribe to a topic on behalf of a Container. Called only by
  414. * Container implementations, NOT by manager applications.
  415. *
  416. * This function:
  417. * 1. Checks with the ManagedHub's onSubscribe security policy
  418. * to determine whether this Container is allowed to subscribe
  419. * to this topic.
  420. * 2. If the subscribe operation is permitted, subscribes to the
  421. * topic and returns the ManagedHub's subscription ID for this
  422. * subscription.
  423. * 3. If the subscribe operation is not permitted, throws
  424. * OpenAjax.hub.Error.NotAllowed.
  425. *
  426. * When data is published on the topic, the ManagedHub's
  427. * onPublish security policy will be invoked to ensure that
  428. * this Container is permitted to receive the published data.
  429. * If the Container is allowed to receive the data, then the
  430. * Container's sendToClient function will be invoked.
  431. *
  432. * When a Container needs to create a subscription on behalf of
  433. * its client, the Container MUST use this function to create
  434. * the subscription.
  435. *
  436. * @param {OpenAjax.hub.Container} container
  437. * A Container
  438. * @param {String} topic
  439. * A valid topic
  440. * @param {String} containerSubID
  441. * Arbitrary string ID that the Container uses to
  442. * represent the subscription. Must be unique within the
  443. * context of the Container
  444. *
  445. * @returns managerSubID
  446. * Arbitrary string ID that this ManagedHub uses to
  447. * represent the subscription. Will be unique within the
  448. * context of this ManagedHub
  449. * @type {String}
  450. *
  451. * @throws {OpenAjax.hub.Error.Disconnected} if this.isConnected() returns false
  452. * @throws {OpenAjax.hub.Error.NotAllowed} if subscription request is denied by the onSubscribe security policy
  453. * @throws {OpenAjax.hub.Error.BadParameters} if one of the parameters, e.g. the topic, is invalid
  454. */
  455. OpenAjax.hub.ManagedHub.prototype.subscribeForClient = function(container, topic, containerSubID) {
  456. this._assertConn();
  457. // check subscribe permission
  458. if (this._invokeOnSubscribe(topic, container)) {
  459. // return ManagedHub's subscriptionID for this subscription
  460. return this._subscribe(topic, this._sendToClient, this, { c: container, sid: containerSubID });
  461. }
  462. throw new Error(OpenAjax.hub.Error.NotAllowed);
  463. }
  464. /**
  465. * Unsubscribe from a subscription on behalf of a Container. Called only by
  466. * Container implementations, NOT by manager application code.
  467. *
  468. * This function:
  469. * 1. Destroys the specified subscription
  470. * 2. Calls the ManagedHub's onUnsubscribe callback function
  471. *
  472. * This function can be called even if the ManagedHub is not in a CONNECTED state.
  473. *
  474. * @param {OpenAjax.hub.Container} container
  475. * container instance that is unsubscribing
  476. * @param {String} managerSubID
  477. * opaque ID of a subscription, returned by previous call to subscribeForClient()
  478. *
  479. * @throws {OpenAjax.hub.Error.NoSubscription} if subscriptionID does not refer to a valid subscription
  480. */
  481. OpenAjax.hub.ManagedHub.prototype.unsubscribeForClient = function(container, managerSubID) {
  482. this._unsubscribe(managerSubID);
  483. this._invokeOnUnsubscribe(container, managerSubID);
  484. }
  485. /**
  486. * Publish data on a topic on behalf of a Container. Called only by
  487. * Container implementations, NOT by manager application code.
  488. *
  489. * @param {OpenAjax.hub.Container} container
  490. * Container on whose behalf data should be published
  491. * @param {String} topic
  492. * Valid topic string. Must NOT contain wildcards.
  493. * @param {*} data
  494. * Valid publishable data. To be portable across different
  495. * Container implementations, this value SHOULD be serializable
  496. * as JSON.
  497. *
  498. * @throws {OpenAjax.hub.Error.Disconnected} if this.isConnected() returns false
  499. * @throws {OpenAjax.hub.Error.BadParameters} if one of the parameters, e.g. the topic, is invalid
  500. */
  501. OpenAjax.hub.ManagedHub.prototype.publishForClient = function(container, topic, data) {
  502. this._assertConn();
  503. this._publish(topic, data, container);
  504. }
  505. /**
  506. * Destroy this ManagedHub
  507. *
  508. * 1. Sets state to DISCONNECTED. All subsequent attempts to add containers,
  509. * publish or subscribe will throw the Disconnected error. We will
  510. * continue to allow "cleanup" operations such as removeContainer
  511. * and unsubscribe, as well as read-only operations such as
  512. * isConnected
  513. * 2. Remove all Containers associated with this ManagedHub
  514. */
  515. OpenAjax.hub.ManagedHub.prototype.disconnect = function() {
  516. this._active = false;
  517. for (var c in this._containers) {
  518. this.removeContainer(this._containers[c]);
  519. }
  520. }
  521. /**
  522. * Get a container belonging to this ManagedHub by its clientID, or null
  523. * if this ManagedHub has no such container
  524. *
  525. * This function can be called even if the ManagedHub is not in a CONNECTED state.
  526. *
  527. * @param {String} containerId
  528. * Arbitrary string ID associated with the container
  529. *
  530. * @returns container associated with given ID
  531. * @type {OpenAjax.hub.Container}
  532. */
  533. OpenAjax.hub.ManagedHub.prototype.getContainer = function(containerId) {
  534. var container = this._containers[containerId];
  535. return container ? container : null;
  536. }
  537. /**
  538. * Returns an array listing all containers belonging to this ManagedHub.
  539. * The order of the Containers in this array is arbitrary.
  540. *
  541. * This function can be called even if the ManagedHub is not in a CONNECTED state.
  542. *
  543. * @returns container array
  544. * @type {OpenAjax.hub.Container[]}
  545. */
  546. OpenAjax.hub.ManagedHub.prototype.listContainers = function() {
  547. var res = [];
  548. for (var c in this._containers) {
  549. res.push(this._containers[c]);
  550. }
  551. return res;
  552. }
  553. /**
  554. * Add a container to this ManagedHub.
  555. *
  556. * This function should only be called by a Container constructor.
  557. *
  558. * @param {OpenAjax.hub.Container} container
  559. * A Container to be added to this ManagedHub
  560. *
  561. * @throws {OpenAjax.hub.Error.Duplicate} if there is already a Container
  562. * in this ManagedHub whose clientId is the same as that of container
  563. * @throws {OpenAjax.hub.Error.Disconnected} if this.isConnected() returns false
  564. */
  565. OpenAjax.hub.ManagedHub.prototype.addContainer = function(container) {
  566. this._assertConn();
  567. var containerId = container.getClientID();
  568. if (this._containers[containerId]) {
  569. throw new Error(OpenAjax.hub.Error.Duplicate);
  570. }
  571. this._containers[containerId] = container;
  572. }
  573. /**
  574. * Remove a container from this ManagedHub immediately
  575. *
  576. * This function can be called even if the ManagedHub is not in a CONNECTED state.
  577. *
  578. * @param {OpenAjax.hub.Container} container
  579. * A Container to be removed from this ManagedHub
  580. *
  581. * @throws {OpenAjax.hub.Error.NoContainer} if no such container is found
  582. */
  583. OpenAjax.hub.ManagedHub.prototype.removeContainer = function(container) {
  584. var containerId = container.getClientID();
  585. if (! this._containers[ containerId ]) {
  586. throw new Error(OpenAjax.hub.Error.NoContainer);
  587. }
  588. container.remove();
  589. delete this._containers[ containerId ];
  590. }
  591. /*** OpenAjax.hub.Hub interface implementation ***/
  592. /**
  593. * Subscribe to a topic.
  594. *
  595. * This implementation of Hub.subscribe is synchronous. When subscribe
  596. * is called:
  597. *
  598. * 1. The ManagedHub's onSubscribe callback is invoked. The
  599. * container parameter is null, because the manager application,
  600. * rather than a container, is subscribing.
  601. * 2. If onSubscribe returns true, then the subscription is created.
  602. * 3. The onComplete callback is invoked.
  603. * 4. Then this function returns.
  604. *
  605. * @param {String} topic
  606. * A valid topic string. MAY include wildcards.
  607. * @param {Function} onData
  608. * Callback function that is invoked whenever an event is
  609. * published on the topic
  610. * @param {Object} [scope]
  611. * When onData callback or onComplete callback is invoked,
  612. * the JavaScript "this" keyword refers to this scope object.
  613. * If no scope is provided, default is window.
  614. * @param {Function} [onComplete]
  615. * Invoked to tell the client application whether the
  616. * subscribe operation succeeded or failed.
  617. * @param {*} [subscriberData]
  618. * Client application provides this data, which is handed
  619. * back to the client application in the subscriberData
  620. * parameter of the onData and onComplete callback functions.
  621. *
  622. * @returns subscriptionID
  623. * Identifier representing the subscription. This identifier is an
  624. * arbitrary ID string that is unique within this Hub instance
  625. * @type {String}
  626. *
  627. * @throws {OpenAjax.hub.Error.Disconnected} if this Hub instance is not in CONNECTED state
  628. * @throws {OpenAjax.hub.Error.BadParameters} if the topic is invalid (e.g. contains an empty token)
  629. */
  630. OpenAjax.hub.ManagedHub.prototype.subscribe = function(topic, onData, scope, onComplete, subscriberData) {
  631. this._assertConn();
  632. this._assertSubTopic(topic);
  633. if (! onData) {
  634. throw new Error(OpenAjax.hub.Error.BadParameters);
  635. }
  636. // check subscribe permission
  637. if (! this._invokeOnSubscribe(topic, null)) {
  638. this._invokeOnComplete(onComplete, scope, null, false, OpenAjax.hub.Error.NotAllowed);
  639. return;
  640. }
  641. // on publish event, check publish permissions
  642. scope = scope || window;
  643. var that = this;
  644. function publishCB(topic, data, sd, pcont) {
  645. if (that._invokeOnPublish(topic, data, pcont, null)) {
  646. try {
  647. onData.call(scope, topic, data, subscriberData);
  648. } catch(e) {
  649. OpenAjax.hub._debugger();
  650. that._log("caught error from onData callback to Hub.subscribe(): " + e.message);
  651. }
  652. }
  653. }
  654. var subID = this._subscribe(topic, publishCB, scope, subscriberData);
  655. this._invokeOnComplete(onComplete, scope, subID, true);
  656. return subID;
  657. }
  658. /**
  659. * Publish an event on a topic
  660. *
  661. * This implementation of Hub.publish is synchronous. When publish
  662. * is called:
  663. *
  664. * 1. The target subscriptions are identified.
  665. * 2. For each target subscription, the ManagedHub's onPublish
  666. * callback is invoked. Data is only delivered to a target
  667. * subscription if the onPublish callback returns true.
  668. * The pcont parameter of the onPublish callback is null.
  669. * This is because the ManagedHub, rather than a container,
  670. * is publishing the data.
  671. *
  672. * @param {String} topic
  673. * A valid topic string. MUST NOT include wildcards.
  674. * @param {*} data
  675. * Valid publishable data. To be portable across different
  676. * Container implementations, this value SHOULD be serializable
  677. * as JSON.
  678. *
  679. * @throws {OpenAjax.hub.Error.Disconnected} if this Hub instance is not in CONNECTED state
  680. * @throws {OpenAjax.hub.Error.BadParameters} if the topic cannot be published (e.g. contains
  681. * wildcards or empty tokens) or if the data cannot be published (e.g. cannot be serialized as JSON)
  682. */
  683. OpenAjax.hub.ManagedHub.prototype.publish = function(topic, data) {
  684. this._assertConn();
  685. this._assertPubTopic(topic);
  686. this._publish(topic, data, null);
  687. }
  688. /**
  689. * Unsubscribe from a subscription
  690. *
  691. * This implementation of Hub.unsubscribe is synchronous. When unsubscribe
  692. * is called:
  693. *
  694. * 1. The subscription is destroyed.
  695. * 2. The ManagedHub's onUnsubscribe callback is invoked, if there is one.
  696. * 3. The onComplete callback is invoked.
  697. * 4. Then this function returns.
  698. *
  699. * @param {String} subscriptionID
  700. * A subscriptionID returned by Hub.subscribe()
  701. * @param {Function} [onComplete]
  702. * Callback function invoked when unsubscribe completes
  703. * @param {Object} [scope]
  704. * When onComplete callback function is invoked, the JavaScript "this"
  705. * keyword refers to this scope object.
  706. * If no scope is provided, default is window.
  707. *
  708. * @throws {OpenAjax.hub.Error.Disconnected} if this Hub instance is not in CONNECTED state
  709. * @throws {OpenAjax.hub.Error.NoSubscription} if no such subscription is found
  710. */
  711. OpenAjax.hub.ManagedHub.prototype.unsubscribe = function(subscriptionID, onComplete, scope) {
  712. this._assertConn();
  713. if (typeof subscriptionID === "undefined" || subscriptionID == null) {
  714. throw new Error(OpenAjax.hub.Error.BadParameters);
  715. }
  716. this._unsubscribe(subscriptionID);
  717. this._invokeOnUnsubscribe(null, subscriptionID);
  718. this._invokeOnComplete(onComplete, scope, subscriptionID, true);
  719. }
  720. /**
  721. * Returns true if disconnect() has NOT been called on this ManagedHub,
  722. * else returns false
  723. *
  724. * @returns Boolean
  725. * @type {Boolean}
  726. */
  727. OpenAjax.hub.ManagedHub.prototype.isConnected = function() {
  728. return this._active;
  729. }
  730. /**
  731. * Returns the scope associated with this Hub instance and which will be used
  732. * with callback functions.
  733. *
  734. * This function can be called even if the Hub is not in a CONNECTED state.
  735. *
  736. * @returns scope object
  737. * @type {Object}
  738. */
  739. OpenAjax.hub.ManagedHub.prototype.getScope = function() {
  740. return this._scope;
  741. }
  742. /**
  743. * Returns the subscriberData parameter that was provided when
  744. * Hub.subscribe was called.
  745. *
  746. * @param subscriberID
  747. * The subscriberID of a subscription
  748. *
  749. * @returns subscriberData
  750. * @type {*}
  751. *
  752. * @throws {OpenAjax.hub.Error.Disconnected} if this Hub instance is not in CONNECTED state
  753. * @throws {OpenAjax.hub.Error.NoSubscription} if there is no such subscription
  754. */
  755. OpenAjax.hub.ManagedHub.prototype.getSubscriberData = function(subscriberID) {
  756. this._assertConn();
  757. var path = subscriberID.split(".");
  758. var sid = path.pop();
  759. var sub = this._getSubscriptionObject(this._subscriptions, path, 0, sid);
  760. if (sub)
  761. return sub.data;
  762. throw new Error(OpenAjax.hub.Error.NoSubscription);
  763. }
  764. /**
  765. * Returns the scope associated with a specified subscription. This scope will
  766. * be used when invoking the 'onData' callback supplied to Hub.subscribe().
  767. *
  768. * @param subscriberID
  769. * The subscriberID of a subscription
  770. *
  771. * @returns scope
  772. * @type {*}
  773. *
  774. * @throws {OpenAjax.hub.Error.Disconnected} if this Hub instance is not in CONNECTED state
  775. * @throws {OpenAjax.hub.Error.NoSubscription} if there is no such subscription
  776. */
  777. OpenAjax.hub.ManagedHub.prototype.getSubscriberScope = function(subscriberID) {
  778. this._assertConn();
  779. var path = subscriberID.split(".");
  780. var sid = path.pop();
  781. var sub = this._getSubscriptionObject(this._subscriptions, path, 0, sid);
  782. if (sub)
  783. return sub.scope;
  784. throw new Error(OpenAjax.hub.Error.NoSubscription);
  785. }
  786. /**
  787. * Returns the params object associated with this Hub instance.
  788. * Allows mix-in code to access parameters passed into constructor that created
  789. * this Hub instance.
  790. *
  791. * @returns params the params object associated with this Hub instance
  792. * @type {Object}
  793. */
  794. OpenAjax.hub.ManagedHub.prototype.getParameters = function() {
  795. return this._p;
  796. }
  797. /* PRIVATE FUNCTIONS */
  798. /**
  799. * Send a message to a container's client.
  800. * This is an OAH subscriber's data callback. It is private to ManagedHub
  801. * and serves as an adapter between the OAH 1.0 API and Container.sendToClient.
  802. *
  803. * @param {String} topic Topic on which data was published
  804. * @param {Object} data Data to be delivered to the client
  805. * @param {Object} sd Object containing properties
  806. * c: container to which data must be sent
  807. * sid: subscription ID within that container
  808. * @param {Object} pcont Publishing container, or null if this data was
  809. * published by the manager
  810. */
  811. OpenAjax.hub.ManagedHub.prototype._sendToClient = function(topic, data, sd, pcont) {
  812. if (!this.isConnected()) {
  813. return;
  814. }
  815. if (this._invokeOnPublish(topic, data, pcont, sd.c)) {
  816. sd.c.sendToClient(topic, data, sd.sid);
  817. }
  818. }
  819. OpenAjax.hub.ManagedHub.prototype._assertConn = function() {
  820. if (!this.isConnected()) {
  821. throw new Error(OpenAjax.hub.Error.Disconnected);
  822. }
  823. }
  824. OpenAjax.hub.ManagedHub.prototype._assertPubTopic = function(topic) {
  825. if ((topic == null) || (topic == "") || (topic.indexOf("*") != -1) ||
  826. (topic.indexOf("..") != -1) || (topic.charAt(0) == ".") ||
  827. (topic.charAt(topic.length - 1) == ".")) {
  828. throw new Error(OpenAjax.hub.Error.BadParameters);
  829. }
  830. }
  831. OpenAjax.hub.ManagedHub.prototype._assertSubTopic = function(topic) {
  832. if (! topic) {
  833. throw new Error(OpenAjax.hub.Error.BadParameters);
  834. }
  835. var path = topic.split(".");
  836. var len = path.length;
  837. for (var i = 0; i < len; i++) {
  838. var p = path[i];
  839. if ((p == "") ||
  840. ((p.indexOf("*") != -1) && (p != "*") && (p != "**"))) {
  841. throw new Error(OpenAjax.hub.Error.BadParameters);
  842. }
  843. if ((p == "**") && (i < len - 1)) {
  844. throw new Error(OpenAjax.hub.Error.BadParameters);
  845. }
  846. }
  847. }
  848. OpenAjax.hub.ManagedHub.prototype._invokeOnComplete = function(func, scope, item, success, errorCode) {
  849. if (func) { // onComplete is optional
  850. try {
  851. scope = scope || window;
  852. func.call(scope, item, success, errorCode);
  853. } catch(e) {
  854. OpenAjax.hub._debugger();
  855. this._log("caught error from onComplete callback: " + e.message);
  856. }
  857. }
  858. }
  859. OpenAjax.hub.ManagedHub.prototype._invokeOnPublish = function(topic, data, pcont, scont) {
  860. try {
  861. return this._p.onPublish.call(this._scope, topic, data, pcont, scont);
  862. } catch(e) {
  863. OpenAjax.hub._debugger();
  864. this._log("caught error from onPublish callback to constructor: " + e.message);
  865. }
  866. return false;
  867. }
  868. OpenAjax.hub.ManagedHub.prototype._invokeOnSubscribe = function(topic, container) {
  869. try {
  870. return this._p.onSubscribe.call(this._scope, topic, container);
  871. } catch(e) {
  872. OpenAjax.hub._debugger();
  873. this._log("caught error from onSubscribe callback to constructor: " + e.message);
  874. }
  875. return false;
  876. }
  877. OpenAjax.hub.ManagedHub.prototype._invokeOnUnsubscribe = function(container, managerSubID) {
  878. if (this._onUnsubscribe) {
  879. var topic = managerSubID.slice(0, managerSubID.lastIndexOf("."));
  880. try {
  881. this._onUnsubscribe.call(this._scope, topic, container);
  882. } catch(e) {
  883. OpenAjax.hub._debugger();
  884. this._log("caught error from onUnsubscribe callback to constructor: " + e.message);
  885. }
  886. }
  887. }
  888. OpenAjax.hub.ManagedHub.prototype._subscribe = function(topic, onData, scope, subscriberData) {
  889. var handle = topic + "." + this._seq;
  890. var sub = { scope: scope, cb: onData, data: subscriberData, sid: this._seq++ };
  891. var path = topic.split(".");
  892. this._recursiveSubscribe(this._subscriptions, path, 0, sub);
  893. return handle;
  894. }
  895. OpenAjax.hub.ManagedHub.prototype._recursiveSubscribe = function(tree, path, index, sub) {
  896. var token = path[index];
  897. if (index == path.length) {
  898. sub.next = tree.s;
  899. tree.s = sub;
  900. } else {
  901. if (typeof tree.c == "undefined") {
  902. tree.c = {};
  903. }
  904. if (typeof tree.c[token] == "undefined") {
  905. tree.c[token] = { c: {}, s: null };
  906. this._recursiveSubscribe(tree.c[token], path, index + 1, sub);
  907. } else {
  908. this._recursiveSubscribe(tree.c[token], path, index + 1, sub);
  909. }
  910. }
  911. }
  912. OpenAjax.hub.ManagedHub.prototype._publish = function(topic, data, pcont) {
  913. // if we are currently handling a publish event, then queue this request
  914. // and handle later, one by one
  915. if (this._isPublishing) {
  916. this._pubQ.push({ t: topic, d: data, p: pcont });
  917. return;
  918. }
  919. this._safePublish(topic, data, pcont);
  920. while (this._pubQ.length > 0) {
  921. var pub = this._pubQ.shift();
  922. this._safePublish(pub.t, pub.d, pub.p);
  923. }
  924. }
  925. OpenAjax.hub.ManagedHub.prototype._safePublish = function(topic, data, pcont) {
  926. this._isPublishing = true;
  927. var path = topic.split(".");
  928. this._recursivePublish(this._subscriptions, path, 0, topic, data, pcont);
  929. this._isPublishing = false;
  930. }
  931. OpenAjax.hub.ManagedHub.prototype._recursivePublish = function(tree, path, index, name, msg, pcont) {
  932. if (typeof tree != "undefined") {
  933. var node;
  934. if (index == path.length) {
  935. node = tree;
  936. } else {
  937. this._recursivePublish(tree.c[path[index]], path, index + 1, name, msg, pcont);
  938. this._recursivePublish(tree.c["*"], path, index + 1, name, msg, pcont);
  939. node = tree.c["**"];
  940. }
  941. if (typeof node != "undefined") {
  942. var sub = node.s;
  943. while (sub) {
  944. var sc = sub.scope;
  945. var cb = sub.cb;
  946. var d = sub.data;
  947. var sid = sub.sid;
  948. if (typeof cb == "string") {
  949. // get a function object
  950. cb = sc[cb];
  951. }
  952. cb.call(sc, name, msg, d, pcont);
  953. sub = sub.next;
  954. }
  955. }
  956. }
  957. }
  958. OpenAjax.hub.ManagedHub.prototype._unsubscribe = function(subscriptionID) {
  959. var path = subscriptionID.split(".");
  960. var sid = path.pop();
  961. if (! this._recursiveUnsubscribe(this._subscriptions, path, 0, sid)) {
  962. throw new Error(OpenAjax.hub.Error.NoSubscription);
  963. }
  964. }
  965. /**
  966. * @returns 'true' if properly unsubscribed; 'false' otherwise
  967. */
  968. OpenAjax.hub.ManagedHub.prototype._recursiveUnsubscribe = function(tree, path, index, sid) {
  969. if (typeof tree == "undefined") {
  970. return false;
  971. }
  972. if (index < path.length) {
  973. var childNode = tree.c[path[index]];
  974. if (! childNode) {
  975. return false;
  976. }
  977. this._recursiveUnsubscribe(childNode, path, index + 1, sid);
  978. if (childNode.s == null) {
  979. for (var x in childNode.c) {
  980. return true;
  981. }
  982. delete tree.c[path[index]];
  983. }
  984. } else {
  985. var sub = tree.s;
  986. var sub_prev = null;
  987. var found = false;
  988. while (sub) {
  989. if (sid == sub.sid) {
  990. found = true;
  991. if (sub == tree.s) {
  992. tree.s = sub.next;
  993. } else {
  994. sub_prev.next = sub.next;
  995. }
  996. break;
  997. }
  998. sub_prev = sub;
  999. sub = sub.next;
  1000. }
  1001. if (! found) {
  1002. return false;
  1003. }
  1004. }
  1005. return true;
  1006. }
  1007. OpenAjax.hub.ManagedHub.prototype._getSubscriptionObject = function(tree, path, index, sid) {
  1008. if (typeof tree != "undefined") {
  1009. if (index < path.length) {
  1010. var childNode = tree.c[path[index]];
  1011. return this._getSubscriptionObject(childNode, path, index + 1, sid);
  1012. }
  1013. var sub = tree.s;
  1014. while (sub) {
  1015. if (sid == sub.sid) {
  1016. return sub;
  1017. }
  1018. sub = sub.next;
  1019. }
  1020. }
  1021. return null;
  1022. }
  1023. ////////////////////////////////////////////////////////////////////////////////
  1024. /**
  1025. * Container
  1026. * @constructor
  1027. *
  1028. * Container represents an instance of a manager-side object that contains and
  1029. * communicates with a single client of the hub. The container might be an inline
  1030. * container, an iframe FIM container, or an iframe PostMessage container, or
  1031. * it might be an instance of some other implementation.
  1032. *
  1033. * @param {OpenAjax.hub.ManagedHub} hub
  1034. * Managed Hub instance
  1035. * @param {String} clientID
  1036. * A string ID that identifies a particular client of a Managed Hub. Unique
  1037. * within the context of the ManagedHub.
  1038. * @param {Object} params
  1039. * Parameters used to instantiate the Container.
  1040. * Once the constructor is called, the params object belongs exclusively to
  1041. * the Container. The caller MUST not modify it.
  1042. * Implementations of Container may specify additional properties
  1043. * for the params object, besides those identified below.
  1044. * The following params properties MUST be supported by all Container
  1045. * implementations:
  1046. * @param {Function} params.Container.onSecurityAlert
  1047. * Called when an attempted security breach is thwarted. Function is defined
  1048. * as follows: function(container, securityAlert)
  1049. * @param {Function} [params.Container.onConnect]
  1050. * Called when the client connects to the Managed Hub. Function is defined
  1051. * as follows: function(container)
  1052. * @param {Function} [params.Container.onDisconnect]
  1053. * Called when the client disconnects from the Managed Hub. Function is
  1054. * defined as follows: function(container)
  1055. * @param {Object} [params.Container.scope]
  1056. * Whenever one of the Container's callback functions is called, references
  1057. * to "this" in the callback will refer to the scope object. If no scope is
  1058. * provided, default is window.
  1059. * @param {Function} [params.Container.log]
  1060. * Optional logger function. Would be used to log to console.log or
  1061. * equivalent.
  1062. *
  1063. * @throws {OpenAjax.hub.Error.BadParameters} if required params are not
  1064. * present or null
  1065. * @throws {OpenAjax.hub.Error.Duplicate} if a Container with this clientID
  1066. * already exists in the given Managed Hub
  1067. * @throws {OpenAjax.hub.Error.Disconnected} if ManagedHub is not connected
  1068. */
  1069. //OpenAjax.hub.Container = function( hub, clientID, params ) {}
  1070. /**
  1071. * Send a message to the client inside this container. This function MUST only
  1072. * be called by ManagedHub.
  1073. *
  1074. * @param {String} topic
  1075. * The topic name for the published message
  1076. * @param {*} data
  1077. * The payload. Can be any JSON-serializable value.
  1078. * @param {String} containerSubscriptionId
  1079. * Container's ID for a subscription, from previous call to
  1080. * subscribeForClient()
  1081. */
  1082. //OpenAjax.hub.Container.prototype.sendToClient = function( topic, data, containerSubscriptionId ) {}
  1083. /**
  1084. * Shut down a container. remove does all of the following:
  1085. * - disconnects container from HubClient
  1086. * - unsubscribes from all of its existing subscriptions in the ManagedHub
  1087. *
  1088. * This function is only called by ManagedHub.removeContainer
  1089. * Calling this function does NOT cause the container's onDisconnect callback to
  1090. * be invoked.
  1091. */
  1092. //OpenAjax.hub.Container.prototype.remove = function() {}
  1093. /**
  1094. * Returns true if the given client is connected to the managed hub.
  1095. * Else returns false.
  1096. *
  1097. * @returns true if the client is connected to the managed hub
  1098. * @type boolean
  1099. */
  1100. //OpenAjax.hub.Container.prototype.isConnected = function() {}
  1101. /**
  1102. * Returns the clientID passed in when this Container was instantiated.
  1103. *
  1104. * @returns The clientID
  1105. * @type {String}
  1106. */
  1107. //OpenAjax.hub.Container.prototype.getClientID = function() {}
  1108. /**
  1109. * If DISCONNECTED:
  1110. * Returns null
  1111. * If CONNECTED:
  1112. * Returns the origin associated with the window containing the HubClient
  1113. * associated with this Container instance. The origin has the format
  1114. *
  1115. * [protocol]://[host]
  1116. *
  1117. * where:
  1118. *
  1119. * [protocol] is "http" or "https"
  1120. * [host] is the hostname of the partner page.
  1121. *
  1122. * @returns Partner's origin
  1123. * @type {String}
  1124. */
  1125. //OpenAjax.hub.Container.prototype.getPartnerOrigin = function() {}
  1126. /**
  1127. * Returns the params object associated with this Container instance.
  1128. * Allows mix-in code to access parameters passed into constructor that created
  1129. * this Container instance.
  1130. *
  1131. * @returns params
  1132. * The params object associated with this Container instance
  1133. * @type {Object}
  1134. */
  1135. //OpenAjax.hub.Container.prototype.getParameters = function() {}
  1136. /**
  1137. * Returns the ManagedHub to which this Container belongs.
  1138. *
  1139. * @returns ManagedHub
  1140. * The ManagedHub object associated with this Container instance
  1141. * @type {OpenAjax.hub.ManagedHub}
  1142. */
  1143. //OpenAjax.hub.Container.prototype.getHub = function() {}
  1144. ////////////////////////////////////////////////////////////////////////////////
  1145. /*
  1146. * Unmanaged Hub
  1147. */
  1148. /**
  1149. * OpenAjax.hub._hub is the default ManagedHub instance that we use to
  1150. * provide OAH 1.0 behavior.
  1151. */
  1152. OpenAjax.hub._hub = new OpenAjax.hub.ManagedHub({
  1153. onSubscribe: function(topic, ctnr) {
  1154. return true;
  1155. },
  1156. onPublish: function(topic, data, pcont, scont) {
  1157. return true;
  1158. }
  1159. });
  1160. /**
  1161. * Subscribe to a topic.
  1162. *
  1163. * @param {String} topic
  1164. * A valid topic string. MAY include wildcards.
  1165. * @param {Function|String} onData
  1166. * Callback function that is invoked whenever an event is published on the
  1167. * topic. If 'onData' is a string, then it represents the name of a
  1168. * function on the 'scope' object.
  1169. * @param {Object} [scope]
  1170. * When onData callback is invoked,
  1171. * the JavaScript "this" keyword refers to this scope object.
  1172. * If no scope is provided, default is window.
  1173. * @param {*} [subscriberData]
  1174. * Client application provides this data, which is handed
  1175. * back to the client application in the subscriberData
  1176. * parameter of the onData callback function.
  1177. *
  1178. * @returns {String} Identifier representing the subscription.
  1179. *
  1180. * @throws {OpenAjax.hub.Error.BadParameters} if the topic is invalid
  1181. * (e.g.contains an empty token)
  1182. */
  1183. OpenAjax.hub.subscribe = function(topic, onData, scope, subscriberData) {
  1184. // resolve the 'onData' function if it is a string
  1185. if (typeof onData === "string") {
  1186. scope = scope || window;
  1187. onData = scope[ onData ] || null;
  1188. }
  1189. return OpenAjax.hub._hub.subscribe(topic, onData, scope, null, subscriberData);
  1190. }
  1191. /**
  1192. * Unsubscribe from a subscription.
  1193. *
  1194. * @param {String} subscriptionID
  1195. * Subscription identifier returned by subscribe()
  1196. *
  1197. * @throws {OpenAjax.hub.Error.NoSubscription} if no such subscription is found
  1198. */
  1199. OpenAjax.hub.unsubscribe = function(subscriptionID) {
  1200. return OpenAjax.hub._hub.unsubscribe(subscriptionID);
  1201. }
  1202. /**
  1203. * Publish an event on a topic.
  1204. *
  1205. * @param {String} topic
  1206. * A valid topic string. MUST NOT include wildcards.
  1207. * @param {*} data
  1208. * Valid publishable data.
  1209. *
  1210. * @throws {OpenAjax.hub.Error.BadParameters} if the topic cannot be published
  1211. * (e.g. contains wildcards or empty tokens)
  1212. */
  1213. OpenAjax.hub.publish = function(topic, data) {
  1214. OpenAjax.hub._hub.publish(topic, data);
  1215. }
  1216. ////////////////////////////////////////////////////////////////////////////////
  1217. // Register the OpenAjax Hub itself as a library.
  1218. OpenAjax.hub.registerLibrary("OpenAjax", "http://openajax.org/hub", "2.0", {});
  1219. } // !window["OpenAjax"]
  1220. ////////////////////////////////////////////////////////////////////////////////
  1221. /*
  1222. Copyright 2006-2009 OpenAjax Alliance
  1223. Licensed under the Apache License, Version 2.0 (the "License");
  1224. you may not use this file except in compliance with the License.
  1225. You may obtain a copy of the License at
  1226. http://www.apache.org/licenses/LICENSE-2.0
  1227. Unless required by applicable law or agreed to in writing, software
  1228. distributed under the License is distributed on an "AS IS" BASIS,
  1229. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  1230. See the License for the specific language governing permissions and
  1231. limitations under the License.
  1232. */
  1233. /**
  1234. * Create a new Inline Container.
  1235. * @constructor
  1236. * @extends OpenAjax.hub.Container
  1237. *
  1238. * InlineContainer implements the Container interface to provide a container
  1239. * that places components within the same browser frame as the main mashup
  1240. * application. As such, this container does not isolate client components into
  1241. * secure sandboxes.
  1242. *
  1243. * @param {OpenAjax.hub.ManagedHub} hub
  1244. * Managed Hub instance to which this Container belongs
  1245. * @param {String} clientID
  1246. * A string ID that identifies a particular client of a Managed Hub. Unique
  1247. * within the context of the ManagedHub.
  1248. * @param {Object} params
  1249. * Parameters used to instantiate the InlineContainer.
  1250. * Once the constructor is called, the params object belongs exclusively to
  1251. * the InlineContainer. The caller MUST not modify it.
  1252. * The following are the pre-defined properties on params:
  1253. * @param {Function} params.Container.onSecurityAlert
  1254. * Called when an attempted security breach is thwarted. Function is defined
  1255. * as follows: function(container, securityAlert)
  1256. * @param {Function} [params.Container.onConnect]
  1257. * Called when the client connects to the Managed Hub. Function is defined
  1258. * as follows: function(container)
  1259. * @param {Function} [params.Container.onDisconnect]
  1260. * Called when the client disconnects from the Managed Hub. Function is
  1261. * defined as follows: function(container)
  1262. * @param {Object} [params.Container.scope]
  1263. * Whenever one of the Container's callback functions is called, references
  1264. * to "this" in the callback will refer to the scope object. If no scope is
  1265. * provided, default is window.
  1266. * @param {Function} [params.Container.log]
  1267. * Optional logger function. Would be used to log to console.log or
  1268. * equivalent.
  1269. *
  1270. * @throws {OpenAjax.hub.Error.BadParameters} if required params are not
  1271. * present or null
  1272. * @throws {OpenAjax.hub.Error.Duplicate} if a Container with this clientID
  1273. * already exists in the given Managed Hub
  1274. * @throws {OpenAjax.hub.Error.Disconnected} if ManagedHub is not connected
  1275. */
  1276. OpenAjax.hub.InlineContainer = function(hub, clientID, params) {
  1277. if (! hub || ! clientID || ! params ||
  1278. ! params.Container || ! params.Container.onSecurityAlert) {
  1279. throw new Error(OpenAjax.hub.Error.BadParameters);
  1280. }
  1281. this._params = params;
  1282. this._hub = hub;
  1283. this._id = clientID;
  1284. this._onSecurityAlert = params.Container.onSecurityAlert;
  1285. this._onConnect = params.Container.onConnect ? params.Container.onConnect : null;
  1286. this._onDisconnect = params.Container.onDisconnect ? params.Container.onDisconnect : null;
  1287. this._scope = params.Container.scope || window;
  1288. if (params.Container.log) {
  1289. var scope = this._scope;
  1290. var logfunc = params.Container.log;
  1291. this._log = function(msg) {
  1292. logfunc.call(scope, "InlineContainer::" + clientID + ": " + msg);
  1293. };
  1294. this._doLog = true; // HW Optimization
  1295. } else {
  1296. this._log = function() {
  1297. };
  1298. }
  1299. this._connected = false;
  1300. this._subs = [];
  1301. this._subIndex = 1; // HW FIX
  1302. hub.addContainer(this);
  1303. }
  1304. /*** OpenAjax.hub.Container interface implementation ***/
  1305. OpenAjax.hub.InlineContainer.prototype.getHub = function() {
  1306. return this._hub;
  1307. };
  1308. OpenAjax.hub.InlineContainer.prototype.sendToClient = function(topic, data, subscriptionID) {
  1309. if (this.isConnected()) {
  1310. var sub = this._subs[ subscriptionID ];
  1311. try {
  1312. sub.cb.call(sub.sc, topic, data, sub.d);
  1313. } catch(e) {
  1314. OpenAjax.hub._debugger();
  1315. this._client._log("caught error from onData callback to HubClient.subscribe(): " + e.message);
  1316. }
  1317. }
  1318. }
  1319. OpenAjax.hub.InlineContainer.prototype.remove = function() {
  1320. if (this.isConnected()) {
  1321. this._disconnect();
  1322. }
  1323. }
  1324. OpenAjax.hub.InlineContainer.prototype.isConnected = function() {
  1325. return this._connected;
  1326. }
  1327. OpenAjax.hub.InlineContainer.prototype.getClientID = function() {
  1328. return this._id;
  1329. }
  1330. OpenAjax.hub.InlineContainer.prototype.getPartnerOrigin = function() {
  1331. if (this._connected) {
  1332. // HW Optimization
  1333. if (!this._cacheOrig)
  1334. this._cacheOrig = window.location.protocol + "//" + window.location.hostname;
  1335. return this._cacheOrig;
  1336. }
  1337. return null;
  1338. }
  1339. OpenAjax.hub.InlineContainer.prototype.getParameters = function() {
  1340. return this._params;
  1341. }
  1342. /*** OpenAjax.hub.HubClient interface implementation ***/
  1343. OpenAjax.hub.InlineContainer.prototype.connect = function(client, onComplete, scope) {
  1344. if (this._connected) {
  1345. throw new Error(OpenAjax.hub.Error.Duplicate);
  1346. }
  1347. this._connected = true;
  1348. this._client = client;
  1349. if (this._onConnect) {
  1350. try {
  1351. this._onConnect.call(this._scope, this);
  1352. } catch(e) {
  1353. OpenAjax.hub._debugger();
  1354. this._log("caught error from onConnect callback to constructor: " + e.message);
  1355. }
  1356. }
  1357. this._invokeOnComplete(onComplete, scope, client, true);
  1358. }
  1359. OpenAjax.hub.InlineContainer.prototype.disconnect = function(client, onComplete, scope) {
  1360. if (!this._connected) {
  1361. throw new Error(OpenAjax.hub.Error.Disconnected);
  1362. }
  1363. this._disconnect();
  1364. if (this._onDisconnect) {
  1365. try {
  1366. this._onDisconnect.call(this._scope, this);
  1367. } catch(e) {
  1368. OpenAjax.hub._debugger();
  1369. this._log("caught error from onDisconnect callback to constructor: " + e.message);
  1370. }
  1371. }
  1372. this._invokeOnComplete(onComplete, scope, client, true);
  1373. }
  1374. /*** OpenAjax.hub.Hub interface implementation ***/
  1375. OpenAjax.hub.InlineContainer.prototype.subscribe = function(topic, onData, scope, onComplete, subscriberData) {
  1376. this._assertConn();
  1377. this._assertSubTopic(topic);
  1378. if (! onData) {
  1379. throw new Error(OpenAjax.hub.Error.BadParameters);
  1380. }
  1381. var subID = "" + this._subIndex++;
  1382. var success = false;
  1383. var msg = null;
  1384. try {
  1385. var handle = this._hub.subscribeForClient(this, topic, subID);
  1386. success = true;
  1387. } catch(e) {
  1388. // failure
  1389. subID = null;
  1390. msg = e.message;
  1391. }
  1392. scope = scope || window;
  1393. if (success) {
  1394. this._subs[ subID ] = { h: handle, cb: onData, sc: scope, d: subscriberData };
  1395. }
  1396. this._invokeOnComplete(onComplete, scope, subID, success, msg);
  1397. return subID;
  1398. }
  1399. OpenAjax.hub.InlineContainer.prototype.publish = function(topic, data) {
  1400. this._assertConn();
  1401. this._assertPubTopic(topic);
  1402. this._hub.publishForClient(this, topic, data);
  1403. }
  1404. OpenAjax.hub.InlineContainer.prototype.unsubscribe = function(subscriptionID, onComplete, scope) {
  1405. this._assertConn();
  1406. if (typeof subscriptionID === "undefined" || subscriptionID == null) {
  1407. throw new Error(OpenAjax.hub.Error.BadParameters);
  1408. }
  1409. var sub = this._subs[ subscriptionID ];
  1410. if (! sub)
  1411. throw new Error(OpenAjax.hub.Error.NoSubscription);
  1412. this._hub.unsubscribeForClient(this, sub.h);
  1413. delete this._subs[ subscriptionID ];
  1414. this._invokeOnComplete(onComplete, scope, subscriptionID, true);
  1415. }
  1416. OpenAjax.hub.InlineContainer.prototype.getSubscriberData = function(subID) {
  1417. this._assertConn();
  1418. return this._getSubscription(subID).d;
  1419. }
  1420. OpenAjax.hub.InlineContainer.prototype.getSubscriberScope = function(subID) {
  1421. this._assertConn();
  1422. return this._getSubscription(subID).sc;
  1423. }
  1424. /*** PRIVATE FUNCTIONS ***/
  1425. OpenAjax.hub.InlineContainer.prototype._invokeOnComplete = function(func, scope, item, success, errorCode) {
  1426. if (func) { // onComplete is optional
  1427. try {
  1428. scope = scope || window;
  1429. func.call(scope, item, success, errorCode);
  1430. } catch(e) {
  1431. OpenAjax.hub._debugger();
  1432. // _invokeOnComplete is only called for client interfaces (Hub and HubClient)
  1433. this._client._log("caught error from onComplete callback: " + e.message);
  1434. }
  1435. }
  1436. }
  1437. OpenAjax.hub.InlineContainer.prototype._disconnect = function() {
  1438. for (var subID in this._subs) {
  1439. this._hub.unsubscribeForClient(this, this._subs[subID].h);
  1440. }
  1441. this._subs = [];
  1442. this._subIndex = 1; // HW FIX
  1443. this._connected = false;
  1444. }
  1445. OpenAjax.hub.InlineContainer.prototype._assertConn = function() {
  1446. if (! this._connected) {
  1447. throw new Error(OpenAjax.hub.Error.Disconnected);
  1448. }
  1449. }
  1450. OpenAjax.hub.InlineContainer.prototype._assertPubTopic = function(topic) {
  1451. if ((topic == null) || (topic == "") || (topic.indexOf("*") != -1) ||
  1452. (topic.indexOf("..") != -1) || (topic.charAt(0) == ".") ||
  1453. (topic.charAt(topic.length - 1) == ".")) {
  1454. throw new Error(OpenAjax.hub.Error.BadParameters);
  1455. }
  1456. }
  1457. OpenAjax.hub.InlineContainer.prototype._assertSubTopic = function(topic) {
  1458. if (! topic) {
  1459. throw new Error(OpenAjax.hub.Error.BadParameters);
  1460. }
  1461. var path = topic.split(".");
  1462. var len = path.length;
  1463. for (var i = 0; i < len; i++) {
  1464. var p = path[i];
  1465. if ((p == "") ||
  1466. ((p.indexOf("*") != -1) && (p != "*") && (p != "**"))) {
  1467. throw new Error(OpenAjax.hub.Error.BadParameters);
  1468. }
  1469. if ((p == "**") && (i < len - 1)) {
  1470. throw new Error(OpenAjax.hub.Error.BadParameters);
  1471. }
  1472. }
  1473. }
  1474. OpenAjax.hub.InlineContainer.prototype._getSubscription = function(subID) {
  1475. var sub = this._subs[ subID ];
  1476. if (sub) {
  1477. return sub;
  1478. }
  1479. throw new Error(OpenAjax.hub.Error.NoSubscription);
  1480. }
  1481. ////////////////////////////////////////////////////////////////////////////////
  1482. /**
  1483. * Create a new InlineHubClient.
  1484. * @constructor
  1485. * @extends OpenAjax.hub.HubClient
  1486. *
  1487. * @param {Object} params
  1488. * Parameters used to instantiate the HubClient.
  1489. * Once the constructor is called, the params object belongs to the
  1490. * HubClient. The caller MUST not modify it.
  1491. * The following are the pre-defined properties on params:
  1492. * @param {Function} params.HubClient.onSecurityAlert
  1493. * Called when an attempted security breach is thwarted
  1494. * @param {Object} [params.HubClient.scope]
  1495. * Whenever one of the HubClient's callback functions is called,
  1496. * references to "this" in the callback will refer to the scope object.
  1497. * If not provided, the default is window.
  1498. * @param {OpenAjax.hub.InlineContainer} params.InlineHubClient.container
  1499. * Specifies the InlineContainer to which this HubClient will connect
  1500. *
  1501. * @throws {OpenAjax.hub.Error.BadParameters} if any of the required
  1502. * parameters are missing
  1503. */
  1504. OpenAjax.hub.InlineHubClient = function(params) {
  1505. if (! params || ! params.HubClient || ! params.HubClient.onSecurityAlert ||
  1506. ! params.InlineHubClient || ! params.InlineHubClient.container) {
  1507. throw new Error(OpenAjax.hub.Error.BadParameters);
  1508. }
  1509. this._params = params;
  1510. this._onSecurityAlert = params.HubClient.onSecurityAlert;
  1511. this._scope = params.HubClient.scope || window;
  1512. this._container = params.InlineHubClient.container;
  1513. if (params.HubClient.log) {
  1514. var id = this._container.getClientID();
  1515. var scope = this._scope;
  1516. var logfunc = params.HubClient.log;
  1517. this._log = function(msg) {
  1518. logfunc.call(scope, "InlineHubClient::" + id + ": " + msg);
  1519. };
  1520. this._doLog = true; // HW Optimization
  1521. } else {
  1522. this._log = function() {
  1523. };
  1524. }
  1525. }
  1526. /*** OpenAjax.hub.HubClient interface implementation ***/
  1527. /**
  1528. * Requests a connection to the ManagedHub, via the InlineContainer
  1529. * associated with this InlineHubClient.
  1530. *
  1531. * If the Container accepts the connection request, this HubClient's
  1532. * state is set to CONNECTED and the HubClient invokes the
  1533. * onComplete callback function.
  1534. *
  1535. * If the Container refuses the connection request, the HubClient
  1536. * invokes the onComplete callback function with an error code.
  1537. * The error code might, for example, indicate that the Container
  1538. * is being destroyed.
  1539. *
  1540. * If the HubClient is already connected, calling connect will cause
  1541. * the HubClient to immediately invoke the onComplete callback with
  1542. * the error code OpenAjax.hub.Error.Duplicate.
  1543. *
  1544. * @param {Function} [onComplete]
  1545. * Callback function to call when this operation completes.
  1546. * @param {Object} [scope]
  1547. * When the onComplete function is invoked, the JavaScript "this"
  1548. * keyword refers to this scope object.
  1549. * If no scope is provided, default is window.
  1550. *
  1551. * In this implementation of InlineHubClient, this function operates
  1552. * SYNCHRONOUSLY, so the onComplete callback function is invoked before
  1553. * this connect function returns. Developers are cautioned that in
  1554. * IframeHubClient implementations, this is not the case.
  1555. *
  1556. * A client application may call InlineHubClient.disconnect and then call
  1557. * InlineHubClient.connect to reconnect to the Managed Hub.
  1558. */
  1559. OpenAjax.hub.InlineHubClient.prototype.connect = function(onComplete, scope) {
  1560. this._container.connect(this, onComplete, scope);
  1561. }
  1562. /**
  1563. * Disconnect from the ManagedHub
  1564. *
  1565. * Disconnect immediately:
  1566. *
  1567. * 1. Sets the HubClient's state to DISCONNECTED.
  1568. * 2. Causes the HubClient to send a Disconnect request to the
  1569. * associated Container.
  1570. * 3. Ensures that the client application will receive no more
  1571. * onData or onComplete callbacks associated with this
  1572. * connection, except for the disconnect function's own
  1573. * onComplete callback.
  1574. * 4. Automatically destroys all of the HubClient's subscriptions.
  1575. *
  1576. * @param {Function} [onComplete]
  1577. * Callback function to call when this operation completes.
  1578. * @param {Object} [scope]
  1579. * When the onComplete function is invoked, the JavaScript "this"
  1580. * keyword refers to the scope object.
  1581. * If no scope is provided, default is window.
  1582. *
  1583. * In this implementation of InlineHubClient, the disconnect function operates
  1584. * SYNCHRONOUSLY, so the onComplete callback function is invoked before
  1585. * this function returns. Developers are cautioned that in IframeHubClient
  1586. * implementations, this is not the case.
  1587. *
  1588. * A client application is allowed to call HubClient.disconnect and
  1589. * then call HubClient.connect in order to reconnect.
  1590. */
  1591. OpenAjax.hub.InlineHubClient.prototype.disconnect = function(onComplete, scope) {
  1592. this._container.disconnect(this, onComplete, scope);
  1593. }
  1594. OpenAjax.hub.InlineHubClient.prototype.getPartnerOrigin = function() {
  1595. return this._container.getPartnerOrigin();
  1596. }
  1597. OpenAjax.hub.InlineHubClient.prototype.getClientID = function() {
  1598. return this._container.getClientID();
  1599. }
  1600. /*** OpenAjax.hub.Hub interface implementation ***/
  1601. /**
  1602. * Subscribe to a topic.
  1603. *
  1604. * @param {String} topic
  1605. * A valid topic string. MAY include wildcards.
  1606. * @param {Function} onData
  1607. * Callback function that is invoked whenever an event is
  1608. * published on the topic
  1609. * @param {Object} [scope]
  1610. * When onData callback or onComplete callback is invoked,
  1611. * the JavaScript "this" keyword refers to this scope object.
  1612. * If no scope is provided, default is window.
  1613. * @param {Function} [onComplete]
  1614. * Invoked to tell the client application whether the
  1615. * subscribe operation succeeded or failed.
  1616. * @param {*} [subscriberData]
  1617. * Client application provides this data, which is handed
  1618. * back to the client application in the subscriberData
  1619. * parameter of the onData and onComplete callback functions.
  1620. *
  1621. * @returns subscriptionID
  1622. * Identifier representing the subscription. This identifier is an
  1623. * arbitrary ID string that is unique within this Hub instance
  1624. * @type {String}
  1625. *
  1626. * @throws {OpenAjax.hub.Error.Disconnected} if this Hub instance is not in CONNECTED state
  1627. * @throws {OpenAjax.hub.Error.BadParameters} if the topic is invalid (e.g. contains an empty token)
  1628. *
  1629. * In this implementation of InlineHubClient, the subscribe function operates
  1630. * Thus, onComplete is invoked before this function returns. Developers are
  1631. * cautioned that in most implementations of HubClient, onComplete is invoked
  1632. * after this function returns.
  1633. *
  1634. * If unsubscribe is called before subscribe completes, the subscription is
  1635. * immediately terminated, and onComplete is never invoked.
  1636. */
  1637. OpenAjax.hub.InlineHubClient.prototype.subscribe = function(topic, onData, scope, onComplete, subscriberData) {
  1638. return this._container.subscribe(topic, onData, scope, onComplete, subscriberData);
  1639. }
  1640. /**
  1641. * Publish an event on 'topic' with the given data.
  1642. *
  1643. * @param {String} topic
  1644. * A valid topic string. MUST NOT include wildcards.
  1645. * @param {*} data
  1646. * Valid publishable data. To be portable across different
  1647. * Container implementations, this value SHOULD be serializable
  1648. * as JSON.
  1649. *
  1650. * @throws {OpenAjax.hub.Error.Disconnected} if this Hub instance
  1651. * is not in CONNECTED state
  1652. *
  1653. * In this implementation, publish operates SYNCHRONOUSLY.
  1654. * Data will be delivered to subscribers after this function returns.
  1655. * In most implementations, publish operates synchronously,
  1656. * delivering its data to the clients before this function returns.
  1657. */
  1658. OpenAjax.hub.InlineHubClient.prototype.publish = function(topic, data) {
  1659. this._container.publish(topic, data);
  1660. }
  1661. /**
  1662. * Unsubscribe from a subscription
  1663. *
  1664. * @param {String} subscriptionID
  1665. * A subscriptionID returned by InlineHubClient.prototype.subscribe()
  1666. * @param {Function} [onComplete]
  1667. * Callback function invoked when unsubscribe completes
  1668. * @param {Object} [scope]
  1669. * When onComplete callback function is invoked, the JavaScript "this"
  1670. * keyword refers to this scope object.
  1671. *
  1672. * @throws {OpenAjax.hub.Error.NoSubscription} if no such subscription is found
  1673. *
  1674. * To facilitate cleanup, it is possible to call unsubscribe even
  1675. * when the HubClient is in a DISCONNECTED state.
  1676. *
  1677. * In this implementation of HubClient, this function operates SYNCHRONOUSLY.
  1678. * Thus, onComplete is invoked before this function returns. Developers are
  1679. * cautioned that in most implementations of HubClient, onComplete is invoked
  1680. * after this function returns.
  1681. */
  1682. OpenAjax.hub.InlineHubClient.prototype.unsubscribe = function(subscriptionID, onComplete, scope) {
  1683. this._container.unsubscribe(subscriptionID, onComplete, scope);
  1684. }
  1685. OpenAjax.hub.InlineHubClient.prototype.isConnected = function() {
  1686. return this._container.isConnected();
  1687. }
  1688. OpenAjax.hub.InlineHubClient.prototype.getScope = function() {
  1689. return this._scope;
  1690. }
  1691. OpenAjax.hub.InlineHubClient.prototype.getSubscriberData = function(subID) {
  1692. return this._container.getSubscriberData(subID);
  1693. }
  1694. OpenAjax.hub.InlineHubClient.prototype.getSubscriberScope = function(subID) {
  1695. return this._container.getSubscriberScope(subID);
  1696. }
  1697. /**
  1698. * Returns the params object associated with this Hub instance.
  1699. * Allows mix-in code to access parameters passed into constructor that created
  1700. * this Hub instance.
  1701. *
  1702. * @returns params the params object associated with this Hub instance
  1703. * @type {Object}
  1704. */
  1705. OpenAjax.hub.InlineHubClient.prototype.getParameters = function() {
  1706. return this._params;
  1707. }
  1708. ////////////////////////////////////////////////////////////////////////////////
  1709. /*
  1710. Copyright 2006-2009 OpenAjax Alliance
  1711. Licensed under the Apache License, Version 2.0 (the "License");
  1712. you may not use this file except in compliance with the License.
  1713. You may obtain a copy of the License at
  1714. http://www.apache.org/licenses/LICENSE-2.0
  1715. Unless required by applicable law or agreed to in writing, software
  1716. distributed under the License is distributed on an "AS IS" BASIS,
  1717. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  1718. See the License for the specific language governing permissions and
  1719. limitations under the License.
  1720. */
  1721. if (typeof OpenAjax === "undefined") {
  1722. OpenAjax = { hub: {} };
  1723. }
  1724. /**
  1725. * Create a new Iframe Container.
  1726. * @constructor
  1727. * @extends OpenAjax.hub.Container
  1728. *
  1729. * IframeContainer implements the Container interface to provide a container
  1730. * that isolates client components into secure sandboxes by leveraging the
  1731. * isolation features provided by browser iframes.
  1732. *
  1733. * @param {OpenAjax.hub.ManagedHub} hub
  1734. * Managed Hub instance to which this Container belongs
  1735. * @param {String} clientID
  1736. * A string ID that identifies a particular client of a Managed Hub. Unique
  1737. * within the context of the ManagedHub.
  1738. * @param {Object} params
  1739. * Parameters used to instantiate the IframeContainer.
  1740. * Once the constructor is called, the params object belongs exclusively to
  1741. * the IframeContainer. The caller MUST not modify it.
  1742. * The following are the pre-defined properties on params:
  1743. * @param {Function} params.Container.onSecurityAlert
  1744. * Called when an attempted security breach is thwarted. Function is defined
  1745. * as follows: function(container, securityAlert)
  1746. * @param {Function} [params.Container.onConnect]
  1747. * Called when the client connects to the Managed Hub. Function is defined
  1748. * as follows: function(container)
  1749. * @param {Function} [params.Container.onDisconnect]
  1750. * Called when the client disconnects from the Managed Hub. Function is
  1751. * defined as follows: function(container)
  1752. * @param {Object} [params.Container.scope]
  1753. * Whenever one of the Container's callback functions is called, references
  1754. * to "this" in the callback will refer to the scope object. If no scope is
  1755. * provided, default is window.
  1756. * @param {Function} [params.Container.log]
  1757. * Optional logger function. Would be used to log to console.log or
  1758. * equivalent.
  1759. * @param {Object} params.IframeContainer.parent
  1760. * Element ID of DOM element that is to be parent of iframe
  1761. * @param {String} params.IframeContainer.uri
  1762. * Initial Iframe URI (Container will add parameters to this URI)
  1763. * @param {String} params.IframeContainer.tunnelURI
  1764. * URI of the tunnel iframe. Must be from the same origin as the page which
  1765. * instantiates the IframeContainer.
  1766. * @param {Object} [params.IframeContainer.iframeAttrs]
  1767. * Attributes to add to IFRAME DOM entity. For example:
  1768. * { style: { width: "100%",
  1769. * height: "100%" },
  1770. * className: "some_class" }
  1771. * @param {Number} [params.IframeContainer.timeout]
  1772. * Load timeout in milliseconds. If not specified, defaults to 15000. If
  1773. * the client at params.IframeContainer.uri does not establish a connection
  1774. * with this container in the given time, the onSecurityAlert callback is
  1775. * called with a LoadTimeout error code.
  1776. * @param {Function} [params.IframeContainer.seed]
  1777. * A function that returns a string that will be used to seed the
  1778. * pseudo-random number generator, which is used to create the security
  1779. * tokens. An implementation of IframeContainer may choose to ignore this
  1780. * value.
  1781. * @param {Number} [params.IframeContainer.tokenLength]
  1782. * Length of the security tokens used when transmitting messages. If not
  1783. * specified, defaults to 6. An implementation of IframeContainer may choose
  1784. * to ignore this value.
  1785. *
  1786. * @throws {OpenAjax.hub.Error.BadParameters} if required params are not
  1787. * present or null
  1788. * @throws {OpenAjax.hub.Error.Duplicate} if a Container with this clientID
  1789. * already exists in the given Managed Hub
  1790. * @throws {OpenAjax.hub.Error.Disconnected} if hub is not connected
  1791. */
  1792. OpenAjax.hub.IframeContainer = function(hub, clientID, params) {
  1793. if (! hub || ! clientID || ! params ||
  1794. ! params.Container || ! params.Container.onSecurityAlert ||
  1795. ! params.IframeContainer || ! params.IframeContainer.parent ||
  1796. ! params.IframeContainer.uri || ! params.IframeContainer.tunnelURI) {
  1797. throw new Error(OpenAjax.hub.Error.BadParameters);
  1798. }
  1799. this._params = params;
  1800. this._id = clientID;
  1801. if (window.postMessage) {
  1802. this._delegate = new OpenAjax.hub.IframePMContainer(this, hub, clientID, params);
  1803. } else {
  1804. this._delegate = new OpenAjax.hub.IframeFIMContainer(this, hub, clientID, params);
  1805. }
  1806. // Create IFRAME to hold the client
  1807. this._iframe = this._createIframe(params.IframeContainer.parent, this._delegate.getURI(),
  1808. params.IframeContainer.iframeAttrs);
  1809. hub.addContainer(this);
  1810. }
  1811. /*** OpenAjax.hub.Container interface implementation ***/
  1812. OpenAjax.hub.IframeContainer.prototype.getHub = function() {
  1813. return this._delegate.getHub();
  1814. }
  1815. OpenAjax.hub.IframeContainer.prototype.sendToClient = function(topic, data, subscriptionID) {
  1816. this._delegate.sendToClient(topic, data, subscriptionID);
  1817. }
  1818. OpenAjax.hub.IframeContainer.prototype.remove = function() {
  1819. this._delegate.remove();
  1820. this._iframe.parentNode.removeChild(this._iframe);
  1821. }
  1822. OpenAjax.hub.IframeContainer.prototype.isConnected = function() {
  1823. return this._delegate.isConnected();
  1824. }
  1825. OpenAjax.hub.IframeContainer.prototype.getClientID = function() {
  1826. return this._id;
  1827. }
  1828. OpenAjax.hub.IframeContainer.prototype.getPartnerOrigin = function() {
  1829. return this._delegate.getPartnerOrigin();
  1830. }
  1831. OpenAjax.hub.IframeContainer.prototype.getParameters = function() {
  1832. return this._params;
  1833. }
  1834. /**
  1835. * Get the iframe associated with this iframe container
  1836. *
  1837. * This function returns the iframe associated with an IframeContainer,
  1838. * allowing the Manager Application to change its size, styles, scrollbars, etc.
  1839. *
  1840. * CAUTION: The iframe is owned exclusively by the IframeContainer. The Manager
  1841. * Application MUST NOT destroy the iframe directly. Also, if the iframe is
  1842. * hidden and disconnected, the Manager Application SHOULD NOT attempt to make
  1843. * it visible. The Container SHOULD automatically hide the iframe when it is
  1844. * disconnected; to make it visible would introduce security risks.
  1845. *
  1846. * @returns iframeElement
  1847. * @type {Object}
  1848. */
  1849. OpenAjax.hub.IframeContainer.prototype.getIframe = function() {
  1850. return this._iframe;
  1851. }
  1852. /*** Helper Functions ***/
  1853. /**
  1854. * Return function that runs in given scope.
  1855. *
  1856. * @param {Object} toWhom scope in which to run given function
  1857. * @param {Function} callback function to run in given scope
  1858. * @returns {Function}
  1859. */
  1860. OpenAjax.hub.IframeContainer.bind = function(toWhom, callback) {
  1861. var __method = callback;
  1862. return function() {
  1863. return __method.apply(toWhom, arguments);
  1864. }
  1865. }
  1866. /*** Private Functions ***/
  1867. OpenAjax.hub.IframeContainer.prototype._createIframe = function(parent, src, attrs) {
  1868. var iframe = document.createElement("iframe");
  1869. // Add iframe attributes
  1870. if (attrs) {
  1871. for (var attr in attrs) {
  1872. if (attr == "style") {
  1873. for (var style in attrs.style) {
  1874. iframe.style[ style ] = attrs.style[ style ];
  1875. }
  1876. } else {
  1877. iframe[ attr ] = attrs[ attr ];
  1878. }
  1879. }
  1880. }
  1881. // initially hide IFRAME content, in order to lessen frame phishing impact
  1882. iframe.style.visibility = "hidden";
  1883. // (1) Setting the iframe src after it has been added to the DOM can cause
  1884. // problems in IE6/7. Specifically, if the code is being executed on a page
  1885. // that was served through HTTPS, then IE6/7 will see an iframe with a blank
  1886. // src as a non-secure item and display a dialog warning the user that "this
  1887. // page contains both secure and nonsecure items." To prevent that, we
  1888. // first set the src to a dummy value, then add the iframe to the DOM, then
  1889. // set the real src value.
  1890. // (2) Trying to fix the above issue by setting the real src before adding
  1891. // the iframe to the DOM breaks Firefox 3.x. For some reason, when
  1892. // reloading a page that has instantiated an IframeContainer, Firefox will
  1893. // load a previously cached version of the iframe content, whose source
  1894. // contains stale URL query params or hash. This results in errors in the
  1895. // Hub code, which is expected different values.
  1896. iframe.src = 'javascript:"<html></html>"';
  1897. parent.appendChild(iframe);
  1898. iframe.src = src;
  1899. return iframe;
  1900. }
  1901. //------------------------------------------------------------------------------
  1902. /**
  1903. * Create a new IframeHubClient.
  1904. * @constructor
  1905. * @extends OpenAjax.hub.HubClient
  1906. *
  1907. * @param {Object} params
  1908. * Once the constructor is called, the params object belongs to the
  1909. * HubClient. The caller MUST not modify it.
  1910. * The following are the pre-defined properties on params:
  1911. * @param {Function} params.HubClient.onSecurityAlert
  1912. * Called when an attempted security breach is thwarted
  1913. * @param {Object} [params.HubClient.scope]
  1914. * Whenever one of the HubClient's callback functions is called,
  1915. * references to "this" in the callback will refer to the scope object.
  1916. * If not provided, the default is window.
  1917. * @param {Function} [params.IframeHubClient.seed]
  1918. * A function that returns a string that will be used to seed the
  1919. * pseudo-random number generator, which is used to create the security
  1920. * tokens. An implementation of IframeHubClient may choose to ignore
  1921. * this value.
  1922. * @param {Number} [params.IframeHubClient.tokenLength]
  1923. * Length of the security tokens used when transmitting messages. If
  1924. * not specified, defaults to 6. An implementation of IframeHubClient
  1925. * may choose to ignore this value.
  1926. *
  1927. * @throws {OpenAjax.hub.Error.BadParameters} if any of the required
  1928. * parameters is missing, or if a parameter value is invalid in
  1929. * some way.
  1930. */
  1931. OpenAjax.hub.IframeHubClient = function(params) {
  1932. if (! params || ! params.HubClient || ! params.HubClient.onSecurityAlert) {
  1933. throw new Error(OpenAjax.hub.Error.BadParameters);
  1934. }
  1935. this._params = params;
  1936. if (window.postMessage) {
  1937. this._delegate = new OpenAjax.hub.IframePMHubClient(this, params);
  1938. } else {
  1939. this._delegate = new OpenAjax.hub.IframeFIMHubClient(this, params);
  1940. }
  1941. }
  1942. /*** OpenAjax.hub.HubClient interface implementation ***/
  1943. OpenAjax.hub.IframeHubClient.prototype.connect = function(onComplete, scope) {
  1944. scope = scope || window;
  1945. if (this.isConnected()) {
  1946. throw new Error(OpenAjax.hub.Error.Duplicate);
  1947. }
  1948. this._delegate.connect(onComplete, scope);
  1949. }
  1950. OpenAjax.hub.IframeHubClient.prototype.disconnect = function(onComplete, scope) {
  1951. scope = scope || window;
  1952. if (! this.isConnected()) {
  1953. throw new Error(OpenAjax.hub.Error.Disconnected);
  1954. }
  1955. this._delegate.disconnect(onComplete, scope);
  1956. }
  1957. OpenAjax.hub.IframeHubClient.prototype.getPartnerOrigin = function() {
  1958. return this._delegate.getPartnerOrigin();
  1959. }
  1960. OpenAjax.hub.IframeHubClient.prototype.getClientID = function() {
  1961. return this._delegate.getClientID();
  1962. }
  1963. /*** OpenAjax.hub.Hub interface implementation ***/
  1964. OpenAjax.hub.IframeHubClient.prototype.subscribe = function(topic, onData, scope, onComplete, subscriberData) {
  1965. this._assertConn();
  1966. this._assertSubTopic(topic);
  1967. if (! onData) {
  1968. throw new Error(OpenAjax.hub.Error.BadParameters);
  1969. }
  1970. scope = scope || window;
  1971. return this._delegate.subscribe(topic, onData, scope, onComplete, subscriberData);
  1972. }
  1973. OpenAjax.hub.IframeHubClient.prototype.publish = function(topic, data) {
  1974. this._assertConn();
  1975. this._assertPubTopic(topic);
  1976. this._delegate.publish(topic, data);
  1977. }
  1978. OpenAjax.hub.IframeHubClient.prototype.unsubscribe = function(subscriptionID, onComplete, scope) {
  1979. this._assertConn();
  1980. if (typeof subscriptionID === "undefined" || subscriptionID == null) {
  1981. throw new Error(OpenAjax.hub.Error.BadParameters);
  1982. }
  1983. scope = scope || window;
  1984. this._delegate.unsubscribe(subscriptionID, onComplete, scope);
  1985. }
  1986. OpenAjax.hub.IframeHubClient.prototype.isConnected = function() {
  1987. return this._delegate.isConnected();
  1988. }
  1989. OpenAjax.hub.IframeHubClient.prototype.getScope = function() {
  1990. return this._delegate.getScope();
  1991. }
  1992. OpenAjax.hub.IframeHubClient.prototype.getSubscriberData = function(subscriptionID) {
  1993. this._assertConn();
  1994. return this._delegate.getSubscriberData(subscriptionID);
  1995. }
  1996. OpenAjax.hub.IframeHubClient.prototype.getSubscriberScope = function(subscriptionID) {
  1997. this._assertConn();
  1998. return this._delegate.getSubscriberScope(subscriptionID);
  1999. }
  2000. OpenAjax.hub.IframeHubClient.prototype.getParameters = function() {
  2001. return this._params;
  2002. }
  2003. /*** Private Functions ***/
  2004. OpenAjax.hub.IframeHubClient.prototype._assertConn = function() {
  2005. if (! this.isConnected()) {
  2006. throw new Error(OpenAjax.hub.Error.Disconnected);
  2007. }
  2008. }
  2009. OpenAjax.hub.IframeHubClient.prototype._assertSubTopic = function(topic) {
  2010. if (! topic) {
  2011. throw new Error(OpenAjax.hub.Error.BadParameters);
  2012. }
  2013. var path = topic.split(".");
  2014. var len = path.length;
  2015. for (var i = 0; i < len; i++) {
  2016. var p = path[i];
  2017. if ((p == "") ||
  2018. ((p.indexOf("*") != -1) && (p != "*") && (p != "**"))) {
  2019. throw new Error(OpenAjax.hub.Error.BadParameters);
  2020. }
  2021. if ((p == "**") && (i < len - 1)) {
  2022. throw new Error(OpenAjax.hub.Error.BadParameters);
  2023. }
  2024. }
  2025. }
  2026. OpenAjax.hub.IframeHubClient.prototype._assertPubTopic = function(topic) {
  2027. if ((topic == null) || (topic == "") || (topic.indexOf("*") != -1) ||
  2028. (topic.indexOf("..") != -1) || (topic.charAt(0) == ".") ||
  2029. (topic.charAt(topic.length - 1) == ".")) {
  2030. throw new Error(OpenAjax.hub.Error.BadParameters);
  2031. }
  2032. }
  2033. /******************************************************************************
  2034. * PostMessage Iframe Container
  2035. *
  2036. * Implementation of the Iframe Container which uses window.postMessage()
  2037. * for communicating between an iframe and its parent.
  2038. ******************************************************************************/
  2039. OpenAjax.hub.IframePMContainer = function(container, hub, clientID, params) {
  2040. this._container = container;
  2041. this._hub = hub;
  2042. this._id = clientID;
  2043. this._onSecurityAlert = params.Container.onSecurityAlert;
  2044. this._onConnect = params.Container.onConnect ? params.Container.onConnect : null;
  2045. this._onDisconnect = params.Container.onDisconnect ? params.Container.onDisconnect : null;
  2046. this._scope = params.Container.scope || window;
  2047. this._uri = params.IframeContainer.uri;
  2048. this._tunnelURI = params.IframeContainer.tunnelURI;
  2049. this._timeout = params.IframeContainer.timeout || 15000;
  2050. if (params.Container.log) {
  2051. var scope = this._scope;
  2052. var logfunc = params.Container.log;
  2053. this._log = function(msg) {
  2054. logfunc.call(scope, "IframeContainer::" + clientID + ": " + msg);
  2055. };
  2056. this._doLog = true; // HW Optimization
  2057. } else {
  2058. this._log = function() {
  2059. };
  2060. }
  2061. this._securityToken = this._generateSecurityToken(params);
  2062. this._connected = false;
  2063. this._subs = {};
  2064. // test if the postMessage impl of this browser is synchronous
  2065. if (typeof OpenAjax.hub.IframePMContainer._pmCapabilities === "undefined") {
  2066. this._testPostMessage();
  2067. }
  2068. // if postMessage is synchronous, wrap in a setTimeout
  2069. if (OpenAjax.hub.IframePMContainer._pmCapabilities.indexOf("s") == -1) {
  2070. this._postMessage = function(win, msg, origin) {
  2071. win.postMessage(msg, origin);
  2072. }
  2073. } else {
  2074. this._postMessage = function(win, msg, origin) {
  2075. setTimeout(
  2076. function() {
  2077. win.postMessage(msg, origin);
  2078. },
  2079. 0
  2080. );
  2081. }
  2082. }
  2083. // register this container with the singleton message listener
  2084. if (! OpenAjax.hub.IframePMContainer._pmListener) {
  2085. OpenAjax.hub.IframePMContainer._pmListener =
  2086. new OpenAjax.hub.IframePMContainer.PMListener();
  2087. }
  2088. // the 'internal ID' is guaranteed to be unique within the page, not just
  2089. // the ManagedHub instance
  2090. this._internalID = OpenAjax.hub.IframePMContainer._pmListener.addContainer(this);
  2091. this._startLoadTimer();
  2092. }
  2093. //communications protocol identifier
  2094. OpenAjax.hub.IframePMContainer.protocolID = "openajax-2.0";
  2095. //Singleton message listener
  2096. OpenAjax.hub.IframePMContainer._pmListener = null;
  2097. OpenAjax.hub.IframePMContainer.prototype.getHub = function() {
  2098. return this._hub;
  2099. };
  2100. OpenAjax.hub.IframePMContainer.prototype.sendToClient = function(topic, data, subscriptionID) {
  2101. this._sendMessage("pub", { t: topic, d: data, s: subscriptionID });
  2102. }
  2103. OpenAjax.hub.IframePMContainer.prototype.remove = function() {
  2104. this._disconnect();
  2105. OpenAjax.hub.IframePMContainer._pmListener.removeContainer(this._internalID);
  2106. clearTimeout(this._loadTimer);
  2107. }
  2108. OpenAjax.hub.IframePMContainer.prototype.isConnected = function() {
  2109. return this._connected;
  2110. }
  2111. OpenAjax.hub.IframePMContainer.prototype.getPartnerOrigin = function() {
  2112. if (this._connected) {
  2113. // remove port, if it is present
  2114. // HW Optimization
  2115. return this._partnerOriginNoPort;
  2116. // return new RegExp( "^([a-zA-Z]+://[^:]+).*" ).exec( this._partnerOrigin )[1];
  2117. }
  2118. return null;
  2119. }
  2120. OpenAjax.hub.IframePMContainer.prototype.receiveMessage = function(event, msg) {
  2121. // check that security token and client window origin for incoming message
  2122. // are what we expect
  2123. if (msg.t != this._securityToken ||
  2124. ( typeof this._partnerOrigin != "undefined" &&
  2125. ! OpenAjax.hub.IframePMContainer.originMatches(this, event))) {
  2126. // security error -- incoming message is not valid; ignore
  2127. this._invokeSecurityAlert(OpenAjax.hub.SecurityAlert.ForgedMsg);
  2128. return;
  2129. }
  2130. if (this._doLog) { // HW Optimization
  2131. this._log("received message: [" + event.data + "]");
  2132. }
  2133. switch (msg.m) {
  2134. // subscribe
  2135. case "sub":
  2136. var errCode = ""; // empty string is success
  2137. try {
  2138. this._subs[ msg.p.s ] = this._hub.subscribeForClient(this._container, msg.p.t, msg.p.s);
  2139. } catch(e) {
  2140. errCode = e.message;
  2141. }
  2142. this._sendMessage("sub_ack", { s: msg.p.s, e: errCode });
  2143. break;
  2144. // publish
  2145. case "pub":
  2146. this._hub.publishForClient(this._container, msg.p.t, msg.p.d);
  2147. break;
  2148. // unsubscribe
  2149. case "uns":
  2150. var handle = this._subs[ msg.p.s ];
  2151. this._hub.unsubscribeForClient(this._container, handle);
  2152. delete this._subs[ msg.p.s ];
  2153. this._sendMessage("uns_ack", msg.p.s);
  2154. break;
  2155. // connect is handled elsewhere -- see IframePMContainer.prototype.connect
  2156. // disconnect
  2157. case "dis":
  2158. this._startLoadTimer();
  2159. this._disconnect();
  2160. this._sendMessage("dis_ack", null);
  2161. if (this._onDisconnect) {
  2162. try {
  2163. this._onDisconnect.call(this._scope, this._container);
  2164. } catch(e) {
  2165. OpenAjax.hub._debugger();
  2166. this._log("caught error from onDisconnect callback to constructor: " + e.message);
  2167. }
  2168. }
  2169. break;
  2170. }
  2171. }
  2172. /**
  2173. * Complete connection from HubClient to this Container.
  2174. *
  2175. * @param {String} origin IframePMHubClient's window's origin
  2176. * @param {String} securityToken Security token originally sent by Container
  2177. * @param {Object} tunnelWindow window object reference of tunnel window
  2178. */
  2179. OpenAjax.hub.IframePMContainer.prototype.connect = function(origin, securityToken, tunnelWindow) {
  2180. this._log("client connecting to container " + this._id +
  2181. " :: origin = " + origin + " :: securityToken = " + securityToken);
  2182. // check that security token is what we expect
  2183. if (securityToken != this._securityToken) {
  2184. // security error -- incoming message is not valid
  2185. this._invokeSecurityAlert(OpenAjax.hub.SecurityAlert.ForgedMsg);
  2186. return;
  2187. }
  2188. // set unload handler on tunnel window
  2189. var that = this;
  2190. tunnelWindow.onunload = function() {
  2191. if (that.isConnected()) {
  2192. // Use a timer to delay the phishing message. This makes sure that
  2193. // page navigation does not cause phishing errors.
  2194. // Setting it to 1 ms is enough for it not to be triggered on
  2195. // regular page navigations.
  2196. setTimeout(
  2197. function() {
  2198. that._invokeSecurityAlert(OpenAjax.hub.SecurityAlert.FramePhish);
  2199. }, 1
  2200. );
  2201. }
  2202. };
  2203. clearTimeout(this._loadTimer);
  2204. this._iframe = this._container.getIframe();
  2205. this._iframe.style.visibility = "visible";
  2206. this._partnerOrigin = origin;
  2207. // HW Optimization
  2208. this._partnerOriginNoPort = new RegExp("^([a-zA-Z]+://[^:]+).*").exec(this._partnerOrigin)[1]; // HW Optimization
  2209. // if "message" event doesn't support "origin" property, then save hostname
  2210. // (domain) also
  2211. if (OpenAjax.hub.IframePMContainer._pmCapabilities.indexOf("d") != -1) {
  2212. this._partnerDomain = new RegExp("^.+://([^:]+).*").exec(this._partnerOrigin)[1];
  2213. }
  2214. this._sendMessage("con_ack", null);
  2215. this._connected = true;
  2216. if (this._onConnect) {
  2217. try {
  2218. this._onConnect.call(this._scope, this._container);
  2219. } catch(e) {
  2220. OpenAjax.hub._debugger();
  2221. this._log("caught error from onConnect callback to constructor: " + e.message);
  2222. }
  2223. }
  2224. }
  2225. OpenAjax.hub.IframePMContainer.prototype.getURI = function() {
  2226. // add the client ID and a security token as URL query params when loading
  2227. // the client iframe
  2228. var paramStr =
  2229. "oahpv=" + encodeURIComponent(OpenAjax.hub.IframePMContainer.protocolID) +
  2230. "&oahi=" + encodeURIComponent(this._internalID) +
  2231. "&oaht=" + this._securityToken +
  2232. "&oahu=" + encodeURIComponent(this._tunnelURI) +
  2233. "&oahpm=" + OpenAjax.hub.IframePMContainer._pmCapabilities;
  2234. if (this._id !== this._internalID) {
  2235. paramStr += "&oahj=" + this._internalID;
  2236. }
  2237. paramStr += OpenAjax.hub.enableDebug ? "&oahd=true" : ""; // REMOVE ON BUILD
  2238. var parts = this._uri.split("#");
  2239. parts[0] = parts[0] + ((parts[0].indexOf("?") != -1) ? "&" : "?") + paramStr;
  2240. if (parts.length == 1) {
  2241. return parts[0];
  2242. }
  2243. return parts[0] + "#" + parts[1];
  2244. }
  2245. /*** Helper Functions ***/
  2246. OpenAjax.hub.IframePMContainer.originMatches = function(obj, event) {
  2247. if (event.origin) {
  2248. return event.origin == obj._partnerOrigin;
  2249. } else {
  2250. return event.domain == obj._partnerDomain;
  2251. }
  2252. }
  2253. /*** Private Function ***/
  2254. OpenAjax.hub.IframePMContainer.prototype._generateSecurityToken = function(params) {
  2255. if (! OpenAjax.hub.IframePMContainer._prng) {
  2256. // create pseudo-random number generator with a default seed
  2257. var seed = new Date().getTime() + Math.random() + document.cookie;
  2258. OpenAjax.hub.IframePMContainer._prng = smash.crypto.newPRNG(seed);
  2259. }
  2260. if (params.IframeContainer.seed) {
  2261. try {
  2262. var extraSeed = params.IframeContainer.seed.call(this._scope);
  2263. OpenAjax.hub.IframePMContainer._prng.addSeed(extraSeed);
  2264. } catch(e) {
  2265. OpenAjax.hub._debugger();
  2266. this._log("caught error from 'seed' callback: " + e.message);
  2267. }
  2268. }
  2269. var tokenLength = params.IframeContainer.tokenLength || 6;
  2270. return OpenAjax.hub.IframePMContainer._prng.nextRandomB64Str(tokenLength);
  2271. }
  2272. /**
  2273. * Some browsers (IE, Opera) have an implementation of postMessage that is
  2274. * synchronous, although HTML5 specifies that it should be asynchronous. In
  2275. * order to make all browsers behave consistently, we run a small test to detect
  2276. * if postMessage is asynchronous or not. If not, we wrap calls to postMessage
  2277. * in a setTimeout with a timeout of 0.
  2278. * Also, Opera's "message" event does not have an "origin" property (at least,
  2279. * it doesn't in version 9.64; presumably, it will in version 10). If
  2280. * event.origin does not exist, use event.domain. The other difference is that
  2281. * while event.origin looks like <scheme>://<hostname>:<port>, event.domain
  2282. * consists only of <hostname>.
  2283. */
  2284. OpenAjax.hub.IframePMContainer.prototype._testPostMessage = function() {
  2285. // String identifier that specifies whether this browser's postMessage
  2286. // implementation differs from the spec:
  2287. // contains "s" - postMessage is synchronous
  2288. // contains "d" - "message" event does not have an "origin" property;
  2289. // the code looks for the "domain" property instead
  2290. OpenAjax.hub.IframePMContainer._pmCapabilities = "";
  2291. var hit = false;
  2292. function receiveMsg(event) {
  2293. if (event.data == "postmessage.test") {
  2294. hit = true;
  2295. if (typeof event.origin === "undefined") {
  2296. OpenAjax.hub.IframePMContainer._pmCapabilities += "d";
  2297. }
  2298. }
  2299. }
  2300. if (window.addEventListener) {
  2301. window.addEventListener("message", receiveMsg, false);
  2302. } else if (window.attachEvent) {
  2303. window.attachEvent("onmessage", receiveMsg);
  2304. }
  2305. window.postMessage("postmessage.test", "*");
  2306. // if 'hit' is true here, then postMessage is synchronous
  2307. if (hit) {
  2308. OpenAjax.hub.IframePMContainer._pmCapabilities += "s";
  2309. }
  2310. if (window.removeEventListener) {
  2311. window.removeEventListener("message", receiveMsg, false);
  2312. } else {
  2313. window.detachEvent("onmessage", receiveMsg);
  2314. }
  2315. }
  2316. OpenAjax.hub.IframePMContainer.prototype._startLoadTimer = function() {
  2317. var that = this;
  2318. this._loadTimer = setTimeout(
  2319. function() {
  2320. // don't accept any messages from client
  2321. OpenAjax.hub.IframePMContainer._pmListener.removeContainer(that._internalID);
  2322. // alert the security alert callback
  2323. that._invokeSecurityAlert(OpenAjax.hub.SecurityAlert.LoadTimeout);
  2324. },
  2325. this._timeout
  2326. );
  2327. }
  2328. /**
  2329. * Send a string message to the associated hub client.
  2330. *
  2331. * The message is a JSON representation of the following object:
  2332. * {
  2333. * m: message type,
  2334. * i: client id,
  2335. * t: security token,
  2336. * p: payload (depends on message type)
  2337. * }
  2338. *
  2339. * The payload for each message type is as follows:
  2340. * TYPE DESCRIPTION PAYLOAD
  2341. * "con_ack" connect acknowledgment N/A
  2342. * "dis_ack" disconnect acknowledgment N/A
  2343. * "sub_ack" subscribe acknowledgment { s: subscription id, e: error code (empty string if no error) }
  2344. * "uns_ack" unsubscribe acknowledgment { s: subscription id }
  2345. * "pub" publish (i.e. sendToClient()) { t: topic, d: data, s: subscription id }
  2346. */
  2347. OpenAjax.hub.IframePMContainer.prototype._sendMessage = function(type, payload) {
  2348. var msg = JSON.stringify({
  2349. m: type,
  2350. i: this._internalID,
  2351. t: this._securityToken,
  2352. p: payload
  2353. });
  2354. this._postMessage(this._iframe.contentWindow, msg, this._partnerOrigin);
  2355. }
  2356. OpenAjax.hub.IframePMContainer.prototype._disconnect = function() {
  2357. if (this._connected) {
  2358. this._connected = false;
  2359. this._iframe.style.visibility = "hidden";
  2360. // unsubscribe from all subs
  2361. for (var sub in this._subs) {
  2362. this._hub.unsubscribeForClient(this._container, this._subs[ sub ]);
  2363. }
  2364. this._subs = {};
  2365. }
  2366. }
  2367. OpenAjax.hub.IframePMContainer.prototype._invokeSecurityAlert = function(errorMsg) {
  2368. try {
  2369. this._onSecurityAlert.call(this._scope, this._container, errorMsg);
  2370. } catch(e) {
  2371. OpenAjax.hub._debugger();
  2372. this._log("caught error from onSecurityAlert callback to constructor: " + e.message);
  2373. }
  2374. }
  2375. //------------------------------------------------------------------------------
  2376. OpenAjax.hub.IframePMContainer.PMListener = function() {
  2377. this._containers = {};
  2378. if (window.addEventListener) {
  2379. window.addEventListener("message",
  2380. OpenAjax.hub.IframeContainer.bind(this, this._receiveMessage), false);
  2381. } else if (window.attachEvent) {
  2382. window.attachEvent("onmessage",
  2383. OpenAjax.hub.IframeContainer.bind(this, this._receiveMessage));
  2384. }
  2385. }
  2386. /**
  2387. * Add an IframePMContainer to listen for messages. Returns an ID for the given
  2388. * container that is unique within the PAGE, not just the ManagedHub instance.
  2389. */
  2390. OpenAjax.hub.IframePMContainer.PMListener.prototype.addContainer = function(container) {
  2391. var id = container._id;
  2392. while (this._containers[ id ]) {
  2393. // a client with the specified ID already exists on this page;
  2394. // create a unique ID
  2395. id = ((0x7fff * Math.random()) | 0).toString(16) + "_" + id;
  2396. }
  2397. this._containers[ id ] = container;
  2398. return id;
  2399. }
  2400. OpenAjax.hub.IframePMContainer.PMListener.prototype.removeContainer = function(internalID) {
  2401. delete this._containers[ internalID ];
  2402. // XXX TODO If no more postMessage containers, remove listener?
  2403. }
  2404. /**
  2405. * Complete connection between HubClient and Container identified by "id". This
  2406. * function is only called by the tunnel window.
  2407. */
  2408. OpenAjax.hub.IframePMContainer.PMListener.prototype.connectFromTunnel = function(internalID, origin, securityToken, tunnelWindow) {
  2409. if (this._containers[ internalID ]) {
  2410. this._containers[ internalID ].connect(origin, securityToken, tunnelWindow);
  2411. }
  2412. }
  2413. OpenAjax.hub.IframePMContainer.PMListener.prototype._receiveMessage = function(event) {
  2414. // If the received message isn't JSON parseable or if the resulting
  2415. // object doesn't have the structure we expect, then just return.
  2416. try {
  2417. var msg = JSON.parse(event.data);
  2418. } catch(e) {
  2419. return;
  2420. }
  2421. if (! this._verifyMsg(msg)) {
  2422. return;
  2423. }
  2424. if (this._containers[ msg.i ]) {
  2425. var container = this._containers[ msg.i ].receiveMessage(event, msg);
  2426. }
  2427. }
  2428. OpenAjax.hub.IframePMContainer.PMListener.prototype._verifyMsg = function(msg) {
  2429. return typeof msg.m == "string" && typeof msg.i == "string" &&
  2430. "t" in msg && "p" in msg;
  2431. }
  2432. //------------------------------------------------------------------------------
  2433. OpenAjax.hub.IframePMHubClient = function(client, params) {
  2434. // check communications protocol ID
  2435. this._checkProtocolID();
  2436. this._client = client;
  2437. this._onSecurityAlert = params.HubClient.onSecurityAlert;
  2438. this._scope = params.HubClient.scope || window;
  2439. this._id = OpenAjax.hub.IframePMHubClient.queryURLParam("oahi");
  2440. this._internalID = OpenAjax.hub.IframePMHubClient.queryURLParam("oahj") || this._id;
  2441. this._securityToken = OpenAjax.hub.IframePMHubClient.queryURLParam("oaht");
  2442. this._tunnelURI = OpenAjax.hub.IframePMHubClient.queryURLParam("oahu");
  2443. OpenAjax.hub.IframePMContainer._pmCapabilities = OpenAjax.hub.IframePMHubClient.queryURLParam("oahpm");
  2444. // if any of the URL params are missing, throw WrongProtocol error
  2445. if (! this._id || ! this._securityToken || ! this._tunnelURI) {
  2446. throw new Error(OpenAjax.hub.Error.WrongProtocol);
  2447. }
  2448. if (OpenAjax.hub.IframePMHubClient.queryURLParam("oahd")) OpenAjax.hub.enableDebug = true; // REMOVE ON BUILD
  2449. this._partnerOrigin = new RegExp("^([a-zA-Z]+://[^/?#]+).*").exec(this._tunnelURI)[1];
  2450. this._partnerOriginNoPort = new RegExp("^([a-zA-Z]+://[^:]+).*").exec(this._partnerOrigin)[1]; // HW Optimization
  2451. // if "message" event doesn't support "origin" property, then save hostname
  2452. // (domain) also
  2453. if (OpenAjax.hub.IframePMContainer._pmCapabilities.indexOf("d") != -1) {
  2454. this._partnerDomain = new RegExp("^.+://([^:]+).*").exec(this._partnerOrigin)[1];
  2455. }
  2456. if (params.HubClient.log) {
  2457. var id = this._id;
  2458. var scope = this._scope;
  2459. var logfunc = params.HubClient.log;
  2460. this._log = function(msg) {
  2461. logfunc.call(scope, "IframeHubClient::" + id + ": " + msg);
  2462. };
  2463. this._doLog = true; // HW Optimization
  2464. } else {
  2465. this._log = function() {
  2466. };
  2467. }
  2468. this._connected = false;
  2469. this._subs = {};
  2470. this._subIndex = 1; // HW FIX
  2471. // if postMessage is synchronous, wrap in a setTimeout
  2472. if (OpenAjax.hub.IframePMContainer._pmCapabilities.indexOf("s") == -1) {
  2473. this._postMessage = function(win, msg, origin) {
  2474. win.postMessage(msg, origin);
  2475. }
  2476. } else {
  2477. this._postMessage = function(win, msg, origin) {
  2478. setTimeout(
  2479. function() {
  2480. win.postMessage(msg, origin);
  2481. },
  2482. 0
  2483. );
  2484. }
  2485. }
  2486. }
  2487. //communications protocol identifier
  2488. OpenAjax.hub.IframePMHubClient.protocolID = "openajax-2.0";
  2489. /*** OpenAjax.hub.HubClient interface implementation ***/
  2490. OpenAjax.hub.IframePMHubClient.prototype.connect = function(onComplete, scope) {
  2491. if (onComplete) {
  2492. this._connectOnComplete = { cb: onComplete, sc: scope };
  2493. }
  2494. // start listening for messages
  2495. this._msgListener = OpenAjax.hub.IframeContainer.bind(this, this._receiveMessage);
  2496. if (window.addEventListener) {
  2497. window.addEventListener("message", this._msgListener, false);
  2498. } else if (window.attachEvent) {
  2499. window.attachEvent("onmessage", this._msgListener);
  2500. }
  2501. // create tunnel iframe, which will finish connection to container
  2502. var origin = window.location.protocol + "//" + window.location.host;
  2503. var iframe = document.createElement("iframe");
  2504. document.body.appendChild(iframe);
  2505. iframe.src = this._tunnelURI +
  2506. (this._tunnelURI.indexOf("?") == -1 ? "?" : "&") +
  2507. "oahj=" + encodeURIComponent(this._internalID) +
  2508. "&oaht=" + this._securityToken +
  2509. "&oaho=" + encodeURIComponent(origin);
  2510. iframe.style.position = "absolute";
  2511. iframe.style.left = iframe.style.top = "-10px";
  2512. iframe.style.height = iframe.style.width = "1px";
  2513. iframe.style.visibility = "hidden";
  2514. this._tunnelIframe = iframe;
  2515. }
  2516. OpenAjax.hub.IframePMHubClient.prototype.disconnect = function(onComplete, scope) {
  2517. this._connected = false;
  2518. if (onComplete) {
  2519. this._disconnectOnComplete = { cb: onComplete, sc: scope };
  2520. }
  2521. this._sendMessage("dis", null);
  2522. }
  2523. OpenAjax.hub.IframePMHubClient.prototype.getPartnerOrigin = function() {
  2524. if (this._connected) {
  2525. // remove port, if it is present
  2526. return new RegExp("^([a-zA-Z]+://[^:]+).*").exec(this._partnerOrigin)[1];
  2527. }
  2528. return null;
  2529. }
  2530. OpenAjax.hub.IframePMHubClient.prototype.getClientID = function() {
  2531. return this._id;
  2532. }
  2533. /*** OpenAjax.hub.Hub interface implementation ***/
  2534. OpenAjax.hub.IframePMHubClient.prototype.subscribe = function(topic, onData, scope, onComplete, subscriberData) {
  2535. var subID = "" + this._subIndex++;
  2536. this._subs[ subID ] = { cb: onData, sc: scope, d: subscriberData, oc: onComplete };
  2537. this._sendMessage("sub", { t: topic, s: subID });
  2538. return subID;
  2539. }
  2540. OpenAjax.hub.IframePMHubClient.prototype.publish = function(topic, data) {
  2541. this._sendMessage("pub", { t: topic, d: data });
  2542. }
  2543. OpenAjax.hub.IframePMHubClient.prototype.unsubscribe = function(subID, onComplete, scope) {
  2544. // if no such subID, or in process of unsubscribing given ID, throw error
  2545. if (! this._subs[ subID ] || this._subs[ subID ].uns) {
  2546. throw new Error(OpenAjax.hub.Error.NoSubscription);
  2547. }
  2548. this._subs[ subID ].uns = { cb: onComplete, sc: scope };
  2549. this._sendMessage("uns", { s: subID });
  2550. }
  2551. OpenAjax.hub.IframePMHubClient.prototype.isConnected = function() {
  2552. return this._connected;
  2553. }
  2554. OpenAjax.hub.IframePMHubClient.prototype.getScope = function() {
  2555. return this._scope;
  2556. }
  2557. OpenAjax.hub.IframePMHubClient.prototype.getSubscriberData = function(subID) {
  2558. var sub = this._subs[ subID ];
  2559. if (sub) {
  2560. return sub.d;
  2561. }
  2562. throw new Error(OpenAjax.hub.Error.NoSubscription);
  2563. }
  2564. OpenAjax.hub.IframePMHubClient.prototype.getSubscriberScope = function(subID) {
  2565. var sub = this._subs[ subID ];
  2566. if (sub) {
  2567. return sub.sc;
  2568. }
  2569. throw new Error(OpenAjax.hub.Error.NoSubscription);
  2570. }
  2571. /*** Helper Functions ***/
  2572. OpenAjax.hub.IframePMHubClient.queryURLParam = function(param) {
  2573. var result = new RegExp("[\\?&]" + param + "=([^&#]*)").exec(window.location.search);
  2574. if (result) {
  2575. return decodeURIComponent(result[1].replace(/\+/g, "%20"));
  2576. }
  2577. return null;
  2578. };
  2579. /*** Private Functions ***/
  2580. OpenAjax.hub.IframePMHubClient.prototype._checkProtocolID = function() {
  2581. var partnerProtocolID = OpenAjax.hub.IframePMHubClient.queryURLParam("oahpv");
  2582. if (partnerProtocolID != OpenAjax.hub.IframePMHubClient.protocolID) {
  2583. throw new Error(OpenAjax.hub.Error.WrongProtocol);
  2584. }
  2585. }
  2586. OpenAjax.hub.IframePMHubClient.prototype._receiveMessage = function(event) {
  2587. // If the received message isn't JSON parseable or if the resulting
  2588. // object doesn't have the structure we expect, then just return. This
  2589. // message might belong to some other code on the page that is also using
  2590. // postMessage for communication.
  2591. try {
  2592. var msg = JSON.parse(event.data);
  2593. } catch(e) {
  2594. return;
  2595. }
  2596. if (! this._verifyMsg(msg)) {
  2597. return;
  2598. }
  2599. // check that security token and window source for incoming message
  2600. // are what we expect
  2601. if (msg.i != this._internalID) {
  2602. // this message might belong to an IframeContainer on this page
  2603. return;
  2604. } else if (! OpenAjax.hub.IframePMContainer.originMatches(this, event) ||
  2605. msg.t != this._securityToken) {
  2606. // security error -- incoming message is not valid
  2607. try {
  2608. this._onSecurityAlert.call(this._scope, this._client,
  2609. OpenAjax.hub.SecurityAlert.ForgedMsg);
  2610. } catch(e) {
  2611. OpenAjax.hub._debugger();
  2612. this._log("caught error from onSecurityAlert callback to constructor: " + e.message);
  2613. }
  2614. return;
  2615. }
  2616. if (this._doLog) { // HW Optimization
  2617. this._log("received message: [" + event.data + "]");
  2618. }
  2619. switch (msg.m) {
  2620. // subscribe acknowledgement
  2621. case "sub_ack":
  2622. var subID = msg.p.s;
  2623. var onComplete = this._subs[ subID ].oc;
  2624. if (onComplete) {
  2625. try {
  2626. delete this._subs[ subID ].oc;
  2627. var scope = this._subs[ subID ].sc;
  2628. onComplete.call(scope, msg.p.s, msg.p.e == "", msg.p.e);
  2629. } catch(e) {
  2630. OpenAjax.hub._debugger();
  2631. this._log("caught error from onComplete callback to HubClient.subscribe(): " + e.message);
  2632. }
  2633. }
  2634. break;
  2635. // publish event
  2636. case "pub":
  2637. var subID = msg.p.s;
  2638. // if subscription exists and we are not in process of unsubscribing...
  2639. if (this._subs[ subID ] && ! this._subs[ subID ].uns) {
  2640. var onData = this._subs[ subID ].cb;
  2641. var scope = this._subs[ subID ].sc;
  2642. var subscriberData = this._subs[ subID ].d;
  2643. try {
  2644. onData.call(scope, msg.p.t, msg.p.d, subscriberData);
  2645. } catch(e) {
  2646. OpenAjax.hub._debugger();
  2647. this._log("caught error from onData callback to HubClient.subscribe(): " + e.message);
  2648. }
  2649. }
  2650. break;
  2651. // unsubscribe acknowledgement
  2652. case "uns_ack":
  2653. var subID = msg.p;
  2654. if (this._subs[ subID ]) {
  2655. var onComplete = this._subs[ subID ].uns.cb;
  2656. if (onComplete) {
  2657. try {
  2658. var scope = this._subs[ subID ].uns.sc;
  2659. onComplete.call(scope, subID, true);
  2660. } catch(e) {
  2661. OpenAjax.hub._debugger();
  2662. this._log("caught error from onComplete callback to HubClient.unsubscribe(): " + e.message);
  2663. }
  2664. }
  2665. delete this._subs[ subID ];
  2666. }
  2667. break;
  2668. // connect acknowledgement
  2669. case "con_ack":
  2670. this._connected = true;
  2671. if (this._connectOnComplete) {
  2672. var onComplete = this._connectOnComplete.cb;
  2673. var scope = this._connectOnComplete.sc;
  2674. try {
  2675. onComplete.call(scope, this._client, true);
  2676. } catch(e) {
  2677. OpenAjax.hub._debugger();
  2678. this._log("caught error from onComplete callback to HubClient.connect(): " + e.message);
  2679. }
  2680. delete this._connectOnComplete;
  2681. }
  2682. break;
  2683. // disconnect acknowledgment
  2684. case "dis_ack":
  2685. // stop listening for messages
  2686. if (window.removeEventListener) {
  2687. window.removeEventListener("message", this._msgListener, false);
  2688. } else {
  2689. window.detachEvent("onmessage", this._msgListener);
  2690. }
  2691. delete this._msgListener;
  2692. this._tunnelIframe.parentNode.removeChild(this._tunnelIframe);
  2693. delete this._tunnelIframe;
  2694. if (this._disconnectOnComplete) {
  2695. try {
  2696. var onComplete = this._disconnectOnComplete.cb;
  2697. var scope = this._disconnectOnComplete.sc;
  2698. onComplete.call(scope, this._client, true);
  2699. } catch(e) {
  2700. OpenAjax.hub._debugger();
  2701. this._log("caught error from onComplete callback to HubClient.disconnect(): " + e.message);
  2702. }
  2703. delete this._disconnectOnComplete;
  2704. }
  2705. break;
  2706. }
  2707. }
  2708. OpenAjax.hub.IframePMHubClient.prototype._verifyMsg = function(msg) {
  2709. return typeof msg.m == "string" && "t" in msg && "p" in msg;
  2710. }
  2711. /**
  2712. * Send a string message to the associated container.
  2713. *
  2714. * The message is a JSON representation of the following object:
  2715. * {
  2716. * m: message type,
  2717. * i: client id,
  2718. * t: security token,
  2719. * p: payload (depends on message type)
  2720. * }
  2721. *
  2722. * The payload for each message type is as follows:
  2723. * TYPE DESCRIPTION PAYLOAD
  2724. * "con" connect N/A
  2725. * "dis" disconnect N/A
  2726. * "sub" subscribe { t: topic, s: subscription id }
  2727. * "uns" unsubscribe { s: subscription id }
  2728. * "pub" publish { t: topic, d: data }
  2729. */
  2730. OpenAjax.hub.IframePMHubClient.prototype._sendMessage = function(type, payload) {
  2731. var msg = JSON.stringify({
  2732. m: type,
  2733. i: this._internalID,
  2734. t: this._securityToken,
  2735. p: payload
  2736. });
  2737. this._postMessage(window.parent, msg, this._partnerOrigin);
  2738. }
  2739. ////////////////////////////////////////////////////////////////////////////////
  2740. /*
  2741. Copyright 2006-2009 OpenAjax Alliance
  2742. Licensed under the Apache License, Version 2.0 (the "License");
  2743. you may not use this file except in compliance with the License.
  2744. You may obtain a copy of the License at
  2745. http://www.apache.org/licenses/LICENSE-2.0
  2746. Unless required by applicable law or agreed to in writing, software
  2747. distributed under the License is distributed on an "AS IS" BASIS,
  2748. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  2749. See the License for the specific language governing permissions and
  2750. limitations under the License.
  2751. */
  2752. //XXX revert r231 - Revision 231 added support for having the client pass back
  2753. //both the initial URI and the current URI, which are different in the case
  2754. //or redirection. However, in order for this to work, the final client code
  2755. //must set smash._initialClientURI to the initial URI (the URI for the page
  2756. //that did the redirection). There isn't a clean way to do this with the
  2757. //current Hub 2.0 APIs, so I'm disabling this feature for now. Search the code
  2758. //for "XXX revert r231".
  2759. if (typeof OpenAjax === "undefined") {
  2760. OpenAjax = { hub: {} };
  2761. }
  2762. OpenAjax.hub.IframeFIMContainer = function(container, hub, clientID, params) {
  2763. this._container = container;
  2764. this._hub = hub;
  2765. this._onSecurityAlert = params.Container.onSecurityAlert;
  2766. this._onConnect = params.Container.onConnect ? params.Container.onConnect : null;
  2767. this._onDisconnect = params.Container.onDisconnect ? params.Container.onDisconnect : null;
  2768. this._scope = params.Container.scope || window;
  2769. // XXX Need to make sure URI is absolute, or change the "clientURI!=componentURI"
  2770. // comparison in SEComm.initializationFinished (where 'clientURI' is always
  2771. // absolute, but 'componentURI' is based on params.IframeContainer.uri and
  2772. // may be relative, which makes the comparison fail)
  2773. this._clientURI = params.IframeContainer.uri;
  2774. smash.SEComm.tunnelURI = params.IframeContainer.tunnelURI;
  2775. smash._loadTimeout = params.IframeContainer.timeout || 15000;
  2776. if (params.Container.log) {
  2777. var scope = this._scope;
  2778. var logfunc = params.Container.log;
  2779. this._log = function(msg) {
  2780. logfunc.call(scope, "IframeContainer::" + clientID + ": " + msg);
  2781. };
  2782. this._doLog = true; // HW Optimization
  2783. } else {
  2784. this._log = function() {
  2785. };
  2786. }
  2787. // configurable goodbyeMessage: protects against malicious unloading of the mashup application
  2788. //if (params.goodbyeMessage != null) {
  2789. //smash._goodbyeMessage = params.goodbyeMessage;
  2790. //}
  2791. // configurable securityTokenLength
  2792. //if (params.securityTokenLength != null) {
  2793. //smash._securityTokenLength = params.securityTokenLength;
  2794. //smash._computeOtherTokenConstants();
  2795. //}
  2796. // create and configure the pseudo-random number generator, used to create
  2797. // security tokens
  2798. smash._createPRNG(this, params);
  2799. smash._ensureSingletonManager();
  2800. // the 'internal ID' is guaranteed to be unique within the page, not just
  2801. // the ManagedHub instance
  2802. this._internalID = smash._singletonManager.generateUniqueClientName(clientID);
  2803. }
  2804. OpenAjax.hub.IframeFIMContainer.prototype.getHub = function() {
  2805. return this._hub;
  2806. };
  2807. OpenAjax.hub.IframeFIMContainer.prototype.sendToClient = function(topic, data, subscriptionID) {
  2808. smash._singletonManager.sendToClient(this._internalID, topic, data, [ subscriptionID ]);
  2809. }
  2810. OpenAjax.hub.IframeFIMContainer.prototype.remove = function() {
  2811. /**
  2812. * Cleans up data-strucrures for communication with the given client. Needs to be called prior to unloading of the
  2813. * client to prevent false positives about 'frame phishing' attacks.
  2814. * smash.prepareForUnload(clientName: string)
  2815. */
  2816. return smash._singletonManager.prepareForUnload(this._internalID);
  2817. }
  2818. OpenAjax.hub.IframeFIMContainer.prototype.isConnected = function() {
  2819. return smash._singletonManager.isConnected(this._internalID);
  2820. }
  2821. OpenAjax.hub.IframeFIMContainer.prototype.getPartnerOrigin = function() {
  2822. return smash._singletonManager.getPartnerOrigin(this._internalID);
  2823. }
  2824. OpenAjax.hub.IframeFIMContainer.prototype.getURI = function() {
  2825. /**
  2826. * Prepares for loading of a client in a separate iframe. In addition to setting up internal data-structures,
  2827. * it updates the URI (potentially adding a fragment identifier and URI parameters).
  2828. * The updates are necessary to pass values needed to bootstrap communication.
  2829. *
  2830. * string smash.prepareForLoad({clientName: string, uri: string,
  2831. * [commErrorCallback:function(clientName:string, error:string)]})
  2832. * return value of null indicates failure, a non-null return value is the updated URI
  2833. */
  2834. var that = this;
  2835. function errorCallback(clientID, error) {
  2836. var alertType = null;
  2837. switch (error) {
  2838. case smash.SecurityErrors.INVALID_TOKEN:
  2839. case smash.SecurityErrors.TOKEN_VERIFICATION_FAILED:
  2840. alertType = OpenAjax.hub.SecurityAlert.ForgedMsg;
  2841. break;
  2842. case smash.SecurityErrors.TUNNEL_UNLOAD:
  2843. alertType = OpenAjax.hub.SecurityAlert.FramePhish;
  2844. break;
  2845. case smash.SecurityErrors.COMPONENT_LOAD:
  2846. alertType = OpenAjax.hub.SecurityAlert.LoadTimeout;
  2847. break;
  2848. }
  2849. try {
  2850. that._onSecurityAlert.call(that._scope, that._container, alertType);
  2851. } catch(e) {
  2852. OpenAjax.hub._debugger();
  2853. that._log("caught error from onSecurityAlert callback to constructor: " + e.message);
  2854. }
  2855. }
  2856. var newURI = smash._singletonManager.prepareForLoad({ clientName: this._internalID,
  2857. uri: this._clientURI, commErrorCallback: errorCallback,
  2858. oaaContainer: this, log: this._log });
  2859. if (newURI && OpenAjax.hub.enableDebug) newURI += ":debug"; // REMOVE ON BUILD
  2860. return newURI;
  2861. }
  2862. //------------------------------------------------------------------------------
  2863. OpenAjax.hub.IframeFIMHubClient = function(client, params) {
  2864. // XXX Since server redirection breaks hash communication (the server does
  2865. // not receive the fragment value, therefore the final URL does not contain
  2866. // this information), the initial message is transmitted as a URL param.
  2867. // The SMash code, though, expects messages after the hash. So we copy
  2868. // the initial message value into the fragment.
  2869. var initialMsg = new RegExp("[\\?&]oahm=([^&#]*)").exec(window.location.search);
  2870. if (! initialMsg) {
  2871. throw new Error(OpenAjax.hub.Error.WrongProtocol);
  2872. }
  2873. initialMsg = initialMsg[1];
  2874. // check communications protocol ID
  2875. var partnerProtocolID = initialMsg.split(":", 1);
  2876. if (partnerProtocolID[0] != smash._protocolID) {
  2877. throw new Error(OpenAjax.hub.Error.WrongProtocol);
  2878. }
  2879. // remove protocol ID from initialMsg, since decodeMessage() doesn't
  2880. // expect it
  2881. initialMsg = initialMsg.substring(partnerProtocolID[0].length + 1);
  2882. // copy initial message into URL fragment
  2883. var url = window.location.href + "#" + initialMsg;
  2884. window.location.replace(url);
  2885. this._client = client;
  2886. this._onSecurityAlert = params.HubClient.onSecurityAlert;
  2887. this._scope = params.HubClient.scope || window;
  2888. // pull out client id from initial message
  2889. var re = new RegExp("\\d{3}.{" + smash._securityTokenLength + "}.{" + smash._securityTokenLength + "}\\d{3}(.*)");
  2890. var payload = re.exec(initialMsg)[1];
  2891. var parts = payload.split(":");
  2892. var internalID = decodeURIComponent(parts[0]);
  2893. this._id = internalID.substring(internalID.indexOf("_") + 1);
  2894. if (parts[2] && parts[2] == "debug") OpenAjax.hub.enableDebug = true; // REMOVE ON BUILD
  2895. if (params.HubClient.log) {
  2896. var id = this._id;
  2897. var scope = this._scope;
  2898. var logfunc = params.HubClient.log;
  2899. this._log = function(msg) {
  2900. logfunc.call(scope, "IframeHubClient::" + id + ": " + msg);
  2901. };
  2902. this._doLog = true; // HW Optimization
  2903. } else {
  2904. this._log = function() {
  2905. };
  2906. }
  2907. this._connected = false;
  2908. this._subs = {};
  2909. this._subIndex = 1; // HW FIX
  2910. // create and configure the pseudo-random number generator, used to create
  2911. // security tokens
  2912. smash._createPRNG(this, params);
  2913. // configurable initialClientURI: only for those clients which perform URI redirection
  2914. // at client load time
  2915. //XXX revert r231
  2916. //if (params.initialClientURI) {
  2917. //smash._initialClientURI = params.initialClientURI;
  2918. //}
  2919. }
  2920. /*** OpenAjax.hub.HubClient interface implementation ***/
  2921. OpenAjax.hub.IframeFIMHubClient.prototype.connect = function(onComplete, scope) {
  2922. if (smash._singletonClientHub == null) {
  2923. // allow a null clientName since the SMash provider can find it in the fragment.
  2924. smash._singletonClientHub = new smash.SEHubClient(null, this._log);
  2925. // set to be notified of security errors
  2926. var that = this;
  2927. smash._singletonClientHub.setSecurityErrorCallback(function(errorcode) {
  2928. if (errorcode != smash.SecurityErrors.INVALID_TOKEN) {
  2929. that._log("unknown smash security error: " + errorcode);
  2930. }
  2931. try {
  2932. that._onSecurityAlert.call(that._scope, that._client, OpenAjax.hub.SecurityAlert.ForgedMsg);
  2933. } catch(e) {
  2934. OpenAjax.hub._debugger();
  2935. that._log("caught error from onSecurityAlert callback to constructor: " + e.message);
  2936. }
  2937. });
  2938. }
  2939. var that = this;
  2940. function cb(success, seHubClient) {
  2941. if (success) {
  2942. that._connected = true;
  2943. }
  2944. if (onComplete) {
  2945. try {
  2946. onComplete.call(scope, that._client, success); // XXX which error to return when success == false?
  2947. } catch(e) {
  2948. OpenAjax.hub._debugger();
  2949. that._log("caught error from onComplete callback to HubClient.connect(): " + e.message);
  2950. }
  2951. }
  2952. }
  2953. smash._singletonClientHub.connect(cb);
  2954. }
  2955. OpenAjax.hub.IframeFIMHubClient.prototype.disconnect = function(onComplete, scope) {
  2956. this._connected = false;
  2957. var that = this;
  2958. function cb(success, seHubClient) {
  2959. // XXX what happens if success == false
  2960. if (onComplete) {
  2961. try {
  2962. onComplete.call(scope, that._client, success); // XXX which error to return when success == false?
  2963. } catch(e) {
  2964. OpenAjax.hub._debugger();
  2965. that._log("caught error from onComplete callback to HubClient.disconnect(): " + e.message);
  2966. }
  2967. }
  2968. }
  2969. smash._singletonClientHub.disconnect(cb);
  2970. }
  2971. OpenAjax.hub.IframeFIMHubClient.prototype.getPartnerOrigin = function() {
  2972. return smash._singletonClientHub ? smash._singletonClientHub.getPartnerOrigin() : null;
  2973. }
  2974. OpenAjax.hub.IframeFIMHubClient.prototype.getClientID = function() {
  2975. return this._id;
  2976. }
  2977. /*** OpenAjax.hub.Hub interface implementation ***/
  2978. OpenAjax.hub.IframeFIMHubClient.prototype.subscribe = function(topic, onData, scope, onComplete, subscriberData) {
  2979. var subID = "" + this._subIndex++;
  2980. var that = this;
  2981. var completeCallback = ! onComplete ? null :
  2982. function (success, subHandle, error) {
  2983. try {
  2984. onComplete.call(scope, subID, success, error);
  2985. } catch(e) {
  2986. OpenAjax.hub._debugger();
  2987. that._log("caught error from onComplete callback to HubClient.subscribe(): " + e.message);
  2988. }
  2989. };
  2990. function dataCallback(subHandle, topic, data) {
  2991. try {
  2992. onData.call(scope, topic, data, subscriberData);
  2993. } catch(e) {
  2994. OpenAjax.hub._debugger();
  2995. that._log("caught error from onData callback to HubClient.subscribe(): " + e.message);
  2996. }
  2997. }
  2998. this._subs[ subID ] = smash._singletonClientHub.subscribe(topic, completeCallback, dataCallback, scope, subscriberData);
  2999. return subID;
  3000. }
  3001. OpenAjax.hub.IframeFIMHubClient.prototype.publish = function(topic, data) {
  3002. smash._singletonClientHub.publish(topic, data);
  3003. }
  3004. OpenAjax.hub.IframeFIMHubClient.prototype.unsubscribe = function(subID, onComplete, scope) {
  3005. if (! this._subs[ subID ]) {
  3006. throw new Error(OpenAjax.hub.Error.NoSubscription);
  3007. }
  3008. var that = this;
  3009. function cb(success, subHandle) {
  3010. delete that._subs[ subID ];
  3011. if (onComplete) {
  3012. try {
  3013. onComplete.call(scope, subID, success/*, error*/);
  3014. } catch(e) {
  3015. OpenAjax.hub._debugger();
  3016. that._log("caught error from onComplete callback to HubClient.unsubscribe(): " + e.message);
  3017. }
  3018. }
  3019. }
  3020. ;
  3021. this._subs[ subID ].unsubscribe(cb);
  3022. }
  3023. OpenAjax.hub.IframeFIMHubClient.prototype.isConnected = function() {
  3024. return this._connected;
  3025. }
  3026. OpenAjax.hub.IframeFIMHubClient.prototype.getScope = function() {
  3027. return this._scope;
  3028. }
  3029. OpenAjax.hub.IframeFIMHubClient.prototype.getSubscriberData = function(subID) {
  3030. var sub = this._subs[ subID ];
  3031. if (sub) {
  3032. return sub.getSubscriberData();
  3033. }
  3034. throw new Error(OpenAjax.hub.Error.NoSubscription);
  3035. }
  3036. OpenAjax.hub.IframeFIMHubClient.prototype.getSubscriberScope = function(subID) {
  3037. var sub = this._subs[ subID ];
  3038. if (sub) {
  3039. return sub.getSubscriberScope();
  3040. }
  3041. throw new Error(OpenAjax.hub.Error.NoSubscription);
  3042. }
  3043. ////////////////////////////////////////////////////////////////////////////////
  3044. ////////////////////////////////////////////////////////////////////////////////
  3045. ////////////////////////////////////////////////////////////////////////////////
  3046. ////////////////////////////////////////////////////////////////////////////////
  3047. ////////////////////////////////////////////////////////////////////////////////
  3048. if (typeof(smash) == 'undefined') {
  3049. var smash = {};
  3050. }
  3051. //Ideally, should use a closure for private (and public) data and functions,
  3052. //but this was easier for the initial SMash refactoring.
  3053. smash._singletonManager = undefined; // the singleton that implements all the manager-side SPI
  3054. smash._singletonClientHub = undefined; // the singleton that implements all the client-side SPI
  3055. smash._protocolID = "openajax-2.0";
  3056. //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
  3057. //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.
  3058. //XXX revert r231
  3059. //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
  3060. //--- security token stuff ---
  3061. //configurable pseudo random number generator (prng) to use for generating the security token.
  3062. //If not set, we use Math.random.
  3063. //If set, the provided random number generator must support a function nextRandomB64Str(strlength:integer)
  3064. //that returns a string of length strlength, where each character is a "modified Base64 for URL" character.
  3065. //This includes A-Z, a-z, and 0-9 for the first 62 digits, like standard Base64 encoding, but
  3066. //no padding '='. And the '+', '/' characters of standard Base64 are replaced by '-', '_'.
  3067. smash._prng = undefined;
  3068. 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.
  3069. smash._securityTokenOverhead = null; // the number of characters in a serialized message consumed by the security tokens
  3070. smash._computeOtherTokenConstants = function() {
  3071. smash._securityTokenOverhead = 2 * smash._securityTokenLength;
  3072. smash._multiplier = Math.pow(10, smash._securityTokenLength - 1);
  3073. }
  3074. smash._computeOtherTokenConstants();
  3075. smash._createPRNG = function(container, params) {
  3076. if (! smash._prng) {
  3077. // create pseudo-random number generator with a default seed
  3078. var seed = new Date().getTime() + Math.random() + document.cookie;
  3079. smash._prng = smash.crypto.newPRNG(seed);
  3080. }
  3081. var p = params.IframeContainer || params.IframeHubClient;
  3082. if (p && p.seed) {
  3083. try {
  3084. var extraSeed = p.seed.call(container._scope);
  3085. smash._prng.addSeed(extraSeed);
  3086. } catch(e) {
  3087. OpenAjax.hub._debugger();
  3088. container._log("caught error from 'seed' callback: " + e.message);
  3089. }
  3090. }
  3091. }
  3092. /**
  3093. * Randomly generates the security token which will be used to ensure message integrity.
  3094. */
  3095. smash._keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
  3096. smash._generateSecurityToken = function() {
  3097. var r;
  3098. if (smash._prng)
  3099. r = smash._prng.nextRandomB64Str(smash._securityTokenLength);
  3100. else {
  3101. var r1 = Math.random(); // value in (0,1)
  3102. r = "";
  3103. // assuming one Math.random() value has enough bits for smash._securityTokenLenght
  3104. for (var i = 0; i < smash._securityTokenLength; i++) {
  3105. var r2 = r1 * 64; // get the most significant base-64 value
  3106. var c = Math.floor(r2);
  3107. r1 = (r2 - c); // the remaining fractional value
  3108. r = r + smash._keyStr.charAt(c);
  3109. }
  3110. }
  3111. return r;
  3112. }
  3113. //------------------------- manager-side implementation ------------------------
  3114. /**
  3115. * lazy creation of the manager-side singleton
  3116. */
  3117. smash._ensureSingletonManager = function() {
  3118. if (smash._singletonManager == null)
  3119. smash._singletonManager = new smash.SEHub();
  3120. }
  3121. /**
  3122. * Constructor.
  3123. * The name SEHub is legacy. The provider on the manager-side does not implement any of the hub functionality
  3124. * other than communication.
  3125. */
  3126. smash.SEHub = function() {
  3127. // 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
  3128. var that = this;
  3129. // associative array indexed by componentId. Each element is a ComponentInfo object.
  3130. // Component is synonymous with client. componentId is the same as clientName
  3131. this.componentInfo = [];
  3132. this._subs = [];
  3133. /**
  3134. * Constructor for ComponentInfo objects
  3135. */
  3136. function ComponentInfo(uri, eCallback) {
  3137. this.uri = uri;
  3138. //this.state = smash.SEHubConstants.START;
  3139. this.connected = false;
  3140. this.errorCallback = eCallback;
  3141. }
  3142. // create an ID that is unique within the page
  3143. this.generateUniqueClientName = function(clientName) {
  3144. do {
  3145. clientName = ((0x7fff * Math.random()) | 0).toString(16) + "_" + clientName;
  3146. } while (that.componentInfo[ clientName ]);
  3147. return clientName;
  3148. }
  3149. // securityListener function registered for each component's security events
  3150. function securityListener(errorType, clientName) {
  3151. //var errorString = that.getSecurityErrorString(errorType); // get the error as a string
  3152. var ci = that.componentInfo[clientName];
  3153. if (ci != null) {
  3154. var errorCallback = ci.errorCallback; // the errorCallback registered by the application
  3155. if (errorCallback != null) { // if one was registered
  3156. // errorCallback(clientName, errorString);
  3157. errorCallback(clientName, errorType);
  3158. }
  3159. }
  3160. }
  3161. /**
  3162. * string prepareForLoad({clientName: string, uri: string,
  3163. * [commErrorCallback:function(clientName:string, error:string)]})
  3164. * return value of null indicates failure, a non-null return value is the updated URI
  3165. */
  3166. this.prepareForLoad = function(params) {
  3167. var clientName = params.clientName; // componentId and clientName are the same thing in this code
  3168. var componentURI = params.uri;
  3169. if ((clientName == null) || (componentURI == null))
  3170. return null;
  3171. if (that.componentInfo[clientName] != null) {
  3172. return null;
  3173. }
  3174. that.componentInfo[clientName] = new ComponentInfo(componentURI, params.commErrorCallback);
  3175. that.componentInfo[clientName].seComm = new smash.SEComm(); //The SEComm library used for this component
  3176. that.componentInfo[clientName].seComm.setSecurityListener(securityListener);
  3177. that.componentInfo[clientName].oaaContainer = params.oaaContainer;
  3178. return that.componentInfo[clientName].seComm.prepareForLoad(clientName, componentURI, that, smash._loadTimeout, params.log);
  3179. }
  3180. /**
  3181. * boolean prepareForUnload(clientName: string)
  3182. */
  3183. this.prepareForUnload = function(clientName) {
  3184. if (!that.componentInfo[clientName]) {
  3185. // component does not exist.
  3186. return true;
  3187. }
  3188. //// change state. pretty useless, since going to delete anyway
  3189. //that.componentInfo[clientName].state = smash.SEHubConstants.UNLOADED;
  3190. that._disconnect(clientName);
  3191. that.componentInfo[clientName].seComm.prepareForUnload();
  3192. // remove the relevant objects
  3193. delete that.componentInfo[clientName];
  3194. return true;
  3195. }
  3196. /**
  3197. * boolean isConnected(clientName:string)
  3198. */
  3199. this.isConnected = function(clientName) {
  3200. //if ( that.componentInfo[clientName] && that.componentInfo[clientName].state == smash.SEHubConstants.LOADED )
  3201. if (that.componentInfo[clientName] && that.componentInfo[clientName].connected) {
  3202. return true;
  3203. }
  3204. return false;
  3205. }
  3206. /**
  3207. * sendToClient(clientName:string, topic: string, data:JSON|string, matchingSubs:array of string)
  3208. */
  3209. this.sendToClient = function(clientName, topic, data, matchingSubs) {
  3210. // send to the component
  3211. if (that.isConnected(clientName)) {
  3212. var comms = that.componentInfo[clientName].seComm;
  3213. if (comms) {
  3214. comms.distribute(topic, matchingSubs, data);
  3215. }
  3216. }
  3217. }
  3218. /** Callback when component loaded */
  3219. this.componentLoaded = function(clientName, partnerURL) {
  3220. if (that.componentInfo[clientName]) {
  3221. // that.componentInfo[clientName].state = smash.SEHubConstants.LOADED;
  3222. that.componentInfo[clientName].connected = true;
  3223. that.componentInfo[clientName].partnerOrigin = new RegExp("^([a-zA-Z]+://[^:/?#]+).*").exec(partnerURL)[1];
  3224. var oaaContainer = that.componentInfo[ clientName ].oaaContainer;
  3225. oaaContainer._container.getIframe().style.visibility = "visible";
  3226. if (oaaContainer._onConnect) {
  3227. try {
  3228. oaaContainer._onConnect.call(oaaContainer._scope, oaaContainer._container);
  3229. } catch(e) {
  3230. OpenAjax.hub._debugger();
  3231. oaaContainer._log("caught error from onConnect callback to constructor: " + e.message);
  3232. }
  3233. }
  3234. }
  3235. }
  3236. /**
  3237. * A message received from a component
  3238. * @param componentId The component that sent the message
  3239. * @param topic
  3240. * @param message The payload of the message (JSON|string)
  3241. */
  3242. this.publishInternal = function(componentId, topic, message) {
  3243. if (that.componentInfo[componentId]) {
  3244. // component exists
  3245. var oaaContainer = that.componentInfo[ componentId ].oaaContainer;
  3246. oaaContainer._hub.publishForClient(oaaContainer._container, topic, message);
  3247. }
  3248. }
  3249. /**
  3250. * A subscribe message received from a component
  3251. * @param componentId The component that sent the message
  3252. * @param subId The subscription id
  3253. * @param topic
  3254. */
  3255. this.subscribeInternal = function(componentId, subId, topic) {
  3256. var oaaContainer = that.componentInfo[ componentId ].oaaContainer;
  3257. that._subs[ subId ] = oaaContainer._hub.subscribeForClient(oaaContainer._container, topic, subId);
  3258. }
  3259. /**
  3260. * A unsubscribe message received from a component
  3261. * @param componentId The component that sent the message
  3262. * @param subId
  3263. * @returns true if unsubscribe was accepted else false
  3264. */
  3265. this.unsubscribeInternal = function(componentId, subId) {
  3266. try {
  3267. var handle = that._subs[ subId ];
  3268. var oaaContainer = that.componentInfo[ componentId ].oaaContainer;
  3269. oaaContainer._hub.unsubscribeForClient(oaaContainer._container, handle);
  3270. return true;
  3271. } catch(e) {
  3272. }
  3273. return false;
  3274. }
  3275. this.disconnect = function(componentId) {
  3276. that._disconnect(componentId);
  3277. var oaaContainer = that.componentInfo[ componentId ].oaaContainer;
  3278. if (oaaContainer._onDisconnect) {
  3279. try {
  3280. oaaContainer._onDisconnect.call(oaaContainer._scope, oaaContainer._container);
  3281. } catch(e) {
  3282. OpenAjax.hub._debugger();
  3283. oaaContainer._log("caught error from onDisconnect callback to constructor: " + e.message);
  3284. }
  3285. }
  3286. }
  3287. this._disconnect = function(componentId) {
  3288. if (that.componentInfo[ componentId ].connected) {
  3289. that.componentInfo[ componentId ].connected = false;
  3290. // hide component iframe
  3291. var oaaContainer = that.componentInfo[ componentId ].oaaContainer;
  3292. oaaContainer._container.getIframe().style.visibility = "hidden";
  3293. // unsubscribe from all subs
  3294. for (var sub in that._subs) {
  3295. oaaContainer._hub.unsubscribeForClient(oaaContainer._container, that._subs[ sub ]);
  3296. }
  3297. that._subs = [];
  3298. }
  3299. }
  3300. this.getPartnerOrigin = function(componentId) {
  3301. if (that.componentInfo[ componentId ].connected) {
  3302. return that.componentInfo[ componentId ].partnerOrigin;
  3303. }
  3304. return null;
  3305. }
  3306. /**
  3307. * Converts a security error code into a readable error message.
  3308. * @param error The error code.
  3309. */
  3310. //this.getSecurityErrorString = function(error) {
  3311. //switch (error) {
  3312. // case smash.SecurityErrors.INVALID_TOKEN: return smash.SecurityErrors.INVALID_TOKEN_MSG;
  3313. // case smash.SecurityErrors.TOKEN_VERIFICATION_FAILED: return smash.SecurityErrors.TOKEN_VERIFICATION_FAILED_MSG;
  3314. // case smash.SecurityErrors.TUNNEL_UNLOAD: return smash.SecurityErrors.TUNNEL_UNLOAD_MSG;
  3315. // case smash.SecurityErrors.COMPONENT_LOAD: return smash.SecurityErrors.COMPONENT_LOAD_MSG;
  3316. // default: return "UNKNOWN";
  3317. //}
  3318. //}
  3319. /**
  3320. * Sets the unload function which shows the goodbye message.
  3321. */
  3322. //window.onunload=function(){
  3323. //if (smash._goodbyeMessage != undefined)
  3324. // alert(smash._goodbyeMessage);
  3325. //}
  3326. }
  3327. //---------- client-side implementation ----------------------------------------
  3328. /**
  3329. * SEHubClient implementation linking the SECommClient together with the component side logic.
  3330. */
  3331. smash.SEHubClient = function(clientName, logfunc) {
  3332. //-------- interface implemented by connHandle in Hub 1.1. We use the SEHub instance itself
  3333. //-------- as the connHandle object for the "manager".
  3334. this.equals = function(anotherConn) {
  3335. return that === anotherConn;
  3336. }
  3337. this.isConnected = function() {
  3338. return connected;
  3339. }
  3340. this.getClientName = function() {
  3341. return clientName;
  3342. }
  3343. this.connect = function(callback) {
  3344. connectCallback = function(success) {
  3345. if (success) {
  3346. connected = true;
  3347. }
  3348. callback(success, that);
  3349. };
  3350. seCommClient.connect(connectCallback);
  3351. }
  3352. this.disconnect = function(callback) {
  3353. disconnectCallback = function(success) {
  3354. if (success) {
  3355. connected = false;
  3356. subHandles = []; // delete all existing subscriptions
  3357. }
  3358. callback(success, that);
  3359. };
  3360. seCommClient.disconnect();
  3361. return;
  3362. }
  3363. /**
  3364. * connHandle.subscribe(topic:string, callback:function, eventCallback:function)
  3365. * returns a subHandle object, or null if it fails immediately.
  3366. */
  3367. this.subscribe = function(topic, callback, eventCallback, scope, subscriberData) {
  3368. // keep track of the callback so that the incomming message can be distributed correctly
  3369. var subId = (subCount + ''); // assign the subscription id - making it a string
  3370. subCount++;
  3371. subHandles[subId] = new SubHandle(subId, topic, callback, eventCallback, that, scope, subscriberData);
  3372. seCommClient.subscribe(subId, topic);
  3373. return subHandles[subId];
  3374. }
  3375. /**
  3376. * connHandle.publish(topic:string, data:JSON|string)
  3377. */
  3378. this.publish = function(topic, data) {
  3379. seCommClient.publish(topic, data);
  3380. return true;
  3381. }
  3382. function SubHandle(subId, topic, callback, eventCallback, sehubClient, scope, subscriberData) {
  3383. var _isSubscribed = false;
  3384. var _data = subscriberData;
  3385. var _scope = scope;
  3386. var that = this;
  3387. this.getTopic = function() {
  3388. return topic;
  3389. }
  3390. this.getConnHandle = function() {
  3391. return sehubClient;
  3392. }
  3393. this.equals = function(anotherSubHandle) {
  3394. if ((anotherSubHandle._getSubId != null) && (typeof anotherSubHandle._getSubId == "function")
  3395. && (anotherSubHandle.getConnHandle != null) && (typeof anotherSubHandle.getConnHandle == "function")) {
  3396. if ((subId === anotherSubHandle._getSubId()) && (sehubClient === anotherSubHandle.getConnHandle()))
  3397. return true;
  3398. }
  3399. return false;
  3400. }
  3401. this.isSubscribed = function() {
  3402. return _isSubscribed;
  3403. }
  3404. this.unsubscribe = function(callback) {
  3405. return sehubClient._unsubscribe(that, callback);
  3406. }
  3407. this.getSubscriberData = function() {
  3408. return _data;
  3409. }
  3410. this.getSubscriberScope = function() {
  3411. return _scope;
  3412. }
  3413. this._getSubId = function() {
  3414. return subId;
  3415. }
  3416. this._setIsSubscribed = function(value) {
  3417. _isSubscribed = value;
  3418. }
  3419. this._getCallback = function() {
  3420. return callback;
  3421. }
  3422. this._getEventCallback = function() {
  3423. return eventCallback;
  3424. }
  3425. }
  3426. this.getPartnerOrigin = function() {
  3427. if (connected && seCommClient != null) {
  3428. var ptu = seCommClient.getParsedTunnelUrl();
  3429. if (ptu != null) {
  3430. return ptu.scheme + "://" + ptu.host;
  3431. }
  3432. }
  3433. return null;
  3434. }
  3435. //-------- end of interface implemented by connHandle in Hub 1.1.
  3436. //------- addition public interfaces not part of Hub 1.1 -----
  3437. /**
  3438. * Set a callback to find out about security errors.
  3439. * Not part of the OpenAjax Hub 1.1 standard
  3440. */
  3441. this.setSecurityErrorCallback = function(errorcallback) {
  3442. securityErrorCallback = errorcallback;
  3443. }
  3444. //this.getManagerDomain = function() {
  3445. //if (seCommClient != null) {
  3446. // var ptu = seCommClient.getParsedTunnelUrl();
  3447. // if (ptu != null) return ptu.host;
  3448. //}
  3449. //return null;
  3450. //}
  3451. //------- private stuff ------
  3452. /**
  3453. * _unsubscribe(subHandle:object, callback:function)
  3454. * returns a subHandle object, or null if it fails immediately.
  3455. */
  3456. this._unsubscribe = function(subHandle, callback) {
  3457. var subId = subHandle._getSubId();
  3458. if (! subHandles[ subId ]) {
  3459. throw new Error(OpenAjax.hub.Error.NoSubscription);
  3460. }
  3461. subHandles[subId] = undefined;
  3462. seCommClient.unsubscribe(subId);
  3463. // no async callback as no confirmation message from manager
  3464. if (callback != null) {
  3465. callback(true, subHandle); // function(success:boolean, subHandle:object).
  3466. }
  3467. return subHandle;
  3468. }
  3469. var securityErrorCallback = undefined; // securityErrorCallback registered by the application in this component/frame
  3470. // subscriptions: each subscription is assigned an integer id that is unique to this client
  3471. var subCount = 0;
  3472. // mapping the subscription ids to the SubHandles
  3473. var subHandles = [];
  3474. // SECommClient serving the communication between the SEHub and the SEHub client
  3475. var seCommClient = new smash.SECommClient(clientName, logfunc);
  3476. //var state = smash.SEHubConstants.LOADED; // initialize my state to LOADED.
  3477. var connected = false;
  3478. // 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
  3479. var that = this;
  3480. var connectCallback = null;
  3481. var disconnectCallback = null;
  3482. /**
  3483. * Processes messages received by the SECommClient
  3484. * @param message The actual message.
  3485. */
  3486. function handleIncomingMessage(message) {
  3487. if (! connected && message.type != smash.SECommMessage.CONNECT_ACK) {
  3488. return;
  3489. }
  3490. switch (message.type) {
  3491. case smash.SECommMessage.DISTRIBUTE:
  3492. if ((message.additionalHeader != null) && (message.additionalHeader.s != null)) {
  3493. var subs = message.additionalHeader.s;
  3494. for (var i = 0; i < subs.length; i++) {
  3495. var subId = subs[i];
  3496. if ((subId != null) && (subHandles[subId] != null)) {
  3497. var eventCallback = subHandles[subId]._getEventCallback();
  3498. if (eventCallback != null)
  3499. eventCallback(subHandles[subId], message.topic, message.payload);
  3500. }
  3501. }
  3502. }
  3503. break;
  3504. case smash.SECommMessage.SUBSCRIBE_ACK:
  3505. if (message.additionalHeader != null) {
  3506. var subId = message.additionalHeader.subId;
  3507. var isOk = message.additionalHeader.isOk;
  3508. var err = message.additionalHeader.err;
  3509. if ((subId != null) && (isOk != null)) {
  3510. if (subHandles[subId] != null) {
  3511. var callback = subHandles[subId]._getCallback();
  3512. if (isOk) {
  3513. subHandles[subId]._setIsSubscribed(true);
  3514. if (callback != null)
  3515. callback(true, subHandles[subId]);
  3516. }
  3517. else {
  3518. if (callback != null)
  3519. callback(false, subHandles[subId], err);
  3520. subHandles[subId] = undefined; // unsubscribe
  3521. }
  3522. }
  3523. }
  3524. }
  3525. // else ignore the message
  3526. break;
  3527. case smash.SECommMessage.CONNECT_ACK:
  3528. connectCallback(true);
  3529. break;
  3530. case smash.SECommMessage.DISCONNECT_ACK:
  3531. disconnectCallback(true);
  3532. break;
  3533. }
  3534. }
  3535. function securityListenerCallback(errorcode) {
  3536. //var errorString = getSecurityErrorString(errorcode);
  3537. if (securityErrorCallback != null) {
  3538. // securityErrorCallback(errorString);
  3539. securityErrorCallback(errorcode);
  3540. }
  3541. else {
  3542. throw new Error(errorString);
  3543. }
  3544. }
  3545. //function getSecurityErrorString(error) {
  3546. //switch (error) {
  3547. // case smash.SecurityErrors.INVALID_TOKEN: return smash.SecurityErrors.INVALID_TOKEN_MSG;
  3548. // default: return "UNKNOWN";
  3549. //}
  3550. //}
  3551. // Override the SECommClient's received method with our own implementation
  3552. seCommClient.handleIncomingMessage = handleIncomingMessage;
  3553. seCommClient.setSecurityListener(securityListenerCallback);
  3554. }
  3555. //-----------------------------------------------------------------------------------------------
  3556. //smash.SEHubConstants = {
  3557. //
  3558. //// Constants representing states of a component.
  3559. //// Component State Machine: START -> LOADED -> UNLOADED
  3560. //
  3561. //START: 0,
  3562. //LOADED: 1,
  3563. //UNLOADED: 2
  3564. //
  3565. //};
  3566. //-----------------------------------------------------------------------------------------------
  3567. /**
  3568. * Constants representing the different types of attacks that can be detected and prevented by the library.
  3569. */
  3570. smash.SecurityErrors = {
  3571. // This error occurs when the CommLib detects a message with a different security token than the one with wich it was initialized.
  3572. INVALID_TOKEN: 0,
  3573. //INVALID_TOKEN_MSG: "The sender of the received message could not be verified because the received security token was not correct.",
  3574. // 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.
  3575. TOKEN_VERIFICATION_FAILED: 1,
  3576. //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.",
  3577. // Phishing error
  3578. TUNNEL_UNLOAD: 2,
  3579. //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.",
  3580. // Phishing error before successfull load
  3581. COMPONENT_LOAD: 3
  3582. //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."
  3583. };
  3584. //-----------------------------------------------------------------------------------------------
  3585. /**
  3586. * The object implementing the message serializer and deserializer for use in SEComm.
  3587. * The topic and payload are typically under application control and may contain URI reserved characters.
  3588. * These will be percent-encoded and decoded, and the application has to deal with the composition issues
  3589. * if it is passing in data or topics that are already percent-encoded.
  3590. */
  3591. smash.SECommMessage = function() {
  3592. // The type of the message. A string
  3593. this.type = null;
  3594. // The topic of the message. A string
  3595. this.topic = null;
  3596. // The remaining header information. A JSON object
  3597. this.additionalHeader = null;
  3598. // The payload of the message. A string
  3599. this.payload = null;
  3600. // The name used in the name value pair transmission. one character for efficiency. only use a letter or number
  3601. var typeName = "y";
  3602. var topicName = "t";
  3603. var additionalHeaderName = "h"; // other header information that is not handled by typeName and topicName
  3604. var payloadName = "p";
  3605. /**
  3606. * Serializes the message into a string which can be transmitted over a communication channel.
  3607. * URI-encodes the topic and payload and uses "=", "&" as separators. The communication channel
  3608. * must not perform any URI-encoding as "=", "&" are not reserved for fragments.
  3609. * If using something other than fragment messaging at the communication channel, the serialization
  3610. * may need to change.
  3611. * @returns The serialized message.
  3612. */
  3613. this.serialize = function() {
  3614. var returnValue = typeName + "=" + this.type;
  3615. if (this.topic != null) {
  3616. var topicString = encodeURIComponent(this.topic);
  3617. var topicSer = "&" + topicName + "=" + topicString;
  3618. returnValue += topicSer;
  3619. }
  3620. if (this.additionalHeader != null) {
  3621. var headerString = encodeURIComponent(JSON.stringify(this.additionalHeader));
  3622. var headerSer = "&" + additionalHeaderName + "=" + headerString;
  3623. returnValue += headerSer;
  3624. }
  3625. if (this.payload != null) {
  3626. var payloadString = encodeURIComponent(this.payload);
  3627. var payloadSer = "&" + payloadName + "=" + payloadString;
  3628. returnValue += payloadSer;
  3629. }
  3630. return returnValue;
  3631. }
  3632. /**
  3633. * Deserializes a serialized message and initializes the objects parameters.
  3634. */
  3635. this.deserialize = function(serializedMessage) {
  3636. var messageParts = serializedMessage.split("&");
  3637. for (var i = 0; i < messageParts.length; i++) {
  3638. var nameValue = messageParts[i].split("=");
  3639. switch (nameValue[0]) {
  3640. case typeName:
  3641. this.type = nameValue[1];
  3642. break;
  3643. case topicName:
  3644. this.topic = decodeURIComponent(nameValue[1]);
  3645. break;
  3646. case additionalHeaderName:
  3647. var headerString = decodeURIComponent(nameValue[1]);
  3648. this.additionalHeader = JSON.parse(headerString);
  3649. break;
  3650. case payloadName:
  3651. this.payload = decodeURIComponent(nameValue[1]);
  3652. break;
  3653. }
  3654. }
  3655. }
  3656. }
  3657. //only use letters or numbers as characters
  3658. //CONNECT message
  3659. smash.SECommMessage.CONNECT = "con";
  3660. smash.SECommMessage.CONNECT_ACK = "cac";
  3661. //DISCONNECT message
  3662. smash.SECommMessage.DISCONNECT = "xcon";
  3663. smash.SECommMessage.DISCONNECT_ACK = "xac";
  3664. //PUBLISH message: additionalHeader is {f:"S"} or {f:"J"} representing that the payload is a string or JSON,
  3665. //topic and payload are topic, payload of message
  3666. smash.SECommMessage.PUBLISH = "pub";
  3667. //DISTRIBUTE message: additionalHeader is {f: string, s:[string, ...]} where f is defined as in the PUBLISH message,
  3668. //and s representing subIds that should receive this message; topic and payload are as in PUBLISH message
  3669. smash.SECommMessage.DISTRIBUTE = "dis";
  3670. //SUSCRIBE message: additionalHeader is {subId: string}, payload==null, topic is subscription topic
  3671. smash.SECommMessage.SUBSCRIBE = "sub";
  3672. //UNSUBSCRIBE message: additionalHeader is {subId: string}, topic==null, payload==null
  3673. smash.SECommMessage.UNSUBSCRIBE = "uns";
  3674. //SUBCRIBE_ACK message: additionalHeader is {subId: string, isOk: boolean, err: string}, topic==null, payload == null
  3675. smash.SECommMessage.SUBSCRIBE_ACK = "sac";
  3676. smash.SECommMessage.ERROR = "err"; // TBD
  3677. //-----------------------------------------------------------------------------------------------
  3678. /**
  3679. * Definitions of exceptions used by SECom
  3680. */
  3681. smash.SECommErrors = {};
  3682. smash.SECommErrors.tunnelNotSetError = new Error("The tunnel URI was not set. Please set the tunnel URI.");
  3683. //smash.SECommErrors.componentNotFoundError = new Error ("The component could not be identified. Please declare the component correctly.");
  3684. //smash.SECommErrors.securityTokenNotVerifiedError = new Error (smash.SecurityErrors.TOKEN_VERIFICATION_FAILED_MSG);
  3685. //smash.SECommErrors.tunnelUnloadError = new Error (smash.SecurityErrors.TUNNEL_UNLOAD_MSG);
  3686. //smash.SECommErrors.componentLoadError = new Error (smash.SecurityErrors.COMPONENT_LOAD_MSG);
  3687. /**
  3688. * Links the SEHub and the SEHubClient together over the communication implemented by CommLib bridge
  3689. *
  3690. * TODO: Check if the component loading allows valid HTML.
  3691. * TODO: Propagate the style of the enclosing tag into the iFrame
  3692. * TODO: Check if there is a better way than polling to see if the tunnel's commLib has been registered
  3693. */
  3694. smash.SEComm = function() {
  3695. // The timer used to delay the phishing message. This makes sure that a page navigation does not cause phishing errors.
  3696. // Setting it to 1 ms is enough for it not to be triggered on regular page navigations.
  3697. var unloadTimer = 1;
  3698. // Variable storing the identifier for the setInterval if processing a registrationTimer
  3699. var registrationTimerProcess = null;
  3700. var loadTimeout = 0;
  3701. var reconnectTimerProcess = null;
  3702. // The URI of the component being manages by this SEComm.
  3703. var componentURI = null;
  3704. // The commLib of the tunnel
  3705. var commLib = null;
  3706. // Variable storing the identifier to clear when the setInterval is called
  3707. var commLibPoll = null;
  3708. // The HTML id of the component for which this is a SEComm
  3709. var componentID = null;
  3710. // A queue for outgoing messages. This queue is used when new send requests are done while we are still sending or receiving a message.
  3711. var queueOut = [];
  3712. // Variable storing the identifier for the setInterval if processing an output queue
  3713. var queueOutProcess = null;
  3714. // Variable storing a reference to the SEHub which is managing this SEComm
  3715. var seHUB = null;
  3716. // The iframe in which the component is loaded
  3717. var myIframe = null;
  3718. // The security token used for this component
  3719. var securityTokenParent = null;
  3720. // Variable storing the callback to the security listener function
  3721. var securityListener = null;
  3722. // 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
  3723. var that = this;
  3724. // keeps track of the initialization
  3725. var initialized = false;
  3726. // logging function
  3727. var logfunc = null;
  3728. /**
  3729. * Sets the callback for security errors.
  3730. *
  3731. * @param The callback for security errors.
  3732. */
  3733. this.setSecurityListener = function(callback) {
  3734. securityListener = callback;
  3735. }
  3736. function insertURLParams(uri, params) {
  3737. var parts = uri.split("?");
  3738. if (parts.length > 1) {
  3739. return parts[0] + "?" + params + "&" + parts[1];
  3740. }
  3741. parts = uri.split("#");
  3742. if (parts.length > 1) {
  3743. return parts[0] + "?" + params + parts[1];
  3744. }
  3745. return uri + "?" + params;
  3746. }
  3747. /**
  3748. * Prepares for loading a component into an iframe.
  3749. * @returns The modified URI
  3750. */
  3751. this.prepareForLoad = function(componentId, frameURI, seHub, loadtimeout, logFunc) {
  3752. logfunc = logFunc;
  3753. this.log("Parent connecting to : " + componentId);
  3754. // Store the SEHub
  3755. seHUB = seHub;
  3756. // Store the component Id
  3757. componentID = componentId;
  3758. loadTimeout = loadtimeout;
  3759. // Check if the tunnel is set
  3760. if (smash.SEComm.tunnelURI == null)throw smash.SECommErrors.tunnelNotSetError;
  3761. // modify the URI
  3762. securityTokenParent = smash._generateSecurityToken();
  3763. // include the token twice since the child token value does not matter yet
  3764. //XXX revert r231
  3765. //componentURI = insertURLParams( frameURI, "id=" + encodeURIComponent(componentId) );
  3766. //var modifiedURIWithFragment = componentURI + "#100" + securityTokenParent + securityTokenParent + "000" + encodeURIComponent(componentId) + ":" + encodeURIComponent(smash.SEComm.tunnelURI);
  3767. // Since a server redirect does not take into account the fragment value
  3768. // (it is not transmitted by the browser to the server), the initial
  3769. // message must be sent as a URL param.
  3770. componentURI = insertURLParams(frameURI, "oahm=" + smash._protocolID + ":100" + securityTokenParent + securityTokenParent + "000" + encodeURIComponent(componentId) + ":" + encodeURIComponent(smash.SEComm.tunnelURI));
  3771. // Make the instance available for the tunnel.
  3772. smash.SEComm.instances[componentId] = that;
  3773. // Set a timer which detects if the component loaded successfully
  3774. // We are using an interval not to lose our evaluation context.
  3775. registrationTimerProcess = setInterval(pollForIncomingCommLibTimeout, loadTimeout);
  3776. return componentURI;
  3777. }
  3778. function pollForIncomingCommLibTimeout() {
  3779. clearInterval(registrationTimerProcess);
  3780. registrationTimerProcess = null;
  3781. //No CommLib has been registered.
  3782. if (! commLib) {
  3783. that.handleSecurityError(smash.SecurityErrors.COMPONENT_LOAD);
  3784. }
  3785. }
  3786. function reconnectTimeout() {
  3787. clearInterval(reconnectTimerProcess);
  3788. that.handleSecurityError(smash.SecurityErrors.COMPONENT_LOAD);
  3789. }
  3790. /**
  3791. * Gets the scope. Should only be used by the tunnel during INIT.
  3792. * @returns scope (object) the scope in which the callback needs to be called.
  3793. **/
  3794. this.getScope = function() {
  3795. return this;
  3796. }
  3797. /**
  3798. * Gets the callback. Should only be used by the tunnel during INIT.
  3799. * @param c (string) the name of the callback method
  3800. **/
  3801. this.getCallback = function() {
  3802. return "messageReceived";
  3803. }
  3804. /**
  3805. * Called when the initialisaiton of the library is done and processes all messages in the queue
  3806. */
  3807. this.initializationFinished = function(tunnelCommLib, token, currentClientURI, initialClientURI, tunnelWindow) {
  3808. this.log("Tunnel commLib initialization finished. Processing outgoing queue. Security token: " + token);
  3809. //XXX revert r231
  3810. //// verify the security token and currentClientURI
  3811. //if ((securityTokenParent!=token) || (initialClientURI!=componentURI)) {
  3812. // verify the security token
  3813. if (securityTokenParent != token) {
  3814. that.handleSecurityError(smash.SecurityErrors.TOKEN_VERIFICATION_FAILED);
  3815. return false;
  3816. }
  3817. else {
  3818. commLib = tunnelCommLib;
  3819. initialized = true;
  3820. this.log("Token verified.");
  3821. // register the onunload handler
  3822. tunnelWindow.onunload = tunnelUnloadHandler;
  3823. // switch the state to loaded in the seHUB.
  3824. seHUB.componentLoaded(componentID, currentClientURI);
  3825. // process the current outgoing queue.
  3826. while (queueOut.length > 0)commLib.send(queueOut.shift());
  3827. return true;
  3828. }
  3829. }
  3830. this.prepareForUnload = function() {
  3831. // stop all timers
  3832. if (registrationTimerProcess != null) {
  3833. clearInterval(registrationTimerProcess);
  3834. registrationTimerProcess = null;
  3835. }
  3836. }
  3837. function securityListenerClosure(error, componentId) {
  3838. return function() {
  3839. securityListener(error, componentId);
  3840. }
  3841. }
  3842. this.handleSecurityError = function(error) {
  3843. // if we have a timeout error, then overwrite initializationFinished()
  3844. // to return false by default, in order to prevent client connection
  3845. if (error == smash.SecurityErrors.COMPONENT_LOAD) {
  3846. this.initializationFinished = function() {
  3847. return false;
  3848. }
  3849. }
  3850. if (securityListener == null) {
  3851. throw new Error(error);
  3852. }
  3853. else {
  3854. securityListener(error, componentID);
  3855. }
  3856. return;
  3857. }
  3858. /**
  3859. *
  3860. */
  3861. function tunnelUnloadHandler() {
  3862. if (securityListener == null) {
  3863. setTimeout("throw tunnelUnloadError;", unloadTimer);
  3864. }
  3865. else {
  3866. setTimeout(securityListenerClosure(smash.SecurityErrors.TUNNEL_UNLOAD, componentID), unloadTimer);
  3867. }
  3868. }
  3869. /**
  3870. * Function processing the incomming data from commLib
  3871. *
  3872. * @param message The message containing the incomming data
  3873. */
  3874. this.messageReceived = function (message) {
  3875. var msg = new smash.SECommMessage();
  3876. msg.deserialize(message);
  3877. switch (msg.type) {
  3878. case smash.SECommMessage.PUBLISH:
  3879. if (msg.additionalHeader != null) {
  3880. var payload = msg.payload;
  3881. if (msg.additionalHeader.f == "J")
  3882. payload = JSON.parse(msg.payload);
  3883. seHUB.publishInternal(componentID, msg.topic, payload);
  3884. } // else no additionalHeader defining the payload format. hence ignore the message
  3885. break;
  3886. case smash.SECommMessage.SUBSCRIBE:
  3887. if (msg.additionalHeader != null) {
  3888. var isOk = true;
  3889. var errMsg = "";
  3890. try {
  3891. seHUB.subscribeInternal(componentID, msg.additionalHeader.subId, msg.topic);
  3892. } catch(e) {
  3893. isOk = false;
  3894. errMsg = e.message;
  3895. }
  3896. var msgack = new smash.SECommMessage();
  3897. msgack.type = smash.SECommMessage.SUBSCRIBE_ACK;
  3898. msgack.additionalHeader = {subId: msg.additionalHeader.subId, isOk: isOk, err: errMsg};
  3899. send(msgack.serialize());
  3900. }
  3901. break;
  3902. case smash.SECommMessage.UNSUBSCRIBE:
  3903. if (msg.additionalHeader != null)
  3904. seHUB.unsubscribeInternal(componentID, msg.additionalHeader.subId);
  3905. break;
  3906. case smash.SECommMessage.CONNECT:
  3907. clearInterval(reconnectTimerProcess);
  3908. // switch the state to loaded in the seHUB.
  3909. seHUB.componentLoaded(componentID, msg.payload);
  3910. // send acknowledgement
  3911. var msg = new smash.SECommMessage();
  3912. msg.type = smash.SECommMessage.CONNECT_ACK;
  3913. send(msg.serialize());
  3914. break;
  3915. case smash.SECommMessage.DISCONNECT:
  3916. seHUB.disconnect(componentID);
  3917. // Set a timer which detects if the component reloaded
  3918. // We are using an interval not to lose our evaluation context.
  3919. reconnectTimerProcess = setInterval(reconnectTimeout, loadTimeout);
  3920. // send acknowledgement
  3921. var msg = new smash.SECommMessage();
  3922. msg.type = smash.SECommMessage.DISCONNECT_ACK;
  3923. send(msg.serialize());
  3924. break;
  3925. }
  3926. }
  3927. /**
  3928. * Sends a published message to the partner component
  3929. */
  3930. this.distribute = function(topic, matchingSubs, payload) {
  3931. var msg = new smash.SECommMessage();
  3932. msg.type = smash.SECommMessage.DISTRIBUTE;
  3933. msg.topic = topic;
  3934. msg.additionalHeader = {s: matchingSubs};
  3935. if ((typeof payload) == "string") {
  3936. msg.additionalHeader.f = "S";
  3937. msg.payload = payload;
  3938. }
  3939. else {
  3940. msg.additionalHeader.f = "J";
  3941. msg.payload = JSON.stringify(payload);
  3942. }
  3943. send(msg.serialize());
  3944. }
  3945. function send(message) {
  3946. // Queue the message if sending or if there is no communication partner yet
  3947. if (initialized == false) {
  3948. queueOut.push(message);
  3949. }
  3950. else {
  3951. commLib.send(message);
  3952. }
  3953. }
  3954. this.log = function(msg) {
  3955. logfunc(msg);
  3956. }
  3957. }
  3958. //Static array which contains the list of the currently loaded instances. The array is indexed by the url of the child component.
  3959. smash.SEComm.instances = [];
  3960. //-----------------------------------------------------------------------------------------------
  3961. /**
  3962. * SEHubClient implementation linking the SEComm together with the component side logic.
  3963. */
  3964. smash.SECommClient = function(clientName, logfunc) {
  3965. // Storing the CommLib used for communicating
  3966. var controllers = [];
  3967. controllers["child"] = this;
  3968. var commLib = new smash.CommLib(true, controllers, clientName);
  3969. // 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
  3970. var that = this;
  3971. // A queue for outgoing messages. This queue is used when new send requests are done while we are still sending or receiving a message.
  3972. var queueOut = [];
  3973. // keeps track of the initialization
  3974. var initialized = false;
  3975. var securityListener = null;
  3976. var jsonPayloadHeader = {f: "J"};
  3977. var stringPayloadHeader = {f: "S"};
  3978. var parsedTunnelUrl = null;
  3979. /**
  3980. * Publishes a message to a certain topic
  3981. * @param topic string
  3982. * @param data JSON|string
  3983. */
  3984. this.publish = function(topic, data) {
  3985. var msg = new smash.SECommMessage();
  3986. msg.type = smash.SECommMessage.PUBLISH;
  3987. msg.topic = topic;
  3988. if ((typeof data) == "string") {
  3989. msg.additionalHeader = stringPayloadHeader;
  3990. msg.payload = data;
  3991. }
  3992. else {
  3993. msg.additionalHeader = jsonPayloadHeader;
  3994. msg.payload = JSON.stringify(data);
  3995. }
  3996. send(msg.serialize());
  3997. }
  3998. /**
  3999. * subscribes to a certain topic
  4000. */
  4001. this.subscribe = function(subId, topic) {
  4002. var msg = new smash.SECommMessage();
  4003. msg.type = smash.SECommMessage.SUBSCRIBE;
  4004. msg.topic = topic;
  4005. msg.additionalHeader = {subId: subId};
  4006. send(msg.serialize());
  4007. }
  4008. this.connect = function(callback) {
  4009. if (initialized) {
  4010. var msg = new smash.SECommMessage();
  4011. msg.type = smash.SECommMessage.CONNECT;
  4012. msg.payload = window.location.href.split("#")[0];
  4013. send(msg.serialize());
  4014. return;
  4015. }
  4016. connectCallback = callback;
  4017. }
  4018. this.disconnect = function() {
  4019. var msg = new smash.SECommMessage();
  4020. msg.type = smash.SECommMessage.DISCONNECT;
  4021. send(msg.serialize());
  4022. }
  4023. /**
  4024. * Called when the initialisaiton of the library is done and processes all messages in the queue
  4025. */
  4026. this.initializationFinished = function(tunnelUrl) {
  4027. this.log("Initialization finished. Processing outgoing queue.");
  4028. parsedTunnelUrl = new ParsedUrl(tunnelUrl);
  4029. initialized = true;
  4030. connectCallback(true);
  4031. while (queueOut.length > 0)commLib.send(queueOut.shift());
  4032. }
  4033. this.getParsedTunnelUrl = function() {
  4034. return parsedTunnelUrl;
  4035. }
  4036. var _regex = new RegExp("^((http|https):)?(//([^/?#:]*))?(:([0-9]*))?([^?#]*)(\\?([^#]*))?");
  4037. function ParsedUrl(url) {
  4038. var matchedurl = url.match(_regex);
  4039. this.scheme = (matchedurl[2] == "") ? null : matchedurl[2];
  4040. this.host = (matchedurl[4] == "") ? null : matchedurl[4];
  4041. this.port = (matchedurl[6] == "") ? null : matchedurl[6];
  4042. this.path = (matchedurl[7] == "") ? null : matchedurl[7];
  4043. this.query = (matchedurl[8] == "") ? null : matchedurl[8];
  4044. }
  4045. /**
  4046. * unsubscribes
  4047. */
  4048. this.unsubscribe = function(subId) {
  4049. var msg = new smash.SECommMessage();
  4050. msg.type = smash.SECommMessage.UNSUBSCRIBE;
  4051. msg.additionalHeader = {subId: subId};
  4052. send(msg.serialize());
  4053. }
  4054. function send(message) {
  4055. // Queue the message if sending or if there is no communication partner yet
  4056. if (initialized == false) {
  4057. queueOut.push(message);
  4058. }
  4059. else {
  4060. commLib.send(message);
  4061. }
  4062. }
  4063. /**
  4064. * Function processing the incomming data from commLib
  4065. *
  4066. * @param message The message containing the incomming data
  4067. */
  4068. this.messageReceived = function (message) {
  4069. var msg = new smash.SECommMessage();
  4070. msg.deserialize(message);
  4071. // parse the JSON payload
  4072. if (msg.type == smash.SECommMessage.DISTRIBUTE) {
  4073. var header = msg.additionalHeader;
  4074. if ((header != null) && (header.f == "J"))
  4075. msg.payload = JSON.parse(msg.payload);
  4076. }
  4077. //For now, pass all messages to handleIncomingMessage()
  4078. //if ((msg.type == smash.SECommMessage.DISTRIBUTE) || (msg.type == smash.SECommMessage.SUBSCRIBE_ACK))
  4079. that.handleIncomingMessage(msg);
  4080. }
  4081. this.handleSecurityError = function (error) {
  4082. if (securityListener == null) {
  4083. throw new Error(error);
  4084. }
  4085. else {
  4086. securityListener(error, clientName);
  4087. }
  4088. return;
  4089. }
  4090. /**
  4091. * Sets the callback for security errors.
  4092. *
  4093. * @param The callback for security errors.
  4094. */
  4095. this.setSecurityListener = function(callback) {
  4096. securityListener = callback;
  4097. }
  4098. /**
  4099. * This method is the location for the callback to the SECommClient library.
  4100. * The application using this library overrides this method with its own implementation.
  4101. * HACK: this is terrible from a layering perspective. Ideally all message formatting details, such
  4102. * as header formats should be handled at this layer alone.
  4103. * The default behavior is to alert a message.
  4104. *
  4105. * @param message The actual message.
  4106. */
  4107. this.handleIncomingMessage = function(message) {
  4108. alert("SECommClient\n\nTopic: " + message.topic + "\n\nPayload: " + message.payload);
  4109. }
  4110. this.log = function(msg) {
  4111. logfunc(msg);
  4112. }
  4113. }
  4114. /**
  4115. * Provides the low level communication layer.
  4116. * @param child (boolean) indicating if this is a child iframe or not.
  4117. * @param controllers (object []) an array indexed by the clientName of objects implementing the controller interface.
  4118. * @param clientName - only explicitly passed for the child iframe
  4119. *
  4120. * controller.messageReceived - called when the commlib recieves an incomming message.
  4121. * controller.initializationFinished - called when the commlib finished its initialzation.
  4122. * controller.handleSecurityError - called when a security error occurs.
  4123. *
  4124. */
  4125. smash.CommLib = function(child, controllers, clientName) {
  4126. /**BEGIN of communcation protocol **/
  4127. /*
  4128. Message format:
  4129. | Message Type | Message Sequence Number | Security Token Parent | Security Token Child | ACK | ACK Message Sequence Number | Payload |
  4130. | 1 character | 2 characters | x characters | x characters | 1 character | 2 characters | varable length |
  4131. */
  4132. // Init message payload=communication partner url
  4133. var INIT = "1";
  4134. // An ack message without any payload. The reciever is not supposed to ack this message therefore the message sequence number will be 00.
  4135. var ACK = "2";
  4136. // 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.
  4137. var PART = "3";
  4138. // The end message indicates that this is the last part of a split up message. The full message has arrived after processing this message.
  4139. var END = "4";
  4140. /** END of communcation protocol **/
  4141. // 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
  4142. var that = this;
  4143. // polling and queue processing interval
  4144. var interval = 100;
  4145. // The maximul length of a URL. If the message is longer it will be split into different parts.
  4146. var urlLimit = 4000;
  4147. // Protocol overhead excluding security token overhead
  4148. var protocolOverhead = 6;
  4149. // Need to do an acknowledgement
  4150. var ack = 0;
  4151. // Raw incoming data
  4152. var currentHash = null;
  4153. // The newly decoded incoming message
  4154. var messageIn = null;
  4155. // The last decoded incoming message
  4156. var previousIn = null;
  4157. // The currently transmitted message
  4158. var messageOut = null;
  4159. // The previously transmitted message
  4160. var previousOut = null;
  4161. // The url of the partner
  4162. var partnerURL = null;
  4163. // The window object of the partner
  4164. var partnerWindow = null;
  4165. // A queue for outgoing messages. This queue is used when new send requests are done while we are still sending or recieving a message.
  4166. var queueOut = [];
  4167. // Storing the last sent message number
  4168. var msn = 00;
  4169. // Buffer for storing the incoming message parts
  4170. var messageBuffer = "";
  4171. // Variable storing the timerId of the message timer.
  4172. var timerId = null;
  4173. // Two security tokens - One created by the parent frame (the manager) and one by the child frame (the client)
  4174. var securityTokenParent = null;
  4175. var securityTokenChild = null;
  4176. //
  4177. var controller = null;
  4178. var logQ = [];
  4179. /**
  4180. * Sends a message to the communication partner
  4181. * @param message (string) the message that needs to be delivered to the communication partner
  4182. */
  4183. this.send = function(message) {
  4184. // check if we are properly initialized
  4185. if (partnerURL == null) {
  4186. log("Trying to send without proper initialization. Message will be discarded. " + message);
  4187. return;
  4188. }
  4189. log("Sending: " + message);
  4190. // URL encode the message
  4191. // var encodedMessage=encodeURIComponent(message);
  4192. var encodedMessage = message;
  4193. // determine the payload size
  4194. var payloadLength = urlLimit - protocolOverhead - smash._securityTokenOverhead - partnerURL.length;
  4195. // DEBUG LARGE MESSAGES
  4196. //if(oah_ifr_debug)payloadLength=1;
  4197. // Split up into separate messages if necessary
  4198. var currentMessage = encodedMessage;
  4199. while (currentMessage.length > 0) {
  4200. // split up and put in output queue
  4201. var part = currentMessage.substr(0, payloadLength);
  4202. currentMessage = currentMessage.substr(payloadLength);
  4203. if (currentMessage == 0) {
  4204. queueOut.push({type: END, payload: part});
  4205. }
  4206. else {
  4207. queueOut.push({type: PART, payload: part});
  4208. }
  4209. }
  4210. }
  4211. /**
  4212. * The timer triggering the flow of messages through the system.
  4213. */
  4214. function messageTimer() {
  4215. // check if there is a new message
  4216. if (checkMessage()) {
  4217. // check if it can be decoded properly
  4218. if (decodeMessage()) {
  4219. // check if it is conform the security requirements
  4220. if (checkSecurity()) {
  4221. // process it
  4222. processMessage();
  4223. }
  4224. }
  4225. }
  4226. // Only sent if an ack was received for the last transmitted message.
  4227. if (checkAck()) {
  4228. // send anything that might be in the out queue
  4229. sendMessage();
  4230. }
  4231. }
  4232. /**
  4233. * Returns true if the previously transmitted message was acknowledged.
  4234. *
  4235. * Possible exception situations to take into account:
  4236. * - One of the parties takes two turns in a row.
  4237. * p p c
  4238. * c p1 -
  4239. * p p1'
  4240. *
  4241. * c p p c
  4242. * c ac1 p1
  4243. * p c1 p1'
  4244. *
  4245. */
  4246. function checkAck() {
  4247. // No ack is expected for an ack.
  4248. if (previousOut.type == ACK)return true;
  4249. // Ack is received.
  4250. if ((previousOut.msn == messageIn.ackMsn) && (messageIn.ack == 1)) return true;
  4251. // Wait for the ack to arrive.
  4252. log("Waiting for ACK : " + previousOut.msn);
  4253. return false;
  4254. }
  4255. /**
  4256. * Helper method providing a new message sequence number
  4257. * @returns (string) the new sequence number
  4258. */
  4259. function getNewMsn() {
  4260. msn++;
  4261. if (msn == 100) msn = 0;
  4262. if (msn < 10) return "0" + msn;
  4263. return "" + msn;
  4264. }
  4265. /**
  4266. * Checks the information after the hash to see if there is a new incomming message.
  4267. */
  4268. function checkMessage() {
  4269. //Can't use location.hash because at least Firefox does a decodeURIComponent on it.
  4270. var urlParts = window.location.href.split("#");
  4271. if (urlParts.length == 2) {
  4272. var newHash = urlParts[1];
  4273. if (newHash != "" && newHash != currentHash) {
  4274. currentHash = newHash;
  4275. return true;
  4276. }
  4277. }
  4278. return false;
  4279. }
  4280. /**
  4281. * Decodes an incomming message and checks to see if it is syntactially valid.
  4282. */
  4283. function decodeMessage() {
  4284. //new RegExp( "(\\d)(\\d{2})(.{" + smash._securityTokenLength + "})(.{" + smash._securityTokenLength + "})(\\d)(\\d{2})(.*)" )
  4285. var type = currentHash.substr(0, 1);
  4286. var msn = currentHash.substr(1, 2);
  4287. var nextStart = 3;
  4288. var tokenParent = currentHash.substr(nextStart, smash._securityTokenLength);
  4289. nextStart += smash._securityTokenLength;
  4290. var tokenChild = currentHash.substr(nextStart, smash._securityTokenLength);
  4291. nextStart += smash._securityTokenLength;
  4292. var ack = currentHash.substr(nextStart, 1);
  4293. nextStart += 1;
  4294. var ackMsn = currentHash.substr(nextStart, 2);
  4295. nextStart += 2;
  4296. // The payload needs to stay raw since the uri decoding needs to happen on the concatenated data in case of a large message
  4297. var payload = currentHash.substr(nextStart);
  4298. log("In : Type: " + type + " msn: " + msn + " tokenParent: " + tokenParent + " tokenChild: " + tokenChild + " ack: " + ack + " msn: " + ackMsn + " payload: " + payload);
  4299. messageIn = {type: type, msn: msn, tokenParent: tokenParent, tokenChild: tokenChild, ack: ack, ackMsn: ackMsn, payload: payload};
  4300. return true;
  4301. }
  4302. /**
  4303. * Check if there have been any security breaches in the message.
  4304. */
  4305. function checkSecurity() {
  4306. // Check the security tokens
  4307. if (messageIn.type != INIT && (messageIn.tokenParent != securityTokenParent || messageIn.tokenChild != securityTokenChild)) {
  4308. log("Security token error: Invalid security token received. The message will be discarded.");
  4309. handleSecurityError(smash.SecurityErrors.INVALID_TOKEN);
  4310. return false;
  4311. }
  4312. // Attacks should never pass the security check. Code below is to debug the implementation.
  4313. //if(oah_ifr_debug){
  4314. // if (messageIn.type!=INIT && messageIn.type!=ACK && messageIn.type!=PART && messageIn.type!=END){
  4315. // if(oah_ifr_debug)debug("Syntax error: Message Type. The message will be discarded.");
  4316. // return false;
  4317. // }
  4318. // if (!(messageIn.msn>=0 && messageIn.msn<=99)){
  4319. // if(oah_ifr_debug)debug("Syntax error: Message Sequence Number. The message will be discarded.");
  4320. // return false;
  4321. // }
  4322. // if (!(messageIn.ack==0 || messageIn.ack==1)){
  4323. // if(oah_ifr_debug)debug("Syntax error: ACK. The message will be discarded.");
  4324. // return false;
  4325. // }
  4326. // if (!(messageIn.ackMsn>=0 && messageIn.ackMsn<=99)){
  4327. // if(oah_ifr_debug)debug("Syntax error: ACK Message Sequence Number. The message will be discarded.");
  4328. // return false;
  4329. // }
  4330. //}
  4331. return true;
  4332. }
  4333. /**
  4334. * Process the incoming message.
  4335. */
  4336. function processMessage() {
  4337. ack = 1;
  4338. // The child is initialized as soon as there is an ack for the init message sent by the child.
  4339. if (messageIn.type != INIT && child && previousOut.type == INIT && messageIn.ack == "1" && previousOut.msn == messageIn.ackMsn) {
  4340. controller.initializationFinished(partnerURL);
  4341. }
  4342. // Call the actual processing functions
  4343. switch (messageIn.type) {
  4344. case INIT:
  4345. processInit();
  4346. break;
  4347. case ACK:
  4348. processAck();
  4349. break;
  4350. case PART:
  4351. processPart();
  4352. break;
  4353. case END:
  4354. processEnd();
  4355. break;
  4356. }
  4357. // Set the processed message as the previousIn message
  4358. previousIn = messageIn;
  4359. }
  4360. /**
  4361. * Implementation of the INIT message type
  4362. **/
  4363. function processInit() {
  4364. var parts = messageIn.payload.split(":");
  4365. var cname = decodeURIComponent(parts[0]);
  4366. partnerURL = decodeURIComponent(parts[1]);
  4367. securityTokenParent = messageIn.tokenParent;
  4368. securityTokenChild = messageIn.tokenChild;
  4369. // Initialize a component
  4370. if (child) {
  4371. if (clientName != null) cname = clientName; // override what is read from the URL
  4372. // generate a real security token for the child
  4373. securityTokenChild = smash._generateSecurityToken();
  4374. // GUI which will be used to name the iFrame tunnel.
  4375. var tunnelGUID = "3827816c-f3b1-11db-8314-0800200c9a66";
  4376. // Generate the hidden iframe for communicating
  4377. var iframe = document.createElement("iframe");
  4378. var currentClientURI = encodeURIComponent(window.location.href.split("#")[0]);
  4379. var initialClientURI = currentClientURI;
  4380. // if (smash._initialClientURI) {
  4381. // initialClientURI = encodeURIComponent(smash._initialClientURI);
  4382. // }
  4383. var initpayload = encodeURIComponent(cname) + ":" + currentClientURI + ":" + initialClientURI;
  4384. // sending an ack for msn "00" to the tunnel, since have processed the INIT message,
  4385. // and so that the INIT message the component is sending to the tunnel will result
  4386. // in an ack to be sent back.
  4387. // XXX Since server redirection breaks hash communication (the server does
  4388. // not receive the fragment value, therefore the final URL does not contain
  4389. // this information), the initial message is transmitted as a URL param.
  4390. partnerURL += (partnerURL.indexOf("?") != -1 ? "&" : "?") + "oahm=100" + securityTokenParent + securityTokenChild + "100" + initpayload;
  4391. iframe.src = partnerURL;
  4392. iframe.name = tunnelGUID;
  4393. iframe.id = tunnelGUID;
  4394. document.body.appendChild(iframe);
  4395. iframe.style.position = "absolute";
  4396. iframe.style.left = iframe.style.top = "-10px";
  4397. iframe.style.height = iframe.style.width = "1px";
  4398. iframe.style.visibility = "hidden";
  4399. // We do not send an ack directly to the parent frame since it is impossible to directly communicate with it in IE7
  4400. // The ack is done indirectly when the registerTunnelCommLib is done
  4401. ack = 0;
  4402. // set up the partner window
  4403. partnerWindow = window.frames[tunnelGUID];
  4404. // store the last sent message - will be used to detect intialization and for detecting security breaches
  4405. 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
  4406. // set the controller for this component
  4407. controller = controllers["child"];
  4408. }
  4409. // Initialize a tunnel
  4410. else {
  4411. var initialClientURI = decodeURIComponent(parts[2]);
  4412. // set up the partner window
  4413. partnerWindow = window.parent;
  4414. // set the controller for this component
  4415. controller = controllers[cname];
  4416. var success = controller.initializationFinished(that, securityTokenParent, partnerURL, initialClientURI, window);
  4417. if (!success) ack = 0; // don't send an ack signalling the completion of connection setup.
  4418. // store the last sent message - will be used to detect intialization and for detecting security breaches
  4419. 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
  4420. }
  4421. if (partnerWindow == null) {
  4422. log("Init failed.");
  4423. }
  4424. }
  4425. /**
  4426. * Implementation of the ACK message type
  4427. **/
  4428. function processAck() {
  4429. // do not ack an ack
  4430. ack = 0;
  4431. }
  4432. /**
  4433. * Implementation of the PART message type
  4434. **/
  4435. function processPart() {
  4436. // Process message
  4437. messageBuffer += messageIn.payload;
  4438. }
  4439. /**
  4440. * Implementation the END message type
  4441. **/
  4442. function processEnd() {
  4443. // Process message
  4444. messageBuffer += messageIn.payload;
  4445. // messageBuffer=decodeURIComponent(messageBuffer);
  4446. log("Received: " + messageBuffer);
  4447. controller.messageReceived(messageBuffer);
  4448. messageBuffer = "";
  4449. }
  4450. /**
  4451. * Send a reply to the incoming message.
  4452. */
  4453. function sendMessage() {
  4454. // If there is nothing in the queue and an ack needs to be sent put the ack on the queue;
  4455. if (queueOut.length == 0 && ack == 1) {
  4456. // The correct values will be filled in later. Just push a clean ack message
  4457. queueOut.push({type: ACK, payload: ""});
  4458. }
  4459. // Process the output queue
  4460. if (queueOut.length != 0) {
  4461. messageOut = queueOut.shift();
  4462. // Fill in the security token
  4463. messageOut.tokenParent = securityTokenParent;
  4464. messageOut.tokenChild = securityTokenChild;
  4465. // Get a new sequence number
  4466. messageOut.msn = getNewMsn();
  4467. // Fill in the right ack values
  4468. // The protocol keeps acking the last received message to ensure that there are no
  4469. // problems with overwriting a pure ack message. Which could happen because there is
  4470. // no waiting for an ack of an ack.
  4471. messageOut.ack = "1";
  4472. messageOut.ackMsn = previousIn.msn;
  4473. // turn of the ack
  4474. ack = 0;
  4475. writeToPartnerWindow();
  4476. }
  4477. }
  4478. /**
  4479. * Writes the message to the partner window's fragment id
  4480. **/
  4481. function writeToPartnerWindow() {
  4482. var url = partnerURL + "#" + messageOut.type + messageOut.msn + messageOut.tokenParent + messageOut.tokenChild + messageOut.ack + messageOut.ackMsn + messageOut.payload;
  4483. partnerWindow.location.replace(url);
  4484. previousOut = messageOut;
  4485. log("Out: Type: " + messageOut.type + " msn: " + messageOut.msn + " tokenParent: " + messageOut.tokenParent + " tokenChild: " + messageOut.tokenChild + " ack: " + messageOut.ack + " msn: " + messageOut.ackMsn + " payload: " + messageOut.payload);
  4486. }
  4487. /**
  4488. * Default handler of the security listener. If a security error occurs, the CommLib is switched off. And communication is no longer possible.
  4489. *
  4490. */
  4491. function handleSecurityError(error) {
  4492. // Stop the communication
  4493. clearInterval(timerId);
  4494. // If there is a securityListener inform the controller of what happened.
  4495. controller.handleSecurityError(error);
  4496. }
  4497. function log(msg) {
  4498. if (controller) {
  4499. while (logQ.length > 0) {
  4500. controller.log(logQ.shift());
  4501. }
  4502. controller.log(msg);
  4503. } else {
  4504. logQ.push(msg);
  4505. }
  4506. }
  4507. // Start listening for incoming messages
  4508. timerId = setInterval(messageTimer, interval);
  4509. };
  4510. ////////////////////////////////////////////////////////////////////////////////
  4511. /*
  4512. Copyright 2006-2009 OpenAjax Alliance
  4513. Licensed under the Apache License, Version 2.0 (the "License");
  4514. you may not use this file except in compliance with the License.
  4515. You may obtain a copy of the License at
  4516. http://www.apache.org/licenses/LICENSE-2.0
  4517. Unless required by applicable law or agreed to in writing, software
  4518. distributed under the License is distributed on an "AS IS" BASIS,
  4519. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  4520. See the License for the specific language governing permissions and
  4521. limitations under the License.
  4522. */
  4523. //SMASH.CRYPTO
  4524. //
  4525. //Small library containing some minimal crypto functionality for a
  4526. //- a hash-function: SHA-1 (see FIPS PUB 180-2 for definition)
  4527. //BigEndianWord[5] <- smash.crypto.sha1( BigEndianWord[*] dataWA, int lenInBits)
  4528. //
  4529. //- a message authentication code (MAC): HMAC-SHA-1 (RFC2104/2202)
  4530. //BigEndianWord[5] <- smash.crypto.hmac_sha1(
  4531. // BigEndianWord[3-16] keyWA,
  4532. // Ascii or Unicode string dataS,
  4533. // int chrsz (8 for Asci/16 for Unicode)
  4534. //
  4535. //- pseudo-random number generator (PRNG): HMAC-SHA-1 in counter mode, following
  4536. //Barak & Halevi, An architecture for robust pseudo-random generation and applications to /dev/random, CCS 2005
  4537. //rngObj <- smash.crypto.newPRNG( String[>=12] seedS)
  4538. //where rngObj has methods
  4539. //addSeed(String seed)
  4540. //BigEndianWord[len] <- nextRandomOctets(int len)
  4541. //Base64-String[len] <- nextRandomB64Str(int len)
  4542. //Note: HMAC-SHA1 in counter-mode does not provide forward-security on corruption.
  4543. // However, the PRNG state is kept inside a closure. So if somebody can break the closure, he probably could
  4544. // break a whole lot more and forward-security of the prng is not the highest of concerns anymore :-)
  4545. if (typeof(smash) == 'undefined') {
  4546. var smash = {};
  4547. }
  4548. smash.crypto = {
  4549. // Some utilities
  4550. // convert a string to an array of big-endian words
  4551. 'strToWA': function (/* Ascii or Unicode string */ str, /* int 8 for Asci/16 for Unicode */ chrsz) {
  4552. var bin = Array();
  4553. var mask = (1 << chrsz) - 1;
  4554. for (var i = 0; i < str.length * chrsz; i += chrsz)
  4555. bin[i >> 5] |= (str.charCodeAt(i / chrsz) & mask) << (32 - chrsz - i % 32);
  4556. return bin;
  4557. },
  4558. // MAC
  4559. 'hmac_sha1' : function(
  4560. /* BigEndianWord[3-16]*/ keyWA,
  4561. /* Ascii or Unicode string */ dataS,
  4562. /* int 8 for Asci/16 for Unicode */ chrsz) {
  4563. // write our own hmac derived from paj's so we do not have to do constant key conversions and length checking ...
  4564. var ipad = Array(16), opad = Array(16);
  4565. for (var i = 0; i < 16; i++) {
  4566. ipad[i] = keyWA[i] ^ 0x36363636;
  4567. opad[i] = keyWA[i] ^ 0x5C5C5C5C;
  4568. }
  4569. var hash = this.sha1(ipad.concat(this.strToWA(dataS, chrsz)), 512 + dataS.length * chrsz);
  4570. return this.sha1(opad.concat(hash), 512 + 160);
  4571. },
  4572. // PRNG factory method
  4573. // see below 'addSeed', 'nextRandomOctets' & 'nextRandomB64Octets' for public methods of returnd prng object
  4574. 'newPRNG' : function (/* String[>=12] */ seedS) {
  4575. that = this;
  4576. // parameter checking
  4577. // We cannot really verify entropy but obviously the string must have at least a minimal length to have enough entropy
  4578. // However, a 2^80 security seems ok, so we check only that at least 12 chars assuming somewhat random ASCII
  4579. if ((typeof seedS != 'string') || (seedS.length < 12)) {
  4580. alert("WARNING: Seed length too short ...");
  4581. }
  4582. // constants
  4583. var __refresh_keyWA = [ 0xA999, 0x3E36, 0x4706, 0x816A,
  4584. 0x2571, 0x7850, 0xC26C, 0x9CD0,
  4585. 0xBA3E, 0xD89D, 0x1233, 0x9525,
  4586. 0xff3C, 0x1A83, 0xD491, 0xFF15 ]; // some random key for refresh ...
  4587. // internal state
  4588. var _keyWA = []; // BigEndianWord[5]
  4589. var _cnt = 0; // int
  4590. function extract(seedS) {
  4591. return that.hmac_sha1(__refresh_keyWA, seedS, 8);
  4592. }
  4593. function refresh(seedS) {
  4594. // HMAC-SHA1 is not ideal, Rijndal 256bit block/key in CBC mode with fixed key might be better
  4595. // but to limit the primitives and given that we anyway have only limited entropy in practise
  4596. // this seems good enough
  4597. var uniformSeedWA = extract(seedS);
  4598. for (var i = 0; i < 5; i++) {
  4599. _keyWA[i] ^= uniformSeedWA[i];
  4600. }
  4601. }
  4602. // inital state seeding
  4603. refresh(seedS);
  4604. // public methods
  4605. return {
  4606. // Mix some additional seed into the PRNG state
  4607. 'addSeed' : function (/* String */ seed) {
  4608. // no parameter checking. Any added entropy should be fine ...
  4609. refresh(seed);
  4610. },
  4611. // Get an array of len random octets
  4612. 'nextRandomOctets' : /* BigEndianWord[len] <- */ function (/* int */ len) {
  4613. var randOctets = [];
  4614. while (len > 0) {
  4615. _cnt += 1;
  4616. var nextBlock = that.hmac_sha1(_keyWA, (_cnt).toString(16), 8);
  4617. for (i = 0; (i < 20) & (len > 0); i++,len--) {
  4618. randOctets.push((nextBlock[i >> 2] >> (i % 4) ) % 256);
  4619. }
  4620. // Note: if len was not a multiple 20, some random octets are ignored here but who cares ..
  4621. }
  4622. return randOctets;
  4623. },
  4624. // Get a random string of Base64-like (see below) chars of length len
  4625. // Note: there is a slightly non-standard Base64 with no padding and '-' and '_' for '+' and '/', respectively
  4626. 'nextRandomB64Str' : /* Base64-String <- */ function (/* int */ len) {
  4627. var b64StrMap = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
  4628. var randOctets = this.nextRandomOctets(len);
  4629. var randB64Str = '';
  4630. for (var i = 0; i < len; i++) {
  4631. randB64Str += b64StrMap.charAt(randOctets[i] & 0x3F);
  4632. }
  4633. return randB64Str;
  4634. }
  4635. }
  4636. },
  4637. // Digest function:
  4638. // BigEndianWord[5] <- sha1( BigEndianWord[*] dataWA, int lenInBits)
  4639. 'sha1' : function() {
  4640. // Note: all Section references below refer to FIPS 180-2.
  4641. // private utility functions
  4642. // - 32bit addition with wrap-around
  4643. var add_wa = function (x, y) {
  4644. var lsw = (x & 0xFFFF) + (y & 0xFFFF);
  4645. var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
  4646. return (msw << 16) | (lsw & 0xFFFF);
  4647. }
  4648. // - 32bit rotatate left
  4649. var rol = function(num, cnt) {
  4650. return (num << cnt) | (num >>> (32 - cnt));
  4651. }
  4652. // - round-dependent function f_t from Section 4.1.1
  4653. function sha1_ft(t, b, c, d) {
  4654. if (t < 20) return (b & c) | ((~b) & d);
  4655. if (t < 40) return b ^ c ^ d;
  4656. if (t < 60) return (b & c) | (b & d) | (c & d);
  4657. return b ^ c ^ d;
  4658. }
  4659. // - round-dependent SHA-1 constants from Section 4.2.1
  4660. function sha1_kt(t) {
  4661. return (t < 20) ? 1518500249 :
  4662. (t < 40) ? 1859775393 :
  4663. (t < 60) ? -1894007588 :
  4664. /* (t < 80) */ -899497514;
  4665. }
  4666. // main algorithm.
  4667. return function(/* BigEndianWord[*] */ dataWA, /* int */ lenInBits) {
  4668. // Section 6.1.1: Preprocessing
  4669. //-----------------------------
  4670. // 1. padding: (see also Section 5.1.1)
  4671. // - append one 1 followed by 0 bits filling up 448 bits of last (512bit) block
  4672. dataWA[lenInBits >> 5] |= 0x80 << (24 - lenInBits % 32);
  4673. // - encode length in bits in last 64 bits
  4674. // Note: we rely on javascript to zero file elements which are beyond last (partial) data-block
  4675. // but before this length encoding!
  4676. dataWA[((lenInBits + 64 >> 9) << 4) + 15] = lenInBits;
  4677. // 2. 512bit blocks (actual split done ondemand later)
  4678. var W = Array(80);
  4679. // 3. initial hash using SHA-1 constants on page 13
  4680. var H0 = 1732584193;
  4681. var H1 = -271733879;
  4682. var H2 = -1732584194;
  4683. var H3 = 271733878;
  4684. var H4 = -1009589776;
  4685. // 6.1.2 SHA-1 Hash Computation
  4686. for (var i = 0; i < dataWA.length; i += 16) {
  4687. // 1. Message schedule, done below
  4688. // 2. init working variables
  4689. var a = H0;
  4690. var b = H1;
  4691. var c = H2;
  4692. var d = H3;
  4693. var e = H4;
  4694. // 3. round-functions
  4695. for (var j = 0; j < 80; j++) {
  4696. // postponed step 2
  4697. W[j] = ( (j < 16) ? dataWA[i + j] : rol(W[j - 3] ^ W[j - 8] ^ W[j - 14] ^ W[j - 16], 1));
  4698. var T = add_wa(add_wa(rol(a, 5), sha1_ft(j, b, c, d)),
  4699. add_wa(add_wa(e, W[j]), sha1_kt(j)));
  4700. e = d;
  4701. d = c;
  4702. c = rol(b, 30);
  4703. b = a;
  4704. a = T;
  4705. }
  4706. // 4. intermediate hash
  4707. H0 = add_wa(a, H0);
  4708. H1 = add_wa(b, H1);
  4709. H2 = add_wa(c, H2);
  4710. H3 = add_wa(d, H3);
  4711. H4 = add_wa(e, H4);
  4712. }
  4713. return Array(H0, H1, H2, H3, H4);
  4714. }
  4715. }()
  4716. };
  4717. ////////////////////////////////////////////////////////////////////////////////
  4718. /*
  4719. http://www.JSON.org/json2.js
  4720. 2008-11-19
  4721. Public Domain.
  4722. NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
  4723. See http://www.JSON.org/js.html
  4724. This file creates a global JSON object containing two methods: stringify
  4725. and parse.
  4726. JSON.stringify(value, replacer, space)
  4727. value any JavaScript value, usually an object or array.
  4728. replacer an optional parameter that determines how object
  4729. values are stringified for objects. It can be a
  4730. function or an array of strings.
  4731. space an optional parameter that specifies the indentation
  4732. of nested structures. If it is omitted, the text will
  4733. be packed without extra whitespace. If it is a number,
  4734. it will specify the number of spaces to indent at each
  4735. level. If it is a string (such as '\t' or '&nbsp;'),
  4736. it contains the characters used to indent at each level.
  4737. This method produces a JSON text from a JavaScript value.
  4738. When an object value is found, if the object contains a toJSON
  4739. method, its toJSON method will be called and the result will be
  4740. stringified. A toJSON method does not serialize: it returns the
  4741. value represented by the name/value pair that should be serialized,
  4742. or undefined if nothing should be serialized. The toJSON method
  4743. will be passed the key associated with the value, and this will be
  4744. bound to the object holding the key.
  4745. For example, this would serialize Dates as ISO strings.
  4746. Date.prototype.toJSON = function (key) {
  4747. function f(n) {
  4748. // Format integers to have at least two digits.
  4749. return n < 10 ? '0' + n : n;
  4750. }
  4751. return this.getUTCFullYear() + '-' +
  4752. f(this.getUTCMonth() + 1) + '-' +
  4753. f(this.getUTCDate()) + 'T' +
  4754. f(this.getUTCHours()) + ':' +
  4755. f(this.getUTCMinutes()) + ':' +
  4756. f(this.getUTCSeconds()) + 'Z';
  4757. };
  4758. You can provide an optional replacer method. It will be passed the
  4759. key and value of each member, with this bound to the containing
  4760. object. The value that is returned from your method will be
  4761. serialized. If your method returns undefined, then the member will
  4762. be excluded from the serialization.
  4763. If the replacer parameter is an array of strings, then it will be
  4764. used to select the members to be serialized. It filters the results
  4765. such that only members with keys listed in the replacer array are
  4766. stringified.
  4767. Values that do not have JSON representations, such as undefined or
  4768. functions, will not be serialized. Such values in objects will be
  4769. dropped; in arrays they will be replaced with null. You can use
  4770. a replacer function to replace those with JSON values.
  4771. JSON.stringify(undefined) returns undefined.
  4772. The optional space parameter produces a stringification of the
  4773. value that is filled with line breaks and indentation to make it
  4774. easier to read.
  4775. If the space parameter is a non-empty string, then that string will
  4776. be used for indentation. If the space parameter is a number, then
  4777. the indentation will be that many spaces.
  4778. Example:
  4779. text = JSON.stringify(['e', {pluribus: 'unum'}]);
  4780. // text is '["e",{"pluribus":"unum"}]'
  4781. text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
  4782. // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
  4783. text = JSON.stringify([new Date()], function (key, value) {
  4784. return this[key] instanceof Date ?
  4785. 'Date(' + this[key] + ')' : value;
  4786. });
  4787. // text is '["Date(---current time---)"]'
  4788. JSON.parse(text, reviver)
  4789. This method parses a JSON text to produce an object or array.
  4790. It can throw a SyntaxError exception.
  4791. The optional reviver parameter is a function that can filter and
  4792. transform the results. It receives each of the keys and values,
  4793. and its return value is used instead of the original value.
  4794. If it returns what it received, then the structure is not modified.
  4795. If it returns undefined then the member is deleted.
  4796. Example:
  4797. // Parse the text. Values that look like ISO date strings will
  4798. // be converted to Date objects.
  4799. myData = JSON.parse(text, function (key, value) {
  4800. var a;
  4801. if (typeof value === 'string') {
  4802. a =
  4803. /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
  4804. if (a) {
  4805. return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
  4806. +a[5], +a[6]));
  4807. }
  4808. }
  4809. return value;
  4810. });
  4811. myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
  4812. var d;
  4813. if (typeof value === 'string' &&
  4814. value.slice(0, 5) === 'Date(' &&
  4815. value.slice(-1) === ')') {
  4816. d = new Date(value.slice(5, -1));
  4817. if (d) {
  4818. return d;
  4819. }
  4820. }
  4821. return value;
  4822. });
  4823. This is a reference implementation. You are free to copy, modify, or
  4824. redistribute.
  4825. This code should be minified before deployment.
  4826. See http://javascript.crockford.com/jsmin.html
  4827. USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
  4828. NOT CONTROL.
  4829. */
  4830. /*jslint evil: true */
  4831. /*global JSON */
  4832. /*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
  4833. call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
  4834. getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
  4835. lastIndex, length, parse, prototype, push, replace, slice, stringify,
  4836. test, toJSON, toString, valueOf
  4837. */
  4838. //Create a JSON object only if one does not already exist. We create the
  4839. //methods in a closure to avoid creating global variables.
  4840. if (!this.JSON) {
  4841. JSON = {};
  4842. }
  4843. (function () {
  4844. function f(n) {
  4845. // Format integers to have at least two digits.
  4846. return n < 10 ? '0' + n : n;
  4847. }
  4848. if (typeof Date.prototype.toJSON !== 'function') {
  4849. Date.prototype.toJSON = function (key) {
  4850. return this.getUTCFullYear() + '-' +
  4851. f(this.getUTCMonth() + 1) + '-' +
  4852. f(this.getUTCDate()) + 'T' +
  4853. f(this.getUTCHours()) + ':' +
  4854. f(this.getUTCMinutes()) + ':' +
  4855. f(this.getUTCSeconds()) + 'Z';
  4856. };
  4857. String.prototype.toJSON =
  4858. Number.prototype.toJSON =
  4859. Boolean.prototype.toJSON = function (key) {
  4860. return this.valueOf();
  4861. };
  4862. }
  4863. var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
  4864. escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
  4865. gap,
  4866. indent,
  4867. meta = { // table of character substitutions
  4868. '\b': '\\b',
  4869. '\t': '\\t',
  4870. '\n': '\\n',
  4871. '\f': '\\f',
  4872. '\r': '\\r',
  4873. '"' : '\\"',
  4874. '\\': '\\\\'
  4875. },
  4876. rep;
  4877. function quote(string) {
  4878. //If the string contains no control characters, no quote characters, and no
  4879. //backslash characters, then we can safely slap some quotes around it.
  4880. //Otherwise we must also replace the offending characters with safe escape
  4881. //sequences.
  4882. escapable.lastIndex = 0;
  4883. return escapable.test(string) ?
  4884. '"' + string.replace(escapable, function (a) {
  4885. var c = meta[a];
  4886. return typeof c === 'string' ? c :
  4887. '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
  4888. }) + '"' :
  4889. '"' + string + '"';
  4890. }
  4891. function str(key, holder) {
  4892. //Produce a string from holder[key].
  4893. var i, // The loop counter.
  4894. k, // The member key.
  4895. v, // The member value.
  4896. length,
  4897. mind = gap,
  4898. partial,
  4899. value = holder[key];
  4900. //If the value has a toJSON method, call it to obtain a replacement value.
  4901. if (value && typeof value === 'object' &&
  4902. typeof value.toJSON === 'function') {
  4903. value = value.toJSON(key);
  4904. }
  4905. //If we were called with a replacer function, then call the replacer to
  4906. //obtain a replacement value.
  4907. if (typeof rep === 'function') {
  4908. value = rep.call(holder, key, value);
  4909. }
  4910. //What happens next depends on the value's type.
  4911. switch (typeof value) {
  4912. case 'string':
  4913. return quote(value);
  4914. case 'number':
  4915. //JSON numbers must be finite. Encode non-finite numbers as null.
  4916. return isFinite(value) ? String(value) : 'null';
  4917. case 'boolean':
  4918. case 'null':
  4919. //If the value is a boolean or null, convert it to a string. Note:
  4920. //typeof null does not produce 'null'. The case is included here in
  4921. //the remote chance that this gets fixed someday.
  4922. return String(value);
  4923. //If the type is 'object', we might be dealing with an object or an array or
  4924. //null.
  4925. case 'object':
  4926. //Due to a specification blunder in ECMAScript, typeof null is 'object',
  4927. //so watch out for that case.
  4928. if (!value) {
  4929. return 'null';
  4930. }
  4931. //Make an array to hold the partial results of stringifying this object value.
  4932. gap += indent;
  4933. partial = [];
  4934. //Is the value an array?
  4935. if (Object.prototype.toString.apply(value) === '[object Array]') {
  4936. //The value is an array. Stringify every element. Use null as a placeholder
  4937. //for non-JSON values.
  4938. length = value.length;
  4939. for (i = 0; i < length; i += 1) {
  4940. partial[i] = str(i, value) || 'null';
  4941. }
  4942. //Join all of the elements together, separated with commas, and wrap them in
  4943. //brackets.
  4944. v = partial.length === 0 ? '[]' :
  4945. gap ? '[\n' + gap +
  4946. partial.join(',\n' + gap) + '\n' +
  4947. mind + ']' :
  4948. '[' + partial.join(',') + ']';
  4949. gap = mind;
  4950. return v;
  4951. }
  4952. //If the replacer is an array, use it to select the members to be stringified.
  4953. if (rep && typeof rep === 'object') {
  4954. length = rep.length;
  4955. for (i = 0; i < length; i += 1) {
  4956. k = rep[i];
  4957. if (typeof k === 'string') {
  4958. v = str(k, value);
  4959. if (v) {
  4960. partial.push(quote(k) + (gap ? ': ' : ':') + v);
  4961. }
  4962. }
  4963. }
  4964. } else {
  4965. //Otherwise, iterate through all of the keys in the object.
  4966. for (k in value) {
  4967. if (Object.hasOwnProperty.call(value, k)) {
  4968. v = str(k, value);
  4969. if (v) {
  4970. partial.push(quote(k) + (gap ? ': ' : ':') + v);
  4971. }
  4972. }
  4973. }
  4974. }
  4975. //Join all of the member texts together, separated with commas,
  4976. //and wrap them in braces.
  4977. v = partial.length === 0 ? '{}' :
  4978. gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
  4979. mind + '}' : '{' + partial.join(',') + '}';
  4980. gap = mind;
  4981. return v;
  4982. }
  4983. }
  4984. //If the JSON object does not yet have a stringify method, give it one.
  4985. if (typeof JSON.stringify !== 'function') {
  4986. JSON.stringify = function (value, replacer, space) {
  4987. //The stringify method takes a value and an optional replacer, and an optional
  4988. //space parameter, and returns a JSON text. The replacer can be a function
  4989. //that can replace values, or an array of strings that will select the keys.
  4990. //A default replacer method can be provided. Use of the space parameter can
  4991. //produce text that is more easily readable.
  4992. var i;
  4993. gap = '';
  4994. indent = '';
  4995. //If the space parameter is a number, make an indent string containing that
  4996. //many spaces.
  4997. if (typeof space === 'number') {
  4998. for (i = 0; i < space; i += 1) {
  4999. indent += ' ';
  5000. }
  5001. //If the space parameter is a string, it will be used as the indent string.
  5002. } else if (typeof space === 'string') {
  5003. indent = space;
  5004. }
  5005. //If there is a replacer, it must be a function or an array.
  5006. //Otherwise, throw an error.
  5007. rep = replacer;
  5008. if (replacer && typeof replacer !== 'function' &&
  5009. (typeof replacer !== 'object' ||
  5010. typeof replacer.length !== 'number')) {
  5011. throw new Error('JSON.stringify');
  5012. }
  5013. //Make a fake root object containing our value under the key of ''.
  5014. //Return the result of stringifying the value.
  5015. return str('', {'': value});
  5016. };
  5017. }
  5018. //If the JSON object does not yet have a parse method, give it one.
  5019. if (typeof JSON.parse !== 'function') {
  5020. JSON.parse = function (text, reviver) {
  5021. //The parse method takes a text and an optional reviver function, and returns
  5022. //a JavaScript value if the text is a valid JSON text.
  5023. var j;
  5024. function walk(holder, key) {
  5025. //The walk method is used to recursively walk the resulting structure so
  5026. //that modifications can be made.
  5027. var k, v, value = holder[key];
  5028. if (value && typeof value === 'object') {
  5029. for (k in value) {
  5030. if (Object.hasOwnProperty.call(value, k)) {
  5031. v = walk(value, k);
  5032. if (v !== undefined) {
  5033. value[k] = v;
  5034. } else {
  5035. delete value[k];
  5036. }
  5037. }
  5038. }
  5039. }
  5040. return reviver.call(holder, key, value);
  5041. }
  5042. //Parsing happens in four stages. In the first stage, we replace certain
  5043. //Unicode characters with escape sequences. JavaScript handles many characters
  5044. //incorrectly, either silently deleting them, or treating them as line endings.
  5045. cx.lastIndex = 0;
  5046. if (cx.test(text)) {
  5047. text = text.replace(cx, function (a) {
  5048. return '\\u' +
  5049. ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
  5050. });
  5051. }
  5052. //In the second stage, we run the text against regular expressions that look
  5053. //for non-JSON patterns. We are especially concerned with '()' and 'new'
  5054. //because they can cause invocation, and '=' because it can cause mutation.
  5055. //But just to be safe, we want to reject all unexpected forms.
  5056. //We split the second stage into 4 regexp operations in order to work around
  5057. //crippling inefficiencies in IE's and Safari's regexp engines. First we
  5058. //replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
  5059. //replace all simple value tokens with ']' characters. Third, we delete all
  5060. //open brackets that follow a colon or comma or that begin the text. Finally,
  5061. //we look to see that the remaining characters are only whitespace or ']' or
  5062. //',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
  5063. if (/^[\],:{}\s]*$/.
  5064. test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
  5065. replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
  5066. replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
  5067. //In the third stage we use the eval function to compile the text into a
  5068. //JavaScript structure. The '{' operator is subject to a syntactic ambiguity
  5069. //in JavaScript: it can begin a block or an object literal. We wrap the text
  5070. //in parens to eliminate the ambiguity.
  5071. j = eval('(' + text + ')');
  5072. //In the optional fourth stage, we recursively walk the new structure, passing
  5073. //each name/value pair to a reviver function for possible transformation.
  5074. return typeof reviver === 'function' ?
  5075. walk({'': j}, '') : j;
  5076. }
  5077. //If the text is not JSON parseable, then a SyntaxError is thrown.
  5078. throw new SyntaxError('JSON.parse');
  5079. };
  5080. }
  5081. })();
  5082. ////////////////////////////////////////////////////////////////////////////////
  5083. if (!window["PageBus"])
  5084. window.PageBus = {};
  5085. // Insert a debugger breakpoint in Dev builds of PageBus only. The debugger line should be removed in production builds.
  5086. PageBus._debug = function() {
  5087. // debugger; // REMOVE ON BUILD
  5088. };
  5089. PageBus._esc = function(s) {
  5090. return s.replace(/\./g, "!");
  5091. };
  5092. PageBus._assertPubTopic = function(topic) {
  5093. if ((topic == null) || (topic == "") || (topic.indexOf("*") != -1) || (topic.indexOf("..") != -1) ||
  5094. (topic.charAt(0) == ".") || (topic.charAt(topic.length - 1) == ".")) {
  5095. throw new Error(OpenAjax.hub.Error.BadParameters);
  5096. }
  5097. };
  5098. PageBus._assertSubTopic = function(topic) {
  5099. if ((topic == null) || (topic == ""))
  5100. throw new Error(OpenAjax.hub.Error.BadParameters);
  5101. var path = topic.split(".");
  5102. var len = path.length;
  5103. for (var i = 0; i < len; i++) {
  5104. var p = path[i];
  5105. if ((p == "") ||
  5106. ((p.indexOf("*") != -1) && (p != "*") && (p != "**"))) {
  5107. throw new Error(OpenAjax.hub.Error.BadParameters);
  5108. }
  5109. if ((p == "**") && (i < len - 1)) {
  5110. throw new Error(OpenAjax.hub.Error.BadParameters);
  5111. }
  5112. }
  5113. };
  5114. PageBus._copy = function(obj) {
  5115. var c;
  5116. if (typeof(obj) == "object") {
  5117. if (obj == null)
  5118. return null;
  5119. else if (obj.constructor == Array) {
  5120. c = [];
  5121. for (var i = 0; i < obj.length; i++)
  5122. c[i] = PageBus._copy(obj[i]);
  5123. return c;
  5124. }
  5125. else if (obj.constructor == Date) {
  5126. c = new Date();
  5127. c.setDate(obj.getDate());
  5128. return c;
  5129. }
  5130. c = {};
  5131. for (var p in obj)
  5132. c[p] = PageBus._copy(obj[p]);
  5133. return c;
  5134. }
  5135. else {
  5136. return obj;
  5137. }
  5138. };
  5139. PageBus._TopicMatcher = function() {
  5140. this._items = {};
  5141. };
  5142. PageBus._TopicMatcher.prototype.store = function(topic, val) {
  5143. var path = topic.split(".");
  5144. var len = path.length;
  5145. _recurse = function(tree, index) {
  5146. if (index == len)
  5147. tree["."] = { topic: topic, value: val };
  5148. else {
  5149. var token = path[index];
  5150. if (!tree[token])
  5151. tree[token] = {};
  5152. _recurse(tree[token], index + 1);
  5153. }
  5154. };
  5155. _recurse(this._items, 0);
  5156. };
  5157. PageBus._TopicMatcher.prototype.match = function(topic, exactMatch) {
  5158. var path = topic.split(".");
  5159. var len = path.length;
  5160. var res = [];
  5161. _recurse = function(tree, index) {
  5162. if (!tree)
  5163. return;
  5164. var node;
  5165. if (index == len)
  5166. node = tree;
  5167. else {
  5168. _recurse(tree[path[index]], index + 1);
  5169. if (exactMatch)
  5170. return;
  5171. if (path[index] != "**")
  5172. _recurse(tree["*"], index + 1);
  5173. node = tree["**"];
  5174. }
  5175. if ((!node) || (!node["."]))
  5176. return;
  5177. res.push(node["."]);
  5178. };
  5179. _recurse(this._items, 0);
  5180. return res;
  5181. };
  5182. PageBus._TopicMatcher.prototype.exists = function(topic, exactMatch) {
  5183. var path = topic.split(".");
  5184. var len = path.length;
  5185. var res = false;
  5186. _recurse = function(tree, index) {
  5187. if (!tree)
  5188. return;
  5189. var node;
  5190. if (index == len)
  5191. node = tree;
  5192. else {
  5193. _recurse(tree[path[index]], index + 1);
  5194. if (res || exactMatch)
  5195. return;
  5196. if (path[index] != "**") {
  5197. _recurse(tree["*"], index + 1);
  5198. if (res)
  5199. return;
  5200. }
  5201. node = tree["**"];
  5202. }
  5203. if ((!node) || (!node["."]))
  5204. return;
  5205. res = true;
  5206. };
  5207. _recurse(this._items, 0);
  5208. return res;
  5209. };
  5210. PageBus._TopicMatcher.prototype.clear = function(topic) {
  5211. var path = topic.split(".");
  5212. var len = path.length;
  5213. _recurse = function(tree, index) {
  5214. if (!tree)
  5215. return;
  5216. if (index == len) {
  5217. if (tree["."])
  5218. delete tree["."];
  5219. }
  5220. else {
  5221. _recurse(tree[path[index]], index + 1);
  5222. for (var x in tree[path[index]]) {
  5223. return;
  5224. }
  5225. delete tree[path[index]];
  5226. }
  5227. };
  5228. _recurse(this._items, 0);
  5229. };
  5230. PageBus._TopicMatcher.prototype.wildcardClear = function(topic) {
  5231. var path = topic.split(".");
  5232. var len = path.length;
  5233. _clean = function(node, tok) {
  5234. for (m in node[tok])
  5235. return;
  5236. delete node[tok];
  5237. };
  5238. _recurse = function(tree, index) {
  5239. if (!tree)
  5240. return;
  5241. if (index == len) {
  5242. if (tree["."])
  5243. delete tree["."];
  5244. return;
  5245. }
  5246. else {
  5247. var tok = path[index];
  5248. var n;
  5249. if (tree[tok]) {
  5250. _recurse(tree[tok], index + 1);
  5251. _clean(tree, tok);
  5252. }
  5253. if (tok == "*") {
  5254. for (n in tree) {
  5255. if (( n != "**" ) && (n != ".")) {
  5256. _recurse(tree[n], index + 1);
  5257. _clean(tree, n);
  5258. }
  5259. }
  5260. }
  5261. else if (tok == "**") {
  5262. for (n in tree) {
  5263. delete tree[n];
  5264. }
  5265. }
  5266. }
  5267. return;
  5268. };
  5269. _recurse(this._items, 0);
  5270. };
  5271. PageBus._TopicMatcher.prototype.wildcardMatch = function(topic) {
  5272. var path = topic.split(".");
  5273. var len = path.length;
  5274. var res = [];
  5275. _recurse = function(tree, index) {
  5276. var tok = path[index];
  5277. var node;
  5278. if ((!tree) || (index == len))
  5279. return;
  5280. if (tok == "**") {
  5281. for (var n in tree) {
  5282. if (n != ".") {
  5283. node = tree[n];
  5284. if (node["."])
  5285. res.push(node["."]);
  5286. _recurse(node, index);
  5287. }
  5288. }
  5289. }
  5290. else if (tok == "*") {
  5291. for (var n in tree) {
  5292. if ((n != ".") && (n != "**")) {
  5293. node = tree[n];
  5294. if (index == len - 1) {
  5295. if (node["."])
  5296. res.push(node["."]);
  5297. }
  5298. else
  5299. _recurse(node, index + 1);
  5300. }
  5301. }
  5302. }
  5303. else {
  5304. node = tree[tok];
  5305. if (!node)
  5306. return;
  5307. if (index == len - 1) {
  5308. if (node["."])
  5309. res.push(node["."]);
  5310. }
  5311. else
  5312. _recurse(node, index + 1);
  5313. }
  5314. };
  5315. _recurse(this._items, 0);
  5316. return res;
  5317. };
  5318. ////////////////////////////////////////////////////////////////////////////////////
  5319. PageBus.policy = {
  5320. Ops: {
  5321. Publish: "p",
  5322. Subscribe: "s"
  5323. },
  5324. Error: {
  5325. BadParameters: "PageBus.policy.Error.BadParameters"
  5326. },
  5327. _assertName: function(topic) {
  5328. if ((topic == null) || (topic == ""))
  5329. throw new Error(OpenAjax.hub.Error.BadParameters);
  5330. if (PageBus.policy._tops[topic])
  5331. return;
  5332. var path = topic.split(".");
  5333. var len = path.length;
  5334. for (var i = 0; i < len; i++) {
  5335. var p = path[i];
  5336. if ((p == "") ||
  5337. ((p.indexOf("*") != -1) && (p != "*") && (p != "**"))) {
  5338. throw new Error(PageBus.policy.Error.BadParameters);
  5339. }
  5340. if ((p == "**") && (i < len - 1)) {
  5341. throw new Error(PageBus.policy.Error.BadParameters);
  5342. }
  5343. }
  5344. PageBus.policy._tops[topic] = true;
  5345. },
  5346. _tops: {}
  5347. };
  5348. PageBus.policy.HubPolicy = function(params) {
  5349. if (!params)
  5350. params = {};
  5351. this._cfg = params;
  5352. this._log = params["log"];
  5353. this._topicMgr = new PageBus._TopicMatcher();
  5354. };
  5355. PageBus.policy.HubPolicy.prototype.onPublish = function(topic, data, pc, sc) {
  5356. var res = true;
  5357. var origin;
  5358. if (sc != null) {
  5359. origin = sc.getPartnerOrigin();
  5360. if (!origin)
  5361. return false;
  5362. res = this.isAllowed.call(this, origin, PageBus.policy.Ops.Subscribe, topic);
  5363. }
  5364. if (res && (pc != null)) {
  5365. origin = pc.getPartnerOrigin();
  5366. if (!origin)
  5367. return false;
  5368. res = this.isAllowed.call(this, origin, PageBus.policy.Ops.Publish, topic);
  5369. }
  5370. if (this._log) {
  5371. var sid = sc ? sc.getClientID() : "(Mgr)";
  5372. var pid = pc ? pc.getClientID() : "(Mgr)";
  5373. this._log("(PageBus.policy) [" + pid + ", " + sid + "] onPublish: " +
  5374. (res ? "ALLOWED " : "DENIED ") + topic);
  5375. }
  5376. return res;
  5377. };
  5378. PageBus.policy.HubPolicy.prototype.onSubscribe = function(topic, sc) {
  5379. var res = true;
  5380. var origin;
  5381. if (sc != null) {
  5382. origin = sc.getPartnerOrigin();
  5383. if (!origin)
  5384. return false;
  5385. res = this.isAllowed.call(this, origin, PageBus.policy.Ops.Subscribe, topic);
  5386. }
  5387. if (this._log) {
  5388. var cid = sc ? sc.getClientID() : "(Mgr)";
  5389. this._log("(PageBus.policy) [" + cid + "] onSubscribe: " +
  5390. (res ? "ALLOWED " : "DENIED ") + topic);
  5391. }
  5392. return res;
  5393. };
  5394. PageBus.policy.HubPolicy.prototype.onUnsubscribe = function(topic, sc) {
  5395. };
  5396. PageBus.policy.HubPolicy.prototype.onSend = function(topic, data, origin) {
  5397. var res = this.isAllowed(origin, PageBus.policy.Ops.Subscribe, topic);
  5398. if (this._log)
  5399. this._log("(PageBus.policy) [" + origin + "] onSend: " + (res ? "ALLOWED " : "DENIED ") + topic);
  5400. return res;
  5401. };
  5402. PageBus.policy.HubPolicy.prototype.onReceive = function(topic, data, origin) {
  5403. var res = this.isAllowed(origin, PageBus.policy.Ops.Publish, topic);
  5404. if (this._log)
  5405. this._log("(PageBus.policy) [" + origin + "] onReceive: " + (res ? "ALLOWED " : "DENIED ") + topic);
  5406. return res;
  5407. };
  5408. PageBus.policy.HubPolicy.prototype._getMyOrigin = function() {
  5409. var o = window.location.href.match(/[^:]*:\/\/[^:\/\?#]*/);
  5410. return o[0];
  5411. };
  5412. PageBus.policy.HubPolicy.prototype.grant = function(origin, op, name) {
  5413. if ((!origin) || (!op) || (!name))
  5414. throw new Error(PageBus.policy.Error.BadParameters);
  5415. var t = PageBus._esc(origin) + "." + op + "." + name;
  5416. PageBus.policy._assertName(t);
  5417. this._topicMgr.store(t, { dm: origin, op: op, tp: name });
  5418. if (this._log)
  5419. this._log("(PageBus.policy) [" + origin + "] grant: " + op + " on " + name);
  5420. var cacheName = "_pagebus.cache.s." + name;
  5421. this._topicMgr.store(PageBus._esc(origin) + "." + op + "." + cacheName, { dm: origin, op: op, tp: cacheName });
  5422. if (this._log)
  5423. this._log("(PageBus.policy) [" + origin + "] implicit grant: " + op + " on " + cacheName);
  5424. };
  5425. PageBus.policy.HubPolicy.prototype.revoke = function(origin, op, name) {
  5426. if ((!origin) || (!op) || (!name))
  5427. throw new Error(PageBus.policy.Error.BadParameters);
  5428. var t = PageBus._esc(origin) + "." + op + "." + name;
  5429. PageBus.policy._assertName(t);
  5430. this._topicMgr.clear(t);
  5431. if (this._log)
  5432. this._log("(PageBus.policy) [" + origin + "] revoke: " + op + " on " + name);
  5433. var cacheName = "_pagebus.cache.s." + name;
  5434. this._topicMgr.clear(PageBus._esc(origin) + "." + op + "." + cacheName);
  5435. if (this._log)
  5436. this._log("(PageBus.policy) [" + origin + "] implicit revoke: " + op + " on " + cacheName);
  5437. };
  5438. PageBus.policy.HubPolicy.prototype.revokeAll = function(origin) {
  5439. if ((!origin))
  5440. throw new Error(PageBus.policy.Error.BadParameters);
  5441. this._topicMgr.wildcardClear(PageBus._esc(origin) + ".**");
  5442. if (this._log)
  5443. this._log("(PageBus.policy) [" + origin + "] revokeAll");
  5444. };
  5445. PageBus.policy.HubPolicy.prototype.isAllowed = function(origin, op, name) {
  5446. if ((!origin) || (origin == "") || (!op) || (op == "") || (!name) || (name == ""))
  5447. throw new Error(PageBus.policy.Error.BadParameters);
  5448. var t = PageBus._esc(origin) + "." + op + "." + name;
  5449. return this._topicMgr.exists(t, false);
  5450. };
  5451. PageBus.policy.HubPolicy.prototype.listAllowed = function(origin, op) {
  5452. if ((!origin) || (!op))
  5453. throw new Error(PageBus.policy.Error.BadParameters);
  5454. var qr = this._topicMgr.wildcardMatch(PageBus._esc(origin) + "." + op + ".**");
  5455. var res = [];
  5456. for (var r in qr) {
  5457. if (qr[r].value.tp.substring(0, 9) != "_pagebus.")
  5458. res.push(qr[r].value.tp);
  5459. }
  5460. return res;
  5461. };
  5462. PageBus.cache = {};
  5463. PageBus.cache.Error = {
  5464. // This topic is not being cached by the local hub instance
  5465. NoCache: "PageBus.cache.Error.NoCache"
  5466. };
  5467. PageBus._cache = {};
  5468. PageBus._cache.isCacheable = function(subData) {
  5469. return ( (subData) && (typeof subData == "object") && (subData["PageBus"]) && (subData.PageBus["cache"]) );
  5470. };
  5471. PageBus._cache.Cache = function() {
  5472. this._refs = {};
  5473. this._doCache = new PageBus._TopicMatcher();
  5474. this._caches = new PageBus._TopicMatcher();
  5475. };
  5476. PageBus._cache.Cache.prototype.add = function(topic, subID) {
  5477. var dc;
  5478. var dca = this._doCache.match(topic, true);
  5479. if (dca.length > 0)
  5480. dc = dca[0].value;
  5481. else {
  5482. dc = { rc: 0 };
  5483. this._doCache.store(topic, dc);
  5484. }
  5485. dc.rc++;
  5486. this._refs[subID] = topic;
  5487. };
  5488. PageBus._cache.Cache.prototype.remove = function(subID) {
  5489. var topic = this._refs[subID];
  5490. if (!topic)
  5491. return;
  5492. delete this._refs[subID];
  5493. var dca = this._doCache.match(topic, true);
  5494. if (dca.length == 0)
  5495. return;
  5496. dca[0].value.rc--;
  5497. if (dca[0].value.rc == 0) {
  5498. this._doCache.clear(topic);
  5499. var caches = this._caches.wildcardMatch(topic);
  5500. for (var i = 0; i < caches.length; i++) {
  5501. if (!(this._doCache.exists(caches[i].topic, false)))
  5502. this._caches.clear(caches[i].topic);
  5503. }
  5504. }
  5505. };
  5506. PageBus._cache.Cache.prototype.storeCopy = function(topic, value) {
  5507. PageBus._assertPubTopic(topic);
  5508. var copy = PageBus._copy(value);
  5509. this._caches.store(topic, copy);
  5510. };
  5511. PageBus._cache.Cache.prototype.clear = function(topic, value) {
  5512. PageBus._assertPubTopic(topic);
  5513. this._caches.clear(topic);
  5514. };
  5515. PageBus._cache.Cache.prototype.query = function(topic) {
  5516. PageBus._assertSubTopic(topic);
  5517. return this._caches.wildcardMatch(topic);
  5518. };
  5519. PageBus._cache.Cache.prototype.isCaching = function(topic) {
  5520. for (var r in this._refs)
  5521. false;
  5522. return this._doCache.exists(topic, false);
  5523. };
  5524. PageBus._enableMH = function() {
  5525. var MHClass = OpenAjax.hub.ManagedHub;
  5526. OpenAjax.hub.ManagedHub = function(params) {
  5527. if (!params) {
  5528. throw new Error(OpenAjax.hub.Error.BadParameters);
  5529. }
  5530. if ((!params.onPublish) || (!params.onSubscribe)) {
  5531. throw new Error(OpenAjax.hub.Error.BadParameters);
  5532. }
  5533. var defaultParams = {};
  5534. var defaultPolicy = null;
  5535. if (!params["PageBus"])
  5536. params.PageBus = {};
  5537. if (!params.PageBus["policy"])
  5538. params.PageBus.policy = defaultPolicy;
  5539. if (!params["scope"])
  5540. params.scope = window;
  5541. this._pagebus = {
  5542. _params: {},
  5543. _getActualParameters: function() {
  5544. return this._pagebus._params;
  5545. },
  5546. _policy: params.PageBus.policy,
  5547. _hub: this
  5548. };
  5549. // newly added
  5550. this.pagebus = { _hub: this };
  5551. var pb = this._pagebus;
  5552. pb._cache = new PageBus._cache.Cache();
  5553. this.pagebus.query = function(topic) {
  5554. return this._hub._pagebus._cache.query(topic);
  5555. };
  5556. this.pagebus.store = function(topic, data) {
  5557. if (this._hub._pagebus._cache.isCaching(topic))
  5558. this._hub.publish(topic, data);
  5559. };
  5560. this.pagebus.clear = function(topic) {
  5561. if (this._hub._pagebus._cache.isCaching(topic))
  5562. this._hub.publish(topic, null);
  5563. };
  5564. var appScope = params.scope ? params.scope : window;
  5565. var params2 = this._pagebus._params;
  5566. for (var pn in params) {
  5567. params2[pn] = params[pn];
  5568. }
  5569. params2.scope = params.scope ? params.scope : window;
  5570. params2.onPublish = function(topic, data, pcont, scont) {
  5571. try {
  5572. var pol = params2.PageBus.policy;
  5573. if (pol) {
  5574. var res = pol.onPublish.call(pol, topic, data, pcont, scont);
  5575. if (!res)
  5576. return false;
  5577. }
  5578. return params.onPublish.call(appScope, topic, data, pcont, scont);
  5579. }
  5580. catch(e) {
  5581. return false;
  5582. }
  5583. };
  5584. params2.onSubscribe = function(topic, scont) {
  5585. try {
  5586. var pol = params2.PageBus.policy;
  5587. if (pol) {
  5588. var res = pol.onSubscribe.call(pol, topic, scont);
  5589. if (!res)
  5590. return false;
  5591. }
  5592. res = params.onSubscribe.call(appScope, topic, scont);
  5593. return res;
  5594. }
  5595. catch(e) {
  5596. return false;
  5597. }
  5598. };
  5599. params2.onUnsubscribe = function(topic, scont) {
  5600. try {
  5601. var pol = params2.PageBus.policy;
  5602. if (pol) {
  5603. pol.onUnsubscribe.call(pol, params.scope, topic, scont);
  5604. }
  5605. params.onUnsubscribe.call(appScope, topic, scont);
  5606. }
  5607. catch(e) {
  5608. return;
  5609. }
  5610. };
  5611. if (!pb._policy) {
  5612. pb._policy = null; // new PageBus.policy.HubPolicy(params);
  5613. }
  5614. MHClass.call(this, params2);
  5615. this.getParameters = function() {
  5616. return params;
  5617. };
  5618. };
  5619. OpenAjax.hub.ManagedHub.prototype = MHClass.prototype;
  5620. var p = OpenAjax.hub.ManagedHub.prototype.publish;
  5621. var s = OpenAjax.hub.ManagedHub.prototype.subscribe;
  5622. var u = OpenAjax.hub.ManagedHub.prototype.unsubscribe;
  5623. var p4c = OpenAjax.hub.ManagedHub.prototype.publishForClient;
  5624. var s4c = OpenAjax.hub.ManagedHub.prototype.subscribeForClient;
  5625. var u4c = OpenAjax.hub.ManagedHub.prototype.unsubscribeForClient;
  5626. var gs = OpenAjax.hub.ManagedHub.prototype.getScope;
  5627. OpenAjax.hub.ManagedHub.prototype.publish = function(topic, data) {
  5628. PageBus._assertPubTopic(topic);
  5629. if (this._pagebus._cache.isCaching(topic)) {
  5630. if (data == null)
  5631. this._pagebus._cache.clear(topic);
  5632. else
  5633. this._pagebus._cache.storeCopy(topic, data);
  5634. }
  5635. p.call(this, topic, data);
  5636. };
  5637. OpenAjax.hub.ManagedHub.prototype.subscribe = function(topic, onData, scope, onComplete, subscriberData) {
  5638. PageBus._assertSubTopic(topic);
  5639. var sid = s.call(this, topic, onData, scope, onComplete, subscriberData);
  5640. if (PageBus._cache.isCacheable(subscriberData)) {
  5641. var cache = this._pagebus._cache;
  5642. cache.add(topic, sid);
  5643. var vals = cache.query(topic);
  5644. for (var i = 0; i < vals.length; i++) {
  5645. try {
  5646. onData.call(scope ? scope : window, vals[i].topic, vals[i].value, subscriberData);
  5647. }
  5648. catch(e) {
  5649. PageBus._debug();
  5650. }
  5651. }
  5652. }
  5653. return sid;
  5654. };
  5655. OpenAjax.hub.ManagedHub.prototype.unsubscribe = function(subID, onComplete, scope) {
  5656. var cache = this._pagebus._cache;
  5657. cache.remove(subID);
  5658. u.call(this, subID, onComplete, scope);
  5659. };
  5660. OpenAjax.hub.ManagedHub.prototype.getScope = function() {
  5661. return gs.call(this);
  5662. };
  5663. OpenAjax.hub.ManagedHub.prototype.publishForClient = function(container, topic, data) {
  5664. PageBus._assertPubTopic(topic);
  5665. if ((!this._pagebus._policy) ||
  5666. ( this._pagebus._policy.isAllowed(container.getPartnerOrigin(), PageBus.policy.Ops.Publish, topic) )) {
  5667. if (this._pagebus._cache.isCaching(topic)) {
  5668. if (data == null)
  5669. this._pagebus._cache.clear(topic);
  5670. else
  5671. this._pagebus._cache.storeCopy(topic, data);
  5672. }
  5673. }
  5674. p4c.call(this, container, topic, data);
  5675. };
  5676. OpenAjax.hub.ManagedHub.prototype.subscribeForClient = function(container, topic, containerSubID) {
  5677. PageBus._assertSubTopic(topic);
  5678. var mgrSubID = s4c.call(this, container, topic, containerSubID);
  5679. if (topic.substring(0, 17) == "_pagebus.cache.s.") {
  5680. var t = topic.substring(17);
  5681. this._pagebus._cache.add(t, mgrSubID);
  5682. var vals = this._pagebus._cache.query(t);
  5683. function _sendValues() {
  5684. for (var i = 0; i < vals.length; i++) {
  5685. container.sendToClient(vals[i].topic, vals[i].value, containerSubID);
  5686. }
  5687. }
  5688. setTimeout(_sendValues, 0);
  5689. }
  5690. return mgrSubID;
  5691. };
  5692. OpenAjax.hub.ManagedHub.prototype.unsubscribeForClient = function(container, managerSubID) {
  5693. this._pagebus._cache.remove(managerSubID);
  5694. try {
  5695. var sdata = this.getSubscriberData(managerSubID);
  5696. if (PageBus._cache.isCacheable(sdata)) {
  5697. if (this._pagebus._cacheSids[managerSubID]) {
  5698. this.unsubscribe(this._pagebus._cacheSids[managerSubID], null, null);
  5699. delete this._pagebus._cacheSids[managerSubID];
  5700. this._pagebus._cache.remove(managerSubID);
  5701. }
  5702. }
  5703. u4c.call(this, container, managerSubID);
  5704. }
  5705. catch(e) {
  5706. PageBus._debug();
  5707. }
  5708. };
  5709. };
  5710. PageBus._enableMH();
  5711. PageBus.HubClientExtender = function(hub, params) {
  5712. var that = this;
  5713. this._hub = hub;
  5714. // Set up default parameters
  5715. this._params = params;
  5716. if (!params["PageBus"])
  5717. this._params.PageBus = { log: params.log };
  5718. if (!params.PageBus["policy"]) {
  5719. params.PageBus.policy = null;
  5720. }
  5721. // Initialize this HubClientExtender
  5722. this._policy = params.PageBus.policy;
  5723. this._cache = new PageBus._cache.Cache();
  5724. this._cacheSids = {};
  5725. // Store references to the hub's sub, unsub and publish functions:
  5726. this._wrappedSubscribe = hub.subscribe;
  5727. this._wrappedUnsubscribe = hub.unsubscribe;
  5728. this._wrappedPublish = hub.publish;
  5729. this._wrappedDisconnect = hub.disconnect;
  5730. // Replace the hub's pub, sub and uns functions with wrappers:
  5731. hub.publish = function(topic, data) {
  5732. that._publishWrapper(topic, data);
  5733. };
  5734. hub.subscribe = function(topic, onData, scope, onComplete, subData) {
  5735. return that._subscribeWrapper(topic, onData, scope, onComplete, subData);
  5736. };
  5737. hub.unsubscribe = function(subscriptionID, onComplete, scope) {
  5738. that._unsubscribeWrapper(subscriptionID, onComplete, scope);
  5739. };
  5740. hub.disconnect = function(onComplete, scope) {
  5741. that._disconnectWrapper(onComplete, scope);
  5742. }
  5743. };
  5744. PageBus.HubClientExtender.prototype._publishWrapper = function(topic, data) {
  5745. if (!this._hub.isConnected())
  5746. throw new Error(OpenAjax.hub.Error.Disconnected);
  5747. PageBus._assertPubTopic(topic);
  5748. if (this._cache.isCaching(topic)) {
  5749. if (data == null)
  5750. this._cache.clear(topic);
  5751. else
  5752. this._cache.storeCopy(topic, data);
  5753. }
  5754. var origin = this._hub.getPartnerOrigin();
  5755. if (!origin)
  5756. throw new Error(OpenAjax.hub.Error.Disconnected);
  5757. if (this._policy && (! this._policy.onSend(topic, data, origin)))
  5758. return;
  5759. this._wrappedPublish.call(this._hub, topic, data);
  5760. };
  5761. PageBus.HubClientExtender.prototype._subscribeWrapper = function(topic, onData, scope, onComplete, subData) {
  5762. var that = this;
  5763. if (!this._hub.isConnected())
  5764. throw new Error(OpenAjax.hub.Error.Disconnected);
  5765. PageBus._assertSubTopic(topic);
  5766. if (!onData)
  5767. throw new Error(OpenAjax.hub.Error.BadParameters);
  5768. var policy = this._policy;
  5769. var origin = this._hub.getPartnerOrigin();
  5770. if (!origin)
  5771. throw new Error(OpenAjax.hub.Error.Disconnected);
  5772. if (policy && (! policy.onReceive.call(policy, topic, null, origin)))
  5773. throw new Error(OpenAjax.hub.Error.NotAllowed);
  5774. dataHook = function(t, d, sd) {
  5775. var policy = that._policy;
  5776. var origin = that._hub.getPartnerOrigin();
  5777. if (policy && (! policy.onReceive.call(policy, t, d, origin)))
  5778. return;
  5779. if (PageBus._cache.isCacheable(sd)) {
  5780. if (that._cache.isCaching(t)) {
  5781. if (d == null)
  5782. that._cache.clear(t);
  5783. else
  5784. that._cache.storeCopy(t, d);
  5785. }
  5786. }
  5787. try {
  5788. var s = scope ? scope : window;
  5789. onData.call(s, t, d, sd);
  5790. }
  5791. catch(e) {
  5792. PageBus._debug();
  5793. }
  5794. };
  5795. completeHook = function(item, suc, err) {
  5796. if (!suc) {
  5797. if (that._cacheSids[item]) {
  5798. that._hub.unsubscribe(that._cacheSids[item], null, null);
  5799. delete that._cacheSids[item];
  5800. that._cache.remove(item);
  5801. }
  5802. }
  5803. try {
  5804. var s = scope ? scope : window;
  5805. onComplete.call(s, item, suc, err);
  5806. }
  5807. catch(e) {
  5808. PageBus._debug();
  5809. }
  5810. };
  5811. var sid = this._wrappedSubscribe.call(this._hub, topic, dataHook, scope, completeHook, subData);
  5812. try {
  5813. this._hub.getSubscriberData(sid);
  5814. } catch(e) {
  5815. if (e.message == OpenAjax.hub.Error.NoSubscription) {
  5816. // unsubscribe was synchronously called within completeHook
  5817. return sid;
  5818. }
  5819. }
  5820. if (PageBus._cache.isCacheable(subData)) {
  5821. this._cache.add(topic, sid);
  5822. this._cacheSids[sid] = this._hub.subscribe(
  5823. "_pagebus.cache.s." + topic,
  5824. function(t, d, sd) {
  5825. var policy = this._policy;
  5826. var origin = this._hub.getPartnerOrigin();
  5827. if (policy && (! this._policy.onReceive(t, d, origin)))
  5828. return;
  5829. if (PageBus._cache.isCacheable(subData)) {
  5830. if (this._cache.isCaching(t)) {
  5831. if (d == null)
  5832. this._cache.clear(t);
  5833. else
  5834. this._cache.storeCopy(t, d);
  5835. }
  5836. }
  5837. try {
  5838. var s = scope ? scope : window;
  5839. onData.call(s, t, d, subData);
  5840. }
  5841. catch(e) {
  5842. PageBus._debug();
  5843. }
  5844. },
  5845. this,
  5846. function(item, suc, err) {
  5847. },
  5848. null);
  5849. }
  5850. return sid;
  5851. };
  5852. PageBus.HubClientExtender.prototype._unsubscribeWrapper = function(subscriptionID, onComplete, scope) {
  5853. if (!this._hub.isConnected())
  5854. throw new Error(OpenAjax.hub.Error.Disconnected);
  5855. if ((subscriptionID == null) || (subscriptionID == ""))
  5856. throw new Error(OpenAjax.hub.Error.BadParameters);
  5857. var sdata = this._hub.getSubscriberData(subscriptionID);
  5858. if (PageBus._cache.isCacheable(sdata)) {
  5859. if (this._cacheSids[subscriptionID]) {
  5860. this._hub.unsubscribe(this._cacheSids[subscriptionID], null, null);
  5861. delete this._cacheSids[subscriptionID];
  5862. this._cache.remove(subscriptionID);
  5863. }
  5864. }
  5865. this._wrappedUnsubscribe.call(this._hub, subscriptionID, onComplete, scope);
  5866. };
  5867. PageBus.HubClientExtender.prototype._disconnectWrapper = function(onComplete, scope) {
  5868. this._cache._caches.wildcardClear("**");
  5869. this._cacheSids = {};
  5870. this._wrappedDisconnect.call(this._hub, onComplete, scope);
  5871. };
  5872. PageBus.HubClientExtender.prototype.query = function(topic) {
  5873. return this._cache.query(topic); // do not throw NoCache; can use query across broad ranges that are partly cached.
  5874. };
  5875. PageBus.HubClientExtender.prototype.store = function(topic, data) {
  5876. if (this._cache.isCaching(topic))
  5877. this._hub.publish(topic, data);
  5878. else
  5879. throw new Error(PageBus.cache.Error.NoCache);
  5880. };
  5881. PageBus.HubClientExtender.prototype.clear = function(topic) {
  5882. if (this._cache.isCaching(topic))
  5883. this._hub.publish(topic, null);
  5884. else
  5885. throw new Error(PageBus.cache.Error.NoCache);
  5886. };
  5887. /**
  5888. * enableHubClientClass
  5889. * Global function that prepares a HubClient class so that when it is
  5890. * instantiated via the "new" operator, the resulting instance is
  5891. * PageBus-enabled.
  5892. */
  5893. PageBus.enableHubClientClass = function(hubClass) {
  5894. hubClass.prototype._pagebusWrappedConnect = hubClass.prototype.connect;
  5895. hubClass.prototype.connect = function(onComplete, scope) {
  5896. var hub = this;
  5897. if (!hub.pagebus) {
  5898. hub.pagebus = new PageBus.HubClientExtender(hub, hub.getParameters());
  5899. }
  5900. hub._pagebusWrappedConnect.call(hub, onComplete, scope);
  5901. };
  5902. };
  5903. //////////////////////////////////////////////////////////////////////
  5904. //PageBus-enable the reference implementation:
  5905. if (!OpenAjax.hub) {
  5906. debugger;
  5907. }
  5908. if (OpenAjax.hub.InlineHubClient)
  5909. PageBus.enableHubClientClass(OpenAjax.hub.InlineHubClient);
  5910. if (OpenAjax.hub.IframeHubClient)
  5911. PageBus.enableHubClientClass(OpenAjax.hub.IframeHubClient);
  5912. OpenAjax.hub._hub = new OpenAjax.hub.ManagedHub({
  5913. onSubscribe: function(topic, ctnr) {
  5914. return true;
  5915. },
  5916. onPublish: function(topic, data, pcont, scont) {
  5917. return true;
  5918. }
  5919. });
  5920. OpenAjax.hub.subscribe = function(topic, onData, scope, subscriberData) {
  5921. if (typeof onData === "string") {
  5922. scope = scope || window;
  5923. onData = scope[ onData ] || null;
  5924. }
  5925. return OpenAjax.hub._hub.subscribe(topic, onData, scope, null, subscriberData);
  5926. }
  5927. OpenAjax.hub.unsubscribe = function(subscriptionID) {
  5928. return OpenAjax.hub._hub.unsubscribe(subscriptionID);
  5929. }
  5930. OpenAjax.hub.publish = function(topic, data) {
  5931. OpenAjax.hub._hub.publish(topic, data);
  5932. }
  5933. //////////////////////////////////////////////////////////////////////
  5934. PageBus.publish = function(topic, data) {
  5935. OpenAjax.hub.publish(topic, data);
  5936. };
  5937. PageBus.subscribe = function(topic, scope, onData, subscriberData) {
  5938. return OpenAjax.hub.subscribe(topic, onData, scope, subscriberData);
  5939. };
  5940. PageBus.unsubscribe = function(sub) {
  5941. OpenAjax.hub.unsubscribe(sub);
  5942. };
  5943. PageBus.store = function(topic, data) {
  5944. OpenAjax.hub._hub.pagebus.store(topic, data);
  5945. };
  5946. PageBus.query = function(topic) {
  5947. return OpenAjax.hub._hub.pagebus.query(topic);
  5948. };
  5949. OpenAjax.hub.registerLibrary("PageBus", "http://pagebus.org/pagebus", "2.0", {});