Annotation of loncom/html/adm/spellcheck/js/jquery.spellchecker.js, revision 1.2

1.1       foxr        1: /*
                      2:  * jquery.spellchecker.js - a simple jQuery Spell Checker
                      3:  * Copyright (c) 2009 Richard Willis
                      4:  * MIT license  : http://www.opensource.org/licenses/mit-license.php
                      5:  * Project      : http://jquery-spellchecker.googlecode.com
                      6:  * Contact      : willis.rh@gmail.com
                      7:  */
                      8: 
                      9: (function($){
                     10: 
                     11: 	$.fn.extend({
                     12: 		
                     13: 		spellchecker : function(options, callback){
                     14: 			return this.each(function(){
                     15: 				var obj = $(this).data('spellchecker');
                     16: 				if (obj && String === options.constructor && obj[options]) {
                     17: 					obj[options](callback);
                     18: 				} else if (obj) {
                     19: 					obj.init();
                     20: 				} else {
                     21: 					$(this).data('spellchecker', new SpellChecker(this, (Object === options.constructor ? options : null)));
                     22: 					(String === options.constructor) && $(this).data('spellchecker')[options](callback);
                     23: 				}
                     24: 			});
                     25: 		}
                     26: 	});
                     27: 
                     28: 	var SpellChecker = function(domObj, options) {
                     29: 		this.options = $.extend({
                     30: 			url: 'checkspelling.php',	// default spellcheck url
                     31: 			lang: 'en',			// default language 
                     32: 			engine: 'pspell',		// pspell or google
                     33: 			addToDictionary: false,		// display option to add word to dictionary (pspell only)
                     34: 			wordlist: {
                     35: 				action: 'after',	// which jquery dom insert action
                     36: 				element: domObj		// which object to apply above method
                     37: 			},
                     38: 			suggestBoxPosition: 'below',	// position of suggest box; above or below the highlighted word
                     39: 			innerDocument: false		// if you want the badwords highlighted in the html then set to true
                     40: 		}, options || {});
                     41: 		this.$domObj = $(domObj);
                     42: 		this.elements = {};
                     43: 		this.init();
                     44: 	};
                     45: 
                     46: 	SpellChecker.prototype = {
                     47: 
                     48: 		init : function(){
                     49: 			var self = this;
                     50: 			this.createElements();
                     51: 			this.$domObj.addClass('spellcheck-container');
                     52: 			// hide the suggest box on document click
                     53: 			$(document).bind('click', function(e){
                     54: 				(!$(e.target).hasClass('spellcheck-word-highlight') && 
                     55: 				!$(e.target).parents().filter('.spellcheck-suggestbox').length) &&
                     56: 				self.hideBox();
                     57: 			});
                     58: 		},
1.2     ! foxr       59: 		option : function(newopts) {
        !            60: 		    for (optname in newopts) {
        !            61: 		        this.options[optname] = newopts[optname];
        !            62: 		    }
        !            63: 		    return this;
        !            64: 	    },
1.1       foxr       65: 
                     66: 		// checks a chunk of text for bad words, then either shows the words below the original element (if texarea) or highlights the bad words
                     67: 		check : function(callback){
                     68: 
                     69: 			var self = this, node = this.$domObj.get(0).nodeName, 
                     70: 			tagExp = '<[^>]+>', 
                     71: 			puncExp = '^[^a-zA-Z\\u00A1-\\uFFFF]|[^a-zA-Z\\u00A1-\\uFFFF]+[^a-zA-Z\\u00A1-\\uFFFF]|[^a-zA-Z\\u00A1-\\uFFFF]$|\\n|\\t|\\s{2,}';
                     72: 
                     73: 			if (node == 'TEXTAREA' || node == 'INPUT') {
                     74: 				this.type = 'textarea';
                     75: 				var text = $.trim(
                     76: 					this.$domObj.val()
                     77: 					.replace(new RegExp(tagExp, 'g'), '')	// strip html tags
                     78: 					.replace(new RegExp(puncExp, 'g'), ' ') // strip punctuation
                     79: 				);
                     80: 			} else {
                     81: 				this.type = 'html';
                     82: 				var text = $.trim(
                     83: 					this.$domObj.text()
                     84: 					.replace(new RegExp(puncExp, 'g'), " ") // strip punctuation
                     85: 				);
                     86: 			}
                     87: 			this.postJson(this.options.url, {
                     88: 				text: encodeURIComponent(text).replace(/%20/g, '+')
                     89: 			}, function(json){
                     90: 				self.type == 'html' && self.options.innerDocument ? 
                     91: 				self.highlightWords(json, callback) : 
                     92: 				self.buildBadwordsBox(json, callback); 
                     93: 			});
                     94: 		},
                     95: 
                     96: 		highlightWords : function(json, callback) {
                     97: 			if (!json.length) { callback.call(this.$domObj, true); return; }
                     98: 
                     99: 			var self = this, html = this.$domObj.html();
                    100: 			
                    101: 			$.each(json, function(key, replaceWord){
                    102: 				html = html.replace(
                    103: 					new RegExp('([^a-zA-Z\\u00A1-\\uFFFF])('+replaceWord+')([^a-zA-Z\\u00A1-\\uFFFF])', 'g'),
                    104: 					'$1<span class="spellcheck-word-highlight">$2</span>$3'
                    105: 				);
                    106: 			});
                    107: 			this.$domObj.html(html).find('.spellcheck-word-highlight').each(function(){
                    108: 				self.elements.highlightWords.push(
                    109: 					$(this).click(function(){
                    110: 						self.suggest(this);
                    111: 					})
                    112: 				);
                    113: 			});
                    114: 			(callback) && callback();
                    115: 		},
                    116: 
                    117: 		buildBadwordsBox : function(json, callback){
                    118: 			if (!json.length) { callback.call(this.$domObj, true); return; }
                    119: 
                    120: 			var self = this, words = [];
                    121: 
                    122: 			// insert badwords list into dom
                    123: 			$(this.options.wordlist.element)[this.options.wordlist.action](this.elements.$badwords);
                    124: 
                    125: 			// empty the badwords container
                    126: 			this.elements.$badwords.empty()
                    127: 
                    128: 			// append incorrectly spelt words
                    129: 			$.each(json, function(key, badword) {
                    130: 				if ($.inArray(badword, words) === -1) {
                    131: 					self.elements.highlightWords.push(
                    132: 						$('<span class="spellcheck-word-highlight">'+badword+'</span>')
                    133: 						.click(function(){ self.suggest(this); })
                    134: 						.appendTo(self.elements.$badwords)
                    135: 						.after('<span class="spellcheck-sep">,</span> ')
                    136: 					);
                    137: 					words.push(badword);
                    138: 				}
                    139: 			});
                    140: 			$('.spellcheck-sep:last', self.elements.$badwords).addClass('spellcheck-sep-last');
                    141: 			(callback) && callback();
                    142: 		},
                    143: 
                    144: 		// gets a list of suggested words, appends to the suggestbox and shows the suggestbox
                    145: 		suggest : function(word){
                    146: 
                    147: 			var self = this, $word = $(word), offset = $word.offset();
                    148: 			this.$curWord = $word;
                    149: 
                    150: 			if (this.options.innerDocument) {
                    151: 				this.elements.$suggestBox = this.elements.$body.find('.spellcheck-suggestbox');
                    152: 				this.elements.$suggestWords = this.elements.$body.find('.spellcheck-suggestbox-words');
                    153: 				this.elements.$suggestFoot = this.elements.$body.find('.spellcheck-suggestbox-foot');
                    154: 			}
                    155: 
                    156: 			this.elements.$suggestFoot.hide();
                    157: 			this.elements.$suggestBox
                    158: 			.stop().hide()
                    159: 			.css({
                    160: 				opacity: 1,
                    161: 				width: "auto",
                    162: 				left: offset.left + "px",
                    163: 				top: 
                    164: 					(this.options.suggestBoxPosition == "above" ?
                    165: 					(offset.top - ($word.outerHeight() + 10)) + "px" :
                    166: 					(offset.top + $word.outerHeight()) + "px")
                    167: 			}).fadeIn(200);
                    168: 			
                    169: 			this.elements.$suggestWords.html('<em>Loading..</em>');
                    170: 
                    171: 			this.postJson(this.options.url, {
                    172: 				suggest: encodeURIComponent($.trim($word.text()))
                    173: 			}, function(json){
                    174: 				self.buildSuggestBox(json, offset);
                    175: 			});
                    176: 		},
                    177: 
                    178: 		buildSuggestBox : function(json, offset){
                    179: 
                    180: 			var self = this, $word = this.$curWord;
                    181: 
                    182: 			this.elements.$suggestWords.empty();
                    183: 
                    184: 			// build suggest word list
                    185: 			for(var i=0; i < (json.length < 5 ? json.length : 5); i++) {
                    186: 				this.elements.$suggestWords.append(
                    187: 					$('<a href="#">'+json[i]+'</a>')
                    188: 					.addClass((!i?'first':''))
                    189: 					.click(function(){ return false; })
                    190: 					.mousedown(function(e){
                    191: 						e.preventDefault();
                    192: 						self.replace(this.innerHTML);
                    193: 						self.hideBox();
                    194: 					})
                    195: 				);
                    196: 			}								
                    197: 
                    198: 			// no word suggestions
                    199: 			(!i) && this.elements.$suggestWords.append('<em>(no suggestions)</em>');
                    200: 
                    201: 			// get browser viewport height
                    202: 			var viewportHeight = window.innerHeight ? window.innerHeight : $(window).height();
                    203: 			
                    204: 			this.elements.$suggestFoot.show();
                    205: 						
                    206: 			// position the suggest box
                    207: 			self.elements.$suggestBox
                    208: 			.css({
                    209: 				top :	(this.options.suggestBoxPosition == 'above') ||
                    210: 					(offset.top + $word.outerHeight() + this.elements.$suggestBox.outerHeight() > viewportHeight + 10) ?
                    211: 					(offset.top - (this.elements.$suggestBox.height()+5)) + "px" : 
                    212: 					(offset.top + $word.outerHeight() + "px"),
                    213: 				width : 'auto',
                    214: 				left :	(this.elements.$suggestBox.outerWidth() + offset.left > $('body').width() ? 
                    215: 					(offset.left - this.elements.$suggestBox.width()) + $word.outerWidth() + 'px' : 
                    216: 					offset.left + 'px')
                    217: 			});
                    218: 			
                    219: 		},
                    220: 
                    221: 		// hides the suggest box	
                    222: 		hideBox : function(callback) {
                    223: 			this.elements.$suggestBox.fadeOut(250, function(){
                    224: 				(callback) && callback();
                    225: 			});				
                    226: 		},
                    227: 	
                    228: 		// replace incorrectly spelt word with suggestion
                    229: 		replace : function(replace) {
                    230: 			switch(this.type) {
                    231: 				case 'textarea': this.replaceTextbox(replace); break;
                    232: 				case 'html': this.replaceHtml(replace); break;
                    233: 			}
                    234: 		},
                    235: 
                    236: 		// replaces a word string in a chunk of text
                    237: 		replaceWord : function(text, replace){
                    238: 			return text
                    239: 				.replace(
                    240: 					new RegExp("([^a-zA-Z\\u00A1-\\uFFFF]?)("+this.$curWord.text()+")([^a-zA-Z\\u00A1-\\uFFFF]?)", "g"),
                    241: 					'$1'+replace+'$3'
                    242: 				)
                    243: 				.replace(
                    244: 					new RegExp("^("+this.$curWord.text()+")([^a-zA-Z\\u00A1-\\uFFFF])", "g"),
                    245: 					replace+'$2'
                    246: 				)
                    247: 				.replace(
                    248: 					new RegExp("([^a-zA-Z\\u00A1-\\uFFFF])("+this.$curWord.text()+")$", "g"),
                    249: 					'$1'+replace
                    250: 				);
                    251: 		},
                    252: 
                    253: 		// replace word in a textarea
                    254: 		replaceTextbox : function(replace){
                    255: 			this.removeBadword(this.$curWord);
                    256: 			this.$domObj.val(
                    257: 				this.replaceWord(this.$domObj.val(), replace)
                    258: 			);
                    259: 		},
                    260: 
                    261: 		// replace word in an HTML container
                    262: 		replaceHtml : function(replace){
                    263: 			var words = this.$domObj.find('.spellcheck-word-highlight:contains('+this.$curWord.text()+')')
                    264: 			if (words.length) {
                    265: 				words.after(replace).remove();
                    266: 			} else {
                    267: 				$(this.$domObj).html(
                    268: 					this.replaceWord($(this.$domObj).html(), replace)
                    269: 				);
                    270: 				this.removeBadword(this.$curWord);
                    271: 			}
                    272: 		},
                    273: 		
                    274: 		// remove spelling formatting from word to ignore in original element
                    275: 		ignore : function() {
                    276: 			if (this.type == 'textarea') {
                    277: 				this.removeBadword(this.$curWord);
                    278: 			} else {
                    279: 				this.$curWord.after(this.$curWord.html()).remove();
                    280: 			}
                    281: 		},
                    282: 		
                    283: 		// remove spelling formatting from all words to ignore in original element
                    284: 		ignoreAll : function() {
                    285: 			var self = this;
                    286: 			if (this.type == 'textarea') {
                    287: 				this.removeBadword(this.$curWord);
                    288: 			} else {
                    289: 				$('.spellcheck-word-highlight', this.$domObj).each(function(){
                    290: 					(new RegExp(self.$curWord.text(), 'i').test(this.innerHTML)) && 
                    291: 					$(this).after(this.innerHTML).remove(); // remove anchor
                    292: 				});
                    293: 			}
                    294: 		},
                    295: 
                    296: 		removeBadword : function($domObj){
                    297: 			($domObj.next().hasClass('spellcheck-sep')) && $domObj.next().remove();
                    298: 			$domObj.remove();
                    299: 			if (!$('.spellcheck-sep', this.elements.$badwords).length){
                    300: 				this.elements.$badwords.remove();
                    301: 			} else {
                    302: 				$('.spellcheck-sep:last', this.elements.$badwords).addClass('spellcheck-sep-last');
                    303: 			}
                    304: 		},
                    305: 		
                    306: 		// add word to personal dictionary (pspell only)
                    307: 		addToDictionary : function() {
                    308: 			var self= this;
                    309: 			this.hideBox(function(){
                    310: 				confirm('Are you sure you want to add the word "'+self.$curWord.text()+'" to the dictionary?') &&
                    311: 				self.postJson(self.options.url, { addtodictionary: self.$curWord.text() }, function(){
                    312: 					self.ignoreAll();
                    313: 					self.check();
                    314: 				});			
                    315: 			});
                    316: 		},
                    317: 		
                    318: 		// remove spell check formatting
                    319: 		remove : function(destroy) {
                    320: 			destroy = destroy || true;
                    321: 			$.each(this.elements.highlightWords, function(val){
                    322: 				this.after(this.innerHTML).remove()
                    323: 			});
                    324: 			this.elements.$badwords.remove();
                    325:                         this.elements.$suggestBox.remove();
                    326: 			$(this.domObj).removeClass('spellcheck-container');
                    327: 			(destroy) && $(this.domObj).data('spellchecker', null);
                    328: 		},
                    329: 		
                    330: 		// sends post request, return JSON object
                    331: 		postJson : function(url, data, callback){
                    332: 			var xhr = $.ajax({
                    333: 				type : 'POST',
                    334: 				url : url,
                    335: 				data : $.extend(data, {
                    336: 					engine: this.options.engine, 
                    337: 					lang: this.options.lang
                    338: 				}),
                    339: 				dataType : 'json',
                    340: 				cache : false,
                    341: 				error : function(XHR, status, error) {
                    342: 					alert('Sorry, there was an error processing the request.');
                    343: 				},
                    344: 				success : function(json){
                    345: 					(callback) && callback(json);
                    346: 				}
                    347: 			});
                    348: 			return xhr;
                    349: 		},
                    350: 
                    351: 		// create the spellchecker elements, prepend to body
                    352: 		createElements : function(){
                    353: 			var self = this;
                    354: 
                    355: 			this.elements.$body = this.options.innerDocument ? this.$domObj.parents().filter('html:first').find("body") : $('body');
                    356: 			this.elements.highlightWords = [];
                    357: 			this.elements.$suggestWords = this.elements.$suggestWords ||
                    358: 				$('<div></div>').addClass('spellcheck-suggestbox-words');
                    359: 			this.elements.$ignoreWord = this.elements.$ignoreWord ||
                    360: 				$('<a href="#">Ignore Word</a>')
                    361: 				.click(function(e){
                    362: 					e.preventDefault();
                    363: 					self.ignore();
                    364: 					self.hideBox();
                    365: 				});
                    366: 			this.elements.$ignoreAllWords = this.elements.$ignoreAllWords ||
                    367: 				$('<a href="#">Ignore all</a>')
                    368: 				.click(function(e){
                    369: 					e.preventDefault();
                    370: 					self.ignoreAll();
                    371: 					self.hideBox();
                    372: 				});
                    373: 			this.elements.$ignoreWordsForever = this.elements.$ignoreWordsForever ||
                    374: 				$('<a href="#" title="ignore word forever (add to dictionary)">Ignore forever</a>')
                    375: 				.click(function(e){
                    376: 					e.preventDefault();
                    377: 					self.addToDictionary();
                    378: 					self.hideBox();
                    379: 				});
                    380: 			this.elements.$suggestFoot = this.elements.$suggestFoot ||
                    381: 				$('<div></div>').addClass('spellcheck-suggestbox-foot')
                    382: 				.append(this.elements.$ignoreWord)
                    383: 				.append(this.elements.$ignoreAllWords)
                    384: 				.append(this.options.engine == "pspell" && self.options.addToDictionary ? this.elements.$ignoreWordsForever : false);
                    385: 			this.elements.$badwords = this.elements.$badwords ||
                    386: 				$('<div></div>').addClass('spellcheck-badwords');
                    387: 			this.elements.$suggestBox = this.elements.$suggestBox ||
                    388: 				$('<div></div>').addClass('spellcheck-suggestbox')
                    389: 				.append(this.elements.$suggestWords)
                    390: 				.append(this.elements.$suggestFoot)
                    391: 				.prependTo(this.elements.$body);
                    392: 		}
                    393: 	};	
                    394: 
                    395: })(jQuery);

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>