function HeadupAnnotation() {
	_headup = this;		// set alias name
    this.widgetMode = 'tabs';
    this.Terms;
    this.toStop = false;
    this.loaderIcon = null;

    this.HeadupWidth = 407;
    this.HeadupHeight = 414;
    this.OpenTimeStatistics = 3 * 1000;

    this.openedTerm = null;

    /***************************
    <script type='text/javascript'>
    var hc_Customer = 'jpost';
    var hc_MaxTotalAnnotations = 20;
    var hc_MaxDuplicateAnnotations = 2;
    var hc_HoverToOpenTime = 1500;
    var hc_MarkLinks = false;
	var hc_Annotation = true;
    </script>
    ****************************/
	
    this.customer = 'genericwidget';
    this.totalAnnotationsLeft = 100;
    this.maxDuplicateAnnotations = 2;
    this.markLinks = false;
	this.markColor;

    this.widgetTimerID;

	this.annotateResult = null;
	this.gloablFuncCounter = 0;
    this.numberOfAnnotations = 0;

	this.triggeredId = "headup_triggered";
    this.hoveredElement = null;
	this.dragged = false;
	this.closeOnMouseOut = true;
	this.hoverToOpenTime = 500;
	
	this.popupTimeout = 0;
    this.x = this.y = 0;
	
    this.preloaded = false;
	this.testGroup = this.getTestGroup();
	//this.initTestGroup();
    this.HeadupInCenter = false;
    
	this.HeadupOpenFlag = false;
	this.HeadupCloseFlag = false;
	
	this.openedTerm;
	this.sessionId;
	this.widgetId;
	this.elementsAnnotated = [];

/* Default Tags Blacklist 
--------------------------------*/
	this.TagsBlackList = ['option','button','script','noscript','headupmark','textarea','style','input','label','object','iframe','param','embed','img','video','link'];
	this.classIdBlackList = [];

	this.clientHost = 'http://mint1.headup.com/Services/ClientScripts/';
    this.cdnHost    = 'http://static1.headup.com/';
}

HeadupAnnotation.prototype.generateGuid = function() {
	function S4() {
	   return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
	}
	//function guid() {
	   return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
	//}
}

HeadupAnnotation.prototype.userGuid = function() {
    var guid = this.readCookie('headup-user-guid');
    if (guid){
        return guid;
    }
    else {
		guid = _headup.generateGuid();
        this.createCookie("headup-user-guid", guid, 365);
        return guid;
    }
}

HeadupAnnotation.prototype.initTestGroup = function() {
	
}

HeadupAnnotation.prototype.getTestGroup = function() {
	var testGroupSubjects = ['ruhanirabin.com','heyuguys.co.uk','jewlicious.com'], testGroup = 'b';
	var d = document.domain.replace('www.','');
	for( var i=0; i < testGroupSubjects.length; i++ ){
		if( testGroupSubjects[i] == d ){
			testGroup = _headup.readCookie('testGroup');
			if (testGroup)
				return testGroup;

			testGroup = (Math.random() > 0.9) ? "a" : "b";
			_headup.createCookie("testGroup", testGroup, 365);
			return testGroup;
		}
	}
	return testGroup;
}

// createCookie
HeadupAnnotation.prototype.createCookie = function(name,value,days) {
	var expires;
	if (days) {
		var date = new Date();
		date.setTime(date.getTime()+(days*24*60*60*1000));
		expires = "; expires="+date.toGMTString();
	}
	else 
		expires = "";
	document.cookie = name+"="+ value + expires +"; path=/";
}

// readCookie
HeadupAnnotation.prototype.readCookie = function(name) {
	var nameEQ = name + "=";
	var ca = document.cookie.split(';');
	for(var i=0;i < ca.length;i++) {
		var c = ca[i];
		while (c.charAt(0)==' ') c = c.substring(1,c.length);
		if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
	}
	return null;
}

// eraseCookie
HeadupAnnotation.prototype.eraseCookie = function(name) {
	this.createCookie(name,"",-1);
}

HeadupAnnotation.prototype.OnPageLoad = function() {
    try {
        this.sessionId = _headup.generateGuid();

        window.onbeforeunload = function() {
            _headup.writeStatistic({ event: 'onbeforeunload' });
        }

        if (!document.body || !this.isPermittedBrowser())
            return;
        // removed by talm until further testing	
        //_headup.initUserSelection();

        this.loadSettings();
		if (!this.isDefined('hc_Annotation')) {
			var url = this.annotationUrl + escape(window.location.href);
			this.loadJSON(url, '_headup.annotationDone');
		}
    }
    catch (err) {
        this.ShowLog('OnPageLoad', err);
    }
}

HeadupAnnotation.prototype.initUserSelection = function() {
	var selection, selectionText, selectionButton, newRange;
	var speed = j$.browser.msie ? 0 : 80;
	
	j$('span.headup-bubble').live('click',function(e){
		e.preventDefault();
		_headup.mouseX = e.pageX;
		_headup.mouseY = e.pageY;
		_headup.OpenOnText(selectionText, true);
		j$(selectionButton).css('visibility','hidden');
		_headup.writeStatistic({ event:'manualAnnotaion', term:selectionText });
	});
	
	j$(document).bind("mouseup", function(e){
		if (e.target == selectionButton)
			return false;
			
		if (selectionButton)
			cleanUp();
		selection = getSelectedText();
		selectionText = selection && selection.toString();
		
		var wc = wordCount(selectionText);
		
		if (selectionText && wc < 6){
			insertButton();
		}
	}).bind("mousedown", function(e){
		if (selectionButton && e.target != selectionButton)
			cleanUp(); 
	});
	
	function cleanUp() {
		selection = null;
		newRange && newRange.pasteHTML && newRange.pasteHTML('');
		newRange = null;
		j$(selectionButton).fadeOut(speed, function(){ j$(this).remove() });
		selectionButton = null;
		selectionText = '';
	}
	
	function insertButton(){
		try{
			selectionButton = document.createElement("span");
			selectionButton.title = 'Learn More';
			selectionButton.className = 'headup-bubble';
			//j$(selectionButton).css("opacity",0.8);
			
			if (document.createRange) {
				var range = selection.getRangeAt(0);
				newRange = document.createRange();
				newRange.setStart(selection.focusNode, range.endOffset);
				newRange.insertNode(selectionButton);
			}
			else{
				newRange = selection.duplicate();
				newRange.setEndPoint( "StartToEnd", selection);
				newRange.pasteHTML( selectionButton.outerHTML );
				selectionButton = j$("span.headup-bubble"); // rebind it
			}
		}
		catch (err){ }
	}
	
	function getSelectedText() {
		var txt = '';
		if( window.getSelection ){
			return window.getSelection();
		}
		else if( document.getSelection ){
			return document.getSelection();
		}
		else if( document.selection && document.selection.createRange() ){
			var selection = document.selection && document.selection.createRange();
			selection.toString = function() { return this.text };
			return selection;
		}
		else return false; 
	}
	
	function wordCount(inStr) {
		var wc;
		wc = inStr && inStr.replace(/[^\s\w]+/g, ""); // get rid of punctuation
		wc = wc && wc.replace(/^\s*/, "").replace(/\s*$/, ""); // trim
		wc = wc && wc.length && wc.split(/\s+/).length; // split & count
		return Number(wc);
	}
}

