/*
 * An URI datatype.  Based upon examples in RFC3986.
 *
 * TODO %-escaping
 * TODO split apart authority
 * TODO split apart query_string (on demand, anyway)
 *
 * @(#) $Id$
 */
 
/* Constructor for the URI object.  Parse a string into its components.*/
function URI(str) {
    if (!str) str = "";
    /* Based on the regex in RFC2396 Appendix B.*/
    var parser = /^(?:([^:\/?\#]+):)?(?:\/\/([^\/?\#]*))?([^?\#]*)(?:\?([^\#]*))?(?:\#(.*))?/;
    var result = str.match(parser);
    this.scheme    = result[1] || null;
    this.authority = result[2] || null;
    this.path      = result[3] || null;
    this.query     = result[4] || null;
    this.fragment  = result[5] || null;
}

/* Restore the URI to it's stringy glory.*/
URI.prototype.toString = function () {
    var str = "";
    if (this.scheme) {
        str += this.scheme + ":";
    }
    if (this.authority) {
        str += "//" + this.authority;
    }
    if (this.path) {
        str += this.path;
    }
    if (this.query) {
        str += "?" + this.query;
    }
    if (this.fragment) {
        str += "#" + this.fragment;
    }
    return str;
};

/* Introduce a new scope to define some private helper functions.*/
(function () {
    /* RFC3986 §5.2.3 (Merge Paths)*/
    function merge(base, rel_path) {
        var dirname = /^(.*)\//;
        if (base.authority && !base.path) {
            return "/" + rel_path;
        }
        else {
            return base.path.match(dirname)[0] + rel_path;
        }
    }

    /* Match two path segments, where the second is ".." and the first must*/
    /* not be "..".*/
    var DoubleDot = /\/((?!\.\.\/)[^\/]*)\/\.\.\//;

    function remove_dot_segments(path) {
        if (!path) return "";
        /* Remove any single dots*/
        var newpath = path.replace(/\/\.\//g, '/');
        /* Remove any trailing single dots.*/
        newpath = newpath.replace(/\/\.$/, '/');
        /* Remove any double dots and the path previous.  NB: We can't use*/
        /* the "g", modifier because we are changing the string that we're*/
        /* matching over.*/
        while (newpath.match(DoubleDot)) {
            newpath = newpath.replace(DoubleDot, '/');
        }
        /* Remove any trailing double dots.*/
        newpath = newpath.replace(/\/([^\/]*)\/\.\.$/, '/');
        /* If there are any remaining double dot bits, then they're wrong*/
        /* and must be nuked.  Again, we can't use the g modifier.*/
        while (newpath.match(/\/\.\.\//)) {
            newpath = newpath.replace(/\/\.\.\//, '/');
        }
        return newpath;
    }

    /* RFC3986 §5.2.2. Transform References;*/
    URI.prototype.resolve = function (base) {
        var target = new URI();
        if (this.scheme) {
            target.scheme    = this.scheme;
            target.authority = this.authority;
            target.path      = remove_dot_segments(this.path);
            target.query     = this.query;
        }
        else {
            if (this.authority) {
                target.authority = this.authority;
                target.path      = remove_dot_segments(this.path);
                target.query     = this.query;
            }        
            else {
                /* XXX Original spec says "if defined and empty"…;*/
                if (!this.path) {
                    target.path = base.path;
                    if (this.query) {
                        target.query = this.query;
                    }
                    else {
                        target.query = base.query;
                    }
                }
                else {
                    if (this.path.charAt(0) === '/') {
                        target.path = remove_dot_segments(this.path);
                    } else {
                        target.path = merge(base, this.path);
                        target.path = remove_dot_segments(target.path);
                    }
                    target.query = this.query;
                }
                target.authority = base.authority;
            }
            target.scheme = base.scheme;
        }

        target.fragment = this.fragment;

        return target;
    };
})();

/*
 * Originally taken from http://tutorialzine.com/2010/07/colortips-jquery-tooltip-plugin/
 * Implemented for QR codes by Konstantin Kovshenin http://kovshenin.com
 * Used For Wordpress Plugin Qrtipsy by Agnel Waghela http://agnelwaghela.bammz.net/
 */

(function($){
	$.fn.colorTip = function(settings){

		var defaultSettings = {
			color		: 'black',
			timeout		: 500,
			size		: 100,
		}
		
		var supportedColors = ['red','green','blue','white','yellow','black'];
		
		/* Combining the default settings object with the supplied one */
		settings = $.extend(defaultSettings,settings);
		
		var shortUrl = '';
		

		/*
		*	Looping through all the elements and returning them afterwards.
		*	This will add chainability to the plugin.
		*/
		
		return this.each(function(){

			var elem = $(this);
			
			/* If the title attribute is empty, continue with the next element*/
			/* if(!elem.attr('title')) return true;*/
			
			/* Creating new eventScheduler and Tip objects for this element.*/
			/* (See the class definition at the bottom).*/
			
			var scheduleEvent = new eventScheduler();
			url = elem.attr("href");
			this_uri = new URI(window.location.href);
			uri = new URI(url);
			url = uri.resolve(this_uri);
			
			content = "<img src='http://chart.apis.google.com/chart?cht=qr&chs=" + settings.size + "x" + settings.size + "&choe=UTF-8&chld=L%7C0&chl=" + url + "' />";
			var tip = new Tip(content);

			/* Adding the tooltip markup to the element and*/
			/* applying a special class:*/
			
			elem.append(tip.generate()).addClass('colorTipContainer');
			
			/* Checking to see whether a supported color has been*/
			/* set as a classname on the element.*/
			
			var hasClass = false;
			for(var i=0;i<supportedColors.length;i++)
			{
				if(elem.hasClass(supportedColors[i])){
					hasClass = true;
					break;
				}
			}
			
			/* If it has been set, it will override the default color*/
			
			if(!hasClass){
				elem.addClass(settings.color);
			}
			
			/* On mouseenter, show the tip, on mouseleave set the*/
			/* tip to be hidden in half a second.*/
			
			elem.hover(function(){

				tip.show(settings.size);
				
				/* If the user moves away and hovers over the tip again,*/
				/* clear the previously set event:*/
				
				scheduleEvent.clear();

			},function(){

				/* Schedule event actualy sets a timeout (as you can*/
				/* see from the class definition below).*/
				
				scheduleEvent.set(function(){
					tip.hide();
				},settings.timeout);

			});
			
			/* Removing the title attribute, so the regular OS titles are*/
			/* not shown along with the tooltips.*/
			
			elem.removeAttr('title');
		});
		
	}


	/*
	*	Event Scheduler Class Definition
	*/

	function eventScheduler(){}
	
	eventScheduler.prototype = {
		set	: function (func,timeout){

			/* The set method takes a function and a time period (ms) as*/
			/* parameters, and sets a timeout*/

			this.timer = setTimeout(func,timeout);
		},
		clear: function(){
			
			/* The clear method clears the timeout*/
			
			clearTimeout(this.timer);
		}
	}


	/*
	/	Tip Class Definition
	*/

	function Tip(txt){
		this.content = txt;
		this.shown = false;
	}
	
	Tip.prototype = {
		generate: function(){
			
			/* The generate method returns either a previously generated element*/
			/* stored in the tip variable, or generates it and saves it in tip for*/
			/* later use, after which returns it.*/
			
			return this.tip || (this.tip = $('<span class="colorTip">'+this.content+
											 '<span class="pointyTipShadow"></span><span class="pointyTip"></span></span>'));
		},
		show: function(size){
			if(this.shown) return;
			
			/* Center the tip and start a fadeIn animation*/
			this.tip.css('margin-top', -size);
			this.tip.css('margin-left',-this.tip.outerWidth()/2).fadeIn('fast');
			this.shown = true;
		},
		hide: function(){
			this.tip.fadeOut();
			this.shown = false;
		}
	}
	
	$.fn.qr = function(settings) {
		var defaultSettings = {
			size		: 100
		}
		settings = $.extend(defaultSettings,settings);
		
		jQuery(this).colorTip(settings);
	};
	
})(jQuery);

