// = Science Profiles =
// 
// Library for swapping between content in a single container element
// by triggers in the document, programmatically, or automatically over time.
// 

if (typeof(Science) == 'undefined') {
    Science = {};
}



// === sortFunctions ===
// 
// Variable that holds all the functions for sorting the data array.
// 
sortFunctions = {
    date: function(a, b) {
        if (a.date[0] > b.date[0]) { return -1; }
        if (a.date[0] < b.date[0]) { return 1; }
        if (a.date[0] == b.date[0]) {
            if (a.date[1] > b.date[1]) { return -1; }
            if (a.date[1] < b.date[1]) { return 1; }
            if (a.date[1] == b.date[1]) {
                if (a.date[2] > b.date[2]) { return -1; }
                if (a.date[2] < b.date[2]) { return 1; }
                if (a.date[2] == b.date[2]) {
                    if (a.title > b.title) { return 1; }
                    if (a.title < b.title) { return -1; }
                }
            }
            return 0;
        }
        return 0;
    },
    title: function(a, b) {
        if (a.headline < b.headline) { return -1; }
        if (a.headline > b.headline) { return 1; }
        return 0;
    }
};
sortFunctions.category = sortFunctions.date;
sortFunctions.video = sortFunctions.date;


// == Science.Profiles ==
// 
// Class that gets the data from an XML AJAX request and creates a data array.
// 
Science.Profiles = Class.create({

    // ** {{{Science.Profiles.initialize(after)}}} **
    // 
    // Initializes the XML HTTP Request to get the data from the XML url.
    // * {{{after}}}: the function to run after successful AJAX request and
    // after the results are parsed into javascript data
    // 
    initialize: function(after) {
        this.after = after;
        this.request = new Ajax.Request(this.url, {
            method: 'get',
            onSuccess: this.handleRequest.bind(this),
            onException: function(r,e) { throw(e); }
        });
    },

    // ** {{{Science.Profiles.sortBy(after)}}} **
    // 
    // utility function for sorting (from sortFunctions)
    // 
    sortBy: sortFunctions,

    // ** {{{Science.Profiles.truncate(string, to)}}} **
    // 
    // Utility function for truncating text. Returns a truncated version of
    // {{{string}}} minus any ending punctuation (,;—) and plus an ellipsis.
    // * {{{string}}}: string to truncate
    // * {{{to}}}: an integer, which is the number of characters to which the
    //   {{{string}}} is truncated
    // 
    truncate: function(string, to) {
        if (string.length > to) {
            string = string.substring(0, to);
            string = string.strip();

            // if we end in punctuation, go ahead and remove that
            if (string.match(/[,;—]$/)) {
                string = string.substring(0, string.length-1);
                string = string.strip();
            }

            return string+'...';
        } else {
            return string;
        }
    },

    // ** {{{Science.Profiles.handleRequest(response)}}} **
    // 
    // Parses the XML into usable JS data. Then run the {{{this.after}}}
    // function which we set up upon initialization.
    // * {{{response}}}: an XML HTTP response object
    // 
    handleRequest: function(response) {
        this.data = [];
        this.hero = false;
        this.featured = [];

        var results = response.responseXML.getElementsByTagName('profiles')[0].getElementsByTagName('item');
        for (var i=0; i<results.length; i++) {
            var result = results[i];

            var date = (result.getElementsByTagName('date').length > 0) ? result.getElementsByTagName('date')[0].firstChild.nodeValue : false;

            var categories = (result.getElementsByTagName('category').length > 0) ? result.getElementsByTagName('category')[0].firstChild.nodeValue : false;
            categories = (categories) ? categories.split(",") : false;

            var images = (result.getElementsByTagName('images').length > 0) ? result.getElementsByTagName('images')[0] : false;

            var item = {
                headline: (result.getElementsByTagName('headline').length > 0) ? result.getElementsByTagName('headline')[0].firstChild.nodeValue : false,
                title: (result.getElementsByTagName('title').length > 0) ? result.getElementsByTagName('title')[0].firstChild.nodeValue : false,
                type: (result.getElementsByTagName('type').length > 0) ? result.getElementsByTagName('type')[0].firstChild.nodeValue : false,
                categories: (categories) ? (typeof(categories) == 'object') ? categories : categories[0] : false,
                date: (date) ? date.split('/') : false,
                description: (result.getElementsByTagName('description').length > 0) ? result.getElementsByTagName('description')[0].firstChild.nodeValue : false,
                url: (result.getElementsByTagName('url').length > 0) ? result.getElementsByTagName('url')[0].firstChild.nodeValue : false,
                images: {
                    posterFrame: (images && images.getElementsByTagName('posterFrame').length > 0) ? images.getElementsByTagName('posterFrame')[0].firstChild.nodeValue : false,
                    endState: (images && images.getElementsByTagName('endState').length > 0) ? images.getElementsByTagName('endState')[0].firstChild.nodeValue : false,
                    hero: (images && images.getElementsByTagName('profile').length > 0) ? images.getElementsByTagName('profile')[0].firstChild.nodeValue : false,
                    featured: (images && images.getElementsByTagName('thumb').length > 0) ? images.getElementsByTagName('thumb')[0].firstChild.nodeValue : false,
                    archive: (images && images.getElementsByTagName('thumb').length > 0) ? images.getElementsByTagName('thumb')[0].firstChild.nodeValue : false
                }
            };

            if (result.getAttribute('scope') == 'hero') {
                this.hero = item;
            } else if (result.getAttribute('scope') == 'featured') {
                this.featured.push(item);
            }
            this.data.push(item);
        }

        this.data.sort(this.sortBy.date);
        this.after(this.data, { hero:this.hero, featured:this.featured });
    }
});

