Source: portlet.js

/*jslint browser: true, devel: true, plusplus: true, unparam: true, white: true, indent: 3 */
/*  Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you under the Apache License, Version 2.0 (the
 *  "License"); you may not use this file except in compliance
 *  with the License.  You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 */

/*
 * This source code implements specifications defined by the Java
 * Community Process. In order to remain compliant with the specification
 * DO NOT add / change / or delete method signatures!
 */

/**
 * @fileOverview
 * This module provides JavaScript functions for portlets.
 *
 * @author Scott Nicklous
 * @copyright IBM Corp., 2014
 */

/**
 * Represents a portlet parameter.
 * <p>
 * Each object property represents a parameter name.
 * The object may also contain no property names,
 * which represents the case of the portlet having no parameters.
 * <p>
 * If properties are present,
 * each property must refer to a array of string values.
 * The array length must be at least 1, because each parameter must have
 * a value.
 * However, a value of 'null' may appear in any array entry.
 * <p>
 * To represent the parameter having a <code>null</code> value, the
 * property value must equal [null].
 * <p>
 * @typedef    PortletParameters
 * @property   {string[]}  {string}    The parameters object may have
 *                                     multiple properties.
 */

/**
 * Represents the render state.
 * <p>
 * RenderState objects obtained from the Portlet Hub define some helper
 * functions for accessing the parameter values.
 * <p>
 * @typedef    RenderState
 * @property   {PortletParameters}   parameters     The portlet parameters
 * @property   {string}             portletMode    The portlet mode
 * @property   {string}             windowState    The window state
 * @property   {function}   clone()                Returns a new copy of this object
 * @property   {function}   setPortletMode(pm)     Sets the portlet mode to the specified value. 
 *               The strings defined by the {@link PortletConstants} object should be used to specify the portlet mode.
 * @property   {function}   getPortletMode()       Returns the current portlet mode 
 * @property   {function}   setWindowState(ws)     Sets the window state to the specified value
 * @property   {function}   getWindowState()       Returns the current window state
 *               The strings defined by the {@link PortletConstants} object should be used to specify the window state.
 * @property   {function}   setValue(n,v)    Sets a parameter with name n and value v.
 *                                           The value v may be a string or an array.
 * @property   {function}   setValues(n,v)   Sets a parameter with name n and value v.
 *                                           The value v may be a string or an array.
 * @property   {function}   getValue(n,d)    Gets the string parameter value for the name n.
 *                                           If n designates a multi-valued parameter, this function returns the first value in the values array.
 *                                           If parameter n is undefined, the function returns the optional default value d.
 * @property   {function}   getValues(n,d)   Gets the string array parameter value for the name n.
 *                                           If parameter n is undefined, the function returns the optional default value array d.
 * @property   {function}   remove(n)        Removes the parameter with name n.
 */

/**
 * Provides defined values for some commonly-used portlet constants
 * <p>
 * @typedef    PortletConstants
 * @property   {string}       VIEW        Specifies portlet mode 'view'    
 * @property   {string}       EDIT        Specifies portlet mode 'edit'    
 * @property   {string}       HELP        Specifies portlet mode 'help'    
 * @property   {string}       NORMAL      Specifies window state 'normal'
 * @property   {string}       MINIMIZED   Specifies window state 'minimized'
 * @property   {string}       MAXIMIZED   Specifies window state 'maximized'
 * @property   {string}       FULL        Specifies resource URL cacheability "cacheLevelFull"
 * @property   {string}       PORTLET     Specifies resource URL cacheability "cacheLevelPage"
 * @property   {string}       PAGE        Specifies resource URL cacheability "cacheLevelPortlet"
 */

/**
 * Optional render data that may be provided by the portlet hub when
 * a render state change occurs.
 *
 * @typedef    RenderData
 * @property   {string}             renderData     The render data
 * @property   {string}             mimeType       The mime type
 */

/**
 * The onStateChange callback function that each portlet client
 * participating in the portlet hub Ajax support must implement.
 * <p>
 * The portlet client registers an onStateChange callback by adding a
 * listener for the portlet.onStateChange
 * event type through the {@link addEventListener} function.
 * <p>
 * When the portlet hub calls the onStateChange callback, it will pass
 * a copy of the current render state as a parameter.
 * The portlet client can make changes to the RenderState object
 * without affecting the actual render state, which is maintained by the
 * portlet hub.
 * The portlet client uses the {@link setRenderState} method to
 * make changes to the local copy of the RenderState object active.
 * <p>
 * Optionally, the portlet hub will make a RenderData object available
 * to the portlet client.
 * When the RenderData object is available, it contains the same data
 * as would be available through a portlet resource request using the
 * current render state with no additional resource parameters and with
 * the resource URL cacheability option set to "PAGE".
 * <p>
 * The portlet client may not call portlet hub functions that can
 * cause state to be updated ({@link action}, {@link updateRenderState},
 * {@link startPartialAction})
 * during onStateChange execution.
 *
 * @name       onStateChange
 * @callback   onStateChange
 * @param      {string}       type           The event type "portlet.onStateChange".
 * @param      {RenderState} RenderState   The render state
 * @param      {RenderData}   renderData     The render data.
 *                                           Optionally provided by the portlet hub.
 */

/**
 * This data is returned to the portlet client by the {@link startPartialAction}
 * function.
 * <p>
 * The portlet client can use the partial action URL to initiate processing.
 * During processing, the portal will perform action and event processing.
 * It will generate a string representing the new page state and pass it to the
 * portlet's serveResource method.
 * <p>
 * The portlet is responsible for transmitting the page state string to the
 * client and for calling the {@link setPageState} callback function.
 * <p>
 * The portlet hub will be blocked until the portlet calls the setPageState
 * function and the portlet hub has successfully performed the state change.
 *
 * @typedef    PartialActionInit
 * @property   {string}             url               The partial action URL
 * @property   {function}           setPageState      Callback function
 */

/**
 * Provides the name of an error that occurred along with an optional
 * message that provide more detail.
 * @typedef    ErrorData
 * @property   {string}    name     The error name
 * @property   {string}    message  An optional message that provides more detail about the error
 */

/**
 * The onError callback function that a portlet client
 * participating in the portlet hub Ajax support may implement.
 * Typically the callback will be used to provide the portlet client with
 * information about asynchronous errors that occurred during processing.
 * <p>
 * The portlet client registers an onError callback by adding a listener for the portlet.onError
 * event type through the {@link addEventListener} function.
 * @name       onError
 * @callback   onError
 * @param      {string}       type           The event type "portlet.onError".
 * @param      {ErrorData}    errorData      Provides information about the error.
 */


