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

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

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