1 /**
  2  * Copyright (C) 2005-2010 Alfresco Software Limited.
  3  *
  4  * This file is part of Alfresco
  5  *
  6  * Alfresco is free software: you can redistribute it and/or modify
  7  * it under the terms of the GNU Lesser General Public License as published by
  8  * the Free Software Foundation, either version 3 of the License, or
  9  * (at your option) any later version.
 10  *
 11  * Alfresco is distributed in the hope that it will be useful,
 12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 14  * GNU Lesser General Public License for more details.
 15  *
 16  * You should have received a copy of the GNU Lesser General Public License
 17  * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 18  */
 19  
 20 /**
 21  * Form UI component.
 22  * 
 23  * @namespace Alfresco
 24  * @class Alfresco.FormUI
 25  */
 26 (function()
 27 {
 28    /**
 29     * YUI Library aliases
 30     */
 31    var Dom = YAHOO.util.Dom,
 32       Event = YAHOO.util.Event,
 33       Element = YAHOO.util.Element;
 34    
 35    /**
 36     * FormUI constructor.
 37     * 
 38     * @param {String} htmlId The HTML id of the parent element
 39     * @return {Alfresco.FormUI} The new FormUI instance
 40     * @constructor
 41     */
 42    Alfresco.FormUI = function FormUI_constructor(htmlId, parentId)
 43    {
 44       Alfresco.FormUI.superclass.constructor.call(this, "Alfresco.FormUI", htmlId, ["button", "menu", "container"]);
 45 
 46       // Initialise prototype properties
 47       this.parentId = parentId;
 48       this.buttons = {};
 49       this.formsRuntime = null;
 50       this.eventGroup = htmlId;
 51       
 52       /* Decoupled event listeners */
 53       YAHOO.Bubbling.on("metadataRefresh", this.onFormRefresh, this);
 54       YAHOO.Bubbling.on("mandatoryControlValueUpdated", this.onMandatoryControlValueUpdated, this);
 55       YAHOO.Bubbling.on("registerValidationHandler", this.onRegisterValidationHandler, this);
 56       YAHOO.Bubbling.on("addSubmitElement", this.onAddSubmitElement, this);
 57 
 58       return this;
 59    };
 60 
 61    /**
 62     * Extend from Base component
 63     */
 64    YAHOO.extend(Alfresco.FormUI, Alfresco.component.Base,
 65    {
 66       /**
 67        * Object container for initialization options
 68        *
 69        * @property options
 70        * @type object
 71        */
 72       options:
 73       {
 74          /**
 75           * Mode the current form is in, can be "view", "edit" or "create", defaults to "edit".
 76           * 
 77           * @property mode
 78           * @type string
 79           */ 
 80          mode: "edit",
 81          
 82          /**
 83           * Encoding type to be used when the form is submitted, can be "multipart/form-data",
 84           * "application/x-www-form-urlencoded" or "application/json", defaults to "multipart/form-data".
 85           * 
 86           * @property enctype
 87           * @type string
 88           */ 
 89          enctype: "multipart/form-data",
 90          
 91          /**
 92           * List of objects representing the id of each form field
 93           * 
 94           * @property fields
 95           * @type array[object]
 96           */
 97          fields: [],
 98          
 99          /**
100           * List of objects representing the constraints to setup on the form fields
101           * 
102           * @property fieldConstraints
103           * @type array[object]
104           */
105          fieldConstraints: [],
106 
107          /**
108           * Arguments used to build the form.
109           * Used to Ajax-rebuild the form when in "view" mode
110           * 
111           * @property arguments
112           * @type object
113           */
114          arguments: {}
115       },
116       
117       /**
118        * Object container for storing YUI button instances.
119        * 
120        * @property buttons
121        * @type object
122        */
123       buttons: null,
124        
125       /**
126        * The forms runtime instance.
127        * 
128        * @property
129        * @type object
130        */
131       formsRuntime: null, 
132       
133       /**
134        * Fired by YUI when parent element is available for scripting.
135        * Component initialisation, including instantiation of YUI widgets and event listener binding.
136        *
137        * @method onReady
138        */
139       onReady: function FormUI_onReady()
140       {
141          if (this.options.mode !== "view")
142          {
143             // make buttons YUI buttons
144             
145             if (Dom.get(this.id + "-submit") !== null)
146             {
147                this.buttons.submit = Alfresco.util.createYUIButton(this, "submit", null,
148                {
149                   type: "submit"
150                });
151    
152                // force the generated button to have a name of "-" so it gets ignored in
153                // JSON submit. TODO: remove this when JSON submit behaviour is configurable
154                Dom.get(this.id + "-submit-button").name = "-";
155             }
156             
157             if (Dom.get(this.id + "-reset") !== null)
158             {
159                this.buttons.reset = Alfresco.util.createYUIButton(this, "reset", null,
160                {
161                   type: "reset"
162                });
163 
164                // force the generated button to have a name of "-" so it gets ignored in
165                // JSON submit. TODO: remove this when JSON submit behaviour is configurable
166                Dom.get(this.id + "-reset-button").name = "-";
167             }
168 
169             if (Dom.get(this.id + "-cancel") !== null)
170             {
171                this.buttons.cancel = Alfresco.util.createYUIButton(this, "cancel", null);
172 
173                // force the generated button to have a name of "-" so it gets ignored in
174                // JSON submit. TODO: remove this when JSON submit behaviour is configurable
175                Dom.get(this.id + "-cancel-button").name = "-";
176             }
177 
178             // fire event to inform any listening components that the form HTML is ready
179             YAHOO.Bubbling.fire("formContentReady", this);
180 
181             this.formsRuntime = new Alfresco.forms.Form(this.id);
182             this.formsRuntime.setShowSubmitStateDynamically(true, false);
183             this.formsRuntime.setSubmitElements(this.buttons.submit);
184             
185             // setup JSON/AJAX mode if appropriate
186             if (this.options.enctype === "application/json")
187             {
188                this.formsRuntime.setAJAXSubmit(true,
189                {
190                   successCallback:
191                   {
192                      fn: this.onJsonPostSuccess,
193                      scope: this
194                   },
195                   failureCallback:
196                   {
197                      fn: this.onJsonPostFailure,
198                      scope: this
199                   }
200                });
201                this.formsRuntime.setSubmitAsJSON(true);
202             }
203 
204             // add field help
205             for (var f = 0; f < this.options.fields.length; f++)
206             {
207                var ff = this.options.fields[f],
208                   iconEl = Dom.get(this.parentId + "_" + ff.id + "-help-icon");
209                if (iconEl)
210                {
211                   Alfresco.util.useAsButton(iconEl, this.toggleHelpText, ff.id, this);
212                }
213             }
214 
215             // add any field constraints present
216             for (var c = 0; c < this.options.fieldConstraints.length; c++)
217             {
218                var fc = this.options.fieldConstraints[c];
219                this.formsRuntime.addValidation(fc.fieldId, fc.handler, fc.params, fc.event, fc.message);
220             }
221 
222             // fire event to inform any listening components that the form is about to be initialised
223             YAHOO.Bubbling.fire("beforeFormRuntimeInit", 
224             {
225                eventGroup: this.eventGroup,
226                component: this,
227                runtime: this.formsRuntime 
228             });
229             
230             this.formsRuntime.init();
231             
232             // fire event to inform any listening components that the form has finished initialising
233             YAHOO.Bubbling.fire("afterFormRuntimeInit",
234             {
235                eventGroup: this.eventGroup,
236                component: this,
237                runtime: this.formsRuntime 
238             });
239          }
240       },
241 
242       /**
243        * Toggles help text for a field.
244        *
245        * @method toggleHelpText
246        * @param event The user event
247        * @param fieldId The id of the field to toggle help text for
248        */
249       toggleHelpText: function FormUI_toggleHelpText(event, fieldId)
250       {
251          Alfresco.util.toggleHelpText(this.parentId + "_" + fieldId + "-help");
252       },
253       
254       /**
255        * Default handler used when submit mode is JSON and the sumbission was successful
256        *
257        * @method onJsonPostSuccess
258        * @param response The response from the submission
259        */
260       onJsonPostSuccess: function FormUI_onJsonPostSuccess(response)
261       {
262          // TODO: Display the JSON response here by default, when it's returned!
263          
264          Alfresco.util.PopupManager.displayPrompt(
265          {
266             text: response.serverResponse.responseText
267          });
268       },
269       
270       /**
271        * Default handler used when submit mode is JSON and the sumbission failed
272        *
273        * @method onJsonPostFailure
274        * @param response The response from the submission
275        */
276       onJsonPostFailure: function FormUI_onJsonPostFailure(response)
277       {
278          var errorMsg = this._msg("form.jsonsubmit.failed");
279          if (response.json && response.json.message)
280          {
281             errorMsg = errorMsg + ": " + response.json.message;
282          }
283          
284          Alfresco.util.PopupManager.displayPrompt(
285          {
286             text: errorMsg
287          });
288       },
289       
290       /**
291        * Form refresh event handler
292        *
293        * @method onFormRefresh
294        * @param layer {object} Event fired
295        * @param args {array} Event parameters (depends on event type)
296        */
297       onFormRefresh: function FormUI_onFormRefresh(layer, args)
298       {
299          // Can't do anything if basic arguments weren't set
300          if (this.options.arguments)
301          {
302             var itemKind = this.options.arguments.itemKind,
303                itemId = this.options.arguments.itemId,
304                formId = this.options.arguments.formId;
305             
306             if (itemKind && itemId)
307             {
308                var fnFormLoaded = function(response, p_formUI)
309                {
310                   Alfresco.util.populateHTML(
311                      [ p_formUI.parentId, response.serverResponse.responseText ]
312                   );
313                };
314                
315                var data =
316                {
317                   htmlid: this.parentId,
318                   formUI: false,
319                   mode: this.options.mode,
320                   itemKind: itemKind,
321                   itemId: itemId,
322                   formId: formId
323                };
324 
325                Alfresco.util.Ajax.request(
326                {
327                   url: Alfresco.constants.URL_SERVICECONTEXT + "components/form",
328                   dataObj: data,
329                   successCallback:
330                   {
331                      fn: fnFormLoaded,
332                      obj: this,
333                      scope: this
334                   },
335                   scope: this,
336                   execScripts: true
337                });
338             }
339          }
340       },
341       
342       /**
343        * Mandatory control value updated event handler
344        *
345        * @method onMandatoryControlValueUpdated
346        * @param layer {object} Event fired
347        * @param args {array} Event parameters (depends on event type)
348        */
349       onMandatoryControlValueUpdated: function FormUI_onMandatoryControlValueUpdated(layer, args)
350       {
351          // the value of a mandatory control on the page (usually represented by a hidden field)
352          // has been updated, force the forms runtime to check if form state is still valid
353          if (this.formsRuntime)
354          {
355             this.formsRuntime.updateSubmitElements();
356          }
357       },
358       
359       /**
360        * Register validation handler event handler
361        *
362        * @method onRegisterValidationHandler
363        * @param layer {object} Event fired
364        * @param args {array} Event parameters (depends on event type)
365        */
366       onRegisterValidationHandler: function FormUI_onRegisterValidationHandler(layer, args)
367       {
368          if (this.formsRuntime)
369          {
370             // extract the validation arguments
371             var validation = args[1];
372             
373             // check the minimim required data is provided
374             if (validation && validation.fieldId && validation.handler)
375             {
376                // register with the forms runtime instance
377                this.formsRuntime.addValidation(validation.fieldId, validation.handler, validation.args, 
378                      validation.when, validation.message);
379             }
380          }
381       },
382       
383       /**
384        * Adds a submit element to the form runtime instance
385        * 
386        * @method onAddSubmitElement
387        * @param layer {object} Event fired
388        * @param args {array} Event parameters (depends on event type)
389        */
390       onAddSubmitElement: function FormUI_onAddSubmitElement(layer, args)
391       {
392          // extract the submit element to add
393          var submitElement = args[1];
394          
395          // add to the forms runtime instance, if there is one
396          if (this.formsRuntime != null)
397          {
398             this.formsRuntime.addSubmitElement(submitElement);
399          }
400       }
401    });
402 })();
403 
404 
405 /**
406  * Helper function to add the current state of the given multi-select list to
407  * the given hidden field.
408  * 
409  * @method updateMultiSelectListValue
410  * @param list {string} The id of the multi-select element
411  * @param hiddenField {string} The id of the hidden field to populate the value with
412  * @param signalChange {boolean} If true a bubbling event is sent to inform any
413  *        interested listeners that the hidden field value changed
414  * @static
415  */
416 Alfresco.util.updateMultiSelectListValue = function(list, hiddenField, signalChange)
417 {
418    var listElement = YUIDom.get(list);
419    
420    if (listElement !== null)
421    {
422       var values = new Array();
423       for (var j = 0, jj = listElement.options.length; j < jj; j++)
424       {
425          if (listElement.options[j].selected)
426          {
427             values.push(listElement.options[j].value);
428          }
429       }
430       
431       YUIDom.get(hiddenField).value = values.join(",");
432       
433       if (signalChange)
434       {
435          YAHOO.Bubbling.fire("mandatoryControlValueUpdated", this);
436       }
437    }
438 };
439 
440 /**
441  * Helper function to toggle the state of the help text element
442  * represented by the given id.
443  * 
444  * @method toggleHelpText
445  * @param helpTextId The id of the help text element to toggle
446  * @static
447  */
448 Alfresco.util.toggleHelpText = function(helpTextId)
449 {
450    var helpElem = YUIDom.get(helpTextId);
451    
452    if (helpElem)
453    {
454       if (helpElem.style.display != "block")
455       {
456          helpElem.style.display = "block";
457       }
458       else
459       {
460          helpElem.style.display = "none";
461       }
462    }
463 };
464 
465