diff options
author | andi <andi@splitbrain.org> | 2005-06-07 21:44:56 +0200 |
---|---|---|
committer | andi <andi@splitbrain.org> | 2005-06-07 21:44:56 +0200 |
commit | dc57ef04af4d12bb589c98e7457a22c44a091815 (patch) | |
tree | 8364d81819ec7745ce4e05c149256c40704f19da /lib/scripts | |
parent | 50d915fed681509e84e625a5bfd4e3602dfe98d2 (diff) | |
download | rpg-dc57ef04af4d12bb589c98e7457a22c44a091815.tar.gz rpg-dc57ef04af4d12bb589c98e7457a22c44a091815.tar.bz2 |
AJAX spellchecker #29
This is nearly a complete rewrite of the gmail like AJAX spellchecker
from http://www.broken-notebook.com/spell_checker/index.php
Here are the differences and features
* seemless integrated into DokuWiki
* no need for the pspell extension
* needs GNU aspell installed (not sure about the version I guess
0.60+ for UTF8)
* needs PHP 4.3.0+
* uses SACK for AJAX
* gets errors and suggestions in one transfer
So far only tested in Firefox. It should work in IE, Safari and
Opera 8, too. Please test and report back.
darcs-hash:20050607194456-9977f-f699144d1fd28359742b2ce0f28c839a1f4cefbb.gz
Diffstat (limited to 'lib/scripts')
-rw-r--r-- | lib/scripts/ajax.js | 10 | ||||
-rw-r--r-- | lib/scripts/spellcheck.js | 326 | ||||
-rw-r--r-- | lib/scripts/tw-sack.js | 2 |
3 files changed, 337 insertions, 1 deletions
diff --git a/lib/scripts/ajax.js b/lib/scripts/ajax.js index b3c32e950..0a4183463 100644 --- a/lib/scripts/ajax.js +++ b/lib/scripts/ajax.js @@ -1,3 +1,13 @@ +/** + * AJAX functions for the pagename quicksearch + * + * We're using a global object with self referencing methods + * here to make callbacks work + * + * @license GPL2 (http://www.gnu.org/licenses/gpl.html) + * @author Andreas Gohr <andi@splitbrain.org> + */ + //prepare class function ajax_qsearch_class(){ this.sack = null; diff --git a/lib/scripts/spellcheck.js b/lib/scripts/spellcheck.js new file mode 100644 index 000000000..5b7833601 --- /dev/null +++ b/lib/scripts/spellcheck.js @@ -0,0 +1,326 @@ +/** + * DokuWiki Spellcheck AJAX clientside script + * + * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) + * @author Andreas Gohr <andi@splitbrain.org> + */ + +/** + * Licence info: This spellchecker is inspired by code by Garrison Locke available + * at http://www.broken-notebook.com/spell_checker/index.php (licensed under the Terms + * of an BSD license). The code in this file was nearly completly rewritten for DokuWiki + * and is licensed under GPL version 2 (See COPYING for details). + * + * Original Copyright notice follows: + * + * Copyright (c) 2005, Garrison Locke + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the http://www.broken-notebook.com nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +/** + * Get the X offset of the top left corner of the given object + * + * @author Garrison Locke <http://www.broken-notebook.com> + */ +function findPosX(object){ + var curleft = 0; + var obj = document.getElementById(object); + if (obj.offsetParent){ + while (obj.offsetParent){ + curleft += obj.offsetLeft; + obj = obj.offsetParent; + } + } + else if (obj.x){ + curleft += obj.x; + } + return curleft; +} //end findPosX function + +/** + * Get the Y offset of the top left corner of the given object + * + * @author Garrison Locke <http://www.broken-notebook.com> + */ +function findPosY(object){ + var curtop = 0; + var obj = document.getElementById(object); + if (obj.offsetParent){ + while (obj.offsetParent){ + curtop += obj.offsetTop; + obj = obj.offsetParent; + } + } + else if (obj.y){ + curtop += obj.y; + } + return curtop; +} //end findPosY function + + +/** + * AJAX Spellchecker Class + * + * Note to some function use a hardcoded instance named ajax_spell to make + * references to object members. Used Object-IDs are hardcoded in the init() + * method. + * + * @author Andreas Gohr <andi@splitbrain.org> + * @author Garrison Locke <http://www.broken-notebook.com> + */ +function ajax_spell_class(){ + this.inited = false; + this.handler = DOKU_BASE+'lib/exe/spellcheck.php'; + // to hold the page objects (initialized with init()) + this.textboxObj = null; + this.showboxObj = null; + this.suggestObj = null; + this.actionObj = null; + this.editbarObj = null; + // hold translations + this.txtStart = 'Check Spelling'; + this.txtStop = 'Resume Editing'; + this.txtRun = 'Checking...'; + this.txtNoErr = 'No Mistakes'; + this.txtNoSug = 'No Suggestions'; + + + /** + * Initializes everything + * + * Call after the page was setup. Hardcoded element IDs here. + * + * @author Andreas Gohr <andi@splitbrain.org> + */ + this.init = function(txtStart,txtStop,txtRun,txtNoErr,txtNoSug){ + // don't run twice + if (this.inited) return; + this.inited = true; + + // check for AJAX availability + var ajax = new sack(); + if(ajax.failed) return; + + // get Elements + this.textboxObj = document.getElementById('wikitext'); + this.editbarObj = document.getElementById('wikieditbar'); + this.showboxObj = document.getElementById('spell_result'); + this.suggestObj = document.getElementById('spell_suggest'); + this.actionObj = document.getElementById('spell_action'); + + // set Translation Strings + this.txtStart = txtStart; + this.txtStop = txtStop; + this.txtRun = txtRun; + this.txtNoErr = txtNoErr; + this.txtNoSug = txtNoSug; + + // register click event + document.onclick = this.docClick; + + // register focus event + this.textboxObj.onfocus = this.setState; + + this.setState('start'); + } + + /** + * Eventhandler for click objects anywhere on the document + * + * Disables the suggestion box + * + * @author Andreas Gohr <andi@splitbrain.org> + * @author Garrison Locke <http://www.broken-notebook.com> + */ + this.docClick = function(e){ + // what was clicked? + try{ + target = window.event.srcElement; + }catch(ex){ + target = e.target; + } + + if (target.id != ajax_spell.suggestObj.id){ + ajax_spell.suggestObj.style.display = "none"; + } + } + + /** + * Changes the Spellchecker link according to the given mode + * + * @author Andreas Gohr <andi@splitbrain.org> + */ + this.setState = function(state){ + switch (state){ + case 'stop': + ajax_spell.actionObj.innerHTML = + '<a class="spell_resume" href="javascript:ajax_spell.resume()">'+ajax_spell.txtStop+'</a>'; + break; + case 'noerr': + ajax_spell.actionObj.innerHTML = + '<span class="spell_noerr">'+ajax_spell.txtNoErr+'</span>'; + break; + case 'run': + ajax_spell.actionObj.innerHTML = + '<span class="spell_wait">'+ajax_spell.txtRun+'</span>'; + break; + default: + ajax_spell.actionObj.innerHTML = + '<a class="spell_start" href="javascript:ajax_spell.run()">'+ajax_spell.txtStart+'</a>'; + break; + } + } + + /** + * Replaces a word identified by id with its correction given in word + * + * @author Garrison Locke <http://www.broken-notebook.com> + */ + this.correct = function (id, word){ + var obj = document.getElementById('spell_error'+id); + obj.innerHTML = decodeURI(word); + obj.style.color = "#005500"; + this.suggestObj.style.display = "none"; + } + + // --- Callbacks --- + + /** + * Callback. Called after finishing spellcheck. + * Inside the callback 'this' is the SACK object!! + * + * @author Andreas Gohr <andi@splitbrain.org> + */ + this.start = function(){ + var data = this.response; + var error = data.charAt(0); + data = data.substring(1); + if(error == '1'){ + ajax_spell.setState('stop'); + // replace textbox through div + ajax_spell.showboxObj.innerHTML = data; + ajax_spell.showboxObj.style.width = ajax_spell.textboxObj.style.width; + ajax_spell.showboxObj.style.height = ajax_spell.textboxObj.style.height; + ajax_spell.textboxObj.style.display = 'none'; + ajax_spell.editbarObj.style.visibility = 'hidden'; + ajax_spell.showboxObj.style.display = 'block'; + }else{ + ajax_spell.textboxObj.disabled = false; + ajax_spell.editbarObj.style.visibility = 'visible'; + ajax_spell.setState('noerr'); + } + } + + /** + * Callback. Gets called by resume() - switches back to edit mode + * Inside the callback 'this' is the SACK object!! + * + * @author Andreas Gohr <andi@splitbrain.org> + */ + this.stop = function(){ + var data = this.response; + ajax_spell.setState('start'); + // replace div with textbox again + ajax_spell.textboxObj.value = data; + ajax_spell.textboxObj.disabled = false; + ajax_spell.showboxObj.style.display = 'none'; + ajax_spell.textboxObj.style.display = 'block'; + ajax_spell.editbarObj.style.visibility = 'visible'; + ajax_spell.showboxObj.innerHTML = ''; + } + + // --- Callers --- + + /** + * Starts the spellchecking by sending an AJAX request + * + * @author Andreas Gohr <andi@splitbrain.org> + */ + this.run = function(){ + this.setState('run'); + this.textboxObj.disabled = true; + var ajax = new sack(this.handler); + ajax.encodeURIString = false; + ajax.onCompletion = this.start; + ajax.runAJAX('call=check&data='+encodeURIComponent(this.textboxObj.value)); + } + + /** + * Rewrites the HTML back to text again using an AJAX request + * + * @author Andreas Gohr <andi@splitbrain.org> + */ + this.resume = function(){ + this.setState('run'); + var text = this.showboxObj.innerHTML; + if(text != ''){ + var ajax = new sack(this.handler); + ajax.encodeURIString = false; + ajax.onCompletion = this.stop; + ajax.runAJAX('call=resume&data='+encodeURIComponent(text)); + } + } + + /** + * Displays the suggestions for a misspelled word + * + * @author Andreas Gohr <andi@splitbrain.org> + * @author Garrison Locke <http://www.broken-notebook.com> + */ + this.suggest = function(){ + var args = this.suggest.arguments; + if(!args[0]) return; + var id = args[0]; + + // set position of the popup + this.suggestObj.style.display = "none"; + var x = findPosX('spell_error'+id); + var y = findPosY('spell_error'+id); + var scrollPos = this.showboxObj.scrollTop; + this.suggestObj.style.left = x+'px'; + this.suggestObj.style.top = (y+16-scrollPos)+'px'; + + // handle suggestions + var text = ''; + if(args.length == 1){ + text += this.txtNoSug; + }else{ + for(var i=1; i<args.length; i++){ + text += '<a href="javascript:ajax_spell.correct('+id+',\''+encodeURIComponent(args[i])+'\')">'; + text += args[i]; + text += '</a><br>'; + } + } + + this.suggestObj.innerHTML = text; + this.suggestObj.style.display = "block"; + } +} + +// create the global object +ajax_spell = new ajax_spell_class(); + +//Setup VIM: ex: et ts=2 enc=utf-8 : diff --git a/lib/scripts/tw-sack.js b/lib/scripts/tw-sack.js index 8a9d12746..18b63730a 100644 --- a/lib/scripts/tw-sack.js +++ b/lib/scripts/tw-sack.js @@ -47,7 +47,7 @@ function sack(file){ urlVars[1] = encodeURIComponent(urlVars[1]); varArray[i] = urlVars.join("="); } - return varArray.join('&'); + return varArray.join('&'); } this.runResponse = function(){ |