tin
2/21/2016 - 6:36 PM

20thingsilearned.js

20thingsilearned.js

/*
  Nice to study. Original here http://www.20thingsilearned.com/js/twentythings.min.js
*/

var TT = TT || {};
TT.PAGE_WIDTH = 800;
TT.PAGE_HEIGHT = 500;
TT.PAGE_MIN_WIDTH = 1000;
TT.PAGE_MIN_HEIGHT = 680;
TT.PAGE_MARGIN_LEFT = 32;
TT.PAGE_MARGIN_TOP = 10;
TT.BOOK_WIDTH = 1660;
TT.BOOK_WIDTH_CLOSED = TT.BOOK_WIDTH / 2;
TT.BOOK_HEIGHT = 520;
TT.BOOK_OFFSET_X = 5;
TT.initialize = function() {
    TT.preloader.initialize();
    TT.storage.initialize();
    TT.cache.initialize();
    TT.search.initialize();
    TT.chapternav.initialize();
    TT.sharing.initialize();
    TT.overlay.initialize();
    TT.paperstack.initialize();
    TT.tableofthings.initialize();
    TT.flipintro.initialize();
    TT.lights();
    $(window).resize(TT.onWindowResize);
    $(window).scroll(TT.onWindowScroll);
    TT.updateLayout();
    $("img").mousedown(function(event) {
        event.preventDefault();
    });
};
TT.startup = function() {
    $("#pages section:not(.current)").width(0).hide();
    TT.navigation.initialize();
    TT.pageflip.initialize();
    TT.history.initialize();
    TT.illustrations.initialize();
    TT.chapternav.updateSelection();
    TT.tableofthings.updateSelection();
    TT.chapternav.updateReadMarkers();
    TT.tableofthings.updateReadMarkers();
    TT.paperstack.updateStack();
    TT.navigation.updateNextPrevLinks($("#pages section.current"));
};
TT.onWindowResize = function(event) {
    TT.updateLayout();
};
TT.onWindowScroll = function(event) {
    TT.updateLayout(true);
};
TT.updateLayout = function(fromScroll) {
    var screenSize = {
        width: $(window).width(),
        height: $(window).height()
        };
    $("body").css({
        overflowX: screenSize.width < TT.PAGE_MIN_WIDTH ? "auto": "hidden",
        overflowY: screenSize.height < TT.PAGE_MIN_HEIGHT ? "auto": "hidden"
    });
    screenSize.width = Math.max(screenSize.width, TT.PAGE_MIN_WIDTH);
    screenSize.height = Math.max(screenSize.height, TT.PAGE_MIN_HEIGHT);
    var centerOfScreen = {
        x: screenSize.width * 0.5,
        y: screenSize.height * 0.5
    };
    if (!fromScroll) {
        if (screenSize.width < TT.PAGE_MIN_WIDTH + $("#grey-mask").width() + 50) {
            $("#grey-mask").css({
                left: -((TT.PAGE_MIN_WIDTH + $("#grey-mask").width() + 50) - screenSize.width)
                });
        } else {
            $("#grey-mask").css({
                left: 0
            });
        }
        $("#book").css({
            left: centerOfScreen.x - (TT.BOOK_WIDTH * 0.5) - (TT.BOOK_WIDTH_CLOSED * 0.5) + TT.BOOK_OFFSET_X,
            top: centerOfScreen.y - (TT.BOOK_HEIGHT * 0.5),
            margin: 0
        });
        $("#table-of-contents div.center").css({
            left: centerOfScreen.x - (parseInt($("#table-of-contents div.center").innerWidth()) * 0.5),
            top: centerOfScreen.y - (parseInt($("#table-of-contents div.center").height()) * 0.5),
            margin: 0
        });
        $("#table-of-contents").css({
            width: screenSize.width
        });
        $("#credits").css({
            left: centerOfScreen.x - ($("#credits").width() * 0.5),
            top: centerOfScreen.y - ($("#credits").height() * 0.5),
            margin: 0
        });
        $("#overlay div.bookmark").css({
            left: centerOfScreen.x - (TT.overlay.BOOKMARK_WIDTH * 0.5),
            top: centerOfScreen.y - (TT.overlay.BOOKMARK_HEIGHT * 0.5) + 15,
            margin: 0
        });
        $("header").css({
            width: screenSize.width
        });
        $("footer").css({
            top: screenSize.height - $("footer").height(),
            width: screenSize.width,
            margin: 0
        });
        $("#search-dropdown").css({
            left: $("#search-field").position().left + 1,
            top: $("#search-field").position().top + $("#search-field").height() + 2
        });
        $("#chapter-nav").css({
            left: centerOfScreen.x - ($("#chapter-nav").width() * 0.5) + 5 + TT.BOOK_OFFSET_X,
            top: $("footer").position().top - $("#chapter-nav").outerHeight() + 5
        });
    }
    $("#pagination-prev").css({
        left: $(window).scrollLeft(),
        top: centerOfScreen.y - 20
    });
    $("#pagination-next").css({
        right: "auto",
        left: $(window).scrollLeft() + $(window).width() - $("#pagination-next").width(),
        top: centerOfScreen.y - 20
    });
};
TT.log = function(o) {};
TT.lights = function() {
    $("footer div.lights a").click(function(e) {
        e.preventDefault();
        if ($("html").hasClass("dark")) {
            $(this).parent().removeClass("clone").appendTo("footer .right-side");
            setTimeout(function() {
                $(".lights .icon").removeClass("off");
            }, 0);
        } else {
            $(this).parent().addClass("clone").appendTo("body");
            setTimeout(function() {
                $(".lights .icon").addClass("off");
            }, 0);
        }
        $("html,body").toggleClass("dark");
    });
    $(".dark footer").live("hover", function() {
        $("div.lights").toggleClass("active");
    });
};
TT.time = function() {
    return new Date().getTime();
};
TT.track = function(url) {
    _gaq.push(["_trackPageview", url]);
};
window.TT = TT;
TT.preloader = {};
TT.preloader.assetsComplete = false;
TT.preloader.contentsComplete = false;
TT.preloader.assetsLoaded = 0;
TT.preloader.assetsToLoad = 0;
TT.preloader.initialize = function() {
    TT.preloader.animation.initialize();
    TT.preloader.animation.activate();
    TT.preloader.assetsToLoad = 9;
    var frontImage = new Image();
    var backImage = new Image();
    var rightImage = new Image();
    var leftImage = new Image();
    var spritesImage = new Image();
    var repeatImage = new Image();
    var paperImage = new Image();
    var leftFlippedImage = new Image();
    var backImageFlipped = new Image();
    TT.preloader.addAssetToPreloadQueue($(frontImage));
    TT.preloader.addAssetToPreloadQueue($(backImage));
    TT.preloader.addAssetToPreloadQueue($(rightImage));
    TT.preloader.addAssetToPreloadQueue($(leftImage));
    TT.preloader.addAssetToPreloadQueue($(spritesImage));
    TT.preloader.addAssetToPreloadQueue($(repeatImage));
    TT.preloader.addAssetToPreloadQueue($(paperImage));
    TT.preloader.addAssetToPreloadQueue($(leftFlippedImage));
    TT.preloader.addAssetToPreloadQueue($(backImageFlipped));
    $("#preloader .contents").delay(50).animate({
        opacity: 1
    }, 300);
    frontImage.src = "/css/images/front-cover.jpg";
    backImage.src = "/css/images/back-cover.jpg";
    rightImage.src = "/css/images/right-page.jpg";
    leftImage.src = "/css/images/left-page.jpg";
    spritesImage.src = "/css/images/sprites.png";
    repeatImage.src = "/css/images/repeat-x.png";
    paperImage.src = "/css/images/right-page-paper.jpg";
    leftFlippedImage.src = "/css/images/left-page-flipped.jpg";
    backImageFlipped.src = "/css/images/back-cover-flipped.jpg";
};
TT.preloader.updateMeter = function() {
    var segmentsTotal = TT.preloader.assetsToLoad;
    var segmentsComplete = TT.preloader.assetsLoaded;
    if (TT.preloader.contentsComplete) {
        segmentsComplete++;
    }
    var progress = Math.min(segmentsComplete / segmentsTotal, 1);
    $("#preloader .progress .fill").width(progress * $("#preloader .progress").width());
};
TT.preloader.addAssetToPreloadQueue = function(asset) {
    asset.load(TT.preloader.onAssetLoaded);
    asset.error(TT.preloader.onAssetLoaded);
};
TT.preloader.onAssetLoaded = function(event) {
    if (++TT.preloader.assetsLoaded >= TT.preloader.assetsToLoad) {
        TT.preloader.onAllAssetsLoaded();
    }
    TT.preloader.updateMeter();
};
TT.preloader.onAllAssetsLoaded = function() {
    if (!TT.preloader.assetsComplete && TT.preloader.assetsLoaded >= TT.preloader.assetsToLoad) {
        TT.preloader.assetsComplete = true;
        TT.preloader.finish();
    }
    TT.preloader.updateMeter();
};
TT.preloader.onContentsLoaded = function() {
    if (!TT.preloader.contentsComplete) {
        TT.preloader.contentsComplete = true;
        TT.preloader.finish();
    }
    TT.preloader.updateMeter();
};
TT.preloader.finish = function() {
    if (TT.preloader.contentsComplete && TT.preloader.assetsComplete) {
        $("#preloader").stop(true, true).fadeOut(200, function() {
            TT.preloader.animation.deactivate();
            $(this).remove();
            $("#book").css({
                opacity: 0
            }).show().delay(100).fadeTo(700, 1);
        });
        TT.updateLayout();
        TT.startup();
        setTimeout(TT.preloader.loadIllustrations, 3000);
    }
    TT.preloader.updateMeter();
};
TT.preloader.loadIllustrations = function() {
    $("div.page").find("img").each(function() {
        if ($(this).attr("src") !== $(this).attr("data-src")) {
            $(this).attr("src", $(this).attr("data-src"));
        }
    });
};
TT.preloader.animation = {};
TT.preloader.animation.loopInterval = -1;
TT.preloader.animation.WIDTH = 89;
TT.preloader.animation.HEIGHT = 29;
TT.preloader.animation.VSPACE = 20;
TT.preloader.animation.canvas = null;
TT.preloader.animation.context = null;
TT.preloader.animation.flip = {
    progress: 0,
    alpha: 0
};
TT.preloader.animation.initialize = function() {
    this.canvas = $("#preloader .animation");
    this.canvas[0].width = this.WIDTH;
    this.canvas[0].height = this.HEIGHT + (this.VSPACE * 2);
    this.context = this.canvas[0].getContext("2d");
};
TT.preloader.animation.activate = function() {
    if (TT.preloader.animation.loopInterval == -1) {
        TT.preloader.animation.flip.progress = 1;
        TT.preloader.animation.loopInterval = setInterval(function() {
            TT.preloader.animation.render();
        }, 32);
    }
};
TT.preloader.animation.deactivate = function() {
    clearInterval(TT.preloader.animation.loopInterval);
    TT.preloader.animation.loopInterval = -1;
};
TT.preloader.animation.render = function() {
    this.context.clearRect(0, 0, this.WIDTH, this.HEIGHT + (this.VSPACE * 2));
    this.context.save();
    this.context.translate(0, this.VSPACE);
    this.context.fillStyle = "#f4f4f4";
    this.context.fillRect(0, 0, this.WIDTH, this.HEIGHT);
    this.context.fillStyle = "#999999";
    this.context.fillRect(0, 0, this.WIDTH, 1);
    this.context.fillRect(0, this.HEIGHT, this.WIDTH, 2);
    this.context.fillRect(0, 0, 1, this.HEIGHT);
    this.context.fillRect(this.WIDTH - 1, 0, 1, this.HEIGHT);
    this.context.fillRect(Math.floor(this.WIDTH * 0.5), 0, 1, this.HEIGHT);
    this.context.fillRect(54, 8, 25, 2);
    this.context.fillRect(54, 11, 25, 2);
    this.context.fillRect(54, 14, 25, 2);
    this.context.fillRect(54, 17, 25, 2);
    this.context.fillRect(54, 20, 25, 2);
    this.context.translate(0, 1);
    TT.preloader.animation.flip.progress -= Math.max(0.12 * (1 - Math.abs(TT.preloader.animation.flip.progress)), 0.02);
    TT.preloader.animation.flip.alpha = 1 - ((Math.abs(TT.preloader.animation.flip.progress) - 0.7) / 0.3);
    if (TT.preloader.animation.flip.progress <= -1.1) {
        TT.preloader.animation.flip.progress = 1;
    }
    var strength = 1 - Math.abs(TT.preloader.animation.flip.progress);
    var anchorOutdent = strength * 12;
    var controlOutdent = strength * 8;
    var source = {
        top: {
            x: this.WIDTH * 0.5,
            y: 0
        },
        bottom: {
            x: this.WIDTH * 0.5,
            y: this.HEIGHT
        }
    };
    var destination = {
        top: {
            x: source.top.x + (this.WIDTH * TT.preloader.animation.flip.progress * 0.55),
            y: 0 - anchorOutdent
        },
        bottom: {
            x: source.bottom.x + (this.WIDTH * TT.preloader.animation.flip.progress * 0.55),
            y: this.HEIGHT - anchorOutdent
        }
    };
    var control = {
        top: {
            x: source.top.x + (12 * TT.preloader.animation.flip.progress),
            y: -controlOutdent
        },
        bottom: {
            x: source.bottom.x + (12 * TT.preloader.animation.flip.progress),
            y: this.HEIGHT - controlOutdent
        }
    };
    this.context.fillStyle = "rgba(245,245,245," + TT.preloader.animation.flip.alpha + ")";
    this.context.strokeStyle = "rgba(90,90,90," + TT.preloader.animation.flip.alpha + ")";
    this.context.beginPath();
    this.context.moveTo(source.top.x, source.top.y);
    this.context.quadraticCurveTo(control.top.x, control.top.y, destination.top.x, destination.top.y);
    this.context.lineTo(destination.bottom.x, destination.bottom.y);
    this.context.quadraticCurveTo(control.bottom.x, control.bottom.y, source.bottom.x, source.bottom.y);
    this.context.fill();
    this.context.stroke();
    this.context.restore();
};
TT.history = {};
TT.history.TABLE_OF_CONTENTS = "table-of-things";
TT.history.HOME = "home";
TT.history.FOREWORD = "foreword";
TT.history.THEEND = "theend";
TT.history.CREDITS = "credits";
TT.history.previousHash = "";
TT.history.hashCheckInterval = -1;
TT.history.stack = [];
TT.history.initialize = function() {
    if (TT.history.supportsHistoryPushState()) {
        $(window).bind("popstate", TT.history.onHistoryChanged);
    } else {
        TT.history.hashCheckInterval = setInterval(TT.history.onCheckHash, 200);
    }
};
TT.history.supportsHistoryPushState = function() {
    return ("pushState" in window.history) && window.history.pushState !== null;
};
TT.history.onCheckHash = function() {
    if (document.location.hash !== TT.history.previousHash) {
        TT.history.navigateToPath(document.location.hash.slice(1));
        TT.history.previousHash = document.location.hash;
    }
};
TT.history.pushState = function(url) {
    if (TT.history.supportsHistoryPushState()) {
        window.history.pushState("", "", url);
    } else {
        TT.history.previousHash = "#" + url;
        document.location.hash = url;
    }
    TT.track(url);
    TT.history.stack.push(url);
};
TT.history.onHistoryChanged = function(event) {
    if (TT.history.supportsHistoryPushState()) {
        TT.history.navigateToPath(document.location.pathname);
    }
};
TT.history.navigateToPath = function(pathname) {
    TT.navigation.hideTableOfContents();
    var part1 = pathname.split("/")[1];
    var part2 = pathname.split("/")[2];
    if (!part1 || part1 == TT.history.HOME) {
        TT.navigation.goToHome(true);
    } else {
        if (part1 == TT.history.CREDITS) {
            TT.navigation.goToCredits(true);
        } else {
            if (part1 == TT.history.TABLE_OF_CONTENTS) {
                TT.navigation.showTableOfContents(true);
            } else {
                if (part1) {
                    if (part2) {
                        TT.navigation.goToPage(part1, part2, true);
                    } else {
                        TT.navigation.goToPage(part1, "1", true);
                    }
                }
            }
        }
    }
};
TT.storage = {};
TT.storage.isFirstTimeVisitor = true;
TT.storage.initialize = function() {
    TT.storage.routeDataRequest();
    if (TT.storage.supportsLocalStorage() && localStorage.data) {
        TT.storage.data = $.parseJSON(localStorage.data);
    }
};
TT.storage.data = {
    articles: {},
    progress: {},
    bookmark: {
        articleId: "",
        pageNumber: ""
    }
};
TT.storage.supportsLocalStorage = function() {
    return ("localStorage" in window) && window.localStorage !== null;
};
TT.storage.getArticlesFromServer = function() {
    TT.log("getting articles from server");
    var disabledArticles = TT.chapternav.getDisabledArticles();
    $.ajax({
        url: "/all",
        success: function(data) {
            var globalPageCounter = 0;
            TT.storage.data.articles = {};
            $(data).each(function() {
                var articleId = $(this).attr("id");
                $(this).find("section").each(function(i) {
                    globalPageCounter++;
                    $(this).addClass("globalPage-" + globalPageCounter).css("zIndex", 500 - globalPageCounter).hide();
                    if (TT.storage.supportsLocalStorage()) {
                        TT.storage.data.articles["/" + articleId + "/" + (i + 1)] = $("<div>").append($(this).clone()).remove().html();
                    }
                    var articleIsDisabled = false;
                    for (var i = 0; i < disabledArticles.length; i++) {
                        if (disabledArticles[i] == articleId) {
                            articleIsDisabled = true;
                        }
                    }
                    if (articleIsDisabled == false) {
                        $("#pages").append($("<div>").append($(this).clone()).remove().html());
                    }
                });
            });
            TT.storage.save();
            TT.storage.onFindBookmark();
            TT.storage.activateCurrentPageAndSetPageCount();
        }
    });
};
TT.storage.getArticlesFromStorage = function() {
    TT.log("getting articles from storage");
    TT.storage.isFirstTimeVisitor = false;
    if (localStorage.data) {
        TT.storage.data = $.parseJSON(localStorage.data);
    } else {
        TT.storage.getArticlesFromServer();
        return;
    }
    var disabledArticles = TT.chapternav.getDisabledArticles();
    for (var articlePath in TT.storage.data.articles) {
        var articleIsDisabled = false;
        for (var i = 0; i < disabledArticles.length; i++) {
            if (disabledArticles[i] == articlePath.split("/")[1]) {
                articleIsDisabled = true;
            }
        }
        if (articleIsDisabled == false) {
            $("#pages").append(TT.storage.data.articles[articlePath]);
        }
    }
    TT.storage.onFindBookmark();
    TT.storage.activateCurrentPageAndSetPageCount();
};
TT.storage.routeDataRequest = function() {
    if (!TT.storage.supportsLocalStorage()) {
        TT.storage.getArticlesFromServer();
    } else {
        var localVersionMatchesServerVersion = true;
        $.ajax({
            url: "/version",
            success: function(version) {
                TT.log("Version on server is: " + version);
                if (version != localStorage.version) {
                    localVersionMatchesServerVersion = false;
                    localStorage.version = version;
                }
                localVersionMatchesServerVersion ? TT.storage.getArticlesFromStorage() : TT.storage.getArticlesFromServer();
            },
            error: function() {
                TT.storage.getArticlesFromStorage();
            }
        });
    }
};
TT.storage.save = function() {
    localStorage.data = $.toJSON(TT.storage.data);
};
TT.storage.activateCurrentPageAndSetPageCount = function() {
    var $origArticle = $("#pages section").eq(0);
    $origArticle.attr("id", "original");
    $("#pages section:not(#original)").each(function(i) {
        if ($(this).hasClass($origArticle.attr("class"))) {
            $origArticle.remove();
            $(this).addClass("current").show().next("section").show();
            $('<span id="currentPage">' + parseFloat(i + 1) + "</span>").appendTo("body");
        }
    });
    if ($("#pages section.current").length === 0) {
        $("#pages section").first().addClass("current");
    }
    $("#pages section div.page").each(function(i) {
        $(this).append('<span class="pageNumber">' + (i + 1) + "</span>");
    });
    if ($("body").hasClass("home")) {
        $("#pages section").removeClass("current");
        $("#pages section").first().addClass("current");
    } else {
        if ($("body").hasClass("credits")) {
            $("#pages section").removeClass("current");
            $("#pages section").last().addClass("current");
        }
    }
    TT.preloader.onContentsLoaded();
};
TT.storage.onFindBookmark = function() {
    if (TT.storage.supportsLocalStorage()) {
        if (TT.storage.data.bookmark.articleId && !(TT.storage.data.bookmark.articleId == $("#articleId").text() && TT.storage.data.bookmark.pageNumber == $("#pageNumber").text())) {
            TT.overlay.showBookmark(function() {
                TT.navigation.goToPage(TT.storage.data.bookmark.articleId, TT.storage.data.bookmark.pageNumber);
            }, function() {
                TT.navigation.goToHome();
            }, function() {
                TT.storage.setBookmark($("#articleId").text(), $("#pageNumber").text());
            });
            TT.log("Bookmark found: " + TT.storage.data.bookmark.articleId + "/" + TT.storage.data.bookmark.pageNumber);
        } else {
            TT.storage.setBookmark($("#articleId").text(), $("#pageNumber").text());
        }
    }
};
TT.storage.setBookmark = function(articleId, pageNumber) {
    if (TT.storage.supportsLocalStorage() && articleId != TT.history.THEEND) {
        TT.storage.data.bookmark.articleId = articleId;
        TT.storage.data.bookmark.pageNumber = pageNumber;
        TT.storage.data.progress["/" + articleId + "/" + pageNumber] = true;
        TT.storage.save();
        TT.chapternav.updateReadMarkers();
        TT.tableofthings.updateReadMarkers();
    }
};
TT.storage.hasArticleBeenRead = function(articleId) {
    return TT.storage.data.progress["/" + articleId + "/1"] == true;
};
TT.pageflip = {};
TT.pageflip.HINT_WIDTH = 100;
TT.pageflip.CANVAS_VERTICAL_PADDING = 80;
TT.pageflip.CANVAS_HORIZONTAL_PADDING = 20;
TT.pageflip.CANVAS_WIDTH = TT.BOOK_WIDTH + (TT.pageflip.CANVAS_HORIZONTAL_PADDING * 2);
TT.pageflip.CANVAS_HEIGHT = TT.BOOK_HEIGHT + (TT.pageflip.CANVAS_VERTICAL_PADDING * 2);
TT.pageflip.FRAMERATE = 30;
TT.pageflip.CLICK_FREQUENCY = 350;
TT.pageflip.SOFT_FLIP = "soft";
TT.pageflip.HARD_FLIP = "hard";
TT.pageflip.ASSETS_URL = "/css/images/";
TT.pageflip.pages = [];
TT.pageflip.flips = [];
TT.pageflip.canvas = null;
TT.pageflip.context = null;
TT.pageflip.dirtyRegion = new Region();
TT.pageflip.dragging = false;
TT.pageflip.turning = false;
TT.pageflip.hinting = false;
TT.pageflip.loopInterval = -1;
TT.pageflip.mouse = {
    x: 0,
    y: 0
};
TT.pageflip.mouseHistory = [];
TT.pageflip.skew = {
    top: 0,
    topTarget: 0,
    bottom: 0,
    bottomTarget: 0
};
TT.pageflip.mouseDownTime = 0;
TT.pageflip.mouseIsDown = false;
TT.pageflip.texture = null;
TT.pageflip.textures = {};
TT.pageflip.flippedLeftPage = null;
TT.pageflip.flippedBackCover = null;
TT.pageflip.lastKeyboardNavigationTime = 0;
TT.pageflip.lastKeyboardNavigationDirection = null;
TT.pageflip.eventsAreBound = null;
TT.pageflip.initialize = function() {
    TT.pageflip.createCanvas();
    TT.pageflip.createTextures();
    if (TT.pageflip.eventsAreBound == null) {
        TT.pageflip.registerEventListeners();
    }
};
TT.pageflip.registerEventListeners = function() {
    TT.pageflip.unregisterEventListeners();
    TT.pageflip.eventsAreBound = true;
    $(document).mousemove(TT.pageflip.onMouseMove);
    $(document).mousedown(TT.pageflip.onMouseDown);
    $(document).mouseup(TT.pageflip.onMouseUp);
    $(document).keydown(TT.pageflip.onKeyPress);
};
TT.pageflip.unregisterEventListeners = function() {
    TT.pageflip.eventsAreBound = false;
    $(document).unbind("mousemove", TT.pageflip.onMouseMove);
    $(document).unbind("mousedown", TT.pageflip.onMouseDown);
    $(document).unbind("mouseup", TT.pageflip.onMouseUp);
    $(document).unbind("keydown", TT.pageflip.onKeyPress);
};
TT.pageflip.createTextures = function() {
    var browser = BrowserDetect.browser.toLowerCase();
    var version = BrowserDetect.version;
    var os = BrowserDetect.OS.toLowerCase();
    TT.pageflip.flippedLeftPage = new Image();
    TT.pageflip.flippedLeftPage.src = TT.pageflip.ASSETS_URL + "left-page-flipped.jpg";
    TT.pageflip.flippedBackCover = new Image();
    TT.pageflip.flippedBackCover.src = TT.pageflip.ASSETS_URL + "back-cover-flipped.jpg";
    TT.pageflip.textures.front = $("#front-cover img")[0];
    TT.pageflip.textures.back = TT.pageflip.flippedBackCover;
    TT.pageflip.textures.left = TT.pageflip.flippedLeftPage;
    TT.pageflip.textures.right = $("#right-page img")[0];
};
TT.pageflip.createCanvas = function() {
    TT.pageflip.canvas = $('<canvas id="pageflip"></canvas>');
    TT.pageflip.canvas.css({
        position: "absolute",
        top: -TT.pageflip.CANVAS_VERTICAL_PADDING,
        left: -TT.pageflip.CANVAS_HORIZONTAL_PADDING,
        zIndex: 0
    });
    TT.pageflip.canvas[0].width = TT.pageflip.CANVAS_WIDTH;
    TT.pageflip.canvas[0].height = TT.pageflip.CANVAS_HEIGHT;
    TT.pageflip.context = TT.pageflip.canvas[0].getContext("2d");
    TT.pageflip.canvas.appendTo($("#book"));
};
TT.pageflip.createCanvasTexture = function(image, translation, scale, rotation) {
    var canvas = $("<canvas></canvas>");
    canvas.css({
        position: "absolute",
        display: "block"
    });
    canvas[0].width = TT.BOOK_WIDTH_CLOSED;
    canvas[0].height = TT.BOOK_HEIGHT;
    var context = canvas[0].getContext("2d");
    context.translate(translation.x, translation.y);
    context.scale(scale.x, scale.y);
    context.rotate(rotation);
    context.drawImage(image, 0, 0);
    return canvas[0];
};
TT.pageflip.activate = function() {
    if (TT.pageflip.loopInterval == -1) {
        clearInterval(TT.pageflip.loopInterval);
        TT.pageflip.loopInterval = setInterval(TT.pageflip.redraw, 1000 / TT.pageflip.FRAMERATE);
    }
    TT.pageflip.canvas.css("z-index", 1010);
};
TT.pageflip.deactivate = function() {
    clearInterval(TT.pageflip.loopInterval);
    TT.pageflip.loopInterval = -1;
    TT.pageflip.context.clearRect(0, 0, TT.pageflip.CANVAS_WIDTH, TT.pageflip.CANVAS_HEIGHT);
    TT.pageflip.canvas.css("z-index", 0);
};
TT.pageflip.redraw = function() {
    var cvs = TT.pageflip.canvas[0];
    var ctx = TT.pageflip.context;
    var dirtyRect = TT.pageflip.dirtyRegion.toRectangle(40);
    if (dirtyRect.width > 1 && dirtyRect.height > 1) {
        ctx.clearRect(dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height);
    }
    TT.pageflip.dirtyRegion.reset();
    for (var i = 0, len = TT.pageflip.flips.length; i < len; i++) {
        var flip = TT.pageflip.flips[i];
        if (flip.type == TT.pageflip.HARD_FLIP) {
            TT.pageflip.renderHardFlip(flip);
        } else {
            TT.pageflip.renderSoftFlip(flip);
        }
    }
    TT.pageflip.removeInactiveFlips();
};
TT.pageflip.renderSoftFlip = function(flip) {
    var mouse = TT.pageflip.mouse;
    var skew = TT.pageflip.skew;
    var cvs = TT.pageflip.canvas[0];
    var ctx = TT.pageflip.context;
    var currentPage = flip.currentPage;
    if (flip.direction === -1) {
        currentPage = flip.targetPage;
    } else {
        flip.targetPage.width(TT.PAGE_WIDTH);
    }
    if (TT.pageflip.dragging && !flip.consumed) {
        mouse.x = Math.max(Math.min(mouse.x, TT.PAGE_WIDTH), -TT.PAGE_WIDTH);
        mouse.y = Math.max(Math.min(mouse.y, TT.PAGE_HEIGHT), 0);
        flip.progress = Math.min(mouse.x / TT.PAGE_WIDTH, 1);
        flip.x = TT.PAGE_WIDTH * flip.progress;
    } else {
        var distance = Math.abs(flip.target - flip.progress);
        var speed = flip.target == -1 ? 0.3: 0.2;
        var ease = distance < 1 ? speed + Math.abs(flip.progress * (1 - speed)) : speed;
        ease *= Math.max(1 - Math.abs(flip.progress), flip.target == 1 ? 0.5: 0.2);
        flip.progress += (flip.target - flip.progress) * ease;
        flip.x = TT.PAGE_WIDTH * flip.progress;
        if (Math.round(flip.progress * 99) == Math.round(flip.target * 99)) {
            flip.progress = flip.target;
            flip.x = TT.PAGE_WIDTH * flip.progress;
            currentPage.css({
                width: flip.x
            });
            if (flip.target == 1 || flip.target == -1) {
                flip.consumed = true;
                TT.pageflip.completeCurrentTurn();
                return false;
            }
        }
    }
    if (flip.target == -1 && flip.progress < -0.9) {
        flip.alpha = 1 - ((Math.abs(flip.progress) - 0.9) / 0.1);
    }
    var shadowAlpha = Math.min(1 - ((Math.abs(flip.progress) - 0.75) / 0.25), 1);
    flip.strength = 1 - (flip.x / TT.PAGE_WIDTH);
    var weighedFoldStrength = flip.strength > 1 ? 2 - flip.strength: flip.strength;
    var verticalOutdent = 40 * weighedFoldStrength;
    var horizontalSpread = (TT.PAGE_WIDTH * 0.5) * flip.strength * 0.95;
    if (flip.x + horizontalSpread < 0) {
        horizontalSpread = Math.abs(flip.x);
    }
    if (TT.navigation.isCreditsPage()) {
        horizontalSpread = 0;
    }
    var shadowSpread = (TT.PAGE_WIDTH * 0.5) * Math.max(Math.min(flip.strength, 0.5), 0);
    var rightShadowWidth = (TT.PAGE_WIDTH * 0.5) * Math.max(Math.min(flip.strength, 0.5), 0);
    var leftShadowWidth = (TT.PAGE_WIDTH * 0.5) * Math.max(Math.min(weighedFoldStrength, 0.5), 0);
    var foldShadowWidth = (TT.PAGE_WIDTH * 0.9) * Math.max(Math.min(flip.strength, 0.05), 0);
    currentPage.css({
        width: Math.max(flip.x + horizontalSpread * 0.5, 0)
        });
    if (TT.pageflip.dragging) {
        skew.topTarget = Math.max(Math.min((mouse.y / (TT.PAGE_HEIGHT * 0.5)), 1), 0) * (40 * weighedFoldStrength);
        skew.bottomTarget = Math.max(Math.min(1 - (mouse.y - (TT.PAGE_HEIGHT * 0.5)) / (TT.PAGE_HEIGHT * 0.5), 1), 0) * (40 * weighedFoldStrength);
    } else {
        skew.topTarget = 0;
        skew.bottomTarget = 0;
    }
    if (flip.progress === 1) {
        skew.top = 0;
        skew.bottom = 0;
    }
    skew.top += (skew.topTarget - skew.top) * 0.3;
    skew.bottom += (skew.bottomTarget - skew.bottom) * 0.3;
    flip.x += horizontalSpread;
    var drawingOffset = {
        x: TT.pageflip.CANVAS_HORIZONTAL_PADDING + TT.PAGE_WIDTH + TT.PAGE_MARGIN_LEFT,
        y: TT.pageflip.CANVAS_VERTICAL_PADDING + TT.PAGE_MARGIN_TOP
    };
    ctx.save();
    ctx.translate(drawingOffset.x, drawingOffset.y);
    ctx.globalAlpha = flip.alpha;
    if (flip.direction == -1) {
        ctx.globalCompositeOperation = "destination-over";
    }
    ctx.strokeStyle = "rgba(0,0,0,0.1)";
    ctx.lineWidth = 0.5;
    ctx.beginPath();
    ctx.moveTo(flip.x + 1, 0);
    ctx.lineTo(flip.x + 1, TT.PAGE_HEIGHT);
    ctx.stroke();
    var foldGradient = ctx.createLinearGradient(flip.x - shadowSpread, 0, flip.x, 0);
    foldGradient.addColorStop(0.35, "#fafafa");
    foldGradient.addColorStop(0.73, "#eeeeee");
    foldGradient.addColorStop(0.9, "#fafafa");
    foldGradient.addColorStop(1, "#e2e2e2");
    ctx.fillStyle = foldGradient;
    ctx.strokeStyle = "rgba(0,0,0,0.1)";
    ctx.lineWidth = 0.5;
    ctx.beginPath();
    ctx.moveTo(flip.x, 0);
    ctx.lineTo(flip.x, TT.PAGE_HEIGHT);
    ctx.quadraticCurveTo(flip.x, TT.PAGE_HEIGHT + (verticalOutdent * 1.9), flip.x - horizontalSpread + skew.bottom, TT.PAGE_HEIGHT + verticalOutdent);
    ctx.lineTo(flip.x - horizontalSpread + skew.top, -verticalOutdent);
    ctx.quadraticCurveTo(flip.x, -verticalOutdent * 1.9, flip.x, 0);
    ctx.fill();
    ctx.stroke();
    ctx.beginPath();
    ctx.strokeStyle = "rgba(0,0,0," + (0.04 * shadowAlpha) + ")";
    ctx.lineWidth = 20 * shadowAlpha;
    ctx.beginPath();
    ctx.moveTo(flip.x + skew.top - horizontalSpread, -verticalOutdent * 0.5);
    ctx.lineTo(flip.x + skew.bottom - horizontalSpread, TT.PAGE_HEIGHT + (verticalOutdent * 0.5));
    ctx.stroke();
    var rightShadowGradient = ctx.createLinearGradient(flip.x, 0, flip.x + rightShadowWidth, 0);
    rightShadowGradient.addColorStop(0, "rgba(0,0,0," + (shadowAlpha * 0.1) + ")");
    rightShadowGradient.addColorStop(0.8, "rgba(0,0,0,0.0)");
    ctx.save();
    ctx.globalCompositeOperation = "destination-over";
    ctx.fillStyle = rightShadowGradient;
    ctx.beginPath();
    ctx.moveTo(flip.x, 0);
    ctx.lineTo(flip.x + rightShadowWidth, 0);
    ctx.lineTo(flip.x + rightShadowWidth, TT.PAGE_HEIGHT);
    ctx.lineTo(flip.x, TT.PAGE_HEIGHT);
    ctx.fill();
    var foldShadowGradient = ctx.createLinearGradient(flip.x, 0, flip.x + foldShadowWidth, 0);
    foldShadowGradient.addColorStop(0, "rgba(0,0,0," + (shadowAlpha * 0.15) + ")");
    foldShadowGradient.addColorStop(1, "rgba(0,0,0,0.0)");
    ctx.fillStyle = foldShadowGradient;
    ctx.beginPath();
    ctx.moveTo(flip.x, 0);
    ctx.lineTo(flip.x + foldShadowWidth, 0);
    ctx.lineTo(flip.x + foldShadowWidth, TT.PAGE_HEIGHT);
    ctx.lineTo(flip.x, TT.PAGE_HEIGHT);
    ctx.fill();
    ctx.restore();
    var leftShadowGradient = ctx.createLinearGradient(flip.x - horizontalSpread - leftShadowWidth, 0, flip.x - horizontalSpread, 0);
    leftShadowGradient.addColorStop(0, "rgba(0,0,0,0.0)");
    leftShadowGradient.addColorStop(1, "rgba(0,0,0," + (shadowAlpha * 0.05) + ")");
    ctx.fillStyle = leftShadowGradient;
    ctx.beginPath();
    ctx.moveTo(flip.x - horizontalSpread + skew.top - leftShadowWidth, 0);
    ctx.lineTo(flip.x - horizontalSpread + skew.top, 0);
    ctx.lineTo(flip.x - horizontalSpread + skew.bottom, TT.PAGE_HEIGHT);
    ctx.lineTo(flip.x - horizontalSpread + skew.bottom - leftShadowWidth, TT.PAGE_HEIGHT);
    ctx.fill();
    ctx.restore();
    TT.pageflip.dirtyRegion.inflate(TT.PAGE_WIDTH + TT.pageflip.CANVAS_HORIZONTAL_PADDING + flip.x - horizontalSpread - leftShadowWidth, 0);
    TT.pageflip.dirtyRegion.inflate(TT.PAGE_WIDTH + TT.pageflip.CANVAS_HORIZONTAL_PADDING + flip.x + rightShadowWidth, TT.pageflip.CANVAS_HEIGHT);
};
TT.pageflip.renderHardFlip = function(flip) {
    var mouse = TT.pageflip.mouse;
    var skew = TT.pageflip.skew;
    var cvs = TT.pageflip.canvas[0];
    var ctx = TT.pageflip.context;
    var currentPage = flip.currentPage;
    if (flip.direction === -1) {
        currentPage = flip.targetPage;
    }
    if (TT.pageflip.dragging) {
        mouse.x = Math.max(Math.min(mouse.x, TT.PAGE_WIDTH), -TT.PAGE_WIDTH);
        mouse.y = Math.max(Math.min(mouse.y, TT.PAGE_HEIGHT), 0);
        flip.target = mouse.x / TT.PAGE_WIDTH;
        flip.progress += (flip.target - flip.progress) * 0.4;
        flip.x = TT.PAGE_WIDTH * flip.progress;
    } else {
        if (Math.abs(flip.target) === 1) {
            flip.progress += Math.max(0.5 * (1 - Math.abs(flip.progress)), 0.02) * (flip.target < flip.progress ? -1: 1);
            flip.progress = Math.max(Math.min(flip.progress, 1), -1);
        } else {
            flip.progress += (flip.target - flip.progress) * 0.4;
        }
        flip.x = TT.PAGE_WIDTH * flip.progress;
        if (flip.progress === 1 || flip.progress === -1) {
            flip.progress = flip.target;
            flip.x = TT.PAGE_WIDTH * flip.progress;
            if (TT.navigation.isCreditsPage()) {
                currentPage.width(flip.x);
            }
            if (flip.target == 1 || flip.target == -1) {
                flip.consumed = true;
                TT.pageflip.completeCurrentTurn();
            }
        }
    }
    if (TT.navigation.isHomePage()) {
        if (flip.progress > 0.99) {
            $("#front-cover-bookmark").stop(true, true).fadeIn(300);
            $("#front-cover").show();
        } else {
            $("#front-cover").hide();
            if (flip.progress < 0.99) {
                $("#front-cover-bookmark").fadeOut(250, function() {
                    $(this).hide();
                });
            }
        }
        if (flip.progress > 0) {
            TT.pageflip.texture = TT.pageflip.textures.front;
            $("#left-page").width(0).hide();
        } else {
            TT.pageflip.texture = TT.pageflip.textures.left;
            if (flip.progress < -0.99) {
                $("#left-page").show().width(TT.BOOK_WIDTH_CLOSED);
            } else {
                $("#left-page").width(0).hide();
                $("#right-page").show();
            }
        }
        $("#page-shadow-overlay").stop(true, true).fadeTo(0.1, flip.progress * 0.3);
    } else {
        if (TT.navigation.isCreditsPage() || TT.navigation.isLastPage()) {
            if (flip.progress < -0.998) {
                if (TT.navigation.isCreditsPage()) {
                    $("#back-cover").show();
                } else {
                    $("#left-page").show().width(TT.BOOK_WIDTH_CLOSED);
                }
            } else {
                $("#back-cover").hide();
                $("body").addClass("credits");
            }
            if (flip.progress > 0) {
                TT.pageflip.texture = TT.pageflip.textures.right;
                if (flip.progress > 0.996) {
                    $("#right-page").show();
                    $("body").removeClass("credits");
                } else {
                    $("#right-page").hide();
                }
            } else {
                TT.pageflip.texture = TT.pageflip.textures.back;
            }
        } else {
            $("#right-page").show();
            $("#left-page").show().width(TT.BOOK_WIDTH_CLOSED);
        }
    }
    if (flip.target == -1 && flip.progress < -0.95) {
        flip.alpha = 1 - ((Math.abs(flip.progress) - 0.95) / 0.05);
    }
    flip.strength = 1 - (flip.x / TT.PAGE_WIDTH);
    var weighedFoldStrength = flip.strength > 1 ? 2 - flip.strength: flip.strength;
    if (TT.navigation.isCreditsPage() || TT.navigation.isLastPage()) {
        currentPage.css({
            width: Math.max(flip.x, 0)
            });
    }
    ctx.save();
    ctx.translate(TT.pageflip.CANVAS_HORIZONTAL_PADDING + TT.BOOK_WIDTH_CLOSED, TT.pageflip.CANVAS_VERTICAL_PADDING);
    var scaleX = flip.progress;
    var scaleY = 0;
    var scaleYFactor = 0.35;
    var scaleYFinal = 1 + (1 * scaleYFactor) * weighedFoldStrength;
    var width = TT.BOOK_WIDTH_CLOSED;
    var height = TT.BOOK_HEIGHT;
    var segments = Math.round(40 + (30 * (0.9999 - ((TT.BOOK_WIDTH_CLOSED * scaleX)) / TT.BOOK_WIDTH_CLOSED)));
    segments = Math.min(width, segments);
    var segmentWidth = width / segments;
    var thickness = 10 * weighedFoldStrength;
    var hoffset = flip.progress <= 0.05 ? 1 + (1 - (Math.max(flip.progress, 0) / 0.05)) * -thickness: -1;
    var voffset = {
        left: Math.abs(Math.min(flip.progress, 0)) * 2,
        right: flip.progress * 2
    };
    if (Math.abs(scaleX) < 0.99) {
        var ext = ((height - (height * scaleYFinal)) / 2);
        ctx.fillStyle = "rgb( 88, 115, 160 )";
        ctx.beginPath();
        ctx.moveTo(0, -0.5);
        ctx.lineTo((width * scaleX) - (2 * scaleX), ext - 0.5);
        ctx.lineTo((width * scaleX) + (thickness + hoffset), ext + voffset.right);
        ctx.lineTo((width * scaleX) + (thickness + hoffset), ext + (height * scaleYFinal) - voffset.right);
        ctx.lineTo((width * scaleX) - (2 * scaleX), ext + (height * scaleYFinal) + 0.5);
        ctx.lineTo(0, height + 0.5);
        ctx.closePath();
        ctx.fill();
    }
    for (var i = 0; i < segments; i++) {
        scaleY = 1 + (i / segments) * scaleYFactor * weighedFoldStrength;
        var y = (height - (height * scaleY)) / 2;
        var sw = i >= segments - 1 ? segmentWidth: segmentWidth + 3;
        ctx.save();
        ctx.translate(0, y);
        ctx.transform(scaleX, 0, 0, scaleY, 0, 0);
        while ((i * segmentWidth) + sw > TT.BOOK_WIDTH_CLOSED) {
            sw *= 0.9999;
        }
        ctx.drawImage(TT.pageflip.texture, i * segmentWidth, 0, sw, height, i * segmentWidth, 0, sw, height);
        ctx.restore();
    }
    var intensity = Math.max(Math.abs(weighedFoldStrength), 0.9);
    var ps = {
        top: {
            x: (width * scaleX) + hoffset,
            y: (height - (height * scaleY)) / 2
        },
        bottom: {
            x: (width * scaleX) + hoffset,
            y: ((height - (height * scaleY)) / 2) + height * scaleY
        }
    };
    ctx.fillStyle = "rgb( " + Math.round(intensity * 88) + ", " + Math.round(intensity * 115) + ", " + Math.round(intensity * 160) + " )";
    ctx.beginPath();
    ctx.moveTo(ps.top.x, ps.top.y + voffset.left);
    ctx.lineTo(ps.top.x + thickness, ps.top.y + voffset.right);
    ctx.lineTo(ps.bottom.x + thickness, ps.bottom.y - voffset.right);
    ctx.lineTo(ps.bottom.x, ps.bottom.y - voffset.left);
    ctx.closePath();
    ctx.fill();
    TT.pageflip.dirtyRegion.inflate(TT.PAGE_WIDTH + TT.pageflip.CANVAS_HORIZONTAL_PADDING + TT.PAGE_MARGIN_LEFT - thickness, ps.top.y + TT.pageflip.CANVAS_VERTICAL_PADDING);
    TT.pageflip.dirtyRegion.inflate(TT.PAGE_WIDTH + TT.pageflip.CANVAS_HORIZONTAL_PADDING + (width * scaleX) + thickness, ps.bottom.y + TT.pageflip.CANVAS_VERTICAL_PADDING);
    ctx.restore();
};
TT.pageflip.removeInactiveFlips = function() {
    var activeFlips = 0;
    for (var i = 0; i < TT.pageflip.flips.length; i++) {
        var flip = TT.pageflip.flips[i];
        if (flip.progress === flip.target && (flip.target === 1 || flip.target === -1)) {
            TT.pageflip.flips.splice(i, 1);
            i--;
        } else {
            activeFlips++;
        }
    }
    if (activeFlips == 0) {
        TT.pageflip.deactivate();
    }
};
TT.pageflip.removeHardFlips = function() {
    for (var i = 0; i < TT.pageflip.flips.length; i++) {
        var flip = TT.pageflip.flips[i];
        if (flip.type == TT.pageflip.HARD_FLIP) {
            TT.pageflip.flips.splice(i, 1);
            i--;
        }
    }
};
TT.pageflip.turnToPage = function(currentPage, targetPage, direction, type) {
    if (type == TT.pageflip.HARD_FLIP && !TT.pageflip.dragging) {
        TT.pageflip.removeHardFlips();
    }
    var flip = TT.pageflip.getCurrentFlip();
    if (flip.consumed) {
        flip = TT.pageflip.createFlip();
    }
    TT.pageflip.dragging = false;
    TT.pageflip.turning = true;
    TT.pageflip.hinting = false;
    flip.currentPage = currentPage;
    flip.targetPage = targetPage;
    flip.direction = direction;
    flip.alpha = 1;
    flip.consumed = true;
    flip.type = type || TT.pageflip.SOFT_FLIP;
    flip.target = -1;
    if (direction === -1) {
        flip.target = 1;
        flip.progress = -1;
    }
    TT.pageflip.activate();
    TT.pageflip.redraw();
};
TT.pageflip.completeCurrentTurn = function() {
    if (TT.pageflip.turning) {
        TT.pageflip.turning = false;
        var flip = TT.pageflip.flips[TT.pageflip.flips.length - 1];
        if (flip) {
            TT.navigation.updateCurrentPointer(flip.currentPage, flip.targetPage);
        }
    }
};
TT.pageflip.getCurrentFlip = function() {
    if (TT.pageflip.flips.length == 0) {
        TT.pageflip.createFlip();
    }
    return TT.pageflip.flips[TT.pageflip.flips.length - 1];
};
TT.pageflip.createFlip = function() {
    if (TT.pageflip.flips.length > 3) {
        TT.pageflip.flips = TT.pageflip.flips.splice(4, 99);
    }
    var flip = new TT.pageflip.Flip();
    TT.pageflip.flips.push(flip);
    return flip;
};
TT.pageflip.getHintRegion = function() {
    if (TT.navigation.isHomePage() || TT.navigation.isLastPage() || TT.navigation.isCreditsPage()) {
        return {
            left: TT.BOOK_WIDTH_CLOSED - TT.pageflip.HINT_WIDTH,
            right: TT.BOOK_WIDTH_CLOSED,
            top: 0,
            bottom: TT.PAGE_HEIGHT
        };
    }
    return {
        left: TT.PAGE_WIDTH - TT.pageflip.HINT_WIDTH,
        right: TT.PAGE_WIDTH,
        top: 0,
        bottom: TT.PAGE_HEIGHT
    };
};
TT.pageflip.onKeyPress = function(event) {
    if (!TT.search.hasFocus) {
        var hasPassedLockdown = TT.time() - TT.pageflip.lastKeyboardNavigationTime > 1000;
        if (event.keyCode == 37 && (TT.pageflip.lastKeyboardNavigationDirection === -1 || hasPassedLockdown)) {
            TT.navigation.goToPreviousPage();
            TT.pageflip.lastKeyboardNavigationDirection = -1;
            TT.pageflip.lastKeyboardNavigationTime = TT.time();
            event.preventDefault();
            return false;
        } else {
            if (event.keyCode == 39 && (TT.pageflip.lastKeyboardNavigationDirection === 1 || hasPassedLockdown)) {
                TT.navigation.goToNextPage();
                TT.pageflip.lastKeyboardNavigationDirection = 1;
                TT.pageflip.lastKeyboardNavigationTime = TT.time();
                event.preventDefault();
                return false;
            }
        }
    }
};
TT.pageflip.onMouseMove = function(event) {
    var mouse = TT.pageflip.getRelativeMousePosition(event.clientX - 14, event.clientY);
    var hinting = TT.pageflip.hinting;
    TT.pageflip.hinting = false;
    $("body").css("cursor", "");
    if (!TT.pageflip.dragging && !TT.pageflip.turning && (!TT.navigation.isCreditsPage() || (TT.navigation.isCreditsPage() && TT.navigation.isBookOpen()))) {
        var flip = TT.pageflip.getCurrentFlip();
        if (flip.progress < 0) {
            flip = TT.pageflip.createFlip();
        }
        var isHardCover = (TT.navigation.isHomePage() || TT.navigation.isLastPage() || (TT.navigation.isCreditsPage() && TT.navigation.isBookOpen()));
        flip.type = isHardCover ? TT.pageflip.HARD_FLIP: TT.pageflip.SOFT_FLIP;
        var hintRegion = TT.pageflip.getHintRegion();
        if (mouse.x > hintRegion.left && mouse.x < hintRegion.right && mouse.y > hintRegion.top && mouse.y < hintRegion.bottom) {
            if (TT.pageflip.mouseHistory[4]) {
                var distanceX = TT.pageflip.mouse.x - (TT.pageflip.mouseHistory[4].x || 0);
                var distanceY = TT.pageflip.mouse.y - (TT.pageflip.mouseHistory[4].y || 0);
                var distanceTravelled = Math.sqrt(distanceX * distanceX + distanceY * distanceY);
            } else {
                var distanceTravelled = 0;
            }
            if (!TT.navigation.isHomePage() || distanceTravelled < 100) {
                flip.target = Math.min(mouse.x / TT.PAGE_WIDTH, 0.98);
                $("body").css("cursor", "pointer");
                TT.pageflip.activate();
                TT.pageflip.hinting = true;
                if (TT.navigation.isHomePage()) {
                    flip.target = Math.min(mouse.x / TT.PAGE_WIDTH, 0.95);
                    $("#pages section.current").show().width(TT.PAGE_WIDTH);
                } else {
                    $("#pages section.current").next("section").show().width(TT.PAGE_WIDTH);
                }
            }
        } else {
            if (flip.progress !== 1 && flip.target !== -1) {
                if (TT.pageflip.hinting == true) {
                    $("#pages section.current").next("section").width(0);
                }
                flip.target = 1;
                TT.pageflip.activate();
                TT.pageflip.hinting = false;
            }
        }
    } else {
        if (TT.pageflip.dragging) {
            if (TT.pageflip.getCurrentFlip().type == TT.pageflip.HARD_FLIP) {
                if (TT.pageflip.getCurrentFlip().target < 0.15) {}
            } else {
                TT.pageflip.getCurrentFlip().alpha = 1;
            }
        }
    }
    while (TT.pageflip.mouseHistory.length > 9) {
        TT.pageflip.mouseHistory.pop();
    }
    TT.pageflip.mouseHistory.unshift(TT.pageflip.mouse);
    TT.pageflip.mouse = mouse;
};
TT.pageflip.onMouseDown = function(event) {
    TT.pageflip.mouseIsDown = true;
    var mouse = TT.pageflip.getRelativeMousePosition(event.clientX, event.clientY);
    var hintRegion = TT.pageflip.getHintRegion();
    if (mouse.x > hintRegion.left && mouse.x < hintRegion.right && mouse.y > hintRegion.top && mouse.y < hintRegion.bottom && !TT.pageflip.turning) {
        if (TT.time() - TT.pageflip.mouseDownTime > TT.pageflip.CLICK_FREQUENCY) {
            TT.pageflip.dragging = true;
        }
        TT.pageflip.mouseDownTime = TT.time();
        TT.pageflip.activate();
        return false;
    }
};
TT.pageflip.onMouseUp = function(event) {
    TT.pageflip.mouseIsDown = false;
    TT.pageflip.onMouseMove(event);
    if (TT.time() - TT.pageflip.mouseDownTime < TT.pageflip.CLICK_FREQUENCY) {
        TT.navigation.goToNextPage();
        TT.pageflip.dragging = false;
        return false;
    }
    var mouse = TT.pageflip.getRelativeMousePosition(event.clientX, event.clientY);
    if (TT.pageflip.dragging && mouse.x < TT.PAGE_WIDTH * 0.45) {
        var succeeded = TT.navigation.goToNextPage();
        if (succeeded == false) {
            TT.pageflip.dragging = false;
        }
    } else {
        TT.pageflip.dragging = false;
        TT.pageflip.onMouseMove(event);
    }
};
TT.pageflip.getRelativeMousePosition = function(globalX, globalY) {
    var mouse = {
        x: globalX,
        y: globalY
    };
    mouse.x -= $("#pages").offset().left + TT.PAGE_WIDTH;
    mouse.y -= $("#pages").offset().top;
    return mouse;
};
TT.pageflip.Flip = function() {
    this.id = Math.round(Math.random() * 1000);
    this.currentPage = $("#pages section.current");
    this.targetPage = $("#pages section.current");
    this.direction = -1;
    this.progress = 1;
    this.target = 1;
    this.strength = 0;
    this.alpha = 1;
    this.type = TT.pageflip.SOFT_FLIP;
    this.x = 0;
    this.consumed = false;
};
function Region() {
    this.left = 999999;
    this.top = 999999;
    this.right = 0;
    this.bottom = 0;
}
Region.prototype.reset = function() {
    this.left = 999999;
    this.top = 999999;
    this.right = 0;
    this.bottom = 0;
};
Region.prototype.inflate = function(x, y) {
    this.left = Math.min(this.left, x);
    this.top = Math.min(this.top, y);
    this.right = Math.max(this.right, x);
    this.bottom = Math.max(this.bottom, y);
};
Region.prototype.toRectangle = function(padding) {
    padding | =0;
    return {
        x: this.left - padding,
        y: this.top - padding,
        width: this.right - this.left + (padding * 2),
        height: this.bottom - this.top + (padding * 2)
        };
};
TT.paperstack = {};
TT.paperstack.container = null;
TT.paperstack.initialize = function() {
    TT.paperstack.container = $("#paperstack");
};
TT.paperstack.updateStack = function(overrideProgress) {
    var availablePapers = $("div.paper", TT.paperstack.container).length;
    var visiblePapers = Math.round(((1 - (overrideProgress ? overrideProgress: TT.chapternav.getProgress())) * availablePapers));
    if (visiblePapers != 0) {
        $(".paper:lt(" + visiblePapers + ")", TT.paperstack.container).css({
            opacity: 1
        });
        $(".paper:gt(" + visiblePapers + ")", TT.paperstack.container).css({
            opacity: 0
        });
        $(".shadow", TT.paperstack.container).css({
            opacity: 1
        });
    } else {
        $(".paper", TT.paperstack.container).css({
            opacity: 0
        });
        $(".shadow", TT.paperstack.container).css({
            opacity: 0
        });
    }
    $(".shadow", TT.paperstack.container).css({
        marginLeft: -9 + visiblePapers
    });
};
TT.illustrations = {};
TT.illustrations.FRAME_RATE = 30;
TT.illustrations.ASSETS_URL = "/media/illustrations/";
TT.illustrations.interval = -1;
TT.illustrations.initialize = function() {};
TT.illustrations.update = function(currentPage) {
    TT.illustrations.deactivate();
    if (currentPage && !TT.navigation.isHomePage()) {
        TT.log("Activate animation: " + currentPage.attr("class"));
        if (currentPage.hasClass("title-html") && currentPage.hasClass("page-3")) {
            TT.illustrations.html.activate($("div.image1", currentPage));
        } else {
            if (currentPage.hasClass("title-foreword") && currentPage.hasClass("page-1")) {
                TT.illustrations.cloud.activate($("div.image1", currentPage));
            } else {
                if (currentPage.hasClass("title-open-source") && currentPage.hasClass("page-1")) {
                    TT.illustrations.opensource.activate($("div.image1", currentPage));
                } else {
                    if (currentPage.hasClass("title-what-is-the-internet") && currentPage.hasClass("page-1")) {
                        TT.illustrations.internet.activate($("div.image1", currentPage));
                    } else {
                        if (currentPage.hasClass("title-page-load") && currentPage.hasClass("page-1")) {
                            TT.illustrations.pageload.activate($("div.image1", currentPage));
                        } else {
                            if (currentPage.hasClass("title-web-apps") && currentPage.hasClass("page-1")) {
                                TT.illustrations.webapps.activate($("div.image1", currentPage));
                            } else {
                                if (currentPage.hasClass("title-threed") && currentPage.hasClass("page-1")) {
                                    TT.illustrations.threed.activate($("div.image1", currentPage));
                                }
                            }
                        }
                    }
                }
            }
        }
    }
};
TT.illustrations.deactivate = function() {
    clearInterval(TT.illustrations.interval);
};
TT.illustrations.createCanvas = function(parent, world) {
    var canvas = $("<canvas></canvas>");
    canvas[0].width = world.width;
    canvas[0].height = world.height;
    parent.append(canvas);
    TT.illustrations.updateCanvasPosition(parent, world);
    return canvas;
};
TT.illustrations.updateCanvasPosition = function(parent, world) {
    var canvas = $("canvas", parent);
    canvas.css({
        position: "absolute",
        left: $("img", parent).position().left + world.x,
        top: $("img", parent).position().top + world.y
    });
    return $("img", parent).width() !== 0 || $("img", parent).height() !== 0;
};
TT.illustrations.threed = {
    canvas: null,
    context: null,
    container: null,
    image: null,
    cloudImage: null,
    clouds: [],
    alpha: 0,
    world: {
        x: 0,
        y: 0,
        width: 320,
        height: 150
    },
    positioned: false,
    initialize: function(container) {
        if (this.canvas === null) {
            this.container = container;
            this.image = $("img", container);
            this.canvas = TT.illustrations.createCanvas(container, this.world);
            this.context = this.canvas[0].getContext("2d");
            this.cloudImage = new Image();
            this.cloudImage.src = TT.illustrations.ASSETS_URL + "3d01_clouds.png";
            this.clouds.push({
                source: {
                    x: 0,
                    y: 0,
                    width: 63,
                    height: 35
                },
                x: 44,
                y: 16,
                originalX: 44,
                originalY: 16,
                velocity: {
                    x: 0,
                    y: 0
                }
            });
            this.clouds.push({
                source: {
                    x: 0,
                    y: 36,
                    width: 70,
                    height: 40
                },
                x: 147,
                y: 10,
                originalX: 147,
                originalY: 10,
                velocity: {
                    x: 0,
                    y: 0
                }
            });
            this.clouds.push({
                source: {
                    x: 0,
                    y: 78,
                    width: 84,
                    height: 50
                },
                x: 213,
                y: 48,
                originalX: 212,
                originalY: 48,
                velocity: {
                    x: 0,
                    y: 0
                }
            });
        } else {
            this.positioned = TT.illustrations.updateCanvasPosition(this.container, this.world);
        }
    },
    activate: function(container) {
        this.initialize(container);
        TT.illustrations.interval = setInterval(this.render, 1000 / TT.illustrations.FRAME_RATE);
    },
    render: function() {
        TT.illustrations.threed.draw();
    },
    draw: function() {
        if (!this.positioned) {
            this.positioned = TT.illustrations.updateCanvasPosition(this.container, this.world);
        }
        this.context.clearRect(0, 0, this.world.width, this.world.height);
        if (this.cloudImage.complete) {
            this.alpha = Math.min(this.alpha + 0.1, 1);
            this.context.globalAlpha = this.alpha;
            for (var i = 0; i < this.clouds.length; i++) {
                var cloud = this.clouds[i];
                cloud.x += cloud.velocity.x;
                cloud.y += cloud.velocity.y;
                cloud.velocity.x *= 0.96;
                cloud.velocity.y *= 0.96;
                var speed = 0.3;
                if (Math.abs(cloud.velocity.x) < 0.01) {
                    if (cloud.x > cloud.originalX) {
                        cloud.velocity.x -= 0.05 + Math.random() * speed;
                    } else {
                        cloud.velocity.x += 0.05 + Math.random() * speed;
                    }
                }
                if (Math.abs(cloud.velocity.y) < 0.01) {
                    if (cloud.y > cloud.originalY) {
                        cloud.velocity.y -= 0.01 + Math.random() * speed;
                    } else {
                        cloud.velocity.y += 0.01 + Math.random() * speed;
                    }
                }
                this.context.save();
                this.context.translate(cloud.x, cloud.y);
                this.context.drawImage(this.cloudImage, cloud.source.x, cloud.source.y, cloud.source.width, cloud.source.height, 0, 0, cloud.source.width, cloud.source.height);
                this.context.restore();
            }
        }
    }
};
TT.illustrations.webapps = {
    GRAVITY: 0.04,
    canvas: null,
    context: null,
    container: null,
    image: null,
    leaves: [],
    points: [{
        x: 86,
        y: 100
    }, {
        x: 35,
        y: 88
    }, {
        x: 168,
        y: 35
    }, {
        x: 250,
        y: 15
    }],
    world: {
        x: 20,
        y: 30,
        width: 300,
        height: 260
    },
    positioned: false,
    initialize: function(container) {
        if (this.canvas === null) {
            this.container = container;
            this.image = $("img", container);
            this.canvas = TT.illustrations.createCanvas(container, this.world);
            this.context = this.canvas[0].getContext("2d");
        } else {
            this.positioned = TT.illustrations.updateCanvasPosition(this.container, this.world);
        }
    },
    activate: function(container) {
        this.initialize(container);
        TT.illustrations.interval = setInterval(this.render, 1000 / TT.illustrations.FRAME_RATE);
    },
    render: function() {
        TT.illustrations.webapps.draw();
    },
    draw: function() {
        if (!this.positioned) {
            this.positioned = TT.illustrations.updateCanvasPosition(this.container, this.world);
        }
        this.context.clearRect(0, 0, this.world.width, this.world.height);
        if (this.leaves.length < 4 && Math.random() > 0.9) {
            var point = this.points[Math.floor(Math.random() * this.points.length)];
            this.leaves.push({
                x: point.x,
                y: point.y,
                w: 18 + (Math.random() * 8),
                h: 6 + (Math.random() * 4),
                alpha: 0,
                rotation: Math.random() * Math.PI,
                velocity: {
                    x: -0.2 + (Math.random() * 0.4),
                    y: Math.random() * 2,
                    rotation: -0.1 + (Math.random() * 0.2)
                    }
            });
        }
        for (var i = 0; i < this.leaves.length; i++) {
            var leaf = this.leaves[i];
            leaf.velocity.y += this.GRAVITY;
            if (leaf.y > this.world.height + 20) {
                this.leaves.splice(i, 1);
                i--;
                continue;
            }
            leaf.x += leaf.velocity.x;
            leaf.y += leaf.velocity.y;
            leaf.rotation += leaf.velocity.rotation;
            leaf.alpha = Math.min(leaf.alpha + 0.1, 1);
            this.context.save();
            var b = 3;
            this.context.globalAlpha = leaf.alpha;
            this.context.beginPath();
            this.context.translate(leaf.x, leaf.y);
            this.context.rotate(leaf.rotation);
            this.context.strokeStyle = "rgba(0,100,20,0.7)";
            this.context.fillStyle = "rgba(159,192,94,0.9)";
            this.context.moveTo(0, leaf.h / 2);
            this.context.quadraticCurveTo(leaf.w / 2, -b, leaf.w, leaf.h / 2);
            this.context.quadraticCurveTo(leaf.w / 2, leaf.h + b, 0, leaf.h / 2);
            this.context.stroke();
            this.context.fill();
            this.context.restore();
        }
        var mask = this.context.createLinearGradient(0, 0, 0, this.world.height);
        mask.addColorStop(0.7, "rgba(255, 255, 255, 0)");
        mask.addColorStop(1, "rgba(255, 255, 255, 1)");
        this.context.save();
        this.context.globalCompositeOperation = "destination-out";
        this.context.beginPath();
        this.context.fillStyle = mask;
        this.context.fillRect(0, 0, this.world.width, this.world.height);
        this.context.restore();
    }
};
TT.illustrations.pageload = {
    GRAVITY: 0.04,
    canvas: null,
    context: null,
    container: null,
    image: null,
    bubbles: [],
    world: {
        x: 10,
        y: 100,
        width: 220,
        height: 100
    },
    positioned: false,
    initialize: function(container) {
        if (this.canvas === null) {
            this.container = container;
            this.image = $("img", container);
            this.canvas = TT.illustrations.createCanvas(container, this.world);
            this.context = this.canvas[0].getContext("2d");
        } else {
            this.positioned = TT.illustrations.updateCanvasPosition(this.container, this.world);
        }
    },
    activate: function(container) {
        this.initialize(container);
        TT.illustrations.interval = setInterval(this.render, 1000 / TT.illustrations.FRAME_RATE);
    },
    render: function() {
        TT.illustrations.pageload.draw();
    },
    draw: function() {
        if (!this.positioned) {
            this.positioned = TT.illustrations.updateCanvasPosition(this.container, this.world);
        }
        this.context.clearRect(0, 0, this.world.width, this.world.height);
        if (this.bubbles.length < 7 && Math.random() > 0.85) {
            this.bubbles.push({
                x: Math.random() * this.world.width,
                y: this.world.height + 10,
                alpha: 0,
                size: 1 + Math.random() * 3,
                velocity: {
                    x: -0.4 + (Math.random() * 0.8),
                    y: Math.random() * -2
                }
            });
        }
        for (var i = 0; i < this.bubbles.length; i++) {
            var bubble = this.bubbles[i];
            bubble.velocity.y -= this.GRAVITY;
            if (bubble.y < -10) {
                this.bubbles.splice(i, 1);
                i--;
                continue;
            } else {
                if (bubble.y < this.world.height * 0.3) {
                    bubble.alpha = Math.max(bubble.y / (this.world.height * 0.3), 0);
                } else {
                    if (bubble.y > this.world.height * 0.7) {
                        bubble.alpha = 1 - Math.min((bubble.y - this.world.height * 0.7) / (this.world.height * 0.3), 1);
                    }
                }
            }
            bubble.x += bubble.velocity.x;
            bubble.y += bubble.velocity.y;
            this.context.beginPath();
            this.context.strokeStyle = "rgba( 0, 0, 0, " + (bubble.alpha * 0.3) + " )";
            this.context.fillStyle = "rgba( 0, 180, 250, " + (bubble.alpha * 0.7) + " )";
            this.context.arc(bubble.x, bubble.y, bubble.size, 0, Math.PI * 2, true);
            this.context.stroke();
        }
        var mask = this.context.createLinearGradient(0, 0, -25, this.world.height);
        mask.addColorStop(0, "rgba(255, 255, 255, 1)");
        mask.addColorStop(0.6, "rgba(255, 255, 255, 0)");
        this.context.save();
        this.context.globalCompositeOperation = "destination-out";
        this.context.beginPath();
        this.context.fillStyle = mask;
        this.context.fillRect(0, 0, this.world.width, this.world.height);
        this.context.restore();
    }
};
TT.illustrations.internet = {
    GRAVITY: 0.04,
    canvas: null,
    context: null,
    container: null,
    image: null,
    zero: null,
    one: null,
    numbers: [],
    positioned: false,
    world: {
        x: 345,
        y: 115,
        width: 120,
        height: 80
    },
    initialize: function(container) {
        if (this.canvas === null) {
            this.container = container;
            this.image = $("img", container);
            this.canvas = TT.illustrations.createCanvas(container, this.world);
            this.context = this.canvas[0].getContext("2d");
            this.zero = new Image();
            this.zero.src = TT.illustrations.ASSETS_URL + "internet01-part1.png";
            this.one = new Image();
            this.one.src = TT.illustrations.ASSETS_URL + "internet01-part2.png";
        } else {
            this.positioned = TT.illustrations.updateCanvasPosition(this.container, this.world);
        }
    },
    activate: function(container) {
        this.initialize(container);
        TT.illustrations.interval = setInterval(this.render, 1000 / TT.illustrations.FRAME_RATE);
    },
    render: function() {
        TT.illustrations.internet.draw();
    },
    draw: function() {
        if (!this.positioned) {
            this.positioned = TT.illustrations.updateCanvasPosition(this.container, this.world);
        }
        this.context.clearRect(0, 0, this.world.width, this.world.height);
        if (this.zero.complete && this.one.complete) {
            if (this.numbers.length < 20 && Math.random() > 0.6) {
                this.numbers.push({
                    type: Math.random() > 0.5 ? 1: 0,
                    x: 5,
                    y: 0,
                    alpha: 0,
                    rotation: Math.random() * Math.PI,
                    velocity: {
                        x: 0.4 + (Math.random() * 1.6),
                        y: Math.random() * 2,
                        rotation: -0.1 + (Math.random() * 0.2)
                        }
                });
            }
            for (var i = 0; i < this.numbers.length; i++) {
                var number = this.numbers[i];
                var image = number.type == 0 ? this.zero: this.one;
                number.velocity.y += this.GRAVITY;
                if (number.y > this.world.height + image.height) {
                    this.numbers.splice(i, 1);
                    i--;
                    continue;
                } else {
                    if (number.y < this.world.height * 0.1) {
                        number.alpha = Math.min(number.y / (this.world.height * 0.1), 1);
                    } else {
                        if (number.y > this.world.height * 0.6) {
                            number.alpha = 1 - Math.min((number.y - this.world.height * 0.5) / (this.world.height * 0.3), 1);
                        }
                    }
                }
                number.x += number.velocity.x;
                number.y += number.velocity.y;
                number.rotation += number.velocity.rotation;
                this.context.save();
                this.context.globalAlpha = number.alpha;
                this.context.translate(number.x + Math.round(image.width * 0.5), number.y + Math.round(image.height * 0.5));
                this.context.rotate(number.rotation);
                this.context.translate( - Math.round(image.width * 0.5), -Math.round(image.height * 0.5));
                this.context.drawImage(image, 0, 0);
                this.context.restore();
            }
        }
    }
};
TT.illustrations.opensource = {
    canvas: null,
    context: null,
    container: null,
    image: null,
    cog1: {
        image: null,
        x: 57,
        y: 14,
        currentRotation: 0,
        targetRotation: 0,
        lastUpdate: 0
    },
    cog2: {
        image: null,
        x: 28,
        y: 38,
        currentRotation: 0,
        targetRotation: 0,
        lastUpdate: 0
    },
    alpha: 0,
    world: {
        x: 90,
        y: 37,
        width: 100,
        height: 100
    },
    positioned: false,
    initialize: function(container) {
        if (this.canvas === null) {
            this.container = container;
            this.image = $("img", container);
            this.canvas = TT.illustrations.createCanvas(container, this.world);
            this.context = this.canvas[0].getContext("2d");
            this.cog1.image = new Image();
            this.cog1.image.src = TT.illustrations.ASSETS_URL + "opensource01-part1.png";
            this.cog2.image = new Image();
            this.cog2.image.src = TT.illustrations.ASSETS_URL + "opensource01-part2.png";
        } else {
            this.positioned = TT.illustrations.updateCanvasPosition(this.container, this.world);
        }
    },
    activate: function(container) {
        this.initialize(container);
        this.cog1.lastUpdate = TT.time();
        this.cog2.lastUpdate = TT.time();
        TT.illustrations.interval = setInterval(this.render, 1000 / TT.illustrations.FRAME_RATE);
    },
    render: function() {
        TT.illustrations.opensource.draw();
    },
    draw: function() {
        this.context.clearRect(0, 0, this.world.width, this.world.height);
        if (!this.positioned) {
            this.positioned = TT.illustrations.updateCanvasPosition(this.container, this.world);
        }
        if (this.cog1.image.complete && this.cog2.image.complete) {
            this.alpha = Math.min(this.alpha + 0.08, 1);
            if (this.cog1.currentRotation > this.cog1.targetRotation - 1 && TT.time() - this.cog1.lastUpdate > 2000) {
                this.cog1.targetRotation += Math.PI / 3;
                this.cog1.lastUpdate = TT.time();
            }
            if (this.cog2.currentRotation > this.cog2.targetRotation - 1 && TT.time() - this.cog2.lastUpdate > 6000) {
                this.cog2.targetRotation += Math.PI / 9;
                this.cog2.lastUpdate = TT.time();
            }
            this.cog1.currentRotation += (this.cog1.targetRotation - this.cog1.currentRotation) * 0.5;
            this.cog2.currentRotation += (this.cog2.targetRotation - this.cog2.currentRotation) * 0.4;
            if (Math.abs(this.cog1.currentRotation - this.cog1.targetRotation) < 0.05) {
                this.cog1.currentRotation = this.cog1.targetRotation;
            }
            if (Math.abs(this.cog2.currentRotation - this.cog2.targetRotation) < 0.05) {
                this.cog2.currentRotation = this.cog2.targetRotation;
            }
            this.context.save();
            this.context.globalAlpha = this.alpha;
            this.context.save();
            this.context.translate(this.cog1.x + Math.round(this.cog1.image.width * 0.5), this.cog1.y + Math.round(this.cog1.image.height * 0.5));
            this.context.rotate(this.cog1.currentRotation);
            this.context.translate( - Math.round(this.cog1.image.width * 0.5), -Math.round(this.cog1.image.height * 0.5));
            this.context.drawImage(this.cog1.image, 0, 0);
            this.context.restore();
            this.context.save();
            this.context.translate(this.cog2.x + Math.round(this.cog2.image.width * 0.5), this.cog2.y + Math.round(this.cog2.image.height * 0.5));
            this.context.rotate(this.cog2.currentRotation);
            this.context.translate( - Math.round(this.cog2.image.width * 0.5), -Math.round(this.cog2.image.height * 0.5));
            this.context.drawImage(this.cog2.image, 0, 0);
            this.context.restore();
            this.context.restore();
        }
    }
};
TT.illustrations.cloud = {
    canvas: null,
    context: null,
    container: null,
    image: null,
    cloutImage: null,
    world: {
        x: 0,
        y: 0,
        width: 240,
        height: 200
    },
    positioned: false,
    curtain: 1,
    clouds: [{
        x: Math.random() * 240,
        y: Math.random() * 200,
        strength: 1,
        velocity: {
            x: 0,
            y: 0
        },
        size: 8 + Math.random() * 8
    }, {
        x: Math.random() * 240,
        y: Math.random() * 200,
        strength: 1,
        velocity: {
            x: 0,
            y: 0
        },
        size: 8 + Math.random() * 8
    }, {
        x: Math.random() * 240,
        y: Math.random() * 200,
        strength: 1,
        velocity: {
            x: 0,
            y: 0
        },
        size: 8 + Math.random() * 8
    }, {
        x: Math.random() * 240,
        y: Math.random() * 200,
        strength: 1,
        velocity: {
            x: 0,
            y: 0
        },
        size: 8 + Math.random() * 8
    }, {
        x: Math.random() * 240,
        y: Math.random() * 200,
        strength: 1,
        velocity: {
            x: 0,
            y: 0
        },
        size: 8 + Math.random() * 8
    }, {
        x: Math.random() * 240,
        y: Math.random() * 200,
        strength: 1,
        velocity: {
            x: 0,
            y: 0
        },
        size: 8 + Math.random() * 8
    }, {
        x: Math.random() * 240,
        y: Math.random() * 200,
        strength: 1,
        velocity: {
            x: 0,
            y: 0
        },
        size: 8 + Math.random() * 8
    }, {
        x: Math.random() * 240,
        y: Math.random() * 200,
        strength: 1,
        velocity: {
            x: 0,
            y: 0
        },
        size: 8 + Math.random() * 8
    }, {
        x: Math.random() * 240,
        y: Math.random() * 200,
        strength: 1,
        velocity: {
            x: 0,
            y: 0
        },
        size: 8 + Math.random() * 8
    }, {
        x: Math.random() * 240,
        y: Math.random() * 200,
        strength: 1,
        velocity: {
            x: 0,
            y: 0
        },
        size: 8 + Math.random() * 8
    }, {
        x: Math.random() * 240,
        y: Math.random() * 200,
        strength: 1,
        velocity: {
            x: 0,
            y: 0
        },
        size: 8 + Math.random() * 8
    }, {
        x: Math.random() * 240,
        y: Math.random() * 200,
        strength: 1,
        velocity: {
            x: 0,
            y: 0
        },
        size: 8 + Math.random() * 8
    }, {
        x: Math.random() * 240,
        y: Math.random() * 200,
        strength: 1,
        velocity: {
            x: 0,
            y: 0
        },
        size: 8 + Math.random() * 8
    }, {
        x: Math.random() * 240,
        y: Math.random() * 200,
        strength: 1,
        velocity: {
            x: 0,
            y: 0
        },
        size: 8 + Math.random() * 8
    }, {
        x: Math.random() * 240,
        y: Math.random() * 200,
        strength: 1,
        velocity: {
            x: 0,
            y: 0
        },
        size: 8 + Math.random() * 8
    }, {
        x: Math.random() * 240,
        y: Math.random() * 200,
        strength: 1,
        velocity: {
            x: 0,
            y: 0
        },
        size: 8 + Math.random() * 8
    }, {
        x: Math.random() * 240,
        y: Math.random() * 200,
        strength: 1,
        velocity: {
            x: 0,
            y: 0
        },
        size: 8 + Math.random() * 8
    }, {
        x: Math.random() * 240,
        y: Math.random() * 200,
        strength: 1,
        velocity: {
            x: 0,
            y: 0
        },
        size: 8 + Math.random() * 8
    }, {
        x: Math.random() * 240,
        y: Math.random() * 200,
        strength: 1,
        velocity: {
            x: 0,
            y: 0
        },
        size: 8 + Math.random() * 8
    }, {
        x: Math.random() * 240,
        y: Math.random() * 200,
        strength: 1,
        velocity: {
            x: 0,
            y: 0
        },
        size: 8 + Math.random() * 8
    }, {
        x: Math.random() * 240,
        y: Math.random() * 200,
        strength: 1,
        velocity: {
            x: 0,
            y: 0
        },
        size: 8 + Math.random() * 8
    }, {
        x: Math.random() * 240,
        y: Math.random() * 200,
        strength: 1,
        velocity: {
            x: 0,
            y: 0
        },
        size: 8 + Math.random() * 8
    }],
    initialize: function(container) {
        if (this.canvas === null) {
            this.container = container;
            this.image = $("img", container);
            this.canvas = TT.illustrations.createCanvas(container, this.world);
            this.context = this.canvas[0].getContext("2d");
            this.cloudImage = new Image();
            this.cloudImage.src = TT.illustrations.ASSETS_URL + "3d01_clouds.png";
        } else {
            this.positioned = TT.illustrations.updateCanvasPosition(this.container, this.world);
        }
    },
    activate: function(container) {
        this.initialize(container);
        TT.illustrations.interval = setInterval(this.render, 1000 / TT.illustrations.FRAME_RATE);
    },
    render: function() {
        TT.illustrations.cloud.draw();
    },
    draw: function() {
        if (!this.positioned) {
            this.positioned = TT.illustrations.updateCanvasPosition(this.container, this.world);
        }
        this.context.clearRect(0, 0, this.world.width, this.world.height);
        if (this.image[0].complete) {
            this.context.drawImage(this.image[0], 0, 0);
        }
        var speed = 3;
        for (var i = 0, len = this.clouds.length; i < len; i++) {
            var cloud = this.clouds[i];
            cloud.velocity.x /= 1.04;
            cloud.velocity.y /= 1.04;
            if (Math.abs(cloud.velocity.x) < 0.2) {
                if (cloud.x > this.world.width / 2) {
                    cloud.velocity.x -= 0.7 + Math.random() * speed;
                } else {
                    cloud.velocity.x += 0.7 + Math.random() * speed;
                }
            }
            if (Math.abs(cloud.velocity.y) < 0.2) {
                if (cloud.y > this.world.height / 2) {
                    cloud.velocity.y -= 0.5 + Math.random() * speed;
                } else {
                    cloud.velocity.y += 0.5 + Math.random() * speed;
                }
            }
            cloud.x += cloud.velocity.x;
            cloud.y += cloud.velocity.y;
            cloud.strength = Math.max(Math.min(cloud.strength, 1), 0);
            var gradient = this.context.createRadialGradient(cloud.x, cloud.y, 0, cloud.x, cloud.y, cloud.size);
            this.context.save();
            var browser = BrowserDetect.browser.toLowerCase();
            if (browser == "firefox") {
                gradient.addColorStop(0.4, "rgba(255,255,255," + (cloud.strength * 0.7) + ")");
                gradient.addColorStop(1, "rgba(255,255,255,0)");
            } else {
                gradient.addColorStop(0.4, "rgba(90,170,190," + (cloud.strength) + ")");
                gradient.addColorStop(1, "rgba(90,170,190,0)");
                this.context.globalCompositeOperation = "source-in";
            }
            this.context.beginPath();
            this.context.fillStyle = gradient;
            this.context.arc(cloud.x, cloud.y, cloud.size, 0, Math.PI * 2, true);
            this.context.fill();
            this.context.restore();
        }
    }
};
TT.illustrations.html = {
    canvas: null,
    context: null,
    container: null,
    image: null,
    world: {
        x: 100,
        y: -15,
        width: 150,
        height: 200
    },
    bulbs: [{
        x: 27,
        y: 22,
        strength: 0,
        momentum: 0,
        size: 10
    }, {
        x: 62,
        y: 30,
        strength: 0,
        momentum: 0,
        size: 10
    }, {
        x: 90,
        y: 39,
        strength: 0,
        momentum: 0,
        size: 10
    }, {
        x: 117,
        y: 48,
        strength: 0,
        momentum: 0,
        size: 10
    }, {
        x: 22,
        y: 59,
        strength: 0,
        momentum: 0,
        size: 10
    }, {
        x: 23,
        y: 89,
        strength: 0,
        momentum: 0,
        size: 10
    }, {
        x: 24,
        y: 115,
        strength: 0,
        momentum: 0,
        size: 10
    }, {
        x: 25,
        y: 139,
        strength: 0,
        momentum: 0,
        size: 10
    }, {
        x: 124,
        y: 87,
        strength: 0,
        momentum: 0,
        size: 10
    }, {
        x: 124,
        y: 116,
        strength: 0,
        momentum: 0,
        size: 10
    }, {
        x: 124,
        y: 144,
        strength: 0,
        momentum: 0,
        size: 10
    }, {
        x: 124,
        y: 178,
        strength: 0,
        momentum: 0,
        size: 10
    }],
    initialize: function(container) {
        if (this.canvas === null) {
            this.container = container;
            this.image = $("img", container);
            this.canvas = TT.illustrations.createCanvas(container, this.world);
            this.context = this.canvas[0].getContext("2d");
        } else {
            TT.illustrations.updateCanvasPosition(this.container, this.world);
        }
    },
    activate: function(container) {
        this.initialize(container);
        TT.illustrations.interval = setInterval(this.render, 1000 / TT.illustrations.FRAME_RATE);
    },
    render: function() {
        TT.illustrations.html.draw();
    },
    draw: function() {
        this.context.clearRect(0, 0, this.world.width, this.world.height);
        for (var i = 0, len = this.bulbs.length; i < len; i++) {
            var bulb = this.bulbs[i];
            if (bulb.strength < 0.1 && bulb.momentum <= 0 && Math.random() > 0.98) {
                bulb.momentum = Math.random() * 0.3;
            } else {
                if (bulb.strength >= 1 && Math.random() > 0.98) {
                    bulb.momentum = -Math.random() * 0.3;
                }
            }
            bulb.strength += bulb.momentum;
            bulb.strength = Math.max(Math.min(bulb.strength, 1), 0);
            var gradient = this.context.createRadialGradient(bulb.x, bulb.y, 0, bulb.x, bulb.y, bulb.size);
            gradient.addColorStop(0.4, "rgba(255,255,100," + (bulb.strength * 0.95) + ")");
            gradient.addColorStop(1, "rgba(255,255,100,0)");
            this.context.beginPath();
            this.context.fillStyle = gradient;
            this.context.arc(bulb.x, bulb.y, bulb.size, 0, Math.PI * 2, true);
            this.context.fill();
        }
    }
};
TT.navigation = {};
TT.navigation.transitioningFromHardCover = false;
TT.navigation.hasNavigated = false;
TT.navigation.currentPageName = "";
TT.navigation.enqueueNavigation = null;
TT.navigation.initialize = function() {
    TT.navigation.assignNavigationHandlers();
    TT.navigation.assignNextPrevHandlers();
    if (!TT.navigation.isHomePage()) {
        $("#front-cover").hide();
        $("#front-cover-bookmark").hide();
        $("#front-cover-arrow").hide();
    } else {
        TT.flipintro.activate();
    }
    if (!TT.navigation.isCreditsPage()) {
        $("#back-cover").hide();
    }
    if (TT.navigation.isTableOfThings()) {
        $("body").addClass("home").addClass("tot");
    } else {
        if (!TT.navigation.isHomePage() && !TT.navigation.isCreditsPage()) {
            $("body").addClass("book");
        }
    }
    TT.sharing.updateSharer();
    TT.navigation.loadImages();
    TT.illustrations.update($("#pages section.current"));
    TT.navigation.updatePageVisibility($("#pages section.current"));
};
TT.navigation.assignNavigationHandlers = function() {
    $("header a.logo").click(function() {
        TT.navigation.goToHome();
        return false;
    });
    $("header li.about a").click(function() {
        TT.navigation.goToPage(TT.history.FOREWORD, 1);
        return false;
    });
    $("header li.credits a").click(function() {
        TT.navigation.goToCredits();
        return false;
    });
    $("#front-cover-bookmark a.open-book").click(function() {
        TT.navigation.goToNextPage();
        return false;
    });
    $("header li.table-of-things a").click(function() {
        TT.navigation.showTableOfContents();
        return false;
    });
    $("#table-of-contents a.go-back").click(function() {
        TT.navigation.hideTableOfContents();
        return false;
    });
    $("#front-cover-arrow").click(function() {
        TT.navigation.goToNextPage();
        return false;
    });
    $("footer .print a").click(function() {
        TT.overlay.showPrint();
        return false;
    });
    $("#pages section a").click(function() {
        var link = $(this).attr("href");
        if (link.indexOf("http://") == -1 && link.indexOf("www.") == -1) {
            var article = link.split("/")[1];
            var page = link.split("/")[2];
            if (article && page) {
                TT.navigation.goToPage(article, page);
            }
            return false;
        }
    });
};
TT.navigation.assignNextPrevHandlers = function() {
    $("#pagination-prev").click(function(e) {
        e.preventDefault();
        TT.navigation.goToPreviousPage();
    });
    $("#pagination-next").click(function(e) {
        e.preventDefault();
        TT.navigation.goToNextPage();
    });
    var element = '<div class="page-progress">						<p class="thing"></p>						<p class="number">Page <span></span></p>					</div>';
    $("#pagination-prev").append(element);
    $("#pagination-next").append(element);
};
TT.navigation.isHomePage = function() {
    return $("body").hasClass("home");
};
TT.navigation.isCreditsPage = function() {
    return $("body").hasClass("credits");
};
TT.navigation.isTableOfThings = function() {
    return $("body").hasClass("tot");
};
TT.navigation.isBookOpen = function() {
    return $("body").hasClass("book");
};
TT.navigation.isForeword = function(target) {
    if (!target) {
        target = $("#pages section.current");
    }
    return TT.navigation.classToArticle(target.attr("class")) == TT.history.FOREWORD;
};
TT.navigation.isLastPage = function(target) {
    if (target) {
        return target.next("section").length == 0 && !TT.navigation.isCreditsPage();
    }
    return $("#pages section.current").next("section").length == 0 && !TT.navigation.isCreditsPage();
};
TT.navigation.isFirstPage = function(target) {
    if (target) {
        return target.prev("section").length == 0 && !TT.navigation.isHomePage();
    }
    return $("#pages section.current").prev("section").length == 0 && !TT.navigation.isHomePage();
};
TT.navigation.classToArticle = function(theClass) {
    return theClass ? theClass.match(/title-([a-zA-Z-0-9]+)/)[1] : null;
};
TT.navigation.classToArticlePage = function(theClass) {
    return theClass ? parseInt(theClass.match(/page-([0-9]+)/)[1]) : null;
};
TT.navigation.classToGlobalPage = function(theClass) {
    return theClass ? parseInt(theClass.match(/globalPage-([0-9]+)/)[1]) : null;
};
TT.navigation.updateNextPrevLinks = function(targetPage) {
    if (TT.navigation.isCreditsPage()) {
        $("#pagination-next").addClass("inactive");
        $("#pagination-prev").removeClass("inactive");
    } else {
        if (TT.navigation.isHomePage()) {
            $("#pagination-prev").addClass("inactive");
            $("#pagination-next").removeClass("inactive");
        } else {
            $("#pagination-prev, #pagination-next").removeClass("inactive");
        }
    }
    var nextPage = TT.navigation.isHomePage() ? targetPage.attr("class") : targetPage.next("section").attr("class");
    var prevPage = targetPage.prev("section").attr("class");
    if (nextPage) {
        TT.navigation.updatePaginationHint(nextPage, $("#pagination-next"));
    } else {
        $("#pagination-next .page-progress").hide();
    }
    if (prevPage && !TT.navigation.isLastPage() && !TT.navigation.isCreditsPage()) {
        TT.navigation.updatePaginationHint(prevPage, $("#pagination-prev"));
    } else {
        $("#pagination-prev .page-progress").hide();
    }
};
TT.navigation.updatePaginationHint = function(page, button) {
    var articleId = TT.navigation.classToArticle(page);
    var articleIndex = $("#chapter-nav ul li").find("[data-article=" + articleId + "]").parent().index() + 1;
    var pageNumber = TT.navigation.classToArticlePage(page);
    var numberOfPages = $("#pages section.title-" + articleId).length;
    if (pageNumber != undefined && numberOfPages != undefined) {
        $(".page-progress", button).show();
        if (articleId == TT.history.FOREWORD) {
            $("p.thing", button).html("FOREWORD");
        } else {
            $("p.thing", button).html("THING #" + articleIndex);
        }
        $("p.number span", button).text(pageNumber + "/" + numberOfPages);
    } else {
        $(".page-progress", button).hide();
    }
};
TT.navigation.getCurrentArticleId = function() {
    return TT.navigation.classToArticle($("#pages section.current").attr("class"));
};
TT.navigation.getCurrentArticlePage = function() {
    return TT.navigation.classToArticlePage($("#pages section.current").attr("class"));
};
TT.navigation.showTableOfContents = function(fromHistoryChange) {
    TT.tableofthings.show();
};
TT.navigation.hideTableOfContents = function() {
    TT.tableofthings.hide();
};
TT.navigation.goToPreviousPage = function() {
    TT.navigation.cleanUpTransitions();
    if (TT.navigation.transitioningFromHardCover) {
        return false;
    }
    if (TT.navigation.isFirstPage()) {
        if (!TT.navigation.isHomePage()) {
            TT.pageflip.completeCurrentTurn();
            TT.navigation.goToHome();
        }
        return false;
    }
    TT.pageflip.completeCurrentTurn();
    var currentPage = $("#pages section.current");
    var prevArticle = TT.navigation.classToArticle(currentPage.prev("section").attr("class"));
    var prevPage = TT.navigation.classToArticlePage(currentPage.prev("section").attr("class"));
    if (TT.navigation.isCreditsPage()) {
        prevArticle = TT.navigation.classToArticle(currentPage.attr("class"));
        prevPage = TT.navigation.classToArticlePage(currentPage.attr("class"));
    }
    TT.navigation.goToPage(prevArticle, prevPage);
};
TT.navigation.goToNextPage = function() {
    TT.navigation.cleanUpTransitions();
    if (TT.navigation.transitioningFromHardCover) {
        return false;
    }
    if (TT.navigation.isLastPage() || TT.navigation.isCreditsPage()) {
        if (!TT.navigation.isCreditsPage() || (TT.navigation.isCreditsPage() && TT.navigation.isBookOpen())) {
            TT.pageflip.completeCurrentTurn();
            TT.navigation.goToCredits();
        }
        return false;
    }
    TT.pageflip.completeCurrentTurn();
    var currentPage = $("#pages section.current");
    if (TT.navigation.isHomePage()) {
        var nextArticle = TT.navigation.classToArticle(currentPage.attr("class"));
        var nextPage = TT.navigation.classToArticlePage(currentPage.attr("class"));
    } else {
        TT.pageflip.completeCurrentTurn();
        var nextArticle = TT.navigation.classToArticle(currentPage.next("section").attr("class"));
        var nextPage = TT.navigation.classToArticlePage(currentPage.next("section").attr("class"));
    }
    TT.navigation.goToPage(nextArticle, nextPage);
};
TT.navigation.goToHome = function(fromHistoryChange) {
    TT.navigation.hideTableOfContents();
    if (!TT.navigation.isHomePage()) {
        if (TT.navigation.isCreditsPage()) {
            TT.navigation.enqueueNavigation = {
                call: function() {
                    delete this.call;
                    setTimeout(TT.navigation.goToHome, 1);
                }
            };
            TT.navigation.goToPage(TT.history.THEEND, 1, false);
            return;
        }
        TT.navigation.currentPageName = TT.history.HOME;
        $("#back-cover").hide();
        $("body").removeClass("book").removeClass(TT.history.CREDITS).addClass(TT.history.HOME);
        TT.flipintro.activate();
        TT.sharing.updateSharer();
        TT.navigation.transitioningFromHardCover = false;
        $("#pages section").removeClass("current");
        $("#pages section").first().addClass("current");
        var currentPage = $("#pages section.current");
        currentPage.width(TT.PAGE_WIDTH);
        if (!fromHistoryChange) {
            TT.history.pushState("/home");
        }
        TT.pageflip.turnToPage(currentPage, currentPage, -1, TT.pageflip.HARD_FLIP);
    }
};
TT.navigation.goToCredits = function(fromHistoryChange) {
    TT.navigation.hideTableOfContents();
    if (!TT.navigation.isCreditsPage() || (TT.navigation.isCreditsPage() && TT.navigation.isBookOpen())) {
        if ((TT.navigation.isBookOpen() || TT.navigation.isHomePage()) && (!TT.navigation.isLastPage() && !TT.navigation.isCreditsPage())) {
            TT.navigation.enqueueNavigation = {
                call: function() {
                    delete this.call;
                    setTimeout(TT.navigation.goToCredits, 1);
                }
            };
            TT.navigation.goToPage(TT.history.THEEND, 1, false);
            TT.paperstack.updateStack(1);
            return;
        }
        TT.navigation.currentPageName = TT.history.CREDITS;
        $("#page-shadow-overlay").hide();
        $("#front-cover").hide();
        $("#front-cover-bookmark").hide();
        $("#front-cover-arrow").hide();
        $("body").removeClass("book").removeClass(TT.history.HOME).addClass(TT.history.CREDITS);
        TT.sharing.updateSharer();
        TT.navigation.transitioningFromHardCover = false;
        $("#pages section").removeClass("current");
        $("#pages section").last().addClass("current");
        var currentPage = $("#pages section.current");
        TT.navigation.updatePageVisibility(currentPage, 1);
        if (!fromHistoryChange) {
            TT.history.pushState("/credits");
        }
        TT.pageflip.turnToPage(currentPage, currentPage, 1, TT.pageflip.HARD_FLIP);
    }
};
TT.navigation.goToPage = function(articleId, pageNumber, fromHistoryChange) {
    TT.navigation.loadImages(articleId, pageNumber);
    if (TT.navigation.isCreditsPage() && articleId !== TT.history.THEEND) {
        TT.navigation.enqueueNavigation = {
            articleId: articleId,
            pageNumber: pageNumber,
            fromHistoryChange: fromHistoryChange,
            call: function() {
                delete this.call;
                TT.navigation.goToPage(this.articleId, this.pageNumber, this.fromHistoryChange);
            }
        };
        articleId = TT.history.THEEND;
        pageNumber = 1;
    }
    var currentPage = $("#pages section.current");
    var targetPage = $("#pages section.title-" + articleId + ".page-" + pageNumber);
    TT.navigation.hasNavigated = true;
    TT.navigation.hideTableOfContents();
    var isSamePageInBook = currentPage.attr("class") === targetPage.attr("class");
    var isSamePageOverall = targetPage.attr("class") === TT.navigation.currentPageName;
    if ((!isSamePageOverall && !isSamePageInBook) || (TT.navigation.isHomePage() || TT.navigation.isCreditsPage())) {
        TT.navigation.currentPageName = targetPage.attr("class");
        if (TT.navigation.classToArticle(TT.navigation.currentPageName) == TT.history.THEEND) {
            TT.sharing.updateSharer(true);
        }
        var type = TT.pageflip.SOFT_FLIP;
        if (TT.navigation.isHomePage() || TT.navigation.isCreditsPage()) {
            type = TT.pageflip.HARD_FLIP;
            TT.navigation.transitioningFromHardCover = true;
        }
        var currentGlobalPageNumber = TT.navigation.classToGlobalPage($(".current").attr("class"));
        var targetGlobalPageNumber = TT.navigation.classToGlobalPage(targetPage.attr("class"));
        if (currentGlobalPageNumber != null && targetGlobalPageNumber != null) {
            var steps = Math.abs(currentGlobalPageNumber - targetGlobalPageNumber);
            var direction = targetGlobalPageNumber > currentGlobalPageNumber ? 1: -1;
            if (targetGlobalPageNumber == currentGlobalPageNumber) {
                direction = TT.navigation.isHomePage() ? 1: -1;
            }
            TT.navigation.updatePageVisibility(targetPage, direction, steps);
            TT.pageflip.turnToPage(currentPage, targetPage, direction, type);
            if (!fromHistoryChange) {
                TT.history.pushState("/" + articleId + "/" + pageNumber);
            }
            TT.storage.setBookmark(articleId, pageNumber);
            TT.navigation.updateNextPrevLinks(targetPage);
            TT.navigation.updatePageReferences(articleId);
            return true;
        }
    }
    return false;
};
TT.navigation.updatePageVisibility = function(targetPage, direction, steps) {
    steps = steps || 0;
    var currentDepth = parseInt($("#pages section.current").css("z-index"));
    if (steps > 1 || TT.navigation.isHomePage()) {
        currentDepth = parseInt(targetPage.css("z-index"));
    }
    $("#pages section:not(.current)").each(function() {
        var z = parseInt($(this).css("z-index"));
        if (z > currentDepth) {
            $(this).width(0).hide().css("top");
        } else {
            if (z < currentDepth - 1) {
                $(this).hide();
            }
        }
    });
    targetPage.show();
    if (steps > 1 && direction == 1 && TT.navigation.isHomePage()) {
        $("#pages section.current").width(0).hide();
        targetPage.width(TT.PAGE_WIDTH).show();
    }
    if (!TT.navigation.isHomePage()) {
        $("#left-page").width(TT.BOOK_WIDTH_CLOSED).show();
    }
};
TT.navigation.updateCurrentPointer = function(currentPage, targetPage) {
    if (TT.navigation.transitioningFromHardCover) {
        $("body").removeClass(TT.history.HOME).removeClass(TT.history.CREDITS).addClass("book");
        $("#page-shadow-overlay").hide();
        TT.navigation.transitioningFromHardCover = false;
    }
    currentPage.removeClass("current");
    targetPage.addClass("current");
    TT.sharing.updateSharer();
    TT.navigation.updatePageReferences();
    TT.navigation.updateNextPrevLinks(targetPage);
    if (TT.navigation.enqueueNavigation && TT.navigation.enqueueNavigation.call) {
        TT.navigation.enqueueNavigation.call();
        TT.navigation.enqueueNavigation = null;
    }
};
TT.navigation.updatePageReferences = function(articleId) {
    TT.chapternav.updateSelection(articleId);
    TT.tableofthings.updateSelection(articleId);
    TT.paperstack.updateStack();
    TT.illustrations.update($("#pages section.current"));
};
TT.navigation.cleanUpTransitions = function(currentPage, targetPage) {
    TT.pageflip.removeInactiveFlips();
    if (TT.pageflip.flips.length == 0) {
        TT.navigation.transitioningFromHardCover = false;
    }
};
TT.navigation.loadImages = function(articleId, pageNumber) {
    var cur = articleId && pageNumber ? $("#pages section.title-" + articleId + ".page-" + pageNumber) : $("#pages section.current");
    var pages = [cur];
    if (cur.prev("section").length) {
        pages.push(cur.prev("section"));
    }
    if (cur.next("section").length) {
        pages.push(cur.next("section"));
    }
    for (var i = 0; i < pages.length; i++) {
        pages[i].find("img").each(function() {
            if ($(this).attr("src") !== $(this).attr("data-src")) {
                $(this).attr("src", $(this).attr("data-src"));
            }
        });
    }
};
TT.cache = {};
TT.cache.initialize = function() {
    $(window.applicationCache).bind("downloading", TT.cache.onDownloadingHandler);
    $(window.applicationCache).bind("progress", TT.cache.onProgressHandler);
    $(window.applicationCache).bind("error", TT.cache.onErrorHandler);
    $(window.applicationCache).bind("cached", TT.cache.onCachedHandler);
    $(window.applicationCache).bind("updateready", TT.cache.onUpdateReadyHandler);
    $(window.applicationCache).bind("noupdate", TT.cache.onNoUpdateHandler);
    $(window.applicationCache).bind("obsolete", TT.cache.onObsoleteHandler);
};
TT.cache.onDownloadingHandler = function(event) {
    TT.log("TT.cache.onDownloadingHandler");
};
TT.cache.onNoUpdateHandler = function(event) {
    TT.log("TT.cache.onNoUpdateHandler");
};
TT.cache.onProgressHandler = function(event) {};
TT.cache.onErrorHandler = function(event) {
    TT.log("TT.cache.onErrorHandler");
};
TT.cache.onObsoleteHandler = function(event) {
    TT.log("TT.cache.onObsoleteHandler");
};
TT.cache.onCachedHandler = function(event) {
    TT.log("TT.cache.onCachedHandler");
};
TT.cache.onUpdateReadyHandler = function(event) {
    TT.log("TT.cache.onCachedHandler");
};
TT.search = {};
TT.search.dropdown = null;
TT.search.dropdownResults = null;
TT.search.dropdownTitles = null;
TT.search.dropdownKeywords = null;
TT.search.field = null;
TT.search.hasFocus = false;
TT.search.titleResults = [];
TT.search.keywordResults = [];
TT.search.hideInterval = -1;
TT.search.THING_RESULTS_LIMIT = 2;
TT.search.KEYWORD_RESULTS_LIMIT = 8;
TT.search.DEFAULT_TEXT = "Search Book";
TT.search.initialize = function() {
    TT.search.field = $("#search-field");
    TT.search.dropdown = $("#search-dropdown");
    TT.search.dropdownResults = $("#search-dropdown div.results");
    TT.search.dropdownTitles = $("#search-dropdown div.results .things");
    TT.search.dropdownKeywords = $("#search-dropdown div.results .keywords");
    TT.search.field.focus(TT.search.onSearchFieldFocus);
    TT.search.field.blur(TT.search.onSearchFieldBlur);
    TT.search.field.change(TT.search.onSearchFieldChange);
    TT.search.field.keyup(TT.search.onSearchFieldChange);
    TT.search.field.click(function(event) {
        if (TT.search.field.val() == "") {
            TT.search.onSearchFieldChange(event);
        }
    });
};
TT.search.onSearchFieldFocus = function(event) {
    clearInterval(TT.search.hideInterval);
    if (event.target.value === TT.search.DEFAULT_TEXT) {
        event.target.value = "";
    }
    TT.search.showResult();
    TT.search.hasFocus = true;
    $("header, #search-dropdown").addClass("searching");
};
TT.search.onSearchFieldBlur = function(event) {
    clearInterval(TT.search.hideInterval);
    if (event.target.value === "") {
        event.target.value = TT.search.DEFAULT_TEXT;
    }
    TT.search.hideInterval = setInterval(TT.search.hideResults, 100);
    TT.search.hasFocus = false;
    $("header, #search-dropdown").removeClass("searching");
};
TT.search.onSearchFieldChange = function(event) {
    clearInterval(TT.search.hideInterval);
    if (TT.search.field.val() == "" || TT.search.field.val().length < 2) {
        TT.search.titleResults = [];
        TT.search.keywordResults = [];
        TT.search.hideResults();
    } else {
        TT.search.searchFor(TT.search.field.val());
    }
};
TT.search.searchFor = function(term) {
    TT.search.titleResults = [];
    TT.search.keywordResults = [];
    TT.search.regexEscape = function(text) {
        return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
    };
    var searchPattern = new RegExp(TT.search.regexEscape(term), "gi");
    $(".page h2, .page h3, .page p").each(function() {
        var elBeingSearched = $(this);
        var elText = $(this).text();
        if (searchPattern.test(elText)) {
            var matchVariations = elText.match(searchPattern);
            var uniqueMatchVariations = {};
            for (var i = 0; i < matchVariations.length; i++) {
                uniqueMatchVariations[matchVariations[i]] = true;
            }
            for (term in uniqueMatchVariations) {
                var elResults = elText.split(term);
                for (i = 1; i < elResults.length; i++) {
                    var result = {};
                    var anteSnippet = elResults[i - 1].substr( - 10).replace(/</, "&lt;");
                    var postSnippet = elResults[i].substr(0, 10).replace(/</, "&lt;");
                    result.articleId = elBeingSearched.parents("section").eq(0).attr("class").match(/title-([a-z-0-9]+)/)[1];
                    term = term.replace(/</, "&lt;");
                    result.snippet = anteSnippet + "<strong>" + term + "</strong>" + postSnippet;
                    var chapterElement = $("#chapter-nav ul li").find("[data-article=" + result.articleId + "]");
                    if (chapterElement.length > 0) {
                        result.articlePage = elBeingSearched.parents("section").eq(0).attr("class").match(/page-([0-9]+)/)[1];
                        result.articleIndex = chapterElement.parent().index() + 1;
                        result.articleTitle = chapterElement.attr("data-title");
                        result.articleGlobalStartPage = $(".pageNumber", elBeingSearched.parents("section")).text();
                        result.articleGlobalEndPage = chapterElement.attr("data-globalendpage");
                        if (result.articleTitle.length > 38) {
                            result.articleTitle = result.articleTitle.slice(0, 36) + "...";
                        }
                        if (elBeingSearched.is("h2") || elBeingSearched.is("h3")) {
                            var isDuplicate = false;
                            for (var j = 0; j < TT.search.titleResults.length; j++) {
                                if (TT.search.titleResults[j].articleTitle == result.articleTitle) {
                                    isDuplicate = true;
                                }
                            }
                            if (!isDuplicate) {
                                TT.search.titleResults.push(result);
                            }
                        } else {
                            TT.search.keywordResults.push(result);
                        }
                    }
                }
            }
        }
    });
    TT.search.showResult();
};
TT.search.showResult = function() {
    TT.search.dropdownTitles.children("ul").remove();
    TT.search.dropdownKeywords.children("ul").remove();
    var hasTitleResults = TT.search.titleResults.length > 0;
    var hasKeywordResults = TT.search.keywordResults.length > 0;
    if (!hasTitleResults) {
        TT.search.dropdownTitles.hide();
    }
    if (!hasKeywordResults) {
        TT.search.dropdownKeywords.hide();
    }
    if (hasKeywordResults || hasTitleResults) {
        TT.search.dropdown.removeClass("no-results").addClass("open");
    } else {
        if (TT.search.field.val() != "") {
            TT.search.dropdown.addClass("no-results").addClass("open");
        }
    }
    if (hasTitleResults) {
        var resultHTML = $("<ul/>");
        for (var i = 0; i < Math.min(TT.search.titleResults.length, TT.search.THING_RESULTS_LIMIT); i++) {
            var result = TT.search.titleResults[i];
            var li = $("<li/>").mousedown(function() {
                TT.navigation.goToPage($(this).attr("class"), 1);
            });
            li.addClass(result.articleId);
            li.append('<div class="illustration"></div>');
            li.append('<p class="title">#' + result.articleIndex + " " + result.articleTitle + "</p>");
            if (Math.abs(parseInt(result.articleGlobalStartPage) - parseInt(result.articleGlobalEndPage)) != 0) {
                li.append('<p class="pages">Pages: ' + result.articleGlobalStartPage + "-" + result.articleGlobalEndPage + "</p>");
            } else {
                li.append('<p class="pages">Page: ' + result.articleGlobalStartPage + "</p>");
            }
            resultHTML.append(li);
        }
        TT.search.dropdownTitles.append(resultHTML);
        TT.search.dropdownTitles.show();
    }
    if (hasKeywordResults) {
        var resultHTML = $("<ul/>");
        for (var i = 0; i < Math.min(TT.search.keywordResults.length, TT.search.KEYWORD_RESULTS_LIMIT); i++) {
            var result = TT.search.keywordResults[i];
            var li = $("<li/>").mousedown(function() {
                TT.navigation.goToPage($(this).attr("data-articleId"), $(this).attr("data-articlePage"));
            });
            li.attr("data-articleId", result.articleId);
            li.attr("data-articlePage", result.articlePage);
            li.append('<p class="snippet">"...' + result.snippet + '..."</p>');
            li.append('<p class="pages">Thing #' + result.articleIndex + " Page: " + result.articleGlobalStartPage + "</p>");
            resultHTML.append(li);
        }
        TT.search.dropdownKeywords.append(resultHTML);
        TT.search.dropdownKeywords.show();
    }
    TT.search.dropdown.children(".fader").height(TT.search.dropdownResults.outerHeight());
};
TT.search.hideResults = function() {
    TT.search.dropdown.removeClass("open");
};
TT.chapternav = {};
TT.chapternav.currentArticle = "";
TT.chapternav.initialize = function() {
    $("#chapter-nav ul li").click(TT.chapternav.onChapterClick);
    $("#chapter-nav ul li").mouseover(TT.chapternav.onChapterMouseOver);
    $("#chapter-nav ul li").mouseout(TT.chapternav.onChapterMouseOut);
    $("#chapter-nav ul li .over div.description").css({
        opacity: 0
    });
};
TT.chapternav.updateReadMarkers = function() {
    $("#chapter-nav ul li").each(function() {
        var articleId = $("a", this).attr("data-article");
        if (TT.storage.hasArticleBeenRead(articleId)) {
            $(this).addClass("read");
        }
    });
};
TT.chapternav.getDisabledArticles = function() {
    var articles = [];
    $("#chapter-nav ul li.disabled").each(function() {
        var article = $("a", this).attr("data-article");
        if (article) {
            articles.push(article);
        }
    });
    return articles;
};
TT.chapternav.updateSelection = function(overrideArticleId) {
    var selectedArticleId = TT.navigation.classToArticle($("#pages section.current").attr("class"));
    if (overrideArticleId) {
        selectedArticleId = overrideArticleId;
    }
    $("#chapter-nav ul li").removeClass("selected");
    if (selectedArticleId && !TT.navigation.isHomePage() && !TT.navigation.isCreditsPage() && !TT.navigation.isForeword()) {
        var element = $("#chapter-nav ul li").find("[data-article=" + selectedArticleId + "]");
        if (element && element.parent()) {
            element.parent().addClass("selected");
            TT.sharing.updateSharerIndex(element.parent().index() + 1);
        }
    }
    if (!TT.storage.isFirstTimeVisitor || !TT.navigation.isHomePage() || TT.navigation.hasNavigated) {
        $("#chapter-nav").show();
    }
    $("#chapter-nav ul li a.over").each(function() {
        $(this).css({
            top: -$(this).height() + 4
        });
    });
};
TT.chapternav.getProgress = function(overrideArticleId) {
    var selectedArticle = $("#chapter-nav ul li.selected");
    if (overrideArticleId) {
        selectedArticle = $("#chapter-nav ul li").find("[data-article=" + overrideArticleId + "]").parent();
    }
    if (TT.navigation.isHomePage() || TT.navigation.isForeword()) {
        return 0;
    } else {
        if (TT.navigation.isCreditsPage() || TT.navigation.isLastPage() || selectedArticle.length == 0) {
            return 1;
        }
    }
    return Math.min(selectedArticle.index() / ($("#chapter-nav ul li:not(.disabled)").length - 1), 1);
};
TT.chapternav.onChapterClick = function(event) {
    var item = $(event.target).is("li") ? $(event.target) : $(event.target).parents("li");
    var articleId = $("a", item).attr("data-article");
    if (articleId && !item.hasClass("disabled")) {
        if (TT.navigation.goToPage(articleId, 1)) {
            if (TT.chapternav.getProgress(articleId) > TT.chapternav.getProgress()) {
                TT.paperstack.updateStack(TT.chapternav.getProgress(articleId));
            }
            TT.chapternav.updateSelection(articleId);
        }
    }
    return false;
};
TT.chapternav.onChapterMouseOver = function(event) {
    var item = $(event.target).is("li") ? $(event.target) : $(event.target).parents("li");
    var description = $("div.description", item);
    description.stop(true, false).fadeTo(200, 1);
};
TT.chapternav.onChapterMouseOut = function(event) {
    var item = $(event.target).is("li") ? $(event.target) : $(event.target).parents("li");
    var description = $("div.description", item);
    description.fadeTo(200, 0);
};
TT.sharing = {};
TT.sharing.BASE_URL = "http://www.20thingsilearned.com/";
TT.sharing.FACEBOOK_SHARER = "http://www.facebook.com/sharer.php";
TT.sharing.TWITTER_SHARER = "http://twitter.com/share";
TT.sharing.BUZZ_SHARER = "http://www.google.com/buzz/post";
TT.sharing.initialize = function() {
    $("footer div.sharing .facebook, #credits div.share .facebook").click(TT.sharing.shareBookOnFacebook);
    $("footer div.sharing .twitter, #credits div.share .twitter").click(TT.sharing.shareBookOnTwitter);
    $("footer div.sharing .buzz, #credits div.share .buzz").click(TT.sharing.shareBookOnBuzz);
    $("footer div.sharing .url").click(TT.sharing.openClipboardNotification);
    $("#sharer div.content ul li.facebook").click(TT.sharing.shareChapterOnFacebook);
    $("#sharer div.content ul li.twitter").click(TT.sharing.shareChapterOnTwitter);
    $("#sharer div.content ul li.buzz").click(TT.sharing.shareChapterOnBuzz);
    $("#sharer div.content ul li.print").click(TT.sharing.printThing);
    $(document).mousedown(TT.sharing.documentMouseDownHandler);
};
TT.sharing.updateSharer = function(hide) {
    var articleId = TT.navigation.classToArticle($("#pages section.current").attr("class"));
    $("#sharer div.content ul li.print a").attr("href", "/" + articleId + "/print");
    if (TT.navigation.isHomePage() || TT.navigation.isCreditsPage() || TT.navigation.isLastPage() || TT.navigation.isForeword() || hide) {
        $("#sharer").stop(true, true);
        $("#sharer").fadeOut(150);
    } else {
        $("#sharer").stop(true, true).delay(150).fadeIn(150);
    }
};
TT.sharing.updateSharerIndex = function(index) {
    if (index != 0) {
        if (index != $("#sharer div.content p.index span").text()) {
            $("#sharer div.content p.index span").each(function(i) {
                if (i > 1) {
                    $(this).remove();
                }
            });
            $("#sharer div.content p.index span").delay(300).fadeOut(200, function() {
                $(this).remove();
            });
            var span = $("<span>" + index + "</span>");
            span.hide().delay(300).fadeIn(200);
            $("#sharer div.content p.index").append(span);
            TT.sharing.updateSharer();
        }
    } else {
        $("#sharer").fadeOut();
    }
};
TT.sharing.openClipboardNotification = function() {
    $("footer .clipboard-notification").show().focus().select();
    return false;
};
TT.sharing.documentMouseDownHandler = function(event) {
    if (event && event.target === $("footer .clipboard-notification")[0]) {
        $("footer .clipboard-notification").focus().select();
        return false;
    } else {
        $("footer .clipboard-notification").fadeOut(200);
    }
};
TT.sharing.shareBookOnFacebook = function() {
    var url = TT.sharing.BASE_URL;
    var title = "A fun guidebook from Google on things you've always wanted to know about browsers & the web (but were afraid to ask).";
    TT.sharing.shareOnFacebook(url, title);
    return false;
};
TT.sharing.shareBookOnTwitter = function() {
    var url = TT.sharing.BASE_URL;
    var title = "A fun guidebook from Google on things you've always wanted to know about browsers & the web:";
    TT.sharing.shareOnTwitter(url, title);
    return false;
};
TT.sharing.shareBookOnBuzz = function() {
    var url = TT.sharing.BASE_URL;
    var message = "A fun guidebook from Google on things you've always wanted to know about browsers & the web (but were afraid to ask): http://goo.gl/20things";
    TT.sharing.shareOnBuzz(url, message);
    return false;
};
TT.sharing.shareChapterOnFacebook = function() {
    var url = TT.sharing.BASE_URL + "/" + TT.navigation.getCurrentArticleId();
    var title = "A fun fact I learned today from Google's guidebook to browsers and the web.";
    TT.sharing.shareOnFacebook(url, title);
    return false;
};
TT.sharing.shareChapterOnTwitter = function() {
    var url = TT.sharing.BASE_URL + "/" + TT.navigation.getCurrentArticleId();
    var title = "A fun fact I learned today from Google's guidebook to browsers and the web: ";
    TT.sharing.shareOnTwitter(url, title);
    return false;
};
TT.sharing.shareChapterOnBuzz = function() {
    var url = TT.sharing.BASE_URL + "/" + TT.navigation.getCurrentArticleId();
    var message = "A fun fact I learned today from Google's guidebook to browsers and the web: " + url;
    TT.sharing.shareOnBuzz(url, message);
    return false;
};
TT.sharing.shareOnFacebook = function(url, title) {
    var shareURL = TT.sharing.FACEBOOK_SHARER;
    shareURL += "?u=" + encodeURIComponent(url);
    shareURL += "&t=" + encodeURIComponent(title);
    window.open(shareURL, "Facebook", "toolbar=0,status=0,width=726,location=no,menubar=no,height=436");
};
TT.sharing.shareOnTwitter = function(url, title) {
    var shareURL = TT.sharing.TWITTER_SHARER;
    shareURL += "?original_referer=" + encodeURIComponent(url);
    shareURL += "&text=" + encodeURIComponent(title);
    shareURL += "&url=" + encodeURIComponent(url);
    window.open(shareURL, "Twitter", "toolbar=0,status=0,width=726,location=no,menubar=no,height=436");
};
TT.sharing.shareOnBuzz = function(url, message) {
    var shareURL = TT.sharing.BUZZ_SHARER;
    shareURL += "?url=" + encodeURIComponent(url);
    shareURL += "&message=" + encodeURIComponent(message);
    shareURL += "&imageurl=" + encodeURIComponent(TT.sharing.BASE_URL + "/css/images/front-cover.jpg");
    window.open(shareURL, "Buzz", "toolbar=0,status=0,width=726,location=no,menubar=no,height=436");
};
TT.overlay = {};
TT.overlay.BOOKMARK_WIDTH = 190;
TT.overlay.BOOKMARK_HEIGHT = 600;
TT.overlay.overlay = null;
TT.overlay.bookmark = null;
TT.overlay.print = null;
TT.overlay.visible = false;
TT.overlay.hasShownBookmark = false;
TT.overlay.hasShownPrint = false;
TT.overlay.initialize = function() {
    TT.overlay.overlay = $("#overlay");
    TT.overlay.bookmark = $("#overlay div.bookmark");
    TT.overlay.print = $("#overlay div.print");
};
TT.overlay.showBookmark = function(continueCallback, restartCallback, cancelCallback) {
    if (!TT.overlay.hasShownBookmark) {
        TT.overlay.overlay.stop().fadeIn(200);
        TT.overlay.bookmark.siblings().hide();
        TT.overlay.bookmark.stop().fadeIn(200);
        $("a.resume", TT.overlay.bookmark).click(function() {
            TT.overlay.hide();
            continueCallback();
            return false;
        });
        $("a.restart", TT.overlay.bookmark).click(function() {
            TT.overlay.hide();
            restartCallback();
            return false;
        });
        $("a.close", TT.overlay.bookmark).click(function() {
            TT.overlay.hide();
            cancelCallback();
            return false;
        });
        TT.overlay.visible = true;
        TT.overlay.hasShownBookmark = true;
        TT.pageflip.unregisterEventListeners();
        $("body").addClass("overlay");
    }
};
TT.overlay.showPrint = function() {
    TT.overlay.overlay.stop().fadeIn("fast");
    TT.overlay.print.siblings().hide();
    TT.overlay.print.stop().fadeIn("fast");
    $("a.close", TT.overlay.print).click(function() {
        TT.overlay.hide();
        return false;
    });
    $("a.downloadPdf.disabled", TT.overlay.print).click(function() {
        return false;
    });
    TT.overlay.visible = true;
    TT.pageflip.unregisterEventListeners();
    $("body").addClass("overlay");
};
TT.overlay.hide = function() {
    TT.overlay.overlay.stop().fadeOut("fast");
    TT.overlay.bookmark.stop().fadeOut("fast");
    TT.overlay.print.stop().fadeOut("fast");
    TT.overlay.visible = false;
    TT.pageflip.registerEventListeners();
    $("body").removeClass("overlay");
};
TT.overlay.isVisible = function() {
    return TT.overlay.visible;
};
TT.tableofthings = {};
TT.tableofthings.visible = false;
TT.tableofthings.initialize = function() {
    $("#table-of-contents ul li").mouseover(TT.tableofthings.onChapterMouseOver);
    $("#table-of-contents ul li").mouseout(TT.tableofthings.onChapterMouseOut);
    $("#table-of-contents ul li").click(TT.tableofthings.onChapterClick);
};
TT.tableofthings.updateReadMarkers = function() {
    $("#table-of-contents ul li").each(function() {
        var articleId = $("a", this).attr("data-article");
        if (TT.storage.hasArticleBeenRead(articleId)) {
            $(this).addClass("read");
        }
    });
};
TT.tableofthings.updateSelection = function(overrideArticleId) {
    var selectedArticleId = TT.navigation.classToArticle($("#pages section.current").attr("class"));
    if (overrideArticleId) {
        selectedArticleId = overrideArticleId;
    }
    $("#table-of-contents ul li").removeClass("selected");
    if (selectedArticleId) {
        var element = $("#table-of-contents ul li").find("[data-article*=" + selectedArticleId + "]");
        if (element && element.parent()) {
            element.parents("li").addClass("selected");
        }
    }
};
TT.tableofthings.show = function() {
    if (!TT.tableofthings.visible) {
        $("body").addClass("tot");
        $("#table-of-contents").stop(true, true).show().fadeTo(200, 1);
        $("#table-of-contents div.header").stop().css({
            opacity: 1
        });
        var columns = 5;
        $("#table-of-contents ul li").each(function(i) {
            var row = Math.floor(i / columns);
            var col = i % columns;
            row++;
            col++;
            $(this).stop().css({
                opacity: 0
            }).show().delay((row + col) * 50).fadeTo(100, 1);
        });
        TT.updateLayout();
    }
    TT.tableofthings.visible = true;
    TT.pageflip.unregisterEventListeners();
};
TT.tableofthings.hide = function() {
    $("body").removeClass("tot");
    $("#table-of-contents").delay(200).fadeTo(200, 0, function() {
        $(this).hide();
    });
    $("#table-of-contents div.header").stop().fadeTo(150, 0);
    var columns = 5;
    var length = $("#table-of-contents ul li").length;
    $("#table-of-contents ul li").each(function(i) {
        var row = Math.floor((length - 1 - i) / columns);
        var col = (length - 1 - i) % columns;
        row++;
        col++;
        $(this).stop().fadeTo((row + col) * 40, 0);
    });
    TT.tableofthings.visible = false;
    TT.pageflip.registerEventListeners();
    TT.updateLayout();
};
TT.tableofthings.onChapterClick = function(event) {
    if ($("body").hasClass("tot")) {
        var articleId = $(event.target).parents("li").children("a").attr("data-article");
        if (!articleId) {
            articleId = $(event.target).children("a").attr("data-article");
        }
        if (TT.navigation.goToPage(articleId, 1)) {
            TT.navigation.hideTableOfContents();
            TT.chapternav.updateSelection(articleId);
            TT.tableofthings.updateSelection(articleId);
        }
    }
    return false;
};
TT.tableofthings.onChapterMouseOver = function(event) {
    if ($(event.target).parents("li").hasClass("disabled") || $(event.target).parents("li").hasClass("selected")) {
        return false;
    }
};
TT.tableofthings.onChapterMouseOut = function(event) {};
TT.flipintro = {};
TT.flipintro.WIDTH = 89;
TT.flipintro.HEIGHT = 29;
TT.flipintro.VSPACE = 20;
TT.flipintro.loopInterval = -1;
TT.flipintro.canvas = null;
TT.flipintro.context = null;
TT.flipintro.flip = {
    progress: 0,
    alpha: 0
};
TT.flipintro.initialize = function() {
    TT.flipintro.canvas = $("#flip-intro");
    TT.flipintro.canvas[0].width = TT.flipintro.WIDTH;
    TT.flipintro.canvas[0].height = TT.flipintro.HEIGHT + (TT.flipintro.VSPACE * 2);
    TT.flipintro.context = TT.flipintro.canvas[0].getContext("2d");
};
TT.flipintro.activate = function() {
    if (TT.flipintro.loopInterval == -1) {
        TT.flipintro.flip.progress = 1;
        TT.flipintro.loopInterval = setInterval(TT.flipintro.render, 32);
    }
};
TT.flipintro.deactivate = function() {
    clearInterval(TT.flipintro.loopInterval);
    TT.flipintro.loopInterval = -1;
};
TT.flipintro.render = function() {
    TT.flipintro.context.clearRect(0, 0, TT.flipintro.WIDTH, TT.flipintro.HEIGHT + (TT.flipintro.VSPACE * 2));
    if (!TT.navigation.isHomePage()) {
        TT.flipintro.deactivate();
    }
    TT.flipintro.flip.progress -= Math.max(0.12 * (1 - Math.abs(TT.flipintro.flip.progress)), 0.02);
    TT.flipintro.flip.alpha = 1 - ((Math.abs(TT.flipintro.flip.progress) - 0.7) / 0.3);
    if (TT.flipintro.flip.progress < -2) {
        TT.flipintro.flip.progress = 1;
    }
    var strength = 1 - Math.abs(TT.flipintro.flip.progress);
    var anchorOutdent = strength * 12;
    var controlOutdent = strength * 8;
    var source = {
        top: {
            x: TT.flipintro.WIDTH * 0.5,
            y: TT.flipintro.VSPACE
        },
        bottom: {
            x: TT.flipintro.WIDTH * 0.5,
            y: TT.flipintro.HEIGHT + TT.flipintro.VSPACE
        }
    };
    var destination = {
        top: {
            x: source.top.x + (TT.flipintro.WIDTH * TT.flipintro.flip.progress * 0.6),
            y: TT.flipintro.VSPACE - anchorOutdent
        },
        bottom: {
            x: source.bottom.x + (TT.flipintro.WIDTH * TT.flipintro.flip.progress * 0.6),
            y: TT.flipintro.HEIGHT + TT.flipintro.VSPACE - anchorOutdent
        }
    };
    var control = {
        top: {
            x: source.top.x + (12 * TT.flipintro.flip.progress),
            y: TT.flipintro.VSPACE - controlOutdent
        },
        bottom: {
            x: source.bottom.x + (12 * TT.flipintro.flip.progress),
            y: TT.flipintro.HEIGHT + TT.flipintro.VSPACE - controlOutdent
        }
    };
    TT.flipintro.context.fillStyle = "rgba(238,238,238," + TT.flipintro.flip.alpha + ")";
    TT.flipintro.context.strokeStyle = "rgba(90,90,90," + TT.flipintro.flip.alpha + ")";
    TT.flipintro.context.beginPath();
    TT.flipintro.context.moveTo(source.top.x, source.top.y);
    TT.flipintro.context.quadraticCurveTo(control.top.x, control.top.y, destination.top.x, destination.top.y);
    TT.flipintro.context.lineTo(destination.bottom.x, destination.bottom.y);
    TT.flipintro.context.quadraticCurveTo(control.bottom.x, control.bottom.y, source.bottom.x, source.bottom.y);
    TT.flipintro.context.fill();
    TT.flipintro.context.stroke();
};