/**
 * The "portlet" namespace is reserved for use by portal JavaScript support
 * implementations.
 * <p>
 * Portlet Specification 2.0 (JSR 286) provided Ajax support for portlets by
 * adding a resource URL, resource request, and serveResource() method to allow
 * portlet JavaScript code to retrieve data directly from the portlet. Portlet
 * Specification 3.0 (JSR 362) expands on that by providing capability allowing
 * a portlet to change its state through JavaScript functions.
 * <p>
 * Portlet Specification 3.0 introduces a JavaScript component called the
 * Portlet Hub that manages the state of all portlets on a portal page.
 * <p>
 * Some terminology:
 * <dl>
 * <dt> Portlet Hub </dt>
 * <dd>JavaScript component that manages the state of all portlets on a portal
 * page. It is considered to be part of the portal implementation. This document
 * describes its JavaScript interfaces and behavior.
 * <p>
 * Portlet JavaScript code can use the Portlet Hub programming interface to
 * modify its state by setting private and public render parameters to submit
 * forms while remaining in the Ajax application paradigm.
 * <p>
 * Many portlets can be present on the page and the portlets are in general
 * independent of one another and they are not aware of the state of other
 * portlets, so they are unable to generate URLs properly representing the
 * complete page state. The Portlet Hub provides methods that allow portlets to
 * obtain URLs representing the current portal page state.
 * <p>
 * The programming interface and behavior of this component is standardized by
 * JSR 362. </dd>
 * <dt> Portlet Client </dt>
 * <dd> JavaScript code written by the portlet developer that uses the portlet
 * hub to manage its state and to obtain URLs for retrieving data. </dd>
 * </dl>
 * <h5> Changing the render state </h5>
 * <p>
 * The portlet client can use portlet hub functions to initiate state changes.
 * <p>
 * The portlet client can set public and private render parameters as well as
 * the portlet mode and window state.
 * <p>
 * In addition, the portlet client can submit a portlet action request that uses
 * HTTP POST semantics. The portal will execute the portlet Action Phase and
 * Event Phase processing on the server and return the updated page state to the
 * portlet hub.
 * <p>
 * After the requested state change has been performed, the portlet hub will
 * usually provide each affected portlet client with its updated state
 * information.
 * <p>
 * However, regardless of whether the state change was initiated by setting
 * parameters or through a portlet action, the portal may respond by completely
 * refreshing the page. The portal may do so in order to support portlets that
 * are affected by the state change but do not participate in the portlet
 * client-side support, or for implementation-specific reasons.
 * <p>
 * If the portal responds to a state change request by refreshing the page, the
 * portlets will not be updated with new page state information before the page
 * refresh is carried out.
 * <h5> Receiving render state Updates </h5>
 * <p>
 * When a state change occurs that affects a portlet, the portlet hub informs
 * the affected portlet client of its new state through use of a callback
 * function. The change causing the update does not necessarily need to be
 * initiated by the portlet client itself.
 * <p>
 * For example, when portlet A changes a public render parameter used by portlet
 * B, the portlet hub will inform both the portlet A and the portlet B clients
 * of that change.
 * <h5> Portlet Client Events </h5>
 * <p>
 * Portlet client events consist of an event type and an event payload. Both are
 * defined by by the portlet clients themselves.
 * <p>
 * Portlet client events have no connection to the server-side portlet event
 * mechanism.
 * <p>
 * The portlet hub provides utility functions that enable the portlet client to
 * dispatch and listen for portlet client events.
 * <h5> Error Handling </h5>
 * <p>
 * When the portlet hub can recognize an error during function execution, the
 * error will be reported to the portlet client through an exception.
 * <p>
 * However, some methods initiate work that is performed asynchronously. Errors
 * that occur during asynchronous processing will be reported to the portelt
 * client through the {@link onError} callback function.
 * <h5> Important Considerations </h5>
 * <p>
 * The portlet hub calls the portlet client callback functions in several
 * situations as described above. When the portlet hub calls the portlet client,
 * the portlet client may navigate to a different page or may initiate another
 * change to the render state that could potentially cause a page refresh.
 * <p>
 * Due to this behavior, the delivery of neither render state updates nor
 * portlet client events can be guaranteed.
 * <p>
 * The portlet hub provides for orderly state transitions by allowing only a
 * single blocking operation ({@link action}, {@link setRenderState},
 * {@link startPartialAction}) to be active at any one time.
 * <p>
 * The state transition is considered to be active from the initial portlet
 * client call to one of the blocking operations until the portlet hub has
 * performed the requested state change and has informed all of the affected
 * portlet clients by firing the corresponding onStateChange events.
 * <p>
 * This has the following implications:
 * <ol>
 * <li> It is not possible to initiate a sequence of blocking operations.
 * <p>
 * For example, once a portlet client calls the {@link setRenderState} method,
 * it cannot call any additional blocking method until after its onStateChange
 * listener function has been called. </li>
 * <li> It is not possible to initiate a blocking operation during execution of
 * the onStateChange listener function, since execution of that function belongs
 * to the preceding state change operation. </li>
 * </ol>
 * <hr>
 * 
 * @namespace
 */
var portlet = portlet || {};