HeadupAnnotation.prototype.initTemplate = function() {
    // Load external Headup CSS file
    var cssfile = this.widgetMode == 'snippet' ? '/css/CharmSnippetUI.css' : '/css/CharmUI.css';
    var myStylesLocation = this.serverWorkplace + cssfile;
    j$('<link rel="stylesheet" type="text/css" href="' + myStylesLocation + '" >').appendTo("head");

    this.templateUrl = this.templateUrl + '&v=1.1';
    this.loadJSON(this.templateUrl, '_headup.TemplateRetrieved');
}

HeadupAnnotation.prototype.TemplateRetrieved = function(data) {
    try {
        // Get the desired widget type from the template response, accorings to 'this.widgetMode'
        var response = j$('<div/>').html(data);
        var index = this.widgetMode == 'snippet' ? 1 : 0;
        var headup_widget = response.children('div')[index];

        j$("body").append(headup_widget);
        this.headupFrame = j$(headup_widget);
	
		this.headupFrame.append('<div class="shadowBG"></div>');
		
		// sets widget width & height
		if( this.widgetMode == 'snippet' ){
			this.HeadupWidth = 360;
			this.HeadupHeight = 254;
		} else{
			this.HeadupWidth = 407;
			this.HeadupHeight = 414;
		}
		// firefox and webkit uses box-shadow CSS instead of a shadow image that is been streched
		if( j$.browser.msie )
			this.headupFrame.append('<img src="'+this.serverWorkplace +'/images/shadowBG.png" alt="" id="shadowBG" />');

		_headup.UserConfig();
		
		// bind the satisfy (yes/no) buttons 
		_headup.satisfyCode = j$("#headup-satisfy").html();
		_headup.satisfy();
		
		// Close the popup when pressing [ESC] key
		j$(document).keyup(function(e){
			if( j$("#headup").is(":visible") && e.keyCode == 27){
				_headup.CloseHeadup();
			}
		});
		
		j$("#headup button.headup-close-btn").click(function(){
			_headup.CloseHeadup();
		});
		
		// bind events to the frame, only for annotated text term
		if( this.widgetMode == 'snippet' && this.closeOnMouseOut ){
			_headup.headupFrame.mouseenter(function(e){
				clearTimeout(_headup.popupTimeout);
			}).mouseleave(function(e){
				if( e.relatedTarget == _headup.hoveredElement || _headup.dragged || _headup.HeadupCloseFlag ){
					clearTimeout(_headup.popupTimeout);
					return false;
				}
				clearTimeout(_headup.popupTimeout);
				_headup.popupTimeout = setTimeout(function(){_headup.CloseHeadup()},1000);
			});
		}

        //this.headupFrame.css({ zIndex: 100, position: 'absolute', top: '50%', left: '50%', width: this.HeadupWidth, height: this.HeadupHeight, marginTop: (-this.HeadupHeight/2), marginLeft: (-this.HeadupWidth/2) });
        //this.headupFrame.css({ marginTop: (-this.HeadupHeight/2), marginLeft: (-this.HeadupWidth/2) });
        var handle = this.widgetMode == 'snippet' ? 'div.headup-title' : 'div.headup-header';
		//var cursor = j$.browser.mozilla ? '-moz-grabbing' : 'move';
		var cursor = 'url(http://maps.gstatic.com/intl/en_us/mapfiles/closedhand_8_8.cur), move';
		
		if( j$().draggable )
			this.headupFrame.draggable({
				iframeFix: true,
				cancel: 'h1',
				cursor: cursor,
				handle: handle, 
				start: function(event, ui){
					_headup.headupFrame.addClass('unselectable').find("b.pointer").hide();
					_headup.dragged = true;
				},
				stop: function(event, ui){ 
					_headup.headupFrame.removeClass('unselectable') 
				},
				drag: function(event, ui){
					_headup.adjustShadow( 
						j$(window).width(), 
						j$(window).height(), 
						Math.floor(_headup.headupFrame.offset().left + _headup.HeadupWidth/2 + 25), 
						Math.floor(_headup.headupFrame.offset().top + _headup.HeadupHeight/2 + 25)-j$(window).scrollTop() 
					);
					clearTimeout(_headup.popupTimeout);
				}
			});
	}
    catch (err) {
        this.ShowLog('TemplateRetrieved', err);
    }
}

HeadupAnnotation.prototype.adjustShadow = function(w,h,sw,sh) {
	//sw = w/sw > 2 ? sw - this.HeadupWidth/2 - 25 : sw + this.HeadupWidth/2 - 25;
	//sh = h/sh > 2 ? sh - this.HeadupHeight/2 - 25 : sh + this.HeadupHeight/2 - 25;
	var x = (24/w)*sw - 12,
		y = (24/h)*sh - 12;
	
	var tiltShadowLeft = -x +'px', 
		tiltShadowTop = -y +'px';
	_headup.headupFrame.find("div.shadowBG")
		.css('-mozBoxShadow',tiltShadowLeft+' '+ tiltShadowTop +' 15px rgba(0,0,0,0.4)')
		.css('-webkitBoxShadow',tiltShadowLeft+' '+ tiltShadowTop +' 15px rgba(0,0,0,0.4)');
}

HeadupAnnotation.prototype.isPermittedBrowser = function() {
    if (this.isChrome())
        return true;
    else if (j$.browser.msie && j$.browser.version == 6.0)
        return false;
    else
        return true;
}

HeadupAnnotation.prototype.isChrome = function(){
    return navigator.userAgent.toLowerCase().indexOf('chrome') != -1;
}

// Get the customerId from the annotate.js?customerid=something
HeadupAnnotation.prototype.getCustomerId = function(){
	var scripts = document.getElementsByTagName('script'), script;
	for(var i=0; i < scripts.length; i++){
		if( scripts[i].src && scripts[i].src.indexOf('annotate.js') != -1 ){
			script = scripts[i];
		}
	}
	//var s = (script.getAttribute.length !== undefined) ? script.getAttribute('src') : script.getAttribute('src', 2);
	if( typeof script === 'object' && script.src )
		var s = script.src;
	else{
		return this.customer;
	}
	return _headup.gup('customerid',s) || this.customer;
};

