
/**
 * DO NOT USE THIS FILE
 *
 * Use a compressed version instead. A compressor is located here: http://javascriptcompressor.com/
 */
// Global variable that will store image URL strings for rollover functions.
var imgs = {};



/**
 * An image rollover trigger.
 * @param {Element} image
 * @param {String} key
 */
function roll(image, key) {
	if (image && imgs && imgs[key]) {image.src = imgs[key].src;}
};


/**
 * Adds the given image to the rollover set. This essentially preloads the image at the given URL
 * and allows it to be referenced by the given key.
 * @param {String} key
 * @param {String} url
 */
function addImage(key, url) {
    if (key in imgs) return;
    imgs[key] = new Image();
    imgs[key].src = url;
};





/**
 * Displays a confirmation box to the user. If the user clicks ID_OK, then a tapestry linkOnClick
 * (AJAX) call is performed on the element with the given id, and to the given URL. Else, (in the
 * event the ID_CANCEL message was returned) this function returns false. Used to confirm AJAX
 * calls.
 * @param {String} message
 * @param {String} url
 * @param {String} id
 */
function verifyClick(message, url, id) {
    if (confirm(message)) return tapestry.linkOnClick(url, id, false);
    return false;
};

function verifyClickAndWait(message) {
    if (confirm(message)) {
    	tapestry.fx.ajaxStatusAction(true);
    	return true;
    }
    return false;
};



/**
 * Returns the height of the entire page in pixels. The height includes any part of the page that
 * isn't currently visible (in other words, returns the inner height of the client's browser window
 * if they have zoomed to a point where the window does not have to be scrolled).
 */
function getPageHeight() {
    var innerHeight = window.innerHeight;

    // First we check to see if this is a Firefox browser.
    if (innerHeight) {
        // We also check this, as this method won't work without scrollMaxY (though the next one
        // should).
        var scrollMaxY = window.scrollMaxY;
        if (scrollMaxY) return innerHeight + scrollMaxY;
    }

    // IE 6 and Mozilla
    var offsetHeight = document.body.offsetHeight;
    var scrollHeight = document.body.scrollHeight;
    if (scrollHeight > offsetHeight) return scrollHeight;

    // Newer browsers like IE 7/8 and Chrome
    var clientHeight = document.documentElement.clientHeight;
    offsetHeight += document.body.offsetTop;

    // We have to make sure the page body isn't smaller than the screen height (aka, short page on
    // a tall screen, and one that doesn't have a 'sticky' footer).
    if (clientHeight > offsetHeight) return clientHeight;
    return offsetHeight;
};

function getPageWidth() {
    var innerWidth = window.innerWidth;

    // First we check to see if this is a Firefox browser.
    if (innerWidth) {
        // We also check this, as this method won't work without scrollMaxY (though the next one
        // should).
        var scrollMaxX = window.scrollMaxX;
        if (scrollMaxX) return innerWidth + scrollMaxX;
    }

    // IE 6 and Mozilla
    var offsetWidth = document.body.offsetWidth;
    var scrollWidth = document.body.scrollWidth;
    if (scrollWidth > offsetWidth) return scrollWidth;

    // Newer browsers like IE 7/8 and Chrome
    var clientWidth = document.documentElement.clientWidth;
    clientWidth += document.body.offsetLeft;

    // We have to make sure the page body isn't smaller than the screen height (aka, short page on
    // a tall screen, and one that doesn't have a 'sticky' footer).
    if (clientWidth > offsetWidth) return clientWidth;
    return offsetWidth;
};

/**
 * A faster innerHTML function, but one with a caveat or two. Essentially sets the innerHTML of the
 * given element to the given html. Care must be taken to snsure that any pointers to the given
 * element are properly restored when this returns. This uses the shallow clone method, so things
 * like events, child nodes, etc are destroyed in the process (if they were defined).
 * @param {Element} element
 * @param {String} html
 */
function replaceHtml(element, html) {
    /*@cc_on // Pure innerHTML is slightly faster in IE
     element.innerHTML = html;
     return element;
     @*/
    var newElement = element.cloneNode(false);
    newElement.innerHTML = html;
    element.parentNode.replaceChild(newElement, element);
    /* Since we just removed the old element from the DOM, return a reference
     to the new element, which can be used to restore variable references. */
    return newElement;
};




/**
 * Returns a shallow clone of the given node. Some attributes are skipped, style and class being
 * two of them. It is also worth noting that the id of the clone is not the id of the original
 * (which would be the case if we used the ECMA clone() function), but rather the original's id
 * with the word 'clone' appended to the end.
 * @param {Element} node
 */
