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  * Period component.
 22  * 
 23  * @namespace Alfresco
 24  * @class Alfresco.Period
 25  */
 26 (function()
 27 {
 28    /**
 29     * YUI Library aliases
 30     */
 31    var Dom = YAHOO.util.Dom,
 32       Event = YAHOO.util.Event;
 33 
 34    /**
 35     * Alfresco Slingshot aliases
 36     */
 37    var $html = Alfresco.util.encodeHTML;
 38 
 39    /**
 40     * Period constructor.
 41     * 
 42     * @param {String} htmlId The HTML id of the parent element
 43     * @param {String} currentValueHtmlId The HTML id of the parent element
 44     * @return {Alfresco.Period} The new Period instance
 45     * @constructor
 46     */
 47    Alfresco.Period = function(htmlId, currentValueHtmlId)
 48    {
 49       // Mandatory properties
 50       this.name = "Alfresco.Period";
 51       this.id = htmlId;
 52       this.currentValueHtmlId = currentValueHtmlId;
 53 
 54       /* Register this component */
 55       Alfresco.util.ComponentManager.register(this);
 56 
 57       /* Load YUI Components */
 58       Alfresco.util.YUILoaderHelper.require(["button"], this.onComponentsLoaded, this);
 59       
 60       // Initialise prototype properties
 61       this.widgets = {};
 62       this.periodDefinitionsByType = {};
 63       this.periodDefinitionsByLabel = {};
 64 
 65       return this;
 66    };
 67    
 68    Alfresco.Period.prototype =
 69    {
 70       /**
 71        * Object container for initialization options
 72        *
 73        * @property options
 74        * @type object
 75        */
 76       options:
 77       {
 78          /**
 79           * The current value
 80           *
 81           * @property currentValue
 82           * @type string
 83           */
 84          currentValue: "",
 85          
 86          /**
 87           * Array of objects representing the period data
 88           * returned from the server
 89           * 
 90           * @property data
 91           * @type Array of objects
 92           */
 93          data: [],
 94          
 95          /**
 96           * Flag to determine whether the picker is in disabled mode
 97           *
 98           * @property disabled
 99           * @type boolean
100           * @default false
101           */
102          disabled: false,
103          
104          /**
105           * Flag to indicate whether the field is mandatory
106           *
107           * @property mandatory
108           * @type boolean
109           * @default false
110           */
111          mandatory: false
112       },
113 
114       /**
115        * Object container for storing YUI widget instances.
116        * 
117        * @property widgets
118        * @type object
119        */
120       widgets: null,
121       
122       /**
123        * Object containing all period definitions keyed by period type.
124        * 
125        * @property periodDefinitionsByType
126        * @type object
127        */
128       periodDefinitionsByType: null,
129       
130       /**
131        * Object containing all period definitions keyed by period label.
132        * 
133        * @property periodDefinitionsByLabel
134        * @type object
135        */
136       periodDefinitionsByLabel: null,
137 
138       /**
139        * Set multiple initialization options at once.
140        *
141        * @method setOptions
142        * @param obj {object} Object literal specifying a set of options
143        * @return {Alfresco.Period} returns 'this' for method chaining
144        */
145       setOptions: function Period_setOptions(obj)
146       {
147          this.options = YAHOO.lang.merge(this.options, obj);
148          return this;
149       },
150       
151       /**
152        * Set messages for this component.
153        *
154        * @method setMessages
155        * @param obj {object} Object literal specifying a set of messages
156        * @return {Alfresco.Period} returns 'this' for method chaining
157        */
158       setMessages: function Period_setMessages(obj)
159       {
160          Alfresco.util.addMessages(obj, this.name);
161          return this;
162       },
163       
164       /**
165        * Fired by YUILoaderHelper when required component script files have
166        * been loaded into the browser.
167        *
168        * @method onComponentsLoaded
169        */
170       onComponentsLoaded: function Period_onComponentsLoaded()
171       {
172          Event.onContentReady(this.id, this.onReady, this, true);
173       },
174 
175       /**
176        * Fired by YUI when parent element is available for scripting.
177        * Component initialisation, including instantiation of YUI widgets and event listener binding.
178        *
179        * @method onReady
180        */
181       onReady: function Period_onReady()
182       {
183          // create the period definition data structure
184          for (var p = 0; p < this.options.data.length; p++)
185          {
186             this.periodDefinitionsByType[this.options.data[p].type] = this.options.data[p];
187             this.periodDefinitionsByLabel[this.options.data[p].label] = this.options.data[p];
188          }
189          
190          // add a 'Not Set' option
191          var notSetOption = 
192          {
193             type: "",
194             label: this._msg("form.notset"),
195             hasExpression: false
196          };
197          
198          this.periodDefinitionsByType[notSetOption.type] = notSetOption;
199          this.periodDefinitionsByLabel[notSetOption.label] = notSetOption;
200          
201          // split the current value into it's parts
202          var parts = this.options.currentValue.split("|"),
203             periodType = this.options.currentValue,
204             expression = 1;
205          if (parts.length == 2)
206          {
207             periodType = parts[0];
208             expression = parts[1];
209          }
210          
211          // get the period definition for the current value
212          var periodDef = this.periodDefinitionsByType[periodType];
213          
214          if (this.options.disabled)
215          {
216             // populate the view mode span with a human readable representation
217             // of the current value
218             var displayValue = this.options.currentValue;
219             if (periodDef !== undefined)
220             {
221                if (periodDef.hasExpression)
222                {
223                   displayValue = expression + " " + periodDef.label;
224                   if (expression > 1)
225                   {
226                      displayValue += "s";
227                   }
228                }
229                else
230                {
231                   displayValue = periodDef.label;
232                }
233             }
234 
235             // update the span to show the display value
236             Dom.get(this.id).innerHTML = $html(displayValue);
237          }
238          else
239          {
240             // sort the period options in display label order
241             var sortedOptions = [];
242             for (var x in this.periodDefinitionsByType)
243             {
244                sortedOptions.push(this.periodDefinitionsByType[x].label);
245             }
246             sortedOptions.sort();
247 
248             // populate the drop down list with period options
249             // using the sorted array of labels
250             var def = null,
251                eSelect = Dom.get(this.id + "-type");
252             
253             for (p = 0; p < sortedOptions.length; p++)
254             {
255                def = this.periodDefinitionsByLabel[sortedOptions[p]];
256                eSelect.options[p] = new Option(def.label, def.type, periodType === def.type, periodType === def.type);
257             }
258             
259             // populate the expression field with the current value
260             if (periodDef !== undefined && periodDef.hasExpression)
261             {
262                Dom.get(this.id + "-expression").value = expression;
263             }
264          }
265          
266          // add an onchange event listener to the select drop down
267          Event.addListener(this.id + "-type", "change", this._handleDataChange, this, true);
268          
269          // add an onkeyup event listender to the expression field
270          Event.addListener(this.id + "-expression", "keyup", this._handleDataChange, this, true);
271       },
272       
273       /**
274        * Handles any change to the control i.e. the drop down list or the
275        * expression field. If the current control data is valid the hidden
276        * field is updated with the data represented in the submission format.
277        * 
278        * @method _handleDataChange
279        * @param event The event that occurred
280        * @private
281        */
282       _handleDataChange: function Period__handleDataChange(event)
283       {
284          // get the current values and construct the new value
285          var type = Dom.get(this.id + "-type").value,
286             expression = Dom.get(this.id + "-expression").value,
287             newValue = type + "|" + expression,
288             valueValid = true;
289          
290          if (type === "")
291          {
292             newValue = "";
293             Dom.get(this.id + "-expression").value = "";
294          }
295          else
296          {
297             var periodDef = this.periodDefinitionsByType[type];
298             if (periodDef.hasExpression)
299             {
300                if (expression === "")
301                {
302                   expression = 1;
303                   newValue += expression;
304                }
305                else if (isNaN(expression))
306                {
307                   valueValid = false;
308                }
309             }
310             else
311             {
312                newValue = type;
313                Dom.get(this.id + "-expression").value = "";
314             }
315          }
316          
317          if (valueValid)
318          {
319             YAHOO.util.Dom.get(this.currentValueHtmlId).value = newValue;
320             
321             if (Alfresco.logger.isDebugEnabled())
322             {
323                Alfresco.logger.debug("Hidden field '" + this.currentValueHtmlId + "' updated to '" + newValue + "'");
324             }
325             
326             // inform the forms runtime that the control value has been updated (if field is mandatory)
327             if (this.options.mandatory)
328             {
329                YAHOO.Bubbling.fire("mandatoryControlValueUpdated", this);
330             }
331          }
332       },
333       
334       /**
335        * Gets a custom message
336        *
337        * @method _msg
338        * @param messageId {string} The messageId to retrieve
339        * @return {string} The custom message
340        * @private
341        */
342       _msg: function Period__msg(messageId)
343       {
344          return Alfresco.util.message.call(this, messageId, "Alfresco.Period", Array.prototype.slice.call(arguments).slice(1));
345       }
346    };
347 })();
348