/**
* 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 extends the standard [Row widget]{@link module:alfresco/lists/views/layouts/Row} to provide the ability to
* toggle between the standard mode (for reading data) and an edit mode.</p>
* <p>It is expected that edit mode will be able to handle user input and during this mode the normal list keyboard
* navigation behaviour (e.g. the ability to use the cursor keys to navigate up and down the list) will be suspended
* until edit mode is exited. When a row has focus in read mode the user can use a combination of the CONTROL and "E"
* keys to enter edit mode, and in edit mode they can use the ESCAPE key to cancel.</p>
* <p>Developers should include widgets for entering and existing edit mode (e.g. they could include a
* [PublishAction widget]{@link module:alfresco/renderers/PublishAction} in the read view to enter edit mode and a
* [button]{@link module:alfresco/buttons/AlfButton} to exit edit mode).</p>
* <p>A typical usage might be to create a [form]{@link module:alfresco/forms/Form} as the edit mode. In this scenario
* it would be expected to set the [okButtonPublishTopic]{@link module:alfresco/forms/Form#okButtonPublishTopic}
* to use the [readModeSavePublishTopic]{@link module:alfresco/lists/views/layouts/EditableRow#readModeSavePublishTopic}
* and the [cancelButtonPublishTopic]{@link module:alfresco/forms/Form#cancelButtonPublishTopic} to use the
* [readModeCancelPublishTopic]{@link module:alfresco/lists/views/layouts/EditableRow#readModeCancelPublishTopic}. An even
* better approach would be to go via an intermediary service so that edit mode is only exited on successful update
* of data to the repository.</p>
*
* @module alfresco/lists/views/layouts/EditableRow
* @extends module:alfresco/lists/views/layouts/Row
* @mixes module:alfresco/core/ObjectProcessingMixin
* @mixes module:alfresco/lists/KeyboardNavigationSuppressionMixin
* @author Dave Draper
*/
define(["dojo/_base/declare",
"alfresco/lists/views/layouts/Row",
"alfresco/core/ObjectProcessingMixin",
"alfresco/lists/KeyboardNavigationSuppressionMixin",
"dojo/_base/lang",
"dojo/_base/array",
"dojo/dom-construct",
"dojo/dom-class",
"dojo/dom-style",
"dojo/on"],
function(declare, Row, ObjectProcessingMixin, KeyboardNavigationSuppressionMixin,
lang, array, domConstruct, domClass, domStyle, on) {
return declare([Row, ObjectProcessingMixin, KeyboardNavigationSuppressionMixin], {
/**
* An array of the CSS files to use with this widget.
*
* @instance
* @type {object[]}
* @default [{cssFile:"./css/EditableRow.css"}]
*/
cssRequirements: [{cssFile:"./css/EditableRow.css"}],
/**
* This is the default topic that will be subscribed to once edit mode has been entered (e.g.
* during the [createEditModeWidgets]{@link module:alfresco/lists/views/layouts/EdiableRow#createEditModeWidgets}
* function. This value can be overridden through configuration if required.
*
* @instance
* @type {string}
* @default
*/
readModeSavePublishTopic: "ALF_EDITABLE_ROW_READ_MODE_SAVE",
/**
* This is the default topic that will be subscribed to once edit mode has been entered (e.g.
* during the [createEditModeWidgets]{@link module:alfresco/lists/views/layouts/EdiableRow#createEditModeWidgets}
* function. This value can be overridden through configuration if required.
*
* @instance
* @type {string}
* @default
*/
readModeCancelPublishTopic: "ALF_EDITABLE_ROW_READ_MODE_CANCEL",
/**
* This is the default topic that will be subscribed to in order to process requests to enter
* edit mode. This value can be overrridden through configuration if required.
*
* @instance
* @type {string}
* @default
*/
editModePublishTopic: "ALF_EDITABLE_ROW_EDIT_MODE",
/**
*
* @instance
*/
postCreate: function alfresco_lists_views_layouts_EditableRow__postCreate() {
// Generate a new pubSubScope to ensure that each row only responds to requests to enter
// edit mode from widgets within itself...
if (!this.pubSubScope)
{
this.pubSubScope = this.generateUuid();
}
domClass.add(this.domNode, this.additionalCssClasses ? this.additionalCssClasses : "");
domClass.add(this.domNode, "alfresco-lists-views-layouts-EditableRow");
// NOTE: We don't rely on the inherited widget capabilities for creating the initial widgets because
// it doesn't clone the model. We need to ensure that the model is cloned so that variable substitution
// behaves correctly each time we enter the read mode (e.g. after making changes to data)...
this.createReadModeWidgets();
// Use the onKeyPress function from the KeyboardNavigationSuppressionMixin to capture CTRL-E events
// to enter edit mode...
on(this.domNode, "keypress", lang.hitch(this, this.onKeyPress));
if (this.widgetsForEditMode)
{
this.alfSubscribe(this.editModePublishTopic, lang.hitch(this, this.onEditMode));
}
else
{
this.alfLog("warn", "No widgets were provided for editing the row", this);
}
},
/**
* Used to indicate whether or not the [widgetsForEditMode]{@link module:alfresco/lists/views/layouts/EditableRow#widgetsForEditMode}
* should be processed.
*
* @instance
* @type {Boolean}
* @default
*/
_alfEditModeProcessing: false,
/**
* This is an extension point for handling the completion of calls to
* [processWidgets]{@link module:alfresco/core/Core#processWidgets}. After processing the
* initial (read display) widgets the [_alfEditModeProcessing]{@link module:alfresco/lists/views/layouts/EdiableRow#_alfEditModeProcessing}
* attribute is set to true to indicate that the next iteration will represent the completion
* of the creation of the edit display widgets.
*
* @instance
* @param {Array} widgets An array of all the widgets that have been processed
*/
allWidgetsProcessed: function alfresco_lists_views_layouts_EditableRow__allWidgetsProcessed(widgets) {
if (!this._alfEditModeProcessing)
{
// Need to count the number of cells, so we can figure out the colspan for our edit cell...
// The assumption here is that there is one cell per widget...
this._requiredColspan = widgets.length;
this._readWidgets = widgets;
// Set this after creating the basic row widgets, next time we enter this function we
this._alfEditModeProcessing = true;
}
else
{
this._editWidgets = widgets;
}
},
/**
* Cleans up the mode by destroying the widgets, emptying the node and then recreating them.
*
* @instance
* @param {array} widgets The widgets to destroy
* @param {object} node The DOM node to empty.
*/
cleanUpMode: function alfresco_lists_views_layouts_EditableRow__cleanUpMode(widgets, node) {
if (widgets)
{
array.forEach(widgets, function(widget) {
if (typeof widget.destroy === "function")
{
widget.destroy(false);
}
});
domConstruct.empty(node);
}
},
/**
* Handles save events. Just because edit mode is exited does not mean that any data needs to be updated (e.g. the user
* could have requested to cancel editing). However when a save is required this function will be used to update the
* currentItem with the updated data.
*
* @instance
* @param {object} payload The payload containing the updated data for the currentItem
*/
onSave: function alfresco_lists_views_layouts_EditableRow__onSave(payload) {
// Update the current item value...
lang.mixin(this.currentItem, payload);
// Re-build the read display...
this.cleanUpMode(this._readWidgets, this.containerNode);
this._alfEditModeProcessing = false;
this.createReadModeWidgets();
// Switch back into read mode to reveal the updated data...
this.onReadMode();
},
/**
* Handles requests to enter edit mode for the row.
*
* @param {object} payload The payload on the request to enter read mode.
*/
onReadMode: function alfresco_lists_views_layouts_EditableRow__onReadMode(/* jshint unused:false */ payload) {
this.suppressContainerKeyboardNavigation(false);
// Show read mode and hide edit mode...
domStyle.set(this.domNode, "display", "table-row");
domStyle.set(this.editModeCell, "display", "none");
// Destroy the edit widgets...
this.cleanUpMode(this._editWidgets, this.editWidgetsNode);
},
/**
* Delegate edit clicks (issued from the [KeyboardNavigationSuppressionMixin]{@link module:alfresco/lists/KeyboardNavigationSuppressionMixin})
* to the [onEditMode]{@link module:alfresco/lists/views/layouts/EdiableRow#onEditMode} function.
*
* @instance
*/
onEditClick: function alfresco_lists_views_layouts_EditableRow__onEditClick() {
this.onEditMode();
},
/**
* Handles requests to enter edit mode for the row.
*
* @param {object} payload The payload on the request to enter edit mode.
*/
onEditMode: function alfresco_lists_views_layouts_EditableRow__onEditMode(/* jshint unused:false */ payload) {
this.suppressContainerKeyboardNavigation(true);
if (!this._editModeInitialised)
{
// Create nodes for edit mode and process widgets...
this.createEditModeWidgets();
this.alfSubscribe(this.readModeSavePublishTopic, lang.hitch(this, this.onSave));
this.alfSubscribe(this.readModeCancelPublishTopic, lang.hitch(this, this.onReadMode));
this._editModeInitialised = true;
}
// Create the edit mode widgets...
var widgets = lang.clone(this.widgetsForEditMode);
this.processObject(["processCurrentItemTokens"], widgets);
this.processWidgets(widgets, this.editWidgetsNode);
domStyle.set(this.domNode, "display", "none");
domStyle.set(this.editModeCell, "display", "table-row");
},
/**
* Creates the widgets for the read display.
*
* @instance
*/
createReadModeWidgets: function alfresco_lists_views_layouts_EditableRow__createReadModeWidgets() {
if (this.widgets)
{
var widgets = lang.clone(this.widgets);
if (this.widgetModelModifiers !== null)
{
this.processObject(this.widgetModelModifiers, widgets);
}
this.processWidgets(widgets, this.containerNode);
}
},
/**
* Used to create the DOM elements to display the edit mode. This is called the first time that the
* [onEditMode]{@link module:alfresco/lists/views/layouts/EdiableRow#onEditMode} is executed.
*
* @instance
*/
createEditModeWidgets: function alfresco_lists_views_layouts_EditableRow__createEditModeWidgets() {
// We need to construct some elements for the edit mode widgets to go into. All the
// edit mode widgets will live under a single cell that is not displayed in read mode.
this.editRowNode = domConstruct.create("tr", {
"class": "editRowNode"
}, this.domNode, "after");
this.editModeCell = domConstruct.create("td", {
colspan: this._requiredColspan
}, this.editRowNode, "last");
// Setup event handler so prevent the outer list from swallowing keyboard activity when in
// edit mode...
on(this.editModeCell, "keypress", lang.hitch(this, this.onValueEntryKeyPress));
on(this.editModeCell, "click", lang.hitch(this, this.suppressFocusRequest));
this.editWidgetsNode = domConstruct.create("div", {}, this.editModeCell);
},
/**
* The JSON model describing the widgets to use to create the edit mode. This needs to be
* configured in order to edit mode to work at all.
*
* @instance
* @type {array}
* @default
*/
widgetsForEditMode: null
});
});