// Copyright (C) 2010-2016 Combodo SARL // // This file is part of iTop. // // iTop is free software; you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // iTop 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 Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with iTop. If not, see //iTop Field set //Used by itop.form_handler and itop.subform_field to list their fields ; $(function() { // the widget definition, where 'itop' is the namespace, // 'field_set' the widget name $.widget( 'itop.field_set', { // default options options: { field_identifier_attr: 'data-field-id', // convention: fields are rendered into a div and are identified by this attribute fields_list: null, fields_impacts: {}, touched_fields: [], is_valid: true, form_path: '', script_element: null, style_element: null }, buildData: { script_code: '', style_code: '' }, // the constructor _create: function() { var me = this; this.element .addClass('field_set'); this.element .bind('field_change', function(oEvent, oData){ me._onFieldChange(oEvent, oData); }) .bind('update_form', function(oEvent, oData){ me._onUpdateForm(oEvent, oData); }) .bind('get_current_values', function(oEvent, oData){ return me._onGetCurrentValues(oEvent, oData); }) .bind('validate', function(oEvent, oData){ if (oData === undefined) { oData = {}; } return me._onValidate(oEvent, oData); }); // Creating DOM elements if not using user's specifics if(this.options.script_element === null) { this.options.script_element = $(''); this.element.after(this.options.script_element); } if(this.options.style_element === null) { this.options.style_element = $(''); this.element.before(this.options.style_element); } // Building the form if(this.options.fields_list !== null) { this.buildForm(); } }, // called when created, and later when changing options _refresh: function() { }, // events bound via _bind are removed automatically // revert other modifications here _destroy: function() { this.element .removeClass('field_set'); }, // _setOptions is called with a hash of all options that are changing // always refresh when changing options _setOptions: function() { this._superApply(arguments); }, // _setOption is called for each individual option that is changing _setOption: function( key, value ) { this._super( key, value ); }, getField: function (sFieldId) { return this.element.find('[' + this.options.field_identifier_attr + '="' + sFieldId + '"][data-form-path="' + this.options.form_path + '"]'); }, _onGetCurrentValues: function(oEvent, oData) { oEvent.stopPropagation(); var oResult = {}; for(var i in this.options.fields_list) { var oField = this.options.fields_list[i]; if(this.getField(oField.id).hasClass('form_field')) { oResult[oField.id] = this.getField(oField.id).triggerHandler('get_current_value'); } else { if(window.console) { console.log('Field set : Cannot retrieve current value from field [' + this.options.field_identifier_attr + '="' + oField.id + '"][data-form-path="' + this.options.form_path + '"] as it seems to have no itop.form_field widget attached.'); } } } return oResult; }, _getRequestedFields: function(sSourceFieldName) { var aFieldsName = []; if(this.options.fields_impacts[sSourceFieldName] !== undefined) { for(var i in this.options.fields_impacts[sSourceFieldName]) { aFieldsName.push(this.options.fields_impacts[sSourceFieldName][i]); } } return aFieldsName; }, _onFieldChange: function(oEvent, oData) { oEvent.stopPropagation(); // Set field as touched so we know that we have to do checks on it later if(this.options.touched_fields.indexOf(oData.name) < 0) { this.options.touched_fields.push(oData.name); } // Validate the field var oResult = this.getField(oData.name).triggerHandler('validate', {touched_fields_only: true}); if ( (oResult !== undefined) && !oResult.is_valid) { this.options.is_valid = false; } var oRequestedFields = this._getRequestedFields(oData.name); if(oRequestedFields.length > 0) { this.element.trigger('update_fields', {form_path: this.options.form_path, requested_fields: oRequestedFields}); } }, _onUpdateForm: function(oEvent, oData) { oEvent.stopPropagation(); this.buildData.script_code = ''; this.buildData.style_code = ''; for (var i in oData.updated_fields) { var oUpdatedField = oData.updated_fields[i]; this.options.fields_list[oUpdatedField.id] = oUpdatedField; this._prepareField(oUpdatedField.id); } // Adding code to the dom // Note : We use text() instead of append(), otherwise the code will be interpreted as DOM tags (text + + ...) and can break some browsers this.options.script_element.text( this.options.script_element.text() + '\n\n// Appended by update on ' + Date() + '\n' + this.buildData.script_code); this.options.style_element.text( this.options.style_element.text() + '\n\n// Appended by update on ' + Date() + '\n' + this.buildData.style_code); // Evaluating script code as adding it to dom did not executed it (only script from update !) eval(this.buildData.script_code); }, _onValidate: function(oEvent, oData) { oEvent.stopPropagation(); this.options.is_valid = true; var aFieldsToValidate = []; if ((oData.touched_fields_only !== undefined) && (oData.touched_fields_only === true)) { aFieldsToValidate = this.options.touched_fields; } else { // TODO : Requires IE9+ Object.keys(this.options.fields_list); for (var sFieldId in this.options.fields_list) { aFieldsToValidate.push(sFieldId); } } for(var i in aFieldsToValidate) { var oField = this.getField(aFieldsToValidate[i]); // Checking if the field still exists as it could have been from a dynamic subform (Typically with custom fields) if(oField.length > 0 && oField.hasClass('form_field')) { var oRes = oField.triggerHandler('validate', oData); if (!oRes.is_valid) { this.options.is_valid = false; } } } return this.options.is_valid; }, _loadCssFile: function(url) { if (!$('link[href="' + url + '"]').length) $('').appendTo('head'); }, _loadJsFile: function(url) { if (!$('script[src="' + url + '"]').length) $.getScript(url); }, // Place a field for which no container exists _addField: function(sFieldId) { $('
').appendTo(this.element); }, _prepareField: function(sFieldId) { var oField = this.options.fields_list[sFieldId]; if(this.getField(oField.id).length === 1) { // We replace the node instead of just replacing the inner html so the previous widget is automatically destroyed. this.getField(oField.id).replaceWith( $('
') ); } else { this._addField(oField.id); } var oFieldContainer = this.getField(oField.id); // HTML if( (oField.html !== undefined) && (oField.html !== '') ) { oFieldContainer.html(oField.html); } // JS files if( (oField.js_files !== undefined) && (oField.js_files.length > 0) ) { for(var i in oField.js_files) { this._loadJsFile(oField.js_files[i]); } } // CSS files if( (oField.css_files !== undefined) && (oField.css_files.length > 0) ) { for(var i in oField.css_files) { this._loadCssFile(oField.css_files[i]); } } // JS inline if( (oField.js_inline !== undefined) && (oField.js_inline !== '') ) { this.buildData.script_code += '; '+ oField.js_inline; } // CSS inline if( (oField.css_inline !== undefined) && (oField.css_inline !== '') ) { this.buildData.style_code += ' '+ oField.css_inline; } }, buildForm: function() { this.buildData.script_code = ''; this.buildData.style_code = ''; for(var i in this.options.fields_list) { var oField = this.options.fields_list[i]; if(oField.id === undefined) { console.log('Field set : A field must have at least an id property.'); return false; } this._prepareField(oField.id); } this.options.script_element.text('$(document).ready(function(){ ' + this.buildData.script_code + ' });'); this.options.style_element.text(this.buildData.style_code); eval(this.options.script_element.text()); // Sending event to let know that form is built this.element.trigger('form_built'); }, hasTouchedFields: function() { return (this.options.touched_fields.length > 0); }, // Debug helper showOptions: function() { return this.options; }, }); });