/**
* Copyright (c) 2014, Oracle and/or its affiliates.
* All rights reserved.
*/
/**
* @ojcomponent oj.dvtBaseComponent
* @augments oj.baseComponent
* @since 0.7
* @abstract
*/
oj.__registerWidget('oj.dvtBaseComponent', $['oj']['baseComponent'], {
//** @inheritdoc */
_ComponentCreate : function() {
this._super();
// Create a reference div within the element to be used for computing relative event coords.
this._referenceDiv = $(document.createElement("div"));
this._referenceDiv.attr("style", "visibility:hidden;");
this.element.append(this._referenceDiv);
// Create the DvtContext, which creates the svg element and adds it to the DOM.
var parentElement = this.element[0].parentElement;
if (parentElement && parentElement._dvtcontext)
this._context = parentElement._dvtcontext;
else
this._context = new dvt.DvtContext(this.element[0], null, this._referenceDiv[0]);
// Set the reading direction on the context
this._context.setReadingDirection(this._GetReadingDirection());
// Set the tooltip and datatip callbacks and div style classes
this._context.setTooltipAttachedCallback(oj.Components.subtreeAttached);
this._context.setTooltipStyleClass('oj-dvt-tooltip');
this._context.setDatatipStyleClass('oj-dvt-datatip');
// Set high contrast mode if needed
if ($(document.body).hasClass('oj-hicontrast'))
dvt.DvtAgent.setHighContrast(true);
// Create and cache the component instance
this._component = this._CreateDvtComponent(this._context, this._HandleEvent, this);
// Add the component to the display tree of the rendering context.
this._context.getStage().addChild(this._component);
// Set the helpers for locale support
this._setLocaleHelpers();
// Retrieve and apply the translated strings onto the component bundle
this._processTranslations();
// Load component resources
this._LoadResources();
},
//** @inheritdoc */
_AfterCreate : function ()
{
// Allow superclass to process root attributes and context menus
this._super();
this.element.attr("tabIndex", 0);
// Render the component
this._Render();
// Add resize listener with 250ms delay
this._resizeListener = this._handleResize.bind(this);
oj.DomUtils.addResizeListener(this.element[0], this._resizeListener, 250);
},
//** @inheritdoc */
refresh : function() {
this._super();
// Update the reading direction on the context
this._context.setReadingDirection(this._GetReadingDirection());
// Retrieve and apply the translated strings onto the component bundle
this._processTranslations();
// Render the component with any changes
this._Render();
},
//** @inheritdoc */
getNodeBySubId : function(locator) {
var automation = (this._component && this._component.getAutomation) ? this._component.getAutomation() : null;
if (automation) {
// Convert the locator to the subid string, since the shared JS layer only accepts the subid string syntax.
var subId = this._ConvertLocatorToSubId(locator);
return automation.getDomElementForSubId(subId);
}
},
//** @inheritdoc */
getSubIdByNode : function(node) {
var automation = (this._component && this._component.getAutomation) ? this._component.getAutomation() : null;
if (automation) {
// Retrieve the subid string from the shared JS layer, and convert it to the locator object.
var subId = automation.getSubIdForDomElement(node);
return subId ? this._ConvertSubIdToLocator(subId) : null;
}
},
/**
* Converts the specified locator object into a subId string.
* @param {Object} locator
* @return {string|null}
* @protected
* @instance
* @memberof oj.dvtBaseComponent
*/
_ConvertLocatorToSubId : function(locator) {
// subclasses must override to support getNodeBySubId
return null;
},
/**
* Converts the specified subId string into a locator object.
* @param {string} subId
* @return {Object|null}
* @protected
* @instance
* @memberof oj.dvtBaseComponent
*/
_ConvertSubIdToLocator : function(subId) {
// subclasses must override to support getSubIdByNode
return null;
},
/**
* Create dummy divs for style classes and merge style class values with json .
* options object
* @protected
* @instance
* @memberof oj.dvtBaseComponent
*/
_ProcessStyles : function() {
// Append the component style classes to the element
var componentStyles = this._GetComponentStyleClasses();
for(var i=0; i<componentStyles.length; i++) {
this.element.addClass(componentStyles[i]);
}
// Process selectors for this component
DvtStyleProcessor.processStyles(this.element, this.options,
this._GetComponentStyleClasses(),
this._GetChildStyleClasses());
},
/**
* Returns the style classes associated with the component.
* @return {Array}
* @protected
* @instance
* @memberof oj.dvtBaseComponent
*/
_GetComponentStyleClasses : function() {
return ['oj-dvtbase'];
},
/**
* Returns a map of the style classes associated with a component's children.
* @return {Object}
* @protected
* @instance
* @memberof oj.dvtBaseComponent
*/
_GetChildStyleClasses : function() {
return {};
},
/**
* Returns an array of supported event types. Used in conjunction with _setOptions to skip unnecessary rendering when
* event listeners are bound. Subclasses must override to return supported event types.
* @return {Array}
* @protected
* @instance
* @memberof oj.dvtBaseComponent
*/
_GetEventTypes : function() {
return [];
},
/**
* Returns a map containing keys corresponding to the string ids in ojtranslations.js and values corresponding to the
* toolkit constants for the DvtBundle objects. This map must be guaranteed to be a new instance so that subclasses can
* add their translations to it.
* @return {Object}
* @protected
* @instance
* @memberof oj.dvtBaseComponent
*/
_GetTranslationMap: function() {
// The translations are stored on the options object.
var translations = this.options['translations'];
// Create the mapping to return.
var ret = {
'DvtUtilBundle.CLEAR_SELECTION': translations['labelClearSelection'],
'DvtUtilBundle.COLON_SEP_LIST' : translations['labelAndValue'],
'DvtUtilBundle.INVALID_DATA': translations['labelInvalidData'],
'DvtUtilBundle.NO_DATA': translations['labelNoData'],
// Accessibility
'DvtUtilBundle.DATA_VISUALIZATION': translations['labelDataVisualization'],
'DvtUtilBundle.STATE_SELECTED': translations['stateSelected'],
'DvtUtilBundle.STATE_UNSELECTED': translations['stateUnselected'],
'DvtUtilBundle.STATE_MAXIMIZED': translations['stateMaximized'],
'DvtUtilBundle.STATE_MINIMIZED': translations['stateMinimized'],
'DvtUtilBundle.STATE_EXPANDED': translations['stateExpanded'],
'DvtUtilBundle.STATE_COLLAPSED': translations['stateCollapsed'],
'DvtUtilBundle.STATE_ISOLATED': translations['stateIsolated'],
'DvtUtilBundle.STATE_HIDDEN': translations['stateHidden'],
'DvtUtilBundle.STATE_VISIBLE': translations['stateVisible'],
'DvtUtilBundle.SCALING_SUFFIX_THOUSAND': translations['labelScalingSuffixThousand'],
'DvtUtilBundle.SCALING_SUFFIX_MILLION': translations['labelScalingSuffixMillion'],
'DvtUtilBundle.SCALING_SUFFIX_BILLION': translations['labelScalingSuffixBillion'],
'DvtUtilBundle.SCALING_SUFFIX_TRILLION': translations['labelScalingSuffixTrillion'],
'DvtUtilBundle.SCALING_SUFFIX_QUADRILLION': translations['labelScalingSuffixQuadrillion']
};
// Add abbreviated month strings
var monthNames = oj.LocaleData.getMonthNames("abbreviated");
ret['DvtUtilBundle.MONTH_SHORT_JANUARY'] = monthNames[0];
ret['DvtUtilBundle.MONTH_SHORT_FEBRUARY'] = monthNames[1];
ret['DvtUtilBundle.MONTH_SHORT_MARCH'] = monthNames[2];
ret['DvtUtilBundle.MONTH_SHORT_APRIL'] = monthNames[3];
ret['DvtUtilBundle.MONTH_SHORT_MAY'] = monthNames[4];
ret['DvtUtilBundle.MONTH_SHORT_JUNE'] = monthNames[5];
ret['DvtUtilBundle.MONTH_SHORT_JULY'] = monthNames[6];
ret['DvtUtilBundle.MONTH_SHORT_AUGUST'] = monthNames[7];
ret['DvtUtilBundle.MONTH_SHORT_SEPTEMBER'] = monthNames[8];
ret['DvtUtilBundle.MONTH_SHORT_OCTOBER'] = monthNames[9];
ret['DvtUtilBundle.MONTH_SHORT_NOVEMBER'] = monthNames[10];
ret['DvtUtilBundle.MONTH_SHORT_DECEMBER'] = monthNames[11];
return ret;
},
/**
* Retrieves the translated resource with the specified
* @param {string} key The key used to retrieve the translated resource.
* @param {Array.<string>} params The array of named parameters that need to be converted into index based parameters.
* @protected
* @instance
* @memberof oj.dvtBaseComponent
*/
_GetTranslatedResource: function(key, params) {
var translatedResource = this.options['translations'][key];
// If named parameters are defined, replace with index based params
if(params) {
var paramMap = {};
for(var i=0; i<params.length; i++) {
paramMap[params[i]] = '{' + i + '}';
}
translatedResource = oj.Translations.applyParameters(translatedResource, paramMap);
}
return translatedResource;
},
/**
* Called to process the translated strings for this widget.
* @private
*/
_processTranslations: function() {
// Retrieve the map of translation keys + DvtBundle identifiers
var translationMap = this._GetTranslationMap();
// Register with the DvtBundle
dvt.DvtBundle.addLocalizedStrings(translationMap);
},
/**
* @private
* @instance
* @memberof! oj.dvtBaseComponent
*/
_setLocaleHelpers : function () {
var helpers = {};
// Number converter factory for use in formatting default strings
helpers['numberConverterFactory'] = oj.Validation.getDefaultConverterFactory('number');
// Date to iso converter to be called before passing to the date time converter
helpers['dateToIsoConverter'] = function(input) {
return (input instanceof Date) ? oj.IntlConverterUtils.dateToLocalIso(input) : input;
}
// Iso to date converter to be called for JS that requires Dates
helpers['isoToDateConverter'] = function(input) {
return (typeof(input) == 'string') ? oj.IntlConverterUtils.isoToLocalDate(input) : input;
}
this._context.setLocaleHelpers(helpers);
},
//** @inheritdoc */
_destroy : function() {
// Hide all component tooltips
this._context.hideTooltips();
// Call destroy on the JS component
if (this._component.destroy)
this._component.destroy();
// Remove DOM resize listener
oj.DomUtils.removeResizeListener(this.element[0], this._resizeListener);
// Remove children and clean up DOM changes
this.element.children().remove();
this.element.removeAttr('role').removeAttr('tabIndex');
// Remove style classes that were added
var componentStyles = this._GetComponentStyleClasses();
for(var i=0; i<componentStyles.length; i++) {
this.element.removeClass(componentStyles[i]);
}
// Call super last for destroy
this._super();
},
//** @inheritdoc */
_setOptions : function(options, flags) {
// Call the super to update the property values
this._superApply(arguments);
// Render the component with the updated options.
if(this._bUserDrivenChange) {
// Option change fired in response to user gesture. Already reflected in UI, so no render needed.
return;
}
else {
// Event listeners don't require rendering. Iterate through options to check for non-event options.
// Also no render is needed if the component has exposed a method to update the option without rerendering.
var bRenderNeeded = false;
var eventTypes = this._GetEventTypes();
var optimizedOptions = ['highlightedCategories', 'selection', 'dataCursorPosition'];
$.each(options, function(key, value) {
if(eventTypes.indexOf(key) < 0 && optimizedOptions.indexOf(key) < 0) {
bRenderNeeded = true;
return false;
}
});
if(bRenderNeeded)
this._Render();
else {
// Update options without rerendering. Check for undefined to allow nulls.
if (options['highlightedCategories'] !== undefined)
this._component.highlight(options['highlightedCategories']);
if (options['selection'] !== undefined)
this._component.select(options['selection']);
if (options['dataCursorPosition'] !== undefined && this._component.positionDataCursor)
this._component.positionDataCursor(options['dataCursorPosition']);
}
}
},
/**
* Called by _create to instantiate the specific DVT component instance. Subclasses must override.
* @param {dvt.DvtContext} context
* @param {Function} callback
* @param {Object} callbackObj
* @protected
* @instance
* @memberof oj.dvtBaseComponent
*/
_CreateDvtComponent : function(context, callback, callbackObj) {
return null; // subclasses must override
},
/**
* Called by the component to process events. Subclasses should override to delegate DVT component events to their
* JQuery listeners.
* @param {Object} event
* @protected
* @instance
* @memberof oj.dvtBaseComponent
*/
_HandleEvent : function(event) {
// TODO: hiddenCategories and highlightedCategories should use DvtOptionChangeEvent
var type = event && event.getType ? event.getType() : null;
if (type === dvt.DvtCategoryHideShowEvent.TYPE_HIDE || type === dvt.DvtCategoryHideShowEvent.TYPE_SHOW) {
this._UserOptionChange('hiddenCategories', event['hiddenCategories']);
}
else if (type === dvt.DvtCategoryRolloverEvent.TYPE_OVER || type === dvt.DvtCategoryRolloverEvent.TYPE_OUT) {
this._UserOptionChange('highlightedCategories', event['categories']);
}
else if (type === dvt.DvtOptionChangeEvent.TYPE) {
this._UserOptionChange(event['key'], event['value'], event['optionMetadata']);
}
else if (this.options['contextMenu'] && type === dvt.DvtComponentTouchEvent.TOUCH_HOVER_END_TYPE) {
this._OpenContextMenu($.Event(event.getNativeEvent()), 'touch');
}
},
/**
* Called when the component is resized.
* @param {number} width
* @param {number} height
* @private
* @instance
* @memberof oj.dvtBaseComponent
*/
_handleResize : function(width, height) {
// Render the component at the new size if it changed enough
var newWidth = this.element.width();
var newHeight = this.element.height();
if(this._width == null || this._height == null || (Math.abs(newWidth - this._width) + Math.abs(newHeight - this._height) >= 5))
this._Render(true);
},
/**
* Called once during component creation to load resources.
* @protected
* @instance
* @memberof oj.dvtBaseComponent
*/
_LoadResources : function() {
// subcomponents should override
},
/**
* Called to render the component at the current size.
* @param {boolean} isResize (optional) Whether it is a resize rerender.
* @protected
* @instance
* @memberof oj.dvtBaseComponent
*/
_Render : function(isResize) {
// Fix 18498656: If the component is not attached to a visible subtree of the DOM, rendering will fail because
// getBBox calls will not return the correct values. Log an error message in this case and avoid rendering.
// Note: Checking offsetParent() does not work here since it returns false for position: fixed.
if(!this._context.isReadyToRender()){
oj.Logger.info(this.options['translations']['messageNotReadyToRender']['summary']);
this._renderNeeded = true;
}
else {
// Add the width, height, and locale as private fields in the options for debugging purposes
this._width = this.element.width();
this._height = this.element.height();
this.options['_width'] = this._width;
this.options['_height'] = this._height;
this.options['_locale'] = oj.Config.getLocale();
// Merge css styles with with json options object
this._ProcessStyles();
// Render the component. Skip the options on resize to suppress animation.
this._component.render(isResize ? null : this.options, this._width, this._height);
this._renderNeeded = false;
}
},
//** @inheritdoc */
_NotifyShown: function()
{
this._super();
if(this._renderNeeded)
this._Render();
},
//** @inheritdoc */
_NotifyAttached: function()
{
this._super();
if(this._renderNeeded)
this._Render();
},
//** @inheritdoc */
_NotifyDetached : function() {
this._super();
this._context.hideTooltips();
},
//** @inheritdoc */
_NotifyHidden : function() {
this._super();
this._context.hideTooltips();
},
/**
* Sets an option change that was driven by user gesture. Used in conjunction with _setOption to ensure that the
* correct optionMetadata flag for writeback is set.
* @param {string} key The name of the option to set.
* @param {Object} value The value to set for the option.
* @param {Object} optionMetadata The optionMetadata for the optionChange event
* @memberof oj.dvtBaseComponent
* @instance
* @protected
*/
_UserOptionChange : function(key, value, optionMetadata) {
this._bUserDrivenChange = true;
this.option(key, value, {'_context': {writeback:true, optionMetadata: optionMetadata, internalSet: true}});
this._bUserDrivenChange = false;
},
//** @inheritdoc */
_NotifyContextMenuGesture: function(menu, event, eventType) {
// DVTs support context menus on touch hold release which is detected by the
// toolkit and handled in _HandleEvent after receiving touch hold release event.
if (eventType === "touch")
return;
// Position context menus relative to the current keyboard focus when keyboard triggered
else if (eventType === "keyboard")
this._OpenContextMenu(event, eventType, {"position": {"of": this._component.getKeyboardFocus()}});
else
this._super(menu, event, eventType);
},
/**
* Returns a DVT component associated with a DOMElement
* @param {Element} element The DOMElement to get the DVT component from.
* @return {Object} The DVT component associated with the DOMElement or null
* @memberof oj.dvtBaseComponent
* @instance
* @protected
*/
_GetDvtComponent: function(element) {
var widget = oj.Components.getWidgetConstructor(element)("instance");
if (widget) {
return widget._component;
}
return null;
},
/**
* Adds getters for the properties on the specified map.
* @param {Object|null} map
* @memberof oj.dvtBaseComponent
* @instance
* @protected
*/
_AddAutomationGetters: function(map) {
if(!map)
return;
// Bug 20884377: Adds legacy getters to the object maps returned by the automation code. These getters are
// deprecated and will be removed in 1.2.0.
var props = {};
for (var key in map) {
this._addGetter(map, key, props);
}
Object.defineProperties(map, props);
},
/**
* Adds getter for the specified property on the specified properties map.
* @param {Object} map
* @param {string} key
* @param {Object} props The properties map onto which the getter will be added.
* @memberof oj.dvtBaseComponent
* @instance
* @private
*/
_addGetter: function(map, key, props) {
var prefix = (key == 'selected') ? 'is' : 'get';
var getterName = prefix + key.charAt(0).toUpperCase() + key.slice(1);
props[getterName] = {'value': function() { return map[key] }};
},
/**
* Converts an indexPath array to a string of the form '[index0][index1]...[indexN]'.
* @param {Array} indexPath
* @return {string} The resulting string.
* @instance
* @protected
*/
_GetStringFromIndexPath : function(indexPath) {
var ret = '';
for(var i=0; i<indexPath.length; i++) {
ret += '[' + indexPath[i] + ']';
}
return ret;
},
/**
* Converts a string containing indices in the form '[index0][index1]...[indexN]' to an array of indices.
* @param {string} subId
* @return {Array} The resulting array to be used for locator indexPath.
* @instance
* @protected
*/
_GetIndexPath : function(subId) {
var indexPath = [];
var currentIndex = 0;
while(subId.indexOf('[', currentIndex) > 0) {
var start = subId.indexOf('[', currentIndex) + 1;
var end = subId.indexOf(']', currentIndex)
indexPath.push(Number(subId.substring(start, end)));
currentIndex = end + 1;
}
return indexPath;
},
/**
* Converts a string containing a single index in the form '[index]' into the numerical index.
* @param {string} subId
* @return {number}
* @instance
* @protected
*/
_GetFirstIndex : function(subId) {
return Number(this._GetFirstBracketedString(subId));
},
/**
* Returns the first bracketed substring in the specified string.
* @param {string} subId
* @return {string}
* @instance
* @protected
*/
_GetFirstBracketedString : function(subId) {
var start = subId.indexOf('[') + 1;
var end = subId.indexOf(']');
return subId.substring(start, end);
}
}, true);
Source: src/main/javascript/oracle/oj/ojdvt-base/ojdvt-base.js
Oracle® JavaScript Extension Toolkit (JET)
1.1.2
E65298-01