Source: src/main/javascript/oracle/oj/ojcomponentcore/Components.js

Oracle® JavaScript Extension Toolkit (JET)
1.1.2

E65298-01

/*
** Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
**
**34567890123456789012345678901234567890123456789012345678901234567890123456789
*/


/**
 * @class JET Component services
 * @export
 */
oj.Components = {};


/**
 * Sets default options values for JET components.
 * @param {Object} options - property values that will be merged into the values
 * that were previously set using this method. The options object is expected to have the format demonstrated
 * by the following example:
 * <pre>
 * {
 *   'default': // properties for all JET components 
 *   {
 *     'option1': 'somevalue'
 *   },
 *   'editableValue': // properties for editableValue components 
 *   {
 *     'option1': 'somevalue1',
 *     'option2': oj.Components.createDynamicPropertyGetter(function(context){
 *                                 return context['containers'].indexOf('ojTable') >= 0 ? 'tableValue' : 'normalValue'})
 *   },
 *   'ojText': // properties for instances of ojText 
 *   {
 *     'option1': 'somevalue2'
 *   }
 * }
 * </pre>
 * To specify a dynamic getter for the property, pass your callback to oj.Components.createDynamicPropertyGetter(). Note
 * that dynamic getters nested within a complex property value are not supported
 * @see oj.Components.createDynamicPropertyGetter
 * @export
 */
oj.Components.setDefaultOptions = function(options)
{
  oj.Components._defaultProperties = $.widget.extend(oj.Components._defaultProperties || {}, options);
};

/**
 * Retrieves default option values for JET components.
 * @return {Object} default option values
 * @see oj.Components.setDefaultOptions
 * @export
 */
oj.Components.getDefaultOptions = function()
{
  return (oj.Components._defaultProperties || {});
};


/**
 * Creates a dynamic getter that can be used as a property value in oj.Components.setDefaultOptions()
 * @param {!Function} callback - dynamic property callback. The callback will receive a context object as a parameter.
 * The following properties are currently supported on the context object:
 * <ul>
 * <li>containers - an array of component names of the current component's containers that require special behavior from
 * their children</li>
 * </ul>
 * The callback should return the computed property value
 * 
 * @return {Object} - dynamic property getter
 * @see oj.Components.setDefaultOptions
 * @export
 */
oj.Components.createDynamicPropertyGetter = function(callback)
{
  return new __ojDynamicGetter(callback);
};

/**
 * Retrieves widget constructor associated with the HTML element
 * or null if none is found. The returned constructor is already bound to the associated 
 * JQuery element, so it can be invoked as a function directly. For example:
 * <pre>
 * widgetConstructor("option", "label", "custom"); // sets label option
 * </pre>
 * If widgetName is not specified, and if more than one widget is associated with the element, 
 * the method will a return the widget that was created first.
 * @param {Element} element - HTML element
 * @param {string=} widgetName - optional widget name
 * @return {Function|null} widget constructor
 * @export
 */
oj.Components.getWidgetConstructor = function(element, widgetName)
{
  var jelem = $(element);
  
  if (widgetName == null)
  {
    var data = jelem.data(_OJ_WIDGET_NAMES_DATA);
    if (data)
    {
      widgetName = data[0]; 
    }
  }
  
  if (widgetName != null)
  {
    var func = jelem[widgetName];
    if ((typeof func) === "function")
    {
      return func.bind(jelem);
    }
  }
  
  return null;
};

/**
 * Notifies JET framework that a subtree possibly containing JET components has been inserted
 * into the document programmatically.
 *
 * Note that there is no need to call this method when the new DOM is being inserted by the template engine
 * in Knockout.js 
 * @param {!Element} node - the root of the subtree
 * @see oj.Components.subtreeDetached
 * @export
 */
oj.Components.subtreeAttached = function(node)
{
  oj.DomUtils.fixResizeListeners(node);
  _applyToComponents(node,
    function(instance)
    {
      instance._NotifyAttached();
    }
  );
};

/**
 * Notifies JET framework that a subtree possibly containing JET components has been removed
 * from the document programmatically.
 * 
 * Note that calling this method is not needs after calling JQuery's .remove() because all JET components would have been 
 * already destroyed in that case. Similarly, there is no need to call this method after the subtree has been removed by 
 * Knockout.js
 * @param {!Element} node - the root of the subtree
 * @see oj.Components.subtreeAttached
 * @export
 */
oj.Components.subtreeDetached = function(node)
{
  _applyToComponents(node,
    function(instance)
    {
      instance._NotifyDetached();
    }
  );
};

/**
 * Notifies JET framework that a subtree possibly containing JET components is no longer hidden with display:none style
 * This method should be called by the application if the 'display' style is being changed from 'hidden' programmatically,
 * such as when JQuery's .show() method is called
 * 
 * @param {!Element} node - the root of the subtree
 * @see oj.Components.subtreeHidden
 * @export
 */
oj.Components.subtreeShown = function(node)
{
  oj.DomUtils.fixResizeListeners(node);
  
  _applyToComponents(node,
    function(instance)
    {
      instance._NotifyShown();
    }
  );
};


/**
 * Notifies JET framework that a subtree possibly containing JET components has been hidden  with display:none style
 * This method should be called by the application after the subtree has been hidden programmatically, such as 
 * when JQuery's .hide() method is called.
 * 
 * @param {!Element} node - the root of the subtree
 * @see oj.Components.subtreeShown
 * @export
 */
oj.Components.subtreeHidden = function(node)
{
  _applyToComponents(node,
    function(instance)
    {
      instance._NotifyHidden();
    }
  );
};


/**
 * Determines if a component identified by the <code>widgetName</code> has been
 * bound and initialized on a given <code>jelement</code>.
 *
 * @param {jQuery} jelement to which the component is bound
 * @param {string} widgetName constructor name of the target component.
 * @return {boolean} <code>true</code> if the component identified by the widgetName
 *  has be bound and initialized to the target element.
 */
oj.Components.isComponentInitialized = function(jelement, widgetName)
{
  /** @type {?} */ 
  var widgets = jelement.data(_OJ_WIDGET_NAMES_DATA);
  if ($.isArray(widgets) && widgets.indexOf(widgetName) > -1 && jelement.is('.' + _OJ_COMPONENT_NODE_CLASS))
    return true;
  else
    return false;
}

/**
 * @private
 */
function _applyToComponents(subtreeRoot, callback)
{
  var processFunc = function() 
  {
    var jelem =  $(this);
    var names = jelem.data(_OJ_WIDGET_NAMES_DATA);
    if (names != null)
    {
      for (var i=0; i < names.length; i++)
      {
        var instance =  jelem.data("oj-" + names[i]);
        if (instance != null)
        {
          callback(instance);
        }
      }
    }
  };
  
  var locator = $(subtreeRoot);
  
  // Include the root node itself, and not just children (bug #19707849)
  if (locator.hasClass(_OJ_COMPONENT_NODE_CLASS))
  {
    processFunc.call(subtreeRoot);
  }
  
  locator.find('.' + _OJ_COMPONENT_NODE_CLASS).each(processFunc);
}


/**
 * @constructor
 * @param {!Function} callback
 * @private
 */
function __ojDynamicGetter(callback)
{
  this.getCallback = function()
  {
    return callback;
  }
};



/**
 * @private
 */
oj.Components._OJ_CONTAINER_ATTR = "data-oj-container";

/**
 * @private
 */
var _OJ_WIDGET_NAMES_DATA = "oj-component-names";

/**
 * @private
 */
var _OJ_COMPONENT_NODE_CLASS = "oj-component-initnode";