/*
* 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.
*/
/**
* Undo editor action.
* @private
*/
Ext.define('Ametys.plugins.cms.editor.basicactions.Undo', {
singleton: true,
/**
* Execute the 'undo' action
* @param {Ametys.cms.editor.EditorButtonController} controller The controller calling the function
*/
apply: function(controller)
{
var field = controller.getCurrentField();
field.getEditor().execCommand('Undo');
field.getEditor().focus();
},
/**
* Enable/disable and toggle/untoggle controller according the current selection
* @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 current selected node. Can be null.
*/
selectionListener: function (controller, field, node)
{
if (!node || !field || !field.getEditor().undoManager.hasUndo())
{
controller.setDisabled(true);
}
}
});
/**
* Redo editor action.
* @private
*/
Ext.define('Ametys.plugins.cms.editor.basicactions.Redo', {
singleton: true,
/**
* Execute the 'redo' action
* @param {Ametys.cms.editor.EditorButtonController} controller The controller calling the function
*/
apply: function(controller)
{
var field = controller.getCurrentField();
field.getEditor().execCommand('Redo');
field.getEditor().focus();
},
/**
* Enable/disable and toggle/untoggle controller according the current selection
* @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 current selected node. Can be null.
*/
selectionListener: function (controller, field, node)
{
if (!node || !field || !field.getEditor().undoManager.hasRedo())
{
controller.setDisabled(true);
}
}
});
/**
* Cut editor action.
* @private
*/
Ext.define('Ametys.plugins.cms.editor.basicactions.Cut', {
singleton: true,
/**
* Execute the 'cut' action
* @param {Ametys.cms.editor.EditorButtonController} controller The controller calling the function
*/
apply: function(controller)
{
var editor = controller.getCurrentField().getEditor();
editor.focus();
try
{
if (Ext.isGecko)
{
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
editor.getDoc().execCommand("cut", false, null);
}
else
{
editor.getDoc().execCommand("cut", false, null);
}
}
catch (e)
{
Ametys.log.ErrorDialog.display({
title: "{{i18n CONTENT_EDITION_BASICACTION_ERROR_SECURITY_TEXT}}",
text: "{{i18n CONTENT_EDITION_BASICACTION_ERROR_SECURITY_DESC_CTRLX}}",
details: "{{i18n CONTENT_EDITION_BASICACTION_ERROR_SECURITY_DESC_EXPL}}" + e,
category: Ext.getClassName(this)
});
}
},
/**
* Enable/disable and toggle/untoggle controller according the current selection
* @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 current selected node. Can be null.
*/
selectionListener: function (controller, field, node)
{
if (!node || !field || field.getEditor().selection.isCollapsed())
{
controller.setDisabled(true);
}
}
});
/**
* Copy editor action.
* @private
*/
Ext.define('Ametys.plugins.cms.editor.basicactions.Copy', {
singleton: true,
/**
* Execute the 'copy' action
* @param {Ametys.cms.editor.EditorButtonController} controller The controller calling the function
*/
apply: function(controller)
{
var editor = controller.getCurrentField().getEditor();
editor.focus();
try
{
if (Ext.isGecko)
{
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
editor.getDoc().execCommand("copy", false, null);
}
else
{
editor.getDoc().execCommand("copy", false, null);
}
}
catch (e)
{
Ametys.log.ErrorDialog.display({
title: "{{i18n CONTENT_EDITION_BASICACTION_ERROR_SECURITY_TEXT}}",
text: "{{i18n CONTENT_EDITION_BASICACTION_ERROR_SECURITY_DESC_CTRLC}}",
details: "{{i18n CONTENT_EDITION_BASICACTION_ERROR_SECURITY_DESC_EXPL}}" + e,
category: Ext.getClassName(this)
});
}
},
/**
* Enable/disable and toggle/untoggle controller according the current selection
* @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 current selected node. Can be null.
*/
selectionListener: function (controller, field, node)
{
if (!node || !field || field.getEditor().selection.isCollapsed())
{
controller.setDisabled(true);
}
}
});
/**
* Paste editor action.
* @private
*/
Ext.define('Ametys.plugins.cms.editor.basicactions.Paste', {
singleton: true,
/**
* Execute the 'paste' action
* @param {Ametys.cms.editor.EditorButtonController} controller The controller calling the function
*/
apply: function(controller)
{
var editor = controller.getCurrentField().getEditor();
editor.focus();
try
{
if (Ext.isGecko)
{
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
editor.getDoc().execCommand("paste", false, null);
}
else
{
if (!editor.getDoc().execCommand("paste", false, null) && !Ext.isIE)
{
throw "Paste failed";
}
}
}
catch (e)
{
Ametys.log.ErrorDialog.display({
title: "{{i18n CONTENT_EDITION_BASICACTION_ERROR_SECURITY_TEXT}}",
text: "{{i18n CONTENT_EDITION_BASICACTION_ERROR_SECURITY_DESC_CTRLV}}",
details: "{{i18n CONTENT_EDITION_BASICACTION_ERROR_SECURITY_DESC_EXPL}}" + e,
category: Ext.getClassName(this)
});
}
}
});
/**
* Text Paste editor action (without tags).
* @private
*/
Ext.define('Ametys.plugins.cms.editor.basicactions.TxtPaste', {
singleton: true,
/**
* @private
* @property {Object} _hiddenTextAreaStyle The css style to hide the textare which will received the txt
*/
_hiddenTextAreaStyle : {
position : 'absolute',
left : -10000,
top : 0,
width : 1,
height : 1,
overflow : 'hidden',
display: 'none'
},
/**
* Execute the 'paste as text' action
* @param {Ametys.cms.editor.EditorButtonController} controller The controller calling the function
*/
apply: function(controller)
{
var pastetext = '';
var field = controller.getCurrentField();
if (Ext.isGecko)
{
try
{
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
}
catch (e)
{
this._manualTxtPaste(field);
return;
}
var clip = Components.classes["@mozilla.org/widget/clipboard;1"].getService(Components.interfaces.nsIClipboard);
if (!clip) return false;
var trans = Components.classes["@mozilla.org/widget/transferable;1"].createInstance(Components.interfaces.nsITransferable);
if (!trans) return false;
trans.addDataFlavor("text/unicode");
clip.getData(trans, clip.kGlobalClipboard);
var str = {};
var strLength = {};
trans.getTransferData("text/unicode", str, strLength);
if (str) str = str.value.QueryInterface(Components.interfaces.nsISupportsString);
if (str) pastetext = str.data.substring(0, strLength.value / 2);
}
else if (Ext.isIE)
{
pastetext = window.clipboardData.getData("Text");
}
else
{
// paste in a textarea input (this is the secret to remove tags properly)
// create or get a textarea input
var textarea = Ext.getDom('txtpaste');
if (!textarea)
{
Ext.getBody().createChild({tag: 'textarea', id:'txtpaste', style: this._hiddenTextAreaStyle});
textarea = Ext.getDom('txtpaste');
}
// initialize the textarea input
textarea.value = '';
textarea.style.display = '';
textarea.focus();
textarea.select();
try
{
textarea.createTextRange().execCommand('paste', false, null);
}
catch (e)
{
this._manualTxtPaste(field);
return;
}
finally
{
// recycle the textarea
textarea.style.display = 'none';
}
pastetext = textarea.value;
}
this._finishTxtPaste(field, pastetext);
},
/**
* @private
* This method is called after paste is done (normal or text paste)
* @param {Ametys.cms.form.widget.RichText} field The field to paste into
* @param {String} txt The text pasted
*/
_finishTxtPaste: function (field, txt)
{
// paste in the html editor
field.getEditor().execCommand('mceInsertContent', false, "<p>" + txt.replace(/\n/g, "</p><p>") + "</p>");
field.getEditor().focus();
},
/**
* When an automatic paste failed due to security options, this method does show a dialog box to allow a manual paste
* @param {Ametys.cms.form.widget.RichText} field The field to paste into
* @private
*/
_manualTxtPaste: function(field)
{
if (!this._box)
{
this._box = Ext.create('Ametys.window.DialogBox', {
title: "{{i18n CONTENT_EDITION_BASICACTION_PASTE_WITHOUT_STYLE}}",
icon: Ametys.getPluginResourcesPrefix('cms') + "/img/content/edition/clipboard/paste_txt_16.png",
layout: 'form',
width: 300,
height: 180,
scrollable: true,
defaults: {
cls: 'ametys',
labelAlign: '130',
labelSeparator: '',
labelWidth: 130
},
items: [{
xtype: 'textarea',
id: 'manualtxtpaste',
listeners: {
'blur': function(textarea) { textarea.focus(true, 100); }
},
style: this._hiddenTextAreaStyle
}, {
xtype: 'component',
cls: 'hint',
html: "{{i18n CONTENT_EDITION_BASICACTION_INFO_COPY}}"
}],
defaultFocus: 'manualtxtpaste',
closeAction: 'hide',
buttons: [{
text: "{{i18n CONTENT_EDITION_BASICACTION_CANCEL}}",
handler: Ext.bind(function() {this._box.hide();}, this)
}]
});
}
this._box.show();
var textarea = this._box.down('textarea');
textarea.reset();
textarea.getEl().dom.style.display = '';
textarea.getEl().dom.style.top = '0px';
textarea.getEl().dom.style.left = '-10000px';
textarea.getEl().dom.style.position = 'absolute';
textarea.focus(true);
this._checkTxtArea(field);
},
/**
* @private
* This method is watching the textera to detect the paste event
* @param {Ametys.cms.form.widget.RichText} field The field to paste into
*/
_checkTxtArea: function(field)
{
if (this._box.isHidden())
{
return;
}
var value = this._box.down('textarea').getValue();
if (value)
{
this._box.hide();
this._finishTxtPaste(field, value);
}
else
{
Ext.defer(this._checkTxtArea, 10, this, [field]);
}
}
});
/**
* Singleton class.
* Apply a style to a node.
* @private
*/
Ext.define('Ametys.plugins.cms.editor.basicactions.ApplyStyle', {
singleton: true,
/**
* Apply style
* @param {Ametys.cms.editor.EditorButtonController} controller The controller calling the function
* @param {Boolean} state the press-state of the controller
*/
apply: function (controller, state)
{
if (state)
{
var editor = controller.getCurrentField().getEditor();
var node = editor.dom.getParent(editor.selection.getNode(), controller.getInitialConfig('tagname'));
if (node != null)
{
var classNames = controller.getInitialConfig('css-class');
this.applyStyle(node, editor, classNames);
}
}
},
/**
* Apply a style directly
* @param {HTMLElement} node The current selected node.
* @param {Object} editor tinyMCE editor
* @param {String} classNames class to add
*/
applyStyle: function(node, editor, classNames)
{
editor.focus();
if (node != null)
{
var element = Ext.get(node);
var tag = Ametys.form.widget.RichText.RichTextConfiguration.getTag(element.dom.tagName.toLowerCase(), editor.getParam('category'))
if (tag && tag.getAttribute("class"))
{
var protectedClasses = tag.getAttribute("class").technicalValues;
if (protectedClasses != null)
{
for (var i = 0; i < protectedClasses.length; i++)
{
if (editor.dom.hasClass(node, protectedClasses[i]))
{
classNames += " " + protectedClasses[i];
}
}
}
}
node.setAttribute("class", classNames);
editor.execCommand('mceAddUndoLevel');
}
},
/**
* Enable/disable and toggle/untoggle controller according the current selection
* @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 current selected node. Can be null.
*/
styleControllerListener: function (controller, field, node)
{
var node = (field != null && node != null && field.getEditor() != null && field.getEditor().dom != null) ? field.getEditor().dom.getParent(node, controller.getInitialConfig('tagname')) : null;
var editor = (field != null && node != null && field.getEditor() != null && field.getEditor().dom != null) ? field.getEditor() : null;
var toggleFunction = Ext.bind(controller.toggle, controller);
var enableFunction = function(enable) {if (enable) {controller.enable()} else {controller.disable()}};
var style = controller.getInitialConfig('css-class');
this.styleListener(node, editor, toggleFunction, enableFunction, style);
},
/**
* Enable/disable and toggle/untoggle controller according the current selection
* @param {HTMLElement} node The current selected node.
* @param {Object} editor tinyMCE editor
* @param {Function} toggleFunction function that will be called with true/false to select/unselect button
* @param {Function} enableFunction function that will be called with true/false to enable/disable button
* @param {String} style button style
*/
styleListener: function(node, editor, toggleFunction, enableFunction, style)
{
if (node != null)
{
toggleFunction(editor.dom.hasClass(node, style));
enableFunction(true);
}
else
{
toggleFunction(false);
enableFunction(false);
}
}
});