/*global jQuery, swfobject, soundManager, $payload, FB */



// http://diveintohtml5.org/detect.html
function supports_video() {
    return !!document.createElement('video').canPlayType;
}
function supports_h264_baseline_video() {
    if (!supports_video()) { return false; }
    var v = document.createElement("video");
    return v.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"');
}
function supports_history_api() {
  return !!(window.history && history.pushState);
}



// http://www.schillmania.com/projects/soundmanager2/
$.extend(soundManager, {
    url:                '/lib/soundmanagerv297a-20110424/',
    debugMode:          false,
    useFlashBlock:      false,
    useHTML5Audio:      true
});

$.easing.def = "easeInOutCubic";

var $defaults = {
    emptyState:                     { path: false, focus: $(), cat: false, catLink: $(), catDesc: $(), catPages: $(), catChildLink: $(), catPage: $() },
    operatorUrl:                    "/operator.php?",
    stateBasePath:                  "",
    baseUrl:                        window.location.href.substr(0, window.location.href.indexOf(window.location.pathname)),
    txDur:                          1000,
    allowPrivate:                   false,
    postsPerCatPage:                4,
    catPagesPerIndexPage:           3,
    categories:                     {},
    pages: {
        "404":                      "Page Not Found"
    },
    searchMap:                      {},
    tagMap:                         {},
    requested:                      [],
    support: {
        video:                      supports_h264_baseline_video(),
        history:                    supports_history_api(),
        chrome:                     navigator.userAgent.indexOf('Chrome') > -1,
        android:                    navigator.userAgent.indexOf('Android') > -1,
        ipad:                       navigator.userAgent.indexOf("iPad") > -1,
        iphone:                     navigator.userAgent.indexOf("iPhone") > -1,
        ipod:                       navigator.userAgent.indexOf("iPod") > -1,
        ff:                         $.browser.mozilla === true,
        ie:                         $.browser.msie === true
    },
    playerAttributes: {
        "class":                    "player-child"
    },
    flashPlayerParams: {
        allowscriptaccess:          'always',
        allowfullscreen:            'true',
        wmode:                      'transparent',
        bgcolor:                    '#000000'
    },
    analyticsUpdateInterval:        10, // (percent)
    bgImgWidth:                     960,
    bgTiles:                        3,
    commentsEnabled:                false,
    autoplay:                       false, // TO DO: this is hacky
    topTenEnabled:                  false
};

var $config = $.extend({}, $defaults, typeof $options != "undefined" ? $options : {});

$config.initPath = window.location.pathname.replace($config.stateBasePath, "");

$config.support.ios = $config.support.ipad || $config.support.iphone || $config.support.ipod;
$config.support.safari = !$config.support.chrome && !$config.support.ios && !$config.support.android && navigator.userAgent.indexOf('Apple') > -1;
if ($config.support.chrome) {
    // chrome doesn't support fullscreen video
    $config.support.video = false;
}
if ($config.support.safari) {
    var versionPos = navigator.userAgent.indexOf("Version/") + 8;
    var safariVersion = parseFloat(navigator.userAgent.substring(versionPos, versionPos + 1));
    
    versionPos = navigator.userAgent.indexOf("Mac OS X ") + 9;
    var macOSVersion = parseFloat(navigator.userAgent.substring(versionPos, navigator.userAgent.indexOf(";", versionPos)).replace(/_/g, "."));
    
    // TO DO: windows safari should also get video support, depending on when fullscreen support was added
    
    if (safariVersion < 5 || macOSVersion < 10.6) {
        // although video support exists in prior versions, only snow leopard safari 5 has a fullscreen mode
        $config.support.video = false;
    }
}
if ($.browser.webkit === true && parseFloat($.browser.version) < 534.10) {
    // WebKit versions < 534.10 have a crippling bug in HTML5 state support:
    // https://bugs.webkit.org/show_bug.cgi?id=42940
    $config.support.history = false;
}
if ($config.support.ios || $config.support.android || $config.support.ie) {
    $config.txDur = 0;
}
// (for testing)
//$config.support.video = true;

var $new = $.extend(true, {}, $config.emptyState), $old = {}, $data = {}, $commentCounts = {};



var $format = {
    date: function(date){
        var toOrdinal = function(day) {
            var n = day % 100;
            var suff = ["th", "st", "nd", "rd", "th"]; // suff for suffix
            var ord = n < 21 ? (n < 4 ? suff[n] : suff[0]) : (n % 10 > 4 ? suff[0] : suff[n % 10]);
            return day + ord;
        };
        
        var monthStrings = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
        var dayStrings = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
        var oneDayAgo = -86400000, oneDayFromNow = 86400000, twoDaysAgo = -172800000, sixDaysAgo = -518400000;
        var now = new Date();
        var reference = new Date(date.getTime());
        
        now.setHours(0, 0, 0, 0);
        reference.setHours(0, 0, 0, 0);
        var offset = reference.getTime() - now.getTime();
        var janFirst = new Date(now.getFullYear(), 0, 1).getTime() - now.getTime();
        
        if (offset > oneDayAgo && offset <= 0) {
            return "Today";
        } else if (offset > twoDaysAgo && offset <= oneDayAgo) {
            return "Yesterday";
        } else if (offset >= sixDaysAgo) {
            return dayStrings[reference.getDay()];
        } else {
            return monthStrings[date.getMonth()] + " " + toOrdinal(date.getDate()) + (offset < janFirst ? ", " + date.getFullYear() : "");
        }
    },
    time: function(date){
        var diff = (((new Date()).getTime() - date.getTime()) / 1000), dayDiff = Math.floor(diff / 86400);
        if (isNaN(dayDiff) || dayDiff < 0 || dayDiff >= 1) {
            return;
        }
        return dayDiff === 0 && (
            diff < 60 && "just now" ||
            diff < 120 && "1 minute ago" ||
            diff < 3600 && Math.floor(diff / 60) + " minutes ago" ||
            diff < 7200 && "1 hour ago" ||
            diff < 86400 && Math.floor(diff / 3600) + " hours ago");
    },
    duration: function(s){
        s = parseFloat(s);
        var dur = { min: Math.floor(s / 60), sec: Math.floor(s % 60) };
        dur.sec = dur.sec < 10 ? "0" + dur.sec : dur.sec;
        return dur.min + ':' + dur.sec;
    }
};



