/**
* 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 widget provides the means to dynamically reveal and hide its contents by clicking on its label. The
* open or closed state is indicated by a "twister" icon. The widget can be configured to render any other widget
* model as its contents. The twister can be configured to be intially open or closed by setting the
* [initiallyOpen]{@link module:alfresco/layout/Twister#initiallyOpen} attribute to true (for open) or false (for closed).</p>
*
* <p>It is also possible for the open or closed state to be stored to a users personal preferences. This can be done
* by configuring a [preferenceName]{@link module:alfresco/layout/Twister#preferenceName} attribute
* and ensuring that the [PreferenceService]{@link module:alfresco/services/PreferenceService} (or equivilant) is included
* on the page. A single [preferenceName]{@link module:alfresco/layout/Twister#preferenceName} can be
* shared between multiple widgets but this will result in all those twisters being in the same state when the page loads.
* Using user preferences will override whatever [initiallyOpen]{@link module:alfresco/layout/Twister#initiallyOpen}
* attribute has been configured.</p>
*
* <p>It is also possible to configure an optional [headingLevel]{@link module:alfresco/layout/Twister#headingLevel} to improve
* the overall accessibility of the page containing the twisters. The level provided should be a number between 1 and 6
* and will control the HTML element that is used for the twister label (e.g. 1 will render an H1 element, etc).</p>
*
* @example <caption>Example twister that is initially closed</caption>
* {
* name: "alfresco/layout/Twister",
* config: {
* label: "Initially Closed Twister",
* headingLevel: 3,
* initiallyOpen: false,
* widgets: [
* {
* id: "LOGO1",
* name: "alfresco/logo/Logo"
* }
* ]
* }
* }
*
* @example <caption>Example twister that uses user preferences to control the open/closed state</caption>
* {
* name: "alfresco/layout/Twister",
* config: {
* label: "Twister State Based on User Preference",
* preferenceName: "Twister1",
* widgets: [
* {
* id: "LOGO1",
* name: "alfresco/logo/Logo"
* }
* ]
* }
* }
*
* @module alfresco/layout/Twister
* @extends external:dijit/_WidgetBase
* @mixes external:dojo/_TemplatedMixin
* @mixes external:dijit/_OnDijitClickMixin
* @mixes module:alfresco/core/Core
* @mixes module:alfresco/core/CoreWidgetProcessing
* @mixes module:alfresco/services/_PreferenceServiceTopicMixin
* @author Dave Draper
*/
define(["dojo/_base/declare",
"dijit/_WidgetBase",
"dijit/_TemplatedMixin",
"dijit/_OnDijitClickMixin",
"alfresco/services/_PreferenceServiceTopicMixin",
"alfresco/core/Core",
"alfresco/core/CoreWidgetProcessing",
"dojo/text!./templates/Twister.html",
"dojo/_base/lang",
"dojo/_base/array",
"dojo/dom-construct",
"dojo/dom-class",
"dojo/dom-style",
"dojo/dom-attr",
"dojo/_base/event"],
function(declare, _WidgetBase, _TemplatedMixin, _OnDijitClickMixin, _PreferenceServiceTopicMixin, AlfCore, CoreWidgetProcessing,
template, lang, array, domConstruct, domClass, domStyle, domAttr, event) {
return declare([_WidgetBase, _TemplatedMixin, _OnDijitClickMixin, AlfCore, CoreWidgetProcessing, _PreferenceServiceTopicMixin], {
/**
* An array of the CSS files to use with this widget.
*
* @instance cssRequirements {Array}
* @type {object[]}
* @default [{cssFile:"./css/Twister.css"}]
*/
cssRequirements: [{cssFile:"./css/Twister.css"}],
/**
* The HTML template to use for the widget.
* @instance
* @type {String}
*/
templateString: template,
/**
* The CSS class applied when the twister is opened (and removed when closed).
*
* @instance
* @type {string}
* @default
*/
CLASS_OPEN: "alfresco-layout-Twister--open",
/**
* The CSS class applied when the twister is closed (and removed when opened).
*
* @instance
* @type {string}
* @default
*/
CLASS_CLOSED: "alfresco-layout-Twister--closed",
/**
* Should the generated twister use a heading or div for it's heading?
*
* @instance
* @type {number}
* @default
*/
headingLevel: null,
/**
* The initial open/closed state of the twister. This value could be overridden by a previously stored user preference
* if a [preferenceName]{@link module:alfresco/layout/Twister#preferenceName} is configured and the
* [PreferenceService]{@link module:alfresco/services/PreferenceService} is included on the page.
*
* @instance
* @type {boolean}
* @default
*/
initiallyOpen: true,
/**
* The preference name to use for storing and retrieving the users preferred open/closed state for this twister.
* In order for this preference to be used it will also be necessary to ensure that the
* [PreferenceService]{@link module:alfresco/services/PreferenceService} is included on the page.
*
* @instance
* @type {string}
* @default
*/
preferenceName: null,
/**
* This is the prefix that will be applied to all preferences. It can be optionally configured to a different value
* but this is typically not necessary. One possible reason to change the prefix would be to allow user preferences
* to be split across different client implementations.
*
* @instance
* @type {string}
* @default
*/
preferencePrefix: "org.alfresco.share.twisters.",
/**
* The width to make the twister. Units such as "px" should be included. By default all available
* horizontal width will be used.
*
* @instance
* @type {string}
* @default
*/
width: "auto",
/**
* @instance
*/
postMixInProperties: function alfresco_layout_Twister__postMixInProperties() {
if (this.label)
{
this.label = this.encodeHTML(this.message(this.label));
}
},
/**
* Processes any widgets defined in the configuration for this instance.
*
* @instance
*/
postCreate: function alfresco_layout_Twister__postCreate() {
if (this.width)
{
domStyle.set(this.domNode, "width", this.width);
}
if (this.label)
{
if (this.additionalCssClasses)
{
domClass.add(this.domNode, this.additionalCssClasses);
}
// If an invalid heading label has been provided then we will be kind and generate a warning
// but remove the invalid setting so that the twister still rendered without a header element
if(this.headingLevel && (isNaN(this.headingLevel) || this.headingLevel < 1 || this.headingLevel > 6))
{
this.alfLog("warn", "A heading must have a numeric level from 1 to 6, level has been reset", this);
this.headingLevel = null;
}
if(this.headingLevel)
{
domConstruct.create("h" + this.headingLevel, {
innerHTML: this.label
}, this.labelNode);
this.createTwister(this.filterPrefsName);
}
else
{
domAttr.set(this.labelNode, "innerHTML", this.label);
this.createTwister();
}
}
else
{
this.alfLog("error", "A twister was configured without a 'label' attribute, so it will not be rendered", this);
domStyle.set(this.domNode, "display", "none");
}
},
/**
* Iterates over the processed widgets and adds each one to the content node.
*
* @instance
* @param {object[]} widgets The widgets that were created.
*/
allWidgetsProcessed: function alfresco_layout_Twister__allWidgetsProcessed(widgets) {
array.forEach(widgets, lang.hitch(this, function(widget) {
widget.placeAt(this.contentNode);
}), this);
if (this.preferencePrefix && this.preferenceName)
{
this.alfPublish(this.getPreferenceTopic, {
preference: this.preferencePrefix + this.preferenceName,
callback: this.onSetTwistState,
callbackScope: this
});
}
},
/**
* Creates a "disclosure twister" UI control from existing mark-up.
*
* @instance
*/
createTwister: function alfresco_layout_Twister__createTwister() {
// Initial State
domClass.add(this.labelNode, this.initiallyOpen ? this.CLASS_OPEN : this.CLASS_CLOSED);
domStyle.set(this.contentNode, "display", this.initiallyOpen ? "block" : "none");
if (this.widgets)
{
this.processWidgets(this.widgets);
}
},
/**
*
* @instance
* @param {boolean} value Whether or not the twister should be opened or closed.
*/
onSetTwistState: function alfresco_layout_Twister_onSetTwistState(open) {
if (open === true)
{
domClass.add(this.labelNode, this.CLASS_OPEN);
domClass.remove(this.labelNode, this.CLASS_CLOSED);
domStyle.set(this.contentNode, "display", "block");
}
else if (open === false)
{
domClass.remove(this.labelNode, this.CLASS_OPEN);
domClass.add(this.labelNode, this.CLASS_CLOSED);
domStyle.set(this.contentNode, "display", "none");
}
else
{
// Don't do anything on unexpected values...
}
},
/**
* Handles requests to twist the twister open or closed.
*
* @instance
* @param {object} evt The click or keyboard event triggering the twist
*/
onTwist: function alfresco_layout_Twister__onTwist(evt) {
// Update UI to new state
var open = domClass.contains(this.labelNode, this.CLASS_CLOSED);
this.onSetTwistState(open);
if (this.preferencePrefix && this.preferenceName)
{
this.alfPublish(this.setPreferenceTopic, {
preference: this.preferencePrefix + this.preferenceName,
value: open
});
}
// Stop the event propogating any further (ie into the parent element)
event.stop(evt);
}
});
});