/*
* Copyright 2015 Anyware Services
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* This class is the abstract mother class for forms layouts.
*/
Ext.define('Ametys.plugins.forms.content.Layout', {
singleton: true,
/**
* @private
* @property {Object} _layoutActions the actions on the layout
*/
/**
* @private
* @property {String[]} _handledLayoutClass the CSS class handled for the layout
*/
constructor: function(config)
{
this._layoutActions = {};
this._handledLayoutClass = [];
this.callParent(arguments);
},
/**
* Register the layout of the button
* @param {String} css the css value for layout
*/
registerLayoutCSS: function(css)
{
this._handledLayoutClass.push(css);
},
/**
* Register the layout of the button
* @param {Ametys.ribbon.element.ui.ButtonController} controller the controller having layout informations ('value' and 'create-action' keys)
*/
registerLayoutAction: function(controller)
{
var value = controller.getInitialConfig('value');
var createAction = controller.getInitialConfig('create-action');
this._layoutActions[value] = createAction;
},
/**
* Enable/disable the layout menu according to the selection
* @param {Ametys.cms.editor.EditorButtonController} controller The button's controller
* @param {Ametys.cms.form.widget.RichText} field The current field. Can be null
* @param {HTMLElement} node The currently selected node. Can be null.
*/
layoutMenuListener: function(controller, field, node)
{
Ametys.plugins.forms.content.Forms._currentNode = (field != null && node != null && field.getEditor() != null) ? Ametys.plugins.forms.content.Forms._getForm(field.getEditor(), field.getEditor().selection.getNode()) : null;
controller.setDisabled(Ametys.plugins.forms.content.Forms._currentNode == null);
},
/**
* Enable/disable the layout buttons according to the selection
* @param {Ametys.cms.editor.EditorButtonController} controller The button's controller
* @param {Ametys.cms.form.widget.RichText} field The current field. Can be null
* @param {HTMLElement} node The currently selected node. Can be null.
*/
layoutListener: function(controller, field, node)
{
var toggled = false;
if (node != null && field != null)
{
var specificNode = Ametys.plugins.forms.content.Forms._getForm(controller.getCurrentField().getEditor(), node);
if (specificNode != null)
{
var layout = specificNode.getAttribute('layout');
if (layout == null)
{
toggled = controller.getInitialConfig('default');
}
else
{
toggled = layout == controller.getInitialConfig('value');
}
}
}
controller.toggle(toggled);
},
/**
* Get the CSS class of handled layout
* @return {String[]} The handled CSS class in a Array
*/
getHandledLayoutClass: function ()
{
return this._handledLayoutClass;
},
/**
* Get the current layout
* @param {HTMLElement} node the node to get the layout from
*/
_getCurrentLayout: function(node)
{
// FIXME replace tinymce.activeEditor
var form = Ametys.plugins.forms.content.Forms._getForm(tinymce.activeEditor, node || tinyMCE.activeEditor.selection.getNode());
var layout = form.getAttribute("layout");
return layout || Ametys.form.widget.RichText.RichTextConfiguration.getTag("div", tinyMCE.activeEditor.getParam('category')).getAttribute("layout").defaultValue;
},
/**
* Clean the current layout
*/
_cleanLayout: function()
{
var me = this;
if (Ametys.plugins.forms.content.Forms._currentNode == null)
{
return;
}
function formClean(parentNode, before, node)
{
var nodeIsOk = me._isNodeOk(node);
if (node.childNodes.length > 0)
{
var nextNode = node.childNodes[0];
while (nextNode != null)
{
formClean(nodeIsOk ? node : parentNode, nodeIsOk ? nextNode : before, nextNode);
nextNode = nextNode.nextSibling;
}
}
if (nodeIsOk && node.parentNode != parentNode && parentNode != null)
{
parentNode.insertBefore(node, before);
}
}
function formDestroy(node)
{
if (node.tagName == null)
{
return null;
}
if (!me._isNodeOk(node))
{
node.parentNode.removeChild(node);
return true;
}
else
{
for (var k = 0; k < node.childNodes.length; k++)
{
if (formDestroy(node.childNodes[k]))
{
k--;
}
}
return false;
}
}
var formNode = Ametys.plugins.forms.content.Forms._currentNode;
formClean(null, null, formNode);
formDestroy(formNode);
},
/**
* Is the node correct ?
* @param {HTMLElement} node the node to check
* @return true if the node is correct, false otherwise
*/
_isNodeOk: function(node)
{
return (/^div$/i.test(node.tagName)
&& node.getAttribute("form") == "form")
|| /^label$/i.test(node.tagName)
|| /^legend$/i.test(node.tagName)
|| this._isNodePartiallyOk(node);
},
/**
* Is the node partially correct ?
* @param {HTMLElement} node the node to check
* @return true if the node is partially correct, false otherwise
*/
_isNodePartiallyOk: function(node)
{
return /^fieldset$/i.test(node.tagName)
|| (/^img$/i.test(node.tagName)
&& node.getAttribute("marker") == "marker"
&& node.getAttribute("form") == "form");
},
/**
* Set the given layout on the selected node
* @param {String} layoutValue the value of the layout to set
* @param {String} cssClass the CSS class of the layout
* @return true if the layout was changed, false otherwise
*/
_setLayoutOnNode: function(layoutValue, cssClass)
{
if (Ametys.plugins.forms.content.Forms._currentNode != null)
{
if (Ametys.plugins.forms.content.Forms._currentNode.getAttribute("layout") != layoutValue)
{
Ametys.plugins.forms.content.Forms._currentNode.setAttribute("layout", layoutValue);
for (var i=0; i < this._handledLayoutClass.length; i++)
{
// remove old css class
tinyMCE.activeEditor.dom.removeClass(Ametys.plugins.forms.content.Forms._currentNode, this._handledLayoutClass[i]);
}
// set new css class
tinyMCE.activeEditor.dom.addClass(Ametys.plugins.forms.content.Forms._currentNode, cssClass);
return true;
}
}
return false;
},
/**
* Go to the first leaf child node
* @param {HTMLElement} element the root element
* @return the first leaf child node or the given node
*/
_goToFirst: function(element)
{
if (element == null)
{
return null;
}
else if (element.childNodes.length == 0)
{
return element;
}
else
{
return this._goToFirst(element.childNodes[0]);
}
},
/**
* Go to the last leaf child node
* @param {HTMLElement} element the root element
* @return the last leaf child node or the given node
*/
_goToLast: function(element)
{
if (element == null)
{
return null;
}
else if (element.childNodes.length == 0)
{
return element;
}
else
{
return this._goToLast(element.childNodes[element.childNodes.length - 1]);
}
},
/**
* Retrieve the previous node
* @param {HTMLElement} element the root element
* @return the previous node or the given node
*/
_findPrevious: function(element)
{
var elt;
if (element == null || (/^div$/i.test(element.tagName) && element.getAttribute("form") == "form"))
{
elt = null;
}
else if (element.previousSibling != null)
{
elt = this._goToLast(element.previousSibling);
}
else
{
elt = element.parentNode;
}
if (elt == null || this._isNodePartiallyOk(elt))
{
return elt;
}
else
{
return this._findPrevious(elt);
}
},
/**
* Retrieve the next node
* @param {HTMLElement} element the root element
* @return the next node or the given node
*/
_findNext: function(element)
{
// FIXME replace tinymce.activeEditor by controller.getCurrentField().getEditor()
var elt;
if (element == null || Ametys.plugins.forms.content.Forms._getForm(tinymce.activeEditor, element) == null)
{
elt = null;
}
else if (element.nextSibling != null)
{
elt = this._goToFirst(element.nextSibling);
}
else
{
elt = element.parentNode;
}
if (elt == null || this._isNodePartiallyOk(elt))
{
return elt;
}
else
{
return this._findNext(elt);
}
}
});