/*
 * This file contains all the application specific code for OAO/OMO.  It
 * mostly uses the jQuery library (http://jquery.com/).  jQuery is a
 * _relatively_ simple library, the main feature of which is the ability to
 * say $('some.css.selector').  Remember that all jQuery methods return the
 * jQuery object itself, so that you can call further methods on it.  What
 * that means in practise is that you have to say .get(0) or
 * .each(function(){}) if you want to run regular DOM methods.
 *
 * $Id: app.js 78706 2009-09-29 09:29:51Z steve $
 */

/*jslint browser: true */
/*global $, jQuery */

// Keep our stuff in a namespace for cleanliness.
var OAOOMO = {};

// A debugging tool for jQuery.
// http://dom.semantico.net/blog/archives/2007/09/26/jquery-logging/
jQuery.fn.log = function (msg) {
    console.log("%s: %o", msg, this);
    return this;
};

// NB: Returns a boolean, not a jQuery object!
jQuery.fn.exists = function () {
    return this.size() > 0;
};

// Write a mini-jQuery extension to pop up a new window on click.
jQuery.fn.popupClick = function (opts) {
    if (!opts) {
        // OMO-134: Default to something which resembles a default browser window.
        opts = 'menubar=yes,directories=yes,toolbar=yes,status=yes,resizable=yes,scrollbars=yes,location=yes';
    }
    if (!this.attr("href")) {
        // Do nothing if no href attr.
        return this;
    } else {
        return this.click(function (ev) {
            if (!this.popup || this.popup.closed) {
                this.popup = open(this.href, '_blank', opts);
            } else {
                this.popup.focus();
            }
            ev.preventDefault();
        });
    }
};

// Another mini jQuery extension to select the contents of an input field when
// it's set to the default value (so it can be overtyped).
jQuery.fn.selectClick = function () {
    var select_me = function (ev) {
        if (this.value === this.defaultValue) {
            this.select();
        }
    };
    // Handle both focus and click events to try and cover all cases..
    return this.focus(select_me).click(select_me);
};

// Allow selecting comments in the DOM.  Annoyingly, we can't do this as a
// selector, as they only handle elements.
jQuery.fn.comment = function () {
    var comments = [];
    this.each(function () {
        for (var i = 0; i < this.childNodes.length; i++) {
            // Node.COMMENT_NODE
            if (this.childNodes[i].nodeType === 8) {
                comments.push(this.childNodes[i]);
            }
        }
    });
    return $(comments);
};

OAOOMO.printToNewWindow = function () {
    var opts = 'toolbar=yes,status=yes,resizable=yes,scrollbars=yes';
    // XXX No need for two selectors, must fix HTML.
    $('#print_icon a, #iconPrint a').popupClick(opts);
};

OAOOMO.illusToNewWindow = function () {
    // Unfortunately, the width of the image isn't easily available.  This
    // value was determined by looking at grove image data and guessing.
    var opts = 'toolbar=yes,status=yes,resizable=yes,scrollbars=yes,width=550';
    $('#illus a').popupClick(opts);
};

OAOOMO.sibeliusToNewWindow = function () {
    // The sibelius plugin HTML is set up be 750px wide, so make the window
    // a little bit wider.
    var opts = 'toolbar=yes,status=yes,resizable=yes,scrollbars=yes,width=800';
    $('.sibelius a').popupClick(opts);
};

// OMO-94: Make the help popup.
OAOOMO.helpToNewWindow = function () {
    var opts = 'toolbar=yes,status=yes,resizable=yes,scrollbars=yes,width=800';
    $('a#help').popupClick(opts);
};

OAOOMO.externalLinksToNewWindow = function () {
    $('a[href]').each(function () {
        // Curse the infernal variations of DOM0. Sometimes hostname
        // has a port number on it, sometimes it doesn't. Ditto for
        // "host" (which should always have a port number). Instead,
        // just strip off anything after a colon and hope for the best.
        var link_hostname = this.hostname.replace(/:[0-9]+$/, "");

        // Ignore internal links.
        if (link_hostname === document.location.hostname) {
            return;
        }
        // We explicitly don't want this one to popup.
        else if ($(this).hasClass('samewindow')) {
            return;
        }

        $(this).popupClick().attr("title", "Opens in a new window").addClass("external");
    });
};

// This can vary depending upon where we are deployed.  Pick out the root from
// a known link instead.
//
// @see http://peter.michaux.ca/article/3556 for the reason behind the double
// function.
OAOOMO.getRootUrl = function () {
    var url = $('#logolink').attr('href').replace(/(public|subscriber)\/$/, "");
    OAOOMO.getRootUrl = function () {
        return url;
    };
    return url;
};

