root.libs.composum.nodes.commons.components.js.components.js Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of composum-nodes-commons Show documentation
Show all versions of composum-nodes-commons Show documentation
general components and objects to use the Sling API
/**
*
*
*/
(function () {
'use strict';
CPM.namespace('core.components');
(function (components, widgets, core) {
//
// Form
//
components.const = _.extend(components.const || {}, {
form: {
css: {
base: 'form-widget',
action: {
slingPost: 'form-action_Sling-POST'
},
status: {
valid: 'valid-form',
invalid: 'invalid-form'
},
selector: {
item: '.multi-form-item'
},
tab: {
base: 'composum-commons-form-tab',
tabbed: 'composum-commons-form-tabbed',
_nav: '-nav',
_panel: '-panel'
}
},
translate: {
uri: {
object: '/bin/cpm/core/translate.object.json',
status: '/bin/cpm/core/translate.status.json'
}
}
}
});
/**
* the 'widget-form'
*
* the widget to handle a HTML form element and its embedded widgets.
* - validates all widgets in the form (found by their 'widget' class)
* - toggles the classes 'valid-form / 'invalid-form'
*
* This 'Widget' doesn't cache or store the validation result!
*/
components.FormWidget = Backbone.View.extend({
initialize: function (options) {
var c = components.const.form;
this.isSlingPost = this.$el.hasClass(c.css.action.slingPost);
this.initTabs();
},
registerWidget: function (widget) {
widget.changed('FormWidget', _.bind(this.onChanged, this));
},
/**
* sets the 'formChanged' to 'true' - form isChanged(): 'true'
*/
onChanged: function (event) {
this.formChanged = true;
},
/**
* @return 'true' if any widgets value in the form is changed
*/
isChanged: function () {
return this.formChanged === true;
},
/**
* the widgets 'isValid' always performs a 'validation' of the form
*/
isValid: function (alertMethod) {
this.validationReset();
return this.validate(alertMethod);
},
/**
* if the form is marked as 'tabbed' find the tab navigation and insert an item for each tab panel
*/
initTabs: function () {
var c = components.const.form.css.tab;
if (this.$('.' + c.tabbed).length === 1) {
this.tabbed = {
$el: this.$('.' + c.tabbed)
};
var $tabNav = this.tabbed.$nav = this.$('.' + c.base + c._nav);
var tabMap = this.tabbed.map = {};
$tabNav.html('');
this.tabbed.$el.find('.' + c.base + c._panel).each(function () {
var $tabPanel = $(this);
var tabId = $tabPanel.attr('id');
var tabLabel = $tabPanel.data('label');
$tabNav.append('' + tabLabel + ' ');
tabMap[tabId] = {
id: tabId,
label: tabLabel,
panel: $tabPanel
}
});
}
},
validationReset: function () {
var c = components.const.form.css;
if (this.tabbed) {
this.tabbed.$nav.find('li').removeClass('has-error');
}
this.$(widgets.const.css.selector.general).each(function () {
if (this.view && _.isFunction(this.view.validationReset)) {
this.view.validationReset.apply(this.view);
}
});
this.$el.removeClass(c.status.invalid);
this.$el.addClass(c.status.valid);
},
onValidationFault: function () {
var c = components.const.form.css;
this.$el.removeClass(c.status.valid);
this.$el.addClass(c.status.invalid);
if (this.tabbed) {
var dialog = this;
var $first = undefined;
this.tabbed.list.forEach(function (item) {
var $tab = $(item);
if ($tab.find('.has-error').length > 0) {
var $tabLink = dialog.tabbed.$nav.find('[href="#' + $tab[0].id + '"]');
$tabLink.parent().addClass('has-error');
if (!$first) {
$first = $tabLink;
$first.tab('show');
}
}
});
}
},
/**
* the validation calls the 'isValid' function of each widget in the form;
* the class of the form signals the result ('valid-form / 'invalid-form')
*/
validate: function (alertMethod) {
var valid = true;
this.$(widgets.const.css.selector.general).each(function () {
if (this.view) {
if (_.isFunction(this.view.isValid)) {
// check each visible widget independent from the current result
valid = (this.view.isValid.apply(this.view, [alertMethod]) && valid);
}
}
});
return valid;
},
/**
* returns the current set of values of the entire form
*/
getValues: function () {
var c = components.const.form;
var values = {};
this.$('.widget').each(function () {
if (this.view) {
if (this.view.$el.parent().closest(c.css.selector.item).length === 0 &&
_.isFunction(this.view.getValue)) {
var name = core.getWidgetNames(this.view);
// store 'structured names' in a complex object...
var object = values;
for (var i = 0; i < name.length; i++) {
if (i < name.length - 1) {
object[name[i]] = object[name[i]] || {};
object = object[name[i]];
} else {
object[name[i]] = this.view.getValue.apply(this.view);
}
}
}
}
});
return values;
},
/**
* presets the values of the entire form
*/
setValues: function (values) {
var c = components.const.form;
this.$('.widget').each(function () {
if (this.view) {
if (this.view.$el.parent().closest(c.css.selector.item).length === 0 &&
_.isFunction(this.view.setValue)) {
var name = core.getWidgetNames(this.view);
// map complex object to 'structured names'...
var object = values;
for (var i = 0; i < name.length; i++) {
if (i < name.length - 1) {
if (object) {
object = object[name[i]];
}
} else {
this.view.setValue.apply(this.view, [object ? object[name[i]] : undefined]);
}
}
}
}
});
},
/**
* a reset of a form resets all widgets calling their 'reset' function
*/
reset: function () {
this.$('.widget').each(function () {
if (this.view) {
if (_.isFunction(this.view.reset)) {
this.view.reset.apply(this.view);
}
}
});
this.formChanged = false;
},
/**
* prepare the validation and a following submit (adjust names and values if useful)
*/
prepare: function () {
var c = components.const.form;
this.$(widgets.const.css.selector.general).each(function () {
if (this.view) {
if (_.isFunction(this.view.prepare)) {
// prepare each widget independent
this.view.prepare.apply(this.view);
}
}
});
},
/**
* finalize all data (values) before the following submit (prepare data for storing)
*/
finalize: function () {
if (!this.finalized) {
this.finalized = true;
var c = components.const.form;
this.$(widgets.const.css.selector.general).each(function () {
if (this.view) {
if (_.isFunction(this.view.finalize)) {
// prepare each widget independent
this.view.finalize.apply(this.view);
}
}
});
}
},
/**
* the general submit action; if a 'doValidate(alertMethod, onSuccess, onError)' function is avaialable
* at the form widget this validation is called instead of the general 'validate(alertMethod)'
* @param alertMethod (type, label, message, hint) - on validation fault
* @param onSuccess () - optional
* @param onError () - optional
*/
doFormSubmit: function (alertMethod, onSuccess, onError) {
this.prepare();
if (_.isFunction(this.doValidate)) {
this.doValidate(alertMethod, _.bind(function () {
this.finalize();
this.submitForm(onSuccess, onError);
}, this), onError);
} else {
if (this.validate(alertMethod)) {
this.finalize();
this.submitForm(onSuccess, onError);
}
}
},
/**
* Submit the form of the dialog.
*/
submitForm: function (onSuccess, onError, onComplete) {
core.submitForm(this.el, _.bind(function (result) {
this.formChanged = false;
if (_.isFunction(onSuccess)) {
onSuccess(result);
}
}, this), onError, onComplete);
},
/**
* Submit the form data using the 'PUT' method instead of 'POST'.
*/
submitFormPut: function (onSuccess, onError, onComplete) {
core.submitFormPut(this.el, this.getValues(), _.bind(function (result) {
this.formChanged = false;
if (_.isFunction(onSuccess)) {
onSuccess(result);
}
}, this), onError, onComplete);
}
});
widgets.register('form.widget-form', components.FormWidget);
//
// Field Types
//
/**
* the 'checkbox-widget' core.components.CheckboxWidget)
* possible attributes:
* - data-rules: 'required'
* - data-value: the initial 'value' (true/false) as an alternative to the 'checked' attribute
*/
components.CheckboxWidget = widgets.Widget.extend({
initialize: function (options) {
widgets.Widget.prototype.initialize.apply(this, [options]);
this.$typeHint = this.$('sling-post-type-hint');
this.$deleteHint = this.$('sling-post-delete-hint');
this.$defaultHint = this.$('sling-post-default-hint');
this.$useDefaultHint = this.$('sling-post-use-default-hint');
if (!this.$input.attr('value')) {
this.$input.attr('value', 'true'); // the value which has to be sent if 'checked'
}
var value = this.$input.data('value');
if (value) {
this.setValue(value);
}
},
retrieveInput: function () {
return this.$el.is('input[type="checkbox"]')
? this.$el : this.$('input[type="checkbox"]');
},
declareName: function (name) {
if (name) {
this.$input.attr(widgets.const.attr.name, name);
this.$typeHint.attr(widgets.const.attr.name, name + '@TypeHint');
this.$deleteHint.attr(widgets.const.attr.name, name + '@Delete');
this.$defaultHint.attr(widgets.const.attr.name, name + '@DefaultValue');
this.$useDefaultHint.attr(widgets.const.attr.name, name + '@UseDefaultWhenMissing');
} else {
this.$input.removeAttr(widgets.const.attr.name);
this.$typeHint.removeAttr(widgets.const.attr.name);
this.$deleteHint.removeAttr(widgets.const.attr.name);
this.$defaultHint.removeAttr(widgets.const.attr.name);
this.$useDefaultHint.removeAttr(widgets.const.attr.name);
}
},
/**
* returns the current validation state, always 'true' for this widget
*/
isValid: function () {
return true;
},
/**
* returns the current value from the input field
*/
getValue: function () {
return this.$input.prop('checked');
},
/**
* defines the (initial) value of the input field
*/
setValue: function (value, triggerChange) {
this.$input.prop('checked', value === 'false' ? false : value);
if (triggerChange) {
this.$el.trigger('change', [value]);
}
},
/**
* resets the validation state and the input field value
*/
reset: function () {
this.$input.prop('checked', false);
}
});
widgets.register('.widget.checkbox-widget', components.CheckboxWidget);
/**
* the 'select-buttons-widget' core.components.SelectButtonsWidget)
* possible attributes:
*/
components.SelectButtonsWidget = widgets.Widget.extend({
initialize: function (options) {
widgets.Widget.prototype.initialize.apply(this, [options]);
// scan 'rules / pattern' attributes
this.initRules();
this.$('.btn').click(_.bind(this.onSelect, this));
},
onSelect: function (event) {
event.preventDefault();
this.setValue($(event.currentTarget).attr('data-value'), true);
return false;
},
getValue: function () {
this.value = this.$('.active').attr('data-value');
return this.value;
},
setValue: function (value, triggerChange) {
this.$('.btn').removeClass('active');
this.$('.btn[data-value="' + value + '"]').addClass('active');
this.value = this.$('.active').attr('data-value');
if (triggerChange) {
this.$el.trigger('change', [value]);
}
}
});
widgets.register('.widget.select-buttons-widget', components.SelectButtonsWidget);
/**
* the 'radio-group-widget' core.components.RadioGroupWidget)
* possible attributes:
*/
components.RadioGroupWidget = widgets.Widget.extend({
initialize: function (options) {
widgets.Widget.prototype.initialize.apply(this, [options]);
// scan 'rules / pattern' attributes
this.initRules();
},
getCount: function () {
return this.$('input[type="radio"]').length;
},
getOnlyOne: function () {
return this.getCount() === 1 ? this.$('input[type="radio"]').val() : undefined;
},
getValue: function () {
this.value = this.$('input[type="radio"]:checked').val();
return this.value;
},
setValue: function (value, triggerChange) {
var $radio = this.$('input[type="radio"][value="' + value + '"]');
$radio.prop("checked", true);
this.getValue();
if (triggerChange) {
this.$el.trigger('change', [value]);
}
},
reset: function () {
this.value = this.$('input[type="radio"]:checked').removeAttr('checked');
}
});
widgets.register('.widget.radio-group-widget', components.RadioGroupWidget);
/**
* the 'select-widget' core.components.SelectWidget)
* possible attributes:
* - data-rules: 'required'
* - data-options: a string of options (value1:label 1,value2,:no value label))
* - data-value: the current / initial value
*/
components.SelectWidget = widgets.Widget.extend({
initialize: function (options) {
widgets.Widget.prototype.initialize.apply(this, [options]);
// scan 'rules / pattern' attributes
this.initRules();
var optionSet = this.$el.data('options');
if (optionSet) {
this.setOptions(optionSet.indexOf('[') === 0 ? JSON.parse(optionSet) : optionSet);
}
var value = this.$el.data('value') || this.$el.data('default');
if (value) {
this.setValue(value);
}
},
retrieveInput: function () {
return this.$el.is('select') ? this.$el : this.$('select');
},
getValue: function () {
return this.$input.val();
},
setValue: function (value, triggerChange) {
this.$input.val(value);
if (triggerChange) {
this.$el.trigger('change', [this.getValue()]);
}
},
reset: function () {
this.$input.val(this.$el.data('default'));
},
setOptions: function (options) {
this.$input.html('');
if (_.isString(options)) {
var array = [];
options.split(',').forEach(function (item) {
var values = /^([^:]*)(:(.*))?$/.exec(item);
array.push({value: values[1], label: values[3] ? values[3] : values[1]});
});
options = array;
}
if (_.isArray(options)) {
options.forEach(function (option) {
if (_.isObject(option)) {
this.$input.append('');
} else {
this.$input.append('');
}
}, this);
}
}
});
widgets.register('.widget.select-widget', components.SelectWidget);
/**
* the behaviour of a table with rows containing a checkbox to implement a multiple selection
*/
components.TableSelectWidget = components.SelectWidget.extend({
initialize: function (options) {
components.SelectWidget.prototype.initialize.apply(this, [options]);
this.$items = this.$('tbody tr');
this.$items.click(_.bind(this.toggleElement, this));
this.$('thead tr input[type="checkbox"]').change(_.bind(this.toggleAll, this));
},
isNotEmpty: function () {
return this.$items.length > 0;
},
toggleElement: function (event) {
event.preventDefault();
var $row = $(event.currentTarget);
var $checkbox = $row.find('input[type="checkbox"]');
$checkbox.prop('checked', !$checkbox.prop('checked'));
return false;
},
toggleAll: function (event) {
var $checkbox = $(event.currentTarget);
var value = $checkbox.prop('checked');
this.$('tbody tr input[type="checkbox"]').prop('checked', value);
return false;
},
// SelectWidget
retrieveInput: function () {
return this.$('input[type="checkbox"]');
},
getValue: function () {
var value = [];
this.$('input[type="checkbox"]:checked').each(_.bind(function (i, el) {
value.push($(el).attr('value'))
}, this));
return value;
},
setValue: function (value, triggerChange) {
if (!_.isArray(value)) {
value = [value];
}
this.$('input[type="checkbox"]').prop('checked', false);
value.forEach(_.bind(function (val) {
this.$('input[type="checkbox"][value="' + val + '"]').prop('checked', true);
}, this));
if (triggerChange) {
this.$el.trigger('change', [value]);
}
}
});
widgets.register('.widget.table-select-widget', components.TableSelectWidget);
/**
* the 'text-field-widget' core.components.TextFieldWidget)
*
* this is the basic class ('superclass') of all text input field based widgets; it is also usable
* as is for normal text input fields; it implements the general validation and reset functions
* possible attributes:
* - data-rules: 'required,unique'
* - data-pattern: a regexp pattern (javascript) as string or in pattern notation (/.../; with flags)
*/
components.TextFieldWidget = widgets.Widget.extend({
initialize: function (options) {
widgets.Widget.prototype.initialize.apply(this, [options]);
this.$textField = this.textField();
// scan 'rules / pattern' attributes
this.initRules();
var typeahead = this.typeahead(options);
if (typeahead) {
// switch off the browsers autocomplete function (!)
if (typeahead.autocomplete !== 'auto') {
this.$textField.attr('autocomplete', typeahead.autocomplete || 'off');
}
this.$textField.typeahead(typeahead);
}
// bind change events if any validation option has been found
if (this.rules) {
this.$textField.on('keyup.validate', _.bind(this.validate, this));
this.$textField.on('change.validate', _.bind(this.validate, this));
}
},
/**
* returns the current value from the input field
* @extends widgets.Widget
*/
getValue: function () {
return this.$textField.val();
},
/**
* defines the (initial) value of the input field
* @extends widgets.Widget
*/
setValue: function (value, triggerChange) {
var currentValue = this.$textField.val();
if ('' + currentValue !== '' + value) {
this.$textField.val(value);
if (triggerChange) {
this.$textField.trigger('change');
}
}
},
/**
* @extends widgets.Widget
*/
setDefaultValue: function (value) {
this.$textField.attr('placeholder', value);
},
/**
* @param options the initializers options object
* @returns the configuration object for the typeahead plugin
*/
typeahead: function (options) {
var typeahead = this.$el.data('typeahead') || options.typeahead;
if (typeahead) {
if (_.isString(typeahead)) {
if (/^(https?:\/\/[^/]+)?\/[^/]+\/.*/i.exec(typeahead)) {
var url = typeahead;
// a typeahead service must return a JSON array of suggestions
// for the current text value sent as 'query' parameter
typeahead = function (query, callback) {
core.ajaxGet(url, {
data: {
query: query
}
}, function (data) {
callback(data);
})
};
} else if (typeahead.indexOf('{') === 0) {
typeahead = JSON.parse(typeahead);
} else if (typeahead.indexOf(',') > 0 && typeahead.indexOf('(') < 0) {
typeahead = typeahead.split(',')
} else {
try {
var f = eval(typeahead);
if (_.isFunction(f)) {
typeahead = f;
}
} catch (ex) {
}
}
}
if (_.isFunction(typeahead) || _.isArray(typeahead)) {
return {
minLength: 1,
source: typeahead
};
}
return typeahead;
}
return undefined;
},
/**
* sets the focus on the textfield
*/
focus: function () {
this.$textField.focus();
},
/**
* selects the complete text of textfield
*/
selectAll: function () {
this.$textField.select();
},
/**
* retrieves the input field to use (for redefinition in more complex widgets)
*/
textField: function () {
return this.$input;
},
/**
* resets the validation state and the input field value
* @extends widgets.Widget
*/
reset: function () {
this.valid = undefined;
this.$textField.closest('.form-group').removeClass('has-error');
this.$textField.val(undefined);
}
});
widgets.register('.widget.text-field-widget', components.TextFieldWidget);
components.ComboBoxWidget = components.TextFieldWidget.extend({
initialize: function (options) {
components.TextFieldWidget.prototype.initialize.apply(this, [options]);
this.$menu = this.$('.dropdown-menu');
this.$menu.find('li a').click(_.bind(this.optionSelected, this));
this.$textField.on('change.combobox', _.bind(this.onValueChange, this));
},
onValueChange: function () {
var value = this.getValue();
var self = this;
this.$('[data-value-class]').each(function () {
var $el = $(this);
var css = $el.data('value-class').replace(/\$/g, value);
$el.removeClass().addClass(css);
});
this.$menu.find('li').removeClass('active');
this.$menu.find('li[data-value="' + value + '"]').addClass('active');
},
optionSelected: function (event) {
event.preventDefault();
var $link = $(event.currentTarget);
var value = $link.closest('li').data('value');
this.setValue(value, true);
this.$menu.dropdown('toggle');
return false;
}
});
widgets.register('.widget.combobox-widget', components.ComboBoxWidget);
/**
* the 'text-field-widget' core.components.TextFieldWidget)
*
* this is the basic class ('superclass') of all text input field based widgets; it is also usable
* as is for normal text input fields; it implements the general validation and reset functions
* possible attributes:
* - data-rules: 'required'
* - data-pattern: a regexp pattern (javascript) as string or in pattern notation (/.../; with flags)
*/
components.TextAreaWidget = widgets.Widget.extend({
initialize: function (options) {
widgets.Widget.prototype.initialize.apply(this, [options]);
// scan 'rules / pattern' attributes
this.initRules(this.$input);
// bind change events if any validation option has been found
if (this.rules) {
this.$input.on('keyup.validate', _.bind(this.validate, this));
this.$input.on('change.validate', _.bind(this.validate, this));
}
},
retrieveInput: function () {
return this.$el.is('textarea') ? this.$el : this.$('textarea');
},
/**
* returns the current value from the input field
*/
getValue: function () {
return this.$input[0].value;
},
/**
* defines the (initial) value of the input field
*/
setValue: function (value, triggerChange) {
var currentValue = this.$input.text();
if ('' + currentValue !== '' + value) {
this.$input[0].value = value;
if (triggerChange) {
this.$input.trigger('change');
}
}
},
/**
* sets the focus on the textfield
*/
focus: function () {
this.$input.focus();
},
/**
* selects the complete text of textfield
*/
selectAll: function () {
this.$input.select();
},
/**
* resets the validation state and the input field value
*/
reset: function () {
this.valid = undefined;
this.$input.closest('.form-group').removeClass('has-error');
this.$input.val(undefined);
}
});
widgets.register('.widget.text-area-widget', components.TextAreaWidget);
/**
* the 'abstract' PathSelector is a Widget which interacts with a PathWidget
*/
components.PathSelector = widgets.Widget.extend({
initialize: function (options) {
widgets.Widget.prototype.initialize.apply(this, [options]);
this.setPathWidget(options.pathWidget);
},
// getEventId: function () {...},
// onPathChanged: function (path) {...}
setPathWidget: function (pathWidget) {
if (this.pathWidget) {
this.pathWidget.$input.off('change.' + this.getEventId());
}
this.pathWidget = pathWidget;
if (this.pathWidget) {
this.pathWidget.$input.on('change.' + this.getEventId(), _.bind(this.pathInputChanged, this));
}
},
/**
* the callback on each change in the input field;
* selects the node in the tree view if the nodes exists
*/
pathInputChanged: function () {
if (!this.busy) {
this.busy = true;
var path = this.pathWidget.getValue();
if (path !== this.lastPathSelected) {
if (path.indexOf('/') === 0) {
core.getJson('/bin/cpm/nodes/node.tree.json' + core.encodePath(path),
_.bind(function (data) {
this.lastPathSelected = data.path;
this.onPathChanged(data.path);
}, this));
}
}
this.busy = false;
}
}
});
/**
* the 'path-widget' core.components.PathWidget)
*
* the widget behaviour to extend an input or an input group to select repository path values
* - adds a typeahead function for the last path segment during input on the input element
* - adds a select path dialog to select the path in a tree view to a '.select' button if present
* possible attributes:
* - data-root: defines the root path for the path retrieval
*/
components.PathWidget = components.TextFieldWidget.extend({
initialize: function (options) {
options = _.extend({
selector: {
button: 'button.select'
},
typeahead: {
minLength: 1,
source: _.bind(function (query, callback) {
// ensure that query is of a valid path pattern
if (query.indexOf('/') === 0) {
var rootPath = this.getRootPath();
if (rootPath !== '/') {
query = rootPath + query;
}
core.getJson('/bin/cpm/nodes/node.typeahead.json' + query, function (data) {
if (rootPath !== '/') {
for (var i = 0; i < data.length; i++) {
if (data[i].indexOf(rootPath + '/') === 0) {
data[i] = data[i].substring(rootPath.length);
}
}
}
callback(data);
});
}
}, this),
// custom matcher to check last name in path only
matcher: function (item) {
var pattern = /^(.*\/)([^\/]*)$/.exec(this.query);
return item.match('.*' + pattern[2] + '.*');
},
// the custom highlighter to mark the name pattern in the last segment
highlighter: function (item) {
var pattern = /^(.*\/)([^\/]*)$/.exec(this.query);
var splitted = new RegExp('^(.*)' + pattern[2] + '(.*)?').exec(item);
return splitted[1] + '' + pattern[2] + '' + (splitted[2] || '');
}
}
}, options);
components.TextFieldWidget.prototype.initialize.apply(this, [options]);
// retrieve element attributes
this.dialogTitle = this.$el.attr('title');
this.dialogLabel = this.$el.data('label');
this.config = {
rootPath: options.rootPath || this.$el.data('root') || '/'
};
this.setRootPath(this.config.rootPath);
this.setFilter(options.filter || this.$el.data('filter'));
// set up '.select' button if present
this.$selectButton = this.$(options.selector.button);
if (this.$selectButton.length > 0) {
this.$selectButton.on('click', _.bind(this.selectPath, this));
}
},
/**
* the callback for the '.select' button opens set dialog with the tree view
*/
selectPath: function (event) {
if (!this.isDisabled()) {
var selectDialog = core.getView('#path-select-dialog', components.SelectPathDialog);
if (!selectDialog) {
var u = components.const.dialog.load;
core.getHtml(u.base + u._path,
_.bind(function (content) {
selectDialog = core.addLoadedDialog(components.SelectPathDialog, content);
this.openDialog(selectDialog);
}, this));
} else {
this.openDialog(selectDialog);
}
}
},
openDialog: function (selectDialog) {
selectDialog.setTitle(this.dialogTitle);
selectDialog.setLabel(this.dialogLabel);
selectDialog.setRootPath(this.getRootPath());
selectDialog.setFilter(this.getFilter());
selectDialog.show(_.bind(function () {
this.getPath(_.bind(selectDialog.setValue, selectDialog));
}, this),
_.bind(function (path) {
this.setPath(path);
}, this));
},
/**
* calls the 'callback' with the current path value (possibly after a path retrieval); extension
* hook for more complex path retrieval - performs the callback with the current value here
*/
getPath: function (callback) {
var value = this.getValue();
if (_.isFunction(callback)) {
callback(value);
} else {
return value;
}
},
/**
* stores the value for a selected path; extension hook for different path based values
*/
setPath: function (path) {
var oldValue = this.getValue();
this.setValue(path, oldValue !== path);
},
/**
* @returns the repository path with the root path prependet if a root is declared
*/
getValue: function () {
return this.applyRootPath(components.TextFieldWidget.prototype.getValue.call(this), false);
},
/**
* sets the value of the input field without a probably declared root path
*/
setValue: function (value, triggerChange) {
components.TextFieldWidget.prototype.setValue.call(this, this.applyRootPath(value, true), triggerChange);
},
finalize: function () {
// set the full path (prpendet with the declared root) on finalizing the form (before save)
components.TextFieldWidget.prototype.setValue.call(this, this.getValue());
},
getRootPath: function () {
return this.rootPath ? this.rootPath : '/';
},
setRootPath: function (path) {
var value = this.getValue();
var placeholder = this.applyRootPath(this.$input.prop('placeholder'), false);
this.rootPath = this.adjustRootPath(path ? path : this.config.rootPath);
if (placeholder) {
this.$input.prop('placeholder', this.applyRootPath(placeholder, true));
}
this.setValue(value, false);
},
getFilter: function () {
return this.filter;
},
setFilter: function (filter) {
this.filter = filter;
},
adjustRootPath: function (path) {
return path;
},
applyRootPath: function (path, shorten) {
if (path) {
if (shorten) {
if (this.rootPath && this.rootPath !== '/'
&& (path === this.rootPath || path.indexOf(this.rootPath + '/') === 0)) {
path = path.substring(this.rootPath.length);
if (!path) {
path = '/';
}
}
} else {
if (this.rootPath && this.rootPath !== '/') {
path = path === '/' ? this.rootPath : this.rootPath + path;
}
}
}
return path;
}
});
widgets.register('.widget.path-widget', components.PathWidget);
/**
* the 'reference-widget' core.components.ReferenceWidget)
*/
components.ReferenceWidget = components.PathWidget.extend({
initialize: function (options) {
components.PathWidget.prototype.initialize.apply(this, [options]);
if (!this.getFilter()) {
this.setFilter('referenceable');
}
},
/**
* extension - retrieves the path by the current reference value and
* calls the 'callback' with the result
*/
getPath: function (callback) {
if (!this.path) { // retrieve the path if not path value is cached
this.retrievePath(_.bind(function (path) {
this.path = path; // caches the path value
callback(path);
}, this));
} else {
callback(this.path); // uses the cached path value
}
},
/**
* extension - determines the reference for the path as the new value
*/
setPath: function (path) {
this.path = undefined; // reset the possibly cached path value
this.retrieveReference(path);
},
/**
* retrieves the referenc for the path and stores this reference as value
*/
retrieveReference: function (path) {
core.getJson('/bin/cpm/nodes/node.tree.json' + core.encodePath(path), _.bind(function (data) {
this.path = path;
this.setValue(data.uuid ? data.uuid : data.id);
}, this));
},
/**
* retrieves the path for the current reference value and calls the 'callback' with the result
*/
retrievePath: function (callback) {
var reference = this.getValue();
if (reference) {
core.getJson('/bin/cpm/nodes/node.reference.json/' + reference, function (data) {
callback(data.path);
});
}
}
});
widgets.register('.widget.reference-widget', components.ReferenceWidget);
/**
* the 'number-field-widget' core.components.NumberFieldWidget)
* possible attributes:
* - data-options: '[min][:step[:max[:default]]]'
*/
components.NumberFieldWidget = components.TextFieldWidget.extend({
initialize: function (options) {
components.TextFieldWidget.prototype.initialize.apply(this, [options]);
var dataOptions = this.$el.data('options');
if (dataOptions) {
var values = ('' + dataOptions).split(':'); // 'toString' - split - parse...
if (values.length > 0) options.minValue = values[0];
if (values.length > 1) options.stepSize = values[1];
if (values.length > 2) options.maxValue = values[2];
if (values.length > 3) options.defValue = values[3];
}
this.minValue = options.minValue ? Number(options.minValue) : undefined;
this.stepSize = Number(options.stepSize || 1);
this.maxValue = options.maxValue ? Number(options.maxValue) : undefined;
this.defValue = options.defValue ? Number(options.defValue) : undefined;
this.$('.clear').click(_.bind(this.clear, this));
this.$('.increment').click(_.bind(this.increment, this));
this.$('.decrement').click(_.bind(this.decrement, this));
this.initValue();
this.$textField.on('change.number', _.bind(this.onChange, this));
},
initValue: function () {
var value = this.getValue();
if (value === undefined) {
value = this.defValue !== undefined ? this.defValue : this.blankAllowed() ? undefined
: this.minValue !== undefined ? this.minValue : this.maxValue;
}
this.setValue(value);
},
onChange: function () {
this.setValue(this.getValue()); // filter new value for number restrictions
},
setValue: function (value, triggerChange) {
if (value !== undefined && (value = this.getNumber(value)) !== undefined) {
if (this.minValue !== undefined && value < this.minValue) {
value = this.minValue;
}
if (this.maxValue !== undefined && value > this.maxValue) {
value = this.maxValue;
}
}
components.TextFieldWidget.prototype.setValue.apply(this, [value !== undefined ? value
: this.blankAllowed() ? undefined : this.defValue, triggerChange]);
},
blankAllowed: function () {
return this.rules === undefined || this.rules.blank || this.rules.required !== true
},
getNumber: function (value) {
if (value === undefined) {
value = this.getValue();
}
if (value !== undefined) {
try {
value = parseInt(value);
} catch (ex) {
}
if (isNaN(value)) {
value = undefined;
}
}
return value;
},
increment: function () {
if (this.stepSize) {
var value = this.getNumber();
this.setValue(value !== undefined ? (value + this.stepSize)
: (this.defValue !== undefined ? this.defValue : this.minValue), true);
}
},
decrement: function () {
if (this.stepSize) {
var value = this.getNumber();
this.setValue(value !== undefined ? (value - this.stepSize)
: (this.defValue !== undefined ? this.defValue : this.maxValue), true);
}
},
clear: function () {
this.setValue(undefined, true);
},
extValidate: function (value) {
value = this.getNumber(value);
var valid = value !== undefined || this.blankAllowed();
if (valid && value !== undefined) {
if (this.minValue !== undefined) {
valid = value >= this.minValue;
}
if (valid && this.maxValue !== undefined) {
valid = value <= this.maxValue;
}
}
return valid;
}
});
widgets.register('.widget.number-field-widget', components.NumberFieldWidget);
/**
* the 'date-time-widget' core.components.DateTimeWidget)
* possible attributes:
*/
components.DateTimeWidget = components.TextFieldWidget.extend({
initialize: function (options) {
components.TextFieldWidget.prototype.initialize.apply(this, [options]);
this.data = {
locale: this.$el.data('locale') || 'en',
format: this.$el.data('format') || 'YYYY-MM-DD HH:mm:ss',
options: {
weeks: core.toBoolean(this.$el.data('weeks'), true)
}
};
this.$el.datetimepicker({
locale: this.data.locale,
format: this.data.format,
extraFormats: [
'YY-MM-DD',
'YY-MM-DD HH:mm',
'YY-MM-DD HH:mm ZZ',
'YY-MM-DD HH:mm:ss',
'YY-MM-DD HH:mm:ss ZZ',
'YYYY-MM-DD',
'YYYY-MM-DD HH:mm',
'YYYY-MM-DD HH:mm ZZ',
'YYYY-MM-DD HH:mm:ss',
'YYYY-MM-DD HH:mm:ss ZZ',
'DD.MM.YY',
'DD.MM.YY HH:mm',
'DD.MM.YY HH:mm ZZ',
'DD.MM.YY HH:mm:ss',
'DD.MM.YY HH:mm:ss ZZ',
'DD.MM.YYYY',
'DD.MM.YYYY HH:mm',
'DD.MM.YYYY HH:mm ZZ',
'DD.MM.YYYY HH:mm:ss',
'DD.MM.YYYY HH:mm:ss ZZ',
'D. MMMM YYYY',
'D. MMMM YYYY HH:mm',
'D. MMMM YYYY HH:mm ZZ',
'D MMM YYYY',
'D MMM YYYY HHmm',
'D MMM YYYY HHmm ZZ',
'D MMM YYYY HH:mm',
'D MMM YYYY HH:mm ZZ',
'MMMM D, YYYY',
'MMMM D, YYYY HHmm',
'MMMM D, YYYY HHmm ZZ',
'MMMM D, YYYY HH:mm',
'MMMM D, YYYY HH:mm ZZ',
'MM/DD/YYYY',
'MM/DD/YYYY HHmm',
'MM/DD/YYYY HHmm ZZ',
'MM/DD/YYYY HH:mm',
'MM/DD/YYYY HH:mm ZZ'
],
calendarWeeks: this.data.options.weeks,
showTodayButton: true,
showClear: true,
showClose: true
});
this.datetimepicker = this.$el.data('DateTimePicker');
},
/**
* defines the (initial) value of the input field
*/
setValue: function (value, triggerChange) {
this.datetimepicker.date(value ? moment(value, this.data.format) : null);
this.validate();
if (triggerChange) {
this.$el.trigger('change', [value]);
}
}
});
widgets.register('.widget.date-time-widget', components.DateTimeWidget);
/**
* the 'file-upload-widget' core.components.FileUploadWidget)
* possible attributes:
* - data-options: 'hidePreview' (no file preview), 'showUpload' (the direct upload button)
* 'browse:
© 2015 - 2025 Weber Informatics LLC | Privacy Policy