(function($){
    $.fn.extend({
        hardStop: function(){
            var executed = false;
            function executeQueue() {
                var e = $(this);
                while (e.queue().length) {
                    // (*should* account for element callbacks that queue functions on itself)
                    e.stop(false, true);
                }
            }
            function verifyQueue() {
                var e = $(this);
                if (e.queue().length) {
                    // (a callback function queued another function on this element)
                    executed = false;
                }
            }
            while (!executed) {
                this.each(executeQueue);
                executed = true;
                this.each(verifyQueue);
            }
            return this;
        },
        delay: function(duration){
            // (we overwrite the delay() function because they can't be $.stop()ed)
            $(this).animate({ "delay": 1 }, duration);
            return this;
        }
    });
    
    
    
    $.widget("ui.textToFit", {
        options: {
            maxWidth: 600,
            maxHeight: 100,
            increment: 1,
            maxIterations: 100,
            minSize: -1,
            trimShoulder: false,
            padShoulder: false,
            relative: false
        },
        _init: function(){
            this.fit();
            if (this.options.minSize > -1 && this.options.size < this.options.minSize) {
                var text = this.element.text();
                var spaces = [], pos = -1;
                while ((pos = text.indexOf(" ", pos + 1)) > -1) {
                    spaces.push(pos);
                }
                if (spaces.length) {
                    var index = spaces[Math.ceil(spaces.length / 2) - 1];
                    this.element.html(text.substr(0, index) + "<br />" + text.substr(index + 1));
                    this.fit();
                }
            }
            this.destroy();
        },
        fit: function(){
            var helveticaLeftShoulders={A:12/667,B:89/848,C:30/529,D:95/892,E:112/911,F:51/380,G:15/259,H:76/723,I:283/834,J:0.026,K:95/868,L:73/556,M:10/119,N:52/515,O:1/21,P:112/911,Q:1/21,R:87/740,S:28/415,T:1/47,U:80/723,V:24/667,W:1/59,Y:13/510,X:13/510,Z:20/611};
            var $this = this.element;
            if ($this.is(":visible") && $this.text() !== "") {
                var opt = this.options, size = parseInt($this.css("font-size"), 10);
                
                var display = $this.css("display");
                $this.css("display", "inline-block").css("width", "");
                $this.html($this.html().replace(/ /g,'&nbsp;'));
                
                var iterations = 0, letterSpacing = 0;
                do {
                    iterations++;
                    size += opt.increment;
                    letterSpacing = parseInt(size * 0.08, 10);
                    $this.css("font-size", size).css("letter-spacing", "-" + letterSpacing + "px");
                    if (this.options.padShoulder) {
                        $this.css("padding-right", letterSpacing);
                    }
                } while (iterations < opt.maxIterations && $this.width() < opt.maxWidth && $this.height() < opt.maxHeight);
                if (size > 0) {
                    size -= opt.increment;
                    $this.css("font-size", size);
                }
                opt.size = size;
                
                if (opt.trimShoulder) {
                    var leaf = $this.children().length ? $(":not(:has(*))", $this) : $this;
                    var text = leaf.html();
                    leaf.html(text.substr(0, 1));
                    var charWidth = $this.width();
                    leaf.html(text);
                    var correction = 0;
                    if ($config.support.ff) {
                        correction = 1;
                    }
                    $this.css("left", (-1 * Math.round(helveticaLeftShoulders[text.substr(0, 1)] * charWidth)) + correction)
                        .css("position", "relative");
                }
                
                $this.css("display", display).html($this.html().replace(/&nbsp;/g,' '));
                if (display == "inline-block" || display == "block") {
                    $this.css("width", "100%");
                }
                if (opt.relative === true) {
                    $this.css("font-size", (size / parseFloat($this.parent().css("font-size")) * 100) + "%");
                }
            }
        }
    });
    
    
    
    $.widget("ui.gasbox", {
        options: {
            borderWidth: 5,
            images: [],
            focus: -1,
            txDur: 500,
            rotator: null,
            rotateIndex: -1,
            rotateDur: 5000
        },
        _init: function(){
            var gasbox = this, $this = this.element, opt = this.options;
            
            $this.addClass("ui-gasbox");
            var images = $('<ul class="ui-gasbox-images" />').appendTo($this);
            var borders = $('<ul class="ui-gasbox-borders" />').appendTo($this);
            var caption = $('<div class="ui-gasbox-caption"><span></span></div>').appendTo($this);
            var gasboxWidth = $this.width();
            var numImages = opt.images.length;
            var borderWidthTotal = (numImages - 1) * opt.borderWidth;
            var defaultWidth = Math.round((gasboxWidth - borderWidthTotal) / numImages);
            
            var width = 0, altWidth = [], hoverWidth = 0, oldWidth = 0, oldAltWidth = 0, oldHoverWidth = 0, oldHoverIndex = -1, isHover = false, oldIsHover = false;
            var position = function(hoverIndex, txDur) {
                hoverIndex = typeof hoverIndex !== "undefined" ? hoverIndex : -1;
                
                oldWidth = width;
                oldAltWidth = altWidth.slice(0);
                oldHoverWidth = hoverWidth;
                oldIsHover = isHover;
                isHover = hoverIndex > -1;
                if (hoverIndex == -1 && opt.focus > -1) {
                    hoverIndex = opt.focus;
                }
                if (hoverIndex == -1 && opt.rotateIndex > -1) {
                    hoverIndex = opt.rotateIndex;
                }
                                
                width = defaultWidth;
                
                // "alt" width is an adjusted pair of widths for the left and right sides of the hoverIndex, used when the diff is below a certain threshold
                altWidth = [];
                if (hoverIndex > -1) {
                    hoverWidth = opt.images[hoverIndex].width;
                    width = Math.round(((gasboxWidth - borderWidthTotal) - hoverWidth) / (numImages - 1));
                    if (width > defaultWidth) {
                         width = defaultWidth;
                    }
                    
                    if (oldHoverIndex > -1 && hoverIndex != oldHoverIndex) {
                        // "total" width is from an edge up to and including the hover width
                        var dir, totalWidth, oldTotalWidth;
                        if (hoverIndex > oldHoverIndex) {
                            dir = 1;
                            totalWidth = (width * hoverIndex) + hoverWidth;
                            oldTotalWidth = (((oldAltWidth.length ? oldAltWidth[0] : oldWidth) * oldHoverIndex) + oldHoverWidth);
                        } else {
                            dir = -1;
                            totalWidth = (width * (numImages - hoverIndex - 1)) + hoverWidth;
                            oldTotalWidth = ((oldAltWidth.length ? oldAltWidth[1] : oldWidth) * (numImages - oldHoverIndex - 1)) + oldHoverWidth;
                        }
                        // "diff" is the difference between total width and the old total width, or -- practically -- the width of the target area
                        var diff = totalWidth - oldTotalWidth;                        
                        if (diff < 50) {
                            // "bias" is the amount of padding added to the target area
                            var bias = diff < 0 ? (-1 * diff) + 50 : 50 - diff;
                            var numAfter = numImages - hoverIndex - 1;
                            if (dir == -1) {
                                altWidth[1] = hoverIndex === numImages - 1 ? 0 : Math.round(((width * numAfter) + bias) / numAfter);
                                altWidth[0] = hoverIndex === 0 ? 0 : Math.round((gasboxWidth - borderWidthTotal - hoverWidth - (altWidth[1] * numAfter)) / hoverIndex);
                                if (altWidth[0] < 5) {
                                    altWidth[0] = 15; // min width
                                    altWidth[1] = Math.round((gasboxWidth - borderWidthTotal - hoverWidth - (altWidth[0] * hoverIndex)) / numAfter);
                                }
                            } else {
                                altWidth[0] = hoverIndex === 0 ? 0 : Math.round(((width * hoverIndex) + bias) / hoverIndex);
                                altWidth[1] = hoverIndex === 0 ? width : Math.round((gasboxWidth - borderWidthTotal - hoverWidth - (altWidth[0] * hoverIndex)) / numAfter);
                                if (altWidth[1] < 5) {
                                    altWidth[1] = 15;
                                    altWidth[0] = Math.round((gasboxWidth - borderWidthTotal - hoverWidth - (altWidth[1] * numAfter)) / hoverIndex);
                                }
                            }
                        }
                    }
                }
                
                txDur = typeof txDur !== "undefined" ? txDur : opt.txDur;
                var animateOpts = { duration: txDur };
                
                var showCaption = isHover || (hoverIndex > -1 && hoverIndex === opt.rotateIndex);
                if (showCaption) {
                    caption.html(opt.images[hoverIndex].title.replace(/ /g,'&nbsp;'));
                }
                var captionWidth = caption.outerWidth();
                var captionLeft = hoverIndex * ((altWidth.length ? altWidth[0] : width) + opt.borderWidth);
                if (captionLeft + captionWidth > gasboxWidth) {
                    captionLeft = gasboxWidth - captionWidth;
                }
                caption.stop().animate({ opacity: showCaption ? 1 : 0, left: captionLeft }, animateOpts);
                
                var left = 0;
                $("li", images).each(function(index){
                    var thisHoverWidth = opt.images[index].width;
                    var thisWidth = altWidth.length === 0 ? width : index < hoverIndex ? altWidth[0] : altWidth[1];
                    //console.log(index + ": " + thisWidth);
                    $(this).stop().animate({ left: left }, animateOpts);
                    $("li:eq(" + index + ")", borders).stop().animate({ left: left + (index === hoverIndex ? thisHoverWidth : thisWidth) }, animateOpts);
                    left = left + (index === hoverIndex ? thisHoverWidth : thisWidth) + opt.borderWidth;
                    var bgPosX = index === hoverIndex ? 0 : -1 * Math.round((thisHoverWidth - thisWidth) / 2);
                    var opacity = hoverIndex === -1 ? 1 : index === hoverIndex ? 1 : 0.4;
                    $(this).children().stop().animate({ backgroundPositionX: bgPosX + "px", opacity: opacity }, animateOpts);
                });
                
                if (opt.rotator !== null && oldIsHover && !isHover) {
                    // reset interval after hover
                    $this.trigger("startRotator");
                }
                
                oldHoverIndex = hoverIndex;
            };
            
            
            
            // setup
            $.each(opt.images, function(index){
                $('<li><a href="/' + this.link + '" style="background-image: url(' + this.src + ');">&nbsp;</a></li>').appendTo(images);
                if (index > 0) {
                    $('<li />').appendTo(borders);
                }
            });
            $("li", borders).css("border-right-width", opt.borderWidth);
            
            var totalWidth = 0, maxWidth = -1, featuredImgLoaded = 0;
            $("li", images).each(function(index){
                var feature = $(this);
                var featureImgSrc = feature.children().css("background-image");
                featureImgSrc = featureImgSrc.substr(0, featureImgSrc.length - 1).replace("url(", "");
                if (featureImgSrc.substr(0, 1) == '"') {
                    featureImgSrc = featureImgSrc.substr(1, featureImgSrc.length - 1);
                }
                $('<img src="' + featureImgSrc + '" style="position: absolute; left: -10000px; top: -10000px;" />')
                    .appendTo("body")
                    .one("load", function(){
                        var img = this;
                        setTimeout(function(){
                            opt.images[index].width = img.width;
                            feature.width(img.width);
                            if (maxWidth < 0 || img.width < maxWidth) {
                                maxWidth = img.width;
                            }
                            totalWidth += img.width;
                            featuredImgLoaded++;
                            $(img).remove();
                            if (featuredImgLoaded == numImages) {
                                $this.show();
                                if (maxWidth < defaultWidth || totalWidth + borderWidthTotal < gasboxWidth) {
                                    gasboxWidth = maxWidth * numImages + borderWidthTotal;
                                    $this.width(gasboxWidth);
                                    defaultWidth = maxWidth;
                                }
                                position(-1, 0);
                            }
                        }, 0);
                    });
            });
            
            
            
            // events
            $this.delegate(".ui-gasbox-images > li", "mouseenter", function(){
                position($("li", images).index(this));
            }).delegate(".ui-gasbox-images > li", "mouseleave", function(event){
                // reset positions unless mouse enters a border or another image
                var borderIndex = $("li", borders).index(event.relatedTarget);
                var imageIndex = $("li > a", images).index(event.relatedTarget);
                if (borderIndex === -1 && imageIndex === -1) {
                    position();
                }
            });
            $this.delegate(".ui-gasbox-borders > li", "mouseleave", function(event){
                // reset positions if mouse leaves a border without entering another image
                if ($("li > a", images).index(event.relatedTarget) === -1) {
                    position();
                }
            });
            $this.bind({
                refresh: function(){
                    if ($this.is(":visible")) {
                        position();
                        return true;
                    } else {
                        return false;
                    }
                },
                startRotator: function(){
                    if (opt.rotator !== null) {
                        clearTimeout(opt.rotator);
                    }
                    opt.rotator = setInterval(function(){
                        if (!isHover && $this.is(":visible")) {
                            opt.rotateIndex = oldHoverIndex == numImages - 1 ? -1 : opt.rotateIndex + 1;
                            position(undefined, opt.txDur * 2);
                        }
                    }, opt.rotateDur);
                },
                stopRotator: function(){
                    if (opt.rotator !== null) {
                        clearInterval(opt.rotator);
                        opt.rotator = null;
                        opt.rotateIndex = -1;
                        position(-1);
                    }
                }
            });
        }
    });
    
    
    
    $.widget("ui.audio", {
        options: {
            src: "",
            artist: "",
            title: "",
            duration: 0,
            color: "white",
            size: "small"
        },
        _init: function(){
            var self = this, $this = this.element, opt = this.options;
            
            var images = {
                small: {
                    white: {
                        playBtn: "/media/play-btn-white-16x16.png",
                        stopBtn: "/media/stop-btn-white-16x16.png",
                        dlBtn: "/media/dl-btn-white-16x16.png",
                        dlCancelBtn: "/media/dl-cancel-btn-white-14x14.png"
                    },
                    black: {
                        playBtn: "/media/play-btn-black-16x16.png",
                        stopBtn: "/media/stop-btn-black-16x16.png",
                        dlBtn: "/media/dl-btn-black-16x16.png",
                        dlCancelBtn: "/media/dl-cancel-btn-black-14x14.png"
                    }
                },
                big: {
                    white: {
                        playBtn: "/media/play-btn-white-32x32.png",
                        stopBtn: "/media/stop-btn-white-32x32.png",
                        dlBtn: "/media/dl-btn-white-32x32.png",
                        dlCancelBtn: "/media/dl-cancel-btn-white-28x28.png"
                    },
                    black: {
                        playBtn: "/media/play-btn-black-32x32.png",
                        stopBtn: "/media/stop-btn-black-32x32.png",
                        dlBtn: "/media/dl-btn-black-32x32.png",
                        dlCancelBtn: "/media/dl-cancel-btn-black-28x28.png"
                    }
                }
            };
            
            if ($this.hasClass("ui-audio")) {
                opt.src = $this.data("src");
                var artist = $(".artist", $this);
                opt.artist = artist.length ? artist.html() : "";
                opt.title = $(".title", $this).html();
                opt.duration = $this.data("duration");
                $this.empty();
            } else {
                $this.addClass("ui-audio");
            }
            if ($this.hasClass("big") || opt.size == "big") {
                $this.addClass("big");
                opt.size = "big";
            }
            $.extend(opt, images[opt.size][opt.color]);
            
            opt.file = opt.src.replace("http://intothewoods.tv/files/", "");
            opt.slug = opt.file.replace(/\//g, "-").split(".");
            opt.slug.pop();
            opt.slug = opt.slug.join("");
            
            var dlBtnAction = "add";
            if ($("#tracks ul .ui-audio-" + opt.slug).length) {
                dlBtnAction = "remove";
            }
            
            // dom setup
            $this.addClass("ui-audio-" + opt.slug);
            var scrubber = $('<div class="scrubber"><div class="progress"></div></div>').appendTo($this).wrap('<div class="wrapper" />');
            var text;
            if ($this.css("display").indexOf("inline") > -1) {
                text = $('<span class="text">' + opt.title + '</span>').appendTo($this);
            } else {
                text = $('<div class="text" />').appendTo($this).wrap('<div class="wrapper" />');
                var duration = $('<div class="duration"><span class="progress"></span>' + $format.duration(opt.duration) + '</div>').appendTo(text);
                var title = $('<div class="title">' + (opt.artist !== "" ? '<span class="artist">' + opt.artist + ' &ndash; </span>' : "") + opt.title + '</div>').appendTo(text);
            }
            var dlBtn = $('<img class="dl-btn" src="' + (dlBtnAction == "remove" ? opt.dlCancelBtn : opt.dlBtn) + '" />')
                .addClass(dlBtnAction).appendTo($this);
            var playBtn = $('<img class="play-btn" src="' + opt.playBtn + '" />').appendTo($this);
            dlBtn.add(playBtn).bind("mouseenter mouseleave", function(event){
                $(this).css("opacity", event.type == "mouseenter" ? 0.65 : 0.4);
            }).mouseleave();
            if ($this.parent().is("ul, ol")) {
                // check if list contains mixed artists
                var mixedArtists = $this.parent().is("#tracks ul") ? true : false;
                var siblings = $this.siblings(".ui-audio:has(.wrapper)");
                siblings.each(function(){ // hacky way to select only initialized ui-audios
                    if (mixedArtists === false && $(this).audio("option", "artist") != opt.artist) {
                        mixedArtists = true;
                    }
                });
                $(".ui-audio .title > .artist", $this.parent()).toggle(mixedArtists);
            } else if ($this.is("div")) {
                $(".title > .artist", $this).show();
            }
            
            // events
            text.bind("mouseenter mouseleave click", function(event){
                if (!$this.is(".playing")) {
                    switch (event.type) {
                    case "mouseenter": playBtn.mouseenter(); break;
                    case "mouseleave": playBtn.mouseleave(); break;
                    case "click": playBtn.click().mouseleave(); break;
                    } 
                }
            });
            var timeUpdate = function(){
                var tracks = $(".ui-audio-" + opt.slug);
                if (tracks.filter(":not(.playing)").length && this.playState == 1 && this.paused === false) {
                    // (whileplaying continues to fire after pause() on FF, so we check the actual sound object)
                    $.each(tracks, function(){
                        if ($(this).parent().length > 0) {
                            $("> .play-btn", this).attr("src", $(this).audio("option", "stopBtn"));
                        }
                    });
                    tracks.removeClass("loading").addClass("loaded playing");
                }
                var percent = parseFloat(((this.position / 1000) / (this.durationEstimate / 1000)) * 100, 10).toFixed(2);
                $(".scrubber > .progress", tracks).width(percent + "%");
                $(".duration > .progress", tracks).html($format.duration(this.position / 1000) + " &ndash; ");
            };
            var onPause = function(){
                var tracks = $(".ui-audio-" + opt.slug);
                $.each(tracks, function(){
                    if ($(this).parent().length > 0) {
                        $("> .play-btn", this).attr("src", $(this).audio("option", "playBtn"));
                    }
                });
                tracks.removeClass("loading playing");
                if (tracks.filter(".playlist-focus").length && this.playState === 0) {
                    //this.setPosition(0); // (causes HTML5 audio to begin playback for some reason)
                    var nextTrack = $(".ui-audio.playlist-focus").next(".ui-audio");
                    if (nextTrack.length) {
                        // play next track
                        $("> .play-btn", nextTrack).click();
                    }
                }
            };
            
            // methods
            playBtn.click(function(event){
                if (soundManager.supported()) {
                    var tracks = $(".ui-audio-" + opt.slug);
                    var finish = function(){
                        $("#now-playing").empty();
                        tracks = tracks.add($('<div />').appendTo("#now-playing").audio($.extend({}, opt, { size: "big", color: "black" })));
                        if (tracks.filter(":not(.loaded)").length) {
                            tracks.addClass("loading");
                            $.each(tracks, function(){
                                $("> .play-btn", this).attr("src", $(this).audio("option", "size") == "small" ? "/media/loading-itw-snake.gif" : "/media/loading-snake-32x32.gif");
                            });
                        }
                        $(".ui-audio").removeClass("playlist-focus");
                        if ($this.parent().is("ul, ol")) {
                            $this.addClass("playlist-focus");
                        }
                        tracks.trigger("play");
                    };
                    var smSound = soundManager.getSoundById(opt.file);
                    if (smSound) {
                        if (smSound.playState == 1 && smSound.paused === false) {
                            smSound.pause();
                            tracks.trigger("pause");
                        } else {
                            soundManager.pauseAll();
                            if (smSound.playState == 1) {
                                smSound.resume();
                            } else {
                                smSound.play();
                            }
                            finish();
                        }
                    } else {
                        soundManager.pauseAll();
                        $.getJSON($config.operatorUrl + "action=get-file-token&path=" + opt.file, function(token){
                            smSound = soundManager.createSound({
                                id:                 opt.file,
                                url:                opt.src + '?ts=' + token.ts + '&sig=' + token.sig,
                                type:               "audio/mp3",
                                autoPlay:           true,
                                multiShot:          false,
                                onpause:            onPause,
                                onstop:             onPause,
                                onfinish:           onPause,
                                whileplaying:       timeUpdate
                            });
                            finish();
                        });
                    }
                }
            });
            dlBtn.click(function(){
                var tracks = $(".ui-audio-" + opt.slug);
                if ($(this).hasClass("add")) {
                    tracks = tracks.add($('<li />').appendTo($("#tracks ul")).audio($.extend({}, opt, { size: "small", color: "black" })));
                    $(".dl-btn", tracks).removeClass("add").addClass("remove");
                    $.each(tracks, function(){
                        if ($(this).parent().length > 0) {
                            $("> .dl-btn", this).attr("src", $(this).audio("option", "dlCancelBtn"));
                        }
                    });
                } else {
                    $("#tracks ul .ui-audio-" + opt.slug).remove();
                    $(".dl-btn", tracks).removeClass("remove").addClass("add");
                    $.each(tracks, function(){
                        if ($(this).parent().length > 0) {
                            $("> .dl-btn", this).attr("src", $(this).audio("option", "dlBtn"));
                        }
                    });
                }
                $("#tracks").trigger("refresh");
            });
            $this.delegate(".playing .text", "click", function(event){
                var title = $this.is("span") ? $(this).siblings(".wrapper").children(".scrubber") : $(".title", this);
                var titleWidth = title.width();
                var pos = event.pageX - title.offset().left;
                pos = pos < 0 || pos > titleWidth ? -1 : pos;
                if (pos > -1) {
                    var smSound = soundManager.getSoundById(opt.file);
                    var time = parseInt((pos / titleWidth) * (smSound.durationEstimate / 1000), 10);
                    smSound.setPosition(time * 1000);
                }
            });
        }
    });
    
    
    
    $.widget("ui.video", {
        options: {
            id: "",
            src: "",
            fileId: 0,
            vimeoId: 0,
            placeholder: "",
            title: "",
            subtitle: "",
            link: "",
            placeholderOnly: false
        },
        _init: function(){
            var self = this, $this = this.element, opt = this.options;
            
            if ($this.hasClass("ui-video")) {
                opt.src = typeof $this.data("src") == "undefined" ? "" : $this.data("src");
                opt.fileId = typeof $this.data("fileId") == "undefined" ? 0 : $this.data("fileId");
                opt.vimeoId = typeof $this.data("vimeoId") == "undefined" ? 0 : $this.data("vimeoId");
                opt.link = $("a", $this).attr("href");
                opt.placeholder = $("img", $this).attr("src");
                opt.title = $(".title", $this).text();
                opt.subtitle = $(".subtitle", $this).text();
                $this.empty().show();
            } else {
                $this.addClass("ui-video");
            }
            if (opt.placeholderOnly) {
                $this.addClass("placeholder-only");
            }
            if (opt.vimeoId) {
                $this.addClass("vimeo");
            }
            
            
            
            $('<div class="player" />').appendTo($this);
            var placeholder = $('<div class="placeholder" />').appendTo($this)
                .bind({
                    click: function(){
                        if (!opt.placeholderOnly) {
                            if ($this.is(".loading, .loaded")) {
                                self.play();
                            } else {
                                self.load();
                            }
                        }
                    },
                    "mouseenter mouseleave": function(event){
                        $(".title, .subtitle, .play-btn", this).stop().animate({ "opacity": event.type == "mouseenter" ? 0.8 : 0.65 }, $config.txDur / 4);
                        $(".title-bg", this).stop().animate({ "opacity": event.type == "mouseenter" ? 0.4 : 0.3 }, $config.txDur / 4);
                    }
                });
            
            $('<img src="' + opt.placeholder + '" />').appendTo(placeholder);
            $('<div class="play-btn" />').css("opacity", 0.65).appendTo(placeholder);
            
            if (opt.title !== "" && !opt.placeholderOnly) {
                var titlesBg = $('<div class="title-bg" />').css("opacity", 0.3).appendTo(placeholder);
                var titles = $('<div class="titles" />').appendTo(placeholder);
                var title = $('<div class="title">' + opt.title.toUpperCase() + '</div>').css("opacity", 0.65).appendTo(titles);
                var subtitle = $('<div class="subtitle">' + opt.subtitle.toUpperCase() + '</div>').css("opacity", 0.65).appendTo(titles);
                
                title.textToFit({ maxWidth: 595, maxHeight: 100, relative: true });
                subtitle.textToFit({ maxWidth: 595, maxHeight: 50, relative: true });
                titlesBg.height(titles.height() + 10);
            } else {
                placeholder.addClass("no-title");
            }
            
            
            
            // share panel
            var shareBtn = $('<a class="share-btn white-btn fixed" href="javascript:;"><span>COPY/EMBED</span></a>').appendTo($this);
            var sharePanel = $('<div class="share-panel" />').appendTo($this);
            var appendShareInput = function(label, details, code) {
                $('<div class="share-input"><div>' + details + '</div>' + label + '<br /><input type="text"' + ($config.support.ios ? '' : ' readonly="readonly"') + ' class="code" /><br /><br /></div>')
                    .appendTo(sharePanel).find("input").val(code);
            };
            appendShareInput("COPY LINK:", "", opt.link);
            if (!opt.vimeoId) {
                appendShareInput("EMBED VIDEO (SCRIPT):", "(compatible with iPhone/iPad)", '<script src="http://intothewoods.tv/player?file_id=' + opt.fileId + '" type="text/javascript" charset="utf-8"></script>');
                appendShareInput("EMBED VIDEO (FLASH):", "(compatible with MySpace)", '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="480" height="270"><param name="movie" value="http://intothewoods.tv/player.swf?config=http%3A%2F%2Fintothewoods.tv%2Fplayer-config.xml%3Ffile_id%3D' + opt.fileId + '"><param name="allowfullscreen" value="true"><param name="allowscriptaccess" value="always"><param name="wmode" value="transparent"><param name="bgcolor" value="#000000"><embed src="http://intothewoods.tv/player.swf?config=http%3A%2F%2Fintothewoods.tv%2Fplayer-config.xml%3Ffile_id%3D' + opt.fileId + '" width="480" height="270" bgcolor="#000000" allowscriptaccess="always" allowfullscreen="true" /></object>');
            } else {
                appendShareInput("EMBED VIDEO (VIMEO):", "", '<iframe src="http://player.vimeo.com/video/' + opt.vimeoId + '" width="400" height="225" frameborder="0"></iframe>');
            }
            $('<br />').appendTo(sharePanel);
            var closeBtn = $('<a class="white-btn" href="javascript:;"><span>CLOSE</span></a>').appendTo(sharePanel);
            $('<div class="social" />').appendTo(sharePanel);
            
            shareBtn.add(closeBtn).click(function(event){
                event.stopPropagation();
                if (sharePanel.hasClass("open")) {
                    $(".social", sharePanel).empty();
                    sharePanel.hide("scale", { percent: 0, origin: ['middle', 'center'] }, $config.txDur / 5);
                } else {
                    /*if ($config.support.ios && $new.epEl[0] == $new.player[0]) {
                        // TO DO: hide player on iOS
                        setState($new.path);
                    }*/
                    $(".loading", sharePanel).remove();
                    sharePanel.show("scale", { percent: 100, origin: ['middle', 'center'] }, $config.txDur / 5, function(){
                        $(".social", sharePanel)
                            .before('<img class="loading" src="/media/loading-itw-snake.gif" />')
                            .html('<iframe onload="$(this).show().parent().prev(\'.loading\').remove();" src="http://www.facebook.com/plugins/like.php?href=' + encodeURIComponent(opt.link) + '&amp;layout=standard&amp;show_faces=false&amp;width=400&amp;action=like&amp;colorscheme=dark&amp;height=35" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:400px; height:35px; display:none;" allowTransparency="true"></iframe>');
                    });
                }
                sharePanel.toggleClass("open");
            });
            if (!$config.support.ios) {
                $this.delegate(".share-panel .code", "click", function(){ $(this).focus().select(); });
            }
            
            
            
            if (!opt.placeholderOnly) {
                var idNum = 1;
                while ($("#ui-video-" + idNum).length) {
                    idNum += 1;
                }
                opt.id = "ui-video-" + idNum;
                $this.attr("id", opt.id);
                
                opt.file = opt.src.replace("http://intothewoods.tv/files/", "");
            }
        },
        load: function(){
            var self = this, $this = this.element, opt = this.options;            
            var load = function(){
                if (!$this.hasClass("loading")) {
                    return false; // in case video is unloaded while waiting for animation to finish
                }
                
                // resize fade container
                var winHeight = $(window).height();
                var docHeight = $($config.support.ios || $config.support.android ? "body" : "#content-container").height(); // TO DO: doesn't work on ios
                $("#fade-container").height(docHeight >= winHeight ? docHeight : "100%");
                
                var player = $(".player", $this);
                var playerChild = $('<span class="player-child" id="' + opt.id + '-player-child"></span>').appendTo(player);
                if (opt.vimeoId) {
                    // vimeo
                    $this.removeClass("loading").addClass("loaded");
                    playerChild.replaceWith('<iframe src="http://player.vimeo.com/video/' + opt.vimeoId + '?api=1&player_id=' + opt.id + '-player-child&title=0&byline=0&portrait=0&color=ffffff&autoplay=1" id="' + opt.id + '-player-child" class="player-child" frameborder="0"></iframe>');
                    playerChild = player.children();
                    /*playerChild.hide().load(function(){
                        $(this).show();
                    });*/
                    var frooga = $f(playerChild[0]);
                    frooga.addEvent("ready", function(){
                        frooga
                            .addEvent("play", function(){ self.onPlaying(); })
                            .addEvent("pause", function(){ self.onPause(); })
                            .addEvent("finish", function(){ self.onPause(); })
                            .addEvent("playProgress", function(){
                                // with autoplay enabled, froogaloop does not fire the play event in some browsers, so we listen for playProgress instead:
                                // http://vimeo.com/forums/topic:40072
                                if (!$this.hasClass("video-playing")) {
                                    self.onPlaying();
                                    frooga.removeEvent("playProgress");
                                }
                            });
                    });
                } else {
                    // self-hosted
                    $.getJSON($config.operatorUrl + "action=get-file-token&path=" + opt.file + "&referrer=" + encodeURIComponent(window.location.href), function(result){
                        if (!$this.hasClass("loading")) {
                            return false; // in case video is unloaded while waiting for token
                        }
                        var src = opt.src + '?ts=' + result.ts + '&sig=' + result.sig;
                        player.data("view", {
                            "id": result.id,
                            "ts": result.ts,
                            "sig": result.sig,
                            "path": opt.file,
                            "lastUpdate": -1
                        });
                        if ($config.support.video) {
                            // html5 <video>
                            $this.removeClass("loading").addClass("loaded");
                            player.html('<video src="' + src + '" controls="controls" x-webkit-airplay="allow"></video>');
                            playerChild = $("video", player);
                            playerChild.attr($config.playerAttributes)
                                .bind("play", self.onPlay)
                                .bind("playing", self.onPlaying)
                                .bind("pause", self.onPause)
                                .bind("timeupdate", self.timeUpdate)
                                .bind("ended", self.onEnd);
                            playerChild[0].load();
                            playerChild[0].play();
                        } else {
                            // jw player
                            var flashvars = {
                                id:             $this.attr("id"),
                                file:           encodeURIComponent(src),
                                provider:       "video",
                                playerready:    "flashPlayerLoaded",
                                controlbar:     "over",
                                icons:          false,
                                autostart:      true,
                                skin:           "/lib/mediaplayer/itwglow.zip",
                                screencolor:    "000000"
                            };
                            swfobject.embedSWF("/lib/mediaplayer/player.swf", playerChild.attr("id"), "600", "338", "9.0.0","expressInstall.swf", flashvars, $config.flashPlayerParams, $config.playerAttributes);
                            playerChild = $(".player-child", player);
                        }
                        
                        /*if ($config.support.ios) {
                            // I hate you, IE. (And you, FireFox for Windows. [Flash unloads])
                            playerChild.css("left", 625);
                        }*/
                    });
                }
            };
            
            // on iOS, open vimeo videos in new window
            /*if (($config.support.ios || $config.support.android) && opt.vimeoId) {
                window.open("http://vimeo.com/" + opt.vimeoId);
                return false;
            } else {*/
                $this.addClass("loading");
            //}
            
            var player = $(".player", $this);
            if (player.children().length) {
                return false;
            }
            player.show();
            var placeholder = $(".placeholder", $this);
            placeholder.hide("scale", { percent: 0, origin: ['middle', 'center'] }, $config.txDur / 5, load);
            $(".share-btn", $this).hide();
        },
        unload: function(){
            var $this = this.element;
            var placeholder = $(".placeholder", $this), player = $(".player", $this);
            if ($this.hasClass("loaded") && player.hasClass("video-started")) {
                this.pause();
            }
            if (this.options.vimeoId) {
                $f(player.children()[0]).api("unload");
            }
            $this.removeClass("loading loaded");
            placeholder.show("scale", { percent: 100, origin: ['middle', 'center'] }, $config.txDur / 5, function(){
                player.empty().hide();
                $(".share-btn", $this).show();
            });
        },
        play: function(){
            var $this = this.element;
            var playerChild = $(".player-child", $this);
            if (this.options.vimeoId) {
                $f(playerChild[0]).api("play");
            } else {
                if (playerChild.is("video")) {
                    playerChild[0].play();
                } else {
                    try { playerChild[0].sendEvent("PLAY", "true"); } catch(e){}
                }
            }
        },
        onPlay: function(){},
        onPlaying: function(event){
            soundManager.pauseAll();
            if (!$config.support.ie && !$config.support.iphone) { // TO DO: support fade container in IE (opaque bug)
                $("#fade-container").stop().show().animate({ "opacity": 1 }, { duration: $config.txDur / 2 });
            }
            var $this = $config.support.video ? $(this).closest(".ui-video") : $("#" + (typeof event == "object" ? event.id : event));
            if ($this.length) {
                $(".player-child", $this).css("left", 0);
                $(".player", $this).addClass("video-started"); // (hides loading indicator)
                $(".share-btn", $this).hide();
            }
        },
        pause: function(){
            var $this = this.element;
            var playerChild = $(".player-child", $this);
            if (this.options.vimeoId) {
                $f(playerChild[0]).api("pause");
            } else {
                if (playerChild.is("video")) {
                    playerChild[0].pause();
                } else {
                    try { playerChild[0].sendEvent("PLAY", "false"); } catch(e){}
                }
            }
        },
        onPause: function(event){
            if (!$config.support.ie && !$config.support.iphone) {
                $("#fade-container").stop().animate({ "opacity": 0 }, { duration: $config.txDur / 2, complete: function(){ $(this).hide(); } });
            }
            var $this = $config.support.video ? $(this).closest(".ui-video") : $("#" + (typeof event == "object" ? event.id : event));
            if ($this.length && !$this.hasClass("vimeo")) {
                $(".share-btn", $this).show();
            }
        },
        timeUpdate: function(event){
            var player, percent;
            if ($config.support.video) {
                player = $(this).parent();
                percent = parseInt((this.currentTime / this.duration) * 100, 10);
            } else {
                player = $("#" + event.id + " > .player");
                percent = parseInt((event.position / event.duration) * 100, 10);
            }
            var view = player.data("view");
            if (view.lastUpdate < 0 || percent >= view.lastUpdate + $config.analyticsUpdateInterval) {
                view.lastUpdate = percent;
                player.data("view", view);
                $.post($config.operatorUrl + "action=update-file-view&id=" + view.id + "&ts=" + view.ts + "&sig=" + view.sig + "&path=" + view.path + "&percent=" + percent);
            }
        },
        onEnd: function(event){
            var player;
            if ($config.support.video) {
                this.currentTime = 0;
                this.pause();
                player = $(this).parent();
            } else {
                player = $("#" + event.id + " > .player");
            }
            var view = player.data("view");
            if (typeof(view) == "object" && view.lastUpdate != 100) {
                view.lastUpdate = 100;
                player.data("view", view);
                $.post($config.operatorUrl + "action=update-file-view&id=" + view.id + "&ts=" + view.ts + "&sig=" + view.sig + "&path=" + view.path + "&percent=100");
            }
            
            var $this = $config.support.video ? $(this).closest(".ui-video") : $("#" + (typeof event == "object" ? event.id : event));
            $this.video("onPause", event);
        },
        flashLoaded: function(){
            var playerChild = $(".player-child", this.element);
            try {
                playerChild[0].addModelListener("STATE", "jwStateChange");
                playerChild[0].addModelListener("TIME", "jwTimeUpdate");
            } catch(e){}
            this.element.removeClass("loading").addClass("loaded");
        }
    });
})(jQuery);

// helper methods for $.video
function flashPlayerLoaded(event) { (typeof(event) == "object" ? $("#" + event.id) : $("#" + event)).video("flashLoaded"); }
function jwStateChange(event) {
    var player = $("#" + event.id);
    switch (event.newstate) {
        case "COMPLETED":   player.video("onEnd", event); break;
        case "PLAYING":     player.video("onPlaying", event); break;
        case "PAUSED":      player.video("onPause", event); break;
    }
}
function jwTimeUpdate(event) { $("#" + event.id).video("timeUpdate", event); }



function windowRefresh() {
    if (!$config.support.ios && !$config.support.android) {
        var smallWindow = $("#body").width() <= 1054;
        $("#bg-container, #fade-container, #dl-tracks-dialog").toggleClass("small-window", smallWindow);
        var scrollTop = $("#body").scrollTop();
        var tracksContainer = $("#tracks-container");
        if (tracksContainer.parent().is("#sidebar")) {
            //tracksContainer.css("top", scrollTop > 235 ? scrollTop - 235 : 0);
            $("#tracks").toggleClass("detached", scrollTop > 235 ? true : false);
        } else {
            tracksContainer.css("top", 0);
            $("#tracks").removeClass("detached");
        }
    }
}
function refreshViewport(height) {
    var focus = $new.focus;
    var viewport = $("#viewport");
    var width = focus.width();
    var left = -1 * (width - 600);
    var step = false;
    if (parseInt(height, 10) > 0) {
        step = true; // (part of a multi-step animation)
    } else {
        height = focus.height();
    }
    viewport.height(viewport.height()); // (*fixing* the *calculated* height)
    $old.focus.css("position", "absolute");
    viewport.stop(true).animate({ height: height, width: width, left: left }, $config.txDur / 2, function(){
        if (!step) {
            focus.css("position", "relative");
            $(this).height("auto");
        }
    });
    
    // TO DO: optimize
    var getWideHeight = function(){
        var lastWideElement = $(".wide:last", focus);
        if (lastWideElement.length && lastWideElement.next().is(".clear-element")) {
            return lastWideElement.offset().top + lastWideElement.height() - $("#viewport").offset().top + 25 - lastWideElement.parents(".post").position().top;
        }
        return -1;
    };
    var floatingSidebarAttempts = 1;
    var showFloatingSidebar = function(){
        if (focus[0] == $new.focus[0]) { // verify that the focus hasn't changed
            var marginTop = getWideHeight();
            if (marginTop > -1) {
                $("#sidebar").hide();
                $("#tracks-container").appendTo($("#floating-sidebar"));
                $("#tracks-container, #tracks").show();
                $new.catLink.parent().appendTo($("#floating-sidebar"));
                $("#floating-sidebar").css("margin-top", marginTop).show().animate({ left: 0 }, $config.txDur / 2);
            } else if (floatingSidebarAttempts <= 10) {
                floatingSidebarAttempts += 1;
                setTimeout(showFloatingSidebar, 1000); // a wide image hasn't loaded yet...
            }
        }
    };
    var refreshFloatingSidebar = function(){
        var marginTop = getWideHeight();
        if (marginTop > -1) {
            $("#floating-sidebar").animate({ marginTop: marginTop }, $config.txDur / 2);
        } else {
            floatingSidebarAttempts += 1;
            setTimeout(refreshFloatingSidebar, 1000); // a wide image hasn't loaded yet...
        }
    };
    var animateSidebar = function(){
        if (width > 600) {
            $("#tracks, #tracks-container").slideUp($config.txDur / 2);
        } else {
            $("#tracks-container, #tracks").hide();
        }
        $("#sidebar").animate({ left: width > 600 ? -403 : 0 }, $config.txDur / 2, function(){
            if (width > 600) {
                showFloatingSidebar();
            } else {
                if ($("#tracks .ui-audio").length) {
                    $("#tracks, #tracks-container").slideDown($config.txDur / 2);
                } else {
                    $("#tracks, #tracks-container").show();
                }
            }
        });
    };
    var sidebarLeft = parseInt($("#sidebar").css("left").replace("px", ""), 10);
    if ((width > 600 && sidebarLeft === 0) || (width <= 600 && sidebarLeft < 0)) {
        if (width > 600) {
            animateSidebar();
        } else {
            $("#floating-sidebar").animate({ left: -403 }, $config.txDur / 2, function(){
                $("#floating-sidebar").hide();
                $("#tracks-container").prependTo($("#sidebar"));
                var cat = $("#floating-sidebar .category-wrapper");
                if (cat.length) {
                    var catData = $(".category-pages", cat).data().category;
                    var catType = $("#" + catData.type + "-category-type");
                    var catAfter = $(".category-wrapper:eq(" + catData.pos + ")", catType);
                    if (catAfter.length) {
                        cat.insertBefore(catAfter);
                    } else {
                        cat.prependTo($("." + (catData.archived == 1 ? "archived" : "active") + "-categories", catType));
                    }
                }
                $("#sidebar").show();
                animateSidebar();
            });
        }
    } else if (width > 600) {
        refreshFloatingSidebar();
    }
}


function refreshCommentCounts() {
    var updateCounts = function(xid, count){
        var uri = decodeURIComponent(xid).replace("http://intothewoods.tv/", "");
        var slug = uri.replace(/\//g, "-");
        var counts = $(".comment-count-" + slug + ":not(.counted)").addClass("counted");
        $commentCounts[uri] = count;
        if (count > 0) {
            counts.text(count);
            counts.parent().show();
            counts.siblings(".comment-count-space").show();
            counts.siblings(".comment-count-multiple").toggle(count > 1 ? true : false);
        }
    };
    if (typeof FB == "object") {
        var xids = [], responseXids = [];
        $(".comment-count:not(.counted)", $new.focus).each(function(){
            var count = $(this);
            var uri = count.data("uri");
            var xid = encodeURIComponent('http://intothewoods.tv/' + uri);
            if ($commentCounts.hasOwnProperty(uri)) {
                updateCounts(xid, $commentCounts[uri]);
            } else {
                xids.push('"' + xid + '"');
            }
        });
        if (xids.length) {
            xids = xids.slice(0, 10); // (hopefully no more than 10 xids are within a single focus, otherwise we'll have to retool this function.)
            var query = FB.Data.query('SELECT xid, count FROM comments_info WHERE app_id=160094387355847 AND xid IN(' + xids.join(", ") + ')');
            //var query = FB.Data.query('SELECT url, commentsbox_count FROM link_stat WHERE url IN(' + xids.join(", ") + ')');
            query.wait(function(response){
                if ($.type(response) == "array") {
                    $.each(response, function(){
                        responseXids.push('"' + this.xid + '"');
                        updateCounts(this.xid, this.count);
                    });
                }
                $.each(xids, function(){
                    if ($.inArray(this, responseXids) == -1) {
                        updateCounts(this.substr(1, this.length - 1), 0);
                    }
                });
            });
        }
    }
}



function buildTracksModule(trackList) {
    var module = $('<div class="tracks module" />');
    var header = $('<div class="header"><div class="duration"></div><span class="title"></span></div>').appendTo(module);
    var list = $('<ul />').appendTo(module);
    var dlAllBtn = $('<a href="javascript:;" class="toggle-all-btn white-btn fixed small"><span>DOWNLOAD ALL</span></a>').prependTo(header).click(function(){
        $("li > .dl-btn.add", list).click();
    });
    
    var tracks = $("li", trackList);
    var count = tracks.length;
    var duration = 0;
    $.each(tracks, function(){
        duration += $(this).data("duration");
        $(this).appendTo(list);
    });
    $(".header > .title", module).text(count + " TRACK" + (count === 0 || count > 1 ? "S" : "") + ":");
    $(".header > .duration", module).text($format.duration(duration));
    
    return module;
}
function buildCommentsModule(uri) {
    var slug = uri.replace(/\//g, "-");
    var module = $('<div id="comments-' + slug + '" class="comments module' + ($config.commentsEnabled ? '' : ' disabled') + '" />');
    var header = $('<div class="header" />').appendTo(module);
    $('<a href="javascript:;" class="toggle-comments-btn white-btn fixed small"><span>' + ($config.commentsEnabled ? 'HIDE' : 'SHOW') + '</span></a>').appendTo(header);
    var title = $('<span class="title"><span class="comment-count-space">&nbsp;</span>COMMENT<span class="comment-count-multiple">S</span>:</span>').appendTo(header);
    var count = $('<span class="comment-count comment-count-' + slug + '" />').data("uri", uri).prependTo(title);
    $('<span class="disabled-msg">&nbsp;(HIDDEN)</span>').appendTo(title);
    //var fbWrap = $('<div class="fb-wrap"><fb:comments xid="' + encodeURIComponent('http://intothewoods.tv/' + uri) + '" migrated="1" num_posts="10" width="585" colorscheme="dark" publish_feed="false"></fb:comments></div>')
    var fbWrap = $('<div class="fb-wrap"><fb:comments xid="' + encodeURIComponent('http://intothewoods.tv/' + uri) + '" css="http://intothewoods.tv/css/fb.css?51" simple="true" numposts="10" width="585" publish_feed="false"></fb:comments></div>')
        .toggle($config.commentsEnabled).appendTo(module);
    return module;
}
function buildTagsModule(tags, tagSlugs) {
    tags = tags.split(", ");
    tagSlugs = $.trim(tagSlugs).split(" ");
    var module = $('<div class="tags module" />');
    $.each(tags, function(i){
        $('<a href="/tags/' + tagSlugs[i] + '" class="tag-link">' + this + '</a>').appendTo(module);
    });
    return module;
}



function appendCategories(cats) {
    $.each(cats, function(slug){
        $config.categories[slug] = this;
        var category = this;
        category.slug = slug;
        var catContainer = $("#" + this.type + "-category-type > ." + (this.archived == 1 ? "archived" : "active") + "-categories");
        var categoryWrapper = $('<div class="category-wrapper" />').appendTo(catContainer);
        $('<a href="/' + slug + '" id="category-link-' + slug + '" class="category-link white-btn"><span>' + this.title.toUpperCase() + '</span></a>').appendTo(categoryWrapper);
        $('<div id="category-desc-' + slug + '" class="category-desc"><span>' + this.desc + '</span></div>').hide().appendTo(categoryWrapper);
        $('<div id="category-pages-' + slug + '" class="category-pages" />').hide().appendTo(categoryWrapper).data({
            category: category,
            count: this.count,
            numPages: Math.ceil(this.count / $config.postsPerCatPage),
            pages: []
        });
    });
    $(".category-type").each(function(){
        var activeCats = $(".active-categories .category-link", this);
        var archivedCats = $(".archived-categories .category-link", this);
        if (archivedCats.length) {
            $('<a href="' + archivedCats.filter(":first").attr("href") + '" class="category-archived-link archived white-btn sawtooth-btm"><span>+' + archivedCats.length + ' MORE</span></a>').appendTo($(".active-categories", this));
            $('<a href="' + activeCats.filter(":last").attr("href") + '" class="category-archived-link active white-btn sawtooth-top"><span>+' + activeCats.length + ' MORE</span></a>').prependTo($(".archived-categories", this));
        }
    });
}
function appendCategoryPage(page) {
    var links = [];
    page.num = parseInt(page.num, 10);
    var pageContainer = $("#category-pages-" + page.cat);
    var pageElement = $('<div id="' + page.cat + '-category-page-' + page.num + '" class="category-page" style="left: -100%;" />').appendTo(pageContainer);
    if (page.num == 1) {
        pageElement.addClass("first");
    }
    if (pageContainer.data("numPages") == page.num) {
        pageElement.addClass("last");
    }
    if (page.newer !== false) {
        $('<a href="/' + page.newer + '" class="category-page-link white-btn sawtooth-top newer"><span>+' + ($config.postsPerCatPage * (page.num - 1)) + ' NEWER</span></a>').appendTo(pageElement);
    }
    $(page.items).each(function(){
        links.push(this.path);
        $data[this.path] = this;
        var link = $('<a href="' + this.path + '" id="category-child-link-' + this.path.replace(/\//g, "-") + '" class="category-child-link white-btn" />').appendTo(pageElement);
        var title = this.title;
        if (this.cat !== "") {
            title = title.indexOf("#") > -1 ? title.substr(title.indexOf("#")).replace(": ", " ") : title.split(" - ").pop();
        }
        $('<span>' + title.toUpperCase() + '</span>').appendTo(link);
    });
    if (page.older !== false) {
        $('<a href="/' + page.older + '" class="category-page-link white-btn sawtooth-btm older"><span>+' + (pageContainer.data("count") - (page.num * $config.postsPerCatPage)) + ' OLDER</span></a>').appendTo(pageElement);
    }
    
    var pages = pageContainer.data("pages");
    if ($.inArray(page.num, pages) == -1) {
        pages.push(page.num);
    }
    return pageElement.data("links", links);
}



function appendPost(post, pageNum, slug) {
    post.slug = post.path.replace(/\//g, "-");
    $data[post.path] = post;

    pageNum = parseInt(pageNum, 10);
    pageNum = isNaN(pageNum) ? false : pageNum;
    
    var feedPage = $(pageNum ? "#" + slug + "-page-" + pageNum : "#post-container");
    var postElement = $('<div class="post" id="' + (pageNum ? 'feed-' : '') + 'post-' + post.slug + '" />').appendTo(feedPage);
    var postLink = $('<a href="/' + post.path + '" class="post-link" />');
    if (post.type == "wide_article" && !pageNum) {
        postElement.addClass("wide");
    }
        
    var date = new Date(post.timestamp * 1000);
    if (pageNum) {
        var lastDate = feedPage.data("lastDate");
        lastDate = typeof lastDate === "undefined" ? new Date(0) : lastDate;    
        feedPage.data("lastDate", date);
        if ((lastDate.getFullYear() + "-" + lastDate.getMonth() + "-" + lastDate.getDate()) != (date.getFullYear() + "-" + date.getMonth() + "-" + date.getDate())) {
            $('<div class="section-header"><span>' + $format.date(date).toUpperCase() + '</span></div>').appendTo(postElement);
        }
    }
    
    var header = $('<div class="post-header" />').appendTo(postElement);
    var title = $('<div class="post-title" />').css("opacity", 0.65);
    if (!(post.type == "episode" && !pageNum)) {
        title.appendTo(header);
    }
    var detailsWrapper = $('<div class="details-wrapper" />').appendTo(postElement);
    
    
    
    // body
    var excerpted = false;
    var bodyWidth = 600 - (pageNum ? 60 : 15);
    var body = $('<div class="body article-copy" />').appendTo(detailsWrapper).html(post.html);
    if (pageNum) {
        if (post.excerpt_html !== "") {
            excerpted = true;
            body.html(post.excerpt_html);
        } else {
            var excerptEndPos = post.html.indexOf('<!-- more -->');
            if (excerptEndPos > -1) {
                excerpted = true;
                body.html(post.html.substr(0, excerptEndPos));
            } 
        }
    }
    var tracksModule = $(".tracks.module", body);
    if (pageNum) {
        tracksModule.remove();
    }
    
    
    
    // title
    if (post.cat === "") {
        $('<span>' + post.title + '</span>').appendTo(title).textToFit({ maxWidth: 550, maxHeight: 50, padShoulder: true });
    } else {
        var videoFirst = $(".ui-video:first-child", body);
        if (pageNum || (post.type == "article" || post.type == "wide_article")) {
            var titleParts = post.title.toUpperCase().split(": ");
            if (titleParts.length == 1) {
                titleParts = titleParts[0].split(" - ");
            }
            $('<span class="title">' + titleParts[1] + '</span>').appendTo(title)
                .textToFit({ maxWidth: 500, maxHeight: 50, padShoulder: true });
            $('<br />').appendTo(title);
            $('<span class="title category">' + titleParts[0] + '</span>').appendTo(title)
                .textToFit({ maxWidth: 500, maxHeight: 25, padShoulder: true });
            title.addClass("categorized");
        } else if (!pageNum && post.type == "episode") {
            videoFirst.appendTo(header);
        }
        if (pageNum && videoFirst.length) {
            videoFirst.appendTo(header).video({ placeholderOnly: true }).children(".placeholder").addClass("loading-indicator-container").wrap(postLink);
        }
    }
    
    
    
    // metabar: timestamp (on individual posts, or when within last 24 hrs), comment count, attribution
    var metabar = $('<ul class="metabar" />').css("opacity", 0.65);
    if (post.type == "episode") {
        metabar.prependTo(detailsWrapper);
    } else {
        metabar.appendTo(header);
    }
    var attribution = "";
    if (post.attribution.indexOf(",") > -1) {
        attribution = "specific";
    } else if (post.attribution !== "") {
        attribution = "posted_by";
    }
    var commentCount = $('<li class="feed-comment-count" />').hide().appendTo(metabar);
    $('<span class="comment-count-space">&nbsp;</span><span>Comment</span><span class="comment-count-multiple">s</span>').appendTo(commentCount);
    $('<span class="comment-count comment-count-' + post.slug + '" />').data("uri", post.path).prependTo(commentCount);
    var time = $format.time(date);
    if (!pageNum || typeof time != "undefined" || attribution == "posted_by") {
        if (!pageNum) {
            time = typeof time == "undefined" ? $format.date(date) : time;
        }
        var postTime = $('<li class="post-time" />').appendTo(metabar);
        postTime.html('Posted' + (!pageNum || typeof time != "undefined" ? " " + time : "") + (attribution == "posted_by" ? " by " + post.attribution : ""));
    }
    if (attribution == "specific") {
        var attributions = {};
        $.each(post.attribution.split(";"), function(){
            var pair = this.split(",");
            if (typeof attributions[pair[0]] == "undefined") {
                attributions[pair[0]] = [];
            }
            attributions[pair[0]].push(pair[1]);
        });
        $.each(attributions, function(key){
            var who = this.join(", ");
            if (this.length > 1) {
                who = who.replace(", " + this[this.length - 1], (this.length > 2 ? "," : "") + " and " + this[this.length - 1]);
            }
            metabar.append('<li>' + key + " by " + who + '</li>');
        });
    }
    $("li", metabar).prepend('<img src="/media/square-bullet.gif" />');
    
    
    
    var firstElement = body.children(":first");
    var firstImage = firstElement.is("img") ? firstElement : firstElement.children("img:only-child");
    var imgOnly = firstImage.length && firstElement.is(":only-child");
    if (pageNum) {
        firstImage.parent("p").addClass("loading-indicator-container");
        title.add(firstImage).wrap(postLink);
    }
    body.find("h1, h2, p, li").wrapInner('<span class="inline-body" />');
    if (excerpted) {
        $('<p><a href="/' + post.path + '" class="post-link white-btn sawtooth-btm"><span>' + (imgOnly ? 'SEE' : 'READ') + ' MORE</span></a></p>').appendTo(body); // &#8230;
    }            
    
    
    
    // image processing
    // 1) copy existing images' dimensions, to prevent individual posts from "snapping" into view
    if (!$config.support.ff && !$config.support.ie) { // ("snapping only occurs in WebKit browsers, and there's a bug with this function in FF and IE)
        body.find("img").add(header.find("img")).each(function(){
            var existing = $(".post img[src='" + $(this).attr("src") + "']");
            if (existing.length) {
                var width = parseInt(existing.attr("width"), 10), height = parseInt(existing.attr("height"), 10);
                if (width > 0 && height > 0) {
                    $(this).attr({ width: width, height: height });
                }
            }
        });
    }
    // 2) add left margin to images within the title that are narrower than the article body
    /*if (post.type != "episode") {
        header.find("img").one("load", function(){
            var img = this;
            setTimeout(function(){
                $(img).attr({ width: img.width, height: img.height });
                if (img.width <= bodyWidth) {
                    $(img).parent().css("margin-left", pageNum ? 60 : 15);
                }
            }, 0);
        });
    }*/
    // 3) images within the body, that are wider than the body, are floated right, so they overflow into the left margin
    body.find("img").one("load", function(){
        var img = this;
        setTimeout(function(){
            $(img).attr({ width: img.width, height: img.height });
            if (img.width > bodyWidth) {
                $(img).closest("p").addClass("wide").after('<div class="clear-element" />');
            }
        }, 0);
    });
        
    
    
    // body processing
    body.children("object, iframe, div").each(function(){
        var height = $(this).height(), width = $(this).width();
        if (height > 0 && width > bodyWidth) {
            $(this).addClass("wide").after('<div class="clear-element" />');
        }
    });
        
    $(".ui-video:not(.placeholder-only)", postElement).video();
    $(".ui-audio", postElement).audio();
    
    
    
    if (!pageNum) {
        if (post.tags !== "") {
            buildTagsModule(post.tags, post.tag_slugs).insertAfter(body);
        }
        if (tracksModule.length) {
            tracksModule.appendTo(detailsWrapper).replaceWith(buildTracksModule(tracksModule));
        }
        buildCommentsModule(post.path).appendTo(detailsWrapper);
    }
}
function appendFeedPage(state, feedPage) {
    var slug = "feed";
    switch (state.type) {
    case 'tag':
        slug = "tag-" + state.path[1];
        break;
    case 'search':
        var time;
        if (!$config.searchMap.hasOwnProperty(state.path[1])) {
            time = new Date().getTime();
            $config.searchMap[state.path[1]] = time;
        } else {
            time = $config.searchMap[state.path[1]];
        }
        slug = "search-" + time;
        break;
    case 'feature':
        slug = state.path[0];
        break;
    }
    slug += '-' + state.postType;
    
    var newerPosts = (state.pageNum - 1) * 10;
    var olderPosts = feedPage.total - state.pageNum * 10;
    
    var container = $("#feed-page-container");
    
    var page = $('<div id="' + slug + '-page-' + state.pageNum + '" class="feed-page" />').appendTo(container);
        
    if (state.type != "feed") {
        var title = "", titlePrefix = "", desc = "";
        switch (state.type) {
        case 'tag':
            var tag = feedPage.tag_string;
            $config.tagMap[state.path[1]] = tag;
            title = tag.toUpperCase();
            desc = 'Posts tagged "' + tag + '"';
            break;
        case 'search':
            title = "SEARCH";
            desc = (state.postType == "episode" ? "Episodes" : "Posts") + ' that contain "' + decodeURIComponent(state.path[1]) + '"';
            break;
        case 'show': case 'feature':
            var category = $config.categories[feedPage.posts[0].cat];
            title = category.title.toUpperCase();
            desc = category.desc;
            break;
        }
        var catHeader = $('<div class="page-header" />').css("opacity", 0.65).appendTo(page);
        $('<span class="title">' + title + '</span>').appendTo(catHeader).textToFit({ maxWidth: 595, maxHeight: 50 });
        if (desc !== "") {
            $('<div class="desc"><span>' + desc + '</span></div>').appendTo(catHeader);
        }
    }
    
    if ($config.topTenEnabled === true && state.type == "feed" && state.pageNum == 1) {
        var popular = $('<div class="popular" />').appendTo(page);
        $('<div class="section-header small"><span>POPULAR NOW</span></div>').appendTo(popular);
        var episodeList = $('<ol class="first" />').appendTo(popular);
        var appendEpisode = function(){
            var title = this.title.split(": ");
            episodeList.append('<li><a href="/' + this.link + '"><span class="artist">' + title[0] + ':</span> ' + title[1] + '</a></li>');
        };
        $.each($payload.popular.episodes.slice(0, 5), appendEpisode);
        episodeList = $('<ol class="second" start="6" />').appendTo(popular);
        $.each($payload.popular.episodes.slice(5), appendEpisode);
        
        /*var trackList = $('<ul />').appendTo(popular);
        $.each($payload.popular.tracks, function(){
            this.title = this.title.split(": ");
            trackList.append('<li><a href="/' + this.link + '"><span class="artist">' + this.title[0] + ':</span> ' + this.title[1] + '</a></li>');
            //$('<li />').appendTo(trackList).audio({ src: "", title: this.title[1], artist: this.title[0], duration: 1000 });
        });*/
    }
        
    $(feedPage.posts).each(function(){
        appendPost(this, state.pageNum, slug);
    });
        
    var basePath = [];
    if (state.path[0] == "tags" || state.path[0] == "search") {
        basePath.push(state.path[0], state.path[1]);
    } else if (state.path.length == 1 || state.path[1] == "page") {
        basePath.push(state.path[0]);
    }
    
    if (state.type == "feed" || state.type == "search" || state.type == "tag") {
        var typeToggle = $('<div class="feed-type-toggle" />');
        if (feedPage.posts.length === 0 && (state.type == "search" || state.type == "tag")) {
            typeToggle.appendTo(page);
        } else {
            typeToggle.prependTo(page.children(".post:first"));
        }
        //typeToggle.append('<span style="padding: 2px; float: left; background-image: url(http://intothewoods.tv/media/black-px-65-opacity.png); font-size: 10pt; color: #ddd;">VIEW:</span>');
        typeToggle.append('<a href="/' + basePath.join("/") + '" class="white-btn fixed small all"><span>ALL POSTS</span></a>');
        typeToggle.append('<a href="/' + basePath.join("/") + '?type=episode" class="white-btn fixed small episode"><span>JUST EPISODES</span></a>');
    }
    
    var queryString = state.postType != "all" ? "?type=" + state.postType : "";
    if (olderPosts > 0) {
        $('<a href="/' + basePath.join("/") + (basePath.length ? '/' : '') + 'page/' + (state.pageNum + 1) + queryString + '" class="white-btn sawtooth-btm feed-loader"><span>+' + olderPosts + ' OLDER POSTS</span></a>').appendTo(page);
    } else {
        $('<div class="feed-footer">' + (state.type == "search" && feedPage.posts.length === 0 ? 'NO SEARCH RESULTS' : 'END OF FEED') + '</div>').css("opacity", 0.65).appendTo(page);
    }
    if (state.pageNum > 1) {
        var newerPath = basePath.slice(0);
        if (state.pageNum > 2) {
            newerPath.push("page", state.pageNum - 1);
        }
        $('<a href="/' + newerPath.join("/") + queryString + '" class="white-btn sawtooth-top feed-loader"><span>+' + newerPosts + ' NEWER POSTS</span></a>').prependTo(page);
    }
}



function appendShowIndexThumbnail(episode){
    var html = $('<div />');
    html.html(episode.html);
    
    var parentCatPage = Math.ceil(episode.cat_pos / $config.postsPerCatPage);
    var parentIndexPage = Math.ceil(parentCatPage / $config.catPagesPerIndexPage);
    var slug = episode.path.split("/").pop();
    
    var numberStart = episode.title.indexOf("#") + 1;
    var number = numberStart > -1 ? parseInt(episode.title.substr(numberStart, episode.title.indexOf(":", numberStart) - numberStart), 10) : 0;
    var title = !isNaN(number) ? episode.title.split(": ").pop() : episode.title.split(" - ").pop();
    
    var indexContainer = $("#" + episode.cat + "-all-page-" + parentIndexPage + " > .thumbs");
    
    var thumb = $('<div id="' + episode.cat + '-' + slug + '-index-thumb" class="index-thumb page-' + parentIndexPage + ' box-' + parentCatPage + '" />')
        .data("pos", parseInt(episode.cat_pos, 10)).data("box", parentCatPage);
    var thumbLink = $('<a href="' + episode.cat + '/' + slug + '" id="' + episode.cat + '-' + slug + '-index-link" class="index-link" />').appendTo(thumb);
    //$('<img src="' + $(".ui-video img", html).attr("src").replace("intothewoods.tv/files/", "intothewoods.tv/files/thumbnails/190x107/") + '" />').appendTo(thumbLink);
    $('<img src="' + $(".ui-video img", html).attr("src") + '" />').appendTo(thumbLink);
    var titleBg = $('<div class="title-bg" />').css("opacity", 0.3).appendTo(thumbLink);
    title = $('<div class="title">' + title.toUpperCase() + '</div>').css("opacity", 0.65).appendTo(thumbLink);
    if (number > 0) {
        $('<div class="number">#' + number + '</div>').appendTo(thumbLink);
    }
    if ($(".ui-audio", html).length) {
        $('<div class="tracks-badge"><img src="/media/dl-btn-white-16x16.png" /></div>').css("opacity", 0.8).appendTo(thumbLink);
    }
    
    thumb.appendTo(indexContainer);
    title.textToFit({ maxWidth: 180, maxHeight: 53, minSize: 25 });
    titleBg.height(title.height());
}
function appendShowIndexPage(showSlug, pageNum){
    var show = $config.categories[showSlug];
    var page = $('<div id="' + show.slug + '-all-page-' + pageNum + '" class="show-index">').appendTo("#index-container");
    var pageHeader = $('<div class="page-header" />').css("opacity", 0.65).appendTo(page);
    $('<span class="title">' + show.title.toUpperCase() + '</span>').appendTo(pageHeader).textToFit({ maxWidth: 595, maxHeight: 50 });
    $('<div class="desc">' + show.desc + '</div>').appendTo(pageHeader);
    $('<div class="thumbs"></div>').appendTo(page);
    
    var appendThumbnail = function(){
        appendShowIndexThumbnail($data[this]);
    };
    for (i = (pageNum - 1) * $config.catPagesPerIndexPage + 1; i <= pageNum * $config.catPagesPerIndexPage; i++) {
        var catPage = $("#" + showSlug + "-category-page-" + i);
        if (catPage.length) {
            $.each(catPage.data("links"), appendThumbnail);
        }
    }
    
    var newerPosts = (pageNum - 1) * ($config.postsPerCatPage * $config.catPagesPerIndexPage);
    var olderPosts = parseInt(show.count, 10) - pageNum * ($config.postsPerCatPage * $config.catPagesPerIndexPage);
    if (olderPosts > 0) {
        $('<a href="/' + showSlug + '/page/' + (pageNum + 1) + '" class="white-btn sawtooth-btm feed-loader"><span>+' + olderPosts + ' OLDER EPISODES</span></a>').appendTo(page);
    }
    if (pageNum > 1) {
        $('<a href="/' + showSlug + (pageNum > 2 ? '/page/' + (pageNum - 1) : '') + '" class="white-btn sawtooth-top feed-loader"><span>+' + newerPosts + ' NEWER EPISODES</span></a>').prependTo(page);
    }
}



function refreshState(state) {
    do { $(":animated").hardStop(); } while ($(":animated").length);
    $(".post-overlay").animate({ opacity: 0 }, { duration: $config.txDur / 2, complete: function(){ $(this).remove(); } });

    $old = $new;
    $new = state;
    
    // setup animation
    var animation = {
        from:           $old.focus,
        to:             $new.focus,
        dir:            "left",
        scale:          false,
        scaleType:      "out",
        scaleOrigin:    ['middle', 'center'],
        dur:            $config.txDur,
        scrollTop:      false
    };
    if (!$new.isNewCat && $old.type == $new.type && ($new.type == "episode" || $new.type == "post")) {
        animation.dir = $data[$old.path.join("/")].timestamp < $data[$new.path.join("/")].timestamp ? "down" : "up";
    } else if ($new.path[0] != $old.path[0] && ($new.type == "show" || $new.type == "feature") && ($old.type == "show" || $old.type == "feature")) {
        animation.dir = $old.catLink.index(".category-link") > $new.catLink.index(".category-link") ? "right" : "left";
    } else if ($old.type == $new.type && ($new.type == "feed" || $new.type == "feature" || $new.type == "show" || $new.type == "search" || $new.type == "tag")) {
        animation.dir = $old.pageNum > $new.pageNum ? "down" : "up";
    } else {
        animation.dir = $old.focus.index("#viewport div") > $new.focus.index("#viewport div") ? "right" : "left";
    }
    
    if ($new.pathString == $old.pathString && $new.postType != $old.postType) {
        // TO DO: prevent animation on init?
        animation.dur = 0;
    }
    
    // pause players
    var playingTrack = $(".ui-audio.playing, .ui-audio.loading");
    if (playingTrack.length) {
        if (!$("#tracks .ui-audio-" + playingTrack.audio("option", "slug")).length) {
            soundManager.pauseAll();
        }
    }
    $(".ui-video.loading, .ui-video.loaded").video("unload");
    
    // close share panel
    $(".share-panel.open .white-btn").click();
    
    // toggle category links
    $(".category-link.toggled, .category-child-link.toggled").removeClass("toggled");
    if ($new.catLink.length) {
        $new.catLink.addClass("toggled");
    }
    if ($new.catChildLink.length) {    
        $new.catChildLink.addClass("toggled");
    }
    
    // search box
    var searchInput = $("#search input");
    if ($new.type == "search") {
        searchInput.val(decodeURIComponent($new.path[1])).addClass("active");
    } else {
        searchInput.val("").removeClass("active");
    }
    
    // featured
    var gasbox = $(".ui-gasbox");
    //var gasboxRotator = gasbox.gasbox("option", "rotator")
    gasbox.gasbox("option", "focus", $(".ui-gasbox a[href$='/" + $new.path.join("/") + "']").parent().index()).trigger("refresh");
    /*if (gasboxRotator !== null && $new.path.length !== 0) {
        gasbox.trigger("stopRotator");
    } else if (gasboxRotator === null && $new.path.length === 0) {
        gasbox.trigger("startRotator");
    }*/
    
    // 404
    if ($new.path[0] == "404" && !$new.focus.hasClass("loaded")) {
        $new.focus.addClass("loaded");
        $('<img src="/media/loading-static.gif" />').css("opacity", 0.4).appendTo($new.focus);
    }
    
    // feed type toggle
    $(".feed-type-toggle a", $new.focus).removeClass("toggled").filter("." + $new.postType).addClass("toggled");
    
    
     
    // viewport, showIndex
    var scaleOrigin = false, indexThumb;
    if ($old.type == "show" && $new.type == "episode" && !$new.isNewCat) {
        indexThumb = $("#" + $new.path.join("-") + "-index-thumb");
        if (indexThumb.length) {
            $new.focus.css("left", 0).css("top", 0);
            animation.to = $new.videoPlaceholder;
            scaleOrigin = indexThumb.position();
            scaleOrigin.top += indexThumb.parent().position().top;
        }
    } else if ($old.type == "episode" && $new.type == "show" && !$new.isNewCat) {
        indexThumb = $("#" + $old.path.join("-") + "-index-thumb");
        if (indexThumb.length) {
            animation.from = $old.videoPlaceholder;
            animation.scaleType = "in";
            scaleOrigin = indexThumb.position();
            scaleOrigin.top += indexThumb.parent().position().top;
        }
    }
    if (scaleOrigin && $config.txDur !== 0) {
        // scale out from/in to show index
        animation.scaleOrigin = [scaleOrigin.top + 53, scaleOrigin.left + 95];
        animation.dur = $config.txDur / 2;
        animation.scale = true;
        refreshViewport(338);
    } else {
        refreshViewport();
    }
        
    
    
    if (animation.to.length && animation.from[0] != animation.to[0]) {
        (function(){
            var newState = $new, oldState = $old; // (in case state is changed during animation)
            
            
            
            var scrollTo = function() {
                if (animation.scrollTop !== false) {
                    if ($config.txDur === 0) {
                        setTimeout(function(){ // (timeout necessary for ios)
                            $($config.support.ios || $config.support.android ? "body" : "#body").scrollTop(animation.scrollTop);
                        }, 0);
                    } else {
                        $("#body").animate({ scrollTop: animation.scrollTop }, $config.txDur / 2);
                    }
                }
            };
            
            
            
            function after() {
                // cleanup
                oldState.focus.not(newState.focus).css("top", "-10000px").css("left", "-10000px");
                
                // cleanup after scaling episode to/from show index
                if (animation.scale) {
                    animation.from.add(animation.to).css("z-index", "auto");
                    if (newState.type == "show") {
                        oldState.videoPlaceholder.show();
                        $(".share-btn", oldState.focus).show();
                    } else if (newState.type == "episode") {
                        $(".share-btn", newState.focus).show();
                    }
                }
                
                // toggle details (and delay after())
                if (newState.type == "episode" && !newState.details.is(":visible")) {
                    newState.details.slideDown($config.txDur / 2, after);
                    return false;
                }
                                
                if ($config.txDur === 0) {
                    scrollTo();
                }
                                
                // ensure state hasn't changed...
                if ($new.focus[0] == newState.focus[0]) {
                    // autoplay
                    if (newState.type == "episode" && $config.autoplay == $new.path.join("/")) {
                        newState.video.video("load");
                    }
                    $config.autoplay = false;
                    
                    // parse comments xbfml
                    refreshCommentCounts();
                    if ($config.commentsEnabled && (newState.type == "episode" || newState.type == "post") && !$(".comments .fb-wrap", newState.focus).hasClass("fb-parsed")) {
                        $(".comments", newState.focus).addClass("loading");
                        if (typeof FB == "object") {
                            FB.XFBML.parse($(".comments .fb-wrap", newState.focus).addClass("fb-parsed")[0]);
                        }
                    }
                }
            }
            
            
            
            function animate() {
                if ($config.txDur > 0) {
                    scrollTo();
                }
                
                
                
                if (animation.scale && $new.type == "episode") {
                    $(".share-btn", $new.focus).hide();
                } else if (animation.scale && $new.type == "show") {
                    $(".share-btn", $old.focus).hide();
                }
                
                
                
                // bg-img (animate when navigating between anything that's not a sibling within a container)
                if ($new.isNewCat || $new.type == "page" || ($new.type == "feed" && $old.type != "feed") || ($new.type == "post" && $old.type != "post")) {
                    (function(){            
                        var start, offset;
                        if (animation.dir == "left") {
                            start = 0;
                            offset = -1 * $config.bgImgWidth * $config.bgTiles;
                        } else {
                            start = -1 * $config.bgImgWidth * $config.bgTiles;
                            offset = 0;
                        }
                        $("#bg-img").css("left", start).animate({ left: offset }, $config.txDur);
                    })();
                }
                
                
                
                var catTypeIsAnimated = false; // TO DO: must be specific to each category-type
                (function(){
                    $.each($(".category-type"), function(){
                        var catType = $(this);
                        var focus = $new.catLink.length && $.contains(catType[0], $new.catLink[0]) ? $new.catLink.parent().parent(".active-categories, .archived-categories") : $();
                        if ($new.init) {
                            if (!focus.length) {
                                focus = $(".active-categories", catType);
                            }
                            focus.show().siblings("div").hide();
                        } else if (focus.length && focus.is(":hidden")) {
                            catTypeIsAnimated = true;
                            var oldFocus = focus.siblings("div");
                            var height = focus.height() + 3, oldHeight = oldFocus.height() + 3;
                            var start, end;
                            if (focus.is(".archived-categories")) {
                                // up/archived
                                start = oldHeight + 15;
                                end = -1 * (oldHeight + 15);
                            } else {
                                // down/active
                                start = -1 * (height + 15);
                                end = height + 15;
                            }
                            focus.css("position", "absolute").css("top", start).css("left", "0").show().animate({ top: 0 }, $config.txDur / 2);
                            oldFocus.css("position", "absolute").css("top", 0).css("left", "0").animate({ top: end }, $config.txDur / 2);
                            catType.height(oldHeight).animate({ height: height }, $config.txDur / 2, function(){
                                oldFocus.hide();
                                oldFocus.add(focus).css("position", "static");
                                catType.css("height", "auto");
                                $old.catPages.add($old.catDesc).hide();
                            });
                        }
                    });
                })();
                
                
                
                // catDesc, catPages, catPage
                (function(){
                    if (($new.type == "episode" || $new.type == "post") && ($new.isNewCat || $old.type == "show" || $old.type == "feature")) {
                        $new.catDesc.delay($config.txDur / 2).slideDown($config.txDur / 2);
                    } else if (!catTypeIsAnimated && ($new.catLink.length === 0 || $new.catChildLink.length === 0 || $new.isNewCat)) {
                        $old.catPages.add($old.catDesc).slideUp($config.txDur / 2);
                    }
                    if ($new.isNewCatPage) {
                        var height = ($(".category-child-link", $new.catPage).length * 25) + ($(".category-page-link", $new.catPage).length * 40);
                        height += $new.catPage.hasClass("first") ? 3 : 0;
                        height += $new.catPage.hasClass("last") ? 3 : 0;
                        if (!$new.isNewCat && $old.catChildLink.length) {
                            var start, end, index = 0;
                            index = $new.catPage.attr("id").split("-");
                            index = index[index.length - 1];
                            
                            var oldHeight, oldIndex;
                            if ($old.catPage.length) {
                                oldHeight = ($(".category-child-link", $old.catPage).length * 25) + ($(".category-page-link", $old.catPage).length * 40);
                                oldHeight += $old.catPage.hasClass("first") ? 3 : 0;
                                oldHeight += $old.catPage.hasClass("last") ? 3 : 0;
                                oldIndex = $old.catPage.attr("id").split("-");
                                oldIndex = oldIndex[oldIndex.length - 1];
                            }
                            
                            if (index > oldIndex) {
                                // up/older
                                start = oldHeight + 15;
                                end = -1 * (oldHeight + 15);
                            } else {
                                // down/newer
                                start = -1 * (height + 15);
                                end = height + 15;
                            }
                            $new.catPage.siblings().not($old.catPage).css("left", "-100%");
                            $new.catPage.css("top", start).css("left", "0").animate({ top: 0 }, $config.txDur / 2);
                            $old.catPage.animate({ top: end }, $config.txDur / 2);
                            $new.catPages.animate({ height: height }, $config.txDur / 2);
                        } else {
                            $new.catPage.css("left", 0).css("top", 0).siblings().css("left", "-100%");
                            if (!catTypeIsAnimated && ($old.type == "episode" || $old.type == "post")) {
                                $old.catDesc.add($old.catPages).slideUp($config.txDur / 2);
                            }
                            $new.catPages.height(height);
                            $new.catPages.delay($config.txDur / 2).slideDown($config.txDur / 2);
                        }
                    }
                })();
                                
                
                
                refreshViewport();
                if (!animation.scale) {
                    var start, end;
                    switch (animation.dir) {
                    case "left":
                        start = animation.from.length ? animation.from.width() + 25 : animation.to.width() + 25;
                        end = -start;
                        break;
                    case "right":
                        start = -1 * animation.to.width() - 25;
                        end = -start;
                        break;
                    case "up":
                        start = animation.from.height() + 25;
                        end = -start;
                        break;
                    case "down":
                        end = animation.to.height() + 25;
                        start = -end;
                        break;
                    }
                    switch (animation.dir) {
                    case "left":
                    case "right":
                        animation.from.animate({ left: end }, $config.txDur);
                        animation.to.show().css("left", start).css("top", 0).animate({ left: 0 }, { duration: animation.dur, complete: after });
                        break;
                    case "up":
                    case "down":
                        animation.from.animate({ top: end }, $config.txDur);
                        animation.to.show().css("top", start).css("left", 0).animate({ top: 0 }, { duration: animation.dur, complete: after });
                        break;
                    }
                } else {
                    if (animation.scaleType == "in") {
                        animation.to.css("top", 0).css("left", 0).css("z-index", 30);
                        animation.from.css("z-index", 35).hide("scale", { percent: 0, origin: animation.scaleOrigin }, animation.dur, after);
                    } else { // (out)
                        animation.from.css("z-index", 30);
                        animation.to.css("top", 0).css("left", 0).css("z-index", 35)
                            .show("scale", { percent: 100, origin: animation.scaleOrigin }, animation.dur, after);
                    }
                }
            }
            
            
            
            if ($new.newEp) {
                $new.details.toggle(animation.scale && $old.type == "show" && !$new.isNewCat ? false : true);
            }
            
            
            
            if ($old.fromFeed && ($new.type == "feed" || $new.type == "feature" || $new.type == "search" || $new.type == "tag")) {
                var postLink = $(".post-link[href$='/" + $old.path.join("/") + "']:first", $new.focus);
                if (postLink.length) {
                    var postTop = postLink.parent().parent().position().top + 180;
                    var scrollTop = $("#body").scrollTop();
                    if (!(scrollTop < postTop + 300 && scrollTop > postTop - 300)) {
                        // show post context within the feed
                        animation.scrollTop = postTop;
                    }
                }
            } else if ($($config.support.ios || $config.support.android ? "body" : "#body").scrollTop() > 210) {
                // scroll to top of content
                animation.scrollTop = 210;
            }
            
            
            
            var delay = false;
            if (animation.scale && $old.type == "episode" && $new.type == "show" && !$new.isNewCat) {
                delay = true;
                $old.details.slideUp($config.txDur / 2, animate);
            }
            if (!delay) {
                animate();
            }
        })();
    }
}



function getState(path, queryString) {
    var state = $.extend(true, {}, $config.emptyState);
    state.fullSlug = path.join("-");
    state.pathString = path.join("/");
    queryString = typeof queryString == "undefined" ? "" : queryString;
    
    // determine type
    if (path.length === 0 || path[0] == "page") {
        state.type = 'feed';
    } else if ($config.pages.hasOwnProperty(path[0])) {
        state.type = 'page';
    } else if ($config.categories.hasOwnProperty(path[0])) {
        if (path.length == 1 || path[1] == "page") {
            state.type = $config.categories[path[0]].type;
        } else if (path.length == 2) {
            state.type = 'episode';
        }
    } else if (path[0] == "tags" && path.length >= 2) {
        state.type = 'tag';
    } else if (path.length == 3) {
        state.type = 'post';
    } else if (path[0] == "search") {
        state.type = 'search';
    } else if (path.length == 2) {
        // episode belonging to hidden show
        state.type = 'episode';
    }
    
    // determine page num
    switch (state.type) {
    case 'feed':
        state.pageNum = path.length === 0 ? 1 : path[1];
        break;
    case 'show': case 'feature':
        state.pageNum = path.length == 1 ? 1 : path[2];
        break;
    case 'tag': case 'search':
        state.pageNum = path.length == 2 ? 1 : path[3];
        break;
    }
    state.pageNum = parseInt(state.pageNum, 10);
    
    // determine post type
    var typeIndex = queryString.indexOf("type=");
    var separatorIndex = typeIndex > -1 ? queryString.indexOf("&", typeIndex) : -1;
    state.postType = typeIndex > -1 ? (separatorIndex > -1 ? queryString.substr(typeIndex + 5, separatorIndex) : queryString.substr(typeIndex + 5)) : "all";
    
    // select the focus
    switch (state.type) {
    case 'page': state.focus = $("#page-" + path[0]); break;
    case 'episode':
    case 'post': state.focus = $("#post-" + state.fullSlug); break;
    case 'feed':
        state.focus = $("#feed-" + state.postType + "-page-" + state.pageNum);
        break;
    case 'show': case 'feature':
        state.focus = $("#" + path[0] + "-" + state.postType + "-page-" + state.pageNum);
        break;
    case 'tag':
        state.focus = $("#tag-" + path[1] + "-" + state.postType + "-page-" + state.pageNum);
        break;
     case 'search':
        state.focus = $("#search-" + $config.searchMap[path[1]] + "-" + state.postType + "-page-" + state.pageNum);
        break;
    }
    
    // load category
    if (state.type == "show" || state.type == "feature" || state.type == "episode") {
        state.cat = path[0];
    }
    var post = $data[state.pathString];
    if (typeof post != "undefined") {
        state.cat = post.cat !== "" && ($config.allowPrivate || parseInt(post.cat_hidden, 10) === 0) ? post.cat : false;
    } else if (state.type == "post") {
        state.cat = false; // post missing: don't get cat elements
    }
    if (state.cat) {
        $.extend(state, {
            catLink:        $("#category-link-" + state.cat),
            catDesc:        $("#category-desc-" + state.cat),
            catPages:       $("#category-pages-" + state.cat)
        });
        if (state.type == "episode" || state.type == "post") {
            state.catChildLink = $("#category-child-link-" + state.fullSlug);
            state.catPage = state.catChildLink.parent();
        }
    }
    
    // load episode
    if (state.type == "episode") {
        state.video = $(".ui-video:first", state.focus);
        state.videoPlaceholder = $(".placeholder", state.video);
        state.videoPlayer = $(".player", state.video);
        state.details = $(".details-wrapper", state.focus);
    }
    
    state.path = path;
    state.queryString = queryString;
    return state;
}
function setState(path, queryString) {
    $(".loading-indicator").siblings(".play-btn").show();
    $(".loading-indicator").remove();
    
    var oldState = $new;
    var newState = getState(path, queryString);
    
    if (newState.pathString == oldState.pathString && newState.postType != oldState.postType) {
        var posts = $(".post", oldState.focus).add($(".post", newState.focus));
        $('<div class="post-overlay" />').css("opacity", 0).appendTo(posts).animate({ opacity: 0.4 }, $config.txDur / 2);
    }
                        
    // look for missing content
    var isCatChild = (newState.type == "episode" || newState.type == "post") && newState.cat !== false;
    if (!newState.focus.length || (isCatChild && newState.catPage.length === 0)) {
        var setDeferredState = function(path, queryString){
            // ensure that the state hasn't changed while waiting for the request to complete
            if ($.address.path() == "/" + newState.pathString) {
                setState(path, queryString);
            }
        };
        
        var requestString = newState.pathString + (queryString !== "" ? "?" + queryString : "");
        if ($.inArray(requestString, $config.requested) > -1) {
            newState.type = null;
        } else {
            $config.requested.push(requestString);
        }
        
        // loading indicator
        var loadingIndicator = $('<img src="/media/loading-itw-snake.gif" class="loading-indicator" />');
        var loadingIndicatorBig = $('<img src="/media/loading-snake-32x32.gif" class="loading-indicator" />');
        var feedPageLink = $(".feed-loader[href='/" + requestString + "']", oldState.focus);
        $("span", feedPageLink).prepend(loadingIndicator);
        if ((newState.type == "feature" || newState.type == "show") && !feedPageLink.length) {
            $("span", newState.catLink).add($(".category-archived-link[href='/" + newState.pathString + "'] span")).prepend(loadingIndicatorBig);
        } else if (newState.type == "episode" || newState.type == "post") {
            var catPageLink = $(".category-page-link[href='/" + newState.pathString + "']");
            $("span", catPageLink).prepend('<img src="/media/loading-itw-snake.gif" class="loading-indicator" />');
            
            var feedPost = $("#feed-post-" + newState.fullSlug, oldState.focus);
            var feedPostTitleImg = $(".loading-indicator-container:first", feedPost);
            loadingIndicatorBig.appendTo(feedPostTitleImg.length ? feedPostTitleImg : $(".body", feedPost));
            
            var featureLink = $("#features a[href='/" + newState.pathString + "']");
            featureLink.prepend('<img src="/media/loading-itw-snake.gif" class="loading-indicator" />');    
                
            $(".loading-indicator").siblings(".play-btn").hide();
        } else if (newState.type == "search" && !feedPageLink.length) {
            $("#search").prepend(loadingIndicator);
        } else if (newState.type == "tag" && !feedPageLink.length) {
            var tagLink = $(".tag-link[href='/" + newState.pathString + "']", oldState.focus);
            loadingIndicator.insertAfter(tagLink);
        }
        if (!$(".loading-indicator").length) {
            $("#tagline > span").prepend(loadingIndicator);
        }
                
        switch (newState.type) {
        case 'feed':
        case 'feature':
        case 'tag':
        case 'search':
            var url = $config.operatorUrl + "action=get-feed-posts&offset=" + ((newState.pageNum - 1) * 10);
            switch (newState.type) {
            case 'tag':
                url += "&tag=" + path[1];
                break;
            case 'search':
                url += "&search=" + path[1];
                break;
            case 'feature':
                url += "&category=" + path[0];
                break;
            }
            if (newState.postType != "all") {
                url += "&type=" + newState.postType;
            }
            
            $.getJSON(url + ($config.allowPrivate ? "&allow_private=true" : "")).complete(function(){
            }).success(function(feedPage){
                if (feedPage.posts.length > 0 || newState.type == "search" || newState.type == "tag") {
                    appendFeedPage(newState, feedPage);
                    setTimeout(function(){ setDeferredState(path, queryString); }, 0); // viewport height set incorrectly w/o timeout
                } else {
                    setDeferredState(["404"]);
                }
            }).error(function(){
                // TO DO: error feedback
            });
            break;
        case 'show':
            var totalPosts = $config.categories[path[0]].count;
            var totalCatPages = Math.ceil(totalPosts / $config.postsPerCatPage);
            var start = (newState.pageNum - 1) * $config.catPagesPerIndexPage + 1;
            var end = newState.pageNum * $config.catPagesPerIndexPage;
            if (end > totalCatPages) {
                end = totalCatPages;
            }
            
            var catPagesToLoad = [];
            for (i = start; i <= end; i++) {
                if ($("#" + path[0] + "-category-page-" + i).length === 0) {
                    catPagesToLoad.push(i);
                }
            }
                        
            $.getJSON($config.operatorUrl + "action=get-category-pages&slug=" + path[0] + "&pages=" + catPagesToLoad.join(",") + ($config.allowPrivate ? "&allow_private=true" : "")).complete(function(){
            }).success(function(result){
                if (result !== null) {
                    $.each(result.pages, function(){
                        appendCategoryPage(this);
                    });
                    appendShowIndexPage(path[0], newState.pageNum);
                    setTimeout(function(){ setDeferredState(path, queryString); }, 0); // viewport height set incorrectly w/o timeout
                } else {
                    setDeferredState(["404"]);
                }
            }).error(function(){
                // TO DO: error feedback
            });
            break;
        case 'episode':
        case 'post':
            if ($data.hasOwnProperty(newState.pathString) && (!isCatChild || newState.catPage.length > 0)) {
                appendPost($data[newState.pathString]);
                setState(path, queryString);
            } else {                
                $.getJSON($config.operatorUrl + "action=get-parent-category-page&path=" + newState.pathString + ($config.allowPrivate ? "&allow_private=true" : "")).complete(function(){
                }).success(function(result){
                    if (result.page) {
                        if (typeof result.page.items != "undefined") {
                            appendCategoryPage(result.page);
                            appendPost($data[newState.pathString]);
                        } else {
                            // TO DO: as with getParentCategoryPage, the JSON structure is misnamed
                            appendPost(result.page);
                        }
                        setDeferredState(path, queryString);
                    } else {
                        setDeferredState(["404"]);
                    }
                }).error(function(){
                    // TO DO: error feedback
                });
            }
            break;
        default:
            setState(["404"]);
        }
        return false;
    }
        
    // compare states
    $.extend(newState, {
        init:           typeof oldState.type === "undefined",
        fromFeed:       $.type(queryString) == "string" && queryString.indexOf("from-feed") > -1,
        newEp:          newState.type == "episode" && (oldState.type != "episode" || oldState.focus[0] != newState.focus[0]),
        isNewCat:       newState.cat && newState.cat != oldState.cat,
        isNewCatPage:   newState.catPage.length && (!oldState.catPage.length || newState.catPage[0] != oldState.catPage[0]) ? true : false
    });
            
    // set page title
    var title = "INTO THE WOODS.TV - ";
    switch (newState.type) {
    case 'feed':    title += "Music Media from the Pacific Northwest"; break;
    case 'tag':     title += "Tag: " + $config.tagMap[path[1]]; break;
    case 'search':  title += "Search: " + path[1]; break;
    case 'feature':
    case 'show':    title += $config.categories[path[0]].title; break;
    case 'page':    title += $config.pages[path[0]]; break;
    case 'episode':
    case 'post':    title += $data[newState.pathString].title; break;
    }
    $.address.title(title);
    
    // set state
    setTimeout(function(){ refreshState(newState); }, 0); // (without timeout, image dimensions aren't set, causing layout issues)
}



// URL handler
if ($config.initPath != "/") {
    var addressValue = $.address.value(),
        newLocation = $config.baseUrl + $config.stateBasePath + ($config.support.history ? "" : "/#!") + (addressValue != "/" ? addressValue : $config.initPath + window.location.search);
    if (newLocation != window.location.href) {
        window.location.replace(newLocation);
    }
}
$.address.crawlable(true);
if ($config.support.history) {
    $.address.state($config.stateBasePath);
}
$("#logo-link, .category-archived-link, .category-link, .category-child-link, .category-page-link, .index-link, .post-link, .tag-link, .feed-loader, .ui-gasbox a, .popular a, .feed-type-toggle > .white-btn").live("click", function(event){
    event.preventDefault();
    var href = $(this).attr("href").replace($config.baseUrl, ""); // (replace absolute URLs in IE)
    if (href.indexOf($config.stateBasePath) === 0) {
        href = href.replace($config.stateBasePath, "");
    }
            
    // custom link behaviors
    if ($(this).hasClass("post-link")) {
        if ($(this).parent().is(".ui-video.placeholder-only")) {
            $config.autoplay = href.substr(1, href.length - 1);
        }
        href += "?from-feed";
    }
    
    $.address.value(href);
});
    


$(function(){    
    // check for comments cookie
    if (document.cookie.length > 0) {
        var c_start = document.cookie.indexOf("commentsEnabled=");
        if (c_start != -1) {
            c_start = c_start + 16; // 16 = "commentsEnabled=" string length
            var c_end = document.cookie.indexOf(";", c_start);
            if (c_end == -1) {
                c_end = document.cookie.length;
            }
            $config.commentsEnabled = document.cookie.substring(c_start, c_end) == "true";
        }
    }
        
    // fades
    $("#logo-link").bind("mouseenter mouseleave", function(event){
        $("#logo-line-1").stop().animate({ opacity: event.type == "mouseenter" ? 0.8 : 0.65 }, $config.txDur / 4);
        $("#logo-line-2").stop().animate({ opacity: event.type == "mouseenter" ? 0.55 : 0.4 }, $config.txDur / 4);
    });
    $("#contact img, #fb-like").bind("mouseenter mouseleave", function(event){
        $(this).stop().animate({ opacity: event.type == "mouseenter" ? 1 : 0.6 }, $config.txDur / 4);
    });
    $(".post-link:not(.white-btn)").live("mouseenter mouseleave", function(event){
        $(this).parents(".post").find(".post-title").css("opacity", event.type == "mouseenter" ? 0.85 : 0.65);
    });
    $(".index-link").live("mouseenter mouseleave", function(event){
        $(".title", this).stop().animate({ opacity: event.type == "mouseenter" ? 0.8 : 0.65 }, $config.txDur / 4);
        $(".title-bg", this).stop().animate({ opacity: event.type == "mouseenter" ? 0.4 : 0.3 }, $config.txDur / 4);
    });
    
    // buttons
    $(".white-btn, .black-btn, .tag-link").live("hover", function(){
        $(this).toggleClass("hover");
    });
    
    // video player
    $("#fade-container").click(function(){
        $(".ui-video.loaded").video("pause");
    });
            
    // tracks
    $("#tracks").bind("refresh", function(){
        var tracks = $("#tracks");
        var count = $("#tracks .tracks li").length;
        var playing = $("#now-playing > .ui-audio").is(".loading, .loaded, .playing");
        
        var height = 0, marginBtm = 0;
        if (playing || count) {
            $("#now-playing").toggle(playing);
            $("#dl-instructions").toggle(count === 0);
            $("#tracks ul, #dl-controls").toggle(count > 0);
            var oldHeight = tracks.css("height").replace("px", "");
            tracks.css("height", "auto");
            height = tracks.outerHeight();
            tracks.height(oldHeight);
            marginBtm = 30;
        }
        $("#dl-tracks-btn span:not(.loading)").text("DOWNLOAD" + (count === 0 ? "" : count == 1 ? " TRACK" : " " + count + " TRACKS"));
        $("#tracks-container").stop().animate({ height: height, marginBottom: marginBtm }, $config.txDur / 2);
        tracks.stop().animate({ height: height }, $config.txDur / 2);
        $("#tracks-header").toggle(playing || count ? true : false);
    });
    $("#tracks .ui-audio").live("play pause", function(){
        $("#tracks").trigger("refresh");
    });
    $("#dl-tracks-btn").click(function(){
        if (!$(".loading:visible", this).length) { // (only allow one click)
            var tracks = $("#tracks ul .ui-audio");
            var name = tracks.length > 1 ? 'into-the-woods-tracks' : tracks.audio("option", "src").split("/").pop().replace(".mp3", "");
            var paths = [];
            $.each(tracks, function(){
                paths.push($(this).audio("option", "file"));
            });
            $("span, img", this).toggle();
            $.getJSON($config.operatorUrl + "action=get-tracks-link&paths=" + paths.join(","), function(result){
                $("#tracks .toggle-all-btn").click();
                $("span, img", $("#dl-tracks-btn")).toggle();
                location.href = 'http://intothewoods.tv/files/' + result.link + '?ts=' + result.ts + '&sig=' + result.sig + "&name=" + name;
            });
        }
    });
    $("#clear-tracks-btn").click(function(){
        $("#tracks > ul .dl-btn").click();
    });
    
    // comments
    $(".toggle-comments-btn").live("click", function(event){
        event.preventDefault();
        $config.commentsEnabled = $config.commentsEnabled === true ? false : true;
        $(".toggle-comments-btn span").html($config.commentsEnabled ? 'HIDE' : 'SHOW');
        $(".comments.module .fb-wrap").toggle($config.commentsEnabled);
        if (typeof FB == "object" && $config.commentsEnabled && !$(this).parent().siblings(".fb-wrap").hasClass("fb-parsed")) {
            FB.XFBML.parse($(this).parent().siblings(".fb-wrap")[0]);
        }
        $(".comments.module").toggleClass("disabled", $config.commentsEnabled ? false : true);
        
        // set cookie
        var exDate = new Date();
        exDate.setDate(exDate.getDate() + 365);
        document.cookie = "commentsEnabled=" + $config.commentsEnabled + ";expires=" + exDate.toUTCString();
    });
    
    // search
    $("#search").submit(function(event){
        event.preventDefault();
        var val = $.trim($("input", this).val());
        if (val.length >= 2) {
            $.address.value("search/" + encodeURIComponent(val));
        }
    });
    $("#search .white-btn").click(function(){
        $("#search").submit();
    });
    $("#search input").focus(function(){
        $(this).addClass("active");
    }).blur(function(){
        if ($.trim($(this).val()) === "") {
            $(this).val("").removeClass("active");
        }
    });
    
    // popular
    $(".popular a").live("mouseenter mouseleave", function(event){
        var gasbox = $("#features");
        var oldFocus = gasbox.gasbox("option", "focus");
        var addressPath = $.address.path();
        var path = $(this).attr("href").replace($config.baseUrl, "");
        var gasboxIndex = $(".ui-gasbox a[href$='" + path + "']").parent().index();
        var focus = event.type == "mouseenter" || path == addressPath ? gasboxIndex : -1;
        if (addressPath == "/" && (focus > -1 || oldFocus > -1) && focus != oldFocus) {
            gasbox.gasbox("option", "focus", focus).trigger("refresh");
        }
    });
    
    // feed type toggle
    $(".feed-type-toggle a").live("click", function(){
        $(this).addClass("toggled").siblings().removeClass("toggled");
    });
    
    
    
    // init
    $.each($payload, function(type){
        switch (type) {
        case 'categories':
            appendCategories(this);
            $("#categories");
            break;
        case 'categoryPages':
            $.each(this, function(){
                appendCategoryPage(this);
            });
            break;
        case 'feedPage':
            appendFeedPage(getState($config.initPath == "/" ? [] : $config.initPath.replace("/", "").split("/")), this);
            break;
        }
    });
    
    if (!$config.support.ios && !$config.support.android) {
        $("body").addClass("wrapper").children().wrapAll('<div id="body" />');
    }
    $("#logo-line-1").css("opacity", 0.65);
    $("#logo-line-2").css("opacity", 0.4);
    $("#facebook-icon, #twitter-icon, #rss-icon, #fb-like").css("opacity", 0.65);
    $("#dl-instructions img").css("opacity", 0.4);
    
    $.each($payload.featured, function(){
        this.title = this.title.indexOf("#") > -1 ? this.title.split(": ") : this.title.split(" - ");
        this.title = '<span>' + this.title[1] + '</span>: ' + this.title[0];
    });
    $("#features").gasbox({ images: $payload.featured, txDur: $config.support.ie ? 500 : $config.txDur / 2 });
    
    // window resize
    $(window).resize(windowRefresh);
    $("#body").scroll(windowRefresh);
    windowRefresh();
    
    $.address.change(function(event){
        setState(event.pathNames, event.queryString);
    });
    
    // article links
    $(".article-copy a").live("click", function(event){
        event.preventDefault();
        var href = $(this).attr("href");
        var internal = href.indexOf('http://intothewoods.tv') === 0 || href.indexOf('http') !== 0 ? true : false;
        var linkClass = $(this).attr("class");
        if (internal && (typeof linkClass == "undefined" || linkClass === "")) { // TO DO: testing the class seems inelegant at best
            $.address.value(href.replace('http://intothewoods.tv', ''));
        } else if (!internal) {
            window.open(href);
        }
    });
});
