(file) Return to spell.html CVS log (file) (dir) Up to [home] / javascript

File: [home] / javascript / spell.html (download) / (as text)
Revision: 1.8, Sun Apr 24 06:37:31 2005 UTC (5 years, 4 months ago) by ws
Branch: MAIN
CVS Tags: HEAD
Changes since 1.7: +148 -189 lines
faster, correct, less compatible -_-

<html>
<head>
<title>a realtime spellcheck using javascript</title>
<style type="text/css">
span.miss {
    border-bottom: 2px #f00 solid;
}
</style>
<!--[if IE]>
<style type="text/css">
span.miss {
    border-bottom: 1px #f00 solid;
}
</style>
<![endif-->
<script type='text/javascript' src='dict.js'></script>
<script type='text/javascript' src='logging.js'></script>

<script type='text/javascript'>
<!--
// realtime JS spellchecker
// by Tony Chang
// BSD License, like always: http://www.opensource.org/licenses/bsd-license.php
//
// Notes
// - So this doesn't work in safari, but it has built in spell checking as
//   you type for text fields.  No need for this client/server technology.

// TODO:
// - better protocol for returning misspelled words (index rather than whole word)
// - handling correction?  might be too bandwidth intense, we'll see

var isIE = (document.all) ? true : false;

function httpRequest(spellerObject) {
  if (isIE)
    this.http = new ActiveXObject("Msxml2.XMLHTTP");
  else
    this.http = new XMLHttpRequest();
  this.words = {}; // the set of words being sent
  this.spell = spellerObject; // pointer to the speller object
}

httpRequest.prototype.response = function() {
    if (4 == this.http.readyState && 200 == this.http.status) {
        var misspelled = new Array();
        if (this.http.responseText) {
            var text = this.http.responseText.substring(0, 
                            this.http.responseText.length - 1);
            misspelled = text.split(' ');
        }
        // check which words this impacts
        for (var w in misspelled) {
          this.spell.misspelled[misspelled[w]] = true;
        }
        for (w in this.words) // any remaining words are correct
          this.spell.dictionary[w] = true;
        this.spell.updatepreview();
    }
};
    
httpRequest.prototype.send = function() {
  var sendStr = '';
  for (var w in this.words)
      sendStr += w + ' ';
  if ('' == sendStr)
    return; // nothing to send, abort

  // make a closure
  var self = this;
  this.http.onreadystatechange = function() {
      self.response(self);
  };
  this.http.open('POST', 'spell.py', true);
  this.http.send(sendStr);
}

httpRequest.prototype.addWord = function(w) {
  this.words[w] = true;
}

function MapToTextNodes(root, f) {
  var len = root.childNodes.length;
  for (var c = 0; c < len; c++) // walk the dom
    MapToTextNodes(root.childNodes[c], f);
  
  if (3 == root.nodeType) {
    f(root);
  }
}

// supports for multiple text areas and outputs would be nice (with shared dictionary)
var speller = {
  dictionary: {}, // seen words
  newWords: {}, // unseen words
  misspelled: {}, // words that are known to be misspelled
  lastUpdate: 0,
  updateInterval: 500,

  keyupdate: function(ev) {
    this.updatepreview();
  },
  
  updatepreview: function() {
    // updatepreview is an expensive operation.  we try to rate limit it.
    var st = (new Date()).getTime();
    if (st - this.lastUpdate < this.updateInterval)
      return;
    
    this.lastUpdate = st;
    
    var text = document.getElementById('in').value;
    text = text.replace(/\n/g,"<br />\n");

    var preview = document.getElementById('preview');

    // now process the tree
    var hidden = document.createElement('div');
    hidden.innerHTML = text;
    this.findAndReplace(hidden);
    
    preview.innerHTML = hidden.innerHTML;

    
    /*  // for debugging
    //var raw = document.getElementById('raw');
    preview = document.getElementById('preview');
    var tmp = preview.innerHTML.replace(/</g, "&lt;");
    raw.innerHTML = tmp.replace(/>/g, "&gt;");
    //*/
    
    var et = (new Date()).getTime();
    this.updateInterval = Math.max((et-st) * 2, 200);
  },
  
  findAndReplace: function(root) {
    var self = this;
    MapToTextNodes(root, function(node) {
        var words = node.nodeValue.split(/(\W+)/);
        for (var w = 0; w < words.length; w++) {
          if (self.misspelled[words[w].toLowerCase()])
            words[w] = "<span class='miss'>" + words[w] + "</span>";
        }
        
        var newNode = document.createElement('span');
        newNode.innerHTML = words.join('');
        node.parentNode.replaceChild(newNode, node);
      });
  },

  updateDictionary: function() {
    // determine what words are new and send a request

    var text = document.getElementById('in').value;
    var elt = document.createElement('div');
    elt.innerHTML = text;

    var req = new httpRequest(this);
    var self = this;
    MapToTextNodes(elt, function(node) {
        var newWords = node.nodeValue.split(/\W+/);
        var len = newWords.length;
        for (var i = 0; i < len; i++) {
          var word = newWords[i].toLowerCase();
          if (!self.dictionary[word] && word.length > 1) // ispell always says that 1 letter words are correct
            req.addWord(word);
        }
      });
    
    req.send();
  }
}

window.onload = function() {
  initDict(speller.dictionary);
  window.setInterval("speller.updateDictionary()", 2000);
  if (isIE)
  {
    tb = document.getElementById('in');
    tb.style.width = document.body.clientWidth - 280;
  }
}

// -->
</script>
</head>
<body>
<div style='float: right; margin: 0 0 0 10; background: #b0ffb0; width: 200px; padding: 0 4px;'>
  <p style='text-align: center; margin: 0;'>NOTES</p>
  <p>This is a demo of a real time spell checker.  It uses xmlhttp request to
  get spelling corrections.  It works in Firefox, but not in IE and Safari due
  to incomplete regular expression implementations.</p>
  <p>You're welcome to take the <a href="http://ponderer.org/cvs/index.pl/javascript/">souce
  code</a>.</p> 
</div>
<div id='container' style='text-align: center; margin: 0 210 8 0; border: 0px; background: #ffb0b0;'>
    <p style='margin: 0 0 0 0;'>ENTER TEXT (or html) HERE</p>
    <textarea style='width:90%; background: #f0f0f0; margin: 0 5 5 5; height: 200px; padding: 2 2 2 2;border: 1px #808080 solid;' id='in'
          onkeyup='speller.keyupdate()'></textarea>
</div>
<div style='text-align: center; margin:0 210 0 0; border: 0px; background: #b0c0ff;'>
    <p style='margin: 0 0 0 0;'>PREVIEW</p>
    <div id='preview' style='padding: 5 5 5 5; text-align: left; background: #d0e0ff; border: 1px #000000 dashed;'></div>
</div>
<!-- for debugging html
<div style='text-align: center; margin:6 210 0 0; border: 0px; background: #b0c0ff;'>
    <p style='margin: 0 0 0 0;'>RAW</p>
    <div id='raw' style='padding: 5 5 5 5; text-align: left; background: #d0e0ff; border: 1px #000000 dashed;'></div>
</div>
-->

</body>
</html>

tony at ponderer dot org
Powered by
ViewCVS 0.9.2