/*  NiceTitles - Nice titles for your links
    Copyright © 2008, 2009 Alwin Garside

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>. */

/**
 * NiceTitles - Nice titles for your links
 * 
 * @author Alwin "Yogarine" Garside <yogarine@gmail.com>
 */

/**
 * Global variable definitions
 */
var XHTMLNS = 'http://www.w3.org/1999/xhtml';
var ntStdWidth = 300;
var currentNiceTitle;
var ntTarget
var ntDelay;
var ntInterval = 600;

function getStyle(element, styleProperty)
{
	var result;

	if (styleProperty == 'opacity' && getStyle(element,'filter') == 'alpha(opacity=0)') {
		element.style.opacity = 0;
		result = 0;
	}
	else if (styleProperty == 'opacity' && getStyle(element,'filter') == 'alpha(opacity=90)') {
		element.style.opacity = 0.9;
		result = 0.9;
	}
	else if (styleProperty == 'opacity' && getStyle(element,'filter') == 'alpha(opacity=100)') {
		element.style.opacity = 1;
		result = 1;
	}
	else if (element.currentStyle)
		result = element.currentStyle[styleProperty];
	else if (window.getComputedStyle)
		result = document.defaultView.getComputedStyle(element,null).getPropertyValue(styleProperty);
	else if (element.style.getPropertyValue)
		result = element.style.getPropertyValue(styleProperty);
	else
		result = element.style[styleProperty];

	return result;
}

function ntFade(elementId, fadeTime, targetOpacity)
{
	// Get timestamp
	var curTick = new Date().getTime();
	// Get element from Id
	var element = document.getElementById(elementId);
	
	// No need to fade if the element is already at the right opacity
	if (targetOpacity == getStyle(element,'opacity')) return;

	// Check if element is not allready fading
	if (element.targetOpacity == null || element.targetOpacity == getStyle(element,'opacity')) {
		// Prepare element for fade
		element.startOpacity = getStyle(element,'opacity');
//alert(elementId+' opacity: '+element.startOpacity);
		element.targetOpacity = targetOpacity;
		element.fadeTime = fadeTime;
		// start new fade
		ntFadeAnimation(elementId, curTick);
	} else {
		// Just update element's fade properties
		if (fadeTime) {
			element.fadeTime = fadeTime;
			element.fadeTimeLeft = fadeTime;
		}
		element.startOpacity = getStyle(element,'opacity');
		element.targetOpacity = targetOpacity;
	}

	if (getStyle(element,'display') == 'none' && element.targetOpacity > 0) {
		if (element.style.setProperty)
			element.style.setProperty('display','block',null);
		element.style.display = 'block';
	}

}

function ntFadeAnimation(elementId, lastTick)
{
	var curTick = new Date().getTime();
	var elapsedTicks = curTick - lastTick;
	var opacityStep;
	var opacity;

	// Get element from Id
	var element = document.getElementById(elementId);

	if (!element.fadeTimeLeft) element.fadeTimeLeft = element.fadeTime;

	if (element.fadeTimeLeft <= elapsedTicks) {
		element.style.opacity = element.targetOpacity;
		element.style.filter = 'alpha(opacity='+element.targetOpacity*100+')';
		element.fadeTimeLeft = 0;
		if (element.targetOpacity == 0) {
			if (element.style.setProperty)
				element.style.setProperty('display','none',null);
			element.style.display == 'none';
		}
		return;
	}

	element.fadeTimeLeft -= elapsedTicks;
	opacityStep = element.fadeTimeLeft/element.fadeTime;

	// Fading in
	if (element.startOpacity < element.targetOpacity) {
		opacityDiff = element.targetOpacity - element.startOpacity;
		opacity = element.targetOpacity - (opacityDiff * opacityStep);
	// Fading out
	} else if (element.startOpacity > element.targetOpacity) {
		opacityDiff = element.startOpacity - element.targetOpacity;
		opacity = element.startOpacity - (opacityDiff - opacityDiff * opacityStep);
	}
	element.style.opacity = opacity;
	element.style.filter = 'alpha(opacity='+opacity*100+')';

	setTimeout("ntFadeAnimation('"+elementId+"',"+curTick+")", 40);
}

