Index: library/src/java/org/sakaiproject/fckeditor/spellcheck/GoogleSpellChecker.java =================================================================== --- library/src/java/org/sakaiproject/fckeditor/spellcheck/GoogleSpellChecker.java (revision 0) +++ library/src/java/org/sakaiproject/fckeditor/spellcheck/GoogleSpellChecker.java (revision 0) @@ -0,0 +1,154 @@ +/********************************************************************************** + * $URL$ + * $Id$ + *********************************************************************************** + * + * Copyright (c) 2013 The Sakai Foundation + * + * Licensed under the Educational Community License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.opensource.org/licenses/ECL-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **********************************************************************************/ + +package org.sakaiproject.fckeditor.spellcheck; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.jsoup.Jsoup; +import org.sakaiproject.util.ResourceLoader; +import org.xeustechnologies.googleapi.spelling.*; + +import java.net.URLDecoder; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; + +public class GoogleSpellChecker { + protected static Log log = LogFactory.getLog(GoogleSpellChecker.class); + + private String textInput; + private JSONArray words = new JSONArray(); + private JSONArray suggestions = new JSONArray(); + private JSONArray suggestionsArray = new JSONArray(); + + private JSONArray wordsArray = new JSONArray(); + + private String error; + public GoogleSpellChecker(String textInput) { + this.textInput = textInput; + words.add(0, wordsArray); + suggestions.add(0,suggestionsArray); + + try { + textInput = Jsoup.parse(URLDecoder.decode(textInput, "UTF-8")).body().text(); + SpellChecker spellCheck = new SpellChecker(); + + spellCheck.setLanguage(getLanguageFromUserLocale()); + SpellRequest spellRequest = new SpellRequest(); + + log.debug("google spell checker initialized"); + + spellRequest.setText(textInput); + SpellResponse spellResponse = spellCheck.check(spellRequest); + + log.debug("google spell checker response received for input: " + spellRequest.getText()); + convertResponseToJson(textInput, spellResponse); + + } catch (Exception e) { + log.warn("spellchecker error parsing input: " + textInput + ":" + e.getMessage(), e ); + error = e.getLocalizedMessage(); + return; + } + + + } + + protected Language getLanguageFromUserLocale() { + String userLocaleLanguageCode = getUserLanguageCode(); + for (Language googleSpellLanguage : Language.values()) { + if (googleSpellLanguage.code().equals(userLocaleLanguageCode)) { + return googleSpellLanguage; + } + } + + log.debug("could not find a google spell check language to match the Sakai user language of " + userLocaleLanguageCode + + " defaulting to English because we have the biggest guns"); + return Language.ENGLISH; + } + + public String getUserLanguageCode() { + Locale locale = (new ResourceLoader()).getLocale(); + return locale.getLanguage(); + } + + protected void convertResponseToJson(String textInput, SpellResponse spellResponse) { + + + + int index = 0; + if(spellResponse.getCorrections() != null){ + for(SpellCorrection correction : spellResponse.getCorrections()){ + JSONArray currentSuggestionsArray = new JSONArray(); + + wordsArray.add(index,textInput.substring(correction.getOffset(), correction.getOffset() + correction.getLength())); + + List suggestionList = Arrays.asList(correction.getWords()); + + if(!suggestionList.isEmpty()){ + + for(Iterator it = suggestionList.iterator(); it.hasNext();){ + currentSuggestionsArray.add(it.next().toString()); + } + } + suggestionsArray.add(index, currentSuggestionsArray); + index++; + } + } + log.debug("words json=" + words.toJSONString()); + log.debug("suggestions json=" + suggestions.toJSONString()); + } + + public String getTextInput() { + return textInput; + } + + public void setTextInput(String textInput) { + this.textInput = textInput; + } + + public String getWordsAsJSON() { + return words.toJSONString(); + } + + public String getSuggestionsAsJSON() { + return suggestions.toJSONString(); + } + + public JSONArray getWords() { + return words; + } + + public JSONArray getSuggestions() { + return suggestions; + } + + public String getError() { + return error; + } + + public boolean hasError() { + return (error != null && error.length() > 0); + } +} Property changes on: library/src/java/org/sakaiproject/fckeditor/spellcheck/GoogleSpellChecker.java ___________________________________________________________________ Added: svn:keywords + Date Revision Author HeadURL Id Added: svn:eol-style + native Index: library/src/webapp/editor/ckextraplugins/aspell/dialogs/aspell.js =================================================================== --- library/src/webapp/editor/ckextraplugins/aspell/dialogs/aspell.js (revision 0) +++ library/src/webapp/editor/ckextraplugins/aspell/dialogs/aspell.js (revision 0) @@ -0,0 +1,188 @@ +/********************************************************************************** + * $URL$ + * $Id$ + *********************************************************************************** + * + * Copyright (c) 2013 The Sakai Foundation + * + * Licensed under the Educational Community License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.opensource.org/licenses/ECL-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **********************************************************************************/ +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.jsp'; + 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); + 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: '
' + } + ] + } + ] + }; +}); Property changes on: library/src/webapp/editor/ckextraplugins/aspell/dialogs/aspell.js ___________________________________________________________________ Added: svn:keywords + Date Revision Author HeadURL Id Added: svn:eol-style + native Index: library/src/webapp/editor/ckextraplugins/aspell/spellerpages/spellChecker.js =================================================================== --- library/src/webapp/editor/ckextraplugins/aspell/spellerpages/spellChecker.js (revision 0) +++ library/src/webapp/editor/ckextraplugins/aspell/spellerpages/spellChecker.js (revision 0) @@ -0,0 +1,482 @@ +/********************************************************************************** + * $URL$ + * $Id$ + *********************************************************************************** + * + * Copyright (c) 2013 The Sakai Foundation + * + * Licensed under the Educational Community License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.opensource.org/licenses/ECL-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **********************************************************************************/ + +//////////////////////////////////////////////////// +// 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; +} Property changes on: library/src/webapp/editor/ckextraplugins/aspell/spellerpages/spellChecker.js ___________________________________________________________________ Added: svn:keywords + Date Revision Author HeadURL Id Added: svn:eol-style + native Index: library/src/webapp/editor/ckextraplugins/aspell/spellerpages/controlWindow.js =================================================================== --- library/src/webapp/editor/ckextraplugins/aspell/spellerpages/controlWindow.js (revision 0) +++ library/src/webapp/editor/ckextraplugins/aspell/spellerpages/controlWindow.js (revision 0) @@ -0,0 +1,106 @@ +/********************************************************************************** + * $URL$ + * $Id$ + *********************************************************************************** + * + * Copyright (c) 2013 The Sakai Foundation + * + * Licensed under the Educational Community License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.opensource.org/licenses/ECL-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **********************************************************************************/ +//////////////////////////////////////////////////// +// 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; + } + } +} Property changes on: library/src/webapp/editor/ckextraplugins/aspell/spellerpages/controlWindow.js ___________________________________________________________________ Added: svn:keywords + Date Revision Author HeadURL Id Added: svn:eol-style + native Index: library/src/webapp/editor/ckextraplugins/aspell/spellerpages/controls.html =================================================================== --- library/src/webapp/editor/ckextraplugins/aspell/spellerpages/controls.html (revision 0) +++ library/src/webapp/editor/ckextraplugins/aspell/spellerpages/controls.html (revision 0) @@ -0,0 +1,175 @@ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + +
Not in dictionary:
Change to:
+ + + + + + + +
+ +
+ +
+
   + + + + + + + + + + + + + + + + + + + + + + +
