diff --git a/phpgwapi/inc/class.html.inc.php b/phpgwapi/inc/class.html.inc.php index ce7942bc8d..820098ba0d 100644 --- a/phpgwapi/inc/class.html.inc.php +++ b/phpgwapi/inc/class.html.inc.php @@ -598,7 +598,11 @@ class html static function &initCKEditor($_height, $_mode) { include_once(EGW_INCLUDE_ROOT."/phpgwapi/js/ckeditor3/ckeditor_php5.php"); - + // use the lang and country information to construct a possible lang info for CKEditor UI and scayt_slang + $lang = ($GLOBALS['egw_info']['user']['preferences']['common']['spellchecker_lang'] ? $GLOBALS['egw_info']['user']['preferences']['common']['spellchecker_lang']: $GLOBALS['egw_info']['user']['preferences']['common']['lang']); + $country = $GLOBALS['egw_info']['user']['preferences']['common']['country']; + if (!(strpos($lang,'-')===false)) list($lang,$country) = explode('-',$lang); + //Get the ckeditor base url $basePath = $GLOBALS['egw_info']['server']['webserver_url'].'/phpgwapi/js/ckeditor3/'; @@ -606,7 +610,7 @@ class html $oCKeditor->returnOutput = true; $oCKeditor->config['customConfig'] = 'ckeditor.egwconfig.js'; - + $oCKeditor->config['language'] = $lang; $oCKeditor->config['resize_enabled'] = false; //switching the encoding as html entities off, as we correctly handle charsets and it messes up the wiki totally $oCKeditor->config['entities'] = true; @@ -626,14 +630,20 @@ class html $oCKeditor->config['toolbarStartupExpanded'] = false; // Now setting the admin settings -/* $spell = ''; + $spell = ''; if (isset($GLOBALS['egw_info']['server']['enabled_spellcheck'])) { $spell = '_spellcheck'; - $oFCKeditor->Config['SpellChecker'] = 'SpellerPages'; - $oFCKeditor->Config['SpellerPagesServerScript'] = 'server-scripts/spellchecker.php?'.$extra; - $oFCKeditor->Config['FirefoxSpellChecker'] = false; - }*/ + if (!empty($GLOBALS['egw_info']['server']['aspell_path']) && + is_executable($GLOBALS['egw_info']['server']['aspell_path'])) + { + $spell = '_aspell'; + $oCKeditor->config['extraPlugins'] = 'aspell'; + } + $oCKeditor->config['scayt_autoStartup']=true; + $oCKeditor->config['scayt_sLang']=$lang.'_'.strtoupper($country); + } + $oCKeditor->config['disableNativeSpellChecker'] = true; // Now setting the user preferences if (isset($GLOBALS['egw_info']['user']['preferences']['common']['rte_enter_mode'])) @@ -675,20 +685,18 @@ class html $oCKeditor->config['skin'] = $skin; - //$oCKeditor->config['spellchecker'] = 'SpellCheck'; - switch($_mode) { case 'simple': - $oCKeditor->config['toolbar'] = 'egw_simple'; + $oCKeditor->config['toolbar'] = 'egw_simple'.$spell; $oCKeditor->config['menu_groups'] = ''; break; default: case 'extended': - $oCKeditor->config['toolbar'] = 'egw_extended'; + $oCKeditor->config['toolbar'] = 'egw_extended'.$spell; break; case 'advanced': - $oCKeditor->config['toolbar'] = 'egw_advanced'; + $oCKeditor->config['toolbar'] = 'egw_advanced'.$spell; break; } return $oCKeditor; diff --git a/phpgwapi/js/ckeditor3/ckeditor.egwconfig.js b/phpgwapi/js/ckeditor3/ckeditor.egwconfig.js index f638814842..c9c49509da 100644 --- a/phpgwapi/js/ckeditor3/ckeditor.egwconfig.js +++ b/phpgwapi/js/ckeditor3/ckeditor.egwconfig.js @@ -14,6 +14,18 @@ CKEDITOR.editorConfig = function( config ) ] ; config.toolbar_egw_simple_spellcheck = [ + ['Bold','Italic','Underline'], + ['JustifyLeft','JustifyCenter','JustifyRight','JustifyBlock'], + ['BulletList','NumberedList','Outdent','Indent','Undo','Redo'], + ['Cut','Copy','Paste','PasteText','PasteFromWord','-','Print'], + ['Maximize','SpellChecker'], + '/', + ['Format','Font','FontSize'], + ['TextColor','BGColor'], + ['ShowBlocks','-','About'] + ] ; + + config.toolbar_egw_simple_aspell = [ ['Bold','Italic','Underline'], ['JustifyLeft','JustifyCenter','JustifyRight','JustifyBlock'], ['BulletList','NumberedList','Outdent','Indent','Undo','Redo'], @@ -25,6 +37,13 @@ CKEDITOR.editorConfig = function( config ) ['ShowBlocks','-','About'] ] ; +// config.toolbar_egw_simple.concat([ +// '/', +// ['Format','Font','FontSize'], +// ['TextColor','BGColor'], +// ['ShowBlocks','-','About'] +// ]); + config.toolbar_egw_extended = [ ['Bold','Italic','Underline'], ['JustifyLeft','JustifyCenter','JustifyRight','JustifyBlock'], @@ -40,6 +59,20 @@ CKEDITOR.editorConfig = function( config ) ] ; config.toolbar_egw_extended_spellcheck = [ + ['Bold','Italic','Underline'], + ['JustifyLeft','JustifyCenter','JustifyRight','JustifyBlock'], + ['BulletList','NumberedList','Outdent','Indent','Undo','Redo'], + ['Cut','Copy','Paste','PasteText','PasteFromWord','-','Print'], + ['Link','Unlink','Anchor'], + ['Find','Replace'], + ['Maximize','SpellChecker','Image','Table'], + '/', + ['Format','Font','FontSize'], + ['TextColor','BGColor'], + ['ShowBlocks','-','About'] + ] ; + + config.toolbar_egw_extended_aspell = [ ['Bold','Italic','Underline'], ['JustifyLeft','JustifyCenter','JustifyRight','JustifyBlock'], ['BulletList','NumberedList','Outdent','Indent','Undo','Redo'], @@ -70,6 +103,22 @@ CKEDITOR.editorConfig = function( config ) ] ; config.toolbar_egw_advanced_spellcheck = [ + ['Source','DocProps','-','Save','NewPage','Preview','-','Templates'], + ['Cut','Copy','Paste','PasteText','PasteFromWord','-','Print','SpellChecker'], + ['Undo','Redo','-','Find','Replace','-','SelectAll','RemoveFormat'], + '/', + ['Bold','Italic','Underline','Strike','-','Subscript','Superscript'], + ['JustifyLeft','JustifyCenter','JustifyRight','JustifyBlock'], + ['BulletList','NumberedList','-','Outdent','Indent'], + ['Link','Unlink','Anchor'], + ['Maximize','Image',/*'Flash',*/'Table','HorizontalRule',/*'Smiley',*/'SpecialChar','PageBreak'], //,'UniversalKey' + '/', + ['Style','Format','Font','FontSize'], + ['TextColor','BGColor'], + ['ShowBlocks','-','About'] + ] ; + + config.toolbar_egw_advanced_aspell = [ ['Source','DocProps','-','Save','NewPage','Preview','-','Templates'], ['Cut','Copy','Paste','PasteText','PasteFromWord','-','Print','SpellCheck'], ['Undo','Redo','-','Find','Replace','-','SelectAll','RemoveFormat'], diff --git a/phpgwapi/js/ckeditor3/egw_integration.txt b/phpgwapi/js/ckeditor3/egw_integration.txt index 8519c8c97b..78c37b5eba 100644 --- a/phpgwapi/js/ckeditor3/egw_integration.txt +++ b/phpgwapi/js/ckeditor3/egw_integration.txt @@ -28,3 +28,10 @@ Index: phpgwapi/js/ckeditor3/ckeditor_php5.php if (is_null($val)) { return 'null'; +--------------- step 3 -------------------------------------- +-added aspell as plugin, as it is not available by default anymore. + phpgwapi/js/ckeditor3/plugins/aspell +-changed phpgwapi/js/ckeditor3/plugins/aspell/spellerpages/server-scripts/spellchecker.php + to allow only configured aspell, tmp dirs and language settings +-removed other than php spellchecker pages from phpgwapi/js/ckeditor3/plugins/aspell/spellerpages/server-scripts/ +-added toolbar options to reflect the use/change of calls of SpellCheck(er) via toolbar button diff --git a/phpgwapi/js/ckeditor3/plugins/aspell/aspell.css b/phpgwapi/js/ckeditor3/plugins/aspell/aspell.css new file mode 100644 index 0000000000..fc68ae235b --- /dev/null +++ b/phpgwapi/js/ckeditor3/plugins/aspell/aspell.css @@ -0,0 +1,6 @@ + +.cke_editor .cke_button_aspell .cke_icon +{ + background-position: 0 -192px !important; +} + diff --git a/phpgwapi/js/ckeditor3/plugins/aspell/dialogs/aspell.js b/phpgwapi/js/ckeditor3/plugins/aspell/dialogs/aspell.js new file mode 100644 index 0000000000..b1f5001e17 --- /dev/null +++ b/phpgwapi/js/ckeditor3/plugins/aspell/dialogs/aspell.js @@ -0,0 +1,170 @@ + +var FCKLang; +var OnSpellerControlsLoad; + +CKEDITOR.dialog.add('aspell', function( editor ) +{ + var number = CKEDITOR.tools.getNextNumber(), + iframeId = 'cke_frame_' + number, + textareaId = 'cke_data_' + number, + interval, + errorMsg = editor.lang.spellCheck.notAvailable; + + var spellHTML = + // Input for exchanging data CK<--->spellcheck + '' + + // Spellcheck iframe + ''; + + function spellTime(dialog, errorMsg) + { + var i = 0; + return function() + { + if (typeof window.spellChecker == 'function') + { + // Call from window.setInteval expected at once. + if (typeof interval != 'undefined') + window.clearInterval(interval); + + // Create spellcheck object, set options/attributes + var oSpeller = new spellChecker(document.getElementById(textareaId)); + oSpeller.spellCheckScript = editor.plugins.aspell.path+'spellerpages/server-scripts/spellchecker.php'; + oSpeller.OnFinished = function (numChanges) { oSpeller_OnFinished(dialog, numChanges) }; + oSpeller.popUpUrl = editor.plugins.aspell.path+'spellerpages/spellchecker.html'; + oSpeller.popUpName = iframeId; + oSpeller.popUpProps = null; + + // Place language in global variable; + // A bit of a hack, but how does e.g. controls.html know which language to use? + FCKLang = {}; + // spellChecker.js + FCKLang.DlgSpellNoChanges = CKEDITOR.lang[editor.langCode].spellCheck.noChanges; + FCKLang.DlgSpellNoMispell = CKEDITOR.lang[editor.langCode].spellCheck.noMispell; + FCKLang.DlgSpellOneChange = CKEDITOR.lang[editor.langCode].spellCheck.oneChange; + FCKLang.DlgSpellManyChanges = CKEDITOR.lang[editor.langCode].spellCheck.manyChanges; + // controls.html + FCKLang.DlgSpellNotInDic = CKEDITOR.lang[editor.langCode].spellCheck.notInDic; + FCKLang.DlgSpellChangeTo = CKEDITOR.lang[editor.langCode].spellCheck.changeTo; + FCKLang.DlgSpellBtnIgnore = CKEDITOR.lang[editor.langCode].spellCheck.btnIgnore; + FCKLang.DlgSpellBtnIgnoreAll = CKEDITOR.lang[editor.langCode].spellCheck.btnIgnoreAll; + FCKLang.DlgSpellBtnReplace = CKEDITOR.lang[editor.langCode].spellCheck.btnReplace; + FCKLang.DlgSpellBtnReplaceAll = CKEDITOR.lang[editor.langCode].spellCheck.btnReplaceAll; + FCKLang.DlgSpellBtnUndo = CKEDITOR.lang[editor.langCode].spellCheck.btnUndo; + // controlWindow.js + FCKLang.DlgSpellNoSuggestions = CKEDITOR.lang[editor.langCode].spellCheck.noSuggestions; + // spellchecker.html + FCKLang.DlgSpellProgress = CKEDITOR.lang[editor.langCode].spellCheck.progress; + // End language + + // Start spellcheck! + oSpeller.openChecker(); + } + else if (i++ == 180) // Timeout: 180 * 250ms = 45s. + { + alert(errorMsg); + dialog.hide(); + } + }; + } + + function oSpeller_OnFinished(dialog, numberOCorrections) + { + if (numberOCorrections > 0) + { + editor.focus(); + editor.fire('saveSnapshot'); // Best way I could find to trigger undo steps. + dialog.getParentEditor().setData(document.getElementById(textareaId).value,function(){ + editor.fire('saveSnapshot'); // But there's a blank one between! + }); + } + dialog.hide(); + } + + // Fx and IE don't see the same sizes, it seems. That or Fx is allowing everything to grow. + var minW = 485; + var minH = 380; + if (document.all) + { + minW = 510; + minH = 405; + } + + return { + title: editor.lang.spellCheck.title, + minWidth: minW, + minHeight: minH, + buttons: [ CKEDITOR.dialog.cancelButton ], + onShow: function() + { + // Put spellcheck input and iframe in the dialog content + var contentArea = this.getContentElement('general', 'content').getElement(); + contentArea.setHtml(spellHTML); + + // Define spellcheck init function + OnSpellerControlsLoad = function (controlsWindow) + { + // Translate the dialog box texts + var spans = controlsWindow.document.getElementsByTagName('span'); + var inputs = controlsWindow.document.getElementsByTagName('input'); + var i, attr; + + for (i=0; i < spans.length; i++) + { + attr = spans[i].getAttribute && spans[i].getAttribute('fckLang'); + if (attr) + spans[i].innerHTML = FCKLang[attr]; + } + for (i=0; i < inputs.length; i++) + { + attr = inputs[i].getAttribute && inputs[i].getAttribute('fckLang'); + if (attr) + inputs[i].value = FCKLang[attr]; + } + } + + // Add spellcheck script to head + CKEDITOR.document.getHead().append(CKEDITOR.document.createElement('script', { + attributes: { + type: 'text/javascript', + src: editor.plugins.aspell.path+'spellerpages/spellChecker.js' + }})); + + // Get the data to be checked. + var sData = editor.getData(); + //CKEDITOR.document.getById(textareaId).setValue(sData); <-- doesn't work for some reason + document.getElementById(textareaId).value = sData; + + // Wait for spellcheck script to load, then execute + interval = window.setInterval(spellTime(this, errorMsg), 250); + }, + onHide: function() + { + window.ooo = undefined; + window.int_framsetLoaded = undefined; + window.framesetLoaded = undefined; + window.is_window_opened = false; + + OnSpellerControlsLoad = null; + FCKLang = null; + }, + contents: [ + { + id: 'general', + label: editor.lang.spellCheck.title, + padding: 0, + elements: [ + { + type: 'html', + id: 'content', + style: 'width:485;height:380px', + html: '
' + } + ] + } + ] + }; +}); diff --git a/phpgwapi/js/ckeditor3/plugins/aspell/plugin.js b/phpgwapi/js/ckeditor3/plugins/aspell/plugin.js new file mode 100644 index 0000000000..6ced98fafe --- /dev/null +++ b/phpgwapi/js/ckeditor3/plugins/aspell/plugin.js @@ -0,0 +1,31 @@ +/** + * Aspell plug-in for CKeditor 3.0 + * Ported from FCKeditor 2.x by Christian Boisjoli, SilenceIT + * Requires toolbar, aspell + */ + +CKEDITOR.plugins.add('aspell', { + init: function (editor) { + // Create dialog-based command named "aspell" + editor.addCommand('aspell', new CKEDITOR.dialogCommand('aspell')); + + // Add button to toolbar. Not sure why only that name works for me. + editor.ui.addButton('SpellCheck', { + label: editor.lang.spellCheck.toolbar, + command: 'aspell' + }); + + // Add link dialog code + CKEDITOR.dialog.add('aspell', this.path + 'dialogs/aspell.js'); + + // Add CSS + var aspellCSS = document.createElement('link'); + aspellCSS.setAttribute( 'rel', 'stylesheet'); + aspellCSS.setAttribute('type', 'text/css'); + aspellCSS.setAttribute('href', this.path+'aspell.css'); + document.getElementsByTagName("head")[0].appendChild(aspellCSS); + delete aspellCSS; + }, + requires: ['toolbar'] +}); + diff --git a/phpgwapi/js/ckeditor3/plugins/aspell/spellerpages/blank.html b/phpgwapi/js/ckeditor3/plugins/aspell/spellerpages/blank.html new file mode 100644 index 0000000000..e69de29bb2 diff --git a/phpgwapi/js/ckeditor3/plugins/aspell/spellerpages/controlWindow.js b/phpgwapi/js/ckeditor3/plugins/aspell/spellerpages/controlWindow.js new file mode 100644 index 0000000000..32ab75989e --- /dev/null +++ b/phpgwapi/js/ckeditor3/plugins/aspell/spellerpages/controlWindow.js @@ -0,0 +1,86 @@ +//////////////////////////////////////////////////// +// controlWindow object +//////////////////////////////////////////////////// +function controlWindow( controlForm ) { + // private properties + this._form = controlForm; + + // public properties + this.windowType = "controlWindow"; + this.noSuggestionSelection = "- No suggestions -"; + // set up the properties for elements of the given control form + this.suggestionList = this._form.sugg; + this.evaluatedText = this._form.misword; + this.replacementText = this._form.txtsugg; + this.undoButton = this._form.btnUndo; + + // public methods + this.addSuggestion = addSuggestion; + this.clearSuggestions = clearSuggestions; + this.selectDefaultSuggestion = selectDefaultSuggestion; + this.resetForm = resetForm; + this.setSuggestedText = setSuggestedText; + this.enableUndo = enableUndo; + this.disableUndo = disableUndo; +} + +function resetForm() { + if( this._form ) { + this._form.reset(); + } +} + +function setSuggestedText() { + var slct = this.suggestionList; + var txt = this.replacementText; + var str = ""; + if( (slct.options[0].text) && slct.options[0].text != this.noSuggestionSelection ) { + str = slct.options[slct.selectedIndex].text; + } + txt.value = str; +} + +function selectDefaultSuggestion() { + var slct = this.suggestionList; + var txt = this.replacementText; + if( slct.options.length == 0 ) { + this.addSuggestion( this.noSuggestionSelection ); + } else { + slct.options[0].selected = true; + } + this.setSuggestedText(); +} + +function addSuggestion( sugg_text ) { + var slct = this.suggestionList; + if( sugg_text ) { + var i = slct.options.length; + var newOption = new Option( sugg_text, 'sugg_text'+i ); + slct.options[i] = newOption; + } +} + +function clearSuggestions() { + var slct = this.suggestionList; + for( var j = slct.length - 1; j > -1; j-- ) { + if( slct.options[j] ) { + slct.options[j] = null; + } + } +} + +function enableUndo() { + if( this.undoButton ) { + if( this.undoButton.disabled == true ) { + this.undoButton.disabled = false; + } + } +} + +function disableUndo() { + if( this.undoButton ) { + if( this.undoButton.disabled == false ) { + this.undoButton.disabled = true; + } + } +} diff --git a/phpgwapi/js/ckeditor3/plugins/aspell/spellerpages/controls.html b/phpgwapi/js/ckeditor3/plugins/aspell/spellerpages/controls.html new file mode 100644 index 0000000000..39c9e68077 --- /dev/null +++ b/phpgwapi/js/ckeditor3/plugins/aspell/spellerpages/controls.html @@ -0,0 +1,153 @@ + + + + + + + +
+ + + + + + + + + + + + + + + + + + +
Not in dictionary:
Change to:
+ + + + + + + +
+ +
+ +
+
   + + + + + + + + + + + + + + + + + + + + + + +
+ +    + +
+ +    + +
+ +    + +
+
+
+ + diff --git a/phpgwapi/js/ckeditor3/plugins/aspell/spellerpages/server-scripts/spellchecker.php b/phpgwapi/js/ckeditor3/plugins/aspell/spellerpages/server-scripts/spellchecker.php new file mode 100644 index 0000000000..fa063a1329 --- /dev/null +++ b/phpgwapi/js/ckeditor3/plugins/aspell/spellerpages/server-scripts/spellchecker.php @@ -0,0 +1,236 @@ + array( + 'currentapp' => 'home', + 'noheader' => true, + 'autocreate_session_callback' => 'deny_no_egw_session', + ) +); +// will not continue, unless the header get's included and there is a valid eGW session +require('../../../../../../../header.inc.php'); + +if (!empty($GLOBALS['egw_info']['user']['preferences']['common']['spellchecker_lang'])) +{ + $lang = $GLOBALS['egw_info']['user']['preferences']['common']['spellchecker_lang']; +} +else +{ + $lang = $GLOBALS['egw_info']['user']['preferences']['common']['lang']; +} + +$aspell_opts = '-a '.escapeshellarg('--lang='.$lang).' --encoding=utf-8 -H --rem-sgml-check=alt'; // by FredCK + +$tempfiledir = "./"; + +$spellercss = '../spellerStyle.css'; // by FredCK +$word_win_src = '../wordWindow.js'; // by FredCK + +$textinputs = $_POST['textinputs']; # array +$input_separator = "A"; + +# set the JavaScript variable to the submitted text. +# textinputs is an array, each element corresponding to the (url-encoded) +# value of the text control submitted for spell-checking +function print_textinputs_var() { + global $textinputs; + foreach( $textinputs as $key=>$val ) { + # $val = str_replace( "'", "%27", $val ); + echo "textinputs[$key] = decodeURIComponent(\"" . $val . "\");\n"; + } +} + +# make declarations for the text input index +function print_textindex_decl( $text_input_idx ) { + echo "words[$text_input_idx] = [];\n"; + echo "suggs[$text_input_idx] = [];\n"; +} + +# set an element of the JavaScript 'words' array to a misspelled word +function print_words_elem( $word, $index, $text_input_idx ) { + echo "words[$text_input_idx][$index] = '" . escape_quote( $word ) . "';\n"; +} + + +# set an element of the JavaScript 'suggs' array to a list of suggestions +function print_suggs_elem( $suggs, $index, $text_input_idx ) { + echo "suggs[$text_input_idx][$index] = ["; + foreach( $suggs as $key=>$val ) { + if( $val ) { + echo "'" . escape_quote( $val ) . "'"; + if ( $key+1 < count( $suggs )) { + echo ", "; + } + } + } + echo "];\n"; +} + +# escape single quote +function escape_quote( $str ) { + return preg_replace ( "/'/", "\\'", $str ); +} + + +# handle a server-side error. +function error_handler( $err ) { + echo "error = '" . preg_replace( "/['\\\\]/", "\\\\$0", $err ) . "';\n"; +} + +## get the list of misspelled words. Put the results in the javascript words array +## for each misspelled word, get suggestions and put in the javascript suggs array +function print_checker_results() { + + global $aspell_prog; + global $aspell_opts; + global $tempfiledir; + global $textinputs; + global $input_separator; + $aspell_err = ""; + if (!empty($GLOBALS['egw_info']['server']['aspell_path']) && + is_executable($GLOBALS['egw_info']['server']['aspell_path'])) + { + $aspell_prog = $GLOBALS['egw_info']['server']['aspell_path']; + } + else // little fallback that might save linux users + { + $aspell_prog = 'aspell'; + } + + # create temp file + // use EGroupware's temp_dir + $tempfile = tempnam( $GLOBALS['egw_info']['server']['temp_dir'], 'aspell_data_' ); + + # open temp file, add the submitted text. + if( $fh = fopen( $tempfile, 'w' )) { + for( $i = 0; $i < count( $textinputs ); $i++ ) { + $text = urldecode( $textinputs[$i] ); + + // Strip all tags for the text. (by FredCK - #339 / #681) + $text = preg_replace( "/<[^>]+>/", " ", $text ) ; + + $lines = explode( "\n", $text ); + fwrite ( $fh, "%\n" ); # exit terse mode + fwrite ( $fh, "^$input_separator\n" ); + fwrite ( $fh, "!\n" ); # enter terse mode + foreach( $lines as $key=>$value ) { + # use carat on each line to escape possible aspell commands + fwrite( $fh, "^$value\n" ); + } + } + fclose( $fh ); + + # exec aspell command - redirect STDERR to STDOUT + $cmd = "$aspell_prog $aspell_opts < $tempfile 2>&1"; + if( $aspellret = shell_exec( $cmd )) { + $linesout = explode( "\n", $aspellret ); + $index = 0; + $text_input_index = -1; + # parse each line of aspell return + foreach( $linesout as $key=>$val ) { + $chardesc = substr( $val, 0, 1 ); + # if '&', then not in dictionary but has suggestions + # if '#', then not in dictionary and no suggestions + # if '*', then it is a delimiter between text inputs + # if '@' then version info + if( $chardesc == '&' || $chardesc == '#' ) { + $line = explode( " ", $val, 5 ); + print_words_elem( $line[1], $index, $text_input_index ); + if( isset( $line[4] )) { + $suggs = explode( ", ", $line[4] ); + } else { + $suggs = array(); + } + print_suggs_elem( $suggs, $index, $text_input_index ); + $index++; + } elseif( $chardesc == '*' ) { + $text_input_index++; + print_textindex_decl( $text_input_index ); + $index = 0; + } elseif( $chardesc != '@' && $chardesc != "" ) { + # assume this is error output + $aspell_err .= $val; + } + } + if( $aspell_err ) { + $aspell_err = "Error executing `$cmd`\\n$aspell_err"; + error_handler( $aspell_err ); + } + } else { + error_handler( "System error: Aspell program execution failed (`$cmd`)" ); + } + } else { + error_handler( "System error: Could not open file '$tempfile' for writing" ); + } + + # close temp file, delete file + unlink( $tempfile ); +} + + +?> + + + + + + + + + + + + + + + diff --git a/phpgwapi/js/ckeditor3/plugins/aspell/spellerpages/spellChecker.js b/phpgwapi/js/ckeditor3/plugins/aspell/spellerpages/spellChecker.js new file mode 100644 index 0000000000..cf22262ba0 --- /dev/null +++ b/phpgwapi/js/ckeditor3/plugins/aspell/spellerpages/spellChecker.js @@ -0,0 +1,461 @@ +//////////////////////////////////////////////////// +// spellChecker.js +// +// spellChecker object +// +// This file is sourced on web pages that have a textarea object to evaluate +// for spelling. It includes the implementation for the spellCheckObject. +// +//////////////////////////////////////////////////// + + +// constructor +function spellChecker( textObject ) { + + // public properties - configurable + this.popUpUrl = '/speller/spellchecker.html'; // by FredCK +// this.popUpUrl = 'fck_spellerpages/spellerpages/spellchecker.html'; // by FredCK + this.popUpName = 'spellchecker'; + this.popUpProps = "menu=no,width=440,height=350,top=70,left=120,resizable=yes,status=yes"; // by FredCK +// this.popUpProps = null ; // by FredCK + this.spellCheckScript = '/speller/server-scripts/spellchecker.php'; // by FredCK + //this.spellCheckScript = '/cgi-bin/spellchecker.pl'; + + // values used to keep track of what happened to a word + this.replWordFlag = "R"; // single replace + this.ignrWordFlag = "I"; // single ignore + this.replAllFlag = "RA"; // replace all occurances + this.ignrAllFlag = "IA"; // ignore all occurances + this.fromReplAll = "~RA"; // an occurance of a "replace all" word + this.fromIgnrAll = "~IA"; // an occurance of a "ignore all" word + // properties set at run time + this.wordFlags = new Array(); + this.currentTextIndex = 0; + this.currentWordIndex = 0; + this.spellCheckerWin = null; + this.controlWin = null; + this.wordWin = null; + this.textArea = textObject; // deprecated + this.textInputs = arguments; + + // private methods + this._spellcheck = _spellcheck; + this._getSuggestions = _getSuggestions; + this._setAsIgnored = _setAsIgnored; + this._getTotalReplaced = _getTotalReplaced; + this._setWordText = _setWordText; + this._getFormInputs = _getFormInputs; + + // public methods + this.openChecker = openChecker; + this.startCheck = startCheck; + this.checkTextBoxes = checkTextBoxes; + this.checkTextAreas = checkTextAreas; + this.spellCheckAll = spellCheckAll; + this.ignoreWord = ignoreWord; + this.ignoreAll = ignoreAll; + this.replaceWord = replaceWord; + this.replaceAll = replaceAll; + this.terminateSpell = terminateSpell; + this.undo = undo; + + // set the current window's "speller" property to the instance of this class. + // this object can now be referenced by child windows/frames. + window.speller = this; +} + +// call this method to check all text boxes (and only text boxes) in the HTML document +function checkTextBoxes() { + this.textInputs = this._getFormInputs( "^text$" ); + this.openChecker(); +} + +// call this method to check all textareas (and only textareas ) in the HTML document +function checkTextAreas() { + this.textInputs = this._getFormInputs( "^textarea$" ); + this.openChecker(); +} + +// call this method to check all text boxes and textareas in the HTML document +function spellCheckAll() { + this.textInputs = this._getFormInputs( "^text(area)?$" ); + this.openChecker(); +} + +// call this method to check text boxe(s) and/or textarea(s) that were passed in to the +// object's constructor or to the textInputs property +function openChecker() { + this.spellCheckerWin = window.open( this.popUpUrl, this.popUpName, this.popUpProps ); + if( !this.spellCheckerWin.opener ) { + this.spellCheckerWin.opener = window; + } +} + +function startCheck( wordWindowObj, controlWindowObj ) { + + // set properties from args + this.wordWin = wordWindowObj; + this.controlWin = controlWindowObj; + + // reset properties + this.wordWin.resetForm(); + this.controlWin.resetForm(); + this.currentTextIndex = 0; + this.currentWordIndex = 0; + // initialize the flags to an array - one element for each text input + this.wordFlags = new Array( this.wordWin.textInputs.length ); + // each element will be an array that keeps track of each word in the text + for( var i=0; i wi ) || i > ti ) { + // future word: set as "from ignore all" if + // 1) do not already have a flag and + // 2) have the same value as current word + if(( this.wordWin.getTextVal( i, j ) == s_word_to_repl ) + && ( !this.wordFlags[i][j] )) { + this._setAsIgnored( i, j, this.fromIgnrAll ); + } + } + } + } + + // finally, move on + this.currentWordIndex++; + this._spellcheck(); + return true; +} + +function replaceWord() { + var wi = this.currentWordIndex; + var ti = this.currentTextIndex; + if( !this.wordWin ) { + alert( 'Error: Word frame not available.' ); + return false; + } + if( !this.wordWin.getTextVal( ti, wi )) { + alert( 'Error: "Not in dictionary" text is missing' ); + return false; + } + if( !this.controlWin.replacementText ) { + return false ; + } + var txt = this.controlWin.replacementText; + if( txt.value ) { + var newspell = new String( txt.value ); + if( this._setWordText( ti, wi, newspell, this.replWordFlag )) { + this.currentWordIndex++; + this._spellcheck(); + } + } + return true; +} + +function replaceAll() { + var ti = this.currentTextIndex; + var wi = this.currentWordIndex; + if( !this.wordWin ) { + alert( 'Error: Word frame not available.' ); + return false; + } + var s_word_to_repl = this.wordWin.getTextVal( ti, wi ); + if( !s_word_to_repl ) { + alert( 'Error: "Not in dictionary" text is missing' ); + return false; + } + var txt = this.controlWin.replacementText; + if( !txt.value ) return false; + var newspell = new String( txt.value ); + + // set this word as a "replace all" word. + this._setWordText( ti, wi, newspell, this.replAllFlag ); + + // loop through all the words after this word + for( var i = ti; i < this.wordWin.textInputs.length; i++ ) { + for( var j = 0; j < this.wordWin.totalWords( i ); j++ ) { + if(( i == ti && j > wi ) || i > ti ) { + // future word: set word text to s_word_to_repl if + // 1) do not already have a flag and + // 2) have the same value as s_word_to_repl + if(( this.wordWin.getTextVal( i, j ) == s_word_to_repl ) + && ( !this.wordFlags[i][j] )) { + this._setWordText( i, j, newspell, this.fromReplAll ); + } + } + } + } + + // finally, move on + this.currentWordIndex++; + this._spellcheck(); + return true; +} + +function terminateSpell() { + // called when we have reached the end of the spell checking. + var msg = ""; // by FredCK + var numrepl = this._getTotalReplaced(); + if( numrepl == 0 ) { + // see if there were no misspellings to begin with + if( !this.wordWin ) { + msg = ""; + } else { + if( this.wordWin.totalMisspellings() ) { +// msg += "No words changed."; // by FredCK + msg += FCKLang.DlgSpellNoChanges ; // by FredCK + } else { +// msg += "No misspellings found."; // by FredCK + msg += FCKLang.DlgSpellNoMispell ; // by FredCK + } + } + } else if( numrepl == 1 ) { +// msg += "One word changed."; // by FredCK + msg += FCKLang.DlgSpellOneChange ; // by FredCK + } else { +// msg += numrepl + " words changed."; // by FredCK + msg += FCKLang.DlgSpellManyChanges.replace( /%1/g, numrepl ) ; + } + if( msg ) { +// msg += "\n"; // by FredCK + alert( msg ); + } + + if( numrepl > 0 ) { + // update the text field(s) on the opener window + for( var i = 0; i < this.textInputs.length; i++ ) { + // this.textArea.value = this.wordWin.text; + if( this.wordWin ) { + if( this.wordWin.textInputs[i] ) { + this.textInputs[i].value = this.wordWin.textInputs[i]; + } + } + } + } + + // return back to the calling window +// this.spellCheckerWin.close(); // by FredCK + if ( typeof( this.OnFinished ) == 'function' ) // by FredCK + this.OnFinished(numrepl) ; // by FredCK + + return true; +} + +function undo() { + // skip if this is the first word! + var ti = this.currentTextIndex; + var wi = this.currentWordIndex; + + if( this.wordWin.totalPreviousWords( ti, wi ) > 0 ) { + this.wordWin.removeFocus( ti, wi ); + + // go back to the last word index that was acted upon + do { + // if the current word index is zero then reset the seed + if( this.currentWordIndex == 0 && this.currentTextIndex > 0 ) { + this.currentTextIndex--; + this.currentWordIndex = this.wordWin.totalWords( this.currentTextIndex )-1; + if( this.currentWordIndex < 0 ) this.currentWordIndex = 0; + } else { + if( this.currentWordIndex > 0 ) { + this.currentWordIndex--; + } + } + } while ( + this.wordWin.totalWords( this.currentTextIndex ) == 0 + || this.wordFlags[this.currentTextIndex][this.currentWordIndex] == this.fromIgnrAll + || this.wordFlags[this.currentTextIndex][this.currentWordIndex] == this.fromReplAll + ); + + var text_idx = this.currentTextIndex; + var idx = this.currentWordIndex; + var preReplSpell = this.wordWin.originalSpellings[text_idx][idx]; + + // if we got back to the first word then set the Undo button back to disabled + if( this.wordWin.totalPreviousWords( text_idx, idx ) == 0 ) { + this.controlWin.disableUndo(); + } + + var i, j, origSpell ; + // examine what happened to this current word. + switch( this.wordFlags[text_idx][idx] ) { + // replace all: go through this and all the future occurances of the word + // and revert them all to the original spelling and clear their flags + case this.replAllFlag : + for( i = text_idx; i < this.wordWin.textInputs.length; i++ ) { + for( j = 0; j < this.wordWin.totalWords( i ); j++ ) { + if(( i == text_idx && j >= idx ) || i > text_idx ) { + origSpell = this.wordWin.originalSpellings[i][j]; + if( origSpell == preReplSpell ) { + this._setWordText ( i, j, origSpell, undefined ); + } + } + } + } + break; + + // ignore all: go through all the future occurances of the word + // and clear their flags + case this.ignrAllFlag : + for( i = text_idx; i < this.wordWin.textInputs.length; i++ ) { + for( j = 0; j < this.wordWin.totalWords( i ); j++ ) { + if(( i == text_idx && j >= idx ) || i > text_idx ) { + origSpell = this.wordWin.originalSpellings[i][j]; + if( origSpell == preReplSpell ) { + this.wordFlags[i][j] = undefined; + } + } + } + } + break; + + // replace: revert the word to its original spelling + case this.replWordFlag : + this._setWordText ( text_idx, idx, preReplSpell, undefined ); + break; + } + + // For all four cases, clear the wordFlag of this word. re-start the process + this.wordFlags[text_idx][idx] = undefined; + this._spellcheck(); + } +} + +function _spellcheck() { + var ww = this.wordWin; + + // check if this is the last word in the current text element + if( this.currentWordIndex == ww.totalWords( this.currentTextIndex) ) { + this.currentTextIndex++; + this.currentWordIndex = 0; + // keep going if we're not yet past the last text element + if( this.currentTextIndex < this.wordWin.textInputs.length ) { + this._spellcheck(); + return; + } else { + this.terminateSpell(); + return; + } + } + + // if this is after the first one make sure the Undo button is enabled + if( this.currentWordIndex > 0 ) { + this.controlWin.enableUndo(); + } + + // skip the current word if it has already been worked on + if( this.wordFlags[this.currentTextIndex][this.currentWordIndex] ) { + // increment the global current word index and move on. + this.currentWordIndex++; + this._spellcheck(); + } else { + var evalText = ww.getTextVal( this.currentTextIndex, this.currentWordIndex ); + if( evalText ) { + this.controlWin.evaluatedText.value = evalText; + ww.setFocus( this.currentTextIndex, this.currentWordIndex ); + this._getSuggestions( this.currentTextIndex, this.currentWordIndex ); + } + } +} + +function _getSuggestions( text_num, word_num ) { + this.controlWin.clearSuggestions(); + // add suggestion in list for each suggested word. + // get the array of suggested words out of the + // three-dimensional array containing all suggestions. + var a_suggests = this.wordWin.suggestions[text_num][word_num]; + if( a_suggests ) { + // got an array of suggestions. + for( var ii = 0; ii < a_suggests.length; ii++ ) { + this.controlWin.addSuggestion( a_suggests[ii] ); + } + } + this.controlWin.selectDefaultSuggestion(); +} + +function _setAsIgnored( text_num, word_num, flag ) { + // set the UI + this.wordWin.removeFocus( text_num, word_num ); + // do the bookkeeping + this.wordFlags[text_num][word_num] = flag; + return true; +} + +function _getTotalReplaced() { + var i_replaced = 0; + for( var i = 0; i < this.wordFlags.length; i++ ) { + for( var j = 0; j < this.wordFlags[i].length; j++ ) { + if(( this.wordFlags[i][j] == this.replWordFlag ) + || ( this.wordFlags[i][j] == this.replAllFlag ) + || ( this.wordFlags[i][j] == this.fromReplAll )) { + i_replaced++; + } + } + } + return i_replaced; +} + +function _setWordText( text_num, word_num, newText, flag ) { + // set the UI and form inputs + this.wordWin.setText( text_num, word_num, newText ); + // keep track of what happened to this word: + this.wordFlags[text_num][word_num] = flag; + return true; +} + +function _getFormInputs( inputPattern ) { + var inputs = new Array(); + for( var i = 0; i < document.forms.length; i++ ) { + for( var j = 0; j < document.forms[i].elements.length; j++ ) { + if( document.forms[i].elements[j].type.match( inputPattern )) { + inputs[inputs.length] = document.forms[i].elements[j]; + } + } + } + return inputs; +} diff --git a/phpgwapi/js/ckeditor3/plugins/aspell/spellerpages/spellchecker.html b/phpgwapi/js/ckeditor3/plugins/aspell/spellerpages/spellchecker.html new file mode 100644 index 0000000000..d803fc34bd --- /dev/null +++ b/phpgwapi/js/ckeditor3/plugins/aspell/spellerpages/spellchecker.html @@ -0,0 +1,71 @@ + + + + + + +Speller Pages + + + + + + diff --git a/phpgwapi/js/ckeditor3/plugins/aspell/spellerpages/spellerStyle.css b/phpgwapi/js/ckeditor3/plugins/aspell/spellerpages/spellerStyle.css new file mode 100644 index 0000000000..d747e315c7 --- /dev/null +++ b/phpgwapi/js/ckeditor3/plugins/aspell/spellerpages/spellerStyle.css @@ -0,0 +1,49 @@ +.blend { + font-family: courier new; + font-size: 10pt; + border: 0; + margin-bottom:-1; +} +.normalLabel { + font-size:8pt; +} +.normalText { + font-family:arial, helvetica, sans-serif; + font-size:10pt; + color:000000; + background-color:FFFFFF; +} +.plainText { + font-family: courier new, courier, monospace; + font-size: 10pt; + color:000000; + background-color:FFFFFF; +} +.controlWindowBody { + font-family:arial, helvetica, sans-serif; + font-size:8pt; + padding-top: 7px; /* by FredCK */ + margin: 0px; /* by FredCK */ + color:000000; + background-color:DADADA; +} +.readonlyInput { + background-color:DADADA; + color:000000; + font-size:8pt; + width:392px; +} +.textDefault { + font-size:8pt; + width: 200px; +} +.buttonDefault { + width:90px; + height:22px; + font-size:8pt; +} +.suggSlct { + width:200px; + margin-top:2; + font-size:8pt; +} diff --git a/phpgwapi/js/ckeditor3/plugins/aspell/spellerpages/wordWindow.js b/phpgwapi/js/ckeditor3/plugins/aspell/spellerpages/wordWindow.js new file mode 100644 index 0000000000..7990296a20 --- /dev/null +++ b/phpgwapi/js/ckeditor3/plugins/aspell/spellerpages/wordWindow.js @@ -0,0 +1,272 @@ +//////////////////////////////////////////////////// +// wordWindow object +//////////////////////////////////////////////////// +function wordWindow() { + // private properties + this._forms = []; + + // private methods + this._getWordObject = _getWordObject; + //this._getSpellerObject = _getSpellerObject; + this._wordInputStr = _wordInputStr; + this._adjustIndexes = _adjustIndexes; + this._isWordChar = _isWordChar; + this._lastPos = _lastPos; + + // public properties + this.wordChar = /[a-zA-Z]/; + this.windowType = "wordWindow"; + this.originalSpellings = new Array(); + this.suggestions = new Array(); + this.checkWordBgColor = "pink"; + this.normWordBgColor = "white"; + this.text = ""; + this.textInputs = new Array(); + this.indexes = new Array(); + //this.speller = this._getSpellerObject(); + + // public methods + this.resetForm = resetForm; + this.totalMisspellings = totalMisspellings; + this.totalWords = totalWords; + this.totalPreviousWords = totalPreviousWords; + //this.getTextObjectArray = getTextObjectArray; + this.getTextVal = getTextVal; + this.setFocus = setFocus; + this.removeFocus = removeFocus; + this.setText = setText; + //this.getTotalWords = getTotalWords; + this.writeBody = writeBody; + this.printForHtml = printForHtml; +} + +function resetForm() { + if( this._forms ) { + for( var i = 0; i < this._forms.length; i++ ) { + this._forms[i].reset(); + } + } + return true; +} + +function totalMisspellings() { + var total_words = 0; + for( var i = 0; i < this.textInputs.length; i++ ) { + total_words += this.totalWords( i ); + } + return total_words; +} + +function totalWords( textIndex ) { + return this.originalSpellings[textIndex].length; +} + +function totalPreviousWords( textIndex, wordIndex ) { + var total_words = 0; + for( var i = 0; i <= textIndex; i++ ) { + for( var j = 0; j < this.totalWords( i ); j++ ) { + if( i == textIndex && j == wordIndex ) { + break; + } else { + total_words++; + } + } + } + return total_words; +} + +//function getTextObjectArray() { +// return this._form.elements; +//} + +function getTextVal( textIndex, wordIndex ) { + var word = this._getWordObject( textIndex, wordIndex ); + if( word ) { + return word.value; + } +} + +function setFocus( textIndex, wordIndex ) { + var word = this._getWordObject( textIndex, wordIndex ); + if( word ) { + if( word.type == "text" ) { + word.focus(); + word.style.backgroundColor = this.checkWordBgColor; + } + } +} + +function removeFocus( textIndex, wordIndex ) { + var word = this._getWordObject( textIndex, wordIndex ); + if( word ) { + if( word.type == "text" ) { + word.blur(); + word.style.backgroundColor = this.normWordBgColor; + } + } +} + +function setText( textIndex, wordIndex, newText ) { + var word = this._getWordObject( textIndex, wordIndex ); + var beginStr; + var endStr; + if( word ) { + var pos = this.indexes[textIndex][wordIndex]; + var oldText = word.value; + // update the text given the index of the string + beginStr = this.textInputs[textIndex].substring( 0, pos ); + endStr = this.textInputs[textIndex].substring( + pos + oldText.length, + this.textInputs[textIndex].length + ); + this.textInputs[textIndex] = beginStr + newText + endStr; + + // adjust the indexes on the stack given the differences in + // length between the new word and old word. + var lengthDiff = newText.length - oldText.length; + this._adjustIndexes( textIndex, wordIndex, lengthDiff ); + + word.size = newText.length; + word.value = newText; + this.removeFocus( textIndex, wordIndex ); + } +} + + +function writeBody() { + var d = window.document; + var is_html = false; + + d.open(); + + // iterate through each text input. + for( var txtid = 0; txtid < this.textInputs.length; txtid++ ) { + var end_idx = 0; + var begin_idx = 0; + d.writeln( '
' ); + var wordtxt = this.textInputs[txtid]; + this.indexes[txtid] = []; + + if( wordtxt ) { + var orig = this.originalSpellings[txtid]; + if( !orig ) break; + + //!!! plain text, or HTML mode? + d.writeln( '
' ); + // iterate through each occurrence of a misspelled word. + for( var i = 0; i < orig.length; i++ ) { + // find the position of the current misspelled word, + // starting at the last misspelled word. + // and keep looking if it's a substring of another word + do { + begin_idx = wordtxt.indexOf( orig[i], end_idx ); + end_idx = begin_idx + orig[i].length; + // word not found? messed up! + if( begin_idx == -1 ) break; + // look at the characters immediately before and after + // the word. If they are word characters we'll keep looking. + var before_char = wordtxt.charAt( begin_idx - 1 ); + var after_char = wordtxt.charAt( end_idx ); + } while ( + this._isWordChar( before_char ) + || this._isWordChar( after_char ) + ); + + // keep track of its position in the original text. + this.indexes[txtid][i] = begin_idx; + + // write out the characters before the current misspelled word + for( var j = this._lastPos( txtid, i ); j < begin_idx; j++ ) { + // !!! html mode? make it html compatible + d.write( this.printForHtml( wordtxt.charAt( j ))); + } + + // write out the misspelled word. + d.write( this._wordInputStr( orig[i] )); + + // if it's the last word, write out the rest of the text + if( i == orig.length-1 ){ + d.write( printForHtml( wordtxt.substr( end_idx ))); + } + } + + d.writeln( '
' ); + + } + d.writeln( '
' ); + } + //for ( var j = 0; j < d.forms.length; j++ ) { + // alert( d.forms[j].name ); + // for( var k = 0; k < d.forms[j].elements.length; k++ ) { + // alert( d.forms[j].elements[k].name + ": " + d.forms[j].elements[k].value ); + // } + //} + + // set the _forms property + this._forms = d.forms; + d.close(); +} + +// return the character index in the full text after the last word we evaluated +function _lastPos( txtid, idx ) { + if( idx > 0 ) + return this.indexes[txtid][idx-1] + this.originalSpellings[txtid][idx-1].length; + else + return 0; +} + +function printForHtml( n ) { + return n ; // by FredCK +/* + var htmlstr = n; + if( htmlstr.length == 1 ) { + // do simple case statement if it's just one character + switch ( n ) { + case "\n": + htmlstr = '
'; + break; + case "<": + htmlstr = '<'; + break; + case ">": + htmlstr = '>'; + break; + } + return htmlstr; + } else { + htmlstr = htmlstr.replace( //g, '>' ); + htmlstr = htmlstr.replace( /\n/g, '
' ); + return htmlstr; + } +*/ +} + +function _isWordChar( letter ) { + if( letter.search( this.wordChar ) == -1 ) { + return false; + } else { + return true; + } +} + +function _getWordObject( textIndex, wordIndex ) { + if( this._forms[textIndex] ) { + if( this._forms[textIndex].elements[wordIndex] ) { + return this._forms[textIndex].elements[wordIndex]; + } + } + return null; +} + +function _wordInputStr( word ) { + var str = ''; + return str; +} + +function _adjustIndexes( textIndex, wordIndex, lengthDiff ) { + for( var i = wordIndex + 1; i < this.originalSpellings[textIndex].length; i++ ) { + this.indexes[textIndex][i] = this.indexes[textIndex][i] + lengthDiff; + } +}