wiki-archive/twiki/pub/TWiki/KupuContrib/_kupu/common/kupuspellchecker.js

201 lines
7.5 KiB
JavaScript

function KupuSpellChecker(buttonid, scripturl, spanstyle,
winwidth, winheight, skip_tags) {
this.button = document.getElementById(buttonid);
this.scripturl = scripturl;
this.spanstyle = spanstyle || 'color: red; ' +
'text-decoration: underline;';
this.winwidth = winwidth || '600';
this.winheight = winheight || '400';
this.skip_tags = skip_tags || ['head', 'script'];
};
KupuSpellChecker.prototype = new KupuTool;
KupuSpellChecker.prototype.initialize = function(editor) {
this.editor = editor;
addEventHandler(this.button, 'click', this.check, this);
};
KupuSpellChecker.prototype.check = function() {
var request = new XMLHttpRequest();
request.open('POST', this.scripturl, true);
request.setRequestHeader('Content-Type',
'application/x-www-form-urlencoded');
request.onreadystatechange = new ContextFixer(
this.stateChangeHandler,
this,
request).execute;
var result = this.getCurrentContents();
result = escape(result.strip().replace('\n', ' ').reduceWhitespace());
request.send('text=' + result);
};
KupuSpellChecker.prototype.stateChangeHandler = function(request) {
if (request.readyState == 4) {
if (request.status == '200') {
var result = request.responseXML;
result = this.xmlToMapping(result);
if (!result) {
alert(_('There were no errors.'));
} else {
this.displayUnrecognized(result);
};
} else {
alert(_('Error loading data, status ${status}',
{'status': request.status}));
};
};
};
KupuSpellChecker.prototype.getCurrentContents = function() {
var doc = this.editor.getInnerDocument().documentElement;
var iterator = new NodeIterator(doc);
var bits = [];
while (true) {
var node = iterator.next();
if (!node) {
break;
};
while (this.skip_tags.contains(node.nodeName.toLowerCase())) {
node = node.nextSibling;
iterator.setCurrent(node);
};
if (node.nodeType == 3) {
bits.push(node.nodeValue);
};
};
return bits.join(' ');
};
KupuSpellChecker.prototype.displayUnrecognized = function(mapping) {
// copy the current editable document into a new window
var doc = this.editor.getInnerDocument();
var docel = doc.documentElement;
var win = window.open('kupublank.html', 'spellchecker',
'width=' + this.winwidth + ',' +
'height=' + this.winheight + ',toolbar=no,' +
'menubar=no,scrollbars=yes,status=yes');
if (!win) {
alert(
_('This feature requires pop-ups to be enabled on your browser!'));
return;
};
var html = docel.innerHTML;
// when Moz tries to set the content-type, for some reason leaving this
// in breaks the feature(?!?)
html = html.replace(/<meta[^>]*http-equiv="[Cc]ontent-[Tt]ype"[^>]*>/gm,
'');
win.document.write('<html>' + html + '</html>');
win.deentitize = function(str) {return str.deentitize()};
win.document.close();
if (!win.document.getElementsByTagName('body').length) {
addEventHandler(win, 'load', this.continueDisplay, this, win, mapping);
} else {
this.continueDisplay(win, mapping);
};
};
KupuSpellChecker.prototype.continueDisplay = function(win, mapping) {
/* walk through all elements of the body, colouring the text nodes */
// start it all with a timeout to make Mozilla render the content first
timer_instance.registerFunction(this, this.continueDisplayHelper,
1000, win, mapping);
};
KupuSpellChecker.prototype.continueDisplayHelper = function(win, mapping) {
var body = win.document.getElementsByTagName('body')[0];
body.setAttribute('contentEditable', 'false');
var iterator = new NodeIterator(body);
var node = iterator.next();
timer_instance.registerFunction(this, this.displayHelperNodeLoop,
10, iterator, node, win, mapping);
};
KupuSpellChecker.prototype.displayHelperNodeLoop = function(iterator, node,
win, mapping) {
if (!node) {
return;
};
var next = iterator.next();
if (node.nodeType == 3) {
if (win.closed) {
return;
};
var span = win.document.createElement('span');
var before = node.nodeValue;
var after = this.colourText(before, mapping);
if (before != after) {
span.innerHTML = after;
var last = span.lastChild;
var parent = node.parentNode;
parent.replaceChild(last, node);
while (span.hasChildNodes()) {
parent.insertBefore(span.firstChild, last);
};
};
} else if (node.nodeType == 1 && node.nodeName.toLowerCase() == 'a') {
var cancelEvent = function(e) {
if (e.preventDefault) {
e.preventDefault();
} else {
e.returnValue = false;
};
return false;
};
addEventHandler(node, 'click', cancelEvent);
addEventHandler(node, 'mousedown', cancelEvent);
addEventHandler(node, 'mouseup', cancelEvent);
};
// using a timeout here makes Moz render the coloring while it's busy, and
// will make it stop popping up 'do you want to continue' prompts...
timer_instance.registerFunction(this, this.displayHelperNodeLoop,
10, iterator, next, win, mapping);
};
KupuSpellChecker.prototype.colourText = function(text, mapping) {
var currtext = text;
var newtext = '';
for (var word in mapping) {
var replacements = mapping[word];
replacements = replacements.entitize();
replacements = replacements.replace(/\'/g, "&apos;");
var reg = new RegExp('^(.*\\\W)?(' + word + ')(\\\W.*)?$', 'mg');
while (true) {
var match = reg.exec(currtext);
if (!match) {
newtext += currtext;
currtext = newtext;
newtext = '';
break;
};
var m = (match[1] || '') + match[2];
newtext += currtext.substr(0, currtext.indexOf(m));
newtext += (match[1] || '') +
'<span style="' + this.spanstyle + '" ' +
'onclick="alert(deentitize(\'' +
replacements + '\'));" ' +
'title="' + replacements + '">' +
match[2] +
'</span>';
currtext = currtext.substr(currtext.indexOf(m) + m.length);
};
};
return currtext;
};
KupuSpellChecker.prototype.xmlToMapping = function(docnode) {
var docel = docnode.documentElement;
var result = {};
var incorrect = docel.getElementsByTagName('incorrect');
for (var i=0; i < incorrect.length; i++) {
var word = incorrect[i].firstChild.firstChild.nodeValue;
var replacements = '';
if (incorrect[i].lastChild.hasChildNodes()) {
replacements = incorrect[i].lastChild.firstChild.nodeValue;
};
result[word] = replacements;
};
var attrs = [];
return result;
};