HeadupAnnotation.prototype.loadSettings = function(customer) {
	try{
		if (this.isDefined('hc_Customer'))
			this.customer = hc_Customer;

		if (this.isDefined('hc_MaxTotalAnnotations'))
			this.totalAnnotationsLeft = hc_MaxTotalAnnotations;
			
		//if user choose not to annotate any words
		if (this.isDefined('hc_Annotation'))
			this.totalAnnotationsLeft = 0;

		if (this.isDefined('hc_MaxDuplicateAnnotations'))
			this.maxDuplicateAnnotations = hc_MaxDuplicateAnnotations;

		if (this.isDefined('hc_HoverToOpenTime'))
			this.hoverToOpenTime = hc_HoverToOpenTime;

		if (this.isDefined('hc_MarkLinks'))
			this.markLinks = hc_MarkLinks;
			
		this.oldSettings();

		this.customer = customer || _headup.getCustomerId();

		//this.backOfficeUrl = this.clientHost + 'backoffice-' + this.customer +'.js';

		this.annotationUrl 		= this.replaceText( this.def_annotationUrl );
		this.annotationTextUrl 	= this.replaceText( this.def_annotationTextUrl );
		this.annotationUserText = this.replaceText( this.def_annotationUserText );
		this.annotationBlockUrl = this.replaceText( this.def_annotationBlockUrl );
		this.serverConfigUrl 	= this.replaceText( this.def_serverConfigUrl );
		this.templateUrl 		= this.replaceText( this.def_templateUrl );
		//this.widgetPage		= this.replaceText( this.def_widgetPage ); // MOVED TO loadServerSettings()
		//this.backOfficeUrl 	= this.replaceText(this.backOfficeUrl);

		// only after the JSON is retrieved from server, continue to initTemplate()
		
		this.loadJSON(this.serverConfigUrl, '_headup.loadServerSettings');
	}
    catch( err ){
		this.ShowLog('loadSettings', err);
    }
}

HeadupAnnotation.prototype.replaceText = function(str) {
	if(str) return str.replace( '[customer]', this.customer ).replace( '[AB]', this.testGroup );
}

HeadupAnnotation.prototype.oldSettings = function() {
	var mintObj = j$('#semantinet-mint');
	if( mintObj.length ){
		if( mintObj.attr('customerid') )
			this.customer = mintObj.attr('customerid');

		if( mintObj.attr('maxTotalAnnotations') )
			this.totalAnnotationsLeft = mintObj.attr('maxTotalAnnotations');

		if( mintObj.attr('maxDuplicateAnnotations') )
			this.maxDuplicateAnnotations = mintObj.attr('maxDuplicateAnnotations');
	}
}

HeadupAnnotation.prototype.loadServerSettings = function(data) {
    try{
		_headup.userConfigJSON = data;
		//_headup.UserConfig();  // should be called when '_headup.userConfigJSON' is ready & template is loaded in page
        if( data ){
            this.totalAnnotationsLeft = data.MaxAnnotations;
            this.maxDuplicateAnnotations = data.MaxDuplicateAnnotations;
            this.markLinks = data.MarkLinks;
			
			if (!this.markLinks )
				this.TagsBlackList.push('a');
			
			// Tags blacklist
			if( data.TagsBlackArray )
				this.TagsBlackList = this.TagsBlackList.concat(data.TagsBlackArray).unique();
			
			// Classes & Ids blacklist
			if( data.ClassesBlackArray )
				this.classIdBlackList = this.classIdBlackList.concat(data.ClassesBlackArray).unique();
				
			if( data.MarkColor )
				_headup.markColor = data.MarkColor;
			
			if( data.pathBlackList )
				_headup.pathBlackList = data.pathBlackList;
			
            //j$.getScript(_headup.backOfficeUrl);
			
			if( data.WidgetMode )
				this.widgetMode = this.testGroup == 'a' ? 'snippet' : data.WidgetMode.toLowerCase() || this.widgetMode;  // if null use default, if A/B testing, use option 'a'

			if( this.widgetMode == 'snippet' )
				this.def_widgetPage = this.serverWorkplace + '/SnippetPage.htm#?v=1.2&uri=[uri]&configid=[customer]&abtest=[AB]';
			
			this.widgetPage	= this.replaceText( this.def_widgetPage );
			
			this.writeStatistic({ event: 'PageView' });  // only after we have all the user settings
			this.initTemplate();
        }
		else{
			this.writeStatistic({ exception:'error', name:'loadServerSettings', error:'Configuration data for: "' + _headup.customer + '" is null or empty' });
			// if user configuration returned empty, try to load the settings again with the default one.
			if( _headup.customer != 'genericwidget' )
				_headup.loadSettings('genericwidget');
			return false;
		}
		_headup.annotationDone();
    }
    catch( err ){
		this.ShowLog('loadServerSettings', err);
    }
}

/* set extra publisher configuration */
HeadupAnnotation.prototype.UserConfig = function() {
	try {
		this.pathBlackList = [];
		// UserConfig is called twice, so need to check both tests are 'true' before continue
		if( j$("#headup").length && _headup.userConfigJSON ){
			if( _headup.userConfigJSON.CustomLogo ){
				var customImg = new Image();
				customImg.onload = function(){
					this.onload = null;
					// position the logo in the middle
					if( customImg.height < 27 ){
						customImg.style.top = '50%';
						customImg.style.marginTop = -customImg.height/2 + 'px';
					}
						
					// if image has a url, wrap it around it
					if( _headup.userConfigJSON.CustomLogoUrl )
						customImg = j$("<a href='"+ _headup.userConfigJSON.CustomLogoUrl +"' style='float:left; margin-right:10px; height:100%;'></a>").append(customImg);
						
					_headup.headupFrame.find('div.headup-footer').prepend(customImg);
				}
				
				customImg.src 		= _headup.userConfigJSON.CustomLogo;
				customImg.className = 'customLogo';
				customImg.alt 		= '';
			}
		}
		
	}
    catch (err) {
        this.ShowLog('UserConfig', err);
    }
}

HeadupAnnotation.prototype.loadJSON = function(url, callback) {
    if(callback != null)
        url = url.replace('callback=\?', 'callback=' + callback);
		
    j$.getScript(url);
}

/*	Handle Get and Set CSS Rules
---------------------------------------*/
	HeadupAnnotation.prototype.setStyleRule = function(selector, rule){
		// TODO: make sure that aditional selectors are added to the same <style> and not create others each time this function is invoked
		/*
		var stylesheet;
        for( var i in document.styleSheets ){
			if( !document.styleSheets[i].href ){
				stylesheet = document.styleSheets[i];
				
				if( stylesheet.addRule ){
					stylesheet.addRule(selector, rule);
				} 
				else if( stylesheet.insertRule ){
					stylesheet.insertRule(selector + ' { ' + rule + ' }', stylesheet.cssRules.length);
				}

				return;
			}
        }
		*/
		
		// added 'body' to strenghten the css specificity (IE thing)
		j$("head").append('<style type="text/css">body ' + selector + '{' + rule + '}' + '</style>');
		
		//stylesheet = document.createElement('style');
		//document.documentElement.firstChild.appendChild(stylesheet);
		//stylesheet = document.head.styleSheets[document.head.styleSheets.length];  // should be the one we just inserted
	
	}
	
	/* getStyle -> grabs a certain style from a CSS selector */
	HeadupAnnotation.prototype.getStyle = function(selector, property) {
		el = j$(selector)[0];
		if( window.getComputedStyle ){
			return document.defaultView.getComputedStyle(el, null).getPropertyValue(property);  
		}
		else{
			return el.currentStyle.name;
		}
	}

