/* iPVP App v 1.5.4.6

1.1     Cleaned up search term handling. Valid chars are now [a-zA-Z\+ ]

1.2     Fixed: Render.pagerHTML() now detects a single page of results and return no html
        Fixed: The list of episodes in the Episode View now displays the last | missing between the last two numbers
        Fixed: The Episode View now has a back button and displays the title associated with the episode
        Fixed: Photo thumbs aren't a fixed size anymore. They still link to full images. Will decide later how to handle that
        Fixed: Porn Stars page has a title now
        Added: New video speed handling mechanism. Use app.getVideoSpeed(). it returns 1-3 (lo-hi) and defaults to 2.

1.3     Fixed Join view needs a back button
        Fixed: Fix styling on images in the Photos View
        Fixed: Bug that hide the page content if a join div didn't exist on the page
        Fixed: #Stars was not properly indicating load done when data was loaded.
        Added: Indicate loading status [pending testing]
        Added: Download App Button

1.4     Fixed: Re-worked handling of Extras data.
        Fixed: Download app back issue fixed. Back button doesn't hide the popup for the sake of the bookmark location
        Fixed: Add spaces in niche keywords
        Fixed: Change episodeList information to display only title/added date/niches

1.4.1   Fixed: Removed processing of actress detail from Render.episodeList()
        Added: Episode Detail html now has .episodeDetail attached so we can target it with specific CSS
        Added: Main Popup Menu hook: app.showMainMenu();

1.4.2   Fixed: Search will use the error message returned from the AjaxData request so we can display different
        messages and change them on the back end
        Fixed: On the star detail page the main image now links to the trailer and trailer button is now Full Episode
        Fixed: Niche list now doesn't have the 4 space indentation on second line and after niches
        Added: Search can now handle upsells

1.4.3   Fixed: Render.extras URL no longer prepends "javascript:"
        Added: Trailer overlay to porn stars detail IF a trailer URL exists
        Added: app.sumbitJoinForm()

1.4.4   Added: #stories and #story handlers

1.4.5   Added buttons for BTS, Strip Tease and model name
        Added: Number of search results to caption in results page
        Fixed: Niches are no longer clickable in episode views
        Fixed: Hide/Show Details in episode detail state is no longer persistent
        Fixed: Bug that didn't allow app mode to properly. App would freeze after JS stopped executing on an error

1.4.6   Added: #view
        Added: app.setAjaxTimeout() and app.removeAjaxTimeout()
        Fixed: Bug with URLs and using window.open() in <a> hrefs. fixURL cleans up the url and links are now passed through onclick

1.4.7   Fixed: Bugs with how the ajax timeout was handling the errors.
        Fixed: When selected a video speed, the #episode view needed to be refreshed
        Fixed: The WallPapers page was giving an error message due to the error update.
        Fixed: Changed the niche and main menu popups to hide when someone clicks outside of the menu

1.4.8   Added: #view lets you specifiy no thumbs
        Added: join view enables Google Analytic tracker. The rest of the pages are tracked in AjaxData
        Fixed: Bug in PopupMenu. Last selected item was not clearing.

1.4.9   Added: Define window.START_PAGE to set a default page dispatched if there currentPath is empty
        Added: Page will not hide header on intial load but it will hide the ULR bar. After that every dispatch will hide the header
        Added: episodeList() lets you definte a thumbURL for custom thumbURL's now. If trailerURL and thumbURL both exist, trailerURL gets used.
        Fixed: Bug in the Dispatcher that was allowing Dispatcher.currentPath to be null even if the url contained a hash on load.

1.4.9.1 Fixed: Bug in the popup menu in how Javascript URL's were getting exectued.

1.5     Added: Transition animation for our pages that works in Webkit browsers

1.5.1   Fixed: hideHeader() calculates the top of nav the correct way
        Added: If the caption in #headerBar navigation is longer than 14 characters it gets turned into the long title style
        Disabled: Sliding animation is temporarily disabled

1.5.2   Fixed: Search results title bar displays "[search term] videos"

1.5.3   Fixed: Finally fixed the issues with the START_PAGE override. Re-worked how the dispatcher works and this should be comopletely
        resolved now.

1.5.3.1 Fixed: The logic in the app load has been changed. If a #path exists when you load the url, it uses that path. If no path exists
        we check for window.START_PAGE and load it if it exists, otherwise load a default in this case #start.

1.5.4   Added: Support for upsells on top of the search results.
        Added: If you load #episodes it will re-direct to #episodes/all

1.5.4.1 Fixed: Restores back button functionality broken in a version 1.5.3
        Fixed: Evaluated all uses of sprintf() to make sure that all data missing was handled

1.5.4.2 Fixed: Stars page - the trailer URL wasn't being filtered through fixURL() so it didn't work properly on the show cased main episode
        Fixed: Stars page - if there was no trailerURL the full espisode button could dissappear on the show cased main episode
        Added: Click tracking on join and trailer functions

1.5.4.3 Fixed: #sites handles null vid counts
        Fixed: #sites uses fixURL() for URL's now so it requires javascript:app.load('yourpath');  to be passed
        Fixed: app.load() strips the hash at the beginning of a path
        Added: If the porn app popup exists it gets displayed when everything is finished loading.

1.5.4.4 Added: noFav in episode detail data returned will hide the favorites button
        Fixed: Bug in the episode detail display where clips would not be diplayed

1.5.4.5 Fixed: No cookie support persistent nav and video speed

1.5.4.6 Added: Enabled downloading by right-clicking a button on episode buttons

*/
(function() {
    window.APP_FORCE_NO_ANIMATION = true;
    window.APP_PC_VIEW_WIDTH = 600;
    window.APP_BODY_OFFSET = 4;
    
    Cookie.create('areCookiesEnabled', true, 1);
    window.APP_COOKIES_ENABLED = Cookie.read('areCookiesEnabled') ? true : false;

    // APP ENTRY POINT
    addEventListener('load', function() {
        var app = new App();
    }, false);

    function App() {
        extend(this, {
            constructor: function() {
                this.firstHideHeader = false;
                this.timeoutMsg = 
                    '<img style="margin-top: 10px;" src="' + IMAGE_PATH + 'aw-snap-120x90.png' + '" alt=""/>' +
                    '<div style="margin: 15px 0 20px 0;">' +
                        'Aw, snap!<br /><br />Something went wrong and the page could not be loaded. Try again in a few seconds.' +
                    '</div>';

                this.dispatcher = new Dispatcher();
                this.dispatcher.receive = delegate(this, this.dispatchReceive);

                this.controller = new Actions();
                this.render = Render;
                this.render.init();

                extendControllerActions(this.controller)
                this.controller.initAction = function(action) {
                    if (action != 'bookmark')
                        app.hideHeader();
                };
                this.controller.route('default', 'sites');
                this.menuBar = MainMenuBar;
                this.nav = null;
                this.pornAppPopup = PornAppPopup;
                
                this.setupWarningPage();
                setupSearchBox();
                setupStreamBox()

                if ($('#nichesMenu')) this.nicheMenu = new PopupMenu('#nichesMenu', true);    
                
                if ((navigator.userAgent.match(/iPhone/i)) || (navigator.userAgent.match(/iPod/i))) 
                    IS_IPHONE = true;

                if (IS_IPHONE) {
                    window.onorientationchange = function() {
                        switch(window.orientation) {
                            case 0:
                                document.body.setAttribute("orient", 'portrait');
                                break;
                            case 90:
                            case -90:
                                document.body.setAttribute("orient", 'landscape');
                        }
                    }

                    window.onorientationchange();
                }

                if (getQueryVar('pcView')) document.body.setAttribute('id', 'pcView');
                if (!wklib.isSupportedMobile()) document.body.setAttribute('id', 'pcView');
                
                window.app = this;
                
                if (this.dispatcher.absolutePath() != '')
                    this.load(this.dispatcher.absolutePath());
                else
                    this.load(window.START_PAGE ? window.START_PAGE : 'sites');

                if (this.pornAppPopup.exists())
                    this.pornAppPopup.show();
            },
            hideHeader: function() {
                if (this.firstHideHeader == true) {
                    var top = $('#mainNav').offsetTop;
                    setTimeout('window.scrollTo(0, '+ top +')', 200);
                }
                else {
                    this.firstHideHeader = true;
                    this.hideURLbar();
                }
            },
            clickTrack: function(track) {
                AjaxData.clickTrack(track);
            },
            hideURLbar: function() {
                setTimeout('window.scrollTo(0, 0)', 200);
            },
            notifyLoad: function() {
                if (!$('#loadBar')) {
                    var div = document.createElement('div');
                    div.setAttribute('id', 'loadBar');
                    div.css('display', 'none');
                    document.body.appendChild(div)
                    div.innerHTML = '<img src="'+ IMAGE_PATH + 'loading.gif" />';
                }
                if ($('#loadBar')) $('#loadBar').css('display', 'block');
            },
            notifyLoadDone: function() {
                if ($('#loadBar')) $('#loadBar').css('display', 'none');
            },
            submitJoinForm: function() { if (document.joinform) document.joinform.submit(); },
            setupWarningPage: function() {
                if ($('#warningPage')) {
                    this.hideAllPageContent();
                    if (("standalone" in window.navigator) && window.navigator.standalone) this.closeWarningPage();
                }
            },
            closeWarningPage: function() {
                this.showAllPageContent();
                document.body.removeChild($('#warningPage'));
                this.hideHeader();
            },
            showMainMenu: function() {
                if ($('#mainPopupMenu')) {
                    if (!this.mainPopupMenu)
                        this.mainPopupMenu = new PopupMenu('#mainPopupMenu', true);
                    this.mainPopupMenu.show();
                }
            },
            showStreamBox: streamBoxShow,
            showSearchBox: searchBoxShow,
            showBookmarkFeature: function() {
                var div = document.createElement('div');
                div.setAttribute('id', 'bookmarkFeature');
                div.css('display','none');
                document.body.appendChild(div)
                div.innerHTML = '<a href="javascript:app.hideBookmarkFeature()"><img src="'+ IMAGE_PATH +'add-bookmark.gif" /></a>';
                div.css('display', 'block');
                this.hideAllPageContent();
                this.hideURLbar();
                if (IS_IPHONE) {
                    addEventListener('orientationchange', this.hideURLbar, true);
                    document.ontouchmove = function(e) { e.preventDefault(); return false; }
                }
            },
            hideBookmarkFeature: function() {
                if ($('#bookmarkFeature')) {
                    document.body.removeChild($('#bookmarkFeature'));
                    this.showAllPageContent();
                    if (IS_IPHONE) {
                        removeEventListener('orientationchange', this.hideURLbar, true);
                        document.ontouchmove = null;
                    }
                }
            },
            hideAllPageContent: function() {
                $('#hideMainPageContent').css('display', 'none');
                $('#mainNav').css('display', 'none');
                $('#masthead').css('display', 'none');
            },
            showAllPageContent: function() {
                $('#hideMainPageContent').css('display', 'block');
                $('#mainNav').css('display', 'block');
                $('#masthead').css('display', 'block');
            },
            doSearch: function(term) {
                this.load('search/' + cleanSearchTerm(term));
            },
            errorMsg: function(msg) {
                app.notifyLoadDone(); 
                if (msg == this.timeoutMsg) {
                    this.render.genericContent('Error', '<div class="genericMsg">' + msg + '</div>');
                }
                else {
                    this.dispatcher.back();
                    alert(msg);
                }
            },
            getVideoSpeed: function() {
                var rate = streamBoxPersist();
                if (rate) {
                    switch (rate) {
                        case 'edge': return 1;
                        case '3g': return 2;
                        case 'wifi': return 3;
                    }
                }
                return 2;
            },
            lastAction: function() { return this.controller.lastAction; },
            load: function(path) {
                if (path.charAt(0) == '#') 
                    path = path.substr(1);
                if (path == 'episodes') path = 'episodes/all';
                this.dispatcher.dispatch(path);
            },
            reload: function() { this.dispatcher.refresh(); },
            dispatchReceive: function(path) {
                var action = path[0];
                var args = path.splice(1, path.length-1);
    
                if (this.controller.isValid(action)) {
                    if (action != 'join' && $('#hideMainPageContent').css('display') == 'none' && !$('#warningPage'))
                        hideJoin();
                    if (!$('#joinContent') && $('#hideMainPageContent').css('display') == 'none')
                        $('#hideMainPageContent').css('display', 'block');
                    if ($('#bookmarkFeature')) app.hideBookmarkFeature();
                        
                    this.controller.doAction(action, args);
                }
                else
                    throw "No controller action for [" + path + "]";
            },
            checkFavToggle: function(buttonID, episodeID, refresh) {
                checkFavToggle(buttonID, episodeID, refresh);
            },
            setAjaxTimeout: function(xmlHttpRequest) {
                if (this.iid) this.removeAjaxTimeout();
                this.xmlHttpRequest = xmlHttpRequest;
                this.iid = setTimeout('app.killAjaxRequest()', AJAX_TIMEOUT);
            },
            killAjaxRequest: function() {
                if (this.xmlHttpRequest) this.xmlHttpRequest.abort();
                this.errorMsg(this.timeoutMsg);
                app.removeAjaxTimeout();
            },
            removeAjaxTimeout: function() {
                if (this.iid) clearTimeout(this.iid);
                this.iid = null;
                this.xmlHttpRequest = null;
            }
        });
        
        this.constructor.apply(this, arguments);
    }


    // Dispatcher class
    // Handle changes to url using the hash tag so the back button can work
    // Use the directory tree concept. root/child/sub-child etc.
    function Dispatcher() {
        extend(this, {
            // public methods
            constructor: function() {
                this.currentHash = null;
            },
            // use this function send to a new url
            // you can pass a path split into an array or a / delimited string to the path            
            dispatch: function(newPath) {
                this.currentHash = '#' + (typeof newPath == 'string' ? newPath.replace(/\/+$/,"") : newPath.join('/'));
                document.location.hash = this.currentHash;
                this.doReceive(this.currentHash);
            },
            absolutePath: function() {
                var path = document.location.hash;
                if (path.charAt(0) == '#') 
                    path = path.substr(1);
                return path;
            },
            currentPath: function() {
                return (this.currentHash ? this.currentHash : '');
            },
            // reloads the current path
            refresh: function() { this.doReceive(this.currentHash); },
            // goes the last accessed path
            back: function() { history.back(); },
            // use this function to update your path without the dispatcher trying to dispatch or route.
            // useful for changing a state 
            updateAppend: function(path) {
                this.currentHash += '/' (typeof path == 'string' ? newPath.replace(/\/+$/,"") : newPath.join('/'));
                document.location.hash = this.currentHash;
            },
            // override this function to handle a new url change
            // the path argument the path split into an array
            receive: function(path) {},
            
            // private methods
            watchHash: function() {
                var checkHash = document.location.hash;
                if (this.currentHash != checkHash) {
                    this.currentHash = checkHash;
                    this.doReceive(checkHash == '' ? '#default' : checkHash);
                }
            },

            doReceive: function(hash) {
                if (this.receive != undefined) {
                    if (hash == '') hash = '#default';
                    if (hash.charAt(0) == '#') 
                        hash = hash.substr(1);
                    this.receive(hash.replace(/\/+$/,"").split('/'));

                    if (!this.watching) {
                        this.watching = true;
                        this.watchHash();
                        window.setInterval(delegate(this, this.watchHash), 500);    
                    }
                }
            },
        });
        this.constructor.apply(this, arguments);
    }
    
    // Action class 
    function Actions() {
        extend(this, {
            constructor: function(app) {
                this.routeMap = {};
                this.lastAction = null;
            },
            doAction: function(action, args) {
                if (this.isValid(action)) {
                    this.initAction(action);
                    if (this.routeMap[action])
                        this[this.a(this.routeMap[action])](args);
                    else
                        this[this.a(action)](args);
                    this.lastAction = action;
                }
            },
            initAction: function() {},
            isValid: function(action) {
                if (typeof action == 'string' && typeof this[this.a(action)] == 'function' && this[this.a(action)])
                    return true;
                else if (typeof action == 'string' && this[this.a(this.routeMap[action])])
                    return true;
                return false;
            },
            addAction: function(action, func) {    if (typeof action == 'string' && typeof func == 'function') this[this.a(action)] = func; },
            bulkActions: function(obj) {
                for (var action in obj) {
                    if (typeof obj[action] == 'function')
                        this.addAction(action, obj[action]);
                }
            },
            route: function(routeFrom, routeTo) {
                if (typeof routeFrom == 'string' && typeof routeTo == 'string')
                    this.routeMap[routeFrom] = routeTo;
            },
            a: function(action) { return 'action_' + action; }
        });
        this.constructor.apply(this, arguments);
    }    
    
    function extendControllerActions(controller) {
        controller.bulkActions({
            view: function(args) {
                if (args.length) {
                    app.notifyLoad();
                    AjaxData.view(args, function(data) {
                        if (data.error) app.errorMsg(data.error); 
                        else Render.view(data);
                        app.notifyLoadDone();
                    });
                }
                else {
                    alert('No view given');
                    app.dispatcher.back();
                }
                
            },
            story: function(args) {
                if (args.length) {
                    app.notifyLoad();
                    AjaxData.story(args[0], function(data) {
                        if (data.error) app.errorMsg(data.error); 
                        else Render.story(data);
                        app.notifyLoadDone();
                    });
                }
                else {
                    alert('No story ID given');
                    app.dispatcher.back();
                }
            },
            stories: function(args) {
                app.notifyLoad();
                AjaxData.stories(args, function(data) {
                    if (data.error) app.errorMsg(data.error);
                    else app.render.stories(data, 'stories');
                    app.notifyLoadDone(); 
                });
            },
            extras: function(args) {
                var page = args.length ? args[0] : 'index';

                switch (page) {
                    case 'index':
                        app.notifyLoad();
                        AjaxData.extras(function(data) {
                            if (data.error) app.errorMsg(data.error);
                            else app.render.extras(data);
                            app.notifyLoadDone();
                        });
                        
                        break;
                        
                    case 'wallpapers':
                        app.notifyLoad();
                        AjaxData.wallPapers(function(data) {
                            if (data.error) app.errorMsg(data.error); 
                            else app.render.genericContent('Wallpapers', data);
                            app.notifyLoadDone();
                        });
                        
                        break;

                    case 'ringtones': 
                        app.nav = new PersistentNav(
                            'pnavRingtones', 'Extras - Ringtones',
                            [['mp3', 'mp3', true], ['m4r', 'm4r']],
                            function() {}
                        );
                        // get the ringtone ajax data and render it
                        AjaxData.ringTones(function(data) {
                            if (data.error) {
                                app.errorMsg(data.error);
                                return;
                            } 
                            app.render.ringTones(app.nav.getState(), data);

                            var li = $$('#ringTonesHTML li');
                            if (!li.length) return;

                            // function used to determine which url (mp3/m4r) to load when you select a ring tone
                            var selectRingTone = function(i) {
                                if (app.menuBar.selectedNav() == 0 && data[i].mp3 !== undefined)
                                    window.location.href = data[i].mp3;
                                else if (app.menuBar.selectedNav() == 1 && data[i].m4r !== undefined)
                                    window.location.href = data[i].m4r;
                            }
                            // what happens when you click a ring tone
                            for (var i=0, l=li.length; i<l-1; i++) {
                                li[i].index = i;
                                li[i].addEventListener('click', function() {
                                    selectRingTone(this.index);
                                }, false);
                            }
                        });
                    
                        break;
                }
            },
            join: function(args) {
                if (window.pageTracker) window.pageTracker._trackPageview();
                if ($('#joinContent')) AjaxData.clickTrack('join');
                showJoin();
            },
            support: function(args) {
                app.notifyLoad();
                AjaxData.support(args, function(data) {
                    if (data.error) { app.errorMsg(data.error); return; }
                    app.render.genericContent(data.caption, data.html);
                    app.notifyLoadDone();
                });
            },
            toprated: function(args) {
                app.notifyLoad();
                AjaxData.topRated(args, function(data) {
                    if (data.error) { app.errorMsg(data.error); return; }
                    app.render.topRated(data, 'toprated', 'Top Rated');
                    app.notifyLoadDone(); 
                });
            },
            episode: function(args) {
                if (args.length > 1 && args[1] == 'photos') {
                    app.notifyLoad();
                    AjaxData.photos(args[0], function(data) {
                        if (data.error) { app.errorMsg(data.error); return; }
                        app.render.photos(data);
                        app.notifyLoadDone(); 
                    });
                }
                else {
                    app.notifyLoad();
                    AjaxData.episode(args[0], function(data) {
                        if (data.error) { app.errorMsg(data.error); return; }
                        app.render.episode(data);
                        app.notifyLoadDone(); 
                    });
                }
            },
            search: function(args) {
                if (args.length) {
                    app.notifyLoad();
                     AjaxData.search(args, function(data) {
                        app.render.search_(data, 'search/' + args[0]);
                        app.notifyLoadDone(); 
                    });
                }
                else {
                    app.dispatcher.back();
                    alert('No search term provided');
                }
            },
            stars: function(args) {
                if (args.length) {
                    app.notifyLoad();
                    AjaxData.star(args[0], function(data) {
                        if (data.error) {
                            alert('No star by that name found');
                            app.dispatcher.back();
                            app.notifyLoadDone(); 
                            return;
                        }
                        app.render.star(data);
                        app.notifyLoadDone();
                    });
                }
                else {
                    alert('No star name given');
                    app.dispatcher.back();
                }
            },
            favorites: function(args) { AjaxData.favorites(args, function(data) { app.render.favorites(data, 'favorites'); }); },
            pornstars: function(args) {
                app.notifyLoad();
                AjaxData.stars(args, function(data) {
                    if (data.error) { app.errorMsg(data.error); return; }
                    app.render.pornStars(data, 'pornstars');
                    app.menuBar.setCaption('Porn Stars');
                    app.notifyLoadDone(); 
                });
             },
            sites: function(args) {
                // list of all of the sites
                app.nav = new PersistentNav(
                    'pnavSiteList', 'Sites',
                    [['Updates', 'updates', true], ['Rating', 'rating']],
                    function() { app.reload(); }
                );
                app.notifyLoad();
                AjaxData.sites(args, app.nav.getState(), function(data) {
                    if (data.error) { app.errorMsg(data.error); return; }
                    app.render.sites(data);
                    app.notifyLoadDone(); 
                });
            },
            episodes: function(args) {
                var siteShort = args[0];
                // list episodes from a particular site
                app.notifyLoad();
                AjaxData.getSiteName(siteShort, function(data) {
                    if (data.error) { app.errorMsg(data.error); return; }
                    app.nav = new PersistentNav(
                        'pnavEpisodeList', data.siteName,
                        [['Updates', 'updates', true], ['Rating', 'rating']],
                        function() { app.reload(); }
                    );
                    app.notifyLoad(); 
                    AjaxData.episodes(args, app.nav.getState(), function(data) {
                        if (data.error) { app.errorMsg(data.error); return; }
                        app.render.episodes(data, 'episodes/'+ siteShort);
                        app.notifyLoadDone(); 
                    });

                });
            }
        });
    }
    
    var Render = {
        init: function() {
            Content.init(this.contentContainer);
        },
        contentContainer: '.pgContent .contentContainer',
        dropShadow: '<div class="dropShadow"><div class="s1"></div><div class="s2"></div><div class="s3"></div><div class="s4"></div></div>',
        resetTopNav: function(selected) {},
        getNicheHTML: function(nicheArray, notClickable) {
            var html = '';
            if (!nicheArray) return '';
            for (var niches='', i=0, l=nicheArray.length, o=nicheArray[0]; i<l; i++, o=nicheArray[i]) {
                if (o.length > 1) {
                    if (notClickable) html += o[0].split(' ').join('&nbsp;');
                    else html += '<a class="niche" href="javascript:app.load(\'search/'+o[1]+'\')">'+o[0].split(' ').join('&nbsp;')+'</a>';

                    if (i<l-1) html += notClickable ? ', ' : ' ';
                }
            }
            return html;
        },
        /* thumbImage, thumbAction, trailerOverlay (true/false), title, lead, leadEm, summary, buttons[].caption, buttons[].action */
        episodeHTML: function(ep, favView, smallIndexView) {
            if (smallIndexView != undefined && smallIndexView)
                var thumbSize = '';
            else
                var thumbSize = ' large';
            if (!ep.thumbImage) {
                thumbSize += ' noThumb';
                var thumbHTML = '';
            } //
            else {
                thumbHTML = sprintf(
                    '<div class="thumb" style="background-image: url(%s)">%s' + 
                        '<a class="hit" %shref="%s"><img src="'+ IMAGE_PATH + 'blank.gif" alt="" /></a>' +
                    '</div>',
                    ep.thumbImage ? ep.thumbImage : '',
                    ep.trailerOverlay ? '<div class="trailerOverlay"></div>' : '',
                    ep.trailerOverlay ? 'onclick="app.clickTrack(\'trailer\');" ' : '',
                    ep.thumbAction ? ep.thumbAction : 'javascript:void(0)'
                );
            }

            var html = '';
            if (ep.buttons && ep.buttons.length) {
                for (var i=0, l=ep.buttons.length;i<l;i++)
                    html += sprintf('<a class="stdButton" onclick="%s" href="javascript:void(0)">%s</a>', (ep.buttons[i].action?fixURL(ep.buttons[i].action):'javascript:void(0)'), (ep.buttons[i].caption?ep.buttons[i].caption:'Untitled'));
            }
            if (favView) {
                if (ep.fav) {
                    var buttonID = 'fav-' + ep.id;
                    html += '<a id="' + buttonID + '"class="toggleButton toggleButtonOff favButton" href="javascript:app.checkFavToggle(\'' + buttonID + '\',\''+ ep.id + '\', true)"><span class="icon fav"></span>Fav</a>';
                }
            }

            return sprintf(
                '<div class="thumbIndex%s">%s<div class="info">%s<div><span class="leadText">%s</span><br />%s</div>%s</div></div>',
                (thumbSize ? thumbSize : ''), (thumbHTML ? thumbHTML : ''),
                ep.title ? '<h1>'+ ep.title + '</h1>'  : '',
                (ep.lead ? ep.lead : '') + (ep.leadEm ? '<span> '+ ep.leadEm +'</span>' : ''),
                ep.summary ? ep.summary : '',
                html
            );
        },
        pagerHTML: function(current, total, path, newClass) {
            if (total < 2) return '';
            var color = newClass ? newClass + ' ' : 'pink ';
            current = parseInt(current);
            total = parseInt(total);
            var nums = centerNum(current, total, 5);
            for (var i=0, l=nums.length, html=''; i<l; i++) {
                html += sprintf('<a class="page%s" href="javascript:app.load(\'%s\')">%d</a>%s',
                    (nums[i]==current?' current':''), (path+'/'+nums[i]), nums[i], (i < l-1 ?'<span class="sep">|</span>':''));
            }
            return sprintf(
                '<div class="largeButton %spager"><div class="overlay"></div><div class="container">%s%s%s</div></div>',
                color,
                current > 1 ? '<a class="prev" href="javascript:app.load(\''+(path+'/'+(current-1))+'\')"">&lt;</a>' : '',
                current < total ? '<a class="next" href="javascript:app.load(\''+(path+'/'+(current+1))+'\')">&gt;</a>' : '', html
            );
        },
        episodeList: function(data, path, options) {
            if (!data) return '';
            var html = '';
            var favView = options != undefined ? options.favView != undefined ? options.favView : false : false;
            var hidePager = options != undefined ? options.hidePager != undefined ? options.hidePager : false : false;
            for (var i=0, l=data.episodes.length;i<l;i++) {
                var o = {
                    id: data.episodes[i]['id'],
                    thumbImage: data.episodes[i]['thumb'],
                    title: (data.episodes[i]['title'] ? data.episodes[i]['title'] : ''),
                    lead: data.episodes[i]['added'],
                    leadEm: (data.episodes[i].niches ? ' - ' + this.getNicheHTML(data.episodes[i].niches, true) : ''),
                    fav: data.episodes[i]['fav']
                };
                
                if (data.episodes[i]['trailerURL']) {
                    o.thumbAction = data.episodes[i]['trailerURL'];
                    o.trailerOverlay = true;
                }
                else if (data.episodes[i]['thumbURL']) {
                    o.thumbAction = data.episodes[i]['thumbURL'];
                    o.trailerOverlay = false;
                }

                var buttons = [];
                if (data.episodes[i]['fullURL']) buttons.push({action: 'javascript:app.load(\'' + data.episodes[i]['fullURL'] + '\')', caption: 'Full Episode'});
                if (data.episodes[i]['photosURL'] && !favView) buttons.push({action: 'javascript:app.load(\'' + data.episodes[i]['photosURL'] + '\')', caption: 'Pics'});
                if (buttons.length) o.buttons = buttons;
                
                html += this.episodeHTML(o, favView);
            }
            
            html = sprintf(
                '<div class="thumbView">%s%s</div>%s',
                (!hidePager ? this.dropShadow : ''), html,
                (!hidePager ? this.pagerHTML(data.current, data.total, path) : '')
            );
            return html;
        },

        /*** PAGE RENDERS ***/
        view: function(data, path) {
            // paged data will be supported in the future
            if (data.thumbs != undefined && (data.thumbs == 'large' || data.thumbs == 'small'))
                var thsize = data.thumbs;
            else
                var thsize = 'small';
            var paged = data.paged ? data.paged : false;
            var html = '';
            for (var i=0, l=data.items.length; i<l; i++) {
                var buttonArray = [];
                for (var j=0, l2=data.items[i].buttons.length; j<l2; j++) {
                    if (data.items[i].buttons[j].length == 2) {
                        buttonArray.push({
                            caption: data.items[i].buttons[j][0],
                            action: data.items[i].buttons[j][1]
                        });
                    }
                }
                html += this.episodeHTML(
                    {    thumbImage: data.items[i].thumb,
                        lead: data.items[i].title,
                        summary: data.items[i].summary,
                        buttons: buttonArray
                    },
                    false,
                    data.thumbs == 'small' ? true : false
                )
            }
            Content.replace('<div class="thumbView">' + this.dropShadow + html + '</div>');
            app.menuBar.setCaption(data.pageTitle);
        },
        stories: function(data, path) {
            var html = '';
            if (data.stories) {
                for (var i=0, l=data.stories.length; i<data.stories.length; i++) {
                    data.stories[i].def({ id: 'not-set', thumb: IMAGE_PATH + 'blank.gif', title: 'Title not set', summary: '' });
                    html += sprintf(
                        '<div class="thumbIndex large"><div onclick="app.load(\'story/%s\')" class="thumb"style="background-image: url(%s)"></div>' +
                        '<div class="info"><h1>%s</h1><div>%s</div><a class="stdButton" href="javascript:app.load(\'story/%s\')">Read Story</a></div></div>',
                        data.stories[i].id, data.stories[i].thumb, data.stories[i].title, data.stories[i].summary, data.stories[i].id
                    );
            
                }
                html = sprintf(
                    '<div class="thumbView">%s%s</div>%s',
                    this.dropShadow, html, data.total ? this.pagerHTML(data.current, data.total, path) : ''
                );
            }
            else 
                html = '<div class="genericMsg">No stories to list</div>';

            Content.replace(html);
            app.menuBar.setCaption('Erotic Stories');
        },
        story: function(data) {
            data.def({title: 'Story title not set', content: 'No story information!<br /><br />' });
            Content.replace(
                sprintf('<div class="topSpacer"></div><div class="storyBox"><h1>%s</h1><div class="story">%s</div></div>', data.title, data.content)
            );
            app.nav = new PersistentNav(
                'pnavEroticStories', 'Erotic Stories',
                [['Back', 'back', true]], function(a) { app.dispatcher.back(); }
            );
        },
        extras: function(data) {
            var html = '<div class="thumbView extrasEntrance">' + this.dropShadow;
            for (var i=0, l=data.length; i<l; i++) {
                data[i].def({thumb: IMAGE_PATH + 'blank.gif', title: 'Title not set', lead: '', desc: '', url: 'javascript:alert(\'Path not set\')', button: 'Caption not set'});
                html += sprintf(
                    '<div class="thumbIndex">' +
                    '<div class="thumb" style="background-image: url(%s);"></div>' +
                    '<div class="info"><h1>%s</h1><div>%s%s</div><a class="stdButton" onclick="%s" href="javascript:void(0)">%s</a></div></div>',
                    data[i].thumb, data[i].title, 
                    data[i].lead ? '<span class="leadText">' + data[i].lead + '</span><span class="sep">|</span>' : '',
                    data[i].desc, fixURL(data[i].url), data[i].button
                );
            }
            html += '</div>';
            Content.replace(html);
            app.menuBar.setCaption('Extras');
        },
        search_: function(data, path) {
            var html = '';
            if (data.error)
                html = '<div class="genericMsg">'+ this.dropShadow + data.error +'</div>';
            else {
                if (data.numResults) 
                    html = this.episodeList(data, path, {favView:true});
                if (data.upsell)
                    html = '<div class="genericMsg">'+ this.dropShadow + data.upsell +'</div>' + html;
            }
            Content.replace(html);
            app.menuBar.setCaption('"' + data.term + '"'+ ' videos');
        },
        favorites: function(data, path) {
            var html = '';
            if (data.error)
                html = '<div class="genericMsg">'+ data.error +'</div>';
            else
                html = this.episodeList(data, path, {favView:true});
            Content.replace(html);
            setupToggleButtons(this.contentContainer);
            app.menuBar.setCaption('Favorites');
        },
        photos: function(data) {
            var html = '';
            if (data.thumbs) { 
                data.def({images: data.thumbs });
                for (var i=0, l=data.thumbs.length; i<l; i++) {
                    html += sprintf(
                        '<div class="photo"><a href="%s"><img src="%s" alt="" /></a></div>&nbsp;&nbsp;',
                        (data.images[i] ? data.images[i] : ''), (data.thumbs[i] ? data.thumbs[i] : '')
                    );
                }
            }
            else
                html = '<div class="genericMsg">No photos for this episode</div';

            html = sprintf('<div id="photoGallery">%s%s</div>', this.dropShadow, html);
            this.genericContent('Photo Gallery', html, true);
        },
        sites: function(data) {
            if (data.sites) {
                for (var i=0, l=data.sites.length, html='';i<l;i++) {
                    var o = {
                        thumbImage: data.sites[i]['thumb'],
                        thumbAction: data.sites[i]['path'],
                        title: data.sites[i]['title'],
                        lead: (data.sites[i]['vids']  ? data.sites[i]['vids'] + ' vids' : null),
                        leadEm: (data.sites[i]['vids'] ? '| ' : '') + (data.sites[i]['summary'] ? data.sites[i]['summary'] : ''),
                        buttons: [{action: data.sites[i]['path'], caption: 'View Episodes'}]
                    };

                    html += this.episodeHTML(o);
                }
                html = sprintf(
                    '<div class="thumbView">%s%s</div>%s',
                    this.dropShadow, html,
                    this.pagerHTML(data.current, data.total, 'sites')
                );
            }
            else
                html = '<div class="genericMsg">No sites to list</div>';
            Content.replace(html);
        },
        pornStars: function(data, path) {
            for (var i=0, l=data.models.length, html='', model=data.models[0]; i<l; i++, model=data.models[i]) {
                data.models[i].def({path: 'stars', thumb: '', star:'Name Not Set', vids: 0, photos: 0});
                html += sprintf(
                    '<a class="starlet" href="javascript:app.load(\'%s\')" rel="%d"><div class="thumb" style="background-image: url(%s)"></div><h1>%s</h1>' +
                    '<span class="numVids">%d Videos</span><span class="numPix">%d Photos</span></a>',
                    model.path, i, model.thumb, model.star, model.vids, model.photos);
            }
            var pgr = this.pagerHTML(data.current, data.total, path, 'darkGrey');
            html = sprintf('<div class="pagerTop"></div>%s<div class="starsView">%s</div>%s', pgr, html, pgr)
            Content.replace(html);
            eqHeights($$('.starlet'), 3);
        },
        star: function(data) {
            data.def({thumb: IMAGE_PATH + 'blank.gif', currentEpisodeTitle: 'Title not set', photos: 0, vids: 0, added: '', star: 'Star name not given'});
            var html = sprintf(
                '<div class="thumbView">%s<div class="pornStarHdr"><div class="largeThumb" onclick="%s">%s<img class="thumb" src="%s" style="background:url(%s);width:282px;height:159px" alt="" /></div>' +
                '<div><h2>%s</h2>%d videos and %d photos<br />Date Added: %s<br />%s</div></div></div>', 
                this.dropShadow, (data.trailerURL?fixURL(data.trailerURL, 'trailer'):''),
                (data.trailerURL?'<div class="trailerOverlay"></div>':''), 
                IMAGE_PATH + 'blank.gif', data.thumb, data.currentEpisodeTitle, data.vids, data.photos, data.added,
                data.trailerURL ? '<a class="stdButton trailerButn" href="javascript:app.load(\''+ data.episodeURL +'\');">Full Episode</a>' : ''
            );

            if (data.episodes) 
                html += this.episodeList(data, null, {hidePager: true});
            
            Content.replace(html);
            app.menuBar.setCaption(data.star);
        },
        episode: function(data) {
            var html = '';
            var stars = Math.ceil(data.rating);
            
            for (var i=1;i<=5; i++)
                html += '<a href="javascript:void(0)"><span class="'+ (i<stars ? 'fullStar' : 'emptyStar') +'">*</span></a>';


            data.def({title: '', rating: 0, thumb: IMAGE_PATH + 'blank.gif', id: '', summary: ''});
            var favHTML = '';
            if (!data.noFav) {
                favHTML = sprintf(
                    '<a id="theFavButton" class="toggleButton %sfavButton" href="javascript:app.checkFavToggle(\'theFavButton\', \'%s\')"><span class="icon fav"></span>Fav</a>',
                    (data.fav?'toggleButtonOff ':'toggleButtonOn '), data.id
                );
            }
            
            html = sprintf(
                '<div class="thumbView episodeDetail">%s<div class="thumbIndex"><div class="thumb" style="background-image: url(%s);"></div><div class="info">' +
                '<h1>%s</h1><div><span class="rating">%s</span>%d stars</div>' +
                '<a id="hideDetailsButton" class="stdButton" href="javascript:void(0)">Hide Details</a>' +
                '%s</div></div></div></div>',
                this.dropShadow, data.thumb, data.title, html, data.rating, favHTML 
            );
            
            html = sprintf('%s<div class="roundedBox episodeDetails" style="display:none"><div class="top"></div><div class="bottom"></div><div class="container">' +
                    '<strong>Description:</strong><p class="description">%s</p><strong>Niches:</strong><p class="niches">%s</p></div></div>',
                    html, data.summary, this.getNicheHTML(data.niches)
            );

            var clips = '';

            if (data.clipURLS && data.clipURLS.length)  {
                for (var i=0, l=data.clipURLS.length; i<l; i++)
                    clips += '<a href="'+data.clipURLS[i]+'">'+(i+1)+'</a>' + (i<l-1?'<span class="sep">|</span>':'');
                clips = '<h1 class="clipHdr">Video Clips</h1><div class="largeButton pink selectClips"><div class="overlay"></div><div class="container">'+ clips +'</div></div>'
            }

            function button(url, caption, js) {
                var rstr =
                    '<div class="largeButton darkGrey trailerVideoButton">' + 
                        '<div class="overlay"></div><div class="container">' +
                            '<a class="hit" ' + (js ? 'onclick="' + js + '"' :'') + 'href="' + url + '"><img src="'+ IMAGE_PATH + 'blank.gif" width="100%" height="100%" alt="" /></a>' +
                            '<h1>' + caption + '</h1>' +
                        '</div>' +
                    '</div>';
                return rstr;
            }

            // create 1 large button, 2 medium buttons or 3 small ones
            var extras = '';
            var count = 0;
            if (data.trailerURL) extras += button(data.trailerURL, '<span class="icon trailerIcon"></span>Play Trailer', 'app.clickTrack(\'trailer\');'), count++;
            if (data.btsURL) extras += button('javascript:app.load(\'episode/' + data.btsURL + '\')', '<span class="icon trailerIcon"></span>BTS'), count++;
            if (data.stripURL) extras += button('javascript:app.load(\'episode/' + data.stripURL + '\')', '<span class="icon trailerIcon"></span>Strip Tease'), count++;
            if (count > 1) extras = sprintf('<div class="groupButtons%s">%s</div>', String(count), extras);
            var fullEpisodeHTML = '';
            if (data.fullVideoURL) { 
                fullEpisodeHTML = 
                    '<div class="largeButton darkPink fullVideoButton">' +
                        '<div class="overlay"></div>' + 
                        '<div class="container">' +
                        '<a class="hit" href="' + data.fullVideoURL + '"><img src="'+ IMAGE_PATH + 'blank.gif" width="100%" height="100%" alt="" /></a>' +
                        '<h1><span class="icon videoIcon"></span>Play Full Episode</h1></div>' +
                    '</div>';
            }

            html += 
                clips + fullEpisodeHTML + extras +
                (data.photosURL ? button('app.load(\''+ data.photosURL +'\')', '<span class="icon photosIcon"></span>Photos</h1>') : '');
            
            if (data.modelNames) {
                for (i=0, l=data.modelNames.length; i<l; i++)
                    html += button('app.doSearch(\'' + data.modelNames[i] + '\')', 'See more of ' + data.modelNames[i]);
            }

            Content.replace(html);
            
            setupToggleButtons(this.contentContainer);
            setupDetailsBox();
            
            app.nav = new PersistentNav(
                'pnavEpisode', (data.site ? data.site : 'Site not currently defined'),
                [['Back', 'back', true]], function(a) { app.dispatcher.back(); }
            );

        },
        search: function(data, path, caption) {
            Content.replace(this.episodeList(data, path));
        },
        topRated: function(data, path, caption) {
            Content.replace(this.episodeList(data, path));
            app.menuBar.setCaption('Top Rated');
        },
        episodes: function(data, path) {
            Content.replace(this.episodeList(data, path));
        },
        ringTones: function(ringtoneType, data) {
            for (var i=0, l=data.length, html=''; i<l; i++)
                html += '<li>' + (data[i].title ? data[i].title : 'Title not set') + '<span class="icon playIcon">play</span></li>';

            Content.replace(
                sprintf('<div id="ringTonesHTML" class="ringTones">%s<ul class="container">%s<li class="last"></li></ul></div>', this.dropShadow, html)
            );
        },
        genericContent: function(caption, html, noAnimation) {
            app.menuBar.setCaption(caption);
            Content.replace(html, noAnimation);
        }
    };
    
    var Content = {
        init: function(contentContainer) {
            this.content = $(contentContainer);
            this.div1 = null;
            this.div2 = null;
            window.contentSwap = window.contentSwap || {};
            window.contentSwap.isAnimating = false;
        },
        setContentHeight: function(divToSizeTo) {
            this.content.css('height', divToSizeTo.getFullHeight() + 10 + 'px');
        },
        replace: function(html, noAnimate) {
            if (noAnimate == undefined)
                noAnimate = false;
            var agent = navigator.userAgent.toLowerCase();

            if (!/webkit/.test(agent) || APP_FORCE_NO_ANIMATION) {
                this.content.css({overflow: 'auto', position: 'static'});
                this.content.innerHTML = html;
                return;
            }

            if (this.div1 && !noAnimate && !window.contentSwap.isAnimating ) {
				var thisObj = this;

                // create the second animation div
                this.div2 = document.createElement('div');
                this.content.appendChild(this.div2);
                this.content.css({overflow: 'hidden', position: 'relative'});
    
                // set up the div 1 and 2 starting positions
                this.div1.css({left: '0', top: '0', width: '100%', position: 'absolute'});
                this.div2.css({left: '-100%', top: '0', width: '100%', position: 'absolute'});

                // add the content to the new div
                this.div2.innerHTML = html;
                
                // total hack right here! something is not allowing us to calculate height right off the bat.
                setTimeout(function() { thisObj.setContentHeight(thisObj.div2); }, 50);
                
                // start the animation
                this.div1.addClass('revealOut');
				this.div1.css({left: '-100%', top: '0'});
				this.div2.addClass('revealIn');
				this.div2.css({left: '0', top: '0'});
				
				window.pageSwapAnimating = true;
			

				// when we're done with the animation reset and remove
				var f = function() {
					thisObj.div2.removeClass("revealIn");
					thisObj.div1.removeClass("revealOut");
					thisObj.content.removeChild(thisObj.div1);
					thisObj.div1 = thisObj.div2;
					thisObj.div2 = null;
					window.contentSwap.isAnimating = false;
				}
                setTimeout(f, 410);
            }
            else {
                this.content.css({overflow: 'auto', position: 'static', height:'auto' });

                if (!this.div1) {
                    this.div1 = document.createElement('div');
                    this.content.appendChild(this.div1);
                }
                this.div1.style.className = '';
                this.div1.css({position: 'static'});
                this.div1.innerHTML = html;
            }
        },
    }

    var MainMenuBar = {
        isMenuNav: false,
        // use the rel attribute to store the id of the button
        setCaption: function(caption) {
            if (!caption) caption = 'Caption not set';
            $('#headerBar').addClass('noNav');
            $('#headerBar .content').innerHTML = '<h1>'+caption+'</h1>'
            this.isMenuNav = false;
        },
        setNav: function(caption, items) {
            this.isMenuNav = true;
            $('#headerBar').removeClass('noNav');
            var html = '';
            var lastSelected = -1;

            for (i in items) {
                if (items[i].caption) {
                    html += '<a href="javascript:void(0)" rel="' + i + '">' + items[i].caption + '</a>';
                    if (items[i].selected !== undefined && items[i].selected == true)
                        lastSelected = i;
                }
            }
            if (!caption) caption = 'Caption not set';
            var longCap = caption.length > 14 ? true : false;
            if (longCap) caption = '<span>'+(caption ? caption : '')+'</span>';
            html = sprintf('<h1%s><span>%s</span></h1><div class="menu">%s</div>', (longCap?' class="longTitle"':''), caption, html);

            $('#headerBar .content').innerHTML = html;
            if (lastSelected > -1) this.selectedNav(lastSelected);
            this.eachNavItem(function(i) {
                this.addEventListener('click', function() {
                    app.menuBar.selectedNav(this.getAttribute('rel'));
                }, false);
            });
        },
        resetNav: function(newElRel) {
            
        },
        selectedNav: function(index) {
            if (!this.isMenuNav) return;
            if (index != undefined) {
                this.eachNavItem(function(i) {
                    if (i == index) this.addClass('selected');
                    else (this.removeClass('selected'));
                });
                return null;
            }
            else {
                var ret = -1;
                this.eachNavItem(function(i) {
                    if (this.hasClass('selected'))
                        ret = i;
                });
                return ret;
            }
        },
        elFromIndex: function(index) {
            if (!this.isMenuNav) return;
            var el = null;
            this.eachNavItem(function(i) { if (i == index) el = this; });
            return el;
        },
        eachNavItem: function(func) {
            if (!this.isMenuNav) return;            
            if (!func && typeof func !== 'function') return;
            var els = $$('#headerBar .menu a');
            for (var i=0, l=els.length; i<l; i++) {
                func.call(els[i], i);
            }
        }
    };

    // constructor does not actually create the navigation. you must call .create()    
    // new persistentNav('someIdentifier','Pretty Nav Title', [['caption', valueOf, optionalDefaultValueBoolean] [...]], otionalDelayCreation)
    function PersistentNav() {
        extend(this, {
            constructor: function(ident, navTitle, buttons, callback) {
                var nav = [];
                var selected = 0;
                this.ident = ident;
                this.values = [];
                this.callback = callback;
                // setup navigation buttons
                for (var i=0, l=buttons.length; i<l; i++) {
                    var val = buttons[i].length > 1 ? buttons[i][1] : i;
                    nav.push({caption: buttons[i][0]});
                    this.values.push(val);
                    if (buttons[i].length > 2 && buttons[i][2]) selected = i;
                }

                // find the persisted state for the nav if it exists
                if (APP_COOKIES_ENABLED) {
                    if (Cookie.read(this.ident) !== null) {
                        var val = Cookie.read(this.ident);
                        if (this.getIndexFromValue(val) != null)
                            selected = this.getIndexFromValue(val);
                    }
                    else
                        Cookie.create(this.ident, this.values[selected], COOKIE_DAYS);
                }
                else {
                    if (!window.persistentNavData)
                        window.persistentNavData = {};

                    if (!window.persistentNavData[this.ident])
                        window.persistentNavData[this.ident] = this.values[selected];
                    else
                        selected = this.getIndexFromValue(window.persistentNavData[this.ident]);
                }
    
                // set the selected to the persisted state or default value
                nav[selected].selected = true;
                var thisObj = this;
                // create nav
                app.menuBar.setNav(navTitle, nav);
                app.menuBar.eachNavItem(function(i) {
                    this.addEventListener('click', function() {
                        var id = this.getAttribute('rel')
                        thisObj.setState(thisObj.values[id]);
                        if (thisObj.callback) thisObj.callback(thisObj.getState(), this);
                    }, false);
                });
            },
            getIndexFromValue: function(str) {
                for (var i=0, l=this.values.length;i<l;i++) {
                    if (this.values[i] == str)
                        return i;
                }
                return null;
            },
            getState: function() {
                if (APP_COOKIES_ENABLED) {
                    if (Cookie.read(this.ident) !== null)
                        return Cookie.read(this.ident);
                }
                else {
                    if (window.persistentNavData[this.ident])
                        return window.persistentNavData[this.ident];
                }
                return null;
            },
            setState: function(type) {
                if (APP_COOKIES_ENABLED) {
                    if (Cookie.read(this.ident))
                        Cookie.erase(this.ident);
                    Cookie.create(this.ident, type, COOKIE_DAYS);
                }
                else {
                    if (window.persistentNavData[this.ident])
                        window.persistentNavData[this.ident] = type;
                }
                
            }
        });
        this.constructor.apply(this, arguments);
    }

    Object.prototype.def = function(obj) {
        for (prop in obj) {
            if (typeof this[prop] == undefined || this[prop] == null && obj[prop] !== undefined)
                this[prop] = obj[prop];
        }
    }

    function getDocumentSize() {
		var docHeight = 0;
		var docWidth = 0;
 		// most browsers in strict or quirks mode
		if (window.innerHeight) {
			docHeight = Math.max(window.innerHeight, document.body.clientHeight, document.body.scrollHeight, document.body.clientHeight);
			docWidth = Math.max(window.innerWidth, document.body.clientWidth, document.body.scrollWidth, document.body.clientWidth);
		}
		// ie6/7 strict
		else if (document.documentElement && document.documentElement.clientWidth) {
			if (ie6) {
				/* this thing is hacky-hackey. + 20 seems to be the default ie body margin.
				it only matters in the case of the height. the width is caculated correctly. */
				docHeight = document.documentElement.scrollHeight + 20;
				docWidth = document.documentElement.scrollWidth;
				el.style.position = 'absolute';
			}
			else docHeight = document.body.clientHeight;
		}
		// ie6/7 quirks mode
		else {
			docHeight = document.body.scrollHeight + 20;
			docWidth = document.body.scrollWidth;
			el.style.position = 'absolute';
		}
		return { width: docWidth, height: docHeight };
    }

    
    // fix urls for onclick="" rather than href="" for anchor tags
    function fixURL(url, clickTrack) {
        url = url.trim();
        if (url.substr(0, 11) == 'javascript:') url = url.substr(11).trim();
        else url = 'document.location.href=\'' + url + '\'';
        
        if (typeof clickTrack == 'string' && clickTrack == 'trailer')
            url = 'app.clickTrack(\'trailer\'); ' + url;
        return url;
    }
    
    function hideJoin() {
        if (!$('#joinContent')) return;
        $('#hideMainPageContent').css('display', 'block');
        if ($('#joinContent')) $('#joinContent').css('display', 'none');
        if ($('#signupButton')) $('#signupButton').addClass('signup');
    }

    function showJoin() {
        if (!$('#joinContent')) return;
        if ($('#joinContent').css('display') == 'none') {
            if ($('#signupButton')) $('#signupButton').removeClass('signup');
            $('#hideMainPageContent').css('display', 'none');
            $('#joinContent').css('display', 'block');
        }
    }
    
    function cleanSearchTerm(str) {
        if (typeof str == 'string') {
            str = str.trim().toLowerCase();
            var c = '';
            var lastC = '';
            var newStr = '';
            for (var i=0, l=str.length; i<l; i++) {
                c = str.charAt(i);
                if (/[a-z0-9\- ]/.test(c)) {
                    if ((c == ' ' && lastC == ' ') || (c == '+' && lastC == '+')) continue;
                    lastC = c;
                    newStr += (c == ' ' ? '+' : c);
                }
            }
            return newStr;
        }
        return null;
    }
    
    function getQueryVar(variable) { 
        var query = window.location.search.substring(1);
        var vars = query.split("&");
        for (var i=0;i<vars.length;i++) {
            var pair = vars[i].split("="); 
            if (pair[0] == variable)
              return pair[1];
        }
    }

    function getDetailsShow() {
        var defaultValue = 'hide';
        // taking away the cookie setting of this data
        /*var sh = Cookie.read('showDetails');
        if (sh != null)
            return sh;
        Cookie.create('showDetails', defaultValue, 365);*/
        return defaultValue;
    }

    function setupDetailsBox() {
        var detailsButton = document.getElementById('hideDetailsButton');
        if (!detailsButton) return;
        var details = $('.episodeDetails');
        var showDetails = getDetailsShow('show');

        var reconcileDetails = function(show) {
            if (show == undefined)
                show = detailsButton.innerHTML == 'Show Details' ? show = 'show': 'hide';
            if (show == 'show') {
                detailsButton.innerHTML = 'Hide Details';
                details.css('display', 'block');
                Cookie.create('showDetails', 'show', 365);
            }
            else {
                detailsButton.innerHTML = 'Show Details';
                details.css('display', 'none');
                Cookie.create('showDetails', 'hide', 365);
            }
        }

        reconcileDetails(showDetails);
        detailsButton.addEventListener('click', function() {
            reconcileDetails();
        }, false);
    }    
    
    function centerNum(current, total, max) {
        var nums = [];
        if (total > max) {
            var sub = Math.floor(max/2);
            if (!max%2 && sub > 1) sub -= 1;

            var start = current <= sub ? 1 : current - sub;
            var end = start + max;
            for (var i=start; i<end; i++)
                nums.push(i);
        }
        else {
            for (var i=1;i<=total;i++)
                nums.push(i);
        }
        return nums;
    }
    
    function eqHeights(items, cols) {
        /* for webkit */
        var n = items.length;

        if (n) {
            var count = 0;
            var lastHeight = 0;
            for (var i=0, obj=items[0];i<n;i++, obj=items[i]) {
                count++;
                lastHeight = Math.max(lastHeight, obj.clientHeight);

                if (count >= cols || i==n-1) {
                    var start = i-count+1;
                    var end = i;
                    if (end - start) {
                        for (var j=start;j<=end;j++)
                            items[j].style.height = lastHeight + 'px';
                    }
                    count = 0;
                    lastHeight = 0;
                }
            }
        }
    }
    
    function setupToggleButtons(context) {
        var buttons = $$((context ? context + ' ': '') + '.toggleButton');
        var l = buttons.length;
        for (var i=0; i<l; i++) {

            var obj = buttons[i];
            obj.addEventListener('click', function() {
                if (this.hasClass('toggleButtonOn')) {
                    this.removeClass('toggleButtonOn');
                    this.addClass('toggleButtonOff');
                }
                else {
                    this.removeClass('toggleButtonOff');
                    this.addClass('toggleButtonOn');
                }
            }, false);
        }
    }

    function checkFavToggle(buttonID, episodeID, refresh) {
        var obj = document.getElementById(buttonID);
        var f = refresh ? function() { app.reload(); } : function() {};
        if (obj.hasClass('toggleButtonOn'))
            AjaxData.deleteFav(episodeID, f);
        else if (obj.hasClass('toggleButtonOff'))
            AjaxData.addFav(episodeID, f);
    }
    
    /*** SETUP SEARCH BOX ***/
    function setupSearchBox() {
        var searchBox = $('#searchBox');
        if (!searchBox) return;
        var cancelButton = $('#searchCancelButton');
        var submitButton = $('#searchSubmitButton');
        var searchForm = $('#searchBox form[name=searchBoxForm]');

        // setup cancel button inside the search box
        cancelButton.addEventListener('click', function() {
            searchResetPageOffset();
            searchBox.css('display', 'none');
        },
        false);

        submitButton.addEventListener('click', searchDoSubmit, false);
        setupSearchCloseButton();
    }
    function searchDoSubmit(e) {
        searchResetPageOffset();
        $('#searchBox').css('display', 'none');
        app.doSearch($(".searchInput").value);
    }
    function searchBoxShow() {
        var searchBox = $('#searchBox');
        var searchInput = $('#searchBox .searchInput');

        searchBox.lastYOffset = window.pageYOffset;
        searchBox.css({'display': 'block', 'color': '#999', 'top': document.getElementById('masthead').getFullHeight() + 'px'});
        searchInput.focus();
        app.hideURLbar();
    }
    function searchResetPageOffset() {
        var searchBox = document.getElementById('searchBox');
        window.scrollTo(0, searchBox.lastYOffset);
    }

    function setupSearchCloseButton() {
         var close = $(".textBoxClose");
        var input = $(".searchInput");
        var icon = $(".textBoxClose .closeIcon");
        var inputContainer = $(".searchInputContainer");
        var emptyMsg = 'search...';
        var firstFocus = true;

        if (IS_IPHONE) {
            inputContainer.removeClass('noClose');
            checkInput();
            close.onmousedown = function(e) { if (e) e.preventDefault(); };
            close.onclick = function(e) {
                input.value = '';
                input.focus();
                icon.css('display', 'none');
                if (e) e.preventDefault();
            };
        }
        if (input.value == '' && firstFocus) {
            input.value = emptyMsg;
            input.css('color', '#999');
        }

        input.onfocus = function() {
            if (firstFocus && input.value == emptyMsg) {
                input.value = '';
                input.css('color', '#000');
                firstFocus = false;
            }
        }
        input.onkeyup = function(e) { if (e.keyCode == 13) searchDoSubmit(); };

        function checkInput() {
            if (input.css('color') == "#999") input.css('color', '#000');

            if (IS_IPHONE) {
                if (input.value == '') {
                    if (icon.css('display') != 'none')
                        icon.css('display', 'none');
                }
                else {
                    if (icon.css('display') == 'none')
                        icon.css('display', 'block');
                }
            }
        }
    }
    
    /*****************************************************************************
     * STREAMBOX (VIDEO SETTINGS)                                                *
     *****************************************************************************/
    function streamBoxShow(msg) {
        var streamBox = document.getElementById('streamBox');
        var underlay  = document.getElementById('streamBoxUnderlay');
        if (!streamBox) return;
        if (streamBox.css('display') == 'none') {
            var doc = getDocumentSize();
            if (underlay) underlay.css({'display': 'block', 'height': doc.height-APP_BODY_OFFSET + 'px', width: (wklib.isSupportedMobile() ? '100%' : APP_PC_VIEW_WIDTH + 'px') });
            streamBox.css({'top': window.pageYOffset + (window.innerHeight/2 - streamBox.getFullHeight()/2) + 'px',    'display': 'block'});
            if (window.pageYOffset < 2) window.scrollTo(0, 1);
        }
        else
            streamBox.css('display', 'none');
    }
    function streamBoxHideUnderlay() {
        document.getElementById('streamBoxUnderlay').css('display','none');
        document.getElementById('streamBox').css('display', 'none');
    }
    function streamBoxSet(type) {
        var buttons = document.querySelectorAll('#streamBox .streamOptions a');
        for (var i=0; i<buttons.length; i++) {
            var el = buttons[i];
            if (el.getAttribute('rel') == type)
                el.addClass('selected');
            else {
                if (el.hasClass('selected'))
                    el.removeClass('selected');
            }
        }
    }
    function streamBoxPersist(val) {
        // if we have a value, set it
        if (val) {
            if (APP_COOKIES_ENABLED) {
                if (Cookie.read('bitrate'))
                    Cookie.erase('bitrate');
                Cookie.create('bitrate', val, 365);
            }
            else
                window.persistentStreamData = val;
        }
        // otherwise return the current data
        else {
            if (APP_COOKIES_ENABLED) {
                if (Cookie.read('bitrate'))
                    return Cookie.read('bitrate');
            }
            else {
                if (window.persistentStreamData)
                    return window.persistentStreamData;
            }
            return null;
        }
    }
    
    function setupStreamBox() {
        var streamBox = document.getElementById('streamBox');
        window.streamBoxHideUnderlay = streamBoxHideUnderlay;
        if (!streamBox) return false;
        var underlay = htmlCreateUnderlay('streamBoxUnderlay');
        var imgs = new Array();

        if (IS_IPHONE) {
            underlay.ontouchstart = function(e) {
                e.preventDefault();
                streamBoxHideUnderlay();
            }
        }
        else
            underlay.addEventListener('click', streamBoxHideUnderlay, false);

        var buttons = document.querySelectorAll('#streamBox .streamOptions a');
        for (var i=0; i<buttons.length; i++) {
            var el = buttons[i];

            if (IS_IPHONE) {
                el.ontouchstart = function(e) {
                    e.preventDefault();
                    streamBoxSet(this.getAttribute('rel'));
                }
                el.ontouchend = function(e) {
                    e.preventDefault();
                    streamBoxPersist(this.getAttribute('rel'));
                    streamBoxSet(this.getAttribute('rel'));
                    app.reload();
                    setTimeout('streamBoxHideUnderlay()', 100);
                }
            }
            else {
                el.addEventListener('click', function() {
                    streamBoxPersist(this.getAttribute('rel'));
                    streamBoxSet(this.getAttribute('rel'));
                    app.reload();
                    setTimeout('streamBoxHideUnderlay()', 100);
                }, false);
            }
        }

        // if the cookie is not set for streaming bitrate ask for it
        if (Cookie.read('bitrate') != null)
            streamBoxSet(Cookie.read('bitrate'));
        else if (streamBox.hasClass('initSetting'))
            streamBoxShow();
    }
    
    var PornAppPopup = {
        id: 'pornAppPopup',
        exists: function() {
            return $('#' + this.id) ? true : false;
        },
        show: function() {
            var popup = $('#' + this.id);
            if (popup) {
                var underlay = htmlCreateUnderlay(this.id + 'Underlay');
                popup.css({display: 'block', marginLeft: '-' + (popup.getFullWidth()/2+2) + 'px'});
                var doc = getDocumentSize();

                underlay.css({
                    height: doc.height-APP_BODY_OFFSET + 'px',
                    width: (wklib.isSupportedMobile() ? '100%' : APP_PC_VIEW_WIDTH + 'px'),
                    top: '0',
                    zIndex: '1999',
                    position: 'absolute',
                    display: 'block'
                });
                underlay.addEventListener('click', delegate(this, this.hide), false);
                var anchors = $$('#' + this.id + ' a');
                if (anchors && !this.init) {
                    this.init = true;
                    for (var i=0;i<anchors.length;i++)
                        anchors[i].addEventListener('click', delegate(this, this.hide), false);
                }
            }
        },
        hide: function() {
            var popup = $('#' + this.id);
            var underlay = $('#' + this.id + 'Underlay');
            if (popup) popup.css('display', 'none');
            if (underlay) document.body.removeChild(underlay);
        }
    };

    /*** POPUP MENU CODE START ***/
    function htmlCreateUnderlay(name) {
        var div = document.createElement('div');
        div.setAttribute('id', name);
        div.css('display', 'none');
        document.body.appendChild(div)
        return div;
    }
    

    /** popup menu only expects an optional closeButton class for closing the popup and menu items in 
        <li></li> or <li class="selected"></li> 

        update to this function requires fixURL()
    **/
    function PopupMenu() {
        Utils.extend(this, {
            me: 'PopupMenu(): ',
            show: function() {
                this.disabled = false;

                if (!this.popup) throw new Error(this.me + 'Popup does not exist');
                var popupTop = 30;
                this.popup.css({'top': window.pageYOffset + popupTop + 'px' , 'display': 'block'});
                var doc = getDocumentSize();
                var popupH = window.pageYOffset + this.popup.getFullHeight() + (popupTop * 2);
                var pageH = doc.height - APP_BODY_OFFSET;
                this.underlay.css({'display': 'block', 'height': (popupH > pageH ? popupH : pageH) + 'px', 'top': '0'});
                if (!wklib.isSupportedMobile()) this.underlay.addClass('pcViewBG');

                if (window.pageYOffset < 2) window.scrollTo(0, 1);

                if (this.outsideHide) {
                    if (IS_IPHONE) {
                        var thisObj = this;
                        this.underlay.ontouchstart = function(e) {
                            e.preventDefault();
                            thisObj.hide();
                            return false;
                        }
                    }
                    else {
                        this.elistener = delegate(this, this.hide);
                        this.underlay.addEventListener('click', this.elistener, false);    
                    }
                }
            },
            hide: function() {
                this.underlay.css('display','none');
                this.popup.css('display', 'none');
                if (this.outsideHide) {
                    if (IS_IPHONE)
                        this.underlay.ontouchstart = null;
                    else {
                        if (this.elistener)
                            this.underlay.removeEventListener('click', this.elistener, false);
                    }
                }
            },
            getUnderlay: function() {
                var underlay = $('#wkPopupMenuUnderlay');
                if (!underlay) underlay = htmlCreateUnderlay('wkPopupMenuUnderlay');
                return underlay;
            },
            select: function(el) {
                if (el && !el.hasClass('stdButton')) el.addClass('selected');
                this.disabled = true;
                var thisObj = this;
                var href = fixURL(el.href);
                setTimeout(function() {
                    thisObj.hide();
                    thisObj.clearSelected();
                    eval(href);
                }, this.menuCloseDelay);
            },
            clearSelected: function() {
                var lis = $$('.menu a', this.popupup), i = lis.length;
                while (i--) {
                    var el = lis[i];
                    if (el.hasClass('selected')) el.removeClass('selected');
                }
            },
            contructor: function(selector, outsideHide) {
                this.id = selector;
                this.menuCloseDelay = 300;
                this.outsideHide = (outsideHide != undefined ? outsideHide : true);
                this.disabled = false;
                this.popup = $(selector);
                this.underlay  = this.getUnderlay();

                if (!this.popup) throw new Error(this.me + 'Popup "' + selector + '" does not exist');3
                if (this.popup) {
                    var close = $('.closeButton', this.popup)
                    if (close) close.addEventListener('click', delegate(this, this.hide), false);

                    var buttons = $$('.menu a', this.popup), l = buttons.length;
                    for (var i=0; i<l; i++) {
                        var el = buttons[i];
                        var thisObj = this;
                        el.addEventListener('click', function(e) {
                            if (!thisObj.disabled && !this.hasClass('selected')) {
                                thisObj.clearSelected();
                                thisObj.select(this);
                            }
                            e.preventDefault();
                            return false;
                        }, false);
                    }
                }
            }
        });

        this.contructor.apply(this, arguments);
    }
    /*** POPUP MENU CODE END ***/


})();
