devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
969 lines (799 loc) • 31.3 kB
JavaScript
"use strict";
var $ = require("../../core/renderer"),
eventsEngine = require("../../events/core/events_engine"),
errors = require("./ui.errors"),
Action = require("../../core/action"),
extend = require("../../core/utils/extend").extend,
inArray = require("../../core/utils/array").inArray,
each = require("../../core/utils/iterator").each,
commonUtils = require("../../core/utils/common"),
typeUtils = require("../../core/utils/type"),
domUtils = require("../../core/utils/dom"),
domAdapter = require("../../core/dom_adapter"),
devices = require("../../core/devices"),
DOMComponent = require("../../core/dom_component"),
Template = require("./jquery.template"),
FunctionTemplate = require("./function_template"),
EmptyTemplate = require("./empty_template"),
ChildDefaultTemplate = require("./child_default_template"),
KeyboardProcessor = require("./ui.keyboard_processor"),
selectors = require("./selectors"),
eventUtils = require("../../events/utils"),
hoverEvents = require("../../events/hover"),
feedbackEvents = require("../../events/core/emitter.feedback"),
clickEvent = require("../../events/click"),
inflector = require("../../core/utils/inflector");
var UI_FEEDBACK = "UIFeedback",
WIDGET_CLASS = "dx-widget",
ACTIVE_STATE_CLASS = "dx-state-active",
DISABLED_STATE_CLASS = "dx-state-disabled",
INVISIBLE_STATE_CLASS = "dx-state-invisible",
HOVER_STATE_CLASS = "dx-state-hover",
FOCUSED_STATE_CLASS = "dx-state-focused",
FEEDBACK_SHOW_TIMEOUT = 30,
FEEDBACK_HIDE_TIMEOUT = 400,
FOCUS_NAMESPACE = "Focus",
ANONYMOUS_TEMPLATE_NAME = "template",
TEXT_NODE = 3,
TEMPLATE_SELECTOR = "[data-options*='dxTemplate']",
TEMPLATE_WRAPPER_CLASS = "dx-template-wrapper";
var DX_POLYMORPH_WIDGET_TEMPLATE = new FunctionTemplate(function (options) {
var widgetName = options.model.widget;
if (widgetName) {
var widgetElement = $("<div>"),
widgetOptions = options.model.options || {};
if (widgetName === "button" || widgetName === "tabs" || widgetName === "dropDownMenu") {
var deprecatedName = widgetName;
widgetName = inflector.camelize("dx-" + widgetName);
errors.log("W0001", "dxToolbar - 'widget' item field", deprecatedName, "16.1", "Use: '" + widgetName + "' instead");
}
widgetElement[widgetName](widgetOptions);
return widgetElement;
}
return $();
});
/**
* @name ui
* @publicName ui
* @section utils
*/
/**
* @name dxItem
* @publicName dxItem
* @type object
* @section uiWidgetMarkupComponents
*/
/**
* @name Widget
* @publicName Widget
* @type object
* @inherits DOMComponent
* @module ui/widget/ui.widget
* @export default
* @hidden
*/
var Widget = DOMComponent.inherit({
_supportedKeys: function _supportedKeys() {
return {};
},
_getDefaultOptions: function _getDefaultOptions() {
return extend(this.callBase(), {
/**
* @name WidgetOptions.disabled
* @publicName disabled
* @type boolean
* @default false
*/
disabled: false,
/**
* @name WidgetOptions.visible
* @publicName visible
* @type boolean
* @default true
*/
visible: true,
/**
* @name WidgetOptions.hint
* @publicName hint
* @type string
* @default undefined
*/
hint: undefined,
/**
* @name WidgetOptions.activeStateEnabled
* @publicName activeStateEnabled
* @type boolean
* @default false
*/
activeStateEnabled: false,
/**
* @name WidgetOptions.onContentReady
* @publicName onContentReady
* @extends Action
* @action
*/
onContentReady: null,
/**
* @name WidgetOptions.hoverStateEnabled
* @publicName hoverStateEnabled
* @type boolean
* @default false
*/
hoverStateEnabled: false,
/**
* @name WidgetOptions.focusStateEnabled
* @publicName focusStateEnabled
* @type boolean
* @default false
*/
focusStateEnabled: false,
/**
* @name WidgetOptions.tabIndex
* @publicName tabIndex
* @type number
* @default 0
*/
tabIndex: 0,
/**
* @name WidgetOptions.accessKey
* @publicName accessKey
* @type string
* @default null
*/
accessKey: null,
/**
* @name WidgetOptions.onFocusIn
* @publicName onFocusIn
* @extends Action
* @action
* @hidden
*/
onFocusIn: null,
/**
* @name WidgetOptions.onFocusOut
* @publicName onFocusOut
* @extends Action
* @action
* @hidden
*/
onFocusOut: null,
integrationOptions: {
watchMethod: function watchMethod(fn, callback, options) {
options = options || {};
if (!options.skipImmediate) {
callback(fn());
}
return commonUtils.noop;
},
templates: {
"dx-polymorph-widget": DX_POLYMORPH_WIDGET_TEMPLATE
},
createTemplate: function createTemplate(element) {
return new Template(element);
}
},
_keyboardProcessor: undefined
/**
* @name template
* @publicName template
* @type String|function|Node|jQuery
* @section Common
*/
/**
* @name format
* @publicName format
* @type Enums.Format|string|function|Object
* @type_function_param1 value:number|date
* @type_function_return string
* @default undefined
* @section Common
*/
/**
* @name format.type
* @publicName type
* @type Enums.Format
*/
/**
* @name format.precision
* @publicName precision
* @type number
*/
/**
* @name format.currency
* @publicName currency
* @type String
*/
/**
* @name format.formatter
* @publicName formatter
* @type function
* @type_function_param1 value:number|date
* @type_function_return string
*/
/**
* @name format.parser
* @publicName parser
* @type function
* @type_function_param1 value:string
* @type_function_return number|date
*/
});
},
_feedbackShowTimeout: FEEDBACK_SHOW_TIMEOUT,
_feedbackHideTimeout: FEEDBACK_HIDE_TIMEOUT,
_init: function _init() {
this.callBase();
this._tempTemplates = [];
this._defaultTemplates = {};
this._initTemplates();
this._initContentReadyAction();
},
_initTemplates: function _initTemplates() {
this._extractTemplates();
this._extractAnonymousTemplate();
},
_extractTemplates: function _extractTemplates() {
var templates = this.option("integrationOptions.templates"),
templateElements = this.$element().contents().filter(TEMPLATE_SELECTOR);
var templatesMap = {};
templateElements.each(function (_, template) {
var templateOptions = domUtils.getElementOptions(template).dxTemplate;
if (!templateOptions) {
return;
}
if (!templateOptions.name) {
throw errors.Error("E0023");
}
$(template).addClass(TEMPLATE_WRAPPER_CLASS).detach();
templatesMap[templateOptions.name] = templatesMap[templateOptions.name] || [];
templatesMap[templateOptions.name].push(template);
});
each(templatesMap, function (templateName, value) {
var deviceTemplate = this._findTemplateByDevice(value);
if (deviceTemplate) {
templates[templateName] = this._createTemplate(deviceTemplate);
}
}.bind(this));
},
_findTemplateByDevice: function _findTemplateByDevice(templates) {
var suitableTemplate = commonUtils.findBestMatches(devices.current(), templates, function (template) {
return domUtils.getElementOptions(template).dxTemplate;
})[0];
each(templates, function (index, template) {
if (template !== suitableTemplate) {
$(template).remove();
}
});
return suitableTemplate;
},
_extractAnonymousTemplate: function _extractAnonymousTemplate() {
var templates = this.option("integrationOptions.templates"),
anonymousTemplateName = this._getAnonymousTemplateName(),
$anonymousTemplate = this.$element().contents().detach();
var $notJunkTemplateContent = $anonymousTemplate.filter(function (_, element) {
var isTextNode = element.nodeType === TEXT_NODE,
isEmptyText = $(element).text().trim().length < 1;
return !(isTextNode && isEmptyText);
}),
onlyJunkTemplateContent = $notJunkTemplateContent.length < 1;
if (!templates[anonymousTemplateName] && !onlyJunkTemplateContent) {
templates[anonymousTemplateName] = this._createTemplate($anonymousTemplate);
}
},
_getAriaTarget: function _getAriaTarget() {
return this._focusTarget();
},
_getAnonymousTemplateName: function _getAnonymousTemplateName() {
return ANONYMOUS_TEMPLATE_NAME;
},
_getTemplateByOption: function _getTemplateByOption(optionName) {
return this._getTemplate(this.option(optionName));
},
_getTemplate: function _getTemplate(templateSource) {
if (typeUtils.isFunction(templateSource)) {
return new FunctionTemplate(function (options) {
var templateSourceResult = templateSource.apply(this, this._getNormalizedTemplateArgs(options));
if (!typeUtils.isDefined(templateSourceResult)) {
return new EmptyTemplate();
}
var dispose = false;
var template = this._acquireTemplate(templateSourceResult, function (templateSource) {
if (templateSource.nodeType || typeUtils.isRenderer(templateSource) && !$(templateSource).is("script")) {
return new FunctionTemplate(function () {
return templateSource;
});
}
dispose = true;
return this._createTemplate(templateSource);
}.bind(this));
var result = template.render(options);
dispose && template.dispose && template.dispose();
return result;
}.bind(this));
}
return this._acquireTemplate(templateSource, this._createTemplateIfNeeded.bind(this));
},
_acquireTemplate: function _acquireTemplate(templateSource, createTemplate) {
if (templateSource == null) {
return new EmptyTemplate();
}
if (templateSource instanceof ChildDefaultTemplate) {
return this._defaultTemplates[templateSource.name];
}
// TODO: templateSource.render is needed for angular2 integration. Try to remove it after supporting TypeScript modules.
if (typeUtils.isFunction(templateSource.render) && !typeUtils.isRenderer(templateSource)) {
return templateSource;
}
if (templateSource.nodeType || typeUtils.isRenderer(templateSource)) {
templateSource = $(templateSource);
return createTemplate(templateSource);
}
if (typeof templateSource === "string") {
var userTemplate = this.option("integrationOptions.templates")[templateSource];
if (userTemplate) {
return userTemplate;
}
var dynamicTemplate = this._defaultTemplates[templateSource];
if (dynamicTemplate) {
return dynamicTemplate;
}
return createTemplate(templateSource);
}
return this._acquireTemplate(templateSource.toString(), createTemplate);
},
_createTemplateIfNeeded: function _createTemplateIfNeeded(templateSource) {
var templateKey = function templateKey(templateSource) {
return typeUtils.isRenderer(templateSource) && templateSource[0] || templateSource;
};
var cachedTemplate = this._tempTemplates.filter(function (t) {
templateSource = templateKey(templateSource);
return t.source === templateSource;
})[0];
if (cachedTemplate) return cachedTemplate.template;
var template = this._createTemplate(templateSource);
this._tempTemplates.push({ template: template, source: templateKey(templateSource) });
return template;
},
_createTemplate: function _createTemplate(templateSource) {
templateSource = typeof templateSource === "string" ? domUtils.normalizeTemplateElement(templateSource) : templateSource;
return this.option("integrationOptions.createTemplate")(templateSource);
},
_getNormalizedTemplateArgs: function _getNormalizedTemplateArgs(options) {
var args = [];
if ("model" in options) {
args.push(options.model);
}
if ("index" in options) {
args.push(options.index);
}
args.push(options.container);
return args;
},
_cleanTemplates: function _cleanTemplates() {
this._tempTemplates.forEach(function (t) {
t.template.dispose && t.template.dispose();
});
this._tempTemplates = [];
},
_initContentReadyAction: function _initContentReadyAction() {
this._contentReadyAction = this._createActionByOption("onContentReady", {
excludeValidators: ["designMode", "disabled", "readOnly"]
});
},
_initMarkup: function _initMarkup() {
this.$element().addClass(WIDGET_CLASS);
this._toggleDisabledState(this.option("disabled"));
this._toggleVisibility(this.option("visible"));
this._renderHint();
if (this._isFocusable()) {
this._renderFocusTarget();
}
this.callBase();
},
_render: function _render() {
this.callBase();
this._renderContent();
this._renderFocusState();
this._attachFeedbackEvents();
this._attachHoverEvents();
},
_renderHint: function _renderHint() {
domUtils.toggleAttr(this.$element(), "title", this.option("hint"));
},
_renderContent: function _renderContent() {
var that = this;
commonUtils.deferRender(function () {
that._renderContentImpl();
});
that._fireContentReadyAction();
},
_renderContentImpl: commonUtils.noop,
_fireContentReadyAction: function _fireContentReadyAction() {
this._contentReadyAction();
},
_dispose: function _dispose() {
this._cleanTemplates();
this._contentReadyAction = null;
this.callBase();
},
_clean: function _clean() {
this._cleanFocusState();
this.callBase();
this.$element().empty();
},
_toggleVisibility: function _toggleVisibility(visible) {
this.$element().toggleClass(INVISIBLE_STATE_CLASS, !visible);
this.setAria("hidden", !visible || undefined);
},
_renderFocusState: function _renderFocusState() {
this._attachKeyboardEvents();
if (!this._isFocusable()) {
return;
}
this._renderFocusTarget();
this._attachFocusEvents();
this._renderAccessKey();
},
_renderAccessKey: function _renderAccessKey() {
var focusTarget = this._focusTarget();
focusTarget.attr("accesskey", this.option("accessKey"));
var clickNamespace = eventUtils.addNamespace(clickEvent.name, UI_FEEDBACK);
eventsEngine.off(focusTarget, clickNamespace);
this.option("accessKey") && eventsEngine.on(focusTarget, clickNamespace, function (e) {
if (eventUtils.isFakeClickEvent(e)) {
e.stopImmediatePropagation();
this.focus();
}
}.bind(this));
},
_isFocusable: function _isFocusable() {
return this.option("focusStateEnabled") && !this.option("disabled");
},
_eventBindingTarget: function _eventBindingTarget() {
return this.$element();
},
_focusTarget: function _focusTarget() {
return this._getActiveElement();
},
_getActiveElement: function _getActiveElement() {
var activeElement = this._eventBindingTarget();
if (this._activeStateUnit) {
activeElement = activeElement.find(this._activeStateUnit).not("." + DISABLED_STATE_CLASS);
}
return activeElement;
},
_renderFocusTarget: function _renderFocusTarget() {
this._focusTarget().attr("tabIndex", this.option("tabIndex"));
},
_keyboardEventBindingTarget: function _keyboardEventBindingTarget() {
return this._eventBindingTarget();
},
_detachFocusEvents: function _detachFocusEvents() {
var $element = this._focusTarget(),
namespace = this.NAME + FOCUS_NAMESPACE,
focusEvents = eventUtils.addNamespace("focusin", namespace);
focusEvents = focusEvents + " " + eventUtils.addNamespace("focusout", namespace);
if (domAdapter.hasDocumentProperty("onbeforeactivate")) {
focusEvents = focusEvents + " " + eventUtils.addNamespace("beforeactivate", namespace);
}
eventsEngine.off($element, focusEvents);
},
_attachFocusEvents: function _attachFocusEvents() {
var namespace = this.NAME + FOCUS_NAMESPACE,
focusInEvent = eventUtils.addNamespace("focusin", namespace),
focusOutEvent = eventUtils.addNamespace("focusout", namespace);
var $focusTarget = this._focusTarget();
eventsEngine.on($focusTarget, focusInEvent, this._focusInHandler.bind(this));
eventsEngine.on($focusTarget, focusOutEvent, this._focusOutHandler.bind(this));
if (domAdapter.hasDocumentProperty("onbeforeactivate")) {
var beforeActivateEvent = eventUtils.addNamespace("beforeactivate", namespace);
eventsEngine.on(this._focusTarget(), beforeActivateEvent, function (e) {
if (!$(e.target).is(selectors.focusable)) {
e.preventDefault();
}
});
}
},
_refreshFocusEvent: function _refreshFocusEvent() {
this._detachFocusEvents();
this._attachFocusEvents();
},
_focusInHandler: function _focusInHandler(e) {
var that = this;
that._createActionByOption("onFocusIn", {
beforeExecute: function beforeExecute() {
that._updateFocusState(e, true);
},
excludeValidators: ["readOnly"]
})({ event: e });
},
_focusOutHandler: function _focusOutHandler(e) {
var that = this;
that._createActionByOption("onFocusOut", {
beforeExecute: function beforeExecute() {
that._updateFocusState(e, false);
},
excludeValidators: ["readOnly", "disabled"]
})({ event: e });
},
_updateFocusState: function _updateFocusState(e, isFocused) {
var target = e.target;
if (inArray(target, this._focusTarget()) !== -1) {
this._toggleFocusClass(isFocused, $(target));
}
},
_toggleFocusClass: function _toggleFocusClass(isFocused, $element) {
var $focusTarget = $element && $element.length ? $element : this._focusTarget();
$focusTarget.toggleClass(FOCUSED_STATE_CLASS, isFocused);
},
_hasFocusClass: function _hasFocusClass(element) {
var $focusTarget = $(element || this._focusTarget());
return $focusTarget.hasClass(FOCUSED_STATE_CLASS);
},
_attachKeyboardEvents: function _attachKeyboardEvents() {
var processor = this.option("_keyboardProcessor");
if (processor) {
this._keyboardProcessor = processor.reinitialize(this._keyboardHandler, this);
} else if (this.option("focusStateEnabled")) {
this._keyboardProcessor = new KeyboardProcessor({
element: this._keyboardEventBindingTarget(),
handler: this._keyboardHandler,
focusTarget: this._focusTarget(),
context: this
});
}
},
_keyboardHandler: function _keyboardHandler(options) {
var e = options.originalEvent,
key = options.key;
var keys = this._supportedKeys(),
func = keys[key];
if (func !== undefined) {
var handler = func.bind(this);
return handler(e) || false;
} else {
return true;
}
},
_refreshFocusState: function _refreshFocusState() {
this._cleanFocusState();
this._renderFocusState();
},
_cleanFocusState: function _cleanFocusState() {
var $element = this._focusTarget();
this._detachFocusEvents();
this._toggleFocusClass(false);
$element.removeAttr("tabIndex");
if (this._keyboardProcessor) {
this._keyboardProcessor.dispose();
delete this._keyboardProcessor;
}
},
_attachHoverEvents: function _attachHoverEvents() {
var that = this,
hoverableSelector = that._activeStateUnit,
nameStart = eventUtils.addNamespace(hoverEvents.start, UI_FEEDBACK),
nameEnd = eventUtils.addNamespace(hoverEvents.end, UI_FEEDBACK);
eventsEngine.off(that._eventBindingTarget(), nameStart, hoverableSelector);
eventsEngine.off(that._eventBindingTarget(), nameEnd, hoverableSelector);
if (that.option("hoverStateEnabled")) {
var startAction = new Action(function (args) {
that._hoverStartHandler(args.event);
that._refreshHoveredElement($(args.element));
}, {
excludeValidators: ["readOnly"]
});
var $eventBindingTarget = that._eventBindingTarget();
eventsEngine.on($eventBindingTarget, nameStart, hoverableSelector, function (e) {
startAction.execute({
element: $(e.target),
event: e
});
});
eventsEngine.on($eventBindingTarget, nameEnd, hoverableSelector, function (e) {
that._hoverEndHandler(e);
that._forgetHoveredElement();
});
} else {
that._toggleHoverClass(false);
}
},
_hoverStartHandler: commonUtils.noop,
_hoverEndHandler: commonUtils.noop,
_attachFeedbackEvents: function _attachFeedbackEvents() {
var that = this,
feedbackSelector = that._activeStateUnit,
activeEventName = eventUtils.addNamespace(feedbackEvents.active, UI_FEEDBACK),
inactiveEventName = eventUtils.addNamespace(feedbackEvents.inactive, UI_FEEDBACK),
feedbackAction,
feedbackActionDisabled;
eventsEngine.off(that._eventBindingTarget(), activeEventName, feedbackSelector);
eventsEngine.off(that._eventBindingTarget(), inactiveEventName, feedbackSelector);
if (that.option("activeStateEnabled")) {
var feedbackActionHandler = function feedbackActionHandler(args) {
var $element = $(args.element),
value = args.value,
dxEvent = args.event;
that._toggleActiveState($element, value, dxEvent);
};
eventsEngine.on(that._eventBindingTarget(), activeEventName, feedbackSelector, { timeout: that._feedbackShowTimeout }, function (e) {
feedbackAction = feedbackAction || new Action(feedbackActionHandler);
feedbackAction.execute({
element: $(e.currentTarget),
value: true,
event: e
});
});
eventsEngine.on(that._eventBindingTarget(), inactiveEventName, feedbackSelector, { timeout: that._feedbackHideTimeout }, function (e) {
feedbackActionDisabled = feedbackActionDisabled || new Action(feedbackActionHandler, { excludeValidators: ["disabled", "readOnly"] });
feedbackActionDisabled.execute({
element: $(e.currentTarget),
value: false,
event: e
});
});
}
},
_toggleActiveState: function _toggleActiveState($element, value) {
this._toggleHoverClass(!value);
$element.toggleClass(ACTIVE_STATE_CLASS, value);
},
_refreshHoveredElement: function _refreshHoveredElement(hoveredElement) {
var selector = this._activeStateUnit || this._eventBindingTarget();
this._forgetHoveredElement();
this._hoveredElement = hoveredElement.closest(selector);
this._toggleHoverClass(true);
},
_forgetHoveredElement: function _forgetHoveredElement() {
this._toggleHoverClass(false);
delete this._hoveredElement;
},
_toggleHoverClass: function _toggleHoverClass(value) {
if (this._hoveredElement) {
this._hoveredElement.toggleClass(HOVER_STATE_CLASS, value && this.option("hoverStateEnabled"));
}
},
_toggleDisabledState: function _toggleDisabledState(value) {
this.$element().toggleClass(DISABLED_STATE_CLASS, Boolean(value));
this._toggleHoverClass(!value);
this.setAria("disabled", value || undefined);
},
_setWidgetOption: function _setWidgetOption(widgetName, args) {
if (!this[widgetName]) {
return;
}
if (typeUtils.isPlainObject(args[0])) {
each(args[0], function (option, value) {
this._setWidgetOption(widgetName, [option, value]);
}.bind(this));
return;
}
var optionName = args[0];
var value = args[1];
if (args.length === 1) {
value = this.option(optionName);
}
var widgetOptionMap = this[widgetName + "OptionMap"];
this[widgetName].option(widgetOptionMap ? widgetOptionMap(optionName) : optionName, value);
},
_optionChanged: function _optionChanged(args) {
switch (args.name) {
case "disabled":
this._toggleDisabledState(args.value);
this._refreshFocusState();
break;
case "hint":
this._renderHint();
break;
case "activeStateEnabled":
this._attachFeedbackEvents();
break;
case "hoverStateEnabled":
this._attachHoverEvents();
break;
case "tabIndex":
case "_keyboardProcessor":
case "focusStateEnabled":
this._refreshFocusState();
break;
case "onFocusIn":
case "onFocusOut":
break;
case "accessKey":
this._renderAccessKey();
break;
case "visible":
var visible = args.value;
this._toggleVisibility(visible);
if (this._isVisibilityChangeSupported()) {
// TODO hiding works wrong
this._checkVisibilityChanged(args.value ? "shown" : "hiding");
}
break;
case "onContentReady":
this._initContentReadyAction();
break;
default:
this.callBase(args);
}
},
_isVisible: function _isVisible() {
return this.callBase() && this.option("visible");
},
beginUpdate: function beginUpdate() {
this._ready(false);
this.callBase();
},
endUpdate: function endUpdate() {
this.callBase();
if (this._initialized) {
this._ready(true);
}
},
_ready: function _ready(value) {
if (arguments.length === 0) {
return this._isReady;
}
this._isReady = value;
},
setAria: function setAria() {
var setAttribute = function setAttribute(option) {
var attrName = option.name === "role" || option.name === "id" ? option.name : "aria-" + option.name,
attrValue = option.value;
if (attrValue === null || attrValue === undefined) {
attrValue = undefined;
} else {
attrValue = attrValue.toString();
}
domUtils.toggleAttr(option.target, attrName, attrValue);
};
if (!typeUtils.isPlainObject(arguments[0])) {
setAttribute({
name: arguments[0],
value: arguments[1],
target: arguments[2] || this._getAriaTarget()
});
} else {
var $target = arguments[1] || this._getAriaTarget();
each(arguments[0], function (key, value) {
setAttribute({
name: key,
value: value,
target: $target
});
});
}
},
isReady: function isReady() {
return this._ready();
},
/**
* @name WidgetMethods.repaint
* @publicName repaint()
*/
repaint: function repaint() {
this._refresh();
},
/**
* @name WidgetMethods.focus
* @publicName focus()
*/
focus: function focus() {
eventsEngine.trigger(this._focusTarget(), "focus");
},
/**
* @name WidgetMethods.registerKeyHandler
* @publicName registerKeyHandler(key, handler)
* @param1 key:string
* @param2 handler:function
*/
registerKeyHandler: function registerKeyHandler(key, handler) {
var currentKeys = this._supportedKeys(),
addingKeys = {};
addingKeys[key] = handler;
this._supportedKeys = function () {
return extend(currentKeys, addingKeys);
};
}
});
module.exports = Widget;