// annotationDone: a callback function after the page has been analyzied 
// by the server and returned the terms that should be annotated.
HeadupAnnotation.prototype.annotationDone = function(data) {
    try{
		this.gloablFuncCounter++;
		
		this.annotateResult = data || this.annotateResult;
		if( this.gloablFuncCounter < 2 )
			return false;
		
		data = this.annotateResult;
		
        var termsToAnnotateArr=[0], stat = {event:'annotated'};
		
		// extract 'termsToAnnotateArr' for satistics
		if (data && typeof data == 'object' && data.hasOwnProperty('Terms') ){
			for(var i in data.Terms){
				if( data.Terms.hasOwnProperty(i) )
					termsToAnnotateArr[i] = data.Terms[i].Term;
			}
			
			if( this.markColor )
				this.setStyleRule( '.headupTerm','border-color:' + this.markColor );
			
			//else{
			//	var siteLinksColor = this.getStyle('a', 'color');
			//	this.setStyleRule( '.headupTerm','border-color:' + siteLinksColor );
			//}
			
			this.bindAnnotations();

			this.Terms = data.Terms;
			var element = this.isDefined('hc_Path') ? $(hc_Path)[0] : document.body;

			var elementsFound = 0, elementsToExplore = ['body div.post','body div.entry, body div.entry_body_text'];
			
			for( var i=0; i < elementsToExplore.length; i++ ){
				var jPath = elementsToExplore[i];
				j$(jPath).each(function(){
					elementsFound++;
					_headup.postAnnotationsLeft = _headup.totalAnnotationsLeft;
					_headup.AnnotateRecursive( this, {} );
				});
			}
			// if no elements found, start scanning from the BODY element
			if( elementsFound == 0 ){
				_headup.postAnnotationsLeft = _headup.totalAnnotationsLeft;
				_headup.AnnotateRecursive( element, {} );
			}
			
			var sortedArr = _headup.elementsAnnotated.sort();
			sortedArr = this.handleDuplicates(sortedArr);
			
			stat.terms = sortedArr.length ? sortedArr.join('$$') : 0;
			stat.terms_from_server = termsToAnnotateArr.join('$$');
			
		} else{
			stat.terms = 0;
			stat.terms_from_server = 0;
		}
		if( typeof data != 'object' || data == null){
			stat.error = 'bad response: '+ data;
		}
		
		// split the annotated terms statistics if GET querystring length is too long
		if( stat.terms && stat.terms.length > 1500 ){
			var i = 0, chunk = 22;
			stat.batchSize = Math.ceil( sortedArr.length / chunk ) + 1;
			_headup.writeStatistic({ event:stat.event, terms_from_server:stat.terms_from_server, batchSize:stat.batchSize });

			(function cut(){
				i += chunk;
				var b = sortedArr.slice(i-chunk,i);
				stat.terms = b.join('$$');
				_headup.writeStatistic({ event:stat.event, terms:stat.terms, batchSize:stat.batchSize });
				if( b.length == chunk && i < sortedArr.length )
					cut();
			})();
		} 
		else{
			stat.batchSize = 1;
			this.writeStatistic(stat);
		}
			
    }
    catch(err) {
		this.ShowLog('annotationDone', err);
    }
}

HeadupAnnotation.prototype.handleDuplicates = function(arr) {
	var i, len=arr.length, out=[], obj={};

	for (i=0; i<len; i++) {
		var item = arr[i];									// for arr=['x','y','y'] get-> obj['x'], obj['y']
		obj[item] = obj[item] >= 1 ? obj[item] + 1 : 1;     // obj['x'] = 1, obj['y'] = 1, obj['y'] = 2
	}
	for (var i in obj) {
		out.push([i+'$$'+obj[i]]);
	}
	return out;
}

HeadupAnnotation.prototype.bindAnnotations = function() {
	try {
		j$(".headupTerm").live("mouseover",function(e){
			j$(this).addClass("hover");
			if( this != _headup.hoveredElement ){
				clearTimeout(_headup.popupTimeout);
				_headup.popupTimeout = setTimeout(function(){
					// only 1 unique headup link has this ID. if mouse is
					// over this element, then do not close the popup
					_headup.CloseHeadup(true);
					//j$("#headupTrigger").removeAttr("id"); // already inside _headup.CloseHeadup();
					_headup.hoveredElement = this
					//_headup.hoveredElement.attr('id',_headup.triggeredId);
					_headup.OnMouseOverHandler(e);
				},_headup.hoverToOpenTime);
			}else
				clearTimeout(_headup.popupTimeout);
				
		}).live("mouseout",function(e){
			if( !_headup.dragged && _headup.widgetMode == 'snippet' && _headup.closeOnMouseOut ){
				clearTimeout(_headup.popupTimeout);
				_headup.popupTimeout = setTimeout(function(){
					_headup.CloseHeadup();
				},1000);
			} else
				clearTimeout(_headup.popupTimeout);
			j$(this).removeClass("hover");
		});
		
		/*  assign events to the term that triggered the popup
		and the popup itself, and start a timeout.
		if mouse leaves the term, or the popup, then close it
		-------------------------------------------------------------
		if( this.widgetMode == 'snippet' ){
			j$(_headup.hoveredElement).mouseleave(function(){
				if( !_headup.dragged ){
					clearTimeout(_headup.popupTimeout);
					_headup.popupTimeout = setTimeout(function(){_headup.CloseHeadup(true)},500);
				}
			}).mouseenter(function(){
				clearTimeout(_headup.popupTimeout);
			});
		}
		*/
	
	}
    catch (err) {
		this.ShowLog('bindAnnotations', err);
    }
}

HeadupAnnotation.prototype.OnMouseOverHandler = function(event) {
    var target = event.srcElement;
    if (target == null)
        target = event.target;
    this.hoveredElement = target;
	
	this.linkTop = j$(target).offset().top;
    this.linkLeft = j$(target).offset().left;
	this.linkWidth = target.offsetWidth;
	this.linkHeight = j$(target).height();
	
    this.x = event.pageX;
    this.y = event.pageY;

    if (this.x < 0)
        this.x = 0;
    if (this.y < 0)
        this.y = 0;
		
    var item = _headup.hoveredElement['headup'];
	this.dragged = false; // need to reset this for every link, but only for annotated text
    _headup.OpenHeadup( item.Senses[0], this.x, this.y, true );
    // init combobox
    _headup.initSuggestMenu(item.Senses);
}