// Create a new scope.  Functions declared in here will be private.
(function () {

    // Return a new button which can manage the visibility of *node*.
    function newTocButton(node) {
        var toggler = document.createElement('span');
        $(toggler).html("&nbsp;");
        // A miniature API.
        toggler.open = function () {
            $(node).show();
            $(this).removeClass('open');
            this.isOpen = true;
        };
        toggler.close = function () {
            $(node).hide();
            $(this).addClass('open');
            this.isOpen = false;
        };
        $(toggler).addClass("toggle").click(function (ev) {
            if (this.isOpen) {
                this.close();
            } else {
                this.open();
            }
        });

        // What should the default state be?
        if ($(node).hasClass("open-node")) {
            toggler.open();
        } else {
            toggler.close();
        }
        return toggler;
    }

    function getIDFromURL() {
        return location.hash;
    }

    function getArticleFromURL() {
        var components = location.pathname.split("/");
        return components[components.length - 1];
    }

    // OMA-56 extract current node functionality to use on 'Works pages'
    
    function setCurrentNode(toc_id) {
        // Mark the current node with a special class:
        // NB: This won't always be accurate.  e.g. #firsthit.
        var current_href = getArticleFromURL() + getIDFromURL();       
        // Check for an URL which _ends_ in this href.
        var current_node = $("#" + toc_id + " a[href$=" + current_href + "]");
        // OAO-94 When matching links we are matching the links as they
        // were written by the application and not how the browser 
        // rewrites them. So for some links that have an anchor we should
        // just look for the anchor.
        var split_href;

        if (current_href.indexOf("#") !== -1) {
            split_href = current_href.split("#");
            current_node = $("#" + toc_id + " a[href$=" + split_href[1] + "]");
        }
        if (current_node.exists()) {
            current_node.addClass('current-node');

            // mark each parent ul node with a special class:
            current_node.parents("ul").addClass('open-node');

            // and also each sibling ul node:
            current_node.siblings("ul").addClass('open-node');
        }

    }
    
    OAOOMO.setupToc = function (toc_id) {
        // Mark the first generation toc entries with a special class,
        // so that they are *always* opened.
        $("#" + toc_id + " > ul > li > ul").addClass('open-node');

        setCurrentNode(toc_id);
        
        $("#" + toc_id + " a").each(function () {
            // If the link contains a sibling "ul", then add a selector
            // and open/close it depending on relationship with current node

            var contents = $(this).next('ul');
            if (contents.exists()) {
                // Stick in a new button to do the toggling.
                $(this).before(newTocButton(contents[0]));
            }
        });
    };
    
    OAOOMO.setCurrentNode = function (toc_id) {

        setCurrentNode(toc_id);
        
    };

    // OMA-105 function added highlight correct link when moving
    // within a page
    OAOOMO.setCurrentNodeOnCurrentPage = function (anchor) {

        // remove any existing current nodes
        $('a.current-node').removeClass('current-node');

        anchor.addClass('current-node');
        // mark each parent ul node with a special class:
        anchor.parents("ul").addClass('open-node');
        // and also each sibling ul node:
        anchor.siblings("ul").addClass('open-node');

    };

})();

OAOOMO.setupQuickSearch = function () {
    // First, install the query from the cookie if there is one.
    var results = $.cookie('q');
    if (results) {
        $('#q').val(results);
    }

    // Next, set up the form to save its contents back into the cookie when
    // it's submitted.
    $('#q').parent('form').submit(function (ev) {
        var q = $('#q').get(0);
        if (q.value !== q.defaultValue) {
            $.cookie('q', q.value, {path: OAOOMO.getRootUrl()});
        }
    });
};

// Stores the users preference for results per page (does this separately for
// browse and search results).
// XXX This should be handled server-side.
OAOOMO.setupResultsPerPage = function () {
    $('#results_per_page').submit(function (ev) {
        var rpp = $('#size', this).val();
        $.cookie('RPP', rpp, {path: OAOOMO.getRootUrl()});
    });
};