+ +    + +
+ +    + +
+ +    + +
+
+
+ + Property changes on: library/src/webapp/editor/ckextraplugins/aspell/spellerpages/controls.html ___________________________________________________________________ Added: svn:mime-type + text/html Added: svn:keywords + Date Revision Author HeadURL Id Added: svn:eol-style + native Index: library/src/webapp/editor/ckextraplugins/aspell/spellerpages/spellchecker.html =================================================================== --- library/src/webapp/editor/ckextraplugins/aspell/spellerpages/spellchecker.html (revision 0) +++ library/src/webapp/editor/ckextraplugins/aspell/spellerpages/spellchecker.html (revision 0) @@ -0,0 +1,92 @@ + + + + + + +Speller Pages + + + + + + Property changes on: library/src/webapp/editor/ckextraplugins/aspell/spellerpages/spellchecker.html ___________________________________________________________________ Added: svn:mime-type + text/html Added: svn:keywords + Date Revision Author HeadURL Id Added: svn:eol-style + native Index: library/src/webapp/editor/ckextraplugins/aspell/spellerpages/blank.html =================================================================== Property changes on: library/src/webapp/editor/ckextraplugins/aspell/spellerpages/blank.html ___________________________________________________________________ Added: svn:mime-type + text/html Added: svn:keywords + Date Revision Author HeadURL Id Added: svn:eol-style + native Index: library/src/webapp/editor/ckextraplugins/aspell/spellerpages/server-scripts/spellchecker.jsp =================================================================== --- library/src/webapp/editor/ckextraplugins/aspell/spellerpages/server-scripts/spellchecker.jsp (revision 0) +++ library/src/webapp/editor/ckextraplugins/aspell/spellerpages/server-scripts/spellchecker.jsp (revision 0) @@ -0,0 +1,83 @@ + + +<%@ page import="org.sakaiproject.fckeditor.spellcheck.GoogleSpellChecker" %> +<%@ page language="java" session="false" pageEncoding="UTF-8" contentType="text/html; charset=UTF-8" %> +<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %> + +<% + GoogleSpellChecker googleSpellChecker = new GoogleSpellChecker(request.getParameter("textinputs[]")); + pageContext.setAttribute("googleSpellChecker", googleSpellChecker); +%> + + + + + + + + + + + + + + + + Property changes on: library/src/webapp/editor/ckextraplugins/aspell/spellerpages/server-scripts/spellchecker.jsp ___________________________________________________________________ Added: svn:keywords + Date Revision Author HeadURL Id Added: svn:eol-style + native Index: library/src/webapp/editor/ckextraplugins/aspell/spellerpages/spellerStyle.css =================================================================== --- library/src/webapp/editor/ckextraplugins/aspell/spellerpages/spellerStyle.css (revision 0) +++ library/src/webapp/editor/ckextraplugins/aspell/spellerpages/spellerStyle.css (revision 0) @@ -0,0 +1,69 @@ +/********************************************************************************** + * $URL: $ + * $Id: $ + *********************************************************************************** + * + * Copyright (c) 2013 The Sakai Foundation + * + * Licensed under the Educational Community License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.opensource.org/licenses/ECL-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **********************************************************************************/ +.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; +} Property changes on: library/src/webapp/editor/ckextraplugins/aspell/spellerpages/spellerStyle.css ___________________________________________________________________ Added: svn:eol-style + native Index: library/src/webapp/editor/ckextraplugins/aspell/spellerpages/wordWindow.js =================================================================== --- library/src/webapp/editor/ckextraplugins/aspell/spellerpages/wordWindow.js (revision 0) +++ library/src/webapp/editor/ckextraplugins/aspell/spellerpages/wordWindow.js (revision 0) @@ -0,0 +1,292 @@ +/********************************************************************************** + * $URL$ + * $Id$ + *********************************************************************************** + * + * Copyright (c) 2013 The Sakai Foundation + * + * Licensed under the Educational Community License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.opensource.org/licenses/ECL-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **********************************************************************************/ +//////////////////////////////////////////////////// +// 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; + } +} Property changes on: library/src/webapp/editor/ckextraplugins/aspell/spellerpages/wordWindow.js ___________________________________________________________________ Added: svn:keywords + Date Revision Author HeadURL Id Added: svn:eol-style + native Index: library/src/webapp/editor/ckextraplugins/aspell/aspell.css =================================================================== --- library/src/webapp/editor/ckextraplugins/aspell/aspell.css (revision 0) +++ library/src/webapp/editor/ckextraplugins/aspell/aspell.css (revision 0) @@ -0,0 +1,25 @@ +/********************************************************************************** + * $URL: $ + * $Id: $ + *********************************************************************************** + * + * Copyright (c) 2013 The Sakai Foundation + * + * Licensed under the Educational Community License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.opensource.org/licenses/ECL-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **********************************************************************************/ +.cke_editor .cke_button_aspell .cke_icon +{ + background-position: 0 -192px !important; +} + Property changes on: library/src/webapp/editor/ckextraplugins/aspell/aspell.css ___________________________________________________________________ Added: svn:eol-style + native Index: library/src/webapp/editor/ckextraplugins/aspell/plugin.js =================================================================== --- library/src/webapp/editor/ckextraplugins/aspell/plugin.js (revision 0) +++ library/src/webapp/editor/ckextraplugins/aspell/plugin.js (revision 0) @@ -0,0 +1,52 @@ +/********************************************************************************** + * $URL$ + * $Id$ + *********************************************************************************** + * + * Copyright (c) 2013 The Sakai Foundation + * + * Licensed under the Educational Community License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.opensource.org/licenses/ECL-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **********************************************************************************/ +/** + * 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) { + + // Add link dialog code + CKEDITOR.dialog.add('aspell', this.path + 'dialogs/aspell.js'); + + // 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 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'] +}); + Property changes on: library/src/webapp/editor/ckextraplugins/aspell/plugin.js ___________________________________________________________________ Added: svn:keywords + Date Revision Author HeadURL Id Added: svn:eol-style + native Index: library/src/webapp/editor/ckeditor.launch.js =================================================================== --- library/src/webapp/editor/ckeditor.launch.js (revision 121087) +++ library/src/webapp/editor/ckeditor.launch.js (working copy) @@ -77,7 +77,7 @@ // Uncomment the next line and comment the following to enable the default spell checker. // Note that it uses spellchecker.net, displays ads and sends content to remote servers without additional setup. //['Cut','Copy','Paste','PasteText','PasteFromWord','-','Print', 'SpellChecker', 'Scayt'], - ['Cut','Copy','Paste','PasteText','PasteFromWord','-','Print'], + ['Cut','Copy','Paste','PasteText','PasteFromWord','-','Print', 'SpellCheck'], ['Undo','Redo','-','Find','Replace','-','SelectAll','RemoveFormat'], ['NumberedList','BulletedList','-','Outdent','Indent','Blockquote','CreateDiv'], '/', @@ -137,7 +137,8 @@ (function() { CKEDITOR.plugins.addExternal('movieplayer',basePath+'movieplayer/', 'plugin.js'); CKEDITOR.plugins.addExternal('wordcount',basePath+'wordcount/', 'plugin.js'); - CKEDITOR.plugins.addExternal('fmath_formula',basePath+'fmath_formula/', 'plugin.js'); + CKEDITOR.plugins.addExternal('fmath_formula',basePath+'fmath_formula/', 'plugin.js'); + CKEDITOR.plugins.addExternal('aspell',basePath+'aspell/', 'plugin.js'); /* To enable after the deadline uncomment these two lines and add atd-ckeditor to toolbar and to extraPlugins. This also needs extra stylesheets. @@ -150,7 +151,7 @@ //ckconfig.extraPlugins+="movieplayer,wordcount,atd-ckeditor,stylesheetparser"; //ckconfig.contentsCss = basePath+'/atd-ckeditor/atd.css'; - ckconfig.extraPlugins+="movieplayer,wordcount,fmath_formula"; + ckconfig.extraPlugins+="movieplayer,wordcount,fmath_formula,aspell"; })(); CKEDITOR.replace(targetId, ckconfig); Index: library/pom.xml =================================================================== --- library/pom.xml (revision 121087) +++ library/pom.xml (working copy) @@ -34,6 +34,34 @@ sakai-fck-connector war + + commons-logging + commons-logging + + + org.xeustechnologies.google-api + google-api-spelling-java + 1.1 + + + org.jsoup + jsoup + 1.7.2 + + + javax.servlet + jstl + + + com.googlecode.json-simple + json-simple + 1.1.1 + + + taglibs + standard + 1.1.2 + @@ -58,6 +86,34 @@ 1.3 war + + commons-logging + commons-logging + + + org.xeustechnologies.google-api + google-api-spelling-java + 1.1 + + + org.jsoup + jsoup + 1.7.2 + + + javax.servlet + jstl + + + com.googlecode.json-simple + json-simple + 1.1.1 + + + taglibs + standard + 1.1.2 +