define([
], function (
) {
'use strict';
/**
* A PropertyInspector is a description of how properties of a component
* can be configured.
*
* <p>
* UI for component configuration is displayed in a panel
* called Property Inspector (PI), which can contain several tabs, and
* usually contains at least one tab called 'Properties'. Each of the tabs
* contains one Knockout template with a view model applied. View models can
* be shared by several tabs, or each tab can have a dedicated view model.
* </p><p>
* When some settings are changed in some of the tabs, the view (for which
* the PI is opened) should be updated accordingly as soon as possible.
* (This is most often arranged by updating view's properties.)
* </p><p>
* Note: There are several standard PI tab names used in default set of ABCS
* components. They can be retrieved from {@link module:components.dt/js/spi/propertyinspectors/TabNames TabNames}
* object to avoid typos and repetition.
* </p><p>
* A property inspector can be created inside a {@link components.dt/js/spi/registration/PropertyInspectorProvider}.
* See example below.
* </p>
* @param {components.dt/js/spi/propertyinspectors/PropertyInspector.Tab[]} tabs - Array of tab definitions.
* @param {components.dt/js/spi/propertyinspectors/PropertyInspector.Controller} [controller] - PI controller.
*
* @example
* <caption>
* Implementation for two tabs:
* </caption>
* define([
* 'components.dt/js/spi/propertyinspectors/PropertyInspector',
* 'components.dt/js/spi/propertyinspectors/TabNames',
* 'text!path/to/mycomponent/mycomponent_pi_data.html',
* 'text!path/to/mycomponent/mycomponent_pi_properties.html'
* ], function (
* PropertyInspector,
* TabNames,
* piDataTemplateHtml,
* piPropertiesTemplateHtml
* ) {
* 'use strict';
*
* var MyPropertyInspectorProvider = function () {
* }
*
* MyPropertyInspectorProvider.prototype.getPropertyInspector = function (view) {
* if (view.getType() !== 'THE_SUPPORTED_VIEW_TYPE') {
* return null;
* }
* var sharedModel = this._getPIModel(view);
* return new PropertyInspector(
* [
* {
* id: 'properties',
* title: TabNames.TAB_PROPERTIES,
* model: sharedModel,
* html: piPropertiesTemplateHtml
* },
* {
* id: 'data',
* title: TabNames.TAB_DATA,
* model: sharedModel,
* html: piDataTemplateHtml
* }
* ],
* {
* activate: function () {
* sharedModel.start();
* },
* beforeDeactivate: function () {
* return sharedModel.canClosePropertyInspector();
* },
* deactivate: function () {
* sharedModel.end();
* }
* }
* });
*
* // Create Knockout view model for PI for the component view.
* //
* // (Model in this example is shared by all PI tabs, but we could also
* // create dedicated model for each tab.)
* //
* MyPropertyInspectorProvider.prototype._getPIModel = function (view) {
* var model = {};
* model.myText = ko.observable(view.getProperties().getValue('my_text'));
* model.myText.subscribe(function (value) {
* view.getProperties().setValue('my_text', value);
* });
*
* model.message = ko.observable();
*
* model.now = ko.observable();
*
* // + some model properties for 'Data' tab.
*
* model.canClosePropertyInspector = function () {
* var res = !model.myText() || model.myText().length === 3;
* model.message(res ? '' : 'Text must be empty or 3 chars long');
* return res;
* };
* model.start = function () {
* model.intervalId = window.setInterval(function () {
* model.now(new Date())
* }, 1000);
* };
* model.end = function () {
* window.clearInterval(model.intervalId);
* }
* return model;
* }
*
* return new MyPropertyInspectorProvider();
* });
*
* @example
* <caption>
* Example Knockout template in file with path
* path/to/mycomponent/mycomponent_pi_properties.html. The template can be
* loaded as RequireJS dependency with prefix 'text!', see example above.
* </caption>
* <div>
* <div data-bind="text: now"></div>
* <div>
* Text: <input type="text" data-bind="value: myText">
* </div>
* <div data-bind="text: message"></div>
* </div>
*
* @AbcsExtension stable
* @exports components.dt/js/spi/propertyinspectors/PropertyInspector
* @final
* @constructor
* @version 16.3.5
*/
var PropertyInspector = function (tabs, controller) {
AbcsLib.checkThis(this);
AbcsLib.checkParameterCount(arguments, 1, 1);
AbcsLib.checkDefined(tabs, 'tabs');
if (!(tabs instanceof Array)) {
throw new Error(PropertyInspector._ERROR_TABS_NOT_ARRAY);
}
tabs.forEach(function (tab) {
AbcsLib.checkDefined(tab.id, 'tab.id');
if (!/^[\w]+$/.test(tab.id)) {
throw new Error(PropertyInspector._ERROR_TAB_INVALID_ID(tab.id));
}
AbcsLib.checkDefined(tab.title, 'tab.title');
if (!tab.html && !tab.template) {
throw new Error(PropertyInspector._ERROR_TEMPLATE_REQUIRED(tab.title));
}
AbcsLib.checkObjectLiteral(tab, [
'id', 'title', 'html', 'template', 'model'
]);
AbcsLib.checkDataType(tab.id, AbcsLib.Type.STRING);
AbcsLib.checkDataType(tab.title, AbcsLib.Type.STRING);
if (tab.html) {
AbcsLib.checkDataType(tab.html, AbcsLib.Type.STRING);
} else if (tab.template) {
AbcsLib.checkDataType(tab.template, AbcsLib.Type.STRING);
}
});
if (controller) {
var allowedKeys = ['activate', 'beforeDeactivate', 'deactivate'];
AbcsLib.checkObjectLiteral(controller, allowedKeys);
allowedKeys.forEach(function (k) {
if (controller[k]) {
AbcsLib.checkDataType(controller[k], AbcsLib.Type.FUNCTION);
}
});
}
this._tabs = tabs;
this._controller = controller;
};
/**
* Error thrown if invalid 'tabs' argument is passed to the constructor.
*
* @type {string}
*/
PropertyInspector._ERROR_TABS_NOT_ARRAY
= 'Expected tabs argument to be an array';
/**
* Error thrown if tab id contains an invalid character.
*
* @param {string} id - Tab id.
*
* @returns {string}
*/
PropertyInspector._ERROR_TAB_INVALID_ID = function (id) {
return 'Invalid character in tab id "' + id + '", only English '
+ 'alphabet letters, numbers and underscores are allowed.';
};
/**
* Error thrown if tab description doesn't contain required template
* settings.
*
* @param {string} tabTitle
* @returns {string}
*/
PropertyInspector._ERROR_TEMPLATE_REQUIRED = function (tabTitle) {
return 'Tab definition for "' + tabTitle + '" is missing "html" '
+ 'or (deprecated) "template" field.';
};
/**
* Return tab definitions.
*
* @returns {module:components.dt/js/spi/propertyinspectors/PropertyInspector.Tab[]}
*/
PropertyInspector.prototype.getTabs = function () {
return this._tabs;
};
/**
* Call 'activate' callback from the controller, if defined.
*
*/
PropertyInspector.prototype.activate = function () {
if (this._controller && this._controller.activate) {
this._controller.activate();
}
};
/**
* Call 'beforeDeactivate' callback from the controller and return it's
* result. If the callback is not defined, return true.
*
* @returns {Boolean}
*/
PropertyInspector.prototype.beforeDeactivate = function () {
if (this._controller && this._controller.beforeDeactivate) {
return this._controller.beforeDeactivate();
} else {
return true;
}
};
/**
* Call 'deactivate' callback from the controller, if defined.
*
*/
PropertyInspector.prototype.deactivate = function () {
if (this._controller && this._controller.deactivate) {
this._controller.deactivate();
}
};
/**
* Description of one tab in Property Inspector panel. Used in constructor
* for {@link components.dt/js/spi/propertyinspectors/PropertyInspector PropertyInspector}.
*
* @AbcsExtension stable
* @memberOf components.dt/js/spi/propertyinspectors/PropertyInspector
* @objectLiteral
* @version 16.3.5
*/
var Tab = function () {};
/**
* Human-readable tab title, possibly translated.
* <p>
* Tip: Pre-defined names for standard Property Inspector tabs can be found
* in {@link components.dt/js/spi/propertyinspectors/TabNames TabNames}.
* </p>
*
* @AbcsExtension stable
* @version 16.3.5
* @type {string}
*/
Tab.prototype.title;
/**
* Unique id of this tab (two tabs in a PI cannot have the same id).
*
* @AbcsExtension stable
* @version 16.3.5
* @type {string}
*/
Tab.prototype.id;
/**
* Knockout view model for the tab.
*
* @AbcsExtension stable
* @version 16.3.5
* @type {object}
*/
Tab.prototype.model;
/**
* Template name for the tab. The template has to be already loaded.
*
* @AbcsExtension stable
* @version 16.3.5
* @type {string}
* @deprecated Deprecated in 17.2.3. Use ["html" field]{@link components.dt/js/spi/propertyinspectors/PropertyInspector.Tab#html} instead.
*/
Tab.prototype.template;
/**
* HTML source fragment to be used as Knockout template for the tab.
*
* @AbcsExtension stable
* @version 17.2.3
* @type {string}
*/
Tab.prototype.html;
/**
* Definition of callbacks to various events or situations related to PI.
* Used in constructor for
* {@link components.dt/js/spi/propertyinspectors/PropertyInspector PropertyInspector}.
*
* @AbcsExtension stable
* @memberOf components.dt/js/spi/propertyinspectors/PropertyInspector
* @objectLiteral
* @version 16.3.5
*/
var Controller = function () {};
/**
* Function called right after the templates were rendered in the Property
* Inspector panel (with applied Knockout bindings for specified models).
* This callback can be used to register listeners, start animations, or
* perform additional UI changes.
*
* @AbcsExtension stable
* @version 16.3.5
*/
Controller.prototype.activate = function () {};
/**
* Function called when the PI is going to be closed. The function can
* return a <code>falsy</code> value to prevent the PI from closing, e.g.
* if it contains some invalid or unsaved settings.
*
* @AbcsExtension stable
* @version 16.3.5
* @returns {Boolean}
*/
Controller.prototype.beforeDeactivate = function () {};
/**
* Function called when the PI was closed. This callback is appropriate for
* removing of listeners and similar cleanup.
*
* @AbcsExtension stable
* @version 16.3.5
*/
Controller.prototype.deactivate = function () {};
return PropertyInspector;
});