function getClone(node) {
    var element = document.createElement(node.tagName);
    element.id = node.id + "-clone";
    element.className = node.className;
    for (var j = 0; j < node.attributes.length; j++) {
        if (node.attributes[j].specified) {
            var name = node.attributes[j].nodeName.toLowerCase();
            if (name != "style" && name != "edited" && name != "contenteditable" && name != "id" && name != "class") {
                element.setAttribute(name, node.attributes[j].nodeValue);
            }
        }
    }
    return element;
};





// This is an exceptionally fast trim method found here:
// http://blog.stevenlevithan.com/archives/faster-trim-javascript (trim 12, updated version)
function trim(s) {
    var s = s.replace(/^\s\s*/, ''), i = s.length;
    while (/\s/.test(s.charAt(--i)))
        ;
    return s.slice(0, i + 1);
};


function getFragDebug(box, node, html, seek, ptr, wordCount, maxHeight) {
    var s = '';
    s += 'html: ' + html + '\n';
    s += 'current word: ' + html.substring(ptr, seek) + '\n';
    s += 'current substring: ' + html.substring(0, seek) + '\n';
    s += 'node html: ' + node.innerHTML + '\n';
    s += 'ptr: ' + ptr + '\n';
    s += 'seek: ' + seek + '\n';
    s += 'word count: ' + wordCount + '\n';
    s += 'box height: ' + box.offsetHeight + '\n';
    s += 'max height: ' + maxHeight + '\n';
    return s;
};

/**
 * Essentially, this is a fit-text-to-box function. The goal is to take the given html, and truncate
 * it in such a way that it does not spill out of the given box when placed in it.
 *
 * 1. Extremely large words will be replaced with an ellipsis instead so that other text might have
 * 		a chance to be displayed.
 * 2. If the last line of text is too large too fit inside the box, then an ellipsis will be used
 * 		to indicate that there is more text that cannot be displayed.
 * 3. If a choice must be made whether or not to display an ellipsis or a single word on the last
 * 		line of display text, then the elipsis is NOT used, and the word is displayed instead,
 * 		regardless of whether or not anymore text follows.
 * 4. At no time shall a word (a series of one or more non-whitespace characters) be cut in two.
 * 		Either the entire word is displayed, or not at all.
 *
 *
 * @param {Element} box the box that will have the width and height restrictions.
 * @param {String} html the text that will be placed inside either the box (if the box has no
 * 						Element children) or the box's first child element.
 * @param {Number} maxWidth the maximum pixel width that the box could have.
 * @param {Number} maxHeight the maximum pixel height that the box could have.
 */
