/*
* Copyright 2013 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 helper provides methods to wrap selected text in inline editor by a defined tag
* See #actionDirect and #action methods
*/
Ext.define('Ametys.cms.editor.InsertElementHelper', {
singleton: true,
/**
* @property {Ametys.window.DialogBox} _box The insert element popup window containing a form.
* @private
*/
/**
* Listener for accessibility basic editor buttons
* @param {Ametys.cms.editor.EditorButtonController} controller The controller
* @param {Ametys.cms.form.widget.RichText} field The current field. Can be null
* @param {HTMLElement} node The editor current node
* @param {String} tagName The tagname to check
* @param {String} attributeName The name of an attribute to check. Can be null.
* @param {String} description The button description to state on enabled state
* @param {String} disabledDescription The button description to state on disabled state
*/
listener: function(controller, field, node, tagName, attributeName, description, disabledDescription)
{
var off = !node;
var state = !off;
var disabled = off;
if (!off)
{
var nodesel = field.getEditor().selection.getNode();
var elt = field.getEditor().dom.getParent(nodesel, tagName);
var sel = field.getEditor().selection.getContent();
disabled = (/<p[> \/]|<td[> \/]|<th[> \/]|<li[> \/]|<div[> \/]/.test(sel)) || (sel == '' && !elt) || nodesel != null && field.getEditor().dom.hasClass(nodesel, 'mceNonEditable');
state = elt != null && elt.nodeName.toLowerCase() == tagName && (!attributeName || elt.getAttribute(attributeName) != null);
}
controller.toggle(state);
controller.setDisabled(off || disabled);
controller.setAdditionalDescription((off || disabled) ? disabledDescription : '');
},
/**
* Will set/remove a tag in the current richtext
*
* Ametys.cms.editor.InsertElementHelper({ tagName: 'b', attributes: {
* 'class': 'mycss',
* 'title': 'my tool tip',
* 'marker': 'marker'
* }
* });
*
* Remember that for a tag to be handled with its attributes, you have to register it with the RichTextConfigurationExtensionPoint.
* To have the css ok, include it with the RichTextConfigurationExtensionPoint.
* Also consider onSetContent and onGetContent of the RichTextConfigurationExtensionPoint to transform a html technical tag (such as span) to a semantic tag when richtext is saved
*
* @param {Object} [config] The object to insert
* @param {String} [config.tagName=span] The name of the tag to create or update
* @param {String} [config.field] The field into which the tag is to be inserted
* @param {Object} [config.attributes] Attributes to add. The key is the name of the attribute, and the value if the value of the attributes
*/
actionDirect: function(config)
{
config = config || {};
config.tagName = config.tagName || "span";
config.attributes = config.attributes || {};
var field = config.field;
var elt = field.getEditor().selection.getNode();
elt = field.getEditor().dom.getParent(elt, function(n) {
if (n.nodeName.toLowerCase() === config.tagName)
{
for(attributeName in config.attributes)
{
var attributeValue = config.attributes[attributeName];
if (n.getAttribute(attributeName) !== attributeValue )
{
return false; // TagName: OK, attributeValues : KO
}
}
return true; // TagName: OK, attributeValues : OK
}
else
{
return false; // TagName: KO
}
});
if (!elt)
{
// Insert element
field.getEditor().execCommand('mceBeginUndoLevel');
var text = field.getEditor().selection.getContent();
var attributePart = '';
for (var attName in config.attributes)
{
attributePart += ' ' + attName + '="' + config.attributes[attName] + '"';
}
field.getEditor().execCommand('mceInsertContent', false, '<' + config.tagName + attributePart + '>' + text + '</' + config.tagName + '>');
field.getEditor().execCommand('mceEndUndoLevel');
}
else
{
// Remove element
field.getEditor().execCommand('mceBeginUndoLevel');
tinyMCE.execCommand('mceRemoveNode', false, elt);
field.getEditor().execCommand('mceEndUndoLevel');
}
},
/**
* Action listener for accessibility basics extension.
* @param {Ametys.cms.form.widget.RichText} field The current field
* @param {String} icon The path to the icon of the dialog box
* @param {String} title The dialog box title
* @param {String} fieldLabel The label of the field
* @param {String} defaultValue The default value when creating
* @param {String} description The dialog box introduction text
* @param {String} postDescription The dialog box end description
* @param {String} tagName The name of the tag to create or update
* @param {String} attributeName The name of the attribute on the tag that will be filled with the user input
* @param {String} [className] A class name to add on the tag
*/
action: function(field, icon, title, fieldLabel, defaultValue, description, postDescription, tagName, attributeName, className)
{
var node = field.getEditor().dom.getParent(field.getEditor().selection.getNode(), tagName);
var elt = null;
if (node && node.nodeName.toLowerCase() == tagName)
{
elt = node;
}
var params = {
field: field,
elt: elt,
icon: icon,
title: title,
description: description,
postDescription: postDescription,
defaultValue: defaultValue,
tagName: tagName,
fieldLabel: fieldLabel,
attributeName: attributeName,
className: className
};
this._act(params);
},
/**
* @private
* Open a dialog box with an input. The current selection will be surrounded with a html element with a particular title value
* @param {Object} params Configuration parameters. See {@link #method-action}
*/
_act: function(params)
{
this._box = Ext.create('Ametys.window.DialogBox', {
title: params.title,
icon: params.icon,
width: 420,
defaultType: 'component',
defaults: {
cls: 'ametys',
labelAlign: 'right',
labelSeparator: '',
labelWidth: 100
},
items: [{
xtype: 'component',
cls: 'a-text',
html: params.description
}, {
xtype: 'textfield',
fieldLabel: params.fieldLabel,
name: 'accessibility-title',
id: 'accessibility-title',
inputWidth: 240,
allowBlank: true
}, {
xtype: 'component',
cls: 'a-text',
html: params.postDescription
}],
selectDefaultFocus: true,
defaultFocus: 'accessibility-title',
closeAction: 'destroy',
referenceHolder: true,
defaultButton: 'validate',
buttons : [{
reference: 'validate',
text: "{{i18n CONTENT_EDITION_ACCESSIBILITY_INSERT_ELEMENT_DIALOG_OK}}",
handler: Ext.bind(this._ok, this, [params])
},{
text: "{{i18n CONTENT_EDITION_ACCESSIBILITY_INSERT_ELEMENT_DIALOG_CANCEL}}",
handler: Ext.bind(this._cancel, this)
}]
});
var field = this._box.down('#accessibility-title');
field.setValue(params.elt ? params.elt.getAttribute(params.attributeName) : params.defaultValue);
this._box.show();
},
/**
* This function is called after pressing 'Ok' the button of dialog box #_box
* @param {Object} params Configuration parameters. See {@link #method-action}
*/
_ok: function(params)
{
//tinyMCE.focus();
var editor = params.field.getEditor();
var value = this._box.down('#accessibility-title').getValue();
if (value != '' && !params.elt)
{
// Insert element
// FIXME CMS-1026 replace special characters
editor.execCommand('mceBeginUndoLevel');
var text = editor.selection.getContent();
var classAttribute = '';
if (params.className)
{
classAttribute += ' class="' + params.className + '"';
}
editor.execCommand('mceInsertContent', false, '<' +params.tagName + ' ' +params.attributeName + '="' + value + '"' + classAttribute + '>' + text + '</' +params.tagName + '>');
editor.execCommand('mceEndUndoLevel');
}
else if (value != '' && params.elt)
{
// Update element
editor.execCommand('mceBeginUndoLevel');
editor.dom.setAttrib(params.elt, params.attributeName, value);
editor.execCommand('mceEndUndoLevel');
}
else if (params.elt != null)
{
// Remove element
editor.execCommand('mceBeginUndoLevel');
tinyMCE.execCommand('mceRemoveNode', false, params.elt);
editor.execCommand('mceEndUndoLevel');
}
this._box.close();
this._box = null;
},
/**
* This function is called after pressing 'Cancel' the button of dialog box #_box
* Closes the dialog box
*/
_cancel: function()
{
this._box.close();
this._box = null;
}
});