HeadupAnnotation.prototype.AnnotateRecursive = function(element, alreadyAnnotated){
    try{
        if( _headup.postAnnotationsLeft == 0 )
			return;

        if( this.toStop ) 
			return;
		
		if( element.nodeType === 1 ){
			if ( !this.ValidElementToAnnotate(element) )
				return;
		}
		
		// if its a text node, and its not empty or has whitespaces & is more than 2 chars
        if (element.nodeType == 3 && j$.trim(element.nodeValue).length > 2 ) {
            var nodeValue = element.nodeValue.toLowerCase();
			var index = 0;
            for (var i = 0; i < this.Terms.length; i++) {
                if (_headup.postAnnotationsLeft > 0) {
                    var loweredTerm = this.Terms[i].Term.toLowerCase();
					
                    var timesAnnotatedSoFar = alreadyAnnotated[loweredTerm] || 0;
					
                    if (timesAnnotatedSoFar <= this.maxDuplicateAnnotations){
						// always continute searching from the last place found for each text node. if the tests didn't pass, 
						// then try again searching for the same term (i--), but start from the last place that was found+1, so it won't fail again on the same place.
						index = nodeValue.indexOf(loweredTerm, index);
                        if (index != -1) {
							if( _headup.validateNodeValue(element.nodeValue,index,loweredTerm) ){
								index ++;
								i--; continue;
							}
							
							if( _headup.widgetMode == 'snippet' && !this.Terms[i].Senses[0].Snippet ){
								index ++;
								i--; continue;
							}
							
                            var beforeText = element.nodeValue.substr(0, index);
                            var term = element.nodeValue.substr(index, this.Terms[i].Term.length);
                            var afterText = element.nodeValue.substr(index + term.length);
							
							var beforeElement = document.createTextNode(beforeText);
                            var termElement = document.createElement('headupmark');
							
							termElement.className = 'headupTerm';
                            j$(termElement).text(term);
							
                            termElement['headup'] = this.Terms[i];
							
							var uri = this.Terms[i].Senses[0].URI.replace('http://schemas.semantinet.com/','');
							_headup.elementsAnnotated.push(uri);
							
							_headup.numberOfAnnotations++;
							
							var afterElement = document.createTextNode(afterText);
								
                            var parent = element.parentNode;

                            parent.insertBefore(beforeElement, element);
                            parent.insertBefore(termElement, element);
                            parent.insertBefore(afterElement, element);
                            parent.removeChild(element);
						
                            timesAnnotatedSoFar = alreadyAnnotated[loweredTerm] ? alreadyAnnotated[loweredTerm]+1 : 1;
								
							//console.log( loweredTerm,timesAnnotatedSoFar );
                            
                            alreadyAnnotated[loweredTerm] = timesAnnotatedSoFar;
							_headup.postAnnotationsLeft--;
                            this.AnnotateRecursive(parent, alreadyAnnotated);
                            return;
                        }
                    }
                }
				index = 0;
            }
        }
        else{
            for (var i = 0; i < element.childNodes.length; i++){
                this.AnnotateRecursive(element.childNodes[i], alreadyAnnotated );
            }
        }
    }
    catch(err){
		_headup.elementsAnnotated.push("-1");
        this.toStop = true;
        this.ShowLog('Annotate', err);
    }
}


// validateNodeValue should return true if one of the tests faild 
HeadupAnnotation.prototype.validateNodeValue = function(text, index, term){
	// var delimiters = " ’.:;!,?-()[]\n\r\"'"; not in use
	var fc = text.charAt(index),					// first char
		alc = text.charAt(index+term.length),		// after last char
		bfc = text.charAt(index-1); 				// before first char
		
	return	fc == fc.toLowerCase() || 				// check if first char is lowercase
			bfc.toLowerCase() != bfc.toUpperCase() || // check if its a letter ( 'a' != 'A' means its a letter )
			(bfc >= '0' && bfc <='9') ||
			alc.toLowerCase() != alc.toUpperCase() ||
			(alc >= '0' && alc <='9') ||
			bfc == '.' && alc == '.';
}

HeadupAnnotation.prototype.ValidElementToAnnotate = function(element) {
	try{
		var tag = element.tagName.toLowerCase();
		var isValid = true;
		
		if( j$.inArray(tag, this.TagsBlackList) != -1 ){
			return false;
		}
		
		var isLink = element.nodeType === 1 && element.tagName === "A";
		var sameDomain = element.host == window.location.host;

		if( isLink && sameDomain )
			return false;
		
		if( isLink && element.href ){
			var isActionLink = j$(element).attr("href").toLowerCase().startsWith('javascript:');
			if( isActionLink )
				return false;
		}
		
		if (element.className.toLowerCase() == 'no_headup' || element.id == 'headup' || j$.inArray(element.id, this.classIdBlackList) != -1 )
			return false;
		
		// go through all the classes this element has and check if one blacklisted
		if( element.className ){
			var elClassArr = element.className.toLowerCase().split(' ');
			
			for(var className=0; className < elClassArr; className++){
				if( j$.inArray(elClassArr[className], this.classIdBlackList) != -1 )
					return false;
			}
		}
		
		// example :
		// _headup.pathBlackList = ['body > p:last > span:first'];
		
		var pbl = this.pathBlackList;
		if( pbl && pbl.length && pbl.length > 0 ){
			for(var i=0; i < pbl; i++){
				var jPath = pbl[i];
				// might find more than one element in the document
				j$(jPath).each(function(){
					// if the element we're validating is in the pathBlackList, return false
					if(this === element){
						//console.log(this +" : "+ element);
						isValid = false;
					}
				});
			}
		}
		
		return isValid;
	}
	catch(err){ this.ShowLog('ValidElementToAnnotate', err); }
}

HeadupAnnotation.prototype.alert = function(text){
	var obj = j$("<div/>");
	obj.text( text );
	obj[0].className = 'headup-alert';
	//obj.css({ display:'none', position:'absolute', padding:'10px', textAlign:'center', maxWidth:'320px', backgroundColor:'#fff', border:'5px solid #CCC', fontSize:'1.4em', color:'#666', MozBorderRadius:'6px',MozBoxShaddow:'0 0 10px rgba(0,0,0,0.8)', WebkitBorderRadius:'6px', marginTop:'10px' });
	obj.appendTo("body");
	
	if( !this.mouseX && !this.mouseY )
		obj.center();
	
	else{
		var posX = (_headup.mouseX + (obj.width()/2) > j$(window).width()) ? 'right' : Math.max(_headup.mouseX - (obj.width()/2),0);
		var posY = _headup.mouseY - 50;
		
		if( posX == 'right' )
			obj.css({ right:0, top:posY });
		else
			obj.css({ left:posX, top:posY });
	}
	
	obj.animate({ marginTop:0, opacity:'toggle' }, 200);
	setTimeout( function(){ 
		obj.animate({ marginTop:10, opacity:'toggle' }, 200, function(){ 
			obj.remove();
		});
	}, 1500);
	
	this.mouseX = this.mouseY = null;
}

HeadupAnnotation.prototype.OpenOnText = function(text, userSelected) {
	this.dragged = true; // trick so when mouseOut from the popup, it will not close
	
    var url =  userSelected ? this.annotationUserText + text : this.annotationTextUrl + text;
	this.loadJSON(url, '_headup.OpenOnTextData');
}

HeadupAnnotation.prototype.OpenOnTextData = function(data) {
	if( data.Log ){
		_headup.alert(data.Log);
		//return false;
		if( !data.Terms[0] )
			return false;
	}
	
	_headup.OpenHeadup( data.Terms[0].Senses[0] );
	_headup.initSuggestMenu( data.Terms[0].Senses );
}

//HeadupAnnotation.prototype.headupFrame = null;

HeadupAnnotation.prototype.CloseHeadup = function(quickClose){
	try{
		j$("#"+this.triggeredId).removeAttr("id");
		// if closeing has began (=true), do not continute
		if( this.HeadupCloseFlag || this.headupFrame.is(':hidden') )
			return false;
			
		this.hoveredElement = null;
		this.HeadupCloseFlag = true;
		this.HeadupOpenFlag = false;
		
		//j$("object, embed").css("visibility","visible");
		
		if (this.openedTerm)
			_headup.writeStatistic({ event:'widgetClosed' });
		this.widgetId = null; // reset the widget ID

		this.openedTerm = null;
		
		this.headupFrame.find("b.pointer").remove();
		j$('#headupWidget').remove(); 		
		
		if( j$.browser.msie )
			this.headupFrame.find("#shadowBG").hide();
			
		if(quickClose){
			this.headupFrame.hide();
			this.HeadupCloseFlag = false;
		}
		else{
			this.headupFrame.animate({ top:"+=30px", opacity:"toggle" }, 180, function(){  
				_headup.HeadupCloseFlag = false;
			})
		}
	}
	catch(err){  }
}