function fragment(box, html, maxWidth, maxHeight) {

    var debug = false;

    // Find the element that the given box references. The given box may be an element, or the id
    // of an element.
    var box = dojo.byId(box);

    // We now need to find the element that will contain the actual html that was passed in. This
    // may be the given box, or it may be one of box's children. If box has no element children,
    // then node will just end up being box.
    var node;
    if (box.hasChildNodes()) {
        var children = box.childNodes;
        for (var i = 0; i < children.length; i++) {
            // A node type of 1 implies that the child is an element node.
            if (children[i] && children[i].nodeType == 1) {
                node = children[i];
                break;
            }
        }
    }

    // We now check to see if node is a child of box or box itself.
    var isShallow;
    if (!node) {
        node = box;
        isShallow = true;
        if (debug) alert('box has no element children');
    }

    // Let's try to add a bit of text to the node and see if we are able to display it at all. If
    // not, then we need to either A) not display any text at all, or B) reduce the point size of
    // the font in node until it is able to display properly, or C) increase the height of the box
    // so that at least one line of text can fit inside.
    node.innerHTML = '|';
    if (box.offsetHeight > maxHeight) {
        box.style.height = maxHeight + 'px';
        if (debug) alert('had to increase the box height');
    }

    // Trim any whitespace from the ends of the string, and then kill any duplicate whitespace.
    html = trim(html).replace(/\s\s+/g, ' ');

    // There isn't anything to do if the html is one character or less.
    var length = html.length;
    if (length < 2) {
        node.innerHTML = html;
        if (debug) alert('text is very short - done');
        return;
    }




    // This pointer points to the first character of any given word that we have found in the
    // given html. We will use this to remember where the 'last' word we found began, so that we
    // can undo the adding of that word to our display text.
    var ptr = 0;

    // This pointer will point to the location (which may not be real) immediately after teh current
    // word we have found. We will use this to find words taht we can then try to add to the display
    // text.
    var seek = 0;

    // This represents the number of words that we have found *on the current line*, as opposed to
    // in all. We will use this to make sure that we have at least one word on a line. In other
    // words, if we are trying to place an ellipsis, but would have to remove the only word on the
    // line in order to do that, we don't.
    var wordCount = 0;

    // This will hold values that we refer to as 'words' here. These may not be actual words, but
    // *will* be non empty strings that do not contain whitespace. Words are defined, here, as one
    // or more non-space characters that are bounded either by space characters, or by the string
    // boundaries.
    var word;

    var boxHeight;



    // We will search for words until there are no more words to be found. ptr points to the first
    // character of a word, and is initially zero. length is the length of the given html string,
    // and has been found to be at least 2.
    while (ptr < length) {

        // Starting at ptr (which must be the first character of a word), we search ahead until we
        // find a whitespace.
        seek = html.indexOf(' ', ptr);

        // If no whitespace was found, then we just let seek point to (one more than) the end of the
        // string. Essentially, there should be exactly one more word (since we trimed the string).
        if (seek == -1) seek = length;

        // Now we collect the word that we have found. It is entirely possible that the word has
        // only a single character. ptr points to the first character of a word. seek points to the
        // first character *after* that word (which may be past the end of the string altogether).
        word = html.substring(ptr, seek);

        // First we need to verify that the current word isn't inordinately long. We do this by
        // setting node's inner HTML to the word, and then checking the width of the box.
        node.innerHTML = word;
        boxHeight = box.offsetHeight;
        node.innerHTML = word + ' |';

        if (debug) alert(getFragDebug(box, node, html, seek, ptr, wordCount, maxHeight) + 'testing word length');

        if (box.offsetHeight > boxHeight) {
            if (debug) alert(getFragDebug(box, node, html, seek, ptr, wordCount, maxHeight) + 'word overflow');

            // The word we just found is really long, and may even spill out of the box. Instead of
            // displaying it, we replace the word with an ellipsis. ptr points to the first
            // character of the word we found (slice 1 will include the space before the word). seek
            // points to the space character after the word we found (slice 2 will include the space
            // after the word).
            html = html.slice(0, ptr) + '...' + html.slice(seek);

            // We now must adjust seek, since the position of the space after the word we replaced
            // with an ellipsis is almost definitely in a different index location. ptr points to
            // the first dot in the ellipsis. seek must point to the space immediately after the
            // ellipsis.
            seek = ptr + 3;

            // We also need to calculate the new string length.
            length = html.length;

            if (debug) alert(getFragDebug(box, node, html, seek, ptr, wordCount, maxHeight) + 'word has been removed');
        }

        // We set node's inner HTML to the string that represents the current line we are working
        // on. We don't care about any of the earlier lines, as all we need to do is make sure the
        // current line still fits inside box.
        node.innerHTML = html.substring(0, seek);

        if (debug) alert(getFragDebug(box, node, html, seek, ptr, wordCount, maxHeight) + 'checking current line');

        // Now we check to make sure clone's height isn't to large.
        if (box.offsetHeight > maxHeight) {

            // We can't add that last word, since it made the height too large. Try to add an
            // ellipsis instead, and if that doesn't fit, then we back off one word at a time
            // until we either have only one word left, or the string fits.

            // Set node's inner HTML to everything except for that last word, and then tack on an ellipsis.
            // Note that ptr points to the first character of the last word we added, so the space character
            // at the end (ptr - 1) will already be there.
            node.innerHTML = html.substring(0, ptr) + '...';

            if (debug) alert(getFragDebug(box, node, html, seek, ptr, wordCount, maxHeight) + 'box overflow backed off and used an ellipsis');

            // See if the ellipsis we just added makes the box too tall.
            while (box.offsetHeight > maxHeight) {
                if (debug) alert(getFragDebug(box, node, html, seek, ptr, wordCount, maxHeight) + 'still too tall');

                // If there is only 1 word, let's just keep it so that there is something to show.
                if (wordCount < 2) {
                    // ptr - 1 will remove the space character at the end of the string.
                    node.innerHTML = html.substring(0, ptr - 1);
                    if (debug) alert(getFragDebug(box, node, html, seek, ptr, wordCount, maxHeight) + 'only one word left');
                    break;
                }

                // There must be at least 2 words left, so we can try to remove the last one and then add the ellipsis
                // ptr - 1 gets rid of the space character right before the word we are currently pointing to.
                // The + 1 makes ptr point to the start of the word instead of the space character before it.
                ptr = html.substring(0, ptr - 1).lastIndexOf(' ') + 1;
                node.innerHTML = html.substring(0, ptr) + '...';
                wordCount--;
            }

            if (debug) alert(getFragDebug(box, node, html, seek, ptr, wordCount, maxHeight) + 'done (inside done)');

            return; // one of two exits.
        } else {

            // boxClone's height must not be too tall. In other words, we are allowed to add another line of text to node.
            // We start a new line that contains the last word we added.

            // Update the word count to reflect the fact that we already have a word on this line.
            wordCount += 1;

            if (debug) alert(getFragDebug(box, node, html, seek, ptr, wordCount, maxHeight) + 'box height good - continuing');
        }


        // seek pointed to the space character at the end of the word, so we set ptr to the following character.
        ptr = seek + 1;
    }



    // We also need to set the node's inner HTML, as that may not contain the entire text yet. (It
    // will if there is only a single line of text, as it is displayed, but if there are multiple
    // lines of text, then only the very last line is currently set).
    node.innerHTML = html;

    if (debug) alert(getFragDebug(box, node, html, seek, ptr, wordCount, maxHeight) + 'done (outer done)');

    // one of two exits
};





