Index: typo3/sysext/rtehtmlarea/htmlarea/htmlarea.js =================================================================== --- typo3/sysext/rtehtmlarea/htmlarea/htmlarea.js (révision 7101) +++ typo3/sysext/rtehtmlarea/htmlarea/htmlarea.js (copie de travail) @@ -1405,6 +1405,424 @@ } }); Ext.reg('htmlareaiframe', HTMLArea.Iframe); +/*! + * Ext JS Library 3.1.1 + * Copyright(c) 2006-2010 Ext JS, LLC + * licensing@extjs.com + * http://www.extjs.com/license + */ +/** + * @class Ext.ux.StatusBar + *

Basic status bar component that can be used as the bottom toolbar of any {@link Ext.Panel}. In addition to + * supporting the standard {@link Ext.Toolbar} interface for adding buttons, menus and other items, the StatusBar + * provides a greedy status element that can be aligned to either side and has convenient methods for setting the + * status text and icon. You can also indicate that something is processing using the {@link #showBusy} method.

+ *

+new Ext.Panel({
+    title: 'StatusBar',
+    // etc.
+    bbar: new Ext.ux.StatusBar({
+        id: 'my-status',
+
+        // defaults to use when the status is cleared:
+        defaultText: 'Default status text',
+        defaultIconCls: 'default-icon',
+
+        // values to set initially:
+        text: 'Ready',
+        iconCls: 'ready-icon',
+
+        // any standard Toolbar items:
+        items: [{
+            text: 'A Button'
+        }, '-', 'Plain Text']
+    })
+});
+
+// Update the status bar later in code:
+var sb = Ext.getCmp('my-status');
+sb.setStatus({
+    text: 'OK',
+    iconCls: 'ok-icon',
+    clear: true // auto-clear after a set interval
+});
+
+// Set the status bar to show that something is processing:
+sb.showBusy();
+
+// processing....
+
+sb.clearStatus(); // once completeed
+
+ * @extends Ext.Toolbar + * @constructor + * Creates a new StatusBar + * @param {Object/Array} config A config object + */ +Ext.ux.StatusBar = Ext.extend(Ext.Toolbar, { + /** + * @cfg {String} statusAlign + * The alignment of the status element within the overall StatusBar layout. When the StatusBar is rendered, + * it creates an internal div containing the status text and icon. Any additional Toolbar items added in the + * StatusBar's {@link #items} config, or added via {@link #add} or any of the supported add* methods, will be + * rendered, in added order, to the opposite side. The status element is greedy, so it will automatically + * expand to take up all sapce left over by any other items. Example usage: + *

+// Create a left-aligned status bar containing a button,
+// separator and text item that will be right-aligned (default):
+new Ext.Panel({
+    title: 'StatusBar',
+    // etc.
+    bbar: new Ext.ux.StatusBar({
+        defaultText: 'Default status text',
+        id: 'status-id',
+        items: [{
+            text: 'A Button'
+        }, '-', 'Plain Text']
+    })
+});
+
+// By adding the statusAlign config, this will create the
+// exact same toolbar, except the status and toolbar item
+// layout will be reversed from the previous example:
+new Ext.Panel({
+    title: 'StatusBar',
+    // etc.
+    bbar: new Ext.ux.StatusBar({
+        defaultText: 'Default status text',
+        id: 'status-id',
+        statusAlign: 'right',
+        items: [{
+            text: 'A Button'
+        }, '-', 'Plain Text']
+    })
+});
+
+ */ + /** + * @cfg {String} defaultText + * The default {@link #text} value. This will be used anytime the status bar is cleared with the + * useDefaults:true option (defaults to ''). + */ + /** + * @cfg {String} defaultIconCls + * The default {@link #iconCls} value (see the iconCls docs for additional details about customizing the icon). + * This will be used anytime the status bar is cleared with the useDefaults:true option (defaults to ''). + */ + /** + * @cfg {String} text + * A string that will be initially set as the status message. This string + * will be set as innerHTML (html tags are accepted) for the toolbar item. + * If not specified, the value set for {@link #defaultText} + * will be used. + */ + /** + * @cfg {String} iconCls + * A CSS class that will be initially set as the status bar icon and is + * expected to provide a background image (defaults to ''). + * Example usage:

+// Example CSS rule:
+.x-statusbar .x-status-custom {
+    padding-left: 25px;
+    background: transparent url(images/custom-icon.gif) no-repeat 3px 2px;
+}
+
+// Setting a default icon:
+var sb = new Ext.ux.StatusBar({
+    defaultIconCls: 'x-status-custom'
+});
+
+// Changing the icon:
+sb.setStatus({
+    text: 'New status',
+    iconCls: 'x-status-custom'
+});
+
+ */ + + /** + * @cfg {String} cls + * The base class applied to the containing element for this component on render (defaults to 'x-statusbar') + */ + cls : 'x-statusbar', + /** + * @cfg {String} busyIconCls + * The default {@link #iconCls} applied when calling + * {@link #showBusy} (defaults to 'x-status-busy'). + * It can be overridden at any time by passing the iconCls + * argument into {@link #showBusy}. + */ + busyIconCls : 'x-status-busy', + /** + * @cfg {String} busyText + * The default {@link #text} applied when calling + * {@link #showBusy} (defaults to 'Loading...'). + * It can be overridden at any time by passing the text + * argument into {@link #showBusy}. + */ + busyText : 'Loading...', + /** + * @cfg {Number} autoClear + * The number of milliseconds to wait after setting the status via + * {@link #setStatus} before automatically clearing the status + * text and icon (defaults to 5000). Note that this only applies + * when passing the clear argument to {@link #setStatus} + * since that is the only way to defer clearing the status. This can + * be overridden by specifying a different wait value in + * {@link #setStatus}. Calls to {@link #clearStatus} + * always clear the status bar immediately and ignore this value. + */ + autoClear : 5000, + + /** + * @cfg {String} emptyText + * The text string to use if no text has been set. Defaults to + * ' '). If there are no other items in the toolbar using + * an empty string ('') for this value would end up in the toolbar + * height collapsing since the empty string will not maintain the toolbar + * height. Use '' if the toolbar should collapse in height + * vertically when no text is specified and there are no other items in + * the toolbar. + */ + emptyText : ' ', + + // private + activeThreadId : 0, + + // private + initComponent : function(){ + if(this.statusAlign=='right'){ + this.cls += ' x-status-right'; + } + Ext.ux.StatusBar.superclass.initComponent.call(this); + }, + + // private + afterRender : function(){ + Ext.ux.StatusBar.superclass.afterRender.call(this); + + var right = this.statusAlign == 'right'; + this.currIconCls = this.iconCls || this.defaultIconCls; + this.statusEl = new Ext.Toolbar.TextItem({ + cls: 'x-status-text ' + (this.currIconCls || ''), + text: this.text || this.defaultText || '' + }); + + if(right){ + this.add('->'); + this.add(this.statusEl); + }else{ + this.insert(0, this.statusEl); + this.insert(1, '->'); + } + this.doLayout(); + }, + + /** + * Sets the status {@link #text} and/or {@link #iconCls}. Also supports automatically clearing the + * status that was set after a specified interval. + * @param {Object/String} config A config object specifying what status to set, or a string assumed + * to be the status text (and all other options are defaulted as explained below). A config + * object containing any or all of the following properties can be passed: + * Example usage:

+// Simple call to update the text
+statusBar.setStatus('New status');
+
+// Set the status and icon, auto-clearing with default options:
+statusBar.setStatus({
+    text: 'New status',
+    iconCls: 'x-status-custom',
+    clear: true
+});
+
+// Auto-clear with custom options:
+statusBar.setStatus({
+    text: 'New status',
+    iconCls: 'x-status-custom',
+    clear: {
+        wait: 8000,
+        anim: false,
+        useDefaults: false
+    }
+});
+
+ * @return {Ext.ux.StatusBar} this + */ + setStatus : function(o){ + o = o || {}; + + if(typeof o == 'string'){ + o = {text:o}; + } + if(o.text !== undefined){ + this.setText(o.text); + } + if(o.iconCls !== undefined){ + this.setIcon(o.iconCls); + } + + if(o.clear){ + var c = o.clear, + wait = this.autoClear, + defaults = {useDefaults: true, anim: true}; + + if(typeof c == 'object'){ + c = Ext.applyIf(c, defaults); + if(c.wait){ + wait = c.wait; + } + }else if(typeof c == 'number'){ + wait = c; + c = defaults; + }else if(typeof c == 'boolean'){ + c = defaults; + } + + c.threadId = this.activeThreadId; + this.clearStatus.defer(wait, this, [c]); + } + return this; + }, + + /** + * Clears the status {@link #text} and {@link #iconCls}. Also supports clearing via an optional fade out animation. + * @param {Object} config (optional) A config object containing any or all of the following properties. If this + * object is not specified the status will be cleared using the defaults below: + * @return {Ext.ux.StatusBar} this + */ + clearStatus : function(o){ + o = o || {}; + + if(o.threadId && o.threadId !== this.activeThreadId){ + // this means the current call was made internally, but a newer + // thread has set a message since this call was deferred. Since + // we don't want to overwrite a newer message just ignore. + return this; + } + + var text = o.useDefaults ? this.defaultText : this.emptyText, + iconCls = o.useDefaults ? (this.defaultIconCls ? this.defaultIconCls : '') : ''; + + if(o.anim){ + // animate the statusEl Ext.Element + this.statusEl.el.fadeOut({ + remove: false, + useDisplay: true, + scope: this, + callback: function(){ + this.setStatus({ + text: text, + iconCls: iconCls + }); + + this.statusEl.el.show(); + } + }); + }else{ + // hide/show the el to avoid jumpy text or icon + this.statusEl.hide(); + this.setStatus({ + text: text, + iconCls: iconCls + }); + this.statusEl.show(); + } + return this; + }, + + /** + * Convenience method for setting the status text directly. For more flexible options see {@link #setStatus}. + * @param {String} text (optional) The text to set (defaults to '') + * @return {Ext.ux.StatusBar} this + */ + setText : function(text){ + this.activeThreadId++; + this.text = text || ''; + if(this.rendered){ + this.statusEl.setText(this.text); + } + return this; + }, + + /** + * Returns the current status text. + * @return {String} The status text + */ + getText : function(){ + return this.text; + }, + + /** + * Convenience method for setting the status icon directly. For more flexible options see {@link #setStatus}. + * See {@link #iconCls} for complete details about customizing the icon. + * @param {String} iconCls (optional) The icon class to set (defaults to '', and any current icon class is removed) + * @return {Ext.ux.StatusBar} this + */ + setIcon : function(cls){ + this.activeThreadId++; + cls = cls || ''; + + if(this.rendered){ + if(this.currIconCls){ + this.statusEl.removeClass(this.currIconCls); + this.currIconCls = null; + } + if(cls.length > 0){ + this.statusEl.addClass(cls); + this.currIconCls = cls; + } + }else{ + this.currIconCls = cls; + } + return this; + }, + + /** + * Convenience method for setting the status text and icon to special values that are pre-configured to indicate + * a "busy" state, usually for loading or processing activities. + * @param {Object/String} config (optional) A config object in the same format supported by {@link #setStatus}, or a + * string to use as the status text (in which case all other options for setStatus will be defaulted). Use the + * text and/or iconCls properties on the config to override the default {@link #busyText} + * and {@link #busyIconCls} settings. If the config argument is not specified, {@link #busyText} and + * {@link #busyIconCls} will be used in conjunction with all of the default options for {@link #setStatus}. + * @return {Ext.ux.StatusBar} this + */ + showBusy : function(o){ + if(typeof o == 'string'){ + o = {text:o}; + } + o = Ext.applyIf(o || {}, { + text: this.busyText, + iconCls: this.busyIconCls + }); + return this.setStatus(o); + } +}); +Ext.reg('statusbar', Ext.ux.StatusBar); /* * HTMLArea.StatusBar extends Ext.Container */ @@ -4006,6 +4424,7 @@ * Dialogue window onClose handler */ onClose: function () { + this.editor.focus(); this.restoreSelection(); this.editor.updateToolbar(); }, @@ -4014,6 +4433,7 @@ */ onCancel: function () { this.dialog.close(); + this.editor.focus(); }, /* * Save selection Index: typo3/sysext/rtehtmlarea/htmlarea/locallang_dialogs.xml =================================================================== --- typo3/sysext/rtehtmlarea/htmlarea/locallang_dialogs.xml (révision 7101) +++ typo3/sysext/rtehtmlarea/htmlarea/locallang_dialogs.xml (copie de travail) @@ -112,6 +112,7 @@ + Index: typo3/sysext/rtehtmlarea/htmlarea/plugins/SpellChecker/locallang.xml =================================================================== --- typo3/sysext/rtehtmlarea/htmlarea/plugins/SpellChecker/locallang.xml (révision 7101) +++ typo3/sysext/rtehtmlarea/htmlarea/plugins/SpellChecker/locallang.xml (copie de travail) @@ -23,6 +23,7 @@ + @@ -36,6 +37,7 @@ + Index: typo3/sysext/rtehtmlarea/htmlarea/plugins/SpellChecker/popups/spell-check-ui-iso-8859-1.html =================================================================== --- typo3/sysext/rtehtmlarea/htmlarea/plugins/SpellChecker/popups/spell-check-ui-iso-8859-1.html (révision 7101) +++ typo3/sysext/rtehtmlarea/htmlarea/plugins/SpellChecker/popups/spell-check-ui-iso-8859-1.html (copie de travail) @@ -1,101 +0,0 @@ - - - - - - Spell Checker - - - - -
- -
Spell Checker
-
- - - -
-
Please wait. Calling spell checker.
-
-
Original word
-
pliz weit ;-)
-
- -
-
-
- - - - - - -
-
-
- -
-
- -
-
-
- -
-
 
-
-
- - -
-
- - Index: typo3/sysext/rtehtmlarea/htmlarea/plugins/SpellChecker/popups/spell-check-ui.html =================================================================== --- typo3/sysext/rtehtmlarea/htmlarea/plugins/SpellChecker/popups/spell-check-ui.html (révision 7101) +++ typo3/sysext/rtehtmlarea/htmlarea/plugins/SpellChecker/popups/spell-check-ui.html (copie de travail) @@ -1,101 +0,0 @@ - - - - - - Spell Checker - - - - -
- -
Spell Checker
-
- - - -
-
Please wait. Calling spell checker.
-
-
Original word
-
pliz weit ;-)
-
- -
-
-
- - - - - - -
-
-
- -
-
- -
-
-
- -
-
 
-
-
- - -
-
- - Index: typo3/sysext/rtehtmlarea/htmlarea/plugins/SpellChecker/spell-check-style.css =================================================================== --- typo3/sysext/rtehtmlarea/htmlarea/plugins/SpellChecker/spell-check-style.css (révision 7101) +++ typo3/sysext/rtehtmlarea/htmlarea/plugins/SpellChecker/spell-check-style.css (copie de travail) @@ -1,11 +1,29 @@ -html, body { background-color: white; color: black; } - -.HA-spellcheck-error { border-bottom: 1px dashed #f00; cursor: default; } -.HA-spellcheck-same { background-color: #cef; color: #000; } -.HA-spellcheck-hover { background-color: #433; color: white; } -.HA-spellcheck-fixed { border-bottom: 1px dashed #0b8; } -.HA-spellcheck-current { background-color: #9be; color: #000; } -.HA-spellcheck-suggestions { display: none; } - -#HA-spellcheck-dictionaries { display: none; } -a:link, a:visited { color: #55e; } +body { + background-color: white; + color: black; + padding: 3px; + margin: 0; +} +.htmlarea-spellcheck-error { + border-bottom: 1px dashed #f00; + cursor: default; +} +.htmlarea-spellcheck-same { + background-color: #cef; + color: #000; +} +.htmlarea-spellcheck-hover { + background-color: #433; + color: white; + cursor: pointer; +} +.htmlarea-spellcheck-fixed { + border-bottom: 1px dashed #0b8; +} +.htmlarea-spellcheck-current { + background-color: #9be; + color: #000; +} +a:link, a:visited { + color: #55e; +} Index: typo3/sysext/rtehtmlarea/htmlarea/plugins/SpellChecker/spell-check-ui.js =================================================================== --- typo3/sysext/rtehtmlarea/htmlarea/plugins/SpellChecker/spell-check-ui.js (révision 7101) +++ typo3/sysext/rtehtmlarea/htmlarea/plugins/SpellChecker/spell-check-ui.js (copie de travail) @@ -1,429 +0,0 @@ -/*************************************************************** -* Copyright notice -* -* (c) 2003 dynarch.com. Authored by Mihai Bazon, sponsored by www.americanbible.org. -* (c) 2004-2009 Stanislas Rolland -* All rights reserved -* -* This script is part of the TYPO3 project. The TYPO3 project is -* free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* The GNU General Public License can be found at -* http://www.gnu.org/copyleft/gpl.html. -* A copy is found in the textfile GPL.txt and important notices to the license -* from the author is found in LICENSE.txt distributed with these scripts. -* -* -* This script 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 General Public License for more details. -* -* This script is a modified version of a script published under the htmlArea License. -* A copy of the htmlArea License may be found in the textfile HTMLAREA_LICENSE.txt. -* -* This copyright notice MUST APPEAR in all copies of the script! -***************************************************************/ -/* - * Spell Checker Plugin for TYPO3 htmlArea RTE - * - * TYPO3 SVN ID: $Id$ - */ -var dialog = window.opener.HTMLArea.Dialog.SpellChecker; -var frame = null; -var currentElement = null; -var wrongWords = null; -var modified = false; -var allWords = {}; -var fixedWords = []; -var suggested_words = {}; - -var to_p_dict = []; // List of words to add to personal dictionary -var to_r_list = []; // List of words to add to replacement list - -function makeCleanDoc(leaveFixed) { - // document.getElementById("status").innerHTML = 'Please wait: rendering valid HTML'; - var words = wrongWords.concat(fixedWords); - for (var i = words.length; --i >= 0;) { - var el = words[i]; - if (!(leaveFixed && /HA-spellcheck-fixed/.test(el.className))) { - el.parentNode.insertBefore(el.firstChild, el); - el.parentNode.removeChild(el); - } else - el.className = "HA-spellcheck-fixed"; - } - return window.opener.HTMLArea.getHTML(frame.contentWindow.document.body, false, dialog.plugin.editor); -}; - -function recheckClicked() { - document.getElementById("status").innerHTML = dialog.plugin.localize("Please wait: changing dictionary to") + ': "' + document.getElementById("f_dictionary").value + '".'; - var field = document.getElementById("f_content"); - field.value = makeCleanDoc(true); - field.form.submit(); -}; - -function saveClicked() { - if (modified) { - dialog.plugin.editor.setHTML(makeCleanDoc(false)); - } - if ((to_p_dict.length || to_r_list.length) && dialog.plugin.enablePersonalDicts) { - var data = {}; - for (var i = 0;i < to_p_dict.length;i++) { - data['to_p_dict[' + i + ']'] = to_p_dict[i]; - } - for (var i = 0;i < to_r_list.length;i++) { - data['to_r_list[' + i + '][0]'] = to_r_list[i][0]; - data['to_r_list[' + i + '][1]'] = to_r_list[i][1]; - } - data['cmd'] = 'learn'; - data['enablePersonalDicts'] = dialog.plugin.enablePersonalDicts; - data['userUid'] = dialog.plugin.userUid; - data['dictionary'] = dialog.plugin.contentISOLanguage; - data['pspell_charset'] = dialog.plugin.contentCharset; - data['pspell_mode'] = dialog.plugin.spellCheckerMode; - window.opener.HTMLArea._postback(dialog.plugin.pageTSconfiguration.path, data); - } - window.close(); - return false; -}; - -function cancelClicked() { - var ok = true; - if (modified) { - ok = confirm(dialog.plugin.localize("QUIT_CONFIRMATION")); - } - if (ok) { - dialog.close(); - } - return false; -}; - -function replaceWord(el) { - var replacement = document.getElementById("v_replacement").value; - var this_word_modified = (el.innerHTML != replacement); - if (this_word_modified) - modified = true; - if (el) { - el.className = el.className.replace(/\s*HA-spellcheck-(hover|fixed)\s*/g, " "); - } - el.className += " HA-spellcheck-fixed"; - el.__msh_fixed = true; - if (!this_word_modified) { - return false; - } - to_r_list.push([el.innerHTML, replacement]); - el.innerHTML = replacement; -}; - -function replaceClicked() { - replaceWord(currentElement); - var start = currentElement.__msh_id; - var index = start; - do { - ++index; - if (index == wrongWords.length) index = 0; - } while((index != start) && wrongWords[index].__msh_fixed); - if (index == start) { - index = 0; - alert(dialog.plugin.localize("Finished list of mispelled words")); - } - wrongWords[index].__msh_wordClicked(true); - return false; -}; - -function revertClicked() { - document.getElementById("v_replacement").value = currentElement.__msh_origWord; - replaceWord(currentElement); - currentElement.className = "HA-spellcheck-error HA-spellcheck-current"; - return false; -}; - -function replaceAllClicked() { - var replacement = document.getElementById("v_replacement").value; - var ok = true; - var spans = allWords[currentElement.__msh_origWord]; - if (spans.length == 0) { - alert("An impossible condition just happened. Call FBI. ;-)"); - } else if (spans.length == 1) { - replaceClicked(); - return false; - } - /* - var message = "The word \"" + currentElement.__msh_origWord + "\" occurs " + spans.length + " times.\n"; - if (replacement == currentElement.__msh_origWord) { - ok = confirm(message + "Ignore all occurrences?"); - } else { - ok = confirm(message + "Replace all occurrences with \"" + replacement + "\"?"); - } - */ - if (ok) { - for (var i = 0; i < spans.length; ++i) { - if (spans[i] != currentElement) { - replaceWord(spans[i]); - } - } - // replace current element the last, so that we jump to the next word ;-) - replaceClicked(); - } - return false; -}; - -function ignoreClicked() { - document.getElementById("v_replacement").value = currentElement.__msh_origWord; - replaceClicked(); - return false; -}; - -function ignoreAllClicked() { - document.getElementById("v_replacement").value = currentElement.__msh_origWord; - replaceAllClicked(); - return false; -}; - -function learnClicked() { - to_p_dict.push(currentElement.__msh_origWord); - return ignoreAllClicked(); -}; - -function initDocument() { - dialog.initialize(); - var plugin = dialog.plugin; - var editor = plugin.editor; - - modified = false; - document.title = dialog.plugin.localize("Spell Checker"); - document.getElementById("spellcheck_form").action = plugin.pageTSconfiguration.path; - frame = document.getElementById("i_framecontent"); - var field = document.getElementById("f_content"); - field.value = HTMLArea.getHTML(editor._doc.body, false, editor); - document.getElementById("f_init").value = "0"; - document.getElementById("f_dictionary").value = plugin.defaultDictionary ? plugin.defaultDictionary : plugin.contentISOLanguage; - document.getElementById("f_charset").value = plugin.contentCharset; - document.getElementById("f_pspell_mode").value = plugin.spellCheckerMode; - document.getElementById("f_user_uid").value = plugin.userUid; - document.getElementById("f_personal_dicts").value = plugin.enablePersonalDicts; - document.getElementById("f_show_dictionaries").value = plugin.showDictionaries; - document.getElementById("f_restrict_to_dictionaries").value = plugin.restrictToDictionaries; - field.form.submit(); - // assign some global event handlers - var select = document.getElementById("v_suggestions"); - select.onchange = function() { - document.getElementById("v_replacement").value = this.value; - }; - HTMLArea._addEvent(select, "dblclick", replaceClicked); - - document.getElementById("b_replace").onclick = replaceClicked; - if (plugin.enablePersonalDicts) document.getElementById("b_learn").onclick = learnClicked; - else document.getElementById("b_learn").style.display = 'none'; - document.getElementById("b_replall").onclick = replaceAllClicked; - document.getElementById("b_ignore").onclick = ignoreClicked; - document.getElementById("b_ignall").onclick = ignoreAllClicked; - document.getElementById("b_recheck").onclick = recheckClicked; - document.getElementById("b_revert").onclick = revertClicked; - document.getElementById("b_info").onclick = displayInfo; - - document.getElementById("b_ok").onclick = saveClicked; - document.getElementById("b_cancel").onclick = cancelClicked; - - select = document.getElementById("v_dictionaries"); - select.onchange = function() { - document.getElementById("f_dictionary").value = this.value; - }; -}; - -function getAbsolutePos(el) { - var r = { x: el.offsetLeft, y: el.offsetTop }; - if (el.offsetParent) { - var tmp = getAbsolutePos(el.offsetParent); - r.x += tmp.x; - r.y += tmp.y; - } - return r; -}; - -function wordClicked(scroll) { - var self = this; - if (scroll) (function() { - var pos = getAbsolutePos(self); - var ws = { x: frame.offsetWidth - 4, - y: frame.offsetHeight - 4 }; - var wp = { x: frame.contentWindow.document.body.scrollLeft, - y: frame.contentWindow.document.body.scrollTop }; - pos.x -= Math.round(ws.x/2); - if (pos.x < 0) pos.x = 0; - pos.y -= Math.round(ws.y/2); - if (pos.y < 0) pos.y = 0; - frame.contentWindow.scrollTo(pos.x, pos.y); - })(); - if (currentElement) { - var a = allWords[currentElement.__msh_origWord]; - currentElement.className = currentElement.className.replace(/\s*HA-spellcheck-current\s*/g, " "); - for (var i = 0; i < a.length; ++i) { - var el = a[i]; - if (el != currentElement) { - el.className = el.className.replace(/\s*HA-spellcheck-same\s*/g, " "); - } - } - } - currentElement = this; - this.className += " HA-spellcheck-current"; - var a = allWords[currentElement.__msh_origWord]; - for (var i = 0; i < a.length; ++i) { - var el = a[i]; - if (el != currentElement) { - el.className += " HA-spellcheck-same"; - } - } - // document.getElementById("b_replall").disabled = (a.length <= 1); - // document.getElementById("b_ignall").disabled = (a.length <= 1); - var txt; - var txt2; - if (a.length == 1) { - txt = dialog.plugin.localize("One occurrence"); - txt2 = dialog.plugin.localize("was found."); - } else if (a.length == 2) { - txt = dialog.plugin.localize("Two occurrences"); - txt2 = dialog.plugin.localize("were found."); - } else { - txt = a.length + " " + dialog.plugin.localize("occurrences"); - txt2 = dialog.plugin.localize("were found."); - } - var suggestions = suggested_words[this.__msh_origWord]; - if (suggestions) suggestions = suggestions.split(/,/); - else suggestions = []; - var select = document.getElementById("v_suggestions"); - document.getElementById("statusbar").innerHTML = txt + " " + dialog.plugin.localize("of the word") + - ' "' + currentElement.__msh_origWord + '"' + " " + txt2; - for (var i = select.length; --i >= 0;) { - select.remove(i); - } - for (var i = 0; i < suggestions.length; ++i) { - var txt = suggestions[i]; - var option = document.createElement("option"); - option.value = txt; - option.appendChild(document.createTextNode(txt)); - select.appendChild(option); - } - document.getElementById("v_currentWord").innerHTML = this.__msh_origWord; - if (suggestions.length > 0) { - select.selectedIndex = 0; - select.onchange(); - } else { - document.getElementById("v_replacement").value = this.innerHTML; - } - select.style.display = "none"; - select.style.display = "inline"; - return false; -}; - -function wordMouseOver() { - this.className += " HA-spellcheck-hover"; -}; - -function wordMouseOut() { - this.className = this.className.replace(/\s*HA-spellcheck-hover\s*/g, " "); -}; - -function displayInfo() { - var info = frame.contentWindow.spellcheck_info; - if (!info) - alert(dialog.plugin.localize("No information available")); - else { - var txt = dialog.plugin.localize("Document information") + "\n" ; - for (var i in info) { - txt += "\n" + dialog.plugin.localize(i) + " : " + info[i]; - } - txt += " " + dialog.plugin.localize("seconds"); - alert(txt); - } - return false; -}; - -function finishedSpellChecking() { - // initialization of global variables - currentElement = null; - wrongWords = null; - allWords = {}; - fixedWords = []; - suggested_words = frame.contentWindow.suggested_words; - - document.getElementById("status").innerHTML = dialog.plugin.localize("HTMLArea Spell Checker"); - var doc = frame.contentWindow.document; - var spans = doc.getElementsByTagName("span"); - var sps = []; - var id = 0; - for (var i = 0; i < spans.length; ++i) { - var el = spans[i]; - if (/HA-spellcheck-error/.test(el.className)) { - sps.push(el); - el.__msh_wordClicked = wordClicked; - el.onclick = function(ev) { - ev || (ev = window.event); - ev && HTMLArea._stopEvent(ev); - return this.__msh_wordClicked(false); - }; - el.onmouseover = wordMouseOver; - el.onmouseout = wordMouseOut; - el.__msh_id = id++; - var txt = (el.__msh_origWord = el.firstChild.data); - el.__msh_fixed = false; - if (typeof allWords[txt] == "undefined") { - allWords[txt] = [el]; - } else { - allWords[txt].push(el); - } - } else if (/HA-spellcheck-fixed/.test(el.className)) { - fixedWords.push(el); - } - } - wrongWords = sps; - if (sps.length == 0) { - if (!modified) { - alert(dialog.plugin.localize("NO_ERRORS_CLOSING")); - window.close(); - } else { - alert(dialog.plugin.localize("NO_ERRORS")); - } - return false; - } - (currentElement = sps[0]).__msh_wordClicked(true); - var as = doc.getElementsByTagName("a"); - for (var i = as.length; --i >= 0;) { - var a = as[i]; - a.onclick = function() { - if (confirm(dialog.plugin.localize("CONFIRM_LINK_CLICK") + ":\n" + - this.href + "\n" + dialog.plugin.localize("I will open it in a new page."))) { - window.open(this.href); - } - return false; - }; - } - var dicts = doc.getElementById("HA-spellcheck-dictionaries"); - if (dicts) { - dicts.parentNode.removeChild(dicts); - dicts = dicts.innerHTML.split(/,/); - var select = document.getElementById("v_dictionaries"); - for (var i = select.length; --i >= 0;) { - select.remove(i); - } - var selectedOptionIndex = 0; - for (var i = 0; i < dicts.length; ++i) { - var txt = dicts[i]; - var option = document.createElement("option"); - if (/^@(.*)$/.test(txt)) { - txt = RegExp.$1; - selectedOptionIndex = i; - if (HTMLArea.is_ie) option.selected = true; - document.getElementById("f_dictionary").value = txt; - } - option.value = txt; - option.appendChild(document.createTextNode(txt)); - select.appendChild(option); - } - select.selectedIndex = selectedOptionIndex; - } -}; - Index: typo3/sysext/rtehtmlarea/htmlarea/plugins/SpellChecker/spell-checker.js =================================================================== --- typo3/sysext/rtehtmlarea/htmlarea/plugins/SpellChecker/spell-checker.js (révision 7101) +++ typo3/sysext/rtehtmlarea/htmlarea/plugins/SpellChecker/spell-checker.js (copie de travail) @@ -33,54 +33,64 @@ * TYPO3 SVN ID: $Id$ */ SpellChecker = HTMLArea.Plugin.extend({ - - constructor : function(editor, pluginName) { + constructor: function(editor, pluginName) { this.base(editor, pluginName); }, - /* * This function gets called by the class constructor */ - configurePlugin : function(editor) { - + configurePlugin: function(editor) { this.pageTSconfiguration = this.editorConfiguration.buttons.spellcheck; this.contentISOLanguage = this.pageTSconfiguration.contentISOLanguage; this.contentCharset = this.pageTSconfiguration.contentCharset; this.spellCheckerMode = this.pageTSconfiguration.spellCheckerMode; this.enablePersonalDicts = this.pageTSconfiguration.enablePersonalDicts; this.userUid = this.editorConfiguration.userUid; - this.defaultDictionary = (this.pageTSconfiguration.dictionaries && this.pageTSconfiguration.dictionaries[this.contentISOLanguage] && this.pageTSconfiguration.dictionaries[this.contentISOLanguage].defaultValue) ? this.pageTSconfiguration.dictionaries[this.contentISOLanguage].defaultValue : ""; - this.showDictionaries = (this.pageTSconfiguration.dictionaries && this.pageTSconfiguration.dictionaries.items) ? this.pageTSconfiguration.dictionaries.items : ""; - this.restrictToDictionaries = (this.pageTSconfiguration.dictionaries && this.pageTSconfiguration.dictionaries.restrictToItems) ? this.pageTSconfiguration.dictionaries.restrictToItems : ""; - + this.defaultDictionary = (this.pageTSconfiguration.dictionaries && this.pageTSconfiguration.dictionaries[this.contentISOLanguage] && this.pageTSconfiguration.dictionaries[this.contentISOLanguage].defaultValue) ? this.pageTSconfiguration.dictionaries[this.contentISOLanguage].defaultValue : ''; + this.showDictionaries = (this.pageTSconfiguration.dictionaries && this.pageTSconfiguration.dictionaries.items) ? this.pageTSconfiguration.dictionaries.items : ''; + this.restrictToDictionaries = (this.pageTSconfiguration.dictionaries && this.pageTSconfiguration.dictionaries.restrictToItems) ? this.pageTSconfiguration.dictionaries.restrictToItems : ''; /* * Registering plugin "About" information */ var pluginInformation = { - version : "2.2", - developer : "Mihai Bazon & Stanislas Rolland", - developerUrl : "http://dynarch.com/mishoo/", - copyrightOwner : "Mihai Bazon & Stanislas Rolland", - sponsor : "American Bible Society & SJBR", - sponsorUrl : "http://www.sjbr.ca/", - license : "GPL" + version : '3.0', + developer : 'Mihai Bazon & Stanislas Rolland', + developerUrl : 'http://www.sjbr.ca/', + copyrightOwner : 'Mihai Bazon & Stanislas Rolland', + sponsor : 'American Bible Society & SJBR', + sponsorUrl : 'http://www.sjbr.ca/', + license : 'GPL' }; this.registerPluginInformation(pluginInformation); - /* * Registering the button */ - var buttonId = "SpellCheck"; + var buttonId = 'SpellCheck'; var buttonConfiguration = { id : buttonId, - tooltip : this.localize("SC-spell-check"), - action : "onButtonPress", + tooltip : this.localize('SC-spell-check'), + action : 'onButtonPress', dialog : true }; this.registerButton(buttonConfiguration); }, - /* + * Sets of default configuration values for dialogue form fields + */ + configDefaults: { + combo: { + editable: true, + typeAhead: true, + triggerAction: 'all', + forceSelection: true, + mode: 'local', + valueField: 'value', + displayField: 'text', + helpIcon: true, + tpl: '
{text}
' + } + }, + /* * This function gets called when the button was pressed. * * @param object editor: the editor instance @@ -88,17 +98,701 @@ * * @return boolean false if action is completed */ - onButtonPress : function (editor, id, target) { + onButtonPress: function (editor, id, target) { // Could be a button or its hotkey var buttonId = this.translateHotKey(id); buttonId = buttonId ? buttonId : id; - switch (buttonId) { - case "SpellCheck": - var charset = (this.contentCharset.toLowerCase() == 'iso-8859-1') ? "-iso-8859-1" : ""; - this.dialog = this.openDialog(buttonId, this.makeUrlFromPopupName("spell-check-ui" + charset), null, null, {width:710, height:600}); - break; + // Open dialogue window + this.openDialogue( + buttonId, + 'Spell Checker', + this.getWindowDimensions( + { + width: 710, + height: 600 + }, + buttonId + ) + ); + return false; + }, + /* + * Open the dialogue window + * + * @param string buttonId: the button id + * @param string title: the window title + * @param object dimensions: the opening dimensions of the window + * + * @return void + */ + openDialogue: function (buttonId, title, dimensions) { + this.dialog = new Ext.Window({ + title: this.localize(title), + cls: 'htmlarea-window', + bodyCssClass: 'spell-check', + border: false, + width: dimensions.width, + height: 'auto', + // As of ExtJS 3.1, JS error with IE when the window is resizable + resizable: !Ext.isIE, + iconCls: buttonId, + listeners: { + afterrender: { + fn: this.onWindowAfterRender, + scope: this + }, + resize: { + fn: this.onWindowResize + }, + close: { + fn: this.onClose, + scope: this + } + }, + items: [{ + // The hidden form + xtype: 'form', + method: 'POST', + itemId: 'spell-check-form', + url: this.pageTSconfiguration.path, + hidden: true, + standardSubmit: true, + items: [{ + xtype: 'hidden', + name: 'editorId', + value: this.editor.editorId + },{ + xtype: 'hidden', + itemId: 'content', + name: 'content', + value: this.editor.getHTML() + },{ + xtype: 'hidden', + itemId: 'dictionary', + name: 'dictionary', + value: this.defaultDictionary ? this.defaultDictionary : this.contentISOLanguage + },{ + xtype: 'hidden', + name: 'pspell_charset', + value: this.contentCharset + },{ + xtype: 'hidden', + name: 'pspell_mode', + value: this.spellCheckerMode + },{ + xtype: 'hidden', + name: 'userUid', + value: this.userUid + },{ + xtype: 'hidden', + name:'enablePersonalDicts', + value: this.enablePersonalDicts + },{ + xtype: 'hidden', + name:'showDictionaries', + value: this.showDictionaries + },{ + xtype: 'hidden', + name:'restrictToDictionaries', + value: this.restrictToDictionaries + } + ] + },{ + // The iframe + xtype: 'box', + itemId: 'spell-check-iframe', + width: dimensions.width - 225, + autoEl: { + name: 'contentframe', + tag: 'iframe', + cls: 'contentframe', + src: Ext.isGecko ? 'javascript:void(0);' : (Ext.isOpera ? _typo3_host_url : '') + _editor_url + 'popups/blank.html' + } + },{ + // The original word + xtype: 'fieldset', + title: this.localize('Original word'), + cls: 'controls', + labelWidth: 0, + defaults: { + hideLabel: true, + disabled: true, + minWidth: 160 + }, + items: [{ + xtype: 'textfield', + itemId: 'word', + disabled: false + }, + this.buildButtonConfig('Revert', this.onRevertClick) + ] + },{ + // The replacement word and actions + xtype: 'fieldset', + title: this.localize('Replacement'), + cls: 'controls', + defaultType: 'button', + labelWidth: 0, + defaults: { + hideLabel: true, + disabled: true, + minWidth: 160 + }, + items: [{ + xtype: 'textfield', + disabled: false, + width: 160, + itemId: 'replacement' + },{ + itemId: 'replace', + text: this.localize('Replace'), + listeners: { + click: { + fn: this.onReplaceClick, + scope: this + } + } + },{ + itemId: 'replaceAll', + text: this.localize('Replace all'), + listeners: { + click: { + fn: this.onReplaceAllClick, + scope: this + } + } + },{ + itemId: 'ignore', + text: this.localize('Ignore'), + listeners: { + click: { + fn: this.onIgnoreClick, + scope: this + } + } + },{ + itemId: 'ignoreAll', + text: this.localize('Ignore all'), + listeners: { + click: { + fn: this.onIgnoreAllClick, + scope: this + } + } + },{ + itemId: 'learn', + text: this.localize('Learn'), + hidden: !this.enablePersonalDicts, + listeners: { + click: { + fn: this.onLearnClick, + scope: this + } + } + } + ] + },{ + // The suggestions + xtype: 'fieldset', + title: this.localize('Suggestions'), + cls: 'controls', + labelWidth: 0, + defaults: { + hideLabel: true, + minWidth: 160 + }, + items: [ + Ext.apply({ + xtype: 'combo', + itemId: 'suggestions', + store: new Ext.data.ArrayStore({ + autoDestroy: true, + fields: [{name: 'text'}, {name: 'value'}], + data: [] + }), + listeners: { + select: { + fn: this.onSuggestionSelect, + scope: this + } + } + }, this.configDefaults['combo']) + ] + },{ + // The dictionaries + xtype: 'fieldset', + title: this.localize('Dictionary'), + cls: 'controls', + defaultType: 'button', + labelWidth: 0, + defaults: { + hideLabel: true, + disabled: true, + minWidth: 160 + }, + items: [ + Ext.apply({ + xtype: 'combo', + itemId: 'dictionaries', + disabled: false, + store: new Ext.data.ArrayStore({ + autoDestroy: true, + fields: [{name: 'text'}, {name: 'value'}], + data: [] + }), + listeners: { + select: { + fn: this.onDictionarySelect, + scope: this + } + } + }, this.configDefaults['combo']), + { + itemId: 'recheck', + text: this.localize('Re-check'), + listeners: { + click: { + fn: this.onRecheckClick, + scope: this + } + } + } + ] + } + ], + bbar: new Ext.ux.StatusBar({ + id: this.editor.editorId + '-spell-check-status', + defaultText: this.localize('statusBarReady'), + defaultIconCls: 'status-ready', + text: this.localize('Please wait. Calling spell checker.'), + iconCls: 'status-wait', + defaults: { + minWidth: 100, + disabled: true + }, + items: [ + this.buildButtonConfig('OK', this.onOK), + this.buildButtonConfig('Info', this.onInfoClick), + this.buildButtonConfig('Cancel', this.onCancel) + ] + }) + }); + this.show(); + }, + /* + * Handler invoked after the window has been rendered + */ + onWindowAfterRender: function () { + // True when some word has been modified + this.modified = false; + // Array of words to add to the personal dictionary + this.addToPersonalDictionary = []; + // List of word pairs to add to replacement list of the personal dictionary + this.addToReplacementList = []; + // Initial submit + this.dialog.getComponent('spell-check-form').getForm().getEl().set({ + target: 'contentframe', + 'accept-charset': this.contentCharset.toUpperCase() + }); + this.dialog.getComponent('spell-check-form').getForm().submit(); + }, + /* + * Handler invoked after the window is resized + */ + onWindowResize: function (window, width, height) { + var frame = window.getComponent('spell-check-iframe').getEl(); + if (frame) { + frame.setSize(width - 225, height - 75); } + }, + /* + * Handler invoked when the OK button is pressed + */ + onOK: function () { + if (this.modified) { + this.editor.setHTML(this.cleanDocument(false)); + } + // Post additions to the Aspell personal dictionary + if ((this.addToPersonalDictionary.length || this.addToReplacementList.length) && this.enablePersonalDicts) { + var data = { + cmd: 'learn', + enablePersonalDicts: this.enablePersonalDicts, + userUid: this.userUid, + dictionary: this.contentISOLanguage, + pspell_charset: this.contentCharset, + pspell_mode: this.spellCheckerMode + }; + Ext.each(this.addToPersonalDictionary, function (word, index) { + data['to_p_dict[' + index + ']'] = word; + }); + Ext.each(this.addToReplacementList, function (replacement, index) { + data['to_r_list[' + index + '][0]'] = replacement[0]; + data['to_r_list[' + index + '][1]'] = replacement[1]; + }); + HTMLArea._postback(this.pageTSconfiguration.path, data); + } + this.close(); return false; + }, + /* + * Handler invoked when the Cancel button is pressed + */ + onCancel: function () { + if (this.modified) { + Ext.MessageBox.confirm('', this.localize('QUIT_CONFIRMATION'), function (button) { + if (button == 'yes') { + this.close(); + } + }, this); + return false; + } else { + return this.base(); + } + }, + /* + * Clean away span elements from the text before leaving or re-submitting + * + * @param boolean leaveFixed: if true, span elements of corrected words will be left in the text (re-submit case) + * + * @return string cleaned-up html + */ + cleanDocument: function (leaveFixed) { + var iframeDocument = this.dialog.getComponent('spell-check-iframe').getEl().dom.contentWindow.document; + Ext.each(this.misspelledWords.concat(this.correctedWords), function (element) { + element.onclick = null; + element.onmouseover = null; + element.onmouseout = null; + if (!leaveFixed || !HTMLArea._hasClass(element, 'htmlarea-spellcheck-fixed')) { + element.parentNode.insertBefore(element.firstChild, element); + element.parentNode.removeChild(element); + } else { + HTMLArea._removeClass(element, 'htmlarea-spellcheck-error'); + HTMLArea._removeClass(element, 'htmlarea-spellcheck-same'); + HTMLArea._removeClass(element, 'htmlarea-spellcheck-current'); + } + }, this); + // Cleanup event handlers on links + Ext.each(iframeDocument.getElementsByTagName('a'), function (link) { + link.onclick = null; + }, this); + return HTMLArea.getHTML(iframeDocument.body, false, this.editor); + }, + /* + * Handler invoked when the response from the server has finished loading + */ + spellCheckComplete: function () { + var contentWindow = this.dialog.getComponent('spell-check-iframe').getEl().dom.contentWindow; + this.currentElement = null; + // Array of misspelled words + this.misspelledWords = []; + // Array of corrected words + this.correctedWords = []; + // Object containing array of occurrences of each misspelled word + this.allWords = {}; + // Suggested words + this.suggestedWords = contentWindow.suggestedWords; + // Set status + Ext.getCmp(this.editor.editorId + '-spell-check-status').setStatus({ + text: this.localize('statusBarReady'), + iconCls: 'status-ready', + clear: false + }); + // Process all misspelled words + var id = 0; + var self = this; + Ext.each(contentWindow.document.getElementsByTagName('span'), function (span) { + if (HTMLArea._hasClass(span, 'htmlarea-spellcheck-error')) { + this.misspelledWords.push(span); + span.onclick = function (event) { self.setCurrentWord(this, false); }; + span.onmouseover = function (event) { HTMLArea._addClass(this, 'htmlarea-spellcheck-hover'); }; + span.onmouseout = function (event) { HTMLArea._removeClass(this, 'htmlarea-spellcheck-hover'); }; + span.htmlareaId = id++; + span.htmlareaOriginalWord = span.firstChild.data; + span.htmlareaFixed = false; + if (typeof(this.allWords[span.htmlareaOriginalWord]) == 'undefined') { + this.allWords[span.htmlareaOriginalWord] = []; + } + this.allWords[span.htmlareaOriginalWord].push(span); + } else if (HTMLArea._hasClass(span, 'htmlarea-spellcheck-fixed')) { + this.correctedWords.push(span); + } + }, this); + // Do not open links in the iframe + Ext.each(contentWindow.document.getElementsByTagName('a'), function (link) { + link.onclick = function (event) { return false; }; + }, this); + // Enable buttons + Ext.each(this.dialog.findByType('button'), function (button) { + button.setDisabled(false); + }); + Ext.each(Ext.getCmp(this.editor.editorId + '-spell-check-status').findByType('button'), function (button) { + button.setDisabled(false); + }); + if (this.misspelledWords.length) { + // Set current element to first misspelled word + this.currentElement = this.misspelledWords[0]; + this.setCurrentWord(this.currentElement, true); + // Populate the dictionaries combo + var dictionaries = contentWindow.dictionaries.split(/,/); + if (dictionaries.length) { + var select = this.dialog.find('itemId', 'dictionaries')[0]; + var store = select.getStore(); + store.removeAll(); + Ext.each(dictionaries, function (dictionary) { + store.add(new store.recordType({ + text: dictionary, + value: dictionary + })); + }); + select.setValue(contentWindow.selectedDictionary); + var selectedIndex = store.find('value', contentWindow.selectedDictionary); + select.fireEvent('select', select, store.getAt(selectedIndex), selectedIndex); + } + } else { + if (!this.modified) { + Ext.MessageBox.alert('', this.localize('NO_ERRORS_CLOSING')); + this.onOK(); + } else { + Ext.MessageBox.alert('', this.localize('NO_ERRORS')); + } + return false; + } + }, + /* + * Get absolute position of an element inside the iframe + */ + getAbsolutePosition: function (element) { + var position = { + x: element.offsetLeft, + y: element.offsetTop + }; + if (element.offsetParent) { + var tmp = this.getAbsolutePosition(element.offsetParent); + position.x += tmp.x; + position.y += tmp.y; + } + return position; + }, + /* + * Update current word + */ + setCurrentWord: function (element, scroll) { + // Scroll element into view + if (scroll) { + var frame = this.dialog.getComponent('spell-check-iframe').getEl().dom; + var position = this.getAbsolutePosition(element); + var frameSize = { + x: frame.offsetWidth - 4, + y: frame.offsetHeight - 4 + }; + position.x -= Math.round(frameSize.x/2); + if (position.x < 0) { + position.x = 0; + } + position.y -= Math.round(frameSize.y/2); + if (position.y < 0) { + position.y = 0; + } + frame.contentWindow.scrollTo(position.x, position.y); + } + // De-highlight all occurrences of current word + if (this.currentElement) { + HTMLArea._removeClass(this.currentElement, 'htmlarea-spellcheck-current'); + Ext.each(this.allWords[this.currentElement.htmlareaOriginalWord], function (word) { + HTMLArea._removeClass(word, 'htmlarea-spellcheck-same'); + }); + } + // Highlight all occurrences of new current word + this.currentElement = element; + HTMLArea._addClass(this.currentElement, 'htmlarea-spellcheck-current'); + var occurrences = this.allWords[this.currentElement.htmlareaOriginalWord]; + Ext.each(occurrences, function (word) { + if (word != this.currentElement) { + HTMLArea._addClass(word, 'htmlarea-spellcheck-same'); + } + }, this); + this.dialog.find('itemId', 'replaceAll')[0].setDisabled(occurrences.length <= 1); + this.dialog.find('itemId', 'ignoreAll')[0].setDisabled(occurrences.length <= 1); + // Display status + var txt; + var txt2; + if (occurrences.length == 1) { + txt = this.localize('One occurrence'); + txt2 = this.localize('was found.'); + } else if (occurrences.length == 2) { + txt = this.localize('Two occurrences'); + txt2 = this.localize('were found.'); + } else { + txt = occurrences.length + ' ' + this.localize('occurrences'); + txt2 = this.localize('were found.'); + } + Ext.getCmp(this.editor.editorId + '-spell-check-status').setStatus({ + text: txt + ' ' + this.localize('of the word') + ' "' + this.currentElement.htmlareaOriginalWord + '" ' + txt2, + iconCls: 'status-info', + clear: false + }); + // Update suggestions + var suggestions = this.suggestedWords[this.currentElement.htmlareaOriginalWord]; + if (suggestions) { + suggestions = suggestions.split(/,/); + } else { + suggestions = []; + } + var select = this.dialog.find('itemId', 'suggestions')[0]; + var store = select.getStore(); + store.removeAll(); + Ext.each(suggestions, function (suggestion) { + store.add(new store.recordType({ + text: suggestion, + value: suggestion + })); + }); + // Update the current word + this.dialog.find('itemId', 'word')[0].setValue(this.currentElement.htmlareaOriginalWord); + if (suggestions.length > 0) { + select.setValue(store.getAt(0).get('value')); + select.fireEvent('select', select, store.getAt(0), 0); + } else { + this.dialog.find('itemId', 'replacement')[0].setVvalue(this.currentElement.innerHTML); + } + return false; + }, + /* + * Handler invoked when the mouse moves over a misspelled word + */ + onWordMouseOver: function (event, element) { + HTMLArea._addClass(element, 'htmlarea-spellcheck-hover'); + }, + /* + * Handler invoked when the mouse moves out of a misspelled word + */ + onWordMouseOut: function (event, element) { + HTMLArea._removeClass(element, 'htmlarea-spellcheck-hover'); + }, + /* + * Handler invoked when a suggestion is selected + */ + onSuggestionSelect: function (select, record, index) { + this.dialog.find('itemId', 'replacement')[0].setValue(record.get('value')); + }, + /* + * Handler invoked when a dictionary is selected + */ + onDictionarySelect: function (select, record, index) { + this.dialog.find('itemId', 'dictionary')[0].setValue(record.get('value')); + }, + /* + * Handler invoked when the Revert button is clicked + */ + onRevertClick: function () { + this.dialog.find('itemId', 'replacement')[0].setValue(this.currentElement.htmlareaOriginalWord); + this.replaceWord(this.currentElement); + HTMLArea._removeClass(this.currentElement, 'htmlarea-spellcheck-fixed'); + HTMLArea._addClass(this.currentElement, 'htmlarea-spellcheck-error'); + HTMLArea._addClass(this.currentElement, 'htmlarea-spellcheck-current'); + return false; + }, + /* + * Replace the word contained in the element + */ + replaceWord: function (element) { + HTMLArea._removeClass(element, 'htmlarea-spellcheck-hover'); + HTMLArea._addClass(element, 'htmlarea-spellcheck-fixed'); + element.htmlareaFixed = true; + var replacement = this.dialog.find('itemId', 'replacement')[0].getValue(); + if (element.innerHTML != replacement) { + this.addToReplacementList.push([element.innerHTML, replacement]); + element.innerHTML = replacement; + this.modified = true; + } + }, + /* + * Handler invoked when the Replace button is clicked + */ + onReplaceClick: function () { + this.replaceWord(this.currentElement); + var start = this.currentElement.htmlareaId; + var index = start; + do { + ++index; + if (index == this.misspelledWords.length) { + index = 0; + } + } while (index != start && this.misspelledWords[index].htmlareaFixed); + if (index == start) { + index = 0; + Ext.MessageBox.alert('', this.localize('Finished list of mispelled words')); + } + this.setCurrentWord(this.misspelledWords[index], true); + return false; + }, + /* + * Handler invoked when the Replace all button is clicked + */ + onReplaceAllClick: function () { + Ext.each(this.allWords[this.currentElement.htmlareaOriginalWord], function (element) { + if (element != this.currentElement) { + this.replaceWord(element); + } + }, this); + // Replace current element last, so that we jump to the next word + return this.onReplaceClick(); + }, + /* + * Handler invoked when the Ignore button is clicked + */ + onIgnoreClick: function () { + this.dialog.find('itemId', 'replacement')[0].setValue(this.currentElement.htmlareaOriginalWord); + return this.onReplaceClick(); + }, + /* + * Handler invoked when the Ignore all button is clicked + */ + onIgnoreAllClick: function () { + this.dialog.find('itemId', 'replacement')[0].setValue(this.currentElement.htmlareaOriginalWord); + return this.onReplaceAllClick(); + }, + /* + * Handler invoked when the Learn button is clicked + */ + onLearnClick: function () { + this.addToPersonalDictionary.push(this.currentElement.htmlareaOriginalWord); + return this.onIgnoreAllClick(); + }, + /* + * Handler invoked when the Re-check button is clicked + */ + onRecheckClick: function () { + // Disable buttons + Ext.each(this.dialog.findByType('button'), function (button) { + button.setDisabled(true); + }); + Ext.each(Ext.getCmp(this.editor.editorId + '-spell-check-status').findByType('button'), function (button) { + button.setDisabled(true); + }); + Ext.getCmp(this.editor.editorId + '-spell-check-status').setStatus({ + text: this.localize('Please wait: changing dictionary to') + ': "' + this.dialog.find('itemId', 'dictionary')[0].getValue() + '".', + iconCls: 'status-wait', + clear: false + }); + this.dialog.find('itemId', 'content')[0].setValue(this.cleanDocument(true)); + this.dialog.getComponent('spell-check-form').getForm().submit(); + }, + /* + * Handler invoked when the Info button is clicked + */ + onInfoClick: function () { + var info = this.dialog.getComponent('spell-check-iframe').getEl().dom.contentWindow.spellcheckInfo; + if (!info) { + Ext.MessageBox.alert('', this.localize('No information available')); + } else { + var txt = this.localize('Document information') + '
'; + Ext.iterate(info, function (key, value) { + txt += '
' + this.localize(key) + ': ' + value; + }, this); + txt += ' ' + this.localize('seconds'); + Ext.MessageBox.alert('', txt); + } + return false; } }); - Index: typo3/sysext/rtehtmlarea/htmlarea/skins/default/htmlarea.css =================================================================== --- typo3/sysext/rtehtmlarea/htmlarea/skins/default/htmlarea.css (révision 7101) +++ typo3/sysext/rtehtmlarea/htmlarea/skins/default/htmlarea.css (copie de travail) @@ -483,56 +483,23 @@ border: 1px solid #A2AAB8; } /* Selectors for the SpellChecker dialogue */ -.htmlarea-spell-check .dictionaries { - float:right; - padding:2px; +.htmlarea-window .spell-check .controls { + text-align: center; + width: 175px; + margin: 3px; } -.htmlarea-spell-check .dictionaries #v_dictionaries { - width:10em; - margin-right:5px; +.htmlarea-window .spell-check .controls .x-btn { + margin-top: 3px; + margin-left: 8px; } -.htmlarea-spell-check #b_recheck { - vertical-align:middle; - width:12em; +.htmlarea-window .spell-check .contentframe { + float: right; + height: 450px; + margin: 9px 0px; + background-color: #FFF; + color: #000; + border: 1px solid #A2AAB8; } -.htmlarea-spell-check .status { - font-weight:bold; - padding:2px; -} -.htmlarea-spell-check .controls { - width:14em; - clear:right;float:left; - text-align:center; - margin:3px; -} -.htmlarea-spell-check .controls .sectitle { - font-weight:bold; - padding:2px 4px; -} -.htmlarea-spell-check .controls .secbody { - margin-bottom:10px; -} -.htmlarea-spell-check .controls #v_currentWord { - color:#F00;font-weight:bold; - margin-bottom:2px; -} -.htmlarea-spell-check .controls #v_replacement { - margin-bottom:3px; -} -.htmlarea-spell-check .controls #v_suggestions, .controls #v_replacement { - width:12em; -} -.htmlarea-spell-check .contentframe { - float:right; margin:3px; -} -.htmlarea-spell-check .contentframe #i_framecontent { - background-color:#FFF;color:#000; - height:450px;width:480px; -} -.htmlarea-spell-check .occurrences-found { - padding:7px 0px 0px 5px; - clear:both; -} /* Selectors for the ContextMenu plugin */ .htmlarea-context-menu { background-color:#EFEFF4; @@ -558,3 +525,57 @@ margin-bottom:0; border-color:#A2AAB8; } +/* Window status bar selectors */ +.htmlarea-window .status-ready { + padding-left: 21px !important; + background-repeat: no-repeat; + background-image: url("images/dialog-ok.png"); + background-position: 0px 2px; +} +.htmlarea-window .status-info { + padding-left: 21px !important; + background-repeat: no-repeat; + background-image: url("images/dialog-information.png"); + background-position: 0px 2px; +} +.htmlarea-window .status-wait { + padding-left: 45px !important; + background-repeat: no-repeat; + background-image:url("images/loading-balls.gif"); + background-position: 0px 6px; +} +.x-statusbar .x-btn { + margin-left: 5px; +} +.x-statusbar .x-status-text { + cursor: default; +} + +/* Button background positioning in window status bar*/ +.x-statusbar .x-btn-tl{ + background-position: 0 0; +} +.x-statusbar .x-btn-tr{ + background-position: -3px 0; +} +.x-statusbar .x-btn-tc{ + background-position: 0 -6px; +} +.x-statusbar .x-btn-ml{ + background-position: 0 -24px; +} +.x-statusbar .x-btn-mr{ + background-position: -3px -24px; +} +.x-statusbar .x-btn-mc{ + background-position: 0 -1096px; +} +.x-statusbar .x-btn-bl{ + background-position: 0 -3px; +} +.x-statusbar .x-btn-br{ + background-position: -3px -3px; +} +.x-statusbar .x-btn-bc{ + background-position: 0 -15px; +} Index: typo3/sysext/rtehtmlarea/htmlarea/skins/default/images/dialog-information.png =================================================================== Impossible d'afficher : fichier considéré comme binaire. svn:mime-type = application/octet-stream Modification de propriétés sur typo3\sysext\rtehtmlarea\htmlarea\skins\default\images\dialog-information.png ___________________________________________________________________ Ajouté : svn:mime-type + application/octet-stream Index: typo3/sysext/rtehtmlarea/htmlarea/skins/default/images/dialog-ok.png =================================================================== Impossible d'afficher : fichier considéré comme binaire. svn:mime-type = application/octet-stream Modification de propriétés sur typo3\sysext\rtehtmlarea\htmlarea\skins\default\images\dialog-ok.png ___________________________________________________________________ Ajouté : svn:mime-type + application/octet-stream Index: typo3/sysext/rtehtmlarea/htmlarea/skins/default/images/loading-balls.gif =================================================================== Impossible d'afficher : fichier considéré comme binaire. svn:mime-type = application/octet-stream Modification de propriétés sur typo3\sysext\rtehtmlarea\htmlarea\skins\default\images\loading-balls.gif ___________________________________________________________________ Ajouté : svn:mime-type + application/octet-stream Index: typo3/sysext/rtehtmlarea/pi1/class.tx_rtehtmlarea_pi1.php =================================================================== --- typo3/sysext/rtehtmlarea/pi1/class.tx_rtehtmlarea_pi1.php (révision 7101) +++ typo3/sysext/rtehtmlarea/pi1/class.tx_rtehtmlarea_pi1.php (copie de travail) @@ -2,7 +2,7 @@ /*************************************************************** * Copyright notice * -* (c) 2003-2009 Stanislas Rolland +* (c) 2003-2010 Stanislas Rolland * All rights reserved * * This script is part of the Typo3 project. The Typo3 project is @@ -112,8 +112,6 @@ if (empty($this->dictionary) || !in_array($this->dictionary, $dictionaryArray)) { $this->dictionary = 'en'; } - $dictionaries = substr_replace($dictionaryList, '@'.$this->dictionary, strpos($dictionaryList, $this->dictionary), strlen($this->dictionary)); - // Setting the pspell suggestion mode $this->pspellMode = t3lib_div::_POST('pspell_mode')?t3lib_div::_POST('pspell_mode'): $this->pspellMode; // Now sanitize $this->pspellMode @@ -214,10 +212,9 @@ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> - - + '; - $this->result .= ''; + $this->result .= ''; $this->result .= preg_replace('/'.preg_quote('').'['.preg_quote(chr(10).chr(13).chr(32)).']*/'.(($this->parserCharset == 'utf-8')?'u':''), '', $this->text); - $this->result .= '
'.$dictionaries.'
'; + $this->result .= '
'.$dictionaries.'
'; // Closing $this->result .= ' @@ -355,7 +354,7 @@ unset($suggest); } if( !in_array($word, $incurrent) ) { - $stringText = preg_replace('/\b'.$word.'\b/'.(($this->parserCharset == 'utf-8')?'u':''), ''.$word.'', $stringText); + $stringText = preg_replace('/\b'.$word.'\b/'.(($this->parserCharset == 'utf-8')?'u':''), ''.$word.'', $stringText); $incurrent[] = $word; } } @@ -391,7 +390,7 @@ unset($suggestions); } if (!in_array($word, $incurrent)) { - $stringText = preg_replace('/\b'.$word.'\b/'.(($this->parserCharset == 'utf-8')?'u':''), ''.$word.'', $stringText); + $stringText = preg_replace('/\b'.$word.'\b/'.(($this->parserCharset == 'utf-8')?'u':''), ''.$word.'', $stringText); $incurrent[] = $word; } } Index: typo3/sysext/t3skin/rtehtmlarea/htmlarea.css =================================================================== --- typo3/sysext/t3skin/rtehtmlarea/htmlarea.css (révision 7101) +++ typo3/sysext/t3skin/rtehtmlarea/htmlarea.css (copie de travail) @@ -490,56 +490,23 @@ border: 1px solid #A2AAB8; } /* Selectors for the SpellChecker dialogue */ -.htmlarea-spell-check .dictionaries { - float:right; - padding:2px; +.htmlarea-window .spell-check .controls { + text-align: center; + width: 175px; + margin: 3px; } -.htmlarea-spell-check .dictionaries #v_dictionaries { - width:10em; - margin-right:5px; +.htmlarea-window .spell-check .controls .x-btn { + margin-top: 3px; + margin-left: 8px; } -.htmlarea-spell-check #b_recheck { - vertical-align:middle; - width:12em; +.htmlarea-window .spell-check .contentframe { + float: right; + height: 450px; + margin: 9px 0px; + background-color: #FFF; + color: #000; + border: 1px solid #A2AAB8; } -.htmlarea-spell-check .status { - font-weight:bold; - padding:2px; -} -.htmlarea-spell-check .controls { - width:14em; - clear:right;float:left; - text-align:center; - margin:3px; -} -.htmlarea-spell-check .controls .sectitle { - font-weight:bold; - padding:2px 4px; -} -.htmlarea-spell-check .controls .secbody { - margin-bottom:10px; -} -.htmlarea-spell-check .controls #v_currentWord { - color:#F00;font-weight:bold; - margin-bottom:2px; -} -.htmlarea-spell-check .controls #v_replacement { - margin-bottom:3px; -} -.htmlarea-spell-check .controls #v_suggestions, .controls #v_replacement { - width:12em; -} -.htmlarea-spell-check .contentframe { - float:right; margin:3px; -} -.htmlarea-spell-check .contentframe #i_framecontent { - background-color:#FFF;color:#000; - height:450px;width:480px; -} -.htmlarea-spell-check .occurrences-found { - padding:7px 0px 0px 5px; - clear:both; -} /* Selectors for the ContextMenu plugin */ .htmlarea-context-menu { background-color:#EFEFF4; @@ -565,3 +532,57 @@ margin-bottom:0; border-color:#A2AAB8; } +/* Window status bar selectors */ +.htmlarea-window .status-ready { + padding-left: 21px !important; + background-repeat: no-repeat; + background-image: url("images/dialog-ok.png"); + background-position: 0px 2px; +} +.htmlarea-window .status-info { + padding-left: 21px !important; + background-repeat: no-repeat; + background-image: url("images/dialog-information.png"); + background-position: 0px 2px; +} +.htmlarea-window .status-wait { + padding-left: 45px !important; + background-repeat: no-repeat; + background-image:url("images/loading-balls.gif"); + background-position: 0px 6px; +} +.x-statusbar .x-btn { + margin-left: 5px; +} +.x-statusbar .x-status-text { + cursor: default; +} + +/* Button background positioning in window status bar*/ +.x-statusbar .x-btn-tl{ + background-position: 0 0; +} +.x-statusbar .x-btn-tr{ + background-position: -3px 0; +} +.x-statusbar .x-btn-tc{ + background-position: 0 -6px; +} +.x-statusbar .x-btn-ml{ + background-position: 0 -24px; +} +.x-statusbar .x-btn-mr{ + background-position: -3px -24px; +} +.x-statusbar .x-btn-mc{ + background-position: 0 -1096px; +} +.x-statusbar .x-btn-bl{ + background-position: 0 -3px; +} +.x-statusbar .x-btn-br{ + background-position: -3px -3px; +} +.x-statusbar .x-btn-bc{ + background-position: 0 -15px; +} Index: typo3/sysext/t3skin/rtehtmlarea/images/dialog-information.png =================================================================== Impossible d'afficher : fichier considéré comme binaire. svn:mime-type = application/octet-stream Modification de propriétés sur typo3\sysext\t3skin\rtehtmlarea\images\dialog-information.png ___________________________________________________________________ Ajouté : svn:mime-type + application/octet-stream Index: typo3/sysext/t3skin/rtehtmlarea/images/dialog-ok.png =================================================================== Impossible d'afficher : fichier considéré comme binaire. svn:mime-type = application/octet-stream Modification de propriétés sur typo3\sysext\t3skin\rtehtmlarea\images\dialog-ok.png ___________________________________________________________________ Ajouté : svn:mime-type + application/octet-stream Index: typo3/sysext/t3skin/rtehtmlarea/images/loading-balls.gif =================================================================== Impossible d'afficher : fichier considéré comme binaire. svn:mime-type = application/octet-stream Modification de propriétés sur typo3\sysext\t3skin\rtehtmlarea\images\loading-balls.gif ___________________________________________________________________ Ajouté : svn:mime-type + application/octet-stream