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  * Global Header
 22  * 
 23  * @namespace Alfresco
 24  * @class Alfresco.component.Header
 25 */
 26 (function()
 27 {
 28    /**
 29     * YUI Library aliases
 30     */
 31    var Dom = YAHOO.util.Dom,
 32       Event = YAHOO.util.Event,
 33       Selector = YAHOO.util.Selector;
 34 
 35    /**
 36     * Alfresco Slingshot aliases
 37     */
 38    var $html = Alfresco.util.encodeHTML,
 39       $siteURL = Alfresco.util.siteURL;
 40 
 41    Alfresco.component.Header = function(htmlId)
 42    {
 43       return Alfresco.component.Header.superclass.constructor.call(this, "Alfresco.component.Header", htmlId, ["button", "menu", "container"]);
 44    };
 45 
 46    YAHOO.extend(Alfresco.component.Header, Alfresco.component.Base,
 47    {
 48       /**
 49        * Object container for initialization options
 50        *
 51        * @property options
 52        * @type object
 53        */
 54       options:
 55       {
 56          /**
 57           * Current siteId.
 58           * 
 59           * @property siteId
 60           * @type string
 61           * @default ""
 62           */
 63          siteId: "",
 64 
 65          /**
 66           * Current site title.
 67           * 
 68           * @property siteTitle
 69           * @type string
 70           * @default ""
 71           */
 72          siteTitle: "",
 73          
 74          /**
 75           * Number of characters required for a search.
 76           *
 77           * @property minSearchTermLength
 78           * @type int
 79           * @default 1
 80           */
 81          minSearchTermLength: 1,
 82          
 83          /**
 84           * URI replacement tokens
 85           * 
 86           * @property tokens
 87           * @type object
 88           * @default {}
 89           */
 90          tokens: {}
 91       },
 92 
 93       /**
 94        * Application Item YAHOO.widget instances
 95        *
 96        * @property appItems
 97        * @type Array
 98        */
 99       appItems: null,
100 
101       /**
102        * User Item YAHOO.widget instances
103        *
104        * @property userItems
105        * @type Array
106        */
107       userItems: null,
108 
109       /**
110        * Default search text
111        *
112        * @property defaultSearchText
113        * @type string
114        */
115       defaultSearchText: null,
116 
117       /**
118        * Last status update time
119        *
120        * @property statusUpdateTime
121        * @type Date
122        */
123       statusUpdateTime: null,
124       
125       /**
126        * Fired by YUI when parent element is available for scripting.
127        * Initial History Manager event registration
128        *
129        * @method onReady
130        */
131       onReady: function Header_onReady()
132       {
133          Dom.removeClass(this.id + "-appItems", "hidden");
134          this.replaceUriTokens();
135          this.configureSearch();
136          this.configureMyStatus();
137       },
138       
139       /**
140        * Called by header rendering code to register Application Item YAHOO.widget instances
141        *
142        * @method setAppItems
143        */
144       setAppItems: function Header_setAppItems(items)
145       {
146          this.appItems = items;
147       },
148 
149       /**
150        * Called by header rendering code to register User Item YAHOO.widget instances
151        *
152        * @method setUserItems
153        */
154       setUserItems: function Header_setUserItems(items)
155       {
156          this.userItems = items;
157       },
158 
159 
160       /**
161        * About Share Handlers
162        */
163 
164       /**
165        * Show the About Share dialog
166        *
167        * @method showAboutShare
168        */
169       showAboutShare: function Header_showAboutShare()
170       {
171          Alfresco.module.getAboutShareInstance().show();
172       },
173 
174 
175       /**
176        * Header Items Handlers
177        */
178 
179       /**
180        * Token replacement for header item URLs
181        *
182        * @method replaceUriTokens
183        */
184       replaceUriTokens: function Header_replaceUriTokens()
185       {
186          var tokens = YAHOO.lang.merge(Alfresco.constants.URI_TEMPLATES, Alfresco.constants.HELP_PAGES, this.options.tokens),
187             links = Selector.query("a", this.id),
188             link,
189             attr;
190          
191          for (var i = 0, ii = links.length; i < ii; i++)
192          {
193             link = links[i];
194             attr = Dom.getAttribute(link, "templateUri");
195             if (attr != null)
196             {
197                link.href = Alfresco.util.renderUriTemplate(attr, tokens);
198             }
199          }
200       },
201 
202 
203       /**
204        * Search Handlers
205        */
206       
207       /**
208        * Configure search area
209        *
210        * @method configureSearch
211        */
212       configureSearch: function Header_configureSearch()
213       {
214          this.widgets.searchBox = Dom.get(this.id + "-searchText");
215          this.defaultSearchText = this.msg("header.search.default");
216          
217          Event.addListener(this.widgets.searchBox, "focus", this.onSearchFocus, null, this);
218          Event.addListener(this.widgets.searchBox, "blur", this.onSearchBlur, null, this);
219          
220          this.setDefaultSearchText();
221          
222          // Register the "enter" event on the search text field
223          var me = this;
224          
225          this.widgets.searchEnterListener = new YAHOO.util.KeyListener(this.widgets.searchBox,
226          {
227             keys: YAHOO.util.KeyListener.KEY.ENTER
228          }, 
229          {
230             fn: me.submitSearch,
231             scope: this,
232             correctScope: true
233          }, "keydown").enable();
234 
235          this.widgets.searchMore = new YAHOO.widget.Button(this.id + "-search_more",
236          {
237             type: "menu",
238             menu: this.id + "-searchmenu_more"
239          });
240       },
241       
242       /**
243        * Update image class when search box has focus.
244        *
245        * @method onSearchFocus
246        */
247       onSearchFocus: function Header_onSearchFocus()
248       {
249          if (this.widgets.searchBox.value == this.defaultSearchText)
250          {
251             Dom.removeClass(this.widgets.searchBox, "faded");
252             this.widgets.searchBox.value = "";
253          }
254          else
255          {
256             this.widgets.searchBox.select();
257          }
258       },
259       
260       /**
261        * Set default search text when box loses focus and is empty.
262        *
263        * @method onSearchBlur
264        */
265       onSearchBlur: function Header_onSearchBlur()
266       {
267          var searchText = YAHOO.lang.trim(this.widgets.searchBox.value);
268          if (searchText.length === 0)
269          {
270             /**
271              * Since the blur event occurs before the KeyListener gets
272              * the enter we give the enter listener a chance of testing
273              * against "" instead of the help text.
274              */
275             YAHOO.lang.later(100, this, this.setDefaultSearchText, []);
276          }
277       }, 
278       
279       /**
280        * Set default search text for search box.
281        *
282        * @method setDefaultSearchText
283        */
284       setDefaultSearchText: function Header_setDefaultSearchText()
285       {
286          Dom.addClass(this.widgets.searchBox, "faded");
287          this.widgets.searchBox.value = this.defaultSearchText;
288       },
289 
290       /**
291        * Get current search text from search box.
292        *
293        * @method getSearchText
294        */
295       getSearchText: function Header_getSearchText()
296       {
297          return YAHOO.lang.trim(this.widgets.searchBox.value);
298       },
299       
300       /**
301        * Will trigger a search, via a page refresh to ensure the Back button works correctly
302        *
303        * @method submitSearch
304        */
305       submitSearch: function Header_submitSearch()
306       {
307          var searchText = this.getSearchText();
308          if (searchText.replace(/\*/g, "").length < this.options.minSearchTermLength)
309          {
310             Alfresco.util.PopupManager.displayMessage(
311             {
312                text: this.msg("message.minimum-length", this.options.minSearchTermLength)
313             });
314          }
315          else
316          {
317             // Redirect to the search page
318             var url = "search?t=" + encodeURIComponent(searchText);
319             // Append repository search argument if within repo browser page or previous repository search
320             if (window.location.pathname.match("/repository$") == "/repository" ||
321                 (window.location.pathname.match("/search$") == "/search" && window.location.search.indexOf("r=true") != -1))
322             {
323                url += "&r=true";
324             }
325             window.location = $siteURL(url);
326          }
327       },
328       
329       /**
330        * This is the user status at the time the page was loaded. It is also updated each time the user makes an update.
331        * 
332        * @property _currentStatus
333        * @type string
334        */
335       _currentStatus: "",
336       
337       /**
338        * My Status handlers
339        */
340       
341       /**
342        * Configure My Status UI
343        *
344        * @method configureMyStatus
345        */
346       configureMyStatus: function Header_configureMyStatus()
347       {
348          this.widgets.statusBox = Dom.get(this.id + "-statusText");
349          this.widgets.statusTime = Dom.get(this.id + "-statusTime");
350          this._currentStatus = this.widgets.statusBox.value; // Store the loaded value.
351          
352          var statusISOTime = this.widgets.statusTime.attributes.title.value;
353          if (statusISOTime !== "")
354          {
355             this.statusUpdateTime = Alfresco.util.fromISO8601(statusISOTime);
356          }
357          this.setStatusRelativeTime();
358 
359          // Find and siable the menuItem containing the My Status elements
360          Alfresco.util.bind(function Header_configureMyStatus_fnDisableUserMenu()
361          {
362             var allItems = this.userItems.concat(this.appItems),
363                item;
364 
365             for (var i = 0, ii = allItems.length; i < ii; i++)
366             {
367                item = allItems[i];
368                if (item instanceof YAHOO.widget.Button && YAHOO.lang.isFunction(item.getMenu) && item.getMenu() !== null)
369                {
370                   var menuItems = item.getMenu().getItems(),
371                      menuItem;
372 
373                   for (var j = 0, jj = menuItems.length; j < jj; j++)
374                   {
375                      menuItem = menuItems[j];
376                      if (Dom.hasClass(menuItem.element, "HEADER-MARKER"))
377                      {
378                         // Found the menu item, now disable it to remove default event handler behaviour
379                         menuItem.cfg.setProperty("disabled", true);
380                         return;
381                      }
382                   }
383                }
384             }
385          }, this)();
386 
387          // Stop the "click" event propagating to the menu handlers
388          Event.on(this.widgets.statusBox, "click", function(p_oEvent)
389          {
390             Event.stopEvent(p_oEvent);
391          });
392 
393          // When the user clicks in the status box, clear the previous status to make it easier to add new information...
394          var _this = this;
395          YAHOO.util.Event.addListener(this.id + "-statusText", "click", function(e)
396          {
397             Dom.get(_this.id + "-statusText").value = "";
398          });
399          
400          // When the user clicks away from the status box, reset the previous status if they have not entered a value...
401          YAHOO.util.Event.addListener(this.id + "-statusText", "blur", function(e)
402          {
403             var textBox = Dom.get(_this.id + "-statusText");
404             if (textBox.value.length == 0)
405             {
406                // If the user has not entered any data then reset the status...
407                textBox.value = _this._currentStatus;
408             }
409          });
410          
411          this.widgets.submitStatus = new YAHOO.widget.Button(this.id + "-submitStatus");
412          this.widgets.submitStatus.on("click", this.submitStatus, this.widgets.submitStatus, this);
413       },
414 
415       /**
416        * Get current status text from textarea.
417        *
418        * @method getStatusText
419        */
420       getStatusText: function Header_getStatusText()
421       {
422          return YAHOO.lang.trim(this.widgets.statusBox.value);
423       },
424 
425       /**
426        * Updates relative status time display.
427        *
428        * @method setStatusRelativeTime
429        */
430       setStatusRelativeTime: function Header_setStatusRelativeTime()
431       {
432          var relativeTime = (this.statusUpdateTime === null) ? this.msg("status.never-updated") : Alfresco.util.relativeTime(this.statusUpdateTime);
433          this.widgets.statusTime.innerHTML = this.msg("status.updated", relativeTime);
434       },
435       
436       
437       /**
438        * Submit status handler
439        *
440        * @method submitStatus
441        */
442       submitStatus: function Header_submitStatus()
443       {
444          Alfresco.util.Ajax.jsonPost(
445          {
446             url: Alfresco.constants.URL_SERVICECONTEXT + "/components/profile/userstatus",
447             dataObj:
448             {
449                status: this.getStatusText()
450             },
451             successCallback:
452             {
453                fn: this.onStatusUpdated,
454                scope: this
455             },
456             failureMessage: this.msg("message.status.failure")
457          });
458       },
459 
460       /**
461        * Status submitted handler
462        *
463        * @method onStatusUpdated
464        */
465       onStatusUpdated: function Header_onStatusUpdated(response)
466       {
467          this.statusUpdateTime = Alfresco.util.fromISO8601(response.json.userStatusTime.iso8601);
468          this.setStatusRelativeTime();
469          this._currentStatus = this.getStatusText();
470          Alfresco.util.PopupManager.displayMessage(
471          {
472             text: this.msg("message.status.success")
473          });
474       }
475    });
476 })();