// JavaScript Document

// Courtesy "The Tallest" -->
// 	http://blog.paranoidferret.com/index.php/2007/10/31/javascript-tutorial-the-scroll-wheel/
/**
 * Causes the event with the given event name to trigger the given callback function when said event
 * occurs for the given element. In other words, this is an event assignment function. This is used
 * to intercept mouse scroll events over flash objects. Essentially, it stops the scroll wheel from
 * scrolling the web page (but not the flash content) when over a flash object.
 * @param {Element} element
 * @param {String} eventName
 * @param {Function} callback
 */
function hookEvent(element, eventName, callback) {
    element = dojo.byId(element);
    if (element == null) return;
    if (element.addEventListener) {
        if (eventName == 'mousewheel') element.addEventListener('DOMMouseScroll', callback, false);
        element.addEventListener(eventName, callback, false);
    } else if (element.attachEvent) element.attachEvent("on" + eventName, callback);
};


/**
 * The 'undo' for the previous function.
 * @param {Element} element
 * @param {String} eventName
 * @param {Function} callback
 */
function unhookEvent(element, eventName, callback) {
    if (typeof(element) == "string") element = document.getElementById(element);
    if (!element) return;
    if (element.removeEventListener) {
        if (eventName == 'mousewheel') element.removeEventListener('DOMMouseScroll', callback, false);
        element.removeEventListener(eventName, callback, false);
    } else if (element.detachEvent) element.detachEvent("on" + eventName, callback);
};

/**
 * Causes the given event to be cancelled.
 * @param {Event} event
 */
function cancelEvent(event) {
    event = event ? event : window.event;
    if (event.stopPropagation) event.stopPropagation();
    if (event.preventDefault) event.preventDefault();
    event.cancelBubble = true;
    event.cancel = true;
    event.returnValue = false;
    return false;
};


function preview(data, swf, deckId) {
	if (!swfobject.hasFlashPlayerVersion('9')) return;
	
	var fp_create = function() {
		dojo.byId('previewContent').innerHTML = '<iframe ' +
			'id="pvFrame"frameborder="0"scrolling="no"width="0"height="0"' +
			'src="/CardPreview.html' +
			'?deckId=' + deckId +
			'&swf=' + swf +
			'&data=' + data +
			'"></iframe>';
	};

	swfobject.addDomLoadEvent(fp_create);
};

function endPreview() {
	dojo.html.setStyle('previewDiv', 'z-index', -1);
	dojo.byId('previewContent').innerHTML = '';
};

function szFrame(width, height) {
	var frame = dojo.byId('pvFrame');
	frame.setAttribute('width', width);
	frame.setAttribute('height', height);
	
	var offset = dojo.html.getScroll().offset;
	var vp = dojo.html.getViewport();
	var pDiv = dojo.byId('previewDiv');

	pDiv.style.top = (offset.y + (vp.height / 2) - (getComputedHeight(pDiv) / 2)) + 'px';
	pDiv.style.left = (offset.x + (vp.width / 2) - (getComputedWidth(pDiv) / 2)) + 'px';

	dojo.html.setStyle(pDiv, 'z-index', 500);
};

function getComputedWidth(e) {
	if (swfobject.ua.ie) { return e.offsetWidth; }
	return document.defaultView.getComputedStyle(e, "").getPropertyValue("width").split('px')[0];
};

function getComputedHeight(e){
	if (swfobject.ua.ie) { return e.offsetHeight; }
	return document.defaultView.getComputedStyle(e, "").getPropertyValue("height").split('px')[0];
};