/**
 * Add an eventListener to an object
 * 
 * Works in a cross-browser compatible way.
 * 
 * @param target
 * @param eventType
 * @param callback
 * @return result
 */
function ntAddEventListener(target, eventType, callback)
{
    if (target.addEventListener) {
        target.addEventListener(eventType, callback, false);
        return true;
    } else if (target.attachEvent) {
        var result = target.attachEvent('on'+eventType, callback);
        return result;
    } else {
        target['on'+eventType] = callback;
        return true;
    }
}

/**
 * Adds createElementNS function to document.
 * 
 * This function will add a createElementNS function to the document,
 * if it doesn't exist. 
 * @return
 */
function ntAddCreateElementNS()
{
	if (!document.createElementNS) {
	    document.createElementNS = function(ns, element)
	    {
	        return document.createElement(element);
	    };
	}
}

/**
 * blockquote citations
 * 
 * If a blockquote tag contains a cite attribute, create a link to it. 
 */
function ntBlockquoteCite()
{
	var cite;
    var blockquotes = document.getElementsByTagName('blockquote');

    for (var i=0; i<blockquotes.length; i++) {
        cite = blockquotes[i].getAttribute('cite');
        if ((cite) && (cite != '')) {
            newLink = document.createElementNS(XHTMLNS, 'a');
            newLink.setAttribute('href', cite);
            newLink.className = 'cite-link';
            newLink.appendChild(document.createTextNode(cite));
            newCite = document.createElementNS(XHTMLNS, 'cite');
            newCite.className = 'blockquote-cite';
            newCite.appendChild(document.createTextNode('Source: '));
            newCite.appendChild(newLink);
            blockquotes[i].appendChild(newCite);
            blockquotes[i].removeAttribute('cite');
        }
    }
}

/**
 * ins and del citations
 * 
 * Find ins and del citations, and add links to them. 
 */
function ntInsDelCite()
{
    var insdel = new Array(2);
    insdel[0] = document.getElementsByTagName('ins');
    insdel[1] = document.getElementsByTagName('del');

    for (var i=0; i<insdel.length; i++) {
        if (insdel[i]) {
            for (var j=0; j<insdel[i].length; j++) {
                var cite = insdel[i][j].getAttribute('cite');
                if ((cite) && (cite != '')) {
                    newLink = document.createElementNS(XHTMLNS, 'a');
                    newLink.setAttribute('href', cite);
                    newLink.className = 'cite-link ' + (i == 0 ? 'ins-cite' : 'del-cite');
                    newLink.setAttribute('title','citation of ' + (i == 0 ? 'added' : 'deleted') + ' text');
                    newLink.appendChild(document.createTextNode('#'));
                    insdel[i][j].appendChild(newLink);
                    insdel[i][j].removeAttribute('cite');
                }
            }
        }
    }
}
/**
 * Force IE not to show alternate text as tooltip
 */
function ntRemoveAlt()
{
    images = document.getElementsByTagName('img');

    for (var i=0; i<images.length; i++) {
        var title = images[i].getAttribute('title');
        var alt = images[i].getAttribute('alt');
        if ((alt) && (!title)) {
            images[i].setAttribute('title', '');
        }
    }
}

/**
 * Create the nicetitles
 * 
 * Runs through all the links on the page and starts listening for actions
 */