HeadupAnnotation.prototype.writeStatistic = function(statsJSON) {
    //var url = [this.clientHost];
    var url = ["?"];

/*
    for (var key in statsJSON) {
        // make sure that the key is an actual property of an object, and doesn't come from the prototype
        if (statsJSON.hasOwnProperty(key)) {
            //alert(key + " -> " + statsJSON[key]);
            var sign = (!url[0]) ? '?' : '&';
            url.push('&');
            url.push(key + '=');
            url.push(encodeURI(statsJSON[key]));
        }
    }
*/
	// Serializes an object
	url.push( j$.param(statsJSON) );
	url.push('&customerId=' + this.customer);
	url.push('&widgetMode=' + this.widgetMode);
    url.push('&testGroup=' + this.testGroup);
    url.push('&userId=' + _headup.userGuid());
    url.push('&sessionId=');
    url.push(_headup.sessionId || 'null');
    url.push('&pageUrl=' + escape(document.location.href));
    url.push('&timeStamp=');
    url.push(new Date().getTime());

    if (this.openedTerm) {
        url.push('&widgetId=' + _headup.widgetId);
        var uri = this.openedTerm['URI'];
        uri = uri.replace('http://schemas.semantinet.com/', '');
        url.push('&URI=' + escape(uri));
    }

    var stat = new Image();
    stat.src = this.clientHost + 'headupStats.gif' + url.join("");
    //this.loadScript( url.join("") );
}

HeadupAnnotation.prototype.dump = function() {
    var str = '', terms = [];

    for (var i = 0; i < _headup.Terms.length; i++) {
        terms.push( _headup.Terms[i].Term );
    }

    str += '\nCustomer Id  :  ' + this.customer;
	str += '\nNumber Of Annotations  :  ' + this.numberOfAnnotations;
    str += '\nMax Annotations (per post/page)  :  ' + this.totalAnnotationsLeft;
    str += '\nMax Duplicates  :  ' + this.maxDuplicateAnnotations;
	str += '\nPosts in Page  :  ' + (j$('.post').length + j$('.entry').length);
    str += '\nMark Links  :  ' + this.markLinks;
	str += '\ntestGroup  :  ' + this.testGroup;
	
	/*
    str += '\nannotationUrl : ' + _headup.annotationUrl;
    str += '\nannotationTextUrl : ' + _headup.annotationTextUrl;
    str += '\nannotationBlockUrl : ' + _headup.annotationBlockUrl;

    str += '\nserverConfigUrl : ' + _headup.serverConfigUrl;
    str += '\ntemplateUrl : ' + _headup.templateUrl;
    str += '\nwidgetPage : ' + _headup.widgetPage;
    str += '\nbackOfficeUrl : ' + _headup.backOfficeUrl;
	*/
    str += '\n\n'; 
	
	str += _headup.Terms.length + ' terms returned from server :';
	str += '\n\n'; 
	str += terms.join(', ');

    //str += '\nclientHost : ' + this.clientHost;
    //str += '\nsettingsUrl : ' + this.settingsUrl;
    //str += '\nbackOfficeUrl : ' + this.backOfficeUrl;

    str += '\n';
    
    alert(str);
}

var lastSelected = 0;

HeadupAnnotation.prototype.OpenHeadup = function(sense, mouseX, mouseY, openOnHover) {
	try {
		//if opening of modal has began (=true), do not continute
		if( this.HeadupOpenFlag )
			return false;
		
		this.openedTerm = sense;
		this.HeadupOpenFlag	= true;
		this.widgetId = _headup.generateGuid();
		
		_headup.writeStatistic({ event:'widgetOpened' });
		//j$("object, embed").css("visibility","hidden");
		
		//this.headupFrame.attr('style', 'display:none');

		var self = this;
		// this.CloseHeadup(false);
    
		_headup.initIframe(sense);
		
		// shows back the 'satisfaction' question
		clearTimeout( _headup.satisfyTimer );
		j$("#headup-satisfy").stop().html(_headup.satisfyCode).show();
		
		// Sets the widget title
		if( sense.FullName.length > 35 ){
			var widgetTitle = sense.FullName.substring(0, 36) + "&hellip;";
			j$('#headup-suggest-text').parent().hide();
		}
		else
			var widgetTitle = sense.FullName;
		
		var widgetFullTitle = sense.DisplayName ? sense.FullName +', ' + sense.DisplayName : sense.FullName;
		_headup.headupFrame.find("h1").html(widgetTitle).attr('title',widgetFullTitle)
		_headup.headupFrame.find("button")[0].blur();  // don't select the Close button
		
		var headupWrap = _headup.headupFrame.find("div.headup-wrap"); // main content that holds the iframe
		headupWrap.hide(); // show it later, after animation
		
		var windowHeight = j$(window).height(),
			windowWidth  = j$(window).width();
			
		function openInCenter(){
			_headup.headupFrame.css({ width:0, height:0, margin:0, top:j$(document).scrollTop() + windowHeight/2 });
			_headup.headupFrame.animate({ width:_headup.HeadupWidth, height:_headup.HeadupHeight, marginTop:-_headup.HeadupHeight/2, marginLeft:-_headup.HeadupWidth/2 }, 200, "swing",function(){
				if( j$.browser.msie )
					_headup.headupFrame.find("#shadowBG").show();
					
				headupWrap.show();
				_headup.HeadupOpenFlag = false;
			});
		}
		
		if( openOnHover ){
			this.mouseX = mouseX;
			this.mouseY = mouseY;
			var sTop = j$(document).scrollTop(),
				baseline = sTop + windowHeight,
				offsetTop = 0,
				offsetLeft = 10,
				posY = this.linkTop,
				direction,
				tipY = 0, 
				tipX = 0,
				shadow = 25;
				hw = this.HeadupWidth + 2*shadow;
				hh = this.HeadupHeight + 2*shadow;
			
				var pointer = j$("<b class='pointer'></b>");
				this.headupFrame.append(pointer);

			// clac Horizontal position
			//if( this.linkLeft + this.linkWidth + hw > windowWidth )
			//	var posX = this.linkLeft - hw;
			//else
			
			var posX = (mouseX + (hw/2) > windowWidth) ? windowWidth - hw : Math.max(mouseX - (hw/2),0);

			//calc vertical position
			if( (posX < this.mouseX && Math.max(posX, 0) + hw > this.mouseX) || true ) {
				if(this.linkTop + hh + offsetTop > baseline && mouseY - sTop > hh + offsetTop){
					posY = this.linkTop - hh - offsetTop;
					direction = "top";
				}else if( this.linkTop + hh + offsetTop < baseline && mouseY - sTop < hh + offsetTop ){
					posY = this.linkTop + this.linkHeight + offsetTop;
					direction = "bottom";
				}
				else{ // If no space for the popup to be opened on top or bottom of the link, place it by its side
					if( this.linkWidth > this.linkLeft && this.linkLeft > hw || this.linkLeft + this.linkWidth + hw > windowWidth ){
						// left
						var posX = this.linkLeft - hw;
					} else{
						// right
						var posX = this.linkWidth + this.linkLeft;
					}
					
					// if distance of modal popup from mouseX > 250 or popup position might be minus
					if( mouseX - posX - hw - offsetLeft > 250 || posX < 0 || posX - mouseX - offsetLeft > 250 ){
						if( mouseX + hw > windowWidth )
							posX = mouseX - hw - offsetLeft;
						else
							posX = mouseX + offsetLeft;
					}
					
					var vMiddle = this.linkTop - hh/2;
					
					// if popup height is bigger than the viewable window height OR if check if popup has enough space to show in vertical middle, stick it to the top
					if( hh >= windowHeight || vMiddle < sTop ){
						posY = sTop;
					}
					// if when positioned at vertical middle, the popup is lower than bottom of the screen
					else if( baseline < this.linkTop + hh/2 ){
						posY = baseline - hh - offsetTop;
					}
					else{
						posY = vMiddle;
					}
					
				}
			}
			else if( this.linkTop + hh + offsetTop > baseline ){  // This part is not really needed
				posY = (hh >= windowHeight) ? sTop : baseline - hh - offsetTop;
			}
			
			if(!direction)
				direction = (posX < mouseX) ? 'left' : 'right';
			
			pointer.addClass(direction).hide();

			if( /(left|right)/.test(direction) && posX >= 0 ){
				tipY = mouseY - posY - pointer.height()/2 - shadow;
				pointer.css({ top:tipY });
			}
			// if popup height fits into document height
			else if( baseline > posY + hh - shadow ){
				tipX = mouseX - posX - shadow;
				pointer.css({ left:tipX });
			}
			
			/*-- Claculate Shadow --*/
			_headup.adjustShadow( 
				j$(window).width(), 
				j$(window).height(), 
				Math.floor(posX + this.HeadupWidth/2 + shadow), 
				Math.floor(posY + this.HeadupHeight/2 + shadow)-j$(window).scrollTop() 
			);
			
			// If the window Height & Width can't fit for the popup, then center it
			animationSpeed = this.widgetMode == 'snippet' ? 250 : 290;
			if( posX >= 0 && baseline > (posY + hh/2) ){
				_headup.headupFrame.stop()
				.css({ left:mouseX - shadow, top:mouseY - shadow, width:0, height:0 }).hide()
				.animate({ left:posX, top:posY, width:_headup.HeadupWidth, height:_headup.HeadupHeight, opacity:'toggle' }, animationSpeed, "easeOutBack",function(){
					if( j$.browser.msie ){
						_headup.headupFrame.find("#shadowBG").show();
						pointer.show();
					}
					else pointer.fadeIn('fast');
					headupWrap.fadeIn(200);
				});
			} else{
				openInCenter();
			}
		
		}else{ // open popup on center of the screen
			openInCenter();
		}
	}
	catch (err) {
		self.ShowLog('OpenHeadup', err);
	}
}