(function() {
   'use strict';

   // If module has already been loaded, don't load again
   if (typeof portlet.register === 'function') {
      return;
   }

   // ~~~~~~~~~~~~~~~~ Helper classes for parameters & state
   // ~~~~~~~~~~~~~~~~~~~
   
   function Parameters(p) {
      var n;
      if (p) {
         for (n in p) {
            if (p.hasOwnProperty(n) && Array.isArray(p[n])) {
               this[n] = p[n].slice(0);
            }
         }
      }
   }

   function State(s) {
      var n;
      this.parameters = {};
      if (s) {
         this.portletMode = s.portletMode;
         this.windowState = s.windowState;
         if (s.parameters) {
            for (n in s.parameters) {
               if (s.parameters.hasOwnProperty(n)
                     && Array.isArray(s.parameters[n])) {
                  this.parameters[n] = s.parameters[n].slice(0);
               }
            }
         }
      } else {
         this.portletMode = 'VIEW';
         this.windowState = 'NORMAL';
      }
   }
   State.prototype.clone = function() {
      return new State(this);
   };
   State.prototype.setPortletMode = function(pm) {
      if (typeof pm !== 'string') {
         throw new TypeError('Portlet mode must be a string');
      }
      this.portletMode = pm;
   };
   State.prototype.getPortletMode = function() {
      return this.portletMode;
   };
   State.prototype.setWindowState = function(ws) {
      if (typeof ws !== 'string') {
         throw new TypeError('Window state must be a string');
      }
      this.windowState = ws;
   };
   State.prototype.getWindowState = function() {
      return this.windowState;
   };
   State.prototype.setValue = function(name, value) {
      if (typeof name !== 'string') {
         throw new TypeError('Parameter name must be a string');
      }
      if (typeof value !== 'string' && value !== null && !Array.isArray(value)) {
         throw new TypeError(
               'Parameter value must be a string, an array, or null');
      }
      var val = value;
      if (!Array.isArray(value)) {
         val = [ value ];
      }
      this.parameters[name] = val;
   };
   State.prototype.setValues = State.prototype.setValue;
   State.prototype.remove = function(name) {
      if (typeof name !== 'string') {
         throw new TypeError('Parameter name must be a string');
      }
      if (this.parameters[name] !== undefined) {
         delete this.parameters[name];
      }
   };
   State.prototype.getValue = function(name, def) {
      if (typeof name !== 'string') {
         throw new TypeError('Parameter name must be a string');
      }
      var res = this.parameters[name];
      if (res) {
         res = res[0];
      }
      if (res === undefined) {
         res = def;
      }
      return res;
   };
   State.prototype.getValues = function(name, def) {
      if (typeof name !== 'string') {
         throw new TypeError('Parameter name must be a string');
      }
      var res = this.parameters[name];
      if (res === undefined) {
         res = def;
      }
      return res;
   };
   
   var portletConstants = {
      
      // Portlet mode
      "VIEW" : "view",
      "EDIT" : "edit",
      "HELP" : "help",
      
      // window state
      "NORMAL"    : "normal",
      "MINIMIZED" : "minimized",
      "MAXIMIZED" : "maximized",
      
      // Resource URL cacheability
      "FULL"      : "cacheLevelFull",
      "PAGE"      : "cacheLevelPage",
      "PORTLET"   : "cacheLevelPortlet"
   },

   // variable declarations
   portletRegex = "^portlet[.].*",

   /**
    * Portlet Hub Mockup internal structure defining the data held for each
    * portlet
    *
    * @typedef    PortletData
    * @property   {RenderState} state       The current RenderState object
    * @property {string[]} pubParms String array declaring the public parameters
    *           for the portlet
    * @property   {RenderData}   renderData  Render data for the portlet
    * @private
    */


   // Helper functions
   wnd = window, delay = function(aCallback, aTimeout) {
      wnd.setTimeout(aCallback, aTimeout);
   },


   // ~~~~~~~~~~~~~~~~~~~~~~ Exceptions ~~~~~~~~~~~~~~~~~~~~~~~~~~

   /**
    * Exception thrown when a portlet client is not allowed to access a data
    * element
    * 
    * @typedef    AccessDeniedException
    * @property {string} name The exception name, equal to
    *           "AccessDeniedException"
    * @property {string} message An optional message that provides more detail
    *           about the exception
    */
   throwAccessDeniedException = function(msg) {
      throw {
         name : "AccessDeniedException",
         message : msg
      };
   },

   /**
    * Thrown when a portlet attempts to use the API without comleting the
    * intialization process. In general, this means that the onStateChange
    * listener has not been added.
    *
    * @typedef    NotInitializedException
    * @property {string} name The exception name, equal to
    *           "NotInitializedException"
    * @property {string} message An optional message that provides more detail
    *           about the exception
    */
   throwNotInitializedException = function(msg) {
      throw {
         name : "NotInitializedException",
         message : msg
      };
   },

   /**
    * Maps from the the ID of the registered portlet to the implementation
    * callback
    */
   _registeredPortlets = {},

   /**
    * Shortcut for Object.keys
    *
    * @const
    * @type {function(Object):Array}
    */
   _getKeys = Object.keys,

   /**
    * Shortcut for the Array.isArray
    *
    * @const
    * @type {function(*):boolean}
    */
   _isArray = Array.isArray,

   /**
    * Checks if the object is an object
    *
    * @param {*}
    *            obj object
    * @return {boolean}
    */
   _isObject = function(obj) {
      return typeof obj === "object";
   },

   /**
    * Constructs a deep copy of the object
    *
    * @param {Object|Array}
    *            data
    * @return {Object|Array}
    */
   _clone = function(data) {
      // some helpers down the road
      var result, i, key, keys;
      // the array case
      if (_isArray(data)) {
         // construct a new array
         result = [];
         // fill the array
         for (i = data.length - 1; i >= 0; --i) {
            result[i] = _clone(data[i]);
         }
      } else if (_isObject(data)) {
         // construct a new object
         result = {};
         keys = _getKeys(data);
         for (i = keys.length - 1; i >= 0; --i) {
            key = keys[i];
            result[key] = _clone(data[key]);
         }
      } else {
         // just use the object as is
         result = data;
      }
      // ok
      return result;
   },

   // ~~~~~~~~~~~~~~~~~~~~~~ Event Handling ~~~~~~~~~~~~~~~~~~~~~~~~~~
   // for event handling
   handleCtr = 0,                         // used to generate handles returned by addEventListener

   /**
    * The internal data structure for managing listeners and the update queue.
    * 
    * @typedef  QueueData
    * @property {String}      handle   The listener handle
    * @property {string}      id       The portlet ID
    * @property {string}      type     The event type
    * @property {function}    callback The callback function for the event
    * @private 
    */

   // System listeners (onStateChange and onError). Associative arrays indexed
   // by portletID
   oscListeners = {}, oeListeners = {}, 

   // Portlet Client Event listeners. Associative array indexed by handle
   pcListeners = {},                      // portlet client event listeners

   // the portlet hub can only execute a single blocking operation
   // at a time. At the beginning of the operation, this flag is set to
   // true to prevent other blocking operations from beginning.
   // It is reset after the last onStateChange event resulting from the
   // state change has been fired.
   // (or also if a communication error occurs.)
   busy = false,

   // queue for the callback functions that need to be called. Each element
   // is a QueueData structure. Associative array indexed by handle.
   updateQueue = [],


   /**
    * Add an event listener of the given type to the specified portlet client
    * listener storage object.
    * <p>
    * The portlet client event listeners are stored in associative arrays
    * (objects) indexed by the event listener handle.
    *
    * @param {object}
    *            list The object where the listener will be stored
    * @param {string}
    *            pid The portletID
    * @param {string}
    *            etype The event type
    * @param {function}
    *            func The the listener function to be called
    * @private
    */
   addPCListener = function(list, pid, etype, func) {
      var hand = "handle-" + (++handleCtr);
      list[hand] = {
                     handle   : hand,
                     id       : pid,
                     type     : etype,
                     callback : func
                   };
      return hand;
   },


   /**
    * Removes an event listener identified by the portletId and handle from the
    * portlet client listener storage object
    * 
    * @param {object}
    *            list The object where the listener will be stored
    * @param {string}
    *            pid The portletID
    * @param {object}
    *            handle The event listener handle
    * @returns    {boolean}            <code>true</code> if the listener was removed
    * @throws     {AccessDeniedException}
    *             Thrown if the event listener associated with this handle was
    *             registered by a different portlet
    * @private
    */
   removePCListener = function(list, pid, handle) {
      if (typeof list[handle] === 'object') {

         // the listener was found, so if the portlet ID matches, remove it.
         if (list[handle].id === pid) {
            delete list[handle];
         } else {
            throwAccessDeniedException("Invalid handle for portlet ID=" + pid);
         }
         return true;
      }
      return false;
   },


   /**
    * Add an event listener of the given type to the specified system event
    * listener storage object.
    * <p>
    * The system event listeners are stored in associative arrays (objects)
    * indexed by portlet ID.
    *
    * @param {object}
    *            list The object where the listener will be stored
    * @param {string}
    *            pid The portletID
    * @param {string}
    *            etype The event type
    * @param {function}
    *            func The the listener function to be called
    * @private
    */
   addSystemListener = function(list, pid, etype, func) {

      // add the listener
      var hand = "handle-" + (++handleCtr);

      console.log("addSystemListener, handle=" + hand + ", pid=" + pid);

      list[hand] = {
                     handle   : hand,
                     id       : pid,
                     type     : etype,
                     callback : func
                   };
      return hand;
   },


   /**
    * Removes an event listener identified by the portletId and handle from the
    * system listener storage object
    * 
    * @param {object}
    *            list The object where the listener will be stored
    * @param {string}
    *            pid The portletID
    * @param {object}
    *            handle The event listener handle
    * @returns    {boolean}            <code>true</code> if the listener was removed
    * @private
    */
   removeSystemListener = function(list, pid, handle) {
      var ii;
      if (typeof list[handle] === 'object') {

         // the listener was found, so if the portlet ID matches, remove it.
         if (list[handle].id === pid) {
            delete list[handle];

            // purge any pending notifications -
            ii = updateQueue.length;
            while (ii-- >= 0) {
               if (updateQueue[ii] === handle) {
                  updateQueue.splice(ii, 1);
               }
            }

            return true;
         }
      }
      return false;
   },


   /**
    * Sends an onError event to all registered error event handlers for a given
    * portlet.
    * 
    * @param {string}
    *            pid The portletID
    * @param {string}
    *            err The error message
    * @private
    */
   reportError = function(pid, err) {
      delay(function() {
         var handle;
         for (handle in oeListeners) {
            if (oeListeners.hasOwnProperty(handle)) {
               if (oeListeners[handle].id === pid) {
                  oeListeners[handle].callback('portlet.onError', err);
               }
            }
         }
      }, 0);
   },
   

   /**
    * Returns true if an onStateChange listener is registered for the portlet
    * 
    * @param {string}
    *            pid The portletID
    * @returns {boolean} <code>true</code> if a listener is registered
    * @private
    */
   hasListener = function(pid) {
      var handle;
      for (handle in oscListeners) {
         if (oscListeners.hasOwnProperty(handle)) {
            if (oscListeners[handle].id === pid) {
               return true;
            }
         }
      }
      return false;
   },

   // ~~~~~~~~~~~~~~~~~~~~~~ State handling ~~~~~~~~~~~~~~~~~~~~~~~~~~
   //

   /**
    * Calls the portlet onStateChange method in an asynchronous manner in order
    * to decouple the public API. This method is intended for use after a
    * portlet client registers an onStateChange listener.
    * <p>
    * In a real implementation, the portlet hub might need to communicate with
    * the portal server to obtain data.
    * 
    * @param {string}
    *            pid The portlet ID
    * @private
    */
   _updateStateForPortlet = function(pid) {
      var handle, qdata, dup, ii;

      console.log("_updateStateForPortlet", pid);

      // enqueue update elements for all listeners registered for pid,
      // adding each listener a single time only.
      for (handle in oscListeners) {
    	 dup = false;
         if (oscListeners.hasOwnProperty(handle)) {
            qdata = oscListeners[handle];
            if (qdata.id === pid) {
               for (ii = 0; ii < updateQueue.length; ii++) {
            	   if (updateQueue[ii].handle == handle) {
            		   dup = true;
            		   break;
            	   }
               }
               if (dup === false) {
            	  updateQueue.push(qdata);
                  console.log("Queued update: handle=" + qdata.handle
                        + ", pid=" + qdata.id + ", Queue length="
                        + updateQueue.length)
               }
            }
         }
      }

      if (updateQueue.length >= 1) {
         delay(function() {
            var qdata, state, data, callback, pi;

            // The busy flag is usually set by the caller.
            // but ... need more thought here. When the onStateChange
            // listener is added, the busy flag isn't set in order to avoid
            // blocking the state changes, since adding a listener doesn't
            // change the state. So it's set here, before onStateChange is
            // called so that the state change functions are blocked.
            busy = true;

            while (updateQueue.length > 0) {

               // get the next queue element and call its
               // onStateChange function
               qdata = updateQueue.shift();

               console.log("Dispatch update: handle=" + qdata.handle + ", pid="
                     + qdata.id + ", Queue length=" + updateQueue.length);

               pi = _registeredPortlets[qdata.id];

               state = new State(pi.getState());
               data = pi.getRenderData();
               callback = qdata.callback;

               if ((data !== undefined) && (data !== null)
                     && (data.content !== undefined)
                     && (data.content !== null)) {
                  callback("portlet.onStateChange", state, data);
               } else {
                  callback("portlet.onStateChange", state);
               }
            }
            busy = false;
         }, 0);
      }
   },

   // queue for client events
   ceQueue = [],

   /**
    * dispatches the client events.
    *
    * @param {string}
    *            type The portlet ID
    * @param {function}
    *            callback Callback function
    * @param {any}
    *            payload Event payload
    * @private
    */
   dispatchCE = function(type, callback, payload) {
      var evt = [];

      evt.push(callback);
      evt.push(type);
      evt.push(payload);

      ceQueue.push(evt);

      if (ceQueue.length === 1) {
         delay(function() {
            var event, aType, aCallback, aPayload;

            while (ceQueue.length > 0) {

               // dispatch next event
               event = ceQueue.shift();

               aCallback = event[0];
               aType = event[1];
               aPayload = event[2];

               aCallback(aType, aPayload);
            }
         }, 0);
      }
   },

   /**
    * Accepts an object containing changed render states. Updates the state for
    * each portlet present.
    *
    * @param {string}
    *            upids array of portlet IDs to be updated
    * @private
    */
   updatePageState = function(upids) {
      var ii;
      
      if (upids.length === 0) {
         busy = false;
      } else {
      for (ii = 0; ii < upids.length; ii++) {
         _updateStateForPortlet(upids[ii]);
      }
      }

   },

   /**
    * Accepts an object containing changed portlet IDs. This function is meant
    * to be used by the Portlet Hub impl in order to initiate an unsolicited
    * state update for the input list of portlet IDs.
    *
    * @returns {Promise} fulfilled with the actual upddate function when the hub
    *          is not busy.
    *
    * @private
    */
   updateWhenIdle = function(upids) {
      return new Promise(function(resolve) {
         function update() {
            if (busy) {
               delay(update, 20);
            } else {
               busy = true;
               resolve(updatePageState);
            }
         }
         update();
      });
   },

   /**
    * Updates the render state, taking the public render parameters into
    * account. The portlet client requesting the change, represented by the
    * supplied portelt ID, is updated, along with each portlet that has a public
    * render parameter update.
    * 
    * @param {string}
    *            pid The portlet ID
    * @param {RenderState}
    *            state The new render state
    * @throws  {AccessDeniedException}
    *             Thrown if a blocking operation is already in progress.
    * @throws  {NotInitializedException}
    *                   Thrown if a portlet ID is provided, but no onStateChange
    *                   listener has been registered.
    * @private
    */
   updateState = function(pid, state) {
      var pi;

      // do necessary checks

      if (busy === true) {
         throwAccessDeniedException("Operation in progress");
      } else if (!hasListener(pid)) {
         throwNotInitializedException("No onStateChange listener registered for portlet: "
               + pid);
      }

      busy = true;
      
      // Set state in the implementation. The setState function returns an
      // array of portlet
      // IDs for portlets that need to be updated. Update the page state using
      // this info.
      // If an error occurred, post the error to the onError handler, if one
      // is present.
      
      pi = _registeredPortlets[pid];
      pi.setState(state).then(function(upids) {
         updatePageState(upids);
      }, function(err) {
         busy = false;
         reportError(pid, err);
      });
       
   },


   // ~~~~~~~~~~~~~~~~~~~~~~ Other functions ~~~~~~~~~~~~~~~~~~~~~~~~~~

   /**
    * Used by the portlet hub methods to check the number and types of the
    * arguments.
    *
    * @private
    * @param {string[]}
    *            parms The argument list to be checked
    * @param {number}
    *            minParms The minimum number of arguments
    * @param {number}
    *            maxParms The maximum number of arguments. If this value is
    *           undefined, the function can take any number of arguments greater
    *           than numArgs
    * @param {string[]}
    *            types An array containing the expected parameter types in the
    *            order of occurrance in the argument array
    * @throws {TypeError}
    *                               Thrown if the parameters are in some manner incorrect
    */
   checkArguments = function(parms, minParms, maxParms, types) {

      // Check for the minimum number of arguments
      if (parms.length < minParms) {
         throw new TypeError(
               "Too few arguments provided. Number of arguments: "
               + parms.length);

         // check for maximum number of arguments
      } else if ((typeof maxParms === 'number') && (parms.length > maxParms)) {
         throw new TypeError("Too many arguments provided: "
               + [].join.call(parms, ', '));

         // check if the argument types are as expected if provided with
         // types
      } else if (types) {

         var ii;
         for (ii = Math.min(parms.length, types.length) - 1; ii >= 0; ii = ii - 1) {
            if (typeof parms[ii] !== types[ii]) {
               throw new TypeError("Parameter " + ii + " is of type "
                     + (typeof parms[ii]) + " rather than the expected type "
                     + types[ii]);
            }

            // If checking for types, also make sure the arguments are
            // neither
            // null nor undefined.
            if ((parms[ii] === null) || (parms[ii] === undefined)) {
               throw new TypeError("Argument is " + (typeof parms[ii]));
            }
         }
      }
   },

   /**
    * Verifies that the input parameters are in valid format.
    * <p>
    * Parameters must be an object containing parameter names. It may also
    * contain no property names which represents the case of having no
    * parameters.
    * <p>
    * If properties are present, each property must refer to a array of string
    * values. The array length must be at least 1, because each parameter must
    * have a value. However, a value of 'null' may appear in any array entry.
    * <p>
    * To represent a <code>null</code> value, the property value must equal
    * [null].
    *
    * @param {PortletParameters}
    *            parms The parameters to check
    * @private
    * @throws {TypeError}
    *             Thrown if the parameters are incorrect
    */
   validateParms = function(parms) {
      var parm;

      // check for null or undefined argument
      if ((parms === null) || (parms === undefined)) {
         throw new TypeError("The parameters object is " + (typeof parms));
      }

      for (parm in parms) {
         if (parms.hasOwnProperty(parm)) {
            if (Object.prototype.toString.call(parms[parm]) !== '[object Array]') {
               throw new TypeError("Invalid parameters. The value of " + parm
                     + " is " + Object.prototype.toString.call(parm)
                     + " rather than '[object Array]'");
            } else if (parms[parm].length === 0) {
               throw new TypeError("Invalid parameters. The value of " + parm
                     + " is " + "an array with length 0.");
            }
         }
      }
   },

   /**
    * Tests whether the given portlet mode is allowed
    */
   _isAllowedPM = function(pid, pm) {
      var pi = _registeredPortlets[pid];
      return (pi.getAllowedPM().indexOf(pm) >= 0);
   },

   /**
    * Tests whether the given window state is allowed
    */
   _isAllowedWS = function(pid, ws) {
      var pi = _registeredPortlets[pid];
      return (pi.getAllowedWS().indexOf(ws) >= 0);
   },

   /**
    * Verifies that the input parameters are in valid format, that the portlet
    * mode and window state values are allowed for the portlet.
    *
    * @param {string}
    *            pid The portlet ID
    * @param {RenderState}
    *            state The render state object to check
    * @private
    * @throws {TypeError}
    *             Thrown if any component of the state is incorrect
    */
   validateState = function(pid, state) {
      var pi = _registeredPortlets[pid];

      validateParms(state.parameters);

      // see if the portlet mode is a string and is a value allowed for the
      // portlet
      if ((state.portletMode === undefined)
            || (typeof state.portletMode !== 'string')) {
         throw new TypeError("Invalid parameters. portletMode is "
               + (typeof state.portletMode));
      } else {
         state.portletMode = state.portletMode.toLowerCase();
         if (!_isAllowedPM(pid, state.portletMode)) {
            throw new TypeError("Invalid portletMode=" + state.portletMode
                  + " is not in " + pi.getAllowedPM());
      }
      }
      
      // see if the windowState is a string and is a value allowed for the
      // portlet
      if ((state.windowState === undefined)
            || (typeof state.windowState !== 'string')) {
         throw new TypeError("Invalid parameters. windowState is "
               + (typeof state.windowState));
      } else {
         state.windowState = state.windowState.toLowerCase();
         if (!_isAllowedWS(pid, state.windowState)) {
            throw new TypeError("Invalid windowState=" + state.windowState
                  + " is not in " + pi.getAllowedWS());
      }
      }

   },


   /**
    * sets up for the action.
    *
    * @param {string}
    *            type The URL type
    * @param {string}
    *            pid The portlet ID
    * @param {PortletParameters}
    *            parms Additional parameters. May be <code>null</code>
    * @param {HTMLFormElement}
    *            Form to be submitted May be <code>null</code>
    * @throws  {AccessDeniedException}
    *             Thrown if a blocking operation is already in progress.
    * @throws  {NotInitializedException}
    *                   Thrown if a portlet ID is provided, but no onStateChange
    *                   listener has been registered.
    * @private
    */
   setupAction = function(pid, parms, element) {
      var pi;
      
      // do necessary checks

      if (busy === true) {
         throwAccessDeniedException("Operation in progress");
      } else if (!hasListener(pid)) {
         throwNotInitializedException("No onStateChange listener registered for portlet: "
               + pid);
      }

      busy = true;

      // execute the action. The promise is fulfilled with an array of IDs of
      // portlets to be updated.
      // If an error occurred, post the error to the onError handler, if one
      // is present.

      pi = _registeredPortlets[pid];
      return pi.executeAction(parms, element).then(function(upids) {
         return updatePageState(upids);
      }, function(err) {
         busy = false;
         reportError(pid, err);
      });

   },

   /**
    * Callback function that must be called after a partial action has been
    * started.
    * <p>
    * The page state is generated by the portal and transmitted to the client by
    * the portlet. The portlet client that initiated the partial action must
    * pass the page state string to this function.
    * <p>
    * The callback should only be called once to conclude a partial action
    * sequence.
    * 
    * @param {string}
    *            pid The portlet ID for operation
    * @param {string}
    *            ustr The new page state in string form
    * @throws {TypeError}
    *                      Thrown if the parameter is not a string
    * @name       setPageState
    * @callback   setPageState
    */
   setPageState = function(pid, ustr) {
      var pi;

      // Perform some checks on the update string. allow null string.
      if ((ustr === undefined)
            || ((ustr !== null) && (typeof ustr !== 'string'))) {
         throw new TypeError("Invalid update string: " + ustr);
      }

      // convert page state into an object.
      // update each affected portlet client. Makes use of a 
      // mockup-specific function for decoding. 

      pi = _registeredPortlets[pid];
      pi.decodeUpdateString(ustr).then(function(upids) {
      updatePageState(upids);
      }, function(err) {
         busy = false;
         reportError(pid, err);
      });

   };

   // ~~~~~~~~~~~~~~~~ Register function ~~~~~~~~~~~~~~~~~~~

   /**
    * Registers a portlet client with the portlet hub.
    * <p>
    * The portlet client calling this method must provide a valid portlet ID.
    * The portlet ID is identical to the unique namespace provided by the portal
    * server for the portlet.
    *
    * @param {string}
    *            portletId The unique portlet identifier
    * @returns {Promise} A Promise object. Returns an {@link PortletInit} object
    *          containing functions for use by the portlet client on successful
    *          resolution. Returns an Error object containing a descriptive
    *          message on failure.
    */
   portlet.register = function(portletId) {

      console.log("register", portletId);

      // check for exactly 1 argument of type 'string'
      checkArguments(arguments, 1, 1, [ 'string' ]);

      /**
       * Dispatched to the implementation. The promise will be fulfilled as soon
       * as the implementation has assembled the required information. From that
       * point on it is valid to call into other functions on the
       * implementation. These other functions as passed in as an argument
       */
      return portlet.impl
            .register(portletId, updateWhenIdle)
            .then(
                  function(portletImpl) {

                     console.log("register implementation is ready ...",
                           portletImpl);

                     /**
                      * Keep track of the callback functions for our portlets
                      */
                     _registeredPortlets[portletId] = portletImpl;

                     /**
                      * Returned by the {@link portlet.register} method to
                      * provide functions for use by the portlet client.
                      * 
                      * @namespace PortletInit
                      */
                     return {
         
            /**
                         * A string array containing the portlet modes that are
                         * defined for the portlet.
             * <p>
                         * Note that even if a portlet mode is defined, it may
                         * not be allowed, depending on access rights or other
                         * conditions.
             *
                         * @property {string[]} portletModes The defined portlet
                         *           mode values
             * @memberOf         PortletInit
             */
            portletModes : portletImpl.getAllowedPM(),
         
            /**
                         * A string array containing the window states that are
                         * defined for the portlet.
             * <p>
                         * Note that even if a window state is defined, it may
                         * not be allowed, depending on access rights or other
                         * conditions.
             *
                         * @property {string[]} windowStates The defined window
                         *           state values
             * @memberOf         PortletInit
             */
            windowStates : portletImpl.getAllowedWS(),
         
            /**
                         * Adds a listener function for specified event type.
             * <p>
                         * The portlet hub defines two classes of events -
                         * System Events and Portlet Client events:
             * <dl>
             * <dt>System Events</dt>
                         * <dd> Events that are generated by the portlet hub.
                         * They are used to pass portlet-specific information to
                         * the registered portlet client. The parameters passed
                         * to the system event callback functions are defined by
                         * the portlet hub.
                         * <p>
                         * Event types prefixed with "portlet." are reserved for
                         * system events. System event types may not be
                         * specified with a regular expression or wildcard.
                         * However, the same event listener may be added for
                         * both types of system events.
             * <p>
                         * Only one listener for each type of system event may
                         * be added.
             * <p>
             * The following system event types are defined:
             * <dl>
             * <dt>portlet.onStateChange</dt>
                         * <dd> Fired when the render state changes. In order to
                         * participate in the portlet Ajax support, a portlet
                         * client must register an {@link onStateChange} event
                         * listener for this event type.
                         * <p>
                         * After the portlet client adds an event listener for
                         * the onStateChange event, the portlet hub will call
                         * the onStateChange callback function to provide the
                         * portlet client with its initial state information.
                         * However, this will not occur before the call to
                         * addEventListener returns. </dd>
             * <dt>portlet.onError</dt>
                         * <dd> Fired when an error occurs that cannot be
                         * communicated through an exception. In general, this
                         * will be some type of asynchronous communication
                         * error. In order to receive notification about errors,
                         * a portlet must register an {@link onError} event
                         * listener for this event type. </dd>
             * </dl>
             * </dd>
             * <dt>Portlet Client Events</dt>
                         * <dd> Events initiated by the portlet client through
                         * the dispatch method.
             * <p>
                         * When adding a listener for a portlet client event,
                         * the event type may be specified by a regular
                         * expression string. The listener will be called for
                         * every event type that the regular expression string
                         * matches.
                         * <p>
                         * Example: <br>
                         * myHub.addEventListener("^myCompany\..*", myListener); //
                         * registers myListener for all event types beginning
                         * with "myCompany." </dd>
             * </dl>
             * <p>
                         * An event listener can be added for multiple event
                         * types. This function returns a handle to identify the
                         * unique listener for the event type and for the
                         * portlet client associated with the function.
                         * 
                         * @param {string}
                         *            type The type of listener
                         * @param {function}
                         *           func Function called when event occurs
             *
                         * @returns {object} A handle that can be used to remove
                         *          the event listener
             *
                         * @throws {TypeError}
                         *            Thrown if the input parameters are invalid
             *
             * @memberOf            PortletInit
             */
                        addEventListener : function(type, func) {
         
                           // check for exactly 2 arguments of type
                           // 'string' and 'function'
                           checkArguments(arguments, 2, 2, [ 'string',
                                 'function' ]);
         
               var handle, listeners;
         
               if (type.match(portletRegex)) {
         
                  // Handle adding system event listener.
                              // if it is neither a
                              // portlet.onStateChange nor a
                              // portlet.onError event, throw
                              if ((type !== "portlet.onStateChange")
                                    && (type !== "portlet.onError")) {
                                 throw new TypeError(
                                       "The system event type is invalid: "
                                       + type);
                  }
         
                  if (type === "portlet.onStateChange") {
                                 listeners = oscListeners; // add
                                                      // the
                                                      // onStateChange
                                                      // listener
                  } else {
                                 listeners = oeListeners; // add
                                                      // the
                                                      // onError
                                                      // listener
                  }
         
                              // will throw an exception if the
                              // listener has already been added
                              handle = addSystemListener(listeners, portletId,
                                    type, func);
         
                              // If just added an onStateChange
                              // listener, update the state
                  if (type === "portlet.onStateChange") {
                     _updateStateForPortlet(portletId);
                  }
         
               } else {
         
                  // Portlet client event listener
                              handle = addPCListener(pcListeners, portletId,
                                    type, func);
               }
         
               return handle;
            },
         
            /**
                         * Removes a previously added listener function
                         * designated by the handle. The handle must be the same
                         * object previously returned by the addEventListener
                         * function.
                         * 
                         * @param {object}
                         *           handle The handle of the listener to be
                         *           removed
             *
                         * @throws {TypeError}
                         *            Thrown if the input parameters are invalid
             * @throws  {AccessDeniedException}
                         *            Thrown if the event listener associated
                         *            with this handle was registered by a
                         *            different portlet
             *
             * @memberOf            PortletInit
             */
                        removeEventListener : function(handle) {
         
                           // check for exactly 1 argument. Don't check
                           // the argument type.
               checkArguments(arguments, 1, 1, []);
         
               // check for null or undefined argument
                           if ((handle === null) || (handle === undefined)) {
                              throw new TypeError("The argument provided is "
                                    + (typeof handle));
                           }

                           // Try to remove the listener from each of
                           // the listener arrays
                           // one after the other. If not successful,
                           // the handle was invalid
                           // (or an exception will be thrown by
                           // removePCListener), so throw.

                           if ((removePCListener(pcListeners, portletId, handle) === false)
                                 && (removeSystemListener(oscListeners,
                                 portletId, handle) === false)
                                 && (removeSystemListener(oeListeners,
                                       portletId, handle) === false)) {
         
                              throw new TypeError(
                                    "The event listener handle doesn't match any listeners.");
               }
            },
         
            /**
                         * Sets the render state, which consists of the public
                         * and private render parameters, the portlet mode, and
                         * the window state.
                         * <p>
                         * When the render state has been successfully set, a
                         * copy of the render state will be provided to the
                         * portlet client through the {@link onStateChange}
                         * listener function. A portlet client can modify this
                         * state object and pass it to the setRenderState
                         * function to update the render state.
                         * <p>
                         * However, it is also possible for the portal to
                         * completely refresh the page as a response to setting
                         * the render state. This may occur in order to support
                         * portlets on the page that do not participate in the
                         * Portlet 3.0 Ajax support or due to configuration
                         * settings, for example.
                         * <p>
                         * If the page is completely refreshed, it will be
                         * rendered according to render parameters set on the
                         * server.
                         * <p>
                         * Setting the render state is a blocking operation. To
                         * allow for orderly state transitions, the portlet hub
                         * does not allow this function to be used while a
                         * blocking operation is in progress. A blocking
                         * operation is considered to be in progress from the
                         * initial call until the final onStateChange event for
                         * that operation has been fired. See {@link portlet}
                         * for further information.
             *
                         * @param {RenderState}
                         *            state The new state to be set
             *
                         * @throws {TypeError}
                         *            Thrown if the input parameters are invalid
             * @throws  {AccessDeniedException}
                         *            Thrown if a blocking operation is already
                         *            in progress.
             * @throws  {NotInitializedException}
                         *            Thrown if a portlet ID is provided, but no
                         *            onStateChange listener has been
                         *            registered.
             *
             * @memberOf   PortletInit
             */
                        setRenderState : function(state) {

               console.log("setRenderState", state);

                           // check for exactly 1 argument of type
                           // 'object'
                           // make sure an onStateChange listener is
                           // registered by providing portlet ID
                           checkArguments(arguments, 1, 1, [ 'object' ]);
         
                           // validate render state & throw exception
                           // if incorrect
               validateState(portletId, state);
         
                           // Ok so far, so do the update. Throws if
                           // busy or no callback registered
               updateState(portletId, state);
         
            },


            /**
                         * Returns a promise for a resource URL with parameters
                         * set appropriately for the page state according to the
                         * resource parameters, cacheability option, and
                         * resource ID provided.
                         * <p>
                         * The portlet client may use the resource URL with any
                         * appropriate javascript framework to retrieve content
                         * from the portlet through the server-side
                         * serveResource method.
                         * <p>
                         * Resource parameters are optional parameters attached
                         * to a resource URL in addition to any render state
                         * values that may be present. Resource parameters do
                         * not influence the render state.
                         * <p>
                         * The resource parameters must be an object containing
                         * properties representing parameter names whose values
                         * must be an array of string values, as described under
                         * {@link PortletParameters}. All of the resource
                         * parameters will be attached to the URL. Use of
                         * resource parameters is optional.
                         * <p>
                         * The cacheability option designates the degree to
                         * which the content to be served can be cached and
                         * influences the type of content that can be served.
                         * There are three possible values:
             * <dl>
                         * <dd>"full"</dd>
                         * <dt> Most cacheable, because the URL contains no
                         * portlet-specific or page-specific information. </dt>
                         * <dd>"portlet"</dd>
                         * <dt> More cacheable, because the URL contains only
                         * portlet-specific but no page-specific information.
                         * </dt>
                         * <dd>"page"</dd>
                         * <dt> Least cacheable because the URL contains
                         * portlet-specific and page-specific information.
                         * <p>
                         * However, cacheability must be set to "page" if the
                         * content to be served contains portal URLs. </dt>
             * </dl>
             * <p>
                         * Specification of cacheability is optional. If the
                         * cacheability is not specified, cacheability for the
                         * URL will be set to "PAGE".
                         * <p>
                         * The resource ID is an additional identifying string
                         * that the resource serving method on the server can
                         * use to determine the information to be provided. The
                         * resource ID argument is optional.
                         * <p>
                         * The parameters must be provided in the defined order.
                         * However, if a preceding argument is not needed, it
                         * may be specified as <code>null</code>. Examples of
                         * valid calls: <code>
             * <br>createResourceUrl();
             * <br>createResourceUrl(resParams, cache);
                         * <br>createResourceUrl(resParams, cache, resid);
             * <br>createResourceUrl(resParams);
                         * <br>createResourceUrl(<code>null</code>, cache);
                         * <br>createResourceUrl(<code>null</code>, <code>null</code>, resid);
             * </code>
             *
                         * @param {PortletParameters}
                         *           resParams Resource parameters to be added
                         *           to the URL
                         * @param {string}
                         *           cache Cacheability option. The strings
                         *           defined under {@link PortletConstants}
                         *           should be used to specifiy cacheability.
                         * @param {string}
                         *            resid Resource ID.
                         * 
                         * @returns {Promise} A Promise object. Returns a string
                         *          representing the resource URL on successful
                         *          resolution. Returns an Error object
                         *          containing a descriptive message on failure.
             *
                         * @throws {TypeError}
                         *            Thrown if the input parameters are invalid
             *
             * @memberOf   PortletInit
             */
                        createResourceUrl : function(resParams, cache, resid) {
                           var ii, arg, cacheability = null, pi, rid = null;

                           // check arguments. make sure there is a
                           // maximum of two
                           // args and determine the types. Check
                           // values as possible.
                           if (arguments.length > 3) {
                              throw new TypeError(
                                    "Too many arguments. 3 arguments are allowed.");
                           }

                           if (resParams) {
                              if (typeof resParams === 'object') {
                                 validateParms(resParams); // throws
                                                      // if
                                                      // parms
                                                      // are
                                                      // invalid
                              } else {
                                 throw new TypeError(
                                       "Invalid argument type. Resource parameters must be a parameters object.");
                              }
               }
         
                           if (cache) {
                              if (typeof cache === 'string') {
                                 switch (cache) {
                     case "cacheLevelPage":
                     case "cacheLevelPortlet":
                     case "cacheLevelFull":
                                    cacheability = cache;
                        break;
                     default:
                                    throw new TypeError(
                                          "Invalid cacheability argument: "
                                          + cache);
                     }
                   } else {
                                 throw new TypeError(
                                       "Invalid argument type. Cacheability argument must be a string.");
                  }
               }
               
               // fallback to page level cacheability
               if (!cacheability) {
                  cacheability = "cacheLevelPage";
               }
         
                           if (resid) {
                              if (typeof resid === 'string') {
                                 rid = resid;
                              } else {
                                 throw new TypeError(
                                       "Invalid argument type. Resource ID argument must be a string.");
                              }
                           }

               // everything ok, so get URL from the impl
               pi = _registeredPortlets[portletId];
                           return pi.getUrl("RESOURCE", resParams,
                                 cacheability, rid);
            },
         
            /**
                         * Initiates a portlet action using the specified action
                         * parameters and element arguments.
             * <p>
                         * When the action has successfully completed, a copy of
                         * the render state will be provided to the portlet
                         * client through the {@link onStateChange} listener
                         * function. A portlet client can modify this state
                         * object and pass it to the setRenderState function to
                         * update the render state.
                         * <p>
                         * However, it is also possible for the portal to
                         * completely refresh the page as a response to the
                         * action. This may occur in order to support portlets
                         * on the page that do not participate in the Portlet
                         * 3.0 Ajax support or due to configuration settings,
                         * for example.
                         * <p>
                         * If the page is completely refreshed, it will be
                         * rendered according to render parameters set on the
                         * server.
                         * <p>
                         * Action parameters are optional parameters attached to
                         * a action URL in addition to any render state values
                         * that may be present. Action parameters do not
                         * influence the render state.
                         * <p>
                         * The action parameters must be an object containing
                         * properties representing parameter names whose values
                         * must be an array of string values, as described under
                         * {@link PortletParameters}. All of the action
                         * parameters will be attached to the URL. Use of action
                         * parameters is optional.
                         * <p>
                         * If the <code>element</code> argument is present, it
                         * must refer to an HTML form to be submitted. The
                         * portlet hub will use this form to execute the action.
                         * <p>
                         * If the form element is specified, the encoding type
                         * must be 'application/x-www-form-urlencoded' or
                         * 'multipart/form-data'. The encoding type 'text/plain'
                         * is not supported.
                         * <p>
                         * If the encoding type is 'multipart/form-data', the
                         * submission method must be 'POST'. Form 'INPUT'
                         * elements of type 'FILE' are supported.
                         * <p>
                         * If the encoding type is
                         * 'application/x-www-form-urlencoded', the submission
                         * method can be either 'GET' or 'POST'. However, form
                         * 'INPUT' elements of type 'FILE' are not supported.
                         * <p>
                         * Specification of <code>element</code> is optional.
                         * If the <code>element</code> is not specified, the
                         * portlet hub will submit the action to the server by
                         * executing a 'POST' with an action URL containing any
                         * action parameters provided.
                         * <p>
                         * The parameters may be specified in either order,
                         * individually, or not at all. Examples of valid calls:
                         * <code>
             * <br>action();
             * <br>action(actParams, element);
             * <br>action(actParams);
             * <br>action(element);
             * </code>
             * <p>
                         * A portlet action is a blocking operation. To allow
                         * for orderly state transitions, the portlet hub does
                         * not allow this function to be used while a blocking
                         * operation is in progress. A blocking operation is
                         * considered to be in progress from the initial call
                         * until the final onStateChange event for that
                         * operation has been fired. See {@link portlet} for
                         * further information.
             * <p>
             * 
             *
                         * @param {PortletParameters}
                         *           actParams Action parameters to be added to
                         *           the URL (optional)
                         * @param {HTMLFormElement}
                         *           element DOM element of form to be submitted
                         *           (optional)
                         * 
                         * @returns {Promise} A Promise object that is resolved
                         *          with no argument when the action request has
                         *          completed.
             *
                         * @throws {TypeError}
                         *            Thrown if the input parameters are invalid
             * @throws  {AccessDeniedException}
                         *            Thrown if a blocking operation is already
                         *            in progress.
             * @throws  {NotInitializedException}
                         *            Thrown if a portlet ID is provided, but no
                         *            onStateChange listener has been
                         *            registered.
             *
             * @memberOf   PortletInit
             */
                        action : function(actParams, element) {
               var ii, arg, type, parms = null, el = null, meth;
         
                           console.log("Executing action for portlet: "
                                       + portletId);
                           // check arguments. make sure there is a
                           // maximum of two
                           // args and determine the types. Check
                           // values as possible.
               if (arguments.length > 2) {
                              throw new TypeError(
                                    "Too many arguments. 2 arguments are allowed.");
               }
         
               ii = arguments.length;
                           while (--ii >= 0) {
                  arg = arguments[ii];
                              type = Object.prototype.toString.call(arg);
                  if (type === '[object HTMLFormElement]') {
                     if (el === null) {
                        el = arg;
                     } else {
                                    throw new TypeError(
                                          "too many [object HTMLFormElement] arguments: "
                               + arg + ", " + el);
                     }
                  } else if (type === '[object Object]') {
                                 validateParms(arg); // throws if
                                                // parms are
                                                // invalid
                     if (parms !== null) {
                                    throw new TypeError(
                                          "too many parameters arguments.");
                     }
                     parms = arg;
                              } else if (arg !== undefined) {
                                 throw new TypeError(
                                       "Invalid argument type. Argument "
                                             + (ii + 1) + " is of type " + type);
                  }
               }
               
                           // if we're dealing with a form, verify
                           // method and enctype
               console.log("form element", el);
               
               if (el) {
                              meth = el.method ? el.method.toUpperCase()
                                    : undefined;
            	   
                              if (meth && (meth !== 'POST') && (meth !== 'GET')) {
                                 throw new TypeError("Invalid form method "
                                       + el.method
                                       + ". Allowed methods are GET & POST ");
                              }

                              // allow the default, which is
                              // 'application/x-www-form-urlencoded'
                              // encoded, and also multipart/form-data
                              if (el.enctype
                                    && el.enctype !== 'application\/x-www-form-urlencoded'
                                    && el.enctype !== 'multipart\/form-data') {
                                 throw new TypeError(
                                       "Invalid form enctype "
                                       + el.enctype
                                       + ". Allowed: 'application\/x-www-form-urlencoded' & 'multipart\/form-data'  ");
                              }

                              if (el.enctype
                                    && (el.enctype === 'multipart\/form-data')
                                    && (meth !== 'POST')) {
                                 throw new TypeError(
                                       "Invalid method with multipart/form-data. Must be POST.");
                  }
                  
                              // if the data is supposed to be
                              // urlencoded, we don't suport FILE
                              // element
                              if (!el.enctype
                                    || (el.enctype === 'application\/x-www-form-urlencoded')) {
                     for (ii = 0; ii < el.elements.length; ii++) {
                                    if (el.elements[ii].nodeName.toUpperCase() === 'INPUT'
                                          && el.elements[ii].type.toUpperCase() === 'FILE') {
                                       throw new TypeError(
                                             "Must use enctype = 'multipart/form-data' with input type FILE.");
                        }
                     }
                  }
              }
         
               // everything ok, so do the action
         
               return setupAction(portletId, parms, el);
            },
         
            /**
             * Starts partial action processing and returns a
             * {@link PartialActionInit} object to the caller. The
                         * <code>PartialActionInit</code> object contains a
                         * partial action URL and a {@setPageState} callback
                         * function. The partial action URL can be used to
                         * initiate an action request. The {@setPageState}
                         * callback function allows the portlet client to
                         * complete the partial action operation by updating the
                         * state of all portlet clients on the page that are
                         * affected by action and event processing on the server
                         * resulting from the partial action.
                         * <p>
                         * After the <code>setPageState</code> function has
                         * been called, a copy of the render state will be
                         * provided to each affected portlet client through the
                         * {@link onStateChange} listener function.
                         * <p>
                         * In addition, the {@link onStateChange} listener
                         * function for the initiating portlet will be provided
                         * with a {@link RenderData} object, which contains the
                         * same data as would be available through a portlet
                         * resource request using the current render state with
                         * no additional resource parameters and with the
                         * resource URL cacheability option set to "PAGE".
                         * <p>
                         * Note that HTTP header information for the partial
                         * action response is set by the action processing
                         * method on the server. Any header information set by
                         * the resource method called to obtain the
                         * {@link RenderData} will be lost. If the portlet
                         * client needs access to the header data set by the
                         * resource method, it should create a resource URL and
                         * fetch the resource data on its own when the
                         * {@link onStateChange} listener function is called
                         * after the partial action has been performed.
                         * <p>
                         * However, it is also possible for the portal to
                         * completely refresh the page as a response to the
                         * action. This may occur in order to support portlets
                         * on the page that do not participate in the Portlet
                         * 3.0 Ajax support or due to configuration settings,
                         * for example.
                         * <p>
                         * If the page is completely refreshed, it will be
                         * rendered according to render parameters set on the
                         * server.
                         * <p>
                         * Action parameters are optional parameters attached to
                         * a action URL in addition to any render state values
                         * that may be present. Action parameters do not
                         * influence the render state.
                         * <p>
                         * The action parameters must be an object containing
                         * properties representing parameter names whose values
                         * must be an array of string values, as described under
                         * {@link PortletParameters}. All of the action
                         * parameters will be attached to the URL. Use of action
                         * parameters is optional.
             * <p>
                         * A partial action is a blocking operation. To allow
                         * for orderly state transitions, the portlet hub does
                         * not allow this function to be used while a blocking
                         * operation is in progress. A blocking operation is
                         * considered to be in progress from the initial call
                         * until the final onStateChange event for that
                         * operation has been fired. See {@link portlet} for
                         * further information.
             * 
             * @param {PortletParameters}
                         *           actParams Action parameters to be added to
                         *           the URL (optional)
             * 
             * @returns {Promise} A Promise object. Returns a
                         *          {PortletActionInit} object containing a
                         *          partial action URL and the setPageState
                         *          callback function on successful resolution.
                         *          Returns an Error object containing a
             *          descriptive message on failure.
             * 
             * @throws {TypeError}
             *            Thrown if the input parameters are invalid
             * @throws {AccessDeniedException}
                         *            Thrown if a blocking operation is already
                         *            in progress.
             * @throws {NotInitializedException}
             *            Thrown if a portlet ID is provided, but no
                         *            onStateChange listener has been
                         *            registered.
             * 
             * @memberOf PortletInit
             */
                        startPartialAction : function(actParams) {
               var  parms = null, paObj = {}, pi;
         
                           // check arguments. make sure there is a
                           // maximum of two
                           // args and determine the types. Check
                           // values as possible.
               if (arguments.length > 1) {
                              throw new TypeError(
                                    "Too many arguments. 1 arguments are allowed.");
               } else if (actParams !== undefined) {
                  if (typeof actParams === 'object') {
                                 validateParms(actParams); // throws
                                                      // if
                                                      // parms
                                                      // are
                                                      // invalid
                     parms = actParams;
                  } else {
                                 throw new TypeError(
                                       "Invalid argument type. Argument is of type "
                           + (typeof actParams));
                  }
               }
         
               // make sure operation is allowed
               if (busy === true) {
                  throwAccessDeniedException("Operation in progress");
                           } else if (!hasListener(portletId)) {
                  throwNotInitializedException("No onStateChange listener registered for portlet: "
                        + portletId);
               }
         
               busy = true;
         
                           // Create the PartialActionInit object
                           // fulfill promise by passing it.
               // let errors propagate to caller.
               
               pi = _registeredPortlets[portletId];
                           return pi.getUrl("PARTIAL_ACTION", parms).then(
                                       function(url) {
                  paObj.url = url;
                                    paObj.setPageState = function(ustr) {
                                       setPageState(portletId, ustr);
                                          };
                  return paObj;
               });
         
            },
         
            /**
                         * Tests whether a blocking operation is in progress.
             * <p>
                         * The portlet client can use this function to test
                         * whether a state change is in progress before
                         * initiating a blocking operation.
                         * <p>
                         * Note that if the portlet client uses this function to
                         * implement a waiting function, the render state may be
                         * changed during the time that the portlet client
                         * waits. Also note that the portal may choose to
                         * refresh the page as a response to a blocking
                         * operation, in which case the waiting function would
                         * not complete.
                         * <p>
                         * The portlet hub provides for orderly state
                         * transitions by allowing only a single blocking
                         * operation ({@link action}, {@link setRenderState},
                         * {@link startPartialAction}) to be active at any one
                         * time.
                         * <p>
                         * The state transition is considered to be active from
                         * the initial portlet client call to one of the
                         * blocking operations until the portlet hub has
                         * performed the requested state change and has informed
                         * all of the affected portlet clients by firing the
                         * corresponding onStateChange events.
             * <p>
             * See {@link portlet} for further information.
             *
                         * @returns {boolean} <code>true</code> if a blocking
                         *          operation is in progress
             *
             * @memberOf   PortletInit
             */
                        isInProgress : function() {
               return busy;
            },
         
            /**
             * Dispatches a client event.
             * <p>
                         * Client events of the specified type are queued for
                         * delivery to registered event listeners of that type.
                         * <p>
                         * The event type is matched against the type strings
                         * associated with registered event listeners. An event
                         * will be dispatched for each matching listener. The
                         * number of matching listeners will be returned.
                         * <p>
                         * The event payload is defined by the dispatcher of the
                         * event. It must be present, but may be of any type or
                         * value.
                         * <p>
                         * The portlet Client may not dispatch event types
                         * beginning with the reserved string "portlet.".
                         * <p>
                         * The client is responsible for preventing race
                         * conditions. For example, a race condition can occur
                         * if portlet A dispatches an event to portlet B,
                         * causing an event to portlet A, which dispatches again
                         * to portlet B, etc.
                         * <p>
                         * Event delivery cannot be guaranteed, and may vary
                         * according to the situation.
                         * <p>
                         * Take for example a scenario where a portlet issues an
                         * event to several other portlets, perhaps including
                         * itself. Due to the event, each portlet updates
                         * parameters through use of the {@link setRenderState}
                         * function.
                         * <p>
                         * The <code>setRenderState</code> function can cause
                         * a page refresh depending on the situation. If all
                         * portlets on the page participate in the JSR 362 Ajax
                         * support, the portlet hub might not cause a page
                         * refresh. However, if there are legacy portlets to be
                         * supported, the portal or portlet hub might choose to
                         * refresh the page.
                         * <p>
                         * In the latter case, this means that the first portlet
                         * receiving the event will be able to successfully
                         * update its parameters, but the remaining portlets
                         * will not. This can have unintentional consequences.
                         * 
                         * @param {string}
                         *            type The type of listener
                         * @param {any}
                         *           payload The payload to be delivered
             *
                         * @returns {number} The number of events queued for
                         *          delivery
             *
             * @memberOf   PortletInit
             */
                        dispatchClientEvent : function(type, payload) {
               var cnt = 0, li;
         
                           // check for 2 arguments, the first of which
                           // must be a string
                           checkArguments(arguments, 2, 2, [ 'string' ]);
         
                           // disallow use of reserved name for system
                           // event types
               if (type.match(portletRegex)) {
                              throw new TypeError("The event type is invalid: "
                                    + type);
               }
         
               for (li in pcListeners) {
                  if (pcListeners.hasOwnProperty(li)) {
         
                                 if (type.match(pcListeners[li].type) !== null) {
                                    dispatchCE(type, pcListeners[li].callback,
                                          payload);
                        cnt++;
                     }
                  }
               }
         
               return cnt;
            },
            
            /**
                         * Creates and returns a new PortletParameters object.
             * <p>
                         * If no argument is provided, an empty
                         * PortletParameters object will be returned. If an
                         * existing PortletParameters object is provided as
                         * argument, a clone of the input object will be
                         * returned.
                         * 
                         * @param {PortletParameters}
                         *           p An optional PortletParameters object to
                         *           be copied
             *
                         * @returns {PortletParameters} The new parameters
                         *          object
             *
             * @memberOf   PortletInit
             */
                        newParameters : function(p) {
                           var n, newparams = {};
                           if (p) {
                              for (n in p) {
                                 if (p.hasOwnProperty(n) && Array.isArray(p[n])) {
                                    newparams[n] = p[n].slice(0);
                                 }
                              }
                           }
                           return newparams;
            },
            
            /**
             * Creates and returns a new RenderState object.
             * <p>
                         * If no argument is provided, an empty RenderState
                         * object will be returned. If an existing RenderState
                         * object is provided as argument, a clone of the input
                         * object will be returned.
                         * 
                         * @param {RenderState}
                         *           s An optional RenderState object to be
                         *           copied
             *
                         * @returns {RenderState} The new RenderState object
             *
             * @memberOf   PortletInit
             */
                        newState : function(s) {
               return new State(s);
            },
            
            /**
                         * The {@link PortletConstants} object that provides
                         * useful field definitions for portlet mode, window
                         * state, and resource URL cacheability settings.
             *
             * @memberOf   PortletInit
             */
            constants : portletConstants
         
         };
      });
   };

}());