/**
 * Glossary plugin for jQuery
 * 
 *  Options
 *  - exact (string, default:"exact") 
 *    "exact" : find and highlight the exact words.
 *    "whole" : find partial matches but highlight whole words
 *    "partial": find and highlight partial matches
 *     
 *  - style_name (string, default:'hilite')
 *    The class given to the span wrapping the matched words.
 *     
 *  - highlight (string, default:null)
 *    A jQuery selector or object to set the elements enabled for highlight.
 *    If null or no elements are found, all the document is enabled for highlight.
 *        
 *  - nohighlight (string, default:null)  
 *    A jQuery selector or object to set the elements not enabled for highlight.
 *    This option has priority on highlight. 
 *    
 *  - keys (string, default:null)
 *    Disable the analisys of the referrer and search for the words given as argument    
 *    
 */

(function($){
  jQuery.fn.GlossaryHighlight = function(options) {
    GlossaryHighlight.options = $.extend({exact:"exact",style_name:'hilite'},options);
    GlossaryHighlight.matchOnlyFirst = (GlossaryHighlight.options.match_only_first?true:false);
		GlossaryHighlight.style = GlossaryHighlight.options.style_name;
		GlossaryHighlight.dataUrl = GlossaryHighlight.options.data_url;
		GlossaryHighlight.toolTip = GlossaryHighlight.options.tooltip?GlossaryHighlight.options.tooltip:"";
		GlossaryHighlight.descriptionTarget = GlossaryHighlight.options.description_target.replace(/&#034;/g,'"');
		GlossaryHighlight.loadJson(this);
	}    

  var GlossaryHighlight = {
    options: {},
    style: '',
		dataUrl: '',
		descriptionTarget: '',
		matchOnlyFirst: false,
		regex: [],
    glossary: {},
		loadJson: function(document) {
			$.ajax({
				global: false,
				url: GlossaryHighlight.dataUrl,
				type: 'GET',
				dataType: 'json',
				timeout: 3000,
				beforeSend: function() {
					//$("#loading").show("slow");
				},
				complete: function(){
					//$("#loading").hide("slow");
				},
				error: function (XMLHttpRequest, textStatus, errorThrown) {
					return this;
				},
				success: function(json){
					if(json.glossList != null) {
						GlossaryHighlight.processing(document,json);
					}
					else {
						return this;
					}
				}
			});
		},
		processing: function(document,json) {
			var q = GlossaryHighlight.glossary["terms"];
			if(json) {
	      GlossaryHighlight.buildReplaceTools(json);
	      return document.each(function(){
	        var el = this;
	        if(el==document) el = $("body")[0];
	        GlossaryHighlight.hiliteElement(el); 
	      })
	    } else return document;
		},
		regexAccent : [
      [/[\xC0-\xC5\u0100-\u0105]/ig,'a'],
      [/[\xC7\u0106-\u010D]/ig,'c'],
      [/[\xC8-\xCB]/ig,'e'],
      [/[\xCC-\xCF]/ig,'i'],
      [/\xD1/ig,'n'],
      [/[\xD2-\xD6\xD8]/ig,'o'],
      [/[\u015A-\u0161]/ig,'s'],
      [/[\u0162-\u0167]/ig,'t'],
      [/[\xD9-\xDC]/ig,'u'],
      [/\xFF/ig,'y'],
      [/[\x91\x92\u2018\u2019]/ig,'\'']
    ],
    matchAccent : /[\x91\x92\xC0-\xC5\xC7-\xCF\xD1-\xD6\xD8-\xDC\xFF\u0100-\u010D\u015A-\u0167\u2018\u2019]/ig,  
		replaceAccent: function(q) {
		  GlossaryHighlight.matchAccent.lastIndex = 0;
      if(GlossaryHighlight.matchAccent.test(q)) {
        for(var i=0,l=GlossaryHighlight.regexAccent.length;i<l;i++)
          q = q.replace(GlossaryHighlight.regexAccent[i][0],GlossaryHighlight.regexAccent[i][1]);
      }
      return q;
    },
    escapeRegEx : /((?:\\{2})*)([[\]{}*?|])/g, //the special chars . and + are already gone at this point because they are considered split chars
    buildReplaceTools : function(json) {
				var re = [], regex;
				$.each(json.glossList,function(i,item){
            if(n = GlossaryHighlight.replaceAccent(item.glossTerm).replace(GlossaryHighlight.escapeRegEx,"$1\\$2")){
              n = n.toLowerCase();
							GlossaryHighlight.glossary[n] = new Array();
							GlossaryHighlight.glossary[n]["firstMatch"] = false; 
							GlossaryHighlight.glossary[n]["url"] = item.glossId;
							
							re.push(n);
						}
				});
        
        regex = re.join("|");
        switch(GlossaryHighlight.options.exact) {
          case "vespa":
					  regex = '\\b('+regex+')(?:en|s|er|e|n)*\\b';
						break;
					case "exact":
            regex = '\\b(?:'+regex+')\\b';
						break;
          case "whole":
            regex = '\\b\\w*('+regex+')\\w*\\b';
						break;
        }    
        GlossaryHighlight.regex = new RegExp(regex, "gi");
		},
    nosearch: /s(?:cript|tyle)|textarea/i,
    hiliteElement: function(el) {
        var opt = GlossaryHighlight.options, elHighlight, noHighlight;
        elHighlight = opt.highlight?$(opt.highlight):$("body"); 
        if(!elHighlight.length) elHighlight = $("body"); 
        noHighlight = opt.nohighlight?$(opt.nohighlight):$([]);
                
        elHighlight.each(function(){
          GlossaryHighlight.hiliteTree(this,noHighlight);
        });
    },
    hiliteTree : function(el,noHighlight) {
        if(noHighlight.index(el)!=-1) return;
        var matchIndex = (GlossaryHighlight.options.exact=="whole" || GlossaryHighlight.options.exact=="vespa")?1:0;
        for(var startIndex=0,endIndex=el.childNodes.length;startIndex<endIndex;startIndex++) {
          var item = el.childNodes[startIndex];
          if ( item.nodeType != 8 ) {//comment node
  				  //text node
            if(item.nodeType==3) {
              var text = item.data, text=text.replace(/</g, "&lt;").replace(/>/g, "&gt;"), textNoAcc = GlossaryHighlight.replaceAccent(text);
              var newtext="",match,index=0;
              GlossaryHighlight.regex.lastIndex = 0;
              while(match = GlossaryHighlight.regex.exec(textNoAcc)) {
				if(!GlossaryHighlight.matchOnlyFirst || (GlossaryHighlight.glossary[match[matchIndex].toLowerCase()]!=null && !GlossaryHighlight.glossary[match[matchIndex].toLowerCase()]["firstMatch"])){
							newtext += text.substr(index,match.index-index)+'<a href="' + GlossaryHighlight.glossary[match[matchIndex].toLowerCase()]["url"] + '" class="'+
							GlossaryHighlight.style+'" '+ GlossaryHighlight.descriptionTarget + 'title="' + GlossaryHighlight.toolTip + '">'+text.substr(match.index,match[0].length)+"</a>";
							index = match.index+match[0].length;
							GlossaryHighlight.glossary[match[matchIndex].toLowerCase()]["firstMatch"] = true;
					
				}
              }
              if(newtext) {
                //add the last part of the text
                newtext += text.substring(index);
                var repl = $.merge([],$("<pre>"+newtext+"</pre>")[0].childNodes);
                endIndex += repl.length-1;
                startIndex += repl.length-1;
                $(item).before(repl).remove();
							}                
            } else {
              if(item.nodeType==1 && item.nodeName.search(GlossaryHighlight.nosearch)==-1)
                  GlossaryHighlight.hiliteTree(item,noHighlight);
            }	
          }
        }    
    }
  };
})(jQuery)