// Introduce a private scope.
(function () {
    function setHHCookie(value) {
        $.cookie('HH', value, {path: OAOOMO.getRootUrl()});
    }

    function getHHCookie() {
        return $.cookie('HH') || 'On';
    }

    // OMO-98: need to reset the hit highlighting on new searches.
    // We test to see if we're on a new search by looking for the absence
    // of a "_start" parameter in the URL.
    OAOOMO.maybeResetHighlighting = function () {
        if (!location.href.match(/[&?]_start=/)) {
            setHHCookie(null);
        }
    };

    OAOOMO.setupHitHighlighting = function () {
        // Initialize the page according to the status of the Cookie.
        var cookieVal = getHHCookie();
        $('span.hit').each(function () {
            if (cookieVal === 'On') {
                $(this).addClass('highlight');
            } else {
                $(this).removeClass('highlight');
            }
        });

        $('#iconHighlighting').removeClass('hidden');
        $('#iconHighlighting a:first').click(function (ev) {
            // First, go through and toggle all the highlights.
            $('span.hit').each(function () {
                $(this).toggleClass('highlight');
            });
            // Now, do we have any highlights?
            setHHCookie($('span.highlight:first').size() > 0 ? 'On' : 'Off');
            // Don't follow the link.
            ev.preventDefault();
        });
    };
    
    OAOOMO.setupInArticleHitHighlighting = function () {
        $('#articleHighlight').removeClass('hidden');
        $('#iconInArticleHighlighting').click(function (ev) {
            // First, go through and toggle all the highlights.
            $('span.hit_in_article').each(function () {
                $(this).toggleClass('hh_in_article');
            });
            // Don't follow the link.
            ev.preventDefault();
        });
    };
    
    OAOOMO.setupInArticleHighlightingToggle = function () {
        $('#toggleArticleSearchFormAndResults').click(function (ev) {
            $('#articleSearchFormAndResults').toggleClass('noDisplay');
            $('#toggleArticleSearchFormAndResults').toggleClass('open');
            if ($('#articleSearchFormAndResults').hasClass('noDisplay')) {
                $('span.hit_in_article').each(function () {
                    $(this).removeClass('hh_in_article');
                });
            }
            // OMA-150.  Don't focus unless the user actually clicked, as
            // opposed to the code calling click().   This is preventing the
            // browser from jumping to the anchor point.
            else if (typeof ev.button !== 'undefined') {
                $('#qa').focus();
            }
            // Don't follow the link.
            ev.preventDefault();
        });
        $('#articleSearchForm label').addClass('hidden');
        // Show expand button
        $('#toggleArticleSearchFormAndResults').removeClass('hidden');
        $('#articleSearchFormAndResults').addClass('noDisplay');
        // If a search has been done then open results
        if ($('#articleSearchFormAndResults .results').length > 0) {
            $('#toggleArticleSearchFormAndResults').click();
        }
    };
    
    
    OAOOMO.setupMarkSelectedHit = function () {
        $('#articleSearchFormAndResults li a').each(function () {
            $(this).click(function () {
                $('#articleSearchFormAndResults li a').each(function () {
                    $(this).removeClass('selected');
                });
                $(this).addClass('selected');
            });
        });
    };
    
})();

// This is a fairly nasty hack, but the SGK needs POST to logout.  So, create
// a hidden form and POST that instead.
OAOOMO.setupLogoutLink = function (link) {
    $('a#logout').click(function (ev) {
        var form = $("<form action='" + link + "' method='post' />").hide();
        $(this).parent().append(form);
        form.submit();
        ev.preventDefault();
    });
};

OAOOMO.appendAnchorTo = function (sel) {
    if (document.location.hash) {
        $(sel).each(function () {
            this.value = this.value + document.location.hash;
        });
    }
};

OAOOMO.highlightFragments = function () {
    $('div.fragment').each(function () {
        var file = this.id.replace(/_/g, "/").replace(/\/\//g, "_");
        this.title = file;
        $(this).css('border', '1px solid red');
    });
};

// Uncomment the stacktrace on an error page.
OAOOMO.showError = function () {
    $('#error #errmsg').each(function () {
        var error = $(this).comment()[0].data;
        var pre = $("<pre></pre>").text(error);
        $(this).append($('<h3>Error</h3>')).append(pre);
    });
};

OAOOMO.showScores = function () {
    $('ul.search_result_list li.result').each(function () {
        var score = this.className.match(/s-(\d+)/)[1];
        var quality = this.className.match(/q-(\d+)/)[1];
        var html = $("<b>score=" + score + ", quality=" + quality + "</b>");
        $(this).find('br:first').before(html);
    });
};

// Run all this as soon as the page is ready, before onload().
$(document).ready(function () {
    OAOOMO.externalLinksToNewWindow();
    OAOOMO.printToNewWindow();
    OAOOMO.setupHitHighlighting();
    OAOOMO.setupInArticleHitHighlighting();
    OAOOMO.setupInArticleHighlightingToggle();
    OAOOMO.setupMarkSelectedHit();
    // Make these fields select value when clicked.
    $("#user, #lib_card").selectClick();
    OAOOMO.setupQuickSearch();
    OAOOMO.setupResultsPerPage();
    OAOOMO.setupLogoutLink(OAOOMO.getRootUrl() + "LOGOUT");
    // OMO-94: Make the music help into a nasty popup.
    // OAO-72: Do the same for art.
    OAOOMO.helpToNewWindow();
});