// == Science.Profiles.Hero ==
// 
// A sub class of Science.Profiles, which parses the hero data into HTML
// elements and inserts them into the DOM.
// 
Science.Profiles.Hero = Class.create(Science.Profiles, {

    // ** {{{Science.Profiles.Hero.initialize(wrapper, data)}}} **
    // 
    // Initializes the hero. Sets up internal variables and run the show method.
    // * {{{wrapper}}}: string ID of element or HTML element for placing the hero.
    // * {{{data}}}: a data object from {{{Science.Profiles.handleRequest}}}.
    // 
    initialize: function(wrapper, data) {
        this.wrapper = $(wrapper);
        this.data = data;

        this.show();
    },

    // ** {{{Science.Profiles.Hero.showItem(result, type)}}} **
    // 
    // Creates the HTML elements for the individual profile result and returns
    // a document fragment containing them.
    // * {{{result}}}: a profile data object, from {{{Science.Profiles.handleRequest}}}.
    // * {{{type}}}: a string, either {{{'hero'}}}, {{{'featured'}}}, or {{{'archive'}}}
    //   which determines the truncation length and the thumbnail.
    // 
    showItem: function(result, type) {
        var frag = document.createDocumentFragment();

        var main = new Element('div', { className:'profile' });
        frag.appendChild(main);

        var link = new Element('a', { href:result.url });
        if (result.type == 'video') {
            link.href = '#'+result.headline.replace(/[ ,—’\'\.]/g, '-').camelize();
            link.innerHTML = '<span class="watch"></span>';
            link.addClassName('OverlayPanel');
        }
        link.innerHTML += '<img alt="'+result.title+'" src="'+result.images[type]+'">';
        main.appendChild(link.cloneNode(true));

        var text = new Element('h4');
        link.innerHTML = this.truncate(result.headline, this.limit[type].headline);
        text.appendChild(link.cloneNode(true));
        main.appendChild(text);

        var text = new Element('strong');
        text.innerHTML = this.truncate(result.title, this.limit[type].title);
        main.appendChild(text);

        var text = new Element('p');
        text.innerHTML = result.date[1]+'.'+result.date[2]+'.'+result.date[0].substring(2,4)+' <span>/</span> ';
        text.innerHTML += this.truncate(result.description, this.limit[type].description)+' ';
        link.addClassName('more');
        if (result.type == 'video') {
            link.innerHTML = this.videoText;
        } else {
            link.innerHTML = this.textText;
        }
        text.appendChild(link);
        main.appendChild(text);

        if (result.type == 'video') {
            var overlayContainer = new Element('div', { id:link.href.replace(/.*#/, ''), className:'overlayprofile' });
            var overlay = new Element('div', { className:'movie' });
            overlay.innerHTML = '<h2>'+result.headline+'</h2>';
            overlay.innerHTML += '<h3>'+result.title+'</h3>';
            overlay.innerHTML += '<a class="movieLink" href="'+result.url+'">Watch '+result.title+'</a>';
            overlay.innerHTML += '<a class="posterLink" href="'+result.images.posterFrame+'"></a>';
            overlay.innerHTML += '<div class="endState"><img src="'+result.images.endState+'"><p class="pillbutton"><a class="replay" href="#replay"><span>Watch again</span><b>&gt;</b></a></p></div>';
            frag.appendChild(overlayContainer);
            overlayContainer.appendChild(overlay)
        }

        return frag;
    },

    // ** {{{Science.Profiles.Hero.show()}}} **
    // 
    // Creates a document fragment, appends the profile data to it, and
    // appends the fragment to the wrapper.
    // 
    show: function() {
        var frag = document.createDocumentFragment();
        frag.appendChild(this.showItem(this.data, 'hero'));
        this.wrapper.appendChild(frag);
    }

});

// == Science.Profiles.Featured ==
// 
// A sub class of Science.Profiles.Hero, which parses the featured data into
// HTML elements and inserts them into the DOM.
// 
Science.Profiles.Featured = Class.create(Science.Profiles.Hero, {

    // ** {{{Science.Profiles.Featured.show()}}} **
    // 
    // Creates a document fragment, runs through the profiles data array and
    // appends each result to the fragment, then appends the fragment to the
    // wrapper.
    // 
    show: function() {
        var frag = document.createDocumentFragment();

        for (var i=0; i<this.featuredAmount; i++) {
            frag.appendChild(this.showItem(this.data[i], 'featured'));
        }

        this.wrapper.appendChild(frag);
    }

});

// == Science.Profiles.Archive ==
// 
// Dependent on [[/global/scripts/swap_view.html]].
// 
// A sub class of Science.Profiles.Featured, which parses all the data into
// HTML elements and inserts them into the DOM. Also creates a
// AC.ViewMaster.Viewer object, which handles all swapping between the sorting
// links (Category, Date, Title, Video Profiles). In addition, this class
// handles the category view, hiding and showing the more section.
// 
Science.Profiles.Archive = Class.create(Science.Profiles.Featured, {

    currentlySortedBy: 'date',

    sortLinks: function(sortBy) {
        if (!this._sortLinks) {
            var sortBar = $('sortbar');
            sortBar.style.display = 'block';

            this._sortLinks = sortBar.select('a');
        }
    },

    // ** {{{Science.Profiles.Archive.show()}}} **
    // 
    // Creates the view master that handles the sort bar.
    // 
    show: function() {
        this.sortLinks();

        // figure out the default sorting order
        var currentlySortedByLink = $('sortbar').down('a.active');
        var sortBy = currentlySortedByLink.href.replace(/.*#/, '');

        if (!this.viewMaster) {
            this.viewMaster = new AC.ViewMaster.Viewer(null, this.wrapper, 'sorttrigger', { silentTriggers:true, shouldAnimateContentChange:false });
            this.viewMaster.setDelegate(this);

            // set the sections by hand, because we don't want the sections to
            // auto trigger before we've set the delegate
            this._sortLinks.each(function(link) {
                this.viewMaster.addSection(link);
            }.bind(this));
            this.viewMaster.show(this.viewMaster.sectionWithId(sortBy));
        }
    },

    // ** {{{Science.Profiles.Archive.willShow()}}} **
    // 
    // Delegate method of {{{AC.ViewMaster.Viewer}}}. This creates the content
    // node for the incoming section (if we don't have it already).
    // 
    willShow: function(viewMaster, outgoing, incoming) {
        var id = incoming.id.replace('sort-', '');

        // if we aren't already sorted, go ahead and sort the array
        if (id != this.currentlySortedBy) {
            this.data.sort(this.sortBy[id]);
            this.currentlySortedBy = id;
        }

        // create the content if we haven't before
        if (incoming.content.hasClassName(viewMaster.triggerClassName)) {
            var container = new Element('div', { id:id });

            if (id == 'date' || id == 'title') {
                for (var i=0; i<this.data.length; i++) {
                    container.appendChild(this.showItem(this.data[i], 'archive'));
                }
            } else if (id == 'category') {
                this.createCategories(container);
                var categories = {};
                for (var i=0; i<this.data.length; i++) {
                    for (var j=0; j<this.data[i].categories.length; j++) {
                        var category = this.data[i].categories[j];
                        if (typeof(categories[category]) == 'number') {
                            categories[category] = categories[category]+1;
                            if (categories[category] == this.categoryAmount) {
                                this.createCategoryMore(category);
                            }
                        } else {
                            categories[category] = 0;
                        }
                        this.categories[category].appendChild(this.showItem(this.data[i], 'archive'));
                    }
                }
            } else if (id == 'video') {
                for (var i=0; i<this.data.length; i++) {
                    if (this.data[i].type == 'video') {
                        container.appendChild(this.showItem(this.data[i], 'archive'));
                    }
                }
            }

            incoming.content = container;
        }
    },

    // ** {{{Science.Profiles.Archive.createCategories()}}} **
    // 
    // Creates category containers, and appends it to the wrapper.
    // 
    createCategories: function(container) {
        for (var category in this.categories) {
            var element = new Element('div', { id:category });
            element.innerHTML = '<h3>'+this.categories[category]+'</h3>';
            this.categories[category] = element;
            container.appendChild(element);
        }
    },

    // ** {{{Science.Profiles.Archive.createCategoryMore()}}} **
    // 
    // Creates category more containers, and appends it to the wrapper.
    // 
    createCategoryMore: function(category) {
        var container = new Element('div', { className:'more', style:'display:none;' });
        this.categories[category].appendChild(container);
        this.categories[category] = container;

        var showMoreLink = new Element('a', { className:'showmore' });
        showMoreLink.innerHTML = 'Show More Stories';
        this.categories[category].parentNode.appendChild(showMoreLink);

        showMoreLink.observe('click', this.clickedCategoryMoreLink.bind(this, container, showMoreLink));
    }, 

    // ** {{{Science.Profiles.Archive.clickedCategoryMoreLink()}}} **
    // 
    // Event handler for the show more/fewer button. Shows and hides the more
    // category container.
    // 
    clickedCategoryMoreLink: function(container, link, evt) {
        if (link.hasClassName('fewer')) {
            new Effect.BlindUp(container);
            link.removeClassName('fewer');
            link.innerHTML = link.innerHTML.replace('Fewer', 'More');
        } else {
            new Effect.BlindDown(container);
            link.addClassName('fewer');
            link.innerHTML = link.innerHTML.replace('More', 'Fewer');
        }
    }

});


// === OnLoad Function ==
// 
// Inializes the profiles, hero, featured, and archive objects. Also figures
// out the data for each.
// 
Event.observe(window, 'load', function() {
    new Science.Profiles(function(data, more) {
        // figure out the data with the hero profile
        var moreData = data;
        var hero = (more.hero) ? more.hero : moreData[0];
        moreData = moreData.without(hero);

        // initialize the hero profile
        if ($('hero')) {
            new Science.Profiles.Hero('hero', hero);
        }

        // figure out the data with the featured profiles
        var featured = [];
        for (var i=0; i<more.featured.length; i++) {
            if (data.indexOf(more.featured[i]) != -1) {
                moreData = moreData.without(more.featured[i]);
                featured.push(more.featured[i]);
            }
        }
        for (var j=i; j<this.featuredAmount; j++) {
            featured.push(moreData[j]);
        }

        // initialize the featured profiles
        if ($('featured')) {
            new Science.Profiles.Featured('featured', featured);
        }

        // initialize the profiles archive
        if ($('archive')) {
            new Science.Profiles.Archive('archive', data);
        }

        if (document.location.hash) {
            var hash = document.location.hash.replace(/#/, '');
            var section = AC.OverlayPanel.overlay.sectionWithId(hash);
            if (section) {
                AC.OverlayPanel.overlay.show(section);
            }
        }
    });
}, false);