HeadupAnnotation.prototype.initIframe = function(sense) {
    var src = _headup.widgetPage.replace('[uri]', sense.URI);
	if( this.widgetMode == 'snippet' ){
		src = src + '&snippet=' + sense.Snippet + '&termName=' + sense.FullName;  // Term name is needed to generate the link inside the snippet for the topic page
	}

    // userId, sessionId, widgetId, testGroup, widgetMode
    src = src + '&userId=' + this.userGuid() + '&sessionId=' + this.sessionId + '&widgetId=' + this.widgetId + '&testGroup=' + this.testGroup + '&widgetMode=' + this.widgetMode;
	
    j$('#headupWidget').remove();
    var iframeObj = document.createElement("iframe");
    iframeObj.id = 'headupWidget';
    iframeObj.scrolling = 'no';
    iframeObj.frameBorder = 0;
    iframeObj.allowTransparency = 'true';
	
    j$("#headup div.headup-iframe-wrap").html(iframeObj);
    iframeObj.src = src;
}

HeadupAnnotation.prototype.ShowLog = function(name, err) {
    if (this.debug) {
        if (name == null)
            name == 'unknown';

        if ( err.message && err.lineNumber )
			alert(name + ': ' + err.message + '\n\Line Number: ' + err.lineNumber);
		else if (err)
			alert(name + ': ' + err.message);
        else
			alert(name + ': some error');
    }
	this.writeStatistic({ exception:'error', name:name, error:err.message, line:err.lineNumber });
}

HeadupAnnotation.prototype.loadJQueryPlugins = function() {
	j$.fn.center = function () {
		this.css("position","absolute")
			.css("top", ( j$(window).height() - this.height() ) / 2 + j$(window).scrollTop() + "px")
			.css("left", ( j$(window).width() - this.width() ) / 2 + j$(window).scrollLeft() + "px");
		return this;
	}
	
	j$.easing['jswing'] = j$.easing['swing'];

	j$.extend( j$.easing,{
		def: 'easeOutQuad',
		swing: function (x, t, b, c, d) {
			//alert(j$.easing.default);
			return j$.easing[j$.easing.def](x, t, b, c, d);
		},
		easeInQuad: function (x, t, b, c, d) {
			return c*(t/=d)*t + b;
		},
		easeOutQuad: function (x, t, b, c, d) {
			return -c *(t/=d)*(t-2) + b;
		},
		easeInBack: function (x, t, b, c, d, s) {
			if (s == undefined) s = 1.70158;
			return c*(t/=d)*t*((s+1)*t - s) + b;
		},
		easeOutBack: function (x, t, b, c, d, s) {
			if (s == undefined) s = 1.70158;
			return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
		}
	});
	
}

HeadupAnnotation.prototype.onJQueryReady = function() {
    j$ = hjQuery.noConflict();
	
	this.loadJQueryPlugins();
	this.settings();
	
	var scriptToLoad = [];

    j$.ajaxSetup({ cache: true });
    j$.getScript(_headup.cdnHost + 'jquery-ui-1.7.1.custom.min.js', function() {
        var scriptCounter = scriptToLoad.length;
		if( scriptCounter > 0 ){
			for (var i = 0; i < scriptToLoad.length; i++)
				j$.getScript(scriptToLoad[i], scriptLoaded);
		}else
			_headup.OnPageLoad();
			
		var scriptLoaded = function() {
            if (--scriptCounter < 0)
                setTimeout(function() { _headup.OnPageLoad(); }, 0);
        }
    });
}

HeadupAnnotation.prototype.settings = function() {
    var workplace 				= 'http://mint1.headup.com/Services/FrontService'; // RemoveThisLineOnSetup1

    this.serverWorkplace 		= workplace;
    this.def_serverConfigUrl 	= workplace + '/GetConfig.ashx?v=1.1&callback=?&configId=[customer]&abtest=[AB]';
    this.def_annotationUrl 		= workplace + '/AnnotateURL.ashx?v=1.1&callback=?&configId=[customer]&abtest=[AB]&url='; // RemoveThisLineOnSetup1
    this.def_annotationTextUrl 	= workplace + '/AnnotateMintLive.ashx?v=1.1&callback=?&configId=[customer]&abtest=[AB]&text=';
    this.def_annotationUserText = workplace + '/AnnotateText.ashx?v=1.1&callback=?&configId=[customer]&abtest=[AB]&text=';
    this.def_annotationBlockUrl = workplace + '/AddToAnnotationBlackList.ashx?v=1.1&callback=?&configId=[customer]&abtest=[AB]&uri=[uri]&term=[term]';
    this.def_templateUrl 		= workplace + '/GetTemplate.ashx?v=1.3&callback=?&configid=[customer]&abtest=[AB]';
    this.def_widgetPage 		= workplace + '/WidgetPage.htm#?v=1.1&uri=[uri]&configid=[customer]&abtest=[AB]';
}

