Source: documentlibrary/views/AlfGalleryView.js

/**
 * Copyright (C) 2005-2016 Alfresco Software Limited.
 *
 * This file is part of Alfresco
 *
 * Alfresco is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Alfresco is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * <p>This defines the widget model for rendering the gallery view. By default this is a grid based layout 
 * of thumbnails that can be scaled using a slider control. There are a number of ways in which this can
 * be configured to obtain alternative rendering.</p>
 * <p>By default the thumbnail size will be determined by the
 * available horizontal space for the configured number of 
 * [columns]{@link module:alfresco/documentlibrary/views/AlfGalleryView#columns} however it is possible to 
 * configure [resizeByColumnCount]{@link module:alfresco/documentlibrary/views/AlfGalleryView#resizeByColumnCount}
 * to be false such that the thumbnails will have a constant width defined by the configured
 * [thumbnailSize]{@link module:alfresco/documentlibrary/views/AlfGalleryView#thumbnailSize}.</p>
 * <p>When used in a [list]{@link module:alfresco/lists/AlfList} that is configured for
 * [infinite scrolling]{@link module:alfresco/lists/AlfList#useInfiniteScroll} it is sensible to
 * configure [showNextLink]{@link module:alfresco/documentlibrary/views/AlfGalleryView#showNextLink} to be true
 * such that a link is provided when the scrolling is not available when the thumbnails are so small
 * that an entire page of data fits within the browser window.</p>
 * <p>If something other than [thumbnails]{@link module:alfresco/renderers/GalleryThumbnail} needs to be
 * displayed then it is possible to 
 * [enable highlighting]{@link module:alfresco/documentlibrary/views/AlfGalleryView#enableHighlighting} so that
 * it is clear what item is currently focused - this will help greatly with keyboard navigation.</p>
 * <p>If more information needs to be displayed for an individual cell then it is possible to configure
 * one or more [expandTopics]{@link module:alfresco/documentlibrary/views/AlfGalleryView#expandTopics} that 
 * when published will reveal a panel in which additional data can be rendered.</p>
 *
 * @example <caption>A document list (for a site with name "site1") containing the default gallery view (rendering thumbnails).
 * The number of thumbnails rendered per row is determined by the "columns" attribute.</caption>
 * {
 *   name: "alfresco/documentlibrary/AlfDocumentList",
 *   config: {
 *     siteId: "site1",
 *     containerId: "documentLibrary",
 *     widgets: [
 *       {
 *         name: "alfresco/documentlibrary/views/AlfGalleryView",
 *         config: {
 *           columns: 8
 *         }
 *       }
 *     ]
 *   }
 * }
 *
 * @example <caption>A document list (for a site with name "site1") containing the default gallery view (rendering thumbnails).
 * Each thumbnail will be 200 pixels wide and as many as possible will be fit into each row depending upon the browser window
 * size.</caption>
 * {
 *   name: "alfresco/documentlibrary/AlfDocumentList",
 *   config: {
 *     siteId: "site1",
 *     containerId: "documentLibrary",
 *     widgets: [
 *       {
 *         name: "alfresco/documentlibrary/views/AlfGalleryView",
 *         config: {
 *           resizeByColumnCount: false,
 *           thumbnailSize: 200
 *         }
 *       }
 *     ]
 *   }
 * }
 * 
 * @example <caption>A document list (for a site with the name "site1") containing the default gallery view (rendering thumbnails). The slider will be
 * displayed in the toolbar.</caption>
 * {
 *   id: "TOOLBAR",
 *   name: "alfresco/documentlibrary/AlfToolbar"
 * },
 * {
 *   name: "alfresco/documentlibrary/AlfDocumentList",
 *   config: {
 *     additionalControlsTarget: "TOOLBAR",
 *     siteId: "site1",
 *     containerId: "documentLibrary",
 *     widgets: [
 *       {
 *         name: "alfresco/documentlibrary/views/AlfGalleryView"
 *       }
 *     ]
 *   }
 * }
 *
 * @example <caption>A document list (for a site with the name "site1") containing an alternative rendering to show the name
 * of each document.</caption>
 * {
 *   name: "alfresco/documentlibrary/AlfDocumentList",
 *   config: {
 *     siteId: "site1",
 *     containerId: "documentLibrary",
 *     widgets: [
 *       {
 *         name: "alfresco/documentlibrary/views/AlfGalleryView",
 *         config: {
 *           widgets: [
 *             {
 *               name: "alfresco/lists/views/layouts/CellContainer",
 *               config: {
 *                 widgets: [
 *                   {
 *                     name: "alfresco/renderers/Property",
 *                     config: {
 *                       propertyToRender: "displayName"
 *                     }
 *                   }
 *                 ]
 *               }
 *             }
 *           ]
 *         }
 *       }
 *     ]
 *   }
 * }
 *
 * @example <caption>A document list (for a site with the name "site1") containing an alternative rendering to show the name
 * of each document. When the CellContainer is clicked it will reveal an expanded section showing a ClassicWindow with the
 * name as it's title.</caption>
 * {
 *   name: "alfresco/documentlibrary/AlfDocumentList",
 *   config: {
 *     siteId: "site1",
 *     containerId: "documentLibrary",
 *     widgets: [
 *       {
 *         name: "alfresco/documentlibrary/views/AlfGalleryView",
 *         config: {
 *           enableHighlighting: true,
 *           itemKeyProperty: "nodeRef",
 *           expandTopics: ["EXPAND"],
 *           widgets: [
 *             {
 *               name: "alfresco/lists/views/layouts/CellContainer",
 *               config: {
 *                 publishTopic: "EXPAND",
 *                 publishPayloadType: "PROCESS",
 *                 publishPayloadModifiers: ["processCurrentItemTokens"],
 *                 publishPayloadItemMixin: true,
 *                 publishPayload: {
 *                   widgets: [
 *                     {
 *                       name: "alfresco/layout/ClassicWindow",
 *                       config: {
 *                         title: "{displayName}"
 *                       }
 *                     }
 *                   ]
 *                 },
 *                 widgets: [
 *                   {
 *                     name: "alfresco/renderers/Property",
 *                     config: {
 *                       propertyToRender: "displayName"
 *                     }
 *                   }
 *                 ]
 *               }
 *             }
 *           ]
 *         }
 *       }
 *     ]
 *   }
 * }
 * 
 * @module alfresco/documentlibrary/views/AlfGalleryView
 * @extends module:alfresco/lists/views/AlfListView
 * @mixes module:alfresco/lists/SelectedItemStateMixin
 * @author Dave Draper
 */
