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  * Share Common JavaScript module
 22  *
 23  * Contains:
 24  *    Alfresco.Share
 25  *    Alfresco.Location
 26  *    Alfresco.widget.Resizer
 27  *    Alfresco.widget.DashletTitleBarActions
 28  *    Alfresco.widget.DashletResizer
 29  *    Alfresco.component.ShareFormManager
 30  *    Alfresco.component.SimpleDocList
 31  */
 32 
 33 /**
 34  * Share common helper classes and static methods
 35  *
 36  * @namespace Alfresco.Share
 37  */
 38 Alfresco.Share = Alfresco.Share || {};
 39 
 40 /**
 41  * Post an Activity to the Activity Service
 42  *
 43  * @method Alfresco.Share.postActivity
 44  * @param siteId {string} Site
 45  * @param activityType {string} e.g. org.alfresco.documentlibrary.file-added
 46  * @param title {string} title string for activity entry
 47  * @param page {string} page to link to from activity (includes ant page request parameters, i.e. queryString)
 48  * @param data {object} data attached to activity, e.g.
 49  * <pre>
 50  *    nodeRef {string} Must have either nodeRef or parentNodeRef
 51  *    parentNodeRef {string} Must have either parentNodeRef or nodeRef
 52  *    appTool {string} Share application used for filtering, e.g. "documentlibrary"|"blog"|"links"
 53  * </pre>
 54  */
 55 Alfresco.Share.postActivity = function(siteId, activityType, title, page, data)
 56 {
 57    // Mandatory parameter check
 58    if (!YAHOO.lang.isString(siteId) || siteId.length === 0 ||
 59       !YAHOO.lang.isString(activityType) || activityType.length === 0 ||
 60       !YAHOO.lang.isString(title) || title.length === 0 ||
 61       !YAHOO.lang.isObject(data) === null ||
 62       !(YAHOO.lang.isString(data.nodeRef) || YAHOO.lang.isString(data.parentNodeRef)))
 63    {
 64       return;
 65    }
 66 
 67    // This is a "fire-and-forget" webscript; we're not concerned with success/failure status
 68    var config =
 69    {
 70       method: "POST",
 71       url: Alfresco.constants.PROXY_URI + "slingshot/activity/create",
 72       dataObj: YAHOO.lang.merge(
 73       {
 74          site: siteId,
 75          type: activityType,
 76          title: title,
 77          page: page
 78       }, data)
 79    };
 80 
 81    Alfresco.logger.debug("Alfresco.Share.postActivity: ", config.dataObj);
 82 
 83    try
 84    {
 85       Alfresco.util.Ajax.jsonRequest(config);
 86    }
 87    catch (e)
 88    {
 89    }
 90 };
 91 
 92 /**
 93  * Asset location helper class.
 94  *
 95  * @namespace Alfresco
 96  * @class Alfresco.Location
 97  */
 98 (function()
 99 {
100    /**
101     * YUI Library aliases
102     */
103    var Dom = YAHOO.util.Dom;
104 
105    /**
106     * Alfresco Slingshot aliases
107     */
108    var $html = Alfresco.util.encodeHTML,
109       $combine = Alfresco.util.combinePaths;
110 
111    /**
112     * Location constructor.
113     *
114     * @param {String} el The HTML id of the parent element
115     * @return {Alfresco.Location} The new Location instance
116     * @constructor
117     */
118    Alfresco.Location = function Location_constructor(el)
119    {
120       if (YAHOO.lang.isString(el))
121       {
122          el = Dom.get(el);
123       }
124       else if (!el.getAttribute("id"))
125       {
126          Alfresco.util.generateDomId(el);
127       }
128 
129       Alfresco.Location.superclass.constructor.call(this, "Alfresco.Location", el.getAttribute("id"), ["json"]);
130 
131       // Save references to dom object
132       this.widgets.spanEl = el;
133 
134       return this;
135    };
136 
137    YAHOO.extend(Alfresco.Location, Alfresco.component.Base,
138    {
139       /**
140        * Object container for initialization options
141        *
142        * @property options
143        * @type object
144        */
145       options:
146       {
147          /**
148           * Repository's rootNode
149           *
150           * @property rootNode
151           * @type Alfresco.util.NodeRef
152           */
153          rootNode: null,
154 
155          /**
156           * Current siteId (if any).
157           *
158           * @property siteId
159           * @type string
160           */
161          siteId: ""
162       },
163 
164       /**
165        * The locations object representing the current location
166        *
167        * @property _locations
168        * @type object
169        */
170       _locations: null,
171 
172       /**
173        * Fired by YUI when parent element is available for scripting.
174        * Template initialisation, including instantiation of YUI widgets and event listener binding.
175        *
176        * @method onReady
177        */
178       onReady: function Location_onReady()
179       {
180       },
181 
182       /**
183        * Set nodeRef, will lookup the cntext of the nodeRef and display the result depending
184        * on the scope of the options (site and rootNode).
185        *
186        * @method displayByNodeRef
187        * @param nodeRef {Alfresco.util.NodeRef|string}
188        */
189       displayByNodeRef: function Location_displayByNodeRef(nodeRef)
190       {
191          // Find the path for the nodeRef
192          if (YAHOO.lang.isString(nodeRef))
193          {
194             nodeRef = Alfresco.util.NodeRef(nodeRef);
195          }
196          if (nodeRef)
197          {
198             var url = Alfresco.constants.PROXY_URI + "slingshot/doclib/node/" + nodeRef.uri + "/location";
199             if (this.options.siteId === "" && this.options.rootNode)
200             {
201                // Repository mode
202                url += "?libraryRoot=" + encodeURIComponent(this.options.rootNode.toString());
203             }
204             Alfresco.util.Ajax.jsonGet(
205             {
206                url: url,
207                successCallback:
208                {
209                   fn: function(response)
210                   {
211                      if (response.json !== undefined)
212                      {
213                         var locations = response.json;
214                         this._locations = locations;
215                         if (locations.site)
216                         {
217                            this.displayByPath($combine(locations.site.path, locations.site.file), locations.site.site, locations.site.siteTitle);
218                         }
219                         else
220                         {
221                            this.displayByPath($combine(locations.repo.path, locations.repo.file));
222                         }
223 
224                         YAHOO.Bubbling.fire("itemLocationLoaded",
225                         {
226                            eventGroup: this,
227                            locations: locations
228                         });
229 
230                      }
231                   },
232                   scope: this
233                },
234                failureCallback:
235                {
236                   fn: function(response)
237                   {
238                      if (this.widgets.spanEl)
239                      {
240                         this.widgets.spanEl.innerHTML = '<span class="location error">' + this.msg("message.failure") + '</span>';
241                      }
242                   },
243                   scope: this
244                }
245             });
246          }
247          else
248          {
249             this.widgets.spanEl.innerHTML = '<span class="location-none">' + this.msg("location.label.none") + '</span>';
250          }
251       },
252 
253       /**
254        * Renders the location path as HTML
255        *
256        * @method displayByPath
257        * @param fullPath {string}
258        * @param siteId {string}
259        * @param siteTitle {string}
260        */
261       displayByPath: function Location_displayByPath(fullPath, siteId, siteTitle)
262       {
263          this._locations = null;
264          if (this.widgets.spanEl)
265          {
266             this.widgets.spanEl.innerHTML = this.generateHTML(fullPath, siteId, siteTitle);
267          }
268       },
269 
270       /**
271        * Create html that represent a path and site
272        *
273        * @method generateHTML
274        * @param fullPath
275        * @param siteId
276        * @param siteTitle
277        * @return {string} html respresenting path and site as span elements
278        */
279       generateHTML: function Location_generateHTML(fullPath, siteId, siteTitle)
280       {
281          var i = fullPath.lastIndexOf("/"),
282             path = i >= 0 ? fullPath.substring(0, i + 1) : "",
283             name = i >= 0 ? fullPath.substring(i + 1) : fullPath;
284 
285          if (siteId)
286          {
287             if (Alfresco.util.arrayContains(["/", ""], name + path))
288             {
289                fullPath = this.msg("location.path.documents");
290                name = this.msg("location.path.documents");
291             }
292             else
293             {
294                fullPath = this.msg("location.path.documents") + fullPath;
295                name = ".../" + name;
296             }
297          }
298          else
299          {
300             if (Alfresco.util.arrayContains(["/", ""], name + path))
301             {
302                fullPath = this.msg("location.path.repository");
303                name = this.msg("location.path.repository");
304             }
305             else
306             {
307                fullPath = this.msg("location.path.repository") + fullPath;
308                name = ".../" + name;
309             }
310          }
311          var pathHtml = '<span class="location-path" title="' + this.msg("location.tooltip.path", fullPath) + '">' + $html(name) + '</span>';
312          if (siteId)
313          {
314             if (siteId && siteId != this.options.siteId)
315             {
316                var siteHtml = '<span class="location-site" title="' + this.msg("location.tooltip.site", siteTitle ? siteTitle : siteId) + '">' + $html(siteTitle ? siteTitle : siteId) + '</span>';
317                return this.msg("location.label.site", pathHtml, siteHtml);
318             }
319             else
320             {
321                return this.msg("location.label.local", pathHtml);
322             }
323          }
324          else
325          {
326             return this.msg("location.label.repository", pathHtml);
327          }
328       }
329    });
330 })();
331 
332 
333 /**
334  * Like widget helper class.
335  *
336  * @namespace Alfresco
337  * @class Alfresco.Like
338  */
339 (function()
340 {
341    /**
342     * YUI Library aliases
343     */
344    var Dom = YAHOO.util.Dom,
345       Selector = YAHOO.util.Selector;
346 
347    /**
348     * Alfresco Slingshot aliases
349     */
350    var $html = Alfresco.util.encodeHTML,
351       $combine = Alfresco.util.combinePaths;
352 
353    /**
354     * Like constructor.
355     *
356     * @param {String} el The HTML id of the parent element
357     * @return {Alfresco.Like} The new Like instance
358     * @constructor
359     */
360    Alfresco.Like = function Like_constructor(el)
361    {
362       if (YAHOO.lang.isString(el))
363       {
364          el = Dom.get(el);
365       }
366       else if (!el.getAttribute("id"))
367       {
368          Alfresco.util.generateDomId(el);
369       }
370       YAHOO.util.Dom.addClass(el, "item-social");
371 
372       Alfresco.Like.superclass.constructor.call(this, "Alfresco.Like", el.getAttribute("id"), ["json"]);
373 
374       // Save references to dom object
375       this.widgets.spanEl = el;
376       this.services.likes = new Alfresco.service.Ratings(Alfresco.service.Ratings.LIKES);
377 
378       return this;
379    };
380 
381    YAHOO.extend(Alfresco.Like, Alfresco.component.Base,
382    {
383 
384       /**
385        * Object container for initialization options
386        *
387        * @property options
388        * @type object
389        */
390       options:
391       {
392          /**
393           * Reference to the current document
394           *
395           * @property nodeRef
396           * @type String
397           */
398          nodeRef: null,
399 
400          /**
401           * Current siteId, if any.
402           *
403           * @property siteId
404           * @type String
405           */
406          siteId: null,
407 
408          /**
409           * The name of the object to like, will be used in activity.
410           *
411           * @property type
412           * @type String
413           */
414          displayName: null,
415          
416          /**
417           * The type of object to like.
418           * Supported types are: "document", "folder".
419           *
420           * @property type
421           * @type String
422           * @default "document"
423           */
424          type: "document",
425 
426          /**
427           * The default activity data that will be posted based on the type option
428           *
429           * @property activity
430           * @type {Object}
431           */
432          activity:
433          {
434             "folder":
435             {
436                type: "org.alfresco.documentlibrary.folder-liked",
437                page: "folder-details?nodeRef={nodeRef}"
438             },
439             "document":
440             {
441                type: "org.alfresco.documentlibrary.file-liked",
442                page: "document-details?nodeRef={nodeRef}"
443             }
444          }            
445       },
446 
447       /**
448        * If the current user likes the nodeRef
449        *
450        * @type {Boolean}
451        * @property isLiked
452        */
453       isLiked: false,
454 
455       /**
456        * The total amount of users that like nodeRef
457        *
458        * @type {Number}
459        * @property isLiked
460        */
461       totalLikes: 0,
462 
463       /**
464        * NOTE! Implement when needed.
465        *
466        * Set id, load Like data and render.
467        *
468        * @method loadAndDisplay
469        */
470       loadAndDisplay: function Like_loadAndDisplay()
471       {
472          throw new Error("Not implemented yet, load data manually and use display(isLiked, totalLikes) instead.");
473       },
474 
475       /**
476        * 
477        *
478        * @method display
479        * @param isLiked {string}
480        * @param totalLikes {string}
481        */
482       display: function Like_display(isLiked, totalLikes)
483       {
484          this.isLiked = isLiked || false;
485          this.totalLikes = totalLikes || 0;
486          this.render();
487       },
488 
489       /**
490        * Create html that represent a like button
491        *
492        * @method render
493        */
494       render: function Like_render()
495       {
496          var html = "";
497          if (this.isLiked)
498          {
499             html = '<a href="#" class="like-action theme-color-1 enabled ' + (this.isLiked ? 'like-action-liked' : '') + '" title="' + this.msg("like." + this.options.type + ".remove.tip") + '"></a>';
500          }
501          else
502          {
503             html = '<a href="#" class="like-action theme-color-1" title="' + this.msg("like." + this.options.type + ".add.tip") + '">' + this.msg("like." + this.options.type + ".add.label") + '</a>';
504          }
505          html += '<span class="likes-count">' + $html(this.totalLikes) + '</span>';
506          this.widgets.spanEl.innerHTML = html;
507          Alfresco.util.useAsButton(Selector.query("a", this.widgets.spanEl, true), function(e)
508          {
509             var isLiked = Dom.hasClass(Selector.query("a", this.widgets.spanEl, true), "like-action-liked");
510             this.like(!isLiked);
511             YAHOO.util.Event.preventDefault(e);
512          }, null, this);
513       },
514 
515       like: function(isLiked)
516       {
517          var orgValues =
518          {
519             isLiked: this.isLiked,
520             totalLikes: this.totalLikes
521          };
522 
523          this.isLiked = isLiked == 'true' || (YAHOO.lang.isBoolean(isLiked) && isLiked);
524          this.totalLikes = this.totalLikes + (this.isLiked ? 1 : -1);
525 
526          var responseConfig =
527          {
528             successCallback:
529             {
530                fn: function Like_like_success(event)
531                {
532                   var data = event.json.data;
533                   if (data)
534                   {
535                      this.totalLikes = data.ratingsCount;
536 
537                      // Post to the Activities Service on the "Like" action
538                      if (this.isLiked)
539                      {
540                         var activity = this.options.activity[this.options.type];
541                         if (activity)
542                         {
543                            var page = YAHOO.lang.substitute(activity.page, { nodeRef: this.options.nodeRef });
544                            Alfresco.Share.postActivity(this.options.siteId, activity.type, this.options.displayName, page,
545                            {
546                               nodeRef: this.options.nodeRef,
547                               fileName: this.options.displayName
548                            })
549                         }
550                      }
551                   }
552                },
553                scope: this
554             },
555             failureCallback:
556             {
557                fn: function Like_like_failure(event)
558                {
559                   this.isLiked = orgValues.isLiked;
560                   this.totalLikes = orgValues.totalLikes;
561                   this.render();
562                   Alfresco.util.PopupManager.displayPrompt(
563                   {
564                      text: this.msg("like.message.failure")
565                   });
566                },
567                scope: this
568             }
569          };
570 
571          if (this.isLiked)
572          {
573             this.services.likes.set(new Alfresco.util.NodeRef(this.options.nodeRef), 1, responseConfig);
574          }
575          else
576          {
577             this.services.likes.remove(new Alfresco.util.NodeRef(this.options.nodeRef), responseConfig);
578          }
579 
580          // Render new (unsaved) values
581          this.render();
582       }
583 
584    });
585 })();
586 
587 /**
588  * Favourite widget helper class.
589  *
590  * @namespace Alfresco
591  * @class Alfresco.Favourite
592  */
593 (function()
594 {
595    /**
596     * YUI Library aliases
597     */
598    var Dom = YAHOO.util.Dom,
599       Selector = YAHOO.util.Selector;
600 
601    /**
602     * Alfresco Slingshot aliases
603     */
604    var $html = Alfresco.util.encodeHTML,
605       $combine = Alfresco.util.combinePaths;
606 
607    /**
608     * Favourite constructor.
609     *
610     * @param {String} el The HTML id of the parent element
611     * @return {Alfresco.Favourite} The new Favourite instance
612     * @constructor
613     */
614    Alfresco.Favourite = function Favourite_constructor(el)
615    {
616       if (YAHOO.lang.isString(el))
617       {
618          el = Dom.get(el);
619       }
620       else if (!el.getAttribute("id"))
621       {
622          Alfresco.util.generateDomId(el);
623       }
624       YAHOO.util.Dom.addClass(el, "item-social");
625 
626       Alfresco.Favourite.superclass.constructor.call(this, "Alfresco.Favourite", el.getAttribute("id"), ["json"]);
627       
628       // Save references to dom object
629       this.widgets.spanEl = el;
630       this.services.preferences = new Alfresco.service.Preferences();
631 
632       return this;
633    };
634 
635    YAHOO.extend(Alfresco.Favourite, Alfresco.component.Base,
636    {
637 
638       /**
639        * Object container for initialization options
640        *
641        * @property options
642        * @type object
643        */
644       options:
645       {
646          /**
647           * Reference to the current document
648           *
649           * @property nodeRef
650           * @type String
651           */
652          nodeRef: null,
653 
654          /**
655           * The type of object to Favourite.
656           * Supported types are: "document", "folder".
657           *
658           * @property type
659           * @type String
660           * @default "document"
661           */
662          type: "document",
663 
664          /**
665           * The preference that will be favourised based on the type option
666           *
667           * @property preference
668           * @type {Object}
669           */
670          preference:
671          {
672             "folder":
673             {
674                key: Alfresco.service.Preferences.FAVOURITE_FOLDERS
675             },
676             "document":
677             {
678                key: Alfresco.service.Preferences.FAVOURITE_DOCUMENTS
679             }
680          }
681       },
682 
683       /**
684        * If the current user favourites the nodeRef
685        *
686        * @type {Boolean}
687        * @property isFavourite
688        */
689       isFavourite: false,
690 
691       /**
692        * NOTE! Implement when needed.
693        *
694        * Set id, load Like data and render.
695        *
696        * @method loadAndDisplay
697        */
698       loadAndDisplay: function Favourite_loadAndDisplay()
699       {
700          throw new Error("Not implemented yet, load data manually and use display(isFavourite) instead.");
701       },
702 
703       /**
704        * Display the favourite widget as a favourite or not depending on the isFavourite parameter
705        *
706        * @method display
707        * @param isFavourite {string}
708        */
709       display: function Favourite_display(isFavourite)
710       {
711          this.isFavourite = isFavourite || false;
712          this.render();
713       },
714 
715       /**
716        * Create html that represent a favourite button
717        *
718        * @method render
719        */
720       render: function Favourite_render()
721       {
722          var html = "";
723          if (this.isFavourite)
724          {
725             html = '<a href="#" class="favourite-action theme-color-1 favourite-' + this.options.type + ' enabled favourite-action-favourite" title="' + this.msg("favourite." + this.options.type + ".remove.tip") + '"></a>';
726          }
727          else
728          {
729             html = '<a href="#" class="favourite-action theme-color-1 favourite-' + this.options.type + '" title="' + this.msg("favourite." + this.options.type + ".add.tip") + '">' + this.msg("favourite." + this.options.type + ".add.label") + '</a>';
730          }
731          this.widgets.spanEl.innerHTML = html;
732          Alfresco.util.useAsButton(Selector.query("a", this.widgets.spanEl, true), function(e)
733          {
734             var isFavourite = Dom.hasClass(Selector.query("a", this.widgets.spanEl, true), "favourite-action-favourite");
735             this.favourite(!isFavourite);
736             YAHOO.util.Event.preventDefault(e);
737          }, null, this);
738 
739       },
740 
741       favourite: function(isFavourite)
742       {
743          var orgValues =
744          {
745             isFavourite: this.isFavourite
746          };
747 
748          this.isFavourite = isFavourite == 'true' || (YAHOO.lang.isBoolean(isFavourite) && isFavourite);
749 
750          var responseConfig =
751          {
752             failureCallback:
753             {
754                fn: function Favourite_favourite_failure(event)
755                {
756                   this.isFavourite = orgValues.isFavourite;
757                   this.render();
758                   Alfresco.util.PopupManager.displayPrompt(
759                   {
760                      text: this.msg("favourite.message.failure")
761                   });
762                },
763                scope: this
764             }
765          };
766 
767          // Save
768          var preference = this.options.preference[this.options.type];
769          if (preference)
770          {
771             var action = this.isFavourite ? "add" : "remove";
772             this.services.preferences[action].call(this.services.preferences, preference.key, this.options.nodeRef, responseConfig);
773          }
774          else
775          {
776             throw new Error("No prefence has been given for type '" + this-options.type + "'.");
777          }
778 
779          // Render new (unsaved) values
780          this.render();
781       }
782 
783    });
784 })();
785 
786 /**
787  * Alfresco Resizer.
788  *
789  * @namespace Alfresco.widget
790  * @class Alfresco.widget.Resizer
791  */
792 (function()
793 {
794    /**
795     * YUI Library aliases
796     */
797    var Dom = YAHOO.util.Dom;
798 
799    /**
800     * Resizer constructor.
801     *
802     * @return {Alfresco.widget.Resizer} The new Alfresco.widget.Resizer instance
803     * @constructor
804     */
805    Alfresco.widget.Resizer = function Resizer_constructor(p_name)
806    {
807       // Load YUI Components
808       Alfresco.util.YUILoaderHelper.require(["resize"], this.onComponentsLoaded, this);
809 
810       this.name = p_name;
811 
812       // Initialise prototype properties
813       this.widgets = {};
814 
815       return this;
816    };
817 
818    Alfresco.widget.Resizer.prototype =
819    {
820       /**
821        * Minimum Filter Panel height.
822        *
823        * @property MIN_FILTER_PANEL_HEIGHT
824        * @type int
825        */
826       MIN_FILTER_PANEL_HEIGHT: 200,
827 
828       /**
829        * Minimum Filter Panel width.
830        *
831        * @property MIN_FILTER_PANEL_WIDTH
832        * @type int
833        */
834       MIN_FILTER_PANEL_WIDTH: 140,
835 
836       /**
837        * Default Filter Panel width.
838        *
839        * @property DEFAULT_FILTER_PANEL_WIDTH
840        * @type int
841        */
842       DEFAULT_FILTER_PANEL_WIDTH: 160,
843 
844       /**
845        * Maximum Filter Panel width.
846        *
847        * @property MAX_FILTER_PANEL_WIDTH
848        * @type int
849        */
850       MAX_FILTER_PANEL_WIDTH: 500,
851 
852       /**
853        * Object container for storing YUI widget instances.
854        *
855        * @property widgets
856        * @type object
857        */
858       widgets: null,
859 
860       /**
861        * Object container for initialisation options
862        *
863        * @property options
864        * @type object
865        */
866       options:
867       {
868          /**
869           * DOM ID of left-hand container DIV
870           *
871           * @property divLeft
872           * @type string
873           * @default "alf-filters"
874           */
875          divLeft: "alf-filters",
876    
877          /**
878           * DOM ID of right-hand container DIV
879           *
880           * @property divRight
881           * @type string
882           * @default "alf-content"
883           */
884          divRight: "alf-content",
885    
886          /**
887           * Used to monitor document length
888           *
889           * @property documentHeight
890           * @type int
891           */
892          documentHeight: -1,
893          
894          /**
895           * Optional initial width of the resizer
896           * 
897           * @property initialWidth
898           * @type int
899           */
900          initialWidth: null
901       },
902       /**
903        * Fired by YUILoaderHelper when required component script files have
904        * been loaded into the browser.
905        *
906        * @method onComponentsLoaded
907        */
908       onComponentsLoaded: function Resizer_onComponentsLoaded()
909       {
910          YAHOO.util.Event.onDOMReady(this.onReady, this, true);
911       },
912 
913       /**
914        * Fired by YUI when parent element is available for scripting.
915        * Template initialisation, including instantiation of YUI widgets and event listener binding.
916        *
917        * @method onReady
918        */
919       onReady: function Resizer_onReady()
920       {
921          // Horizontal Resizer
922          this.widgets.horizResize = new YAHOO.util.Resize(this.options.divLeft,
923          {
924             handles: ["r"],
925             minWidth: this.MIN_FILTER_PANEL_WIDTH,
926             maxWidth: this.MAX_FILTER_PANEL_WIDTH
927          });
928 
929          // Before and End resize event handlers
930          this.widgets.horizResize.on("beforeResize", function(eventTarget)
931          {
932             this.onResize(eventTarget.width);
933          }, this, true);
934          this.widgets.horizResize.on("endResize", function(eventTarget)
935          {
936             this.onResize(eventTarget.width);
937          }, this, true);
938 
939          // Recalculate the vertical size on a browser window resize event
940          YAHOO.util.Event.on(window, "resize", function(e)
941          {
942             this.onResize();
943          }, this, true);
944 
945          // Monitor the document height for ajax updates
946          this.options.documentHeight = Dom.getXY("alf-ft")[1];
947 
948          YAHOO.lang.later(1000, this, function()
949          {
950             var h = Dom.getXY("alf-ft")[1];
951             if (Math.abs(this.options.documentHeight - h) > 4)
952             {
953                this.options.documentHeight = h;
954                this.onResize();
955             }
956          }, null, true);
957 
958          // Initial size
959          var width = (this.options.initialWidth ? this.options.initialWidth : this.DEFAULT_FILTER_PANEL_WIDTH);
960          if (YAHOO.env.ua.ie > 0)
961          {
962             this.widgets.horizResize.resize(null, this.widgets.horizResize.get("element").offsetHeight, width, 0, 0, true);
963          }
964          else
965          {
966             this.widgets.horizResize.resize(null, this.widgets.horizResize.get("height"), width, 0, 0, true);
967          }
968 
969          this.onResize(width);
970       },
971 
972       /**
973        * Fired by via resize event listener.
974        *
975        * @method onResize
976        */
977       onResize: function Resizer_onResize(width)
978       {
979          var cn = Dom.get(this.options.divLeft).childNodes,
980             handle = cn[cn.length - 1];
981 
982          Dom.setStyle(this.options.divLeft, "height", "auto");
983          Dom.setStyle(handle, "height", "");
984 
985          var h = Dom.getXY("alf-ft")[1] - Dom.getXY("alf-hd")[1] - Dom.get("alf-hd").offsetHeight;
986 
987          if (YAHOO.env.ua.ie === 6)
988          {
989             var hd = Dom.get("alf-hd"), tmpHeight = 0;
990             for (var i = 0, il = hd.childNodes.length; i < il; i++)
991             {
992                tmpHeight += hd.childNodes[i].offsetHeight;
993             }
994             h = Dom.get("alf-ft").parentNode.offsetTop - tmpHeight;
995          }
996          if (h < this.MIN_FILTER_PANEL_HEIGHT)
997          {
998             h = this.MIN_FILTER_PANEL_HEIGHT;
999          }
1000 
1001          Dom.setStyle(handle, "height", h + "px");
1002 
1003          if (width !== undefined)
1004          {
1005             // 8px breathing space for resize gripper
1006             Dom.setStyle(this.options.divRight, "margin-left", 8 + width + "px");
1007          }
1008 
1009          // Callback
1010          this.onResizeNotification();
1011       },
1012 
1013       /**
1014        * Fired after the onResize event
1015        * This needs overriding at the component level.
1016        *
1017        * @method onResizeNotification
1018        */
1019       onResizeNotification: function Resizer_onResizeNotification()
1020       {
1021       },
1022             
1023       /**
1024        * Set multiple initialization options at once.
1025        *
1026        * @method setOptions
1027        * @param obj {object} Object literal specifying a set of options
1028        * @return {Alfresco.widget.Resizer} returns 'this' for method chaining
1029        */
1030       setOptions: function Resizer_setOptions(obj)
1031       {
1032          this.options = YAHOO.lang.merge(this.options, obj);
1033          return this;
1034       }
1035    };
1036 })();
1037 
1038 
1039 /**
1040  * Dashlet title bar action controller
1041  *
1042  * When creating a new title bar action controller it is necessary to call setOptions with the following
1043  * attributes in a hash:
1044  * - actions: an array of the actions to display (see below)
1045  *
1046  * Actions:
1047  * Each action can have the following attributes:
1048  * - cssClass (required)      : this should be a CSS class that defines a 16x16 image to render as the action icon
1049  * - tooltip (options)        : this should be a message to use for the hover help tooltip
1050  * - eventOnClick (optional)  : this is the custom event event that will be fired when the action is clicked
1051  * - linkOnClick (optional)   : this is URL that the browser will redirect to when the action is clicked
1052  * - targetOnClick (optional) : this is the URL that the browser display in a new window/tab
1053  * - bubbleOnClick (optional) : this should be an object containing "message" (String) and "messageArgs" (String array) attributes
1054  *
1055  * @namespace Alfresco.widget
1056  * @class Alfresco.widget.DashletTitleBarActions
1057  */
1058 
1059 var DASHLET_TITLE_BAR_ACTIONS_OPACITY = 0,
1060    OPACITY_FADE_SPEED = 0.2;
1061 
1062 (function()
1063 {
1064    /**
1065     * YUI Library aliases
1066     */
1067    var Dom = YAHOO.util.Dom,
1068       Event = YAHOO.util.Event,
1069       Selector = YAHOO.util.Selector;
1070 
1071    /**
1072     * Dashlet Title Bar Action controller constructor.
1073     *
1074     * @return {Alfresco.widget.DashletTitleBarActions} The new Alfresco.widget.DashletTitleBarActions instance
1075     * @constructor
1076     */
1077    Alfresco.widget.DashletTitleBarActions = function DashletTitleBarActions_constructor(htmlId)
1078    {
1079       return Alfresco.widget.DashletTitleBarActions.superclass.constructor.call(this, "Alfresco.widget.DashletTitleBarActions", htmlId, ["selector"]);
1080    };
1081 
1082    YAHOO.extend(Alfresco.widget.DashletTitleBarActions, Alfresco.component.Base,
1083    {
1084       /**
1085        * DOM node of dashlet
1086        * Looks for first child DIV of dashlet with class="dashlet" and attach to this
1087        *
1088        * @property dashlet
1089        * @type object
1090        * @default null
1091        */
1092       dashlet: null,
1093 
1094       /**
1095        * DOM node of dashlet title
1096        * The first child DIV of dashlet with class="title"
1097        *
1098        * @property dashletTitle
1099        * @type object
1100        * @default null
1101        */
1102       dashletTitle: null,
1103 
1104       /**
1105        * DOM node of dashlet body
1106        * Resizer will look for first child DIV of dashlet with class="body" and resize this element
1107        *
1108        * @property dashletBody
1109        * @type object
1110        * @default null
1111        */
1112       dashletBody: null,
1113 
1114       /**
1115        * The that node containing all the actions nodes. The actions are
1116        * grouped under a single parent so that only one animation effect needs
1117        * to be applied.
1118        *
1119        * @property actionsNode
1120        * @type object
1121        * @default null
1122        */
1123       actionsNode: null,
1124 
1125       /**
1126        * Fired by YUI when parent element is available for scripting.
1127        * Template initialisation, including instantiation of YUI widgets and event listener binding.
1128        *
1129        * @method onReady
1130        */
1131       onReady: function DashletTitleBarActions_onReady()
1132       {
1133          this.dashlet = Selector.query("div.dashlet", Dom.get(this.id), true);
1134          this.dashletTitle = Selector.query("div.title", this.dashlet, true);
1135          this.dashletBody = Selector.query("div.body", this.dashlet, true);
1136          if (this.dashlet && this.dashletTitle && this.dashletBody)
1137          {
1138             this.actionsNode = document.createElement("div");
1139             Dom.addClass(this.actionsNode, "titleBarActions");  // This class sets the position of the actions.
1140             if (YAHOO.env.ua.ie > 0)
1141             {
1142                // IE doesn't handle the fading in/out very well so we won't do it. 
1143             }
1144             else
1145             {
1146                Dom.setStyle(this.actionsNode, "opacity", DASHLET_TITLE_BAR_ACTIONS_OPACITY);
1147             }
1148           
1149 
1150             // Add the actions node before the dashlet body...
1151             this.dashlet.insertBefore(this.actionsNode, this.dashletBody);
1152 
1153             // Reverse the order of the arrays so that the first entry is furthest to the left...
1154             this.options.actions.reverse();
1155             // Iterate through the array of actions creating a node for each one...
1156             for (var i = 0; i < this.options.actions.length; i++)
1157             {
1158                var currAction = this.options.actions[i];
1159                if (currAction.cssClass && (currAction.eventOnClick ||
1160                                            currAction.linkOnClick ||
1161                                            currAction.targetOnClick ||
1162                                            currAction.bubbleOnClick))
1163                {
1164                   var currActionNode = document.createElement("div");  // Create the node
1165                   if (currAction.tooltip)
1166                   {
1167                      Dom.setAttribute(currActionNode, "title", currAction.tooltip);
1168                   }
1169                   Dom.addClass(currActionNode, "titleBarActionIcon");
1170                   Dom.addClass(currActionNode, currAction.cssClass);   // Set the class (this should add the icon image
1171                   this.actionsNode.appendChild(currActionNode);        // Add the node to the parent
1172 
1173                   if (currAction.id)
1174                   {
1175                      currActionNode.id = this.id + currAction.id;
1176                   }
1177 
1178                   var _this = this;
1179                   if (currAction.eventOnClick)
1180                   {
1181                      Event.addListener(currActionNode, "click", (function(e)
1182                      {
1183                         // If the action is an event then the value passed should be a custom event that
1184                         // we will simply fire when the action node is clicked...
1185                         var customEvent = currAction.eventOnClick; // Copy this value as the currAction handle will be reassigned...
1186                         
1187                         return function(e)
1188                         {
1189                            _this._fadeOut(e, _this);
1190                            customEvent.fire({});
1191                         }
1192                      })());
1193                   }
1194                   else if (currAction.linkOnClick)
1195                   {
1196                      Event.addListener(currActionNode, "click", (function()
1197                      {
1198                         // If the action is a navigation link, then add a listener function that updates
1199                         // the browsers current location to be the supplied value...
1200                         var link = currAction.linkOnClick; // Copy this value as the currAction handle will be reassigned...
1201                         
1202                         return function()
1203                         { 
1204                            window.location = link;
1205                         };
1206                      })());
1207                   }
1208                   else if (currAction.targetOnClick)
1209                   {
1210                      Event.addListener(currActionNode, "click", (function()
1211                      {
1212                         // If the action is a target link, then open a new window/tab and set its location
1213                         // to the supplied value...
1214                         var target = currAction.targetOnClick; // Copy this value as the currAction handle will be reassigned...
1215                          
1216                         return function()
1217                         {
1218                            window.open(target);
1219                         };
1220                      })());
1221                   }
1222                   else if (currAction.bubbleOnClick)
1223                   {
1224                      var balloon = Alfresco.util.createBalloon(this.id,
1225                      {
1226                         html: currAction.bubbleOnClick.message,
1227                         width: "30em"
1228                      });
1229 
1230                      Event.addListener(currActionNode, "click", balloon.show, balloon, true);
1231                   }
1232                }
1233                else
1234                {
1235                   Alfresco.logger.warn("DashletTitleBarActions_onReady: Action is not valid.");
1236                }
1237             }
1238 
1239             // Add a listener to animate the actions...
1240             Event.addListener(this.dashlet, "mouseover", this._fadeIn, this);
1241             Event.addListener(this.dashlet, "mouseout", this._fadeOut, this);
1242          }
1243          else
1244          {
1245             // It's not possible to set up the actions without the dashlet, its title and the body
1246          }
1247       },
1248 
1249       /**
1250        * Fade the node actions out
1251        *
1252        * @method _fadeOut
1253        * @param e {event} The current event
1254        * @param me {scope} the context to run in
1255        * @protected
1256        */
1257       _fadeOut: function DashletTitleBarActions__fadeOut(e, me)
1258       {
1259          if (YAHOO.env.ua.ie > 0 && YAHOO.env.ua.ie < 9)
1260          {
1261             me.actionsNode.style.display = "none";
1262          }
1263          else
1264          {
1265             // Only fade out if the mouse has left the dashlet entirely
1266             if (!Dom.isAncestor(me.dashlet, Event.getRelatedTarget(e)))
1267             {
1268                var fade = new YAHOO.util.Anim(me.actionsNode,
1269                {
1270                   opacity:
1271                   {
1272                      to: DASHLET_TITLE_BAR_ACTIONS_OPACITY
1273                   }
1274                }, OPACITY_FADE_SPEED);
1275                fade.animate();
1276             }
1277          }
1278       },
1279 
1280       /**
1281        * Fade the actions node in
1282        *
1283        * @method _fadeIn
1284        * @param e {event} The current event
1285        * @param me {scope} the context to run in
1286        * @protected
1287        */
1288       _fadeIn: function DashletTitleBarActions__fadeIn(e, me)
1289       {
1290          if (YAHOO.env.ua.ie > 0 && YAHOO.env.ua.ie < 9)
1291          {
1292             me.actionsNode.style.display = "block";
1293          }
1294          else
1295          {
1296             var fade = new YAHOO.util.Anim(me.actionsNode,
1297             {
1298                opacity:
1299                {
1300                   to: 1
1301                }
1302             }, OPACITY_FADE_SPEED);
1303             fade.animate();
1304          }
1305       }
1306    });
1307 })();
1308 
1309 
1310 
1311 /**
1312  * Dashlet Resizer.
1313  *
1314  * @namespace Alfresco.widget
1315  * @class Alfresco.widget.DashletResizer
1316  */
1317 (function()
1318 {
1319    /**
1320    * YUI Library aliases
1321    */
1322    var Dom = YAHOO.util.Dom,
1323       Event = YAHOO.util.Event,
1324       Selector = YAHOO.util.Selector;
1325 
1326    /**
1327     * Dashlet Resizer constructor.
1328     *
1329     * @return {Alfresco.widget.DashletResizer} The new Alfresco.widget.DashletResizer instance
1330     * @constructor
1331     */
1332    Alfresco.widget.DashletResizer = function DashletResizer_constructor(htmlId, dashletId)
1333    {
1334       this.name = "Alfresco.widget.DashletResizer";
1335       this.id = htmlId;
1336       this.dashletId = dashletId;
1337 
1338       // Load YUI Components
1339       Alfresco.util.YUILoaderHelper.require(["resize", "selector"], this.onComponentsLoaded, this);
1340 
1341       // Initialise prototype properties
1342       this.widgets = {};
1343 
1344       return this;
1345    };
1346 
1347    Alfresco.widget.DashletResizer.prototype =
1348    {
1349       /**
1350        * Object container for initialization options
1351        *
1352        * @property options
1353        * @type object
1354        */
1355       options:
1356       {
1357          /**
1358           * The initial dashlet height.
1359           *
1360           * @property dashletHeight
1361           * @type int
1362           */
1363          dashletHeight: -1,
1364 
1365          /**
1366           * Minimum Dashlet height.
1367           *
1368           * @property minDashletHeight
1369           * @type int
1370           * @default 100
1371           */
1372          minDashletHeight: 80,
1373 
1374          /**
1375           * Maximum Dashlet height.
1376           *
1377           * @property maxDashletHeight
1378           * @type int
1379           * @default 1200
1380           */
1381          maxDashletHeight: 1200
1382       },
1383 
1384       /**
1385        * The dashletId.
1386        *
1387        * @property dashletId
1388        * @type string
1389        */
1390       dashletId: "",
1391 
1392       /**
1393        * Object container for storing YUI widget instances.
1394        *
1395        * @property widgets
1396        * @type object
1397        */
1398       widgets: null,
1399 
1400       /**
1401        * DOM node of dashlet
1402        * Resizer will look for first child DIV of dashlet with class="dashlet" and attach to this
1403        *
1404        * @property dashlet
1405        * @type object
1406        * @default null
1407        */
1408       dashlet: null,
1409 
1410       /**
1411        * DOM node of dashlet body
1412        * Resizer will look for first child DIV of dashlet with class="body" and resize this element
1413        *
1414        * @property dashletBody
1415        * @type object
1416        * @default null
1417        */
1418       dashletBody: null,
1419 
1420       /**
1421        * Difference in height between dashlet offsetHeight and dashletBody CSS height
1422        *
1423        * @property heightDelta
1424        * @type int
1425        * @default 0
1426        */
1427       heightDelta: 0,
1428 
1429       /**
1430        * Set multiple initialization options at once.
1431        *
1432        * @method setOptions
1433        * @param obj {object} Object literal specifying a set of options
1434        * @return {Alfresco.widget.DashletResizer} returns 'this' for method chaining
1435        */
1436       setOptions: function DashletResizer_setOptions(obj)
1437       {
1438          this.options = YAHOO.lang.merge(this.options, obj);
1439          return this;
1440       },
1441 
1442       /**
1443        * Fired by YUILoaderHelper when required component script files have
1444        * been loaded into the browser.
1445        *
1446        * @method onComponentsLoaded
1447        */
1448       onComponentsLoaded: function DashletResizer_onComponentsLoaded()
1449       {
1450          Event.onDOMReady(this.onReady, this, true);
1451       },
1452 
1453       /**
1454        * Fired by YUI when parent element is available for scripting.
1455        * Template initialisation, including instantiation of YUI widgets and event listener binding.
1456        *
1457        * @method onReady
1458        */
1459       onReady: function DashletResizer_onReady()
1460       {
1461          // Have permission to resize?
1462          if (!Alfresco.constants.DASHLET_RESIZE)
1463          {
1464             return;
1465          }
1466 
1467          // Find dashlet div
1468          this.dashlet = Selector.query("div.dashlet", Dom.get(this.id), true);
1469          if (!this.dashlet)
1470          {
1471             return;
1472          }
1473          Dom.addClass(this.dashlet, "resizable");
1474 
1475          // Find dashlet body div?
1476          this.dashletBody = Selector.query("div.body", this.dashlet, true);
1477          if (!this.dashletBody)
1478          {
1479             return;
1480          }
1481 
1482          // Difference in height between dashlet and dashletBody for resize events
1483          var origHeight = Dom.getStyle(this.dashlet, "height");
1484          if (origHeight == "auto")
1485          {
1486             origHeight = this.dashlet.offsetHeight - parseInt(Dom.getStyle(this.dashlet, "padding-bottom"), 10);
1487          }
1488          else
1489          {
1490             origHeight = parseInt(origHeight, 10);
1491          }
1492          this.heightDelta = origHeight - parseInt(Dom.getStyle(this.dashletBody, "height"), 10);
1493 
1494          // Create and attach Vertical Resizer
1495          this.widgets.resizer = new YAHOO.util.Resize(this.dashlet,
1496          {
1497             handles: ["b"],
1498             minHeight: this.options.minDashletHeight,
1499             maxHeight: this.options.maxDashletHeight
1500          });
1501 
1502          // During resize event handler
1503          this.widgets.resizer.on("resize", function()
1504          {
1505             this.onResize();
1506          }, this, true);
1507 
1508          // End resize event handler
1509          this.widgets.resizer.on("endResize", function(eventTarget)
1510          {
1511             this.onEndResize(eventTarget.height);
1512          }, this, true);
1513 
1514          // Clear the fixed-pixel width the dashlet has been given
1515          Dom.setStyle(this.dashlet, "width", "");
1516       },
1517 
1518       /**
1519        * Keeps track of iFrames that have been hidden during resize events so that they can be
1520        * made visible once resizing is complete.
1521        * 
1522        * @property 
1523        */
1524       _hiddenOnResize: null,
1525       
1526       /**
1527        * Fired by resize event listener.
1528        *
1529        * @method onResize
1530        */
1531       onResize: function DashletResizer_onResize()
1532       {
1533          var height = parseInt(Dom.getStyle(this.dashlet, "height"), 10) - this.heightDelta;
1534          
1535          // Find all the iFrames in the body of the dashlet and hide any that are visible. This
1536          // is done because an iFrame may contain Flash (or other objects that swallow mouseover
1537          // events) which will make it impossible to make the dashlet smaller. By hiding the 
1538          // events we make sure that the dashlet can be shrunk. However, we need to keep track
1539          // of the iFrames that we hide so that we can restore them when the resize operation is
1540          // completed.
1541          this._hiddenOnResize = [];
1542          var iFrames = this.dashletBody.getElementsByTagName("iframe");
1543          for (var i = 0; i<iFrames.length; i++)
1544          {
1545             var currStyle = Dom.getStyle(iFrames[i], "visibility");
1546             if (currStyle = "visible")
1547             {
1548                // Hide if visible and add to the list to make visible when resize is complete...
1549                this._hiddenOnResize.push(iFrames[i]);
1550                Dom.setStyle(iFrames[i], "visibility", "hidden");
1551             }
1552          }
1553          
1554          Dom.setStyle(this.dashletBody, "height", height + "px");
1555          Dom.setStyle(this.dashletBody.getElementsByTagName("iframe"), "height", height + "px");
1556       },
1557 
1558       /**
1559        * Fired by end resize event listener.
1560        *
1561        * @method onResize
1562        * @param h Height - not used
1563        */
1564       onEndResize: function DashletResizer_onEndResize(h)
1565       {
1566          // Clear the fixed-pixel width the dashlet has been given
1567          Dom.setStyle(this.dashlet, "width", "");
1568 
1569          // Make any iFrames that were hidden at the start of the resize operation
1570          // visible again.
1571          for (var i = 0; i<this._hiddenOnResize.length; i++)
1572          {
1573             Dom.setStyle(this._hiddenOnResize[i], "visibility", "visible");
1574          }
1575          this._hiddenOnResize = {};
1576          
1577          Alfresco.util.Ajax.jsonRequest(
1578          {
1579             method: "POST",
1580             url: Alfresco.constants.URL_SERVICECONTEXT + "modules/dashlet/config/" + this.dashletId,
1581             dataObj:
1582             {
1583                height: parseInt(Dom.getStyle(this.dashlet, "height"), 10) - this.heightDelta
1584             },
1585             successCallback: function(){},
1586             successMessage: null,
1587             failureCallback: function(){},
1588             failureMessage: null
1589          });
1590       }
1591    };
1592 })();
1593 
1594 /**
1595  * ShareFormManager component.
1596  *
1597  * Determines those pages defined as "AJAX state pages" and thus able to restore previous
1598  * state from URL arguments.
1599  *
1600  * @namespace Alfresco.component
1601  * @class Alfresco.component.ShareFormManager
1602  */
1603 (function()
1604 {
1605    /**
1606     * ShareFormManager constructor.
1607     *
1608     * @param {String} el The HTML id of the parent element
1609     * @return {Alfresco.component.ShareFormManager} The new ShareFormManager instance
1610     * @constructor
1611     */
1612    Alfresco.component.ShareFormManager = function ShareFormManager_constructor(el)
1613    {
1614       Alfresco.component.ShareFormManager.superclass.constructor.call(this, el);
1615 
1616       // Re-register with our own name
1617       this.name = "Alfresco.component.ShareFormManager";
1618       Alfresco.util.ComponentManager.reregister(this);
1619 
1620       // Instance variables
1621       this.options = YAHOO.lang.merge(this.options, Alfresco.component.ShareFormManager.superclass.options);
1622 
1623       return this;
1624    };
1625 
1626    YAHOO.extend(Alfresco.component.ShareFormManager, Alfresco.component.FormManager,
1627    {
1628       /**
1629        * Share pages that use ajax state ("#").
1630        *
1631        * @override
1632        * @method pageUsesAjaxState
1633        * @param url
1634        * @return {boolean} True if the url is recognised as a page that uses ajax states (adds values after "#" on the url)
1635        */
1636       pageUsesAjaxState: function FormManager_pageUsesAjaxState(url)
1637       {
1638          return (url.match(/documentlibrary([?]|$)/) ||
1639                url.match(/repository([?]|$)/) ||
1640                url.match(/my-workflows([?]|$)/) ||
1641                url.match(/my-tasks([?]|$)/));
1642       },
1643 
1644       /**
1645        * Override this method to make the user visit this url if no preferred url was given for a form and
1646        * there was no page visited before the user came to the form page.
1647        *
1648        * @method getSiteDefaultUrl
1649        * @return {string} The url to make the user visit if no other alternatives have been found
1650        */
1651       getSiteDefaultUrl: function FormManager_getSiteDefaultUrl()
1652       {
1653          return Alfresco.util.uriTemplate("userdashboardpage",
1654          {
1655             userid: encodeURIComponent(Alfresco.constants.USERNAME)
1656          });
1657       }
1658    });
1659 })();
1660 
1661 /**
1662  * Creates img markup representing a users avatar.
1663  *
1664  * @param userName {string} Username to display the avatar for
1665  * @param size {number} Optional: 64|32 are the currently supported avatar sizes. Default is 64px
1666  */
1667 Alfresco.Share.userAvatar = function(userName, size)
1668 {
1669    var imgUrl = Alfresco.constants.URL_CONTEXT + "components/images/no-user-photo-64.png";
1670    if (userName)
1671    {
1672       imgUrl = Alfresco.constants.PROXY_URI + "slingshot/profile/avatar/" + encodeURIComponent(userName);
1673       if (size === 32)
1674       {
1675          imgUrl += "/thumbnail/avatar32";
1676       }
1677    }
1678    return '<img src="' + imgUrl + '" alt="' + Alfresco.util.message("label.avatar") + '"/>';
1679 };
1680 
1681 /**
1682  * SimpleDocList component.
1683  *
1684  * Generates a simple DataTable-based document list view
1685  *
1686  * @namespace Alfresco.component
1687  * @class Alfresco.component.SimpleDocList
1688  */
1689 (function()
1690 {
1691    /**
1692     * YUI Library aliases
1693     */
1694    var Dom = YAHOO.util.Dom,
1695       Event = YAHOO.util.Event;
1696 
1697    /**
1698     * Alfresco Slingshot aliases
1699     */
1700    var $html = Alfresco.util.encodeHTML,
1701       $links = Alfresco.util.activateLinks,
1702       $userProfile = Alfresco.util.userProfileLink,
1703       $siteDashboard = Alfresco.util.siteDashboardLink,
1704       $relTime = Alfresco.util.relativeTime;
1705 
1706    /**
1707     * Use the getDomId function to get unique names for global event handling
1708     */
1709    var FAVOURITE_EVENTCLASS = Alfresco.util.generateDomId(null, "favourite"),
1710       LIKE_EVENTCLASS = Alfresco.util.generateDomId(null, "like");
1711 
1712    /**
1713     * SimpleDocList constructor.
1714     *
1715     * @param {String} htmlid The HTML id of the parent element
1716     * @return {Alfresco.component.SimpleDocList} The new SimpleDocList instance
1717     * @constructor
1718     */
1719    Alfresco.component.SimpleDocList = function SimpleDocList_constructor(htmlId)
1720    {
1721       Alfresco.component.SimpleDocList.superclass.constructor.call(this, "Alfresco.component.SimpleDocList", htmlId, ["button", "container", "datasource", "datatable", "animation"]);
1722 
1723       this.previewTooltips = [];
1724       this.metadataTooltips = [];
1725 
1726       // Preferences service
1727       this.services.preferences = new Alfresco.service.Preferences();
1728       this.services.likes = new Alfresco.service.Ratings(Alfresco.service.Ratings.LIKES);
1729       
1730       return this;
1731    };
1732    
1733    /**
1734     * Generate "Favourite" UI
1735     *
1736     * @method generateFavourite
1737     * @param scope {object} DocumentLibrary instance
1738     * @param record {object} DataTable record
1739     * @return {string} HTML mark-up for Favourite UI
1740     */
1741    Alfresco.component.SimpleDocList.generateFavourite = function SimpleDocList_generateFavourite(scope, record)
1742    {
1743       var i18n = "favourite." + (record.getData("isFolder") ? "folder." : "document."),
1744          html = "";
1745 
1746       if (record.getData("isFavourite"))
1747       {
1748          html = '<a class="favourite-action ' + FAVOURITE_EVENTCLASS + ' enabled" title="' + scope.msg(i18n + "remove.tip") + '" tabindex="0"></a>';
1749       }
1750       else
1751       {
1752          html = '<a class="favourite-action ' + FAVOURITE_EVENTCLASS + '" title="' + scope.msg(i18n + "add.tip") + '" tabindex="0">' + scope.msg(i18n + "add.label") + '</a>';
1753       }
1754 
1755       return html;
1756    };
1757 
1758    /**
1759     * Generate "Likes" UI
1760     *
1761     * @method generateLikes
1762     * @param scope {object} DocumentLibrary instance
1763     * @param record {object} DataTable record
1764     * @return {string} HTML mark-up for Likes UI
1765     */
1766    Alfresco.component.SimpleDocList.generateLikes = function SimpleDocList_generateLikes(scope, record)
1767    {
1768       var likes = record.getData("likes"),
1769          i18n = "like." + (record.getData("isFolder") ? "folder." : "document."),
1770          html = "";
1771 
1772       if (likes.isLiked)
1773       {
1774          html = '<a class="like-action ' + LIKE_EVENTCLASS + ' enabled" title="' + scope.msg(i18n + "remove.tip") + '" tabindex="0"></a>';
1775       }
1776       else
1777       {
1778          html = '<a class="like-action ' + LIKE_EVENTCLASS + '" title="' + scope.msg(i18n + "add.tip") + '" tabindex="0">' + scope.msg(i18n + "add.label") + '</a>';
1779       }
1780 
1781       html += '<span class="likes-count">' + $html(likes.totalLikes) + '</span>';
1782 
1783       return html;
1784    };
1785 
1786    /**
1787     * Generate "Comments" UI
1788     *
1789     * @method generateComments
1790     * @param scope {object} DocumentLibrary instance
1791     * @param record {object} DataTable record
1792     * @return {string} HTML mark-up for Comments UI
1793     */
1794    Alfresco.component.SimpleDocList.generateComments = function SimpleDocList_generateComments(scope, record)
1795    {
1796       var file = record.getData(),
1797          url = Alfresco.constants.URL_PAGECONTEXT + "site/" + file.location.site + "/" + (file.isFolder ? "folder" : "document") + "-details?nodeRef=" + file.nodeRef + "#comment",
1798          i18n = "comment." + (file.isFolder ? "folder." : "document.");
1799 
1800       return '<a href="' + url + '" class="comment" title="' + scope.msg(i18n + "tip") + '" tabindex="0">' + scope.msg(i18n + "label") + '</a>';
1801    };
1802 
1803    YAHOO.extend(Alfresco.component.SimpleDocList, Alfresco.component.Base,
1804    {
1805       /**
1806        * Object container for initialization options
1807        *
1808        * @property options
1809        * @type object
1810        */
1811       options:
1812       {
1813          /**
1814           * Show File size metadata
1815           *
1816           * @property showFileSize
1817           * @type boolean
1818           * @default true
1819           */
1820          showFileSize: true
1821       },
1822 
1823       /**
1824        * Holds IDs to register preview tooltips with.
1825        *
1826        * @property previewTooltips
1827        * @type array
1828        */
1829       previewTooltips: null,
1830 
1831       /**
1832        * Holds IDs to register metadata tooltips with.
1833        *
1834        * @property metadataTooltips
1835        * @type array
1836        */
1837       metadataTooltips: null,
1838 
1839       /**
1840        * Fired by YUI when parent element is available for scripting
1841        *
1842        * @method onReady
1843        */
1844       onReady: function SimpleDocList_onReady()
1845       {
1846          var me = this;
1847 
1848          // Tooltip for thumbnail on mouse hover
1849          this.widgets.previewTooltip = new YAHOO.widget.Tooltip(this.id + "-previewTooltip",
1850          {
1851             width: "108px"
1852          });
1853          this.widgets.previewTooltip.contextTriggerEvent.subscribe(function(type, args)
1854          {
1855             var context = args[0],
1856                record = me.widgets.alfrescoDataTable.getData(context.id),
1857                thumbnailUrl = Alfresco.constants.PROXY_URI + "api/node/" + record.nodeRef.replace(":/", "") + "/content/thumbnails/doclib?c=queue&ph=true";
1858 
1859             this.cfg.setProperty("text", '<img src="' + thumbnailUrl + '" />');
1860          });
1861 
1862          // Tooltip for metadata on mouse hover
1863          this.widgets.metadataTooltip = new YAHOO.widget.Tooltip(this.id + "-metadataTooltip");
1864          this.widgets.metadataTooltip.contextTriggerEvent.subscribe(function(type, args)
1865          {
1866             var context = args[0],
1867                record = me.widgets.alfrescoDataTable.getData(context.id),
1868                locn = record.location;
1869 
1870             var text = '<em>' + me.msg("label.site") + ':</em> ' + $html(locn.siteTitle) + '<br />';
1871             text += '<em>' + me.msg("label.path") + ':</em> ' + $html(locn.path);
1872 
1873             this.cfg.setProperty("text", text);
1874          });
1875 
1876          /**
1877           * Create datatable
1878           */
1879          this.widgets.alfrescoDataTable = new Alfresco.util.DataTable(
1880          {
1881             dataSource:
1882             {
1883                url: this.getWebscriptUrl(),
1884                initialParameters: this.getParameters(),
1885                config:
1886                {
1887                   responseSchema:
1888                   {
1889                      resultsList: "items"
1890                   }
1891                }
1892             },
1893             dataTable:
1894             {
1895                container: this.id + "-documents",
1896                columnDefinitions:
1897                [
1898                   { key: "thumbnail", sortable: false, formatter: this.bind(this.renderCellThumbnail), width: 16 },
1899                   { key: "detail", sortable: false, formatter: this.bind(this.renderCellDetail) }
1900                ],
1901                config:
1902                {
1903                   className: "alfresco-datatable simple-doclist",
1904                   renderLoopSize: 4
1905                }
1906             }
1907          });
1908 
1909          // Override DataTable function to set custom empty message
1910          var me = this,
1911             dataTable = this.widgets.alfrescoDataTable.getDataTable(),
1912             original_doBeforeLoadData = dataTable.doBeforeLoadData;
1913 
1914          dataTable.doBeforeLoadData = function SimpleDocList_doBeforeLoadData(sRequest, oResponse, oPayload)
1915          {
1916             if (oResponse.results.length === 0)
1917             {
1918                oResponse.results.unshift(
1919                {
1920                   isInfo: true,
1921                   title: me.msg("empty.title"),
1922                   description: me.msg("empty.description")
1923                });
1924             }
1925 
1926             return original_doBeforeLoadData.apply(this, arguments);
1927          };
1928 
1929          // Rendering complete event handler
1930          dataTable.subscribe("renderEvent", function()
1931          {
1932             // Register tooltip contexts
1933             this.widgets.previewTooltip.cfg.setProperty("context", this.previewTooltips);
1934             this.widgets.metadataTooltip.cfg.setProperty("context", this.metadataTooltips);
1935          }, this, true);
1936 
1937          // Hook favourite document events
1938          var fnFavouriteHandler = function SimpleDocList_fnFavouriteHandler(layer, args)
1939          {
1940             var owner = YAHOO.Bubbling.getOwnerByTagName(args[1].anchor, "div");
1941             if (owner !== null)
1942             {
1943                me.onFavourite.call(me, args[1].target.offsetParent, owner);
1944             }
1945             return true;
1946          };
1947          YAHOO.Bubbling.addDefaultAction(FAVOURITE_EVENTCLASS, fnFavouriteHandler);
1948 
1949          // Hook like/unlike events
1950          var fnLikesHandler = function SimpleDocList_fnLikesHandler(layer, args)
1951          {
1952             var owner = YAHOO.Bubbling.getOwnerByTagName(args[1].anchor, "div");
1953             if (owner !== null)
1954             {
1955                me.onLikes.call(me, args[1].target.offsetParent, owner);
1956             }
1957             return true;
1958          };
1959          YAHOO.Bubbling.addDefaultAction(LIKE_EVENTCLASS, fnLikesHandler);
1960       },
1961 
1962       /**
1963        * Generate base webscript url.
1964        * Can be overridden.
1965        *
1966        * @method getWebscriptUrl
1967        */
1968       getWebscriptUrl: function SimpleDocList_getWebscriptUrl()
1969       {
1970          return Alfresco.constants.PROXY_URI + "slingshot/doclib/doclist/documents/node/alfresco/sites/home?max=50";
1971       },
1972 
1973       /**
1974        * Generates webscript parameters based on current filters, etc.
1975        * Meant to be overridden depending on use case.
1976        *
1977        * @method getParameters
1978        */
1979       getParameters: function SimpleDocList_getParameters()
1980       {
1981          return "";
1982       },
1983 
1984       /**
1985        * Reloads the DataTable
1986        *
1987        * @method reloadDataTable
1988        */
1989       reloadDataTable: function SimpleDocList_reloadDataTable()
1990       {
1991          // Reset tooltips arrays
1992          this.previewTooltips = [];
1993          this.metadataTooltips = [];
1994 
1995          this.widgets.alfrescoDataTable.loadDataTable(this.getParameters());
1996       },
1997 
1998       /**
1999        * Thumbnail custom datacell formatter
2000        *
2001        * @method renderCellThumbnail
2002        * @param elCell {object}
2003        * @param oRecord {object}
2004        * @param oColumn {object}
2005        * @param oData {object|string}
2006        */
2007       renderCellThumbnail: function SimpleDocList_renderCellThumbnail(elCell, oRecord, oColumn, oData)
2008       {
2009          var columnWidth = 40,
2010             record = oRecord.getData(),
2011             desc = "";
2012 
2013          if (record.isInfo)
2014          {
2015             columnWidth = 52;
2016             desc = '<img src="' + Alfresco.constants.URL_RESCONTEXT + 'components/images/help-docs-bw-32.png" />';
2017          }
2018          else
2019          {
2020             var name = record.fileName,
2021                extn = name.substring(name.lastIndexOf(".")),
2022                locn = record.location,
2023                nodeRef = new Alfresco.util.NodeRef(record.nodeRef),
2024                docDetailsUrl = Alfresco.constants.URL_PAGECONTEXT + "site/" + locn.site + "/document-details?nodeRef=" + nodeRef.toString();
2025 
2026             if (this.options.simpleView)
2027             {
2028                /**
2029                 * Simple View
2030                 */
2031                var id = this.id + '-preview-' + oRecord.getId();
2032                desc = '<span id="' + id + '" class="icon32"><a href="' + docDetailsUrl + '"><img src="' + Alfresco.constants.URL_RESCONTEXT + 'components/images/filetypes/' + Alfresco.util.getFileIcon(name) + '" alt="' + extn + '" title="' + $html(name) + '" /></a></span>';
2033 
2034                // Preview tooltip
2035                this.previewTooltips.push(id);
2036             }
2037             else
2038             {
2039                /**
2040                 * Detailed View
2041                 */
2042                columnWidth = 100;
2043                desc = '<span class="thumbnail"><a href="' + docDetailsUrl + '"><img src="' + Alfresco.constants.PROXY_URI + 'api/node/' + nodeRef.uri + '/content/thumbnails/doclib?c=queue&ph=true" alt="' + extn + '" title="' + $html(name) + '" /></a></span>';
2044             }
2045          }
2046 
2047          oColumn.width = columnWidth;
2048 
2049          Dom.setStyle(elCell, "width", oColumn.width + "px");
2050          Dom.setStyle(elCell.parentNode, "width", oColumn.width + "px");
2051 
2052          elCell.innerHTML = desc;
2053       },
2054 
2055       /**
2056        * Detail custom datacell formatter
2057        *
2058        * @method renderCellDetail
2059        * @param elCell {object}
2060        * @param oRecord {object}
2061        * @param oColumn {object}
2062        * @param oData {object|string}
2063        */
2064       renderCellDetail: function SimpleDocList_renderCellDetail(elCell, oRecord, oColumn, oData)
2065       {
2066          var record = oRecord.getData(),
2067             desc = "";
2068 
2069          if (record.isInfo)
2070          {
2071             desc += '<div class="empty"><h3>' + record.title + '</h3>';
2072             desc += '<span>' + record.description + '</span></div>';
2073          }
2074          else
2075          {
2076             var id = this.id + '-metadata-' + oRecord.getId(),
2077                version = "",
2078                description = '<span class="faded">' + this.msg("details.description.none") + '</span>',
2079                dateLine = "",
2080                canComment = record.permissions.userAccess.create,
2081                locn = record.location,
2082                nodeRef = new Alfresco.util.NodeRef(record.nodeRef),
2083                docDetailsUrl = Alfresco.constants.URL_PAGECONTEXT + "site/" + locn.site + "/document-details?nodeRef=" + nodeRef.toString();
2084 
2085             // Description non-blank?
2086             if (record.description && record.description !== "")
2087             {
2088                description = $links($html(record.description));
2089             }
2090 
2091             // Version display
2092             if (record.version && record.version !== "")
2093             {
2094                version = '<span class="document-version">' + $html(record.version) + '</span>';
2095             }
2096             
2097             // Date line
2098             var dateI18N = "modified", dateProperty = record.modifiedOn;
2099             if (record.custom && record.custom.isWorkingCopy)
2100             {
2101                dateI18N = "editing-started";
2102             }
2103             else if (record.modifiedOn === record.createdOn)
2104             {
2105                dateI18N = "created";
2106                dateProperty = record.createdOn;
2107             }
2108             if (Alfresco.constants.SITE === "")
2109             {
2110                dateLine = this.msg("details." + dateI18N + "-in-site", $relTime(dateProperty), $siteDashboard(locn.site, locn.siteTitle, 'class="site-link theme-color-1" id="' + id + '"'));
2111             }
2112             else
2113             {
2114                dateLine = this.msg("details." + dateI18N + "-by", $relTime(dateProperty), $userProfile(record.modifiedByUser, record.modifiedBy, 'class="theme-color-1"'));
2115             }
2116 
2117             if (this.options.simpleView)
2118             {
2119                /**
2120                 * Simple View
2121                 */
2122                desc += '<h3 class="filename simple-view"><a class="theme-color-1" href="' + docDetailsUrl + '">' + $html(record.displayName) + '</a></h3>';
2123                desc += '<div class="detail"><span class="item-simple">' + dateLine + '</span></div>';
2124             }
2125             else
2126             {
2127                /**
2128                 * Detailed View
2129                 */
2130                desc += '<h3 class="filename"><a class="theme-color-1" href="' + docDetailsUrl + '">' + $html(record.displayName) + '</a>' + version + '</h3>';
2131 
2132                desc += '<div class="detail">';
2133                desc +=    '<span class="item">' + dateLine + '</span>';
2134                if (this.options.showFileSize)
2135                {
2136                   desc +=    '<span class="item">' + Alfresco.util.formatFileSize(record.size) + '</span>';
2137                }
2138                desc += '</div>';
2139                desc += '<div class="detail"><span class="item">' + description + '</span></div>';
2140 
2141                /* Favourite / Likes / Comments */
2142                desc += '<div class="detail detail-social">';
2143                desc +=    '<span class="item item-social">' + Alfresco.component.SimpleDocList.generateFavourite(this, oRecord) + '</span>';
2144                desc +=    '<span class="item item-social item-separator">' + Alfresco.component.SimpleDocList.generateLikes(this, oRecord) + '</span>';
2145                if (canComment)
2146                {
2147                   desc +=    '<span class="item item-social item-separator">' + Alfresco.component.SimpleDocList.generateComments(this, oRecord) + '</span>';
2148                }
2149                desc += '</div>';
2150             }
2151             
2152             // Metadata tooltip
2153             this.metadataTooltips.push(id);
2154          }
2155 
2156          elCell.innerHTML = desc;
2157       },
2158 
2159       /**
2160        * Like/Unlike event handler
2161        *
2162        * @method onLikes
2163        * @param row {HTMLElement} DOM reference to a TR element (or child thereof)
2164        */
2165       onLikes: function SimpleDocList_onLikes(row)
2166       {
2167          var file = this.widgets.alfrescoDataTable.getData(row),
2168             nodeRef = new Alfresco.util.NodeRef(file.nodeRef),
2169             likes = file.likes;
2170 
2171          likes.isLiked = !likes.isLiked;
2172          likes.totalLikes += (likes.isLiked ? 1 : -1);
2173 
2174          var responseConfig =
2175          {
2176             successCallback:
2177             {
2178                fn: function SimpleDocList_onLikes_success(event, p_nodeRef)
2179                {
2180                   var data = event.json.data;
2181                   if (data)
2182                   {
2183                      // Update the record with the server's value
2184                      var record = this.widgets.alfrescoDataTable.findRecordByParameter("nodeRef", p_nodeRef),
2185                         file = record.getData(),
2186                         likes = file.likes;
2187 
2188                      likes.totalLikes = data.ratingsCount;
2189                      this.widgets.alfrescoDataTable.getDataTable().updateRow(record, file);
2190 
2191                      // Post to the Activities Service on the "Like" action
2192                      if (likes.isLiked)
2193                      {
2194                         var activityData =
2195                         {
2196                            nodeRef: file.nodeRef
2197                         };
2198                         Alfresco.Share.postActivity(this.options.siteId, "file-liked", file.fileName, "document-details", activityData);
2199                      }
2200                   }
2201                },
2202                scope: this,
2203                obj: nodeRef.toString()
2204             },
2205             failureCallback:
2206             {
2207                fn: function SimpleDocList_onLikes_failure(event, p_nodeRef)
2208                {
2209                   // Reset the flag to it's previous state
2210                   var record = this.widgets.alfrescoDataTable.findRecordByParameter("nodeRef", p_nodeRef),
2211                      file = record.getData(),
2212                      likes = file.likes;
2213 
2214                   likes.isLiked = !likes.isLiked;
2215                   likes.totalLikes += (likes.isLiked ? 1 : -1);
2216                   this.widgets.alfrescoDataTable.getDataTable().updateRow(record, file);
2217                   Alfresco.util.PopupManager.displayPrompt(
2218                   {
2219                      text: this.msg("message.save.failure", file.displayName)
2220                   });
2221                },
2222                scope: this,
2223                obj: nodeRef.toString()
2224             }
2225          };
2226 
2227          if (likes.isLiked)
2228          {
2229             this.services.likes.set(nodeRef, 1, responseConfig);
2230          }
2231          else
2232          {
2233             this.services.likes.remove(nodeRef, responseConfig);
2234          }
2235          this.widgets.alfrescoDataTable.getDataTable().updateRow(record, file);
2236       },
2237 
2238       /**
2239        * Handler to set/reset favourite for document or folder
2240        *
2241        * @method onFavourite
2242        * @private
2243        * @param row {HTMLElement} DOM reference to a TR element (or child thereof)
2244        * @param prefKey {String} The preferences key
2245        */
2246       onFavourite: function SimpleDocList_onFavourite(row)
2247       {
2248          var record = this.widgets.alfrescoDataTable.getRecord(row),
2249             file = record.getData(),
2250             nodeRef = file.nodeRef;
2251 
2252          file.isFavourite = !file.isFavourite;
2253          this.widgets.alfrescoDataTable.getDataTable().updateRow(record, file);
2254 
2255          var responseConfig =
2256          {
2257             failureCallback:
2258             {
2259                fn: function SimpleDocList_onFavourite_failure(event, p_oRow)
2260                {
2261                   // Reset the flag to it's previous state
2262                   var record = this.widgets.alfrescoDataTable.getRecord(p_oRow),
2263                      file = record.getData();
2264 
2265                   file.isFavourite = !file.isFavourite;
2266                   this.widgets.alfrescoDataTable.getDataTable().updateRow(record, file);
2267                   Alfresco.util.PopupManager.displayPrompt(
2268                   {
2269                      text: this.msg("message.save.failure", file.displayName)
2270                   });
2271                },
2272                scope: this,
2273                obj: row
2274             }
2275          };
2276 
2277          this.services.preferences[file.isFavourite ? "add" : "remove"].call(this.services.preferences, Alfresco.service.Preferences.FAVOURITE_DOCUMENTS, nodeRef, responseConfig);
2278       }
2279    });
2280 })();