function makeNiceTitles() {
	var i, j, link, element, month, day, date;

    if (!document.createElement || !document.getElementsByTagName) return;

    // do links
    var links = document.getElementsByTagName('a');
    for (i=0; i<links.length; i++) {
        link = links[i];
        if (link.title) {
            link.setAttribute('nicetitle', link.title);
            link.removeAttribute('title');
            ntAddEventListener(link, 'mouseover', ntShowDelay);
            ntAddEventListener(link, 'mouseout', hideNiceTitle);
            ntAddEventListener(link, 'focus', ntShowDelay);
            ntAddEventListener(link, 'blur', hideNiceTitle);
        }
    }

    // do ins and del tags
    var elements = new Array(2);
    elements[0] = document.getElementsByTagName('ins');
    elements[1] = document.getElementsByTagName('del');
    for (i=0; i<elements.length; i++) {
        if (elements[i]) {
            for (j=0; j<elements[i].length; j++) {
                element = elements[i][j];
                if (element.dateTime) {
                    // HTML/ISO8601 date: yyyy-mm-ddThh:mm:ssTZD (Z, -hh:mm, +hh:mm)
                    month = element.dateTime.substring(5,7);
                    day = element.dateTime.substring(8,10);
                    if (month[0] == '0') month = month[1];
                    if (day[0] == '0') day = day[1];
                    date = new Date(element.dateTime.substring(0,4), month-1, day, element.dateTime.substring(11,13), element.dateTime.substring(14,16), element.dateTime.substring(17,19));
                    element.setAttribute('nicetitle', (i == 0 ? 'Added' : 'Deleted') + ' on ' + date.toString());
                    ntAddEventListener(element, 'mouseover', ntShowDelay);
                    ntAddEventListener(element, 'mouseout', hideNiceTitle);
                    ntAddEventListener(element, 'focus', ntShowDelay);
                    ntAddEventListener(element, 'blur', hideNiceTitle);
                }
            }
        }
    }
}

/**
 * Get position of an element
 * 
 * Returns the X and Y positions of the element
 * @param element
 * @return X and Y positions of the element
 */
function ntGetPosition(element)
{
    var xPos = 0;
    var yPos = 0;

    if (element.offsetParent) {
        do {
            xPos += element.offsetLeft;
            yPos += element.offsetTop;
        } while (element = element.offsetParent);
        return [xPos, yPos];
    } else {
        return [element.x, element.y];
    }
}

/**
 * Get a parent element of a certain element type.
 * 
 * @param element
 * @param parentTagName
 * @return
 */
function ntGetParent(element, parentTagName)
{
    if (element == null) {
        return null;
    } else if (element.nodeType == 1 && element.tagName.toLowerCase() == parentTagName.toLowerCase()) {
        return element;
    } else {
        return ntGetParent(element.parentNode, parentTagName);
    }
}

/**
 * Show nice titles after a delay
 * @param e
 * @return
 */
function ntShowDelay(e)
{
    if (window.event && window.event.srcElement) {
        ntTarget = window.event.srcElement;
	} else if (e && e.target) {
        ntTarget = e.target;
    }
    if (!ntTarget) return;

    // Check if we're actually dealing with the link
    if (ntTarget.nodeType == 3 || (ntTarget.nodeType == 1 && ntTarget.tagName.toLowerCase() != 'ins' && ntTarget.tagName.toLowerCase() != 'del') ) {
        ntTarget = ntGetParent(ntTarget, 'a');
    }
    ntDelay = setTimeout("showNiceTitle(ntTarget)", ntInterval);
}

/**
 * Display nice title for a link.
 * 
 * @param link
 */
function showNiceTitle(link)
{
	var div, contentDiv, content, accessKeyText, accessKeySpan,
	    addressText, address, urlLength, width, height;

    if (currentNiceTitle) hideNiceTitle(currentNiceTitle);

    nicetitle = link.getAttribute('nicetitle');
    div = document.createElementNS(XHTMLNS, 'div');
    div.className = 'nicetitle';
    contentDiv = document.createElementNS(XHTMLNS, 'div');
    contentDiv.className = 'nicetitle-content';
    div.appendChild(contentDiv);
    textNode = document.createTextNode(nicetitle);
    content = document.createElementNS(XHTMLNS, 'p');
    content.className = 'titletext';
    content.appendChild(textNode);

    if (link.accessKey) {
        accessKeyText = document.createTextNode(' [' + link.accessKey + ']');
        accessKeySpan = document.createElementNS(XHTMLNS, 'span');
        accessKeySpan.className = 'accesskey';
        accessKeySpan.appendChild(accessKeyText);
        content.appendChild(accessKeySpan);
    }
    contentDiv.appendChild(content);

    if (link.href) {
        addressText = document.createTextNode(link.href);
        address = document.createElementNS(XHTMLNS, 'p');
        address.className = 'destination';
        address.appendChild(addressText);
        contentDiv.appendChild(address);
    }

	if (link.href) {
        urlLength = link.href.length;
    } else {
        urlLength = nicetitle.length;
    }

    var titleLength = nicetitle.length;

    var urlPixels = urlLength * 6;
    var titlePixels = titleLength * 10;

    if (urlPixels > ntStdWidth) {
    	width = urlPixels;
    } else {
        width = ntStdWidth;
    }
    if (titlePixels > width) {
        width = titlePixels;
    }

    div.style.width = width + 'px';

    var pos = ntGetPosition(link);
    var xPos = pos[0];
    var yPos = pos[1];

    div.style.left = (xPos+15) + 'px';
    div.style.top = (yPos+35) + 'px';

    if (window.innerWidth && ((xPos + width) > window.innerWidth)) {
        div.style.left = (window.innerWidth - width - 25) + 'px';
    }
    if (document.body.scrollWidth && ((xPos + width) > document.body.scrollWidth)) {
        div.style.left = (document.body.scrollWidth - width - 25) + 'px';
    }

    document.getElementsByTagName('body')[0].appendChild(div);

    currentNiceTitle = div;
}

