/*jsl:import ipa.js */ /* Authors: * Endi Sukma Dewata * Adam Young * Pavel Zuna * * Copyright (C) 2010 Red Hat * see file 'COPYING' for use and warranty information * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ define(['dojo/_base/array', 'dojo/_base/lang', 'dojo/Evented', 'dojo/has', 'dojo/keys', 'dojo/on', './builder', './ipa', './jquery', './phases', './reg', './text' ], function(array, lang, Evented, has, keys, on, builder, IPA, $, phases, reg, text) { /** * Widget module * ============= * * External usage: * * var widget = require('freeipa/widget') * @class widget * @singleton */ var exp = {}; /** * Width of column which contains only checkbox * @member IPA * @property {number} */ IPA.checkbox_column_width = 13; /** * String to show next to required fields to indicate that the field is required. * @member IPA * @property {string} */ IPA.required_indicator = '*'; /** * Base widget * @class * @param {Object} spec * @abstract */ IPA.widget = function(spec) { spec = spec || {}; var that = new Evented(); /** * Widget name. Should be container unique. */ that.name = spec.name; /** * Widget element ID. * @deprecated */ that.id = spec.id; /** * Label * @property {string} */ that.label = text.get(spec.label); /** * Helper text * @property {string} */ that.tooltip = text.get(spec.tooltip); /** * Measurement unit * @property {string} */ that.measurement_unit = spec.measurement_unit; /** * Parent entity * @deprecated * @property {IPA.entity} */ that.entity = IPA.get_entity(spec.entity); //some old widgets still need it /** * Parent facet * @property {IPA.facet} */ that.facet = spec.facet; /** * Widget is enabled - can be focus and edited (depends also on writable * and read_only) * @property {boolean} */ that.enabled = spec.enabled === undefined ? true : spec.enabled; /** * Create HTML representation of a widget. * @method * @param {HTMLElement} container - Container node */ that.create = function(container) { container.addClass('widget'); that.container = container; }; /** * Reset widget content. All user-modifiable information have to be * changed back to widgets defaults. */ that.clear = function() { }; /** * Set enabled state. * @param {boolean} value - True - enabled; False - disabled */ that.set_enabled = function(value) { var changed = that.enabled !== value; that.enabled = value; if (changed) { that.emit('enabled-change', { source: that, enabled: value }); } }; /** * Whether widget should be displayed. * @param {boolean} value - True - visible; False - hidden */ that.set_visible = function(visible) { var changed = visible !== that.container.is(':visible'); if (visible) { that.container.show(); } else { that.container.hide(); } if (changed) { that.emit('visible-change', { source: that, visible: visible }); } }; /** * Utility method. Build widget based on spec with this widget's context. * @param {boolean} spec - Widget specification object * @param {Object} context - Context object. Gets mixed with this widget context. * @param {Object} overrides - Build overrides */ that.build_child = function(spec, context, overrides) { var def_c = { entity: that.entity, facet: that.facet }; context = lang.mixin(def_c, context); var child = builder.build('widget', spec, context, overrides); return child; }; that.widget_create = that.create; that.widget_set_enabled = that.set_enabled; return that; }; /** * Base class for input gathering widgets. * @class * @extends IPA.widget * @abstract */ IPA.input_widget = function(spec) { spec = spec || {}; var that = IPA.widget(spec); /** * Widget's width. * @deprecated * @property {number} */ that.width = spec.width; /** * Widget's height. * @deprecated * @property {number} */ that.height = spec.height; /** * Widget is required * @property {boolean} */ that.required = spec.required; /** * Enable undo button showing. Undo button is displayed when user * modifies data. * @property {boolean} undo=true */ that.undo = spec.undo === undefined ? true : spec.undo; /** * User has rights to modify widgets content. Ie. based on LDAP ACL. * @property {boolean} writable=true */ that.writable = spec.writable === undefined ? true : spec.writable; /** * This widget content is read-only. * @property {boolean} */ that.read_only = spec.read_only; /** * Mark the widget to be hidden (form or dialog may not display it). * @property {boolean} */ that.hidden = spec.hidden; //events //each widget can contain several events /** * Value changed event. * * Raised when user modifies data by hand. * * @event */ that.value_changed = IPA.observer(); /** * Undo clicked event. * * @event */ that.undo_clicked = IPA.observer(); /** * Updated event. * * Raised when widget content gets updated - raised by * {@link IPA.input_widget#update} method. * * @event */ that.updated = IPA.observer(); /** * Creates HTML representation of error link * @param {HTMLElement} container - node to place the error link */ that.create_error_link = function(container) { container.append(' '); $('', { name: 'error_link', 'class': 'help-inline', style: 'display:none' }).appendTo(container); }; /** * Creates HTML representation of required indicator. * @param {HTMLElement} container - node to place the indicator */ that.create_required = function(container) { that.required_indicator = $('', { 'class': 'required-indicator', text: IPA.required_indicator, style: 'display: none;' }).appendTo(container); }; /** * Update displayed information by supplied values. * @param {Object|Array|null} values - values to be edited/displayed by * widget. */ that.update = function() { }; /** * This function saves the values entered in the UI. * It returns the values in an array, or null if * the field should not be saved. * @returns {Array|null} entered values */ that.save = function() { return []; }; /** * This function creates an undo link in the container. * On_undo is a link click callback. It can be specified to custom * callback. If a callback isn't set, default callback is used. If * spefified to value other than a function, no callback is registered. * @param {HTMLElement} container * @param {Function} link clicked callback */ that.create_undo = function(container, on_undo) { container.append(' '); that.undo_span = IPA.button({ name: 'undo', style: 'display: none;', 'class': 'undo', label: text.get('@i18n:widget.undo') }).appendTo(container); if(on_undo === undefined) { on_undo = function() { that.undo_clicked.notify([], that); that.emit('undo-click', { source: that }); }; } if(typeof on_undo === 'function') { that.undo_span.click(on_undo); } }; /** * Get reference to undo element * @return {jQuery} undo button jQuery reference */ that.get_undo = function() { return $(that.undo_span); }; /** * Display undo button */ that.show_undo = function() { that.get_undo().css('display', ''); }; /** * Hide undo button */ that.hide_undo = function() { $(that.undo_span).css('display', 'none'); }; /** * Get error link reference * @return {jQuery} error link jQuery reference */ that.get_error_link = function() { return $('span[name="error_link"]', that.container); }; /** * Show error message * @protected * @param {string} message */ that.show_error = function(message) { var error_link = that.get_error_link(); error_link.html(message); error_link.css('display', ''); that.emit('error-show', { source: that, error: message }); }; /** * Hide error message * @protected */ that.hide_error = function() { var error_link = that.get_error_link(); error_link.css('display', 'none'); that.emit('error-hide', { source: that }); }; /** * Set required * @param {boolean} required */ that.set_required = function(required) { var changed = required !== that.required; that.required = required; if (that.required_indicator) { that.required_indicator.css('display', that.required ? '' : 'none'); } if (changed) { that.emit('require-change', { source: that, required: required }); } }; /** * Set enabled * @param {boolean} value - enabled */ that.set_enabled = function(value) { that.widget_set_enabled(value); if (that.input) { that.input.prop('disabled', !value); } }; /** * Raise value change event * @protected */ that.on_value_changed = function() { var value = that.save(); that.value_changed.notify([value], that); that.emit('value-change', { source: that, value: value }); }; /** * Widget is writable * @return {boolean} */ that.is_writable = function() { return !that.read_only && !!that.writable; }; /** * Focus input element * @abstract */ that.focus_input = function() { var input = that.get_input(); if (!input) { return; } else if (input.jquery || input.length === undefined) { input.focus(); } else if (input.length) { input[0].focus(); } }; /** * Get input element or array of input elements in case of multivalued * widgets. * * - useful for label.for * * @return {null|HTMLElement[]} */ that.get_input = function() { if (that.input) return that.input; return null; }; /** * Mark element as deleted. * * Ie. textbox with strike-through * @abstract */ that.set_deleted = function() {}; // methods that should be invoked by subclasses that.widget_hide_error = that.hide_error; that.widget_show_error = that.show_error; return that; }; /** * Select text in input. * Uses a browser specific technique to select a range. * @member IPA * @param {jQuery} input jQuery reference * @param {number} start * @param {number} end */ IPA.select_range = function(input,start, end) { input.focus(); if (input[0].setSelectionRange) { input[0].setSelectionRange(start, end); } else if (input[0].createTextRange) { var range = input[0].createTextRange(); range.collapse(true); range.moveEnd('character', end); range.moveStart('character', start); range.select(); } }; /** * A textbox widget. Displayed as label when not modifiable. * @class * @extends IPA.input_widget */ IPA.text_widget = function(spec) { spec = spec || {}; var that = IPA.input_widget(spec); /** * Size of the input. * @property {number} */ that.size = spec.size || 30; /** * Input type * @property {string} input_type='text' */ that.input_type = spec.input_type || 'text'; /** * Select range of text */ that.select_range = function(start, end){ IPA.select_range(that.input, start, end); }; /** * @inheritDoc */ that.create = function(container) { that.widget_create(container); container.addClass('text-widget'); that.display_control = $('