HeadupAnnotation.prototype.preLoad = function() {
    try{
        if (this.preloaded) 
			return;
			
        this.preloaded = true;
        var jqueryUrl = this.cdnHost + 'hjquery-1.3.2.js';
		
        (typeof hjQuery == 'undefined') ? this.loadScript(jqueryUrl, function(){ _headup.onJQueryReady(); }) : this.onJQueryReady();
    }
    catch (err){
        this.ShowLog('preLoad', err.message);
    }
}

HeadupAnnotation.prototype.loadScript = function(url, callback){
	var head = document.getElementsByTagName("head")[0];
	var script = document.createElement("script");
	script.src = url;

	// Attach handlers for all browsers
	var done = false;
	script.onload = script.onreadystatechange = function(){
		if( !done && ( !this.readyState || this.readyState == "loaded" || this.readyState == "complete") ){
				done = true;

				// Continue your code
				if(callback)
					callback();

				// Handle memory leak in IE
				script.onload = script.onreadystatechange = null;
				head.removeChild( script );
		}
	};
	head.appendChild(script);
}

HeadupAnnotation.prototype.DoNotAnnotateClicked = function() {
    var url = this.annotationBlockUrl.replace('[uri]', this.openedTerm['uri']).replace('[term]', this.openedTerm['headup']);

    j$.getJSON(url, function(data) {
        alert(data);
    });
}

HeadupAnnotation.prototype.isDefined = function(variable) {
    return (typeof (window[variable]) == "undefined") ? false : true;
}

HeadupAnnotation.prototype.gup = function(name, query) {
    name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
    var regexS = "[\\?&]" + name + "=([^&#]*)";
    var regex = new RegExp(regexS);
    var results = regex.exec(query);
    if (results == null)
        return "";
    else
        return results[1];
}

HeadupAnnotation.prototype.getQueryStrings = function(){
    var argList = {};

    if(window.location != null && window.location.search.length > 1) {
        var urlParms = window.location.search.substring(1);
        var argPairs = urlParms.split('&');

        for(var i = 0; i < argPairs.length; i++) {
            var pos = argPairs[i].indexOf('=');

            if(pos == -1)
                continue;
            else {
                var argName = argPairs[i].substring(0, pos);
                var argVal = argPairs[i].substring(pos + 1);

                if(argVal.indexOf('+') != -1)
                    argVal = argVal.replace(/\+/g, ' ');

                argList[argName] = unescape(argVal);
            }
        }
    }

    return argList;
}();

HeadupAnnotation.prototype.addEvent = function(obj, evType, fn) {
    if (obj.addEventListener) {
        obj.addEventListener(evType, fn, false);
        return true;
    } else if (obj.attachEvent) {
        var r = obj.attachEvent("on" + evType, fn);
        return r;
    } else {
        return false;
    }
}

HeadupAnnotation.prototype.isBookmarklet = function() {
    return _headup.isDefined('bookmarklet');
}

HeadupAnnotation.prototype.initSuggestMenu = function(items) {
	var fn = items[0].FullName, dn = items[0].DisplayName;
	
	if( this.widgetMode == 'snippet' ){
		if( fn.length > 25 && dn.length > 12 || fn.length + dn.length > 35 )
			return false;
		
		var type = dn ? ', ' + dn : '';
		j$("#headup div.headup-title span").html( type );
		return false;
	}
	
	if( fn.length > 25 && dn.length > 12 || fn.length + dn.length > 45 )
		return false;

    var menuObj = j$("#headup div.headup-suggest-menu");
    menuObj.html("");
    menuObj.append("<span id='headup-suggest-text'>" + items[0].DisplayName + "</span>");
	
	if( items.length > 1 ){
		var suggestionsList = j$("<ul/>");
		
		menuObj.addClass("headup-has-items");
		j$(items).each(function(index, item) {
			var itemLink = j$("<a/>").append(item.DisplayName);
			itemLink.data("uri", item);

			suggestionsList.append(j$("<li/>").append(itemLink));

			if (index == items.length - 1)
				suggestionsList.appendTo(menuObj);
		});
	
		// now bind the appropriate events to the menu
		
			// handle click outside the Suggest Sense box
			j$("#headup").click(function(e) {
				if ( !j$(e.target).is("#headup-suggest-text") )
					j$("#headup-suggest-text + ul").slideUp("fast");
			});
			
			j$("#headup-suggest-text").click(function(){
				j$(this).next("ul").slideToggle("fast");
			});

			j$("#headup div.headup-suggest-menu ul li a").click(function() {
				j$("#headup div.headup-suggest-menu ul").hide();
				if (j$(this).text() == j$("#headup-suggest-text").text()) // check if already selected
					return false;

				j$("#headup-suggest-text").text( j$(this).text() ); // set the selecated value

				_headup.initIframe( j$(this).data("uri") );
			});
	}else
		menuObj.removeClass("headup-has-items");
}

HeadupAnnotation.prototype.satisfy = function(items){
	j$("#headup-satisfy button").live('click',function(){
		var term = _headup.hoveredElement['headup'].Senses[0].Term;
		var quality = this.className, parentDiv = j$(this.parentNode);
		_headup.writeStatistic({ satisfy:quality, term:term });
		j$("#headup-satisfy").html('Is this helpful? - Thanks!');
		_headup.satisfyTimer = setTimeout(function(){ j$("#headup-satisfy").fadeOut(400) }, 2000);
	});
}

var j$;

String.prototype.startsWith = function(str){
	return (this.indexOf(str) === 0);
}

// remove duplicate values from givven Array
// example: var arr = [1,2,2,3,3,4,5,6,2,3,7,8,5,9];
// 			var unique = arr.unique();
Array.prototype.unique = function(){
    var r = [];
	for(var i = 0; i < this.length; i++){
		if( j$.inArray(this[i], r) == -1 )
			r.push( this[i] );
	}
	return r;
}

if( typeof headup == 'undefined' || !headup ){
	headup = new HeadupAnnotation();
	if (!headup.getQueryStrings.debug || headup.isBookmarklet()) {
		headup.addEvent(window, 'load', function() { headup.preLoad(); });
		setTimeout(function() { headup.preLoad(); }, 1000);
	}
	else
		headup = null;
	
	// ignore firebug console when its not available
	if (!window.console || !console.firebug){
		var names = ["log","debug","info","warn","error","assert","dir","dirxml","group","groupEnd","time","timeEnd","count","trace","profile","profileEnd"];
		window.console = {};
		for (var i = 0; i < names.length; ++i)
			window.console[names[i]] = function() {}
	}
}