function hideNiceTitle(e)
{
    // clearTimeout
    if (ntDelay) clearTimeout(ntDelay);
    if (!document.getElementsByTagName) return;
    if (currentNiceTitle) {
        document.getElementsByTagName('body')[0].removeChild(currentNiceTitle);
        currentNiceTitle = null;
    }
}

/*
Developed by Robert Nyman, http://www.robertnyman.com
Code/licensing: http://code.google.com/p/getelementsbyclassname/
*/	
var getElementsByClassName = function(className, tag, elm) {
	if (document.getElementsByClassName) {
		getElementsByClassName = function (className, tag, elm) {
			elm = elm || document;
			var elements = elm.getElementsByClassName(className),
				nodeName = (tag)? new RegExp("\\b" + tag + "\\b", "i") : null,
				returnElements = [],
				current;
			for(var i=0, il=elements.length; i<il; i+=1){
				current = elements[i];
				if(!nodeName || nodeName.test(current.nodeName)) {
					returnElements.push(current);
				}
			}
			return returnElements;
		};
	}
	else if (document.evaluate) {
		getElementsByClassName = function (className, tag, elm) {
			tag = tag || "*";
			elm = elm || document;
			var classes = className.split(" "),
				classesToCheck = "",
				xhtmlNamespace = "http://www.w3.org/1999/xhtml",
				namespaceResolver = (document.documentElement.namespaceURI === xhtmlNamespace)? xhtmlNamespace : null,
				returnElements = [],
				elements,
				node;
			for(var j=0, jl=classes.length; j<jl; j+=1){
				classesToCheck += "[contains(concat(' ', @class, ' '), ' " + classes[j] + " ')]";
			}
			try	{
				elements = document.evaluate(".//" + tag + classesToCheck, elm, namespaceResolver, 0, null);
			}
			catch (e) {
				elements = document.evaluate(".//" + tag + classesToCheck, elm, null, 0, null);
			}
			while ((node = elements.iterateNext())) {
				returnElements.push(node);
			}
			return returnElements;
		};
	}
	else {
		getElementsByClassName = function (className, tag, elm) {
			tag = tag || "*";
			elm = elm || document;
			var classes = className.split(" "),
				classesToCheck = [],
				elements = (tag === "*" && elm.all)? elm.all : elm.getElementsByTagName(tag),
				current,
				returnElements = [],
				match;
			for(var k=0, kl=classes.length; k<kl; k+=1){
				classesToCheck.push(new RegExp("(^|\\s)" + classes[k] + "(\\s|$)"));
			}
			for(var l=0, ll=elements.length; l<ll; l+=1){
				current = elements[l];
				match = false;
				for(var m=0, ml=classesToCheck.length; m<ml; m+=1){
					match = classesToCheck[m].test(current.className);
					if (!match) {
						break;
					}
				}
				if (match) {
					returnElements.push(current);
				}
			}
			return returnElements;
		};
	}
	return getElementsByClassName(className, tag, elm);
};