define(["dojo/_base/declare",
        "alfresco/lists/views/AlfListView",
        "alfresco/lists/SelectedItemStateMixin",
        "dojo/text!./templates/AlfGalleryView.html",
        "alfresco/lists/views/layouts/Grid",
        "alfresco/documentlibrary/AlfGalleryViewSlider",
        "alfresco/documentlibrary/ThumbnailSizeSlider",
        "dojo/_base/lang",
        "dojo/dom-style",
        "dojo/dom-geometry",
        "alfresco/core/topics"], 
        function(declare, AlfListView, SelectedItemStateMixin, template, Grid, AlfGalleryViewSlider, ThumbnailSizeSlider, 
                 lang, domStyle, domGeom, topics) {
   
   return declare([AlfListView, SelectedItemStateMixin], {
      
      /**
       * An array of the CSS files to use with this widget.
       * 
       * @instance
       * @type {object[]}
       * @default [{cssFile:"./css/AlfGalleryView.css"}]
       */
      cssRequirements: [{cssFile:"./css/AlfGalleryView.css"}],

      /**
       * The HTML template to use for the widget.
       * @instance
       * @type {String}
       */
      templateString: template,

      /**
       * This is the number of columns to use in the grid.
       * 
       * @instance
       * @type {number}
       * @default
       */
      columns: 4,

      /**
       * The preference property to use for retrieving and setting the users preferred number of columns
       *
       * @instance
       * @type {string}
       * @default
       * @since 1.0.35
       */
      columnsPreferenceProperty: "org.alfresco.share.documentList.galleryColumns",

      /**
       * Indicates whether or not highlighting should be enabled. If this is configured to be
       * true then highlighting of focus and expansion will be handled.
       *
       * @instance
       * @type {boolean}
       * @default
       * @since 1.0.44
       */
      enableHighlighting: false,

      /**
       * This is an array of optional topics that can be subscribed to to create a panel within the 
       * [grid]{@link module:alfresco/lists/views/layouts/Grid} for showing additional data about a 
       * particular cell in the grid. The payload should contain a "widgets" attribute that represents the model 
       * to render within the panel.
       * 
       * @instance
       * @type {string[]}
       * @default
       * @since 1.0.44
       */
      expandTopics: null,

      /**
       * This is the property that is used to uniquely identify each 
       * [item]{@link module:alfresco/core/CoreWidgetProcessing#currentItem} rendered in the
       * [grid]{@link module:alfresco/lists/views/layouts/Grid}. It is used
       * as the key in the [gridCellMapping]{@link module:alfresco/lists/views/layouts/Grid#gridCellMapping}
       * to map each item to the cell that it is rendered in. This is required in order to know where to 
       * exand the grid when the 
       * [expandTopics]{@link module:alfresco/lists/views/layouts/Grid#expandTopics} is
       * published.
       * 
       * @instance
       * @type {string}
       * @default
       * @since 1.0.44
       */
      itemKeyProperty: null,

      /**
       * This enables the mixed in [SelectedItemStateMixin]{@link module:alfresco/lists/SelectedItemStateMixin}
       * capabilities to track items as they are selected and deselected. This should only be changed from the
       * default when the view is not used within a [list]{@link module:alfresco/lists/AlfList} (as lists will
       * track selected items). This also ensures that when used outside of a list that the selected item 
       * state will be maintained when the thumbnails are resized.
       * 
       * @instance
       * @type {boolean}
       * @default
       * @since 1.0.39
       */
      manageSelectedItemState: false,

      /**
       * The label to use for the next link. This defaults to null, so MUST be set for the next link to be displayed.
       * 
       * @instance
       * @type {string}
       * @default
       */
      nextLinkLabel: null,

      /**
       * The topic to publish when the next link is clicked.
       * 
       * @instance
       * @type {string}
       * @default [topics.SCROLL_NEAR_BOTTOM]{@link module:alfresco/core/topics#SCROLL_NEAR_BOTTOM}
       * @event
       */
      nextLinkPublishTopic: topics.SCROLL_NEAR_BOTTOM,

      /**
       * Indicates whether resizing of thumbnails should be done by setting the number of columns to be
       * displayed (the number of columns will remain constant and the thumbnail size will change as
       * the size of the view changes). If this is configured to be false then a 
       * [thumbnailSize]{@link module:alfresco/documentlibrary/views/AlfGalleryView#thumbnailSize}
       * will be used and the number of columns will change as the size of the view changes.
       * 
       * @instance
       * @type {boolean}
       * @default
       * @since 1.0.40
       */
      resizeByColumnCount: true,

      /**
       * When set to true this will show a link for requesting more data (if available). This should be used when
       * the grid is rendering data in an infinite scroll view. It is required because when the grid cells are small
       * the data may not be sufficient to allow the scrolling events to occur that will request more data.
       * 
       * @instance
       * @type {boolean}
       * @default
       */
      showNextLink: false,

      /**
       * This is the size in pixels to set thumbnails initially. This only applies when 
       * [resizeByColumnCount]{@link module:alfresco/documentlibrary/views/AlfGalleryView#resizeByColumnCount}
       * is configured to be false.
       * 
       * @instance
       * @type {number}
       * @default
       * @since 1.0.40
       */
      thumbnailSize: 400,

      /**
       * The preference property to use for retrieving and setting the users preferred 
       * thumbnail size. This only applies when 
       * [resizeByColumnCount]{@link module:alfresco/documentlibrary/views/AlfGalleryView#resizeByColumnCount}
       * is configured to be false.
       *
       * @instance
       * @type {string}
       * @default
       * @since 1.0.40
       */
      thumbnailSizePreferenceProperty: "org.alfresco.share.documentList.thumbnailSize",

      /**
       * Returns the name of the view that is used when saving user view preferences.
       * 
       * @instance
       * @returns {string} "gallery"
       */
      getViewName: function alfresco_documentlibrary_views_AlfGalleryView__getViewName() {
         return "gallery";
      },
      
      /**
       * The configuration for selecting the view (configured the menu item)
       * @instance
       * @type {object}
       * @property {string|null} label The label or message key for the view (as appears in the menus)
       * @property {string|null} iconClass The class to place next to the label
       */
      viewSelectionConfig: {
         label: "doclist.view.gallery.label",
         iconClass: "alf-gallery-icon"
      },

      /**
       * <p>If [resizeByColumnCount]{@link module:alfresco/documentlibrary/views/AlfGalleryView#resizeByColumnCount}
       * is configured to be true this will subscribe to the
       * [SET_COLUMNS]{@link module:alfresco/core/topics#SET_COLUMNS} topic and each time the column count
       * is changed the [updateColumns]{@link module:alfresco/documentlibrary/views/AlfGalleryView#updateColumns}
       * function will be called.</p>
       * <p>If resizeByColumnCount]{@link module:alfresco/documentlibrary/views/AlfGalleryView#resizeByColumnCount}
       * is configured to be false this will subscribe to the
       * [SET_THUMBNAIL_SIZE]{@link module:alfresco/core/topics#SET_THUMBNAIL_SIZE} topic and each time the 
       * thumbnail size is changed the 
       * [updateThumbnailSize]{@link module:alfresco/documentlibrary/views/AlfGalleryView#updateThumbnailSize} function 
       * will be called.</p>
       * 
       * @instance
       * @listens module:alfresco/core/topics#SET_COLUMNS
       * @listens module:alfresco/core/topics#SET_THUMBNAIL_SIZE
       */
      postCreate: function alfresco_documentlibrary_views_AlfGalleryView__postCreate() {
         this.inherited(arguments);

         if (this.resizeByColumnCount)
         {
            this.alfSubscribe(topics.SET_COLUMNS, lang.hitch(this, this.updateColumns));
         }
         else
         {
            this.alfSubscribe(topics.SET_THUMBNAIL_SIZE, lang.hitch(this, this.updateThumbnailSize));
         }
         
         if (this.manageSelectedItemState)
         {
            this.createSelectedItemSubscriptions();
         }
      },

      /**
       * This function updates the [columns]{@link module:alfresco/documentlibrary/views/AlfGalleryView#columns}
       * attribute with the value attribute of the payload argument and then calls the
       * [renderView]{@link module:alfresco/documentlibrary/views/AlfGalleryView#renderView} function followed by the
       * [resizeCells]{@link module:alfresco/documentlibrary/views/AlfGalleryView#resizeCells} function
       * 
       * @instance
       * @param {object}
       */
      updateColumns: function alfresco_documentlibrary_views_AlfGalleryView__updateColumns(payload) {
         var newNumCols = payload && !isNaN(payload.value) && payload.value;
         if (newNumCols && newNumCols !== this.columns)
         {
            this.columns = payload.value;
            if (this.docListRenderer)
            {
               // In the case of infinite scroll, we need to ensure that we reset the count for rendering
               // data so that all the items are re-rendered and sized appropriately...
               if (lang.exists("docListRenderer.currentData.previousItemCount"), this)
               {
                  this.docListRenderer.currentData.previousItemCount = 0;
               }
               this.renderView(false);
            }
         }
      },

      /**
       * Updates the [thumbnailSize]{@link module:alfresco/documentlibrary/views/AlfGalleryView#thumbnailSize}
       * to of each displayed item.
       * 
       * @instance
       * @param  {object} payload A payload where the value is the new thumbnail size.
       * @since 1.0.40
       */
      updateThumbnailSize: function alfresco_documentlibrary_views_AlfGalleryView__updateThumbnailSize(payload) {
         var thumbnailSize = payload && !isNaN(payload.value) && payload.value;
         if (thumbnailSize && thumbnailSize !== this.thumbnailSize)
         {
            this.thumbnailSize = thumbnailSize;
            if (this.docListRenderer)
            {
               // In the case of infinite scroll, we need to ensure that we reset the count for rendering
               // data so that all the items are re-rendered and sized appropriately...
               if (lang.exists("docListRenderer.currentData.previousItemCount"), this)
               {
                  this.docListRenderer.currentData.previousItemCount = 0;
               }
               this.renderView(false);
            }
         }
      },
      
      /**
       * Overridden to return a new instance of [AlfGalleryViewSlider]{@link module:alfresco/documentlibrary/AlfGalleryViewSlider} to control the 
       * number of columns that should be displayed in the gallery.
       * 
       * @instance
       * @returns {object} A new slider control [AlfGalleryViewSlider]{@link module:alfresco/documentlibrary/AlfGalleryViewSlider}
       */
      getAdditionalControls: function alfresco_documentlibrary_views_AlfGalleryView__getAdditionalControls() {
         if (this.resizeByColumnCount)
         {
            // NOTE: Can't call createWidget because it is overridden by the MultiItemMixinRenderer...
            return [new AlfGalleryViewSlider({
               relatedViewName: this.getViewName(),
               pubSubScope: this.pubSubScope,
               parentPubSubScope: this.parentPubSubScope,
               columns: this.columns,
               columnsPreferenceProperty: this.columnsPreferenceProperty
            })];
            
         }
         else
         {
            // NOTE: Can't call createWidget because it is overridden by the MultiItemMixinRenderer...
            return [new ThumbnailSizeSlider({
               relatedViewName: this.getViewName(),
               pubSubScope: this.pubSubScope,
               parentPubSubScope: this.parentPubSubScope,
               value: this.thumbnailSize,
               preferenceProperty: this.thumbnailSizePreferenceProperty
            })];
         }
      },
      
      /**
       * Extends the default implementation to resize the cells in the gallery.
       * 
       * @instance
       * @param {boolean} preserveCurrentData This should be set to true when you don't want to clear the old data, the
       * most common example of this is when infinite scroll is being used.
       */
      renderView: function alfresco_documentlibrary_views_AlfGalleryView__renderView(preserveCurrentData) {
         // jshint unused:false
         if (this._renderingView)
         {
            // If currently rendering a view, then indicate that a request to re-render is pending...
            this._pendingViewRendering = true;
         }
         else
         {
            this.alfLog("log", "Rendering view for " + this.columns + " columns");
            
            // Set a flag to indicate that view rendering has started (so that we don't re-start rendering 
            // before the current render completes)...
            this._renderingView = true;
            this.inherited(arguments);
            if (this.docListRenderer)
            {
               this.docListRenderer.resizeCells();
            }
            if (this.manageSelectedItemState)
            {
               this.publishSelectedItems();
            }
            this._renderingView = false;

            // Once the current render has finished, check to see if there is pending render request to fulfil...
            if (this._pendingViewRendering)
            {
               this._pendingViewRendering = false;
               this.renderView(preserveCurrentData);
            }
         }
      },
      
      /**
       * Creates a new [ListRenderer]{@link module:alfresco/lists/views/ListRenderer}
       * which is used to render the actual items in the view. This function can be overridden by extending views
       * (such as the [Film Strip View]{@link module:alfresco/documentlibrary/views/AlfFilmStripView}) to create
       * alternative widgets applicable to that view.
       * 
       * @instance
       * @returns {object} A new [ListRenderer]{@link module:alfresco/lists/views/ListRenderer}
       */
      createListRenderer: function alfresco_documentlibrary_views_AlfGalleryView__createListRenderer() {
         var dlr = new Grid({
            id: this.id + "_ITEMS",
            widgets: lang.clone(this.widgets),
            currentData: this.currentData,
            pubSubScope: this.pubSubScope,
            parentPubSubScope: this.parentPubSubScope,
            columns: this.columns,
            fixedColumns: this.resizeByColumnCount,
            showNextLink: this.showNextLink,
            nextLinkLabel: this.nextLinkLabel,
            nextLinkPublishTopic: this.nextLinkPublishTopic,
            thumbnailSize: this.thumbnailSize,
            itemKeyProperty: this.itemKeyProperty,
            expandTopics: this.expandTopics,
            enableHighlighting: this.enableHighlighting,
            widgetsForAppendix: this.widgetsForAppendix
         });
         return dlr;
      },

      /**
       * Called after the view has been shown (note that [renderView]{@link module:alfresco/lists/views/AlfListView#renderView}
       * does not mean that the view has been displayed, just that it has been rendered. 
       * @instance
       */
      onViewShown: function alfresco_documentlibrary_views_AlfGalleryView__onViewShown() {
         if (this.docListRenderer)
         {
            this.docListRenderer.resizeCells();
         }
      },
      
      /**
       * The definition for rendering an item in the view.
       * 
       * @instance
       * @type {object[]}
       */
      widgets: [
         {
            name: "alfresco/renderers/GalleryThumbnail"
         }
      ]
   });
});