<commit_before>$(document).ready(function(){

    var isMobile = {
        Android: function() {
            return navigator.userAgent.match(/Android/i);
        }
        ,BlackBerry: function() {
            return navigator.userAgent.match(/BlackBerry/i);
        }
        ,iOS: function() {
            return navigator.userAgent.match(/iPhone|iPad|iPod/i);
        }
        ,Opera: function() {
            return navigator.userAgent.match(/Opera Mini/i);
        }
        ,Windows: function() {
            return navigator.userAgent.match(/IEMobile/i);
        }
        ,any: function() {
            return (isMobile.Android() || isMobile.BlackBerry() || isMobile.iOS() || isMobile.Opera() || isMobile.Windows());
        }
    };

    $('pre').addClass('prettyprint linenums'); //添加Google code Hight需要的class

    //***********************
    //**评论的代码也删掉哦***
    window.disqus_shortname = 'lingxiankong'; // required: replace example with your forum shortname
    $('#disqus_container .comment').on('click',function(){
        $(this).html('加载中...');
        var that = this;
        $.getScript('http://' + disqus_shortname + '.disqus.com/embed.js',function(){$(that).remove()});
    });
    //**评论的代码也删掉哦***
    //***********************

    $('.entry a').each(function(index,element){
        var href = $(this).attr('href');
        if(href){
            if(href.indexOf('#') == 0){
            }else if ( href.indexOf('/') == 0 || href.toLowerCase().indexOf('beiyuu.com')>-1 ){
            }else if ($(element).has('img').length){
            }else{
                $(this).attr('target','_blank');
                $(this).addClass('external');
            }
        }
    });

    (function(){
        var ie6 = ($.browser.msie && $.browser.version=="6.0") ? true : false;

        function initHeading(){
            var h2 = [];
            var h3 = [];
            var h2index = 0;

            $.each($('.entry h2, .entry h3'),function(index,item){
                if(item.tagName.toLowerCase() == 'h2'){
                    var h2item = {};
                    h2item.name = $(item).text();
                    h2item.id = 'menuIndex'+index;
                    h2.push(h2item);
                    h2index++;
                }else{
                    var h3item = {};
                    h3item.name = $(item).text();
                    h3item.id = 'menuIndex'+index;
                    if(!h3[h2index-1]){
                        h3[h2index-1] = [];
                    }
                    h3[h2index-1].push(h3item);
                }
                item.id = 'menuIndex' + index;
            });

            return {h2:h2,h3:h3}
        }

        function genTmpl(){
            var h1txt = $('h1').text();
            var tmpl = '

' + h1txt + '
';

            var heading = initHeading();
            var h2 = heading.h2;
            var h3 = heading.h3;

            for(var i=0;i<h2.length;i++){
                tmpl += '
'+h2[i].name+'
';

                if(h3[i]){
                    for(var j=0;j<h3[i].length;j++){
                        tmpl += '
'+h3[i][j].name+'
';
                    }
                }
            }
            tmpl += '
';

            return tmpl;
        }

        function genIndex(){
            var tmpl = genTmpl();
            var indexCon = '

';

            $('#content').append(indexCon);

            $('#menuIndex')
                .append($(tmpl))
                .delegate('a','click',function(e){
                    e.preventDefault();

                    var selector = $(this).attr('data-id') ? '#'+$(this).attr('data-id') : 'h1'
                    var scrollNum = $(selector).offset().top;

                    $('body, html').animate({ scrollTop: scrollNum-30 }, 400, 'swing');
                });
        }

        var waitForFinalEvent = (function () {
            var timers = {};
            return function (callback, ms, uniqueId) {
                if (!uniqueId) {
                    uniqueId = "Don't call this twice without a uniqueId";
                }
                if (timers[uniqueId]) {
                    clearTimeout (timers[uniqueId]);
                }
                timers[uniqueId] = setTimeout(callback, ms);
            };
        })();

        if($('.entry h2').length > 2 && !isMobile.any() && !ie6){

            genIndex();

            $(window).load(function(){
                var scrollTop = [];
                $.each($('#menuIndex li a'),function(index,item){
                    var selector = $(item).attr('data-id') ? '#'+$(item).attr('data-id') : 'h1'
                    var top = $(selector).offset().top;
                    scrollTop.push(top);
                });

                var menuIndexTop = $('#menuIndex').offset().top;
                var menuIndexLeft = $('#menuIndex').offset().left;

                $(window).scroll(function(){
                    waitForFinalEvent(function(){
                        var nowTop = $(window).scrollTop();
                        var length = scrollTop.length;
                        var index;

                        if(nowTop+20 > menuIndexTop){
                            $('#menuIndex').css({
                                position:'fixed'
                                ,top:'20px'
                                ,left:menuIndexLeft
                            });
                        }else{
                            $('#menuIndex').css({
                                position:'static'
                                ,top:0
                                ,left:0
                            });
                        }

                        if(nowTop+60 > scrollTop[length-1]){
                            index = length;
                        }else{
                            for(var i=0;i<length;i++){
                                if(nowTop+60 <= scrollTop[i]){
                                    index = i;
                                    break;
                                }
                            }
                        }
                        $('#menuIndex li').removeClass('on');
                        $('#menuIndex li').eq(index-1).addClass('on');
                    });
                });

                $(window).resize(function(){
                    $('#menuIndex').css({
                        position:'static'
                        ,top:0
                        ,left:0
                    });

                    menuIndexTop = $('#menuIndex').offset().top;
                    menuIndexLeft = $('#menuIndex').offset().left;

                    $(window).trigger('scroll')
                    $('#menuIndex').css('max-height',$(window).height()-80);
                });
            })

            //用js计算屏幕的高度
            $('#menuIndex').css('max-height',$(window).height()-80);
        }
    })();

    $.getScript('/js/prettify/prettify.js',function(){
        prettyPrint();
    });

    if(/\#comment/.test(location.hash)){
        $('#disqus_container .comment').trigger('click');
    }

    if(/css3-animation/.test(location.href)){
        $("head").append("<link rel='stylesheet' type='text/css' href='/css/css3-ani.css'/>");
        $.getScript('/js/css3-ani.js',function(){});
    }
});
<commit_msg>test for the entry<commit_after>$(document).ready(function(){

    var isMobile = {
        Android: function() {
            return navigator.userAgent.match(/Android/i);
        }
        ,BlackBerry: function() {
            return navigator.userAgent.match(/BlackBerry/i);
        }
        ,iOS: function() {
            return navigator.userAgent.match(/iPhone|iPad|iPod/i);
        }
        ,Opera: function() {
            return navigator.userAgent.match(/Opera Mini/i);
        }
        ,Windows: function() {
            return navigator.userAgent.match(/IEMobile/i);
        }
        ,any: function() {
            return (isMobile.Android() || isMobile.BlackBerry() || isMobile.iOS() || isMobile.Opera() || isMobile.Windows());
        }
    };

    $('pre').addClass('prettyprint linenums'); //添加Google code Hight需要的class

    //***********************
    //**评论的代码也删掉哦***
    window.disqus_shortname = 'lingxiankong'; // required: replace example with your forum shortname
    $('#disqus_container .comment').on('click',function(){
        $(this).html('加载中...');
        var that = this;
        $.getScript('http://' + disqus_shortname + '.disqus.com/embed.js',function(){$(that).remove()});
    });
    //**评论的代码也删掉哦***
    //***********************

    $('.entry a').each(function(index,element){
        var href = $(this).attr('href');
        if(href){
            if(href.indexOf('#') == 0){
            }else if ( href.indexOf('/') == 0 || href.toLowerCase().indexOf('beiyuu.com')>-1 ){
            }else if ($(element).has('img').length){
            }else{
                $(this).attr('target','_blank');
                $(this).addClass('external');
            }
        }
    });

    (function(){
        var ie6 = ($.browser.msie && $.browser.version=="6.0") ? true : false;

        function initHeading(){
            var h2 = [];
            var h3 = [];
            var h2index = 0;

            $.each($('.entry h2, .entry h3'),function(index,item){
                if(item.tagName.toLowerCase() == 'h2'){
                    var h2item = {};
                    h2item.name = $(item).text();
                    h2item.id = 'menuIndex'+index;
                    h2.push(h2item);
                    h2index++;
                }else{
                    var h3item = {};
                    h3item.name = $(item).text();
                    h3item.id = 'menuIndex'+index;
                    if(!h3[h2index-1]){
                        h3[h2index-1] = [];
                    }
                    h3[h2index-1].push(h3item);
                }
                item.id = 'menuIndex' + index;
            });

            return {h2:h2,h3:h3}
        }

        function genTmpl(){
            var h1txt = $('h1').text();
            var tmpl = '

' + h1txt + '
';

            var heading = initHeading();
            var h2 = heading.h2;
            var h3 = heading.h3;

            for(var i=0;i<h2.length;i++){
                tmpl += '
'+h2[i].name+'
';

                if(h3[i]){
                    for(var j=0;j<h3[i].length;j++){
                        tmpl += '
'+h3[i][j].name+'
';
                    }
                }
            }
            tmpl += '
';

            return tmpl;
        }

        function genIndex(){
            var tmpl = genTmpl();
            var indexCon = '

';

            $('#content').append(indexCon);

            $('#menuIndex')
                .append($(tmpl))
                .delegate('a','click',function(e){
                    e.preventDefault();

                    var selector = $(this).attr('data-id') ? '#'+$(this).attr('data-id') : 'h1'
                    var scrollNum = $(selector).offset().top;

                    $('body, html').animate({ scrollTop: scrollNum-30 }, 400, 'swing');
                });
        }

        var waitForFinalEvent = (function () {
            var timers = {};
            return function (callback, ms, uniqueId) {
                if (!uniqueId) {
                    uniqueId = "Don't call this twice without a uniqueId";
                }
                if (timers[uniqueId]) {
                    clearTimeout (timers[uniqueId]);
                }
                timers[uniqueId] = setTimeout(callback, ms);
            };
        })();

        if($('.entry h2').length > 1 && !isMobile.any() && !ie6){

            genIndex();

            $(window).load(function(){
                var scrollTop = [];
                $.each($('#menuIndex li a'),function(index,item){
                    var selector = $(item).attr('data-id') ? '#'+$(item).attr('data-id') : 'h1'
                    var top = $(selector).offset().top;
                    scrollTop.push(top);
                });

                var menuIndexTop = $('#menuIndex').offset().top;
                var menuIndexLeft = $('#menuIndex').offset().left;

                $(window).scroll(function(){
                    waitForFinalEvent(function(){
                        var nowTop = $(window).scrollTop();
                        var length = scrollTop.length;
                        var index;

                        if(nowTop+20 > menuIndexTop){
                            $('#menuIndex').css({
                                position:'fixed'
                                ,top:'20px'
                                ,left:menuIndexLeft
                            });
                        }else{
                            $('#menuIndex').css({
                                position:'static'
                                ,top:0
                                ,left:0
                            });
                        }

                        if(nowTop+60 > scrollTop[length-1]){
                            index = length;
                        }else{
                            for(var i=0;i<length;i++){
                                if(nowTop+60 <= scrollTop[i]){
                                    index = i;
                                    break;
                                }
                            }
                        }
                        $('#menuIndex li').removeClass('on');
                        $('#menuIndex li').eq(index-1).addClass('on');
                    });
                });

                $(window).resize(function(){
                    $('#menuIndex').css({
                        position:'static'
                        ,top:0
                        ,left:0
                    });

                    menuIndexTop = $('#menuIndex').offset().top;
                    menuIndexLeft = $('#menuIndex').offset().left;

                    $(window).trigger('scroll')
                    $('#menuIndex').css('max-height',$(window).height()-80);
                });
            })

            //用js计算屏幕的高度
            $('#menuIndex').css('max-height',$(window).height()-80);
        }
    })();

    $.getScript('/js/prettify/prettify.js',function(){
        prettyPrint();
    });

    if(/\#comment/.test(location.hash)){
        $('#disqus_container .comment').trigger('click');
    }

    if(/css3-animation/.test(location.href)){
        $("head").append("<link rel='stylesheet' type='text/css' href='/css/css3-ani.css'/>");
        $.getScript('/js/css3-ani.js',function(){});
    }
});
<|endoftext|>


<commit_before>"use strict";
(function() {
  var baseURL = "https://dl.dropboxusercontent.com/s/";
  var chapterInfo = [{},
    {
      "number": "1",
      "title": "One Punch",
      "extension": "jpg",
      "pages": ["",
        "cadz9o1zrnftgjd/OPM_1_01.jpg",
        "tm10u82i6fisd8c/OPM_1_02-03.jpg",
        "dnccp9idmwlbj82/OPM_1_04.jpg",
        "1ppjf2cdj708zku/OPM_1_05.jpg",
        "q46jiur8xkmtrxv/OPM_1_06.jpg",
        "yf2ch4b9m9pxtyp/OPM_1_07.jpg",
        "b8n0t27zf0ruzt7/OPM_1_08.jpg",
        "efcfbrw4jzglpyz/OPM_1_09.jpg",
        "gtowm1j5qrtbgm5/OPM_1_10.jpg",
        "r4ajxgkmfzr0xa3/OPM_1_11.jpg",
        "a6znj0s5zsp5we4/OPM_1_12.jpg",
        "trzmatbr9s1rpdm/OPM_1_13.jpg",
        "xm05vuha9g3mdo6/OPM_1_14-15.jpg",
        "ng0x19z2r740hf5/OPM_1_16.jpg",
        "41yx5va4bwyk9i4/OPM_1_17.jpg",
        "d1pc2ruexmo8box/OPM_1_18-19.jpg",
        "98rx0erbl7boqnh/OPM_1_20-21.jpg",
        "9gsjxzy85na9h0q/OPM_1_22.jpg"
      ]
    },
    {
      "number": "2",
      "title": "Crab & Job Hunting",
      "extension": "jpg",
      "pages": ["",
        "tq8zyif6xbqmweu/OPM_2_01.jpg",
        "upl37zdg436ona9/OPM_2_02.jpg",
        "j9f43kybqkey89k/OPM_2_03.jpg",
        "9ae9y9dhwrcwc82/OPM_2_04.jpg",
        "v3gyfcqyeaacyoy/OPM_2_05.jpg",
        "934cyuol31865a1/OPM_2_06.jpg",
        "p2g8wvcgjfv2a3n/OPM_2_07.jpg",
        "8wncqu1e4zd94p1/OPM_2_08.jpg",
        "nvtirqk2n9cvg5r/OPM_2_09.jpg",
        "5pfwt22hzb4cts3/OPM_2_10.jpg",
        "oy7hdqs0abjb4pc/OPM_2_11.jpg",
        "q5n2y5a5sjf4v86/OPM_2_12.jpg",
        "quefekggtoyuayw/OPM_2_13.jpg",
        "v8jyp2mhintzldn/OPM_2_14.jpg",
        "qpgtvdplhvksxa8/OPM_2_15.jpg",
        "w8ttwgqdb8mqe5y/OPM_2_16.jpg"
      ]
    }
  ]

  var lastChapter = chapterInfo.length - 1;

  var currentChapter = -1;
  var currentTitle = "";
  var currentNumber = "";
  var currentPage = -1;
  var chapterSize = -1;
  var currentExtension = "";

  var imageElement = null;
  var loadingElement = null;
  var leftElement = null;
  var rightElement = null;

  function switchChapter(chapterIndex, pageIndex) {
    currentChapter = chapterIndex;
    currentExtension = chapterInfo[currentChapter].extension;
    currentTitle = chapterInfo[currentChapter].title;
    currentNumber = chapterInfo[currentChapter].number;
    chapterSize = chapterInfo[currentChapter].pages.length - 1;
    loadPage(pageIndex > 0 ? pageIndex : 1); // handles not-passed case too becuase lol Javascript
  }

  function loadPage(pageIndex) {
    startLoading();
    currentPage = pageIndex;
    document.title = seriesTitle + " Ch " + currentNumber + ": " + currentTitle + " p" + currentPage;
    setFragment();
    var newURL = baseURL + chapterInfo[currentChapter].pages[currentPage];
    if (currentPage < 2 && currentChapter < 2) {
      leftElement.className = "side";
    }
    else {
      leftElement.className = "side hand";
    }
    if (currentPage >= chapterSize && currentChapter >= lastChapter) {
      rightElement.className = "side";
    }
    else {
      rightElement.className = "side hand";
    }
    loadingElement.innerHTML = "Loading... 0.00%";
    var xhr = new XMLHttpRequest();
    xhr.open("GET", newURL);
    xhr.responseType = "arraybuffer";
    xhr.onprogress = function(data) {
      var percentDisplay = (data.loaded / data.total * 100).toFixed(2);
      loadingElement.innerHTML = "Loading..." + percentDisplay + "%";
    };
    xhr.onload = function(data) {
      if (this.status === "304") {
        imageElement.src = newURL;
      }
      else {
        loadingElement.innerHTML = "Loading... 100.00%";
        var blob = new Blob([this.response], {type: "image/" + currentExtension});
        imageElement.src = window.URL.createObjectURL(blob);
      }
    };
    xhr.send();
  }

  function startLoading() {
    loadingElement.className = "";
  }

  function stopLoading() {
    imageElement.className = "";
    loadingElement.className = "hidden";
    window.scrollTo(0, 0);
  }

  function pageBack() {
    if (currentPage < 2) {
      if (currentChapter > 1) {
        switchChapter(currentChapter - 1);
      }
    }
    else {
      loadPage(currentPage - 1);
    }
  }

  function pageForward() {
    if (currentPage >= chapterSize) {
      if (currentChapter < lastChapter) {
        switchChapter(currentChapter + 1);
      }
    }
    else {
      loadPage(currentPage + 1);
    }
  }

  function keyCheck(e) {
    if (e.keyCode === 37) { // Left Arrow
      pageBack();
    }
    else if (e.keyCode === 39) { // Right Arrow
      pageForward();
    }
  }

  function setFragment() {
    window.location.hash = currentChapter + "," + currentPage;
  }

  function parseFragment() {
    var split = window.location.hash.split("#")[1].split(",");
    var chapterIndex = parseInt(split[0]);
    if (isNaN(chapterIndex)) {
      chapterIndex = 1;
    }
    var pageIndex = parseInt(split[1]);
    if (isNaN(pageIndex)) {
      pageIndex = 1;
    }
    switchChapter(chapterIndex, pageIndex);
  }

  function setup() {
    document.removeEventListener("DOMContentLoaded", setup);
    imageElement = document.getElementById("image");
    loadingElement = document.getElementById("loading");
    leftElement = document.getElementById("left")
    rightElement = document.getElementById("right");
    leftElement.addEventListener("click", pageBack);
    rightElement.addEventListener("click", pageForward);
    imageElement.addEventListener("load", stopLoading);
    document.addEventListener("keydown", keyCheck);
    if (window.location.hash === "") {
      switchChapter(1);
    }
    else {
      parseFragment();
    }
  }

  document.addEventListener("DOMContentLoaded", setup);
  var seriesTitle = "One-Punch Man";
  document.title = seriesTitle;
}).call(this);
<commit_msg>Update read.js<commit_after>"use strict";
(function() {
  var baseURL = "https://dl.dropboxusercontent.com/s/";
  var chapterInfo = [{},
    {
      "number": "1",
      "title": "One Punch",
      "extension": "jpg",
      "pages": ["",
        "cadz9o1zrnftgjd/OPM_1_01.jpg",
        "tm10u82i6fisd8c/OPM_1_02-03.jpg",
        "dnccp9idmwlbj82/OPM_1_04.jpg",
        "1ppjf2cdj708zku/OPM_1_05.jpg",
        "q46jiur8xkmtrxv/OPM_1_06.jpg",
        "yf2ch4b9m9pxtyp/OPM_1_07.jpg",
        "b8n0t27zf0ruzt7/OPM_1_08.jpg",
        "efcfbrw4jzglpyz/OPM_1_09.jpg",
        "gtowm1j5qrtbgm5/OPM_1_10.jpg",
        "r4ajxgkmfzr0xa3/OPM_1_11.jpg",
        "a6znj0s5zsp5we4/OPM_1_12.jpg",
        "trzmatbr9s1rpdm/OPM_1_13.jpg",
        "xm05vuha9g3mdo6/OPM_1_14-15.jpg",
        "ng0x19z2r740hf5/OPM_1_16.jpg",
        "41yx5va4bwyk9i4/OPM_1_17.jpg",
        "d1pc2ruexmo8box/OPM_1_18-19.jpg",
        "98rx0erbl7boqnh/OPM_1_20-21.jpg",
        "9gsjxzy85na9h0q/OPM_1_22.jpg"
      ]
    },
    {
      "number": "2",
      "title": "Crab & Job Hunting",
      "extension": "jpg",
      "pages": ["",
        "tq8zyif6xbqmweu/OPM_2_01.jpg",
        "upl37zdg436ona9/OPM_2_02.jpg",
        "j9f43kybqkey89k/OPM_2_03.jpg",
        "9ae9y9dhwrcwc82/OPM_2_04.jpg",
        "v3gyfcqyeaacyoy/OPM_2_05.jpg",
        "934cyuol31865a1/OPM_2_06.jpg",
        "p2g8wvcgjfv2a3n/OPM_2_07.jpg",
        "8wncqu1e4zd94p1/OPM_2_08.jpg",
        "nvtirqk2n9cvg5r/OPM_2_09.jpg",
        "5pfwt22hzb4cts3/OPM_2_10.jpg",
        "oy7hdqs0abjb4pc/OPM_2_11.jpg",
        "q5n2y5a5sjf4v86/OPM_2_12.jpg",
        "quefekggtoyuayw/OPM_2_13.jpg",
        "v8jyp2mhintzldn/OPM_2_14.jpg",
        "qpgtvdplhvksxa8/OPM_2_15.jpg",
        "w8ttwgqdb8mqe5y/OPM_2_16.jpg"
      ]
    }
  ]

  var lastChapter = chapterInfo.length - 1;

  var currentChapter = -1;
  var currentTitle = "";
  var currentNumber = "";
  var currentPage = -1;
  var chapterSize = -1;
  var currentExtension = "";

  var imageElement = null;
  var loadingElement = null;
  var leftElement = null;
  var rightElement = null;

  function switchChapter(chapterIndex, pageIndex) {
    currentChapter = chapterIndex;
    currentExtension = chapterInfo[currentChapter].extension;
    currentTitle = chapterInfo[currentChapter].title;
    currentNumber = chapterInfo[currentChapter].number;
    chapterSize = chapterInfo[currentChapter].pages.length - 1;
    loadPage(pageIndex > 0 ? pageIndex : 1); // handles not-passed case too becuase lol Javascript
  }

  function loadPage(pageIndex) {
    startLoading();
    currentPage = pageIndex;
    document.title = seriesTitle + " Ch " + currentNumber + ": " + currentTitle + " p" + currentPage;
    setFragment();
    var newURL = baseURL + chapterInfo[currentChapter].pages[currentPage];
    if (currentPage < 2 && currentChapter < 2) {
      leftElement.className = "side";
    }
    else {
      leftElement.className = "side hand";
    }
    if (currentPage >= chapterSize && currentChapter >= lastChapter) {
      rightElement.className = "side";
    }
    else {
      rightElement.className = "side hand";
    }
    loadingElement.innerHTML = "Loading... 0.00%";
    var xhr = new XMLHttpRequest();
    xhr.open("GET", newURL);
    xhr.responseType = "arraybuffer";
    xhr.onprogress = function(data) {
      var percentDisplay = (data.loaded / data.total * 100).toFixed(2);
      loadingElement.innerHTML = "Loading..." + percentDisplay + "%";
    };
    xhr.onload = function(data) {
      if (this.status === 304) {
        imageElement.src = newURL;
      }
      else {
        loadingElement.innerHTML = "Loading... 100.00%";
        var blob = new Blob([this.response], {type: "image/" + currentExtension});
        imageElement.src = window.URL.createObjectURL(blob);
      }
    };
    xhr.send();
  }

  function startLoading() {
    loadingElement.className = "";
  }

  function stopLoading() {
    imageElement.className = "";
    loadingElement.className = "hidden";
    window.scrollTo(0, 0);
  }

  function pageBack() {
    if (currentPage < 2) {
      if (currentChapter > 1) {
        switchChapter(currentChapter - 1);
      }
    }
    else {
      loadPage(currentPage - 1);
    }
  }

  function pageForward() {
    if (currentPage >= chapterSize) {
      if (currentChapter < lastChapter) {
        switchChapter(currentChapter + 1);
      }
    }
    else {
      loadPage(currentPage + 1);
    }
  }

  function keyCheck(e) {
    if (e.keyCode === 37) { // Left Arrow
      pageBack();
    }
    else if (e.keyCode === 39) { // Right Arrow
      pageForward();
    }
  }

  function setFragment() {
    window.location.hash = currentChapter + "," + currentPage;
  }

  function parseFragment() {
    var split = window.location.hash.split("#")[1].split(",");
    var chapterIndex = parseInt(split[0]);
    if (isNaN(chapterIndex)) {
      chapterIndex = 1;
    }
    var pageIndex = parseInt(split[1]);
    if (isNaN(pageIndex)) {
      pageIndex = 1;
    }
    switchChapter(chapterIndex, pageIndex);
  }

  function setup() {
    document.removeEventListener("DOMContentLoaded", setup);
    imageElement = document.getElementById("image");
    loadingElement = document.getElementById("loading");
    leftElement = document.getElementById("left")
    rightElement = document.getElementById("right");
    leftElement.addEventListener("click", pageBack);
    rightElement.addEventListener("click", pageForward);
    imageElement.addEventListener("load", stopLoading);
    document.addEventListener("keydown", keyCheck);
    if (window.location.hash === "") {
      switchChapter(1);
    }
    else {
      parseFragment();
    }
  }

  document.addEventListener("DOMContentLoaded", setup);
  var seriesTitle = "One-Punch Man";
  document.title = seriesTitle;
}).call(this);
<|endoftext|>


<commit_before>function setMeter(rating) {
	if(rating>=6) {
		var x=500;
	} else if(rating<=-6) {
		var x=-500;
	} else {
		var x=rating*100;
	}
	var r=$("#meterRed");
	var g=$("#meterGreen");
	g.animate({width: (583+x)}, Math.abs(g.width()-(583+x)));
	r.animate({width: (583-x)}, Math.abs(r.width()-(583-x)));
}

var player;
var firebase = new Firebase('https://adr2370.firebaseio.com/');
var songdb = firebase.child('songs');
var playerdb = firebase.child('playerdb');
var commentdb = firebase.child('comments');
var currentdb = firebase.child('current');

var songs=new Array();

function startVideo() {
	//youtube video
	currentdb.once('value', function(dataSnapshot) {
		var id=dataSnapshot.child('id').val();
		if(player==null) {
			player=new YT.Player('youtube', {
					height: '390',
					width: '640',
					videoId: id,
			  playerVars: { autoplay:1, enablejsapi:1, modestbranding:1, rel:0, showinfo:0, iv_load_policy:3, volume:50 },
					events: {
						'onStateChange': onPlayerStateChange,
						//'onError': onError
					}});
		} else {
			player.loadVideoById(id, 5, "large");
		}
		/*if(player!=null) {
			playerdb.child('volume').once('value', function(dataSnapshot) {
				player.setVolume(dataSnapshot.val());
			});
		} else {
			console.log("SOMETHING WENT WRONG");
		}*/
		$("#visuals canvas").hide();
		$("#"+id).remove();
		songdb.child(id).remove();
		removeA(songs, id);
	});
}

function nextVideo() {
	setMeter(0);
	$("#comments").html("");
	commentdb.remove();
	currentdb.remove();
	if(songs.length>0) {
		console.log("hello");
		songdb.child(songs[0]).once('value', function(dataSnapshot) {
			currentdb.set(dataSnapshot.val());
			currentdb.child('rating').set(0);
			currentdb.child('skip').set(0);
			currentdb.child('play').set(0);
			currentdb.child('pause').set(0);
			currentdb.child('fullScreen').set(0);
			currentdb.child('id').set(songs[0]);
			console.log(dataSnapshot.val());
		});
	} else {
		console.log("goodbye");
		$("#youtube").replaceWith($('
<\/div>'));
		player=null;
	}
}

function onPlayerStateChange(event) {
	if(event.data == 0) {
		currentdb.remove();
	}
}

function onError(event) {
	currentdb.remove();
}

currentdb.on('child_changed', function(snapshot, prevChildName) {
	//CHANGE RATING OR SKIP VIDEO
	if(snapshot.name()=="rating") {
		setMeter(snapshot.val());
	} else if(snapshot.name()=="skip"&&snapshot.val()==1) {
		nextVideo();
	} else if(snapshot.name()=="play"&&snapshot.val()==1) {
		currentdb.child('play').set(0);
		player.playVideo();
	} else if(snapshot.name()=="pause"&&snapshot.val()==1) {
		currentdb.child('pause').set(0);
		player.pauseVideo();
	}  else if(snapshot.name()=="fullScreen"&&snapshot.val()==1) {
		currentdb.child('fullScreen').set(0);
		window.fullScreen();
	}
});

currentdb.on('child_added', function(snapshot, prevChildName) {
	//ADDED SONG
	//songs.unshift(snapshot.name());
	if(snapshot.name()=="id") {
		startVideo();
	}
});

currentdb.on('child_removed', function(snapshot, prevChildName) {
	//ADDED SONG
	//songs.unshift(snapshot.name());
	if(snapshot.name()=="id") {
		//nextVideo();
	}
});

songdb.on('child_added', function(snapshot, prevChildName) {
	//ADDS SOMETHING TO QUEUE
	//snapshot.name() is youtube id, all else is below
	songs.push(snapshot.name());
	$("#queue").append('
'+snapshot.child('name').val()+'<\/td>'+snapshot.child('length').val()+'<\/td><\/td>'+snapshot.child('numViews').val()+'<\/tr>');
	currentdb.on('value', function(snapshot, prevChildName) {
		if(snapshot.val()==null) {
			nextVideo();
		}
	});
});

songdb.on('child_moved', function(snapshot, prevChildName) {
	//SWITCHES ORDER OF STUFF IN QUEUE
	//prevChildName is the name of the previous one, snapshot.name is the id of the snapshot
	if(prevChildName==null) {
		$("#topQueue").after($("#"+snapshot.name()).remove());
	} else {
		$("#"+prevChildName).after($("#"+snapshot.name()).remove());
	}
});

playerdb.on('child_changed', function(snapshot, prevChildName) {
	player.setVolume(snapshot.val());
});

commentdb.on('child_added', function(snapshot, prevChildName) {
	//COMMENTS ADDED
	//snapshot.val() is each comment
	$("#comments").prepend("
" + snapshot.val()+"<\/p>");
});

function removeA(arr) {
    var what, a = arguments, L = a.length, ax;
    while (L > 1 && arr.length) {
        what = a[--L];
        while ((ax= arr.indexOf(what)) !== -1) {
            arr.splice(ax, 1);
        }
    }
    return arr;
}<commit_msg>Added in next video check<commit_after>function setMeter(rating) {
	if(rating>=6) {
		var x=500;
	} else if(rating<=-6) {
		var x=-500;
	} else {
		var x=rating*100;
	}
	var r=$("#meterRed");
	var g=$("#meterGreen");
	g.animate({width: (583+x)}, Math.abs(g.width()-(583+x)));
	r.animate({width: (583-x)}, Math.abs(r.width()-(583-x)));
}

var player;
var firebase = new Firebase('https://adr2370.firebaseio.com/');
var songdb = firebase.child('songs');
var playerdb = firebase.child('playerdb');
var commentdb = firebase.child('comments');
var currentdb = firebase.child('current');
var goingToNext = false;

var songs=new Array();

function startVideo() {
	//youtube video
	currentdb.once('value', function(dataSnapshot) {
		var id=dataSnapshot.child('id').val();
		if(player==null) {
			player=new YT.Player('youtube', {
					height: '390',
					width: '640',
					videoId: id,
			  playerVars: { autoplay:1, enablejsapi:1, modestbranding:1, rel:0, showinfo:0, iv_load_policy:3, volume:50 },
					events: {
						'onStateChange': onPlayerStateChange,
						//'onError': onError
					}});
		} else {
			player.loadVideoById(id, 5, "large");
		}
		/*if(player!=null) {
			playerdb.child('volume').once('value', function(dataSnapshot) {
				player.setVolume(dataSnapshot.val());
			});
		} else {
			console.log("SOMETHING WENT WRONG");
		}*/
		$("#visuals canvas").hide();
		$("#"+id).remove();
		songdb.child(id).remove();
		removeA(songs, id);
	});
}

function nextVideo() {
	setMeter(0);
	$("#comments").html("");
	commentdb.remove();
	currentdb.remove();
	if(songs.length>0) {
		songdb.child(songs[0]).once('value', function(dataSnapshot) {
			currentdb.set(dataSnapshot.val());
			currentdb.child('rating').set(0);
			currentdb.child('skip').set(0);
			currentdb.child('play').set(0);
			currentdb.child('pause').set(0);
			currentdb.child('fullScreen').set(0);
			currentdb.child('id').set(songs[0]);
			goingToNext=false;
		});
	} else {
		$("#youtube").replaceWith($('
<\/div>'));
		player=null;
		goingToNext=false;
	}
}

function onPlayerStateChange(event) {
	if(event.data == 0) {
		currentdb.remove();
	}
}

function onError(event) {
	currentdb.remove();
}

currentdb.on('child_changed', function(snapshot, prevChildName) {
	//CHANGE RATING OR SKIP VIDEO
	if(snapshot.name()=="rating") {
		setMeter(snapshot.val());
	} else if(snapshot.name()=="skip"&&snapshot.val()==1) {
		nextVideo();
	} else if(snapshot.name()=="play"&&snapshot.val()==1) {
		currentdb.child('play').set(0);
		player.playVideo();
	} else if(snapshot.name()=="pause"&&snapshot.val()==1) {
		currentdb.child('pause').set(0);
		player.pauseVideo();
	}  else if(snapshot.name()=="fullScreen"&&snapshot.val()==1) {
		currentdb.child('fullScreen').set(0);
		window.fullScreen();
	}
});

currentdb.on('child_added', function(snapshot, prevChildName) {
	//ADDED SONG
	//songs.unshift(snapshot.name());
	if(snapshot.name()=="id") {
		startVideo();
	}
});

currentdb.on('child_removed', function(snapshot, prevChildName) {
	//ADDED SONG
	//songs.unshift(snapshot.name());
	if(snapshot.name()=="id") {
		//nextVideo();
	}
});

songdb.on('child_added', function(snapshot, prevChildName) {
	//ADDS SOMETHING TO QUEUE
	//snapshot.name() is youtube id, all else is below
	songs.push(snapshot.name());
	$("#queue").append('
'+snapshot.child('name').val()+'<\/td>'+snapshot.child('length').val()+'<\/td><\/td>'+snapshot.child('numViews').val()+'<\/tr>');
	currentdb.on('value', function(snapshot, prevChildName) {
		if(snapshot.val()==null&&!goingToNext) {
			goingToNext=true;
			nextVideo();
		}
	});
});

songdb.on('child_moved', function(snapshot, prevChildName) {
	//SWITCHES ORDER OF STUFF IN QUEUE
	//prevChildName is the name of the previous one, snapshot.name is the id of the snapshot
	if(prevChildName==null) {
		$("#topQueue").after($("#"+snapshot.name()).remove());
	} else {
		$("#"+prevChildName).after($("#"+snapshot.name()).remove());
	}
});

playerdb.on('child_changed', function(snapshot, prevChildName) {
	player.setVolume(snapshot.val());
});

commentdb.on('child_added', function(snapshot, prevChildName) {
	//COMMENTS ADDED
	//snapshot.val() is each comment
	$("#comments").prepend("
" + snapshot.val()+"<\/p>");
});

function removeA(arr) {
    var what, a = arguments, L = a.length, ax;
    while (L > 1 && arr.length) {
        what = a[--L];
        while ((ax= arr.indexOf(what)) !== -1) {
            arr.splice(ax, 1);
        }
    }
    return arr;
}<|endoftext|>


<commit_before>function runCodes() {
    var v = editor.getValue().split('\n');
    var res = '';
    for (var i = 0; i < v.length; i++) {
        if (v[i]) {
            try {
                if (v[i].match(/^\s*?\/\//)) {
                    res += '\n';
                } else {
                    res += JSON.stringify(
                        // give eval scope.
                        (function() {
                            return eval(v.slice(0,i+1).join('\n'));
                        })()
                    ) + '\n';
                }
            } catch(e) {
                if (!(e instanceof SyntaxError)) {
                    res += e;
                } else {
                    res += '\n';
                }
            }
        } else {
            res += '\n';
        }
    }
    result.setValue(res);
}

function require(x) {
    var scripts = document.head.getElementsByTagName('script');
    // do not re-add scripts
    for (var i = 0; i < scripts.length; i++) {
        if (scripts[i].src == x) return 'loaded';
    }
    var scr = document.head.appendChild(document.createElement('script'));
    scr.onload = runCodes;
    scr.src = x;
}

var editor = CodeMirror.fromTextArea(document.getElementById('code'), {
    mode: 'javascript',
    matchBrackets: true,
    onChange: runCodes,
    tabSize: 2,
    smartIndent: false
});

var result = CodeMirror.fromTextArea(document.getElementById('results'), {
    mode: 'javascript',
    tabSize: 2,
    readOnly: true
});

function xhr(url, callback) {
    var x = new XMLHttpRequest();
    x.open("GET", url, true);
    x.onload = callback;
    x.send();
}

function gist(id) {
    xhr("https://api.github.com/gists/" + id, function() {
        var r = JSON.parse(this.response);
        for (var k in r.files) {
            return editor.setValue(r.files[k].content);
        }
    });
}

if (window.location.hash) {
    gist(window.location.hash.substring(1));
}
window.onhashchange = function() {
    gist(window.location.hash.substring(1));
};
<commit_msg>Show fns<commit_after>function stringify(x) {
    if (typeof x == 'function') {
        return '[function]';
    } else {
        try {
            return JSON.stringify(x);
        } catch(e) {
            return '';
        }
    }
}

function runCodes() {
    var v = editor.getValue().split('\n');
    var res = '';
    for (var i = 0; i < v.length; i++) {
        if (v[i]) {
            try {
                if (v[i].match(/^\s*?\/\//)) {
                    res += '\n';
                } else {
                    var output = (function() {
                        return eval(v.slice(0,i+1).join('\n'));
                    })();
                    res += stringify(output) + '\n';
                }
            } catch(e) {
                if (!(e instanceof SyntaxError)) {
                    res += e;
                } else {
                    res += '\n';
                }
            }
        } else {
            res += '\n';
        }
    }
    result.setValue(res);
}

function require(x) {
    var scripts = document.head.getElementsByTagName('script');
    // do not re-add scripts
    for (var i = 0; i < scripts.length; i++) {
        if (scripts[i].src == x) return 'loaded';
    }
    var scr = document.head.appendChild(document.createElement('script'));
    scr.onload = runCodes;
    scr.src = x;
}

var editor = CodeMirror.fromTextArea(document.getElementById('code'), {
    mode: 'javascript',
    matchBrackets: true,
    onChange: runCodes,
    tabSize: 2,
    smartIndent: false
});

var result = CodeMirror.fromTextArea(document.getElementById('results'), {
    mode: 'javascript',
    tabSize: 2,
    readOnly: true
});

function xhr(url, callback) {
    var x = new XMLHttpRequest();
    x.open("GET", url, true);
    x.onload = callback;
    x.send();
}

function gist(id) {
    xhr("https://api.github.com/gists/" + id, function() {
        var r = JSON.parse(this.response);
        for (var k in r.files) {
            return editor.setValue(r.files[k].content);
        }
    });
}

if (window.location.hash) {
    gist(window.location.hash.substring(1));
}
window.onhashchange = function() {
    gist(window.location.hash.substring(1));
};
<|endoftext|>


<commit_before>// UNDO - REDO functionality
//
// API
//
// name: name of action: Draw, Pan, Rotate, Zoom, Clear, New Frame, Copy Frame, Delete Frame
// type: frame or document
//
// pushUndo(name, type, applyFn, restoreFn);
// undo(type); pops the relevant undo stack
// redo(type); pops the relevant redo stack
// switchFrame(newFrame); // because most undo is frame-based
// clear(); // reset, i.e., when a new document is created
//
// Use events for enabling/disabling buttons and changing button labels
// Events: document.addEventListener('shimmy-undo-change', handler, true);
// function undoHandler(evt){
//   evt.frameUndo = nameOfFrameUndo or null;
//   evt.frameRedo = nameOfFrameRedo or null;
//   evt.docUndo = nameOfDocUndo or null;
//   evt.docRedo = nameOfDocRedo or null;
// }

function UndoRedo(frame){
  const documentUndoStack = [];
  const documentRedoStack = [];
  const framesUndoStack = new Map();
  const frameRedoStack = new Map();
  let currentFrame = frame;

  const sendEvent()

  const pushUndo = (name, type, applyFn, restoreFn) => {
    if (type === 'document'){
      document
    }

  };

  const undo = (type) => {

  }

  const redo = (type) => {

  }

  return{
    undo,
    redo,
    pushUndo
  };
}<commit_msg>👆😇 Checkpoint ./js/undo.js:739936/357<commit_after>// UNDO - REDO functionality
//
// API
//
// name: name of action: Draw, Pan, Rotate, Zoom, Clear, New Frame, Copy Frame, Delete Frame
// type: frame or document
//
// pushUndo(name, type, applyFn, restoreFn);
// undo(type); pops the relevant undo stack
// redo(type); pops the relevant redo stack
// switchFrame(newFrame); // because most undo is frame-based
// clear(); // reset, i.e., when a new document is created
//
// Use events for enabling/disabling buttons and changing button labels
// Events: document.addEventListener('shimmy-undo-change', handler, true);
// function undoHandler(evt){
//   evt.frameUndo = nameOfFrameUndo or null;
//   evt.frameRedo = nameOfFrameRedo or null;
//   evt.docUndo = nameOfDocUndo or null;
//   evt.docRedo = nameOfDocRedo or null;
// }

function UndoRedo(frame){
  const documentUndoStack = [];
  const documentRedoStack = [];
  const framesUndoStack = new Map();
  const frameRedoStack = new Map();
  let currentFrame = frame;

  // look at the top item of a stack
  const peek = (stack) => return stack.length ? stack[stack.length -1].name : null;

  const sendEvent = () => {
    let evt = new Event('shimmy-undo-change');
    evt.frameUndo = peek(framesUndoStack[currentFrame]);
    evt.frameRedo = peek(framesRedoStack[])

  }

  const pushUndo = (name, type, applyFn, restoreFn) => {
    if (type === 'document'){
      document
    }

  };

  const undo = (type) => {

  }

  const redo = (type) => {

  }

  return{
    undo,
    redo,
    pushUndo
  };
}<|endoftext|>


<commit_before>/**
 * Week-related views
 */

var DURATION_GRANULARITY = 5;

var DAY_HEIGHT_PX = 42;  // Keep in sync with svg {...} in main.css
var PX_PER_MIN = 700 / 420;

var weekGoal = 300;

// @see http://jsfiddle.net/bryanbuchs/c72Vg/
var WeekView = Marionette.ItemView.extend({
    template: "#weekTpl",
    className: "week",
    initialize: function(options) {
        this.weekSessions = this.model.attributes.sessions;
        this.listenTo(this.weekSessions, 'add', this.render);
        this.listenTo(this.weekSessions, 'change', this.render);
        this.listenTo(this.weekSessions, 'remove', this.render);
        this.listenTo(this.model, 'change', this.render);
        this.dragInfo = null;
        this.goalDragInfo = null;
    },
    onBeforeRender: function() {
        this.byDay = this.weekSessions.groupBy(function(session, i) {
            return session.day();
        });
        // cache
        this.beginning = moment(this.weekSessions.at(0).attributes.date).startOf('isoWeek');
        this.end = +moment(this.beginning).endOf('isoWeek');
        this.cumulativeSum = {};
        var day = moment(this.beginning);
        var sum = 0;
        while(+day < this.end){
            var key = dayFormat(day);
            sum += _.reduce(this.byDay[key] || [], function(sum, session) {
                return sum + session.attributes.duration;
            }, 0);
            this.cumulativeSum[key] = sum;
            day = day.add(1, 'days');
        }
    },
    templateHelpers: function() {
        return {
            self: this,
            DAY_HEIGHT_PX: DAY_HEIGHT_PX,
            include: function(tmpl){
                return templates[tmpl];
            },
            // Round so our 1px borders are aligned with the screen pixels.
            rm2p: function(min) {
                return Math.round(PX_PER_MIN * min);
            },
            weekAttr: function(name) {
                return this.self.model.get(name);
            },
            attr: function(model, name){
                return model.get(name);
            },
            sessionClass: function(session){
                if(this.self.dragInfo)
                    return this.self.dragInfo.session.cid == session.cid ? "drag-target" : "";
                return "";
            },
            iconURL: function(session){
                var label = session.get('label');
                var data = DEFAULT_ACTIVITY_TYPES[label] || DEFAULT_ACTIVITY_TYPES['unspecified'];
                return "img/freepik-icons/png/neg/" + data.icon + '.png';
            },
            iconClass: function(session){
                var label = session.get('label');
                return label == 'unspecified' || !(label in DEFAULT_ACTIVITY_TYPES) ? 'bad' : 'good';
            },
            days: function() {
                return [0, 1, 2, 3, 4, 5, 6];
            },
            reversedDays: function() {
                return [6, 5, 4, 3, 2, 1, 0];
            },
            day: function(i){
                return moment(this.self.beginning).add(i, 'days');
            },
            daySessions: function(i) {
                var day = this.day(i);
                return this.self.byDay[dayFormat(day)];
            },
            emptyDay: function(day) {
                return !this.daySessions(day);
            },
            daySum: function(day, upTo) {
                if(arguments.length < 2)
                    upTo = Infinity;
                var sessionList = this.daySessions(day) || [];
                return _.reduce(sessionList.slice(0, upTo), function(sum, session){
                  return sum + session.attributes.duration;
                }, 0);
            },
            cumulative: function(i) {
                var m = this.day(i);
                return this.self.cumulativeSum[dayFormat(m)];
            },
            weekNumber: function(){
                return this.self.beginning.format('W');
            },
            goalLineColor: function(px) {
                var defaultPixel = 500;
                return px >= defaultPixel ? "rgb(0,150,0)":"rgb(225,0,0)";
            },
            totalBarColor: function(mins) {
                var goalMins = this.self.model.get('goal');
                return mins >= goalMins ? "fill:rgb(0,150,0)":"fill:rgb(227,227,22)";
            }
        };
    },
    events: {
      "mousedown rect.session":     "mousedownSession",
      "mousedown rect.new-session": "mousedownPlus",
      // Implicit by setting window.dragHandler = this.
      //"mousemove": "mousemove",
      //"mouseup": "mouseup",
      "mousedown g.goal":           "mousedownGoal",
    },
    mousedownSession: function(event){
        var cid = $(event.target).data('cid');
        this.startDragging(this.weekSessions.get(cid), false, event);
    },
    mousedownPlus: function(event){
        var day = $(event.target).data('day');
        var date = this.templateHelpers().day(day);
        var newSession = new Session({date: date});
        this.weekSessions.add(newSession);
        // The view should render synchronously, so the new rect should be
        // present in the DOM if we need it.
        this.startDragging(newSession, true, event);
    },
    mousedownGoal: function(event) {
        this.dragGoalInfo = {
            origMouseX : event.pageX,
            origDuration: this.model.get('goal')
        };
        window.startDrag(this);
    },
    startDragging: function(session, isCreate, event){
        this.dragInfo = {
                session: session,
                isCreate: isCreate,
                startTime: moment(),
                origDuration: session.attributes.duration,
                origMouseX: event.pageX
        };
        window.startDrag(this);
    },
    mousemove: function(event){
        if (this.dragInfo) {
            var newDuration = Math.max(0,
                    this.dragInfo.origDuration +
                    (event.pageX - this.dragInfo.origMouseX) / PX_PER_MIN);
            newDuration = DURATION_GRANULARITY * Math.round(newDuration / DURATION_GRANULARITY);
            this.dragInfo.session.set('duration', newDuration);
        }
        if (this.dragGoalInfo) {
            var newDuration = Math.max(0,
                    this.dragGoalInfo.origDuration +
                    (event.pageX - this.dragGoalInfo.origMouseX) / PX_PER_MIN);
            newDuration = DURATION_GRANULARITY * Math.round(newDuration / DURATION_GRANULARITY);
            // move goal line and flag
            this.model.set('goal', newDuration);
        }
    },
    mouseup: function(event){
        if (this.dragInfo) {
            var dragInfo = this.dragInfo;
            this.dragInfo = null;

            x = event.pageX;
            y = event.pageY;

            // XXX: Ideally this time threshold would be a system setting
            // like the double-click timeout.
            if (moment().diff(dragInfo.startTime, 'milliseconds') < 300) {
                console.log('Treat as a click at position ' + x + ',' + y);
                showActivityInfo(dragInfo.session, this.updateSession.bind(this, dragInfo.session));
                event.stopPropagation(); // event outside -> close popup
                return;
            }

            // if creating a session, ask for activity type and duration
            if (dragInfo.isCreate) {
                showActivityInfo(dragInfo.session, this.updateSession.bind(this, dragInfo.session));
                event.stopPropagation();
                // TODO set duration to activity's current duration (some
                // default if just a click on plus) to handle both
                // click and drag cases
                return;
            }

            // Delete if duration has been reduced to zero.
            this.updateSession(dragInfo.session);
        }

        if (this.dragGoalInfo) {
            this.dragGoalInfo = null;
        }
    },
    updateSession: function(session){
        if (session.attributes.duration == 0){
            this.weekSessions.remove(session);
        }
    }
});

var FullView = Marionette.CollectionView.extend({
    childView: WeekView,
    initialize: function() {
        var sessionsByWeek = this.collection.groupBy(function(session, i) {
            return session.week();
        });
        var weeks = Object.keys(sessionsByWeek);
        weeks.sort();
        weeks.reverse();
        /*
        console.log(weeks);
        for(var w in weekSessions){
            var sessions = weekSessions[w];
            console.log('%s -> %o', w, sessions);
        }
        */
        var weekModels = _.map(weeks, function(w) {
            return new Week({sessions: new WeekSessions(sessionsByWeek[w])});
        });
        this.collection = new Backbone.Collection(weekModels);
    }
});
<commit_msg>Fix updating of drag-target class.<commit_after>/**
 * Week-related views
 */

var DURATION_GRANULARITY = 5;

var DAY_HEIGHT_PX = 42;  // Keep in sync with svg {...} in main.css
var PX_PER_MIN = 700 / 420;

var weekGoal = 300;

// @see http://jsfiddle.net/bryanbuchs/c72Vg/
var WeekView = Marionette.ItemView.extend({
    template: "#weekTpl",
    className: "week",
    initialize: function(options) {
        this.weekSessions = this.model.attributes.sessions;
        this.listenTo(this.weekSessions, 'add', this.render);
        this.listenTo(this.weekSessions, 'change', this.render);
        this.listenTo(this.weekSessions, 'remove', this.render);
        this.listenTo(this.model, 'change', this.render);
        this.dragInfo = null;
        this.goalDragInfo = null;
    },
    onBeforeRender: function() {
        this.byDay = this.weekSessions.groupBy(function(session, i) {
            return session.day();
        });
        // cache
        this.beginning = moment(this.weekSessions.at(0).attributes.date).startOf('isoWeek');
        this.end = +moment(this.beginning).endOf('isoWeek');
        this.cumulativeSum = {};
        var day = moment(this.beginning);
        var sum = 0;
        while(+day < this.end){
            var key = dayFormat(day);
            sum += _.reduce(this.byDay[key] || [], function(sum, session) {
                return sum + session.attributes.duration;
            }, 0);
            this.cumulativeSum[key] = sum;
            day = day.add(1, 'days');
        }
    },
    templateHelpers: function() {
        return {
            self: this,
            DAY_HEIGHT_PX: DAY_HEIGHT_PX,
            include: function(tmpl){
                return templates[tmpl];
            },
            // Round so our 1px borders are aligned with the screen pixels.
            rm2p: function(min) {
                return Math.round(PX_PER_MIN * min);
            },
            weekAttr: function(name) {
                return this.self.model.get(name);
            },
            attr: function(model, name){
                return model.get(name);
            },
            sessionClass: function(session){
                if(this.self.dragInfo)
                    return this.self.dragInfo.session.cid == session.cid ? "drag-target" : "";
                return "";
            },
            iconURL: function(session){
                var label = session.get('label');
                var data = DEFAULT_ACTIVITY_TYPES[label] || DEFAULT_ACTIVITY_TYPES['unspecified'];
                return "img/freepik-icons/png/neg/" + data.icon + '.png';
            },
            iconClass: function(session){
                var label = session.get('label');
                return label == 'unspecified' || !(label in DEFAULT_ACTIVITY_TYPES) ? 'bad' : 'good';
            },
            days: function() {
                return [0, 1, 2, 3, 4, 5, 6];
            },
            reversedDays: function() {
                return [6, 5, 4, 3, 2, 1, 0];
            },
            day: function(i){
                return moment(this.self.beginning).add(i, 'days');
            },
            daySessions: function(i) {
                var day = this.day(i);
                return this.self.byDay[dayFormat(day)];
            },
            emptyDay: function(day) {
                return !this.daySessions(day);
            },
            daySum: function(day, upTo) {
                if(arguments.length < 2)
                    upTo = Infinity;
                var sessionList = this.daySessions(day) || [];
                return _.reduce(sessionList.slice(0, upTo), function(sum, session){
                  return sum + session.attributes.duration;
                }, 0);
            },
            cumulative: function(i) {
                var m = this.day(i);
                return this.self.cumulativeSum[dayFormat(m)];
            },
            weekNumber: function(){
                return this.self.beginning.format('W');
            },
            goalLineColor: function(px) {
                var defaultPixel = 500;
                return px >= defaultPixel ? "rgb(0,150,0)":"rgb(225,0,0)";
            },
            totalBarColor: function(mins) {
                var goalMins = this.self.model.get('goal');
                return mins >= goalMins ? "fill:rgb(0,150,0)":"fill:rgb(227,227,22)";
            }
        };
    },
    events: {
      "mousedown rect.session":     "mousedownSession",
      "mousedown rect.new-session": "mousedownPlus",
      // Implicit by setting window.dragHandler = this.
      //"mousemove": "mousemove",
      //"mouseup": "mouseup",
      "mousedown g.goal":           "mousedownGoal",
    },
    mousedownSession: function(event){
        var cid = $(event.target).data('cid');
        this.startDragging(this.weekSessions.get(cid), false, event);
    },
    mousedownPlus: function(event){
        var day = $(event.target).data('day');
        var date = this.templateHelpers().day(day);
        var newSession = new Session({date: date});
        this.weekSessions.add(newSession);
        // The view should render synchronously, so the new rect should be
        // present in the DOM if we need it.
        this.startDragging(newSession, true, event);
    },
    mousedownGoal: function(event) {
        this.dragGoalInfo = {
            origMouseX : event.pageX,
            origDuration: this.model.get('goal')
        };
        window.startDrag(this);
    },
    startDragging: function(session, isCreate, event){
        this.dragInfo = {
                session: session,
                isCreate: isCreate,
                startTime: moment(),
                origDuration: session.attributes.duration,
                origMouseX: event.pageX
        };
        this.render();  // update drag-target class
        window.startDrag(this);
    },
    mousemove: function(event){
        if (this.dragInfo) {
            var newDuration = Math.max(0,
                    this.dragInfo.origDuration +
                    (event.pageX - this.dragInfo.origMouseX) / PX_PER_MIN);
            newDuration = DURATION_GRANULARITY * Math.round(newDuration / DURATION_GRANULARITY);
            this.dragInfo.session.set('duration', newDuration);
        }
        if (this.dragGoalInfo) {
            var newDuration = Math.max(0,
                    this.dragGoalInfo.origDuration +
                    (event.pageX - this.dragGoalInfo.origMouseX) / PX_PER_MIN);
            newDuration = DURATION_GRANULARITY * Math.round(newDuration / DURATION_GRANULARITY);
            // move goal line and flag
            this.model.set('goal', newDuration);
        }
    },
    mouseup: function(event){
        if (this.dragInfo) {
            var dragInfo = this.dragInfo;
            this.dragInfo = null;
            this.render();  // update drag-target class

            x = event.pageX;
            y = event.pageY;

            // XXX: Ideally this time threshold would be a system setting
            // like the double-click timeout.
            if (moment().diff(dragInfo.startTime, 'milliseconds') < 300) {
                console.log('Treat as a click at position ' + x + ',' + y);
                showActivityInfo(dragInfo.session, this.updateSession.bind(this, dragInfo.session));
                event.stopPropagation(); // event outside -> close popup
                return;
            }

            // if creating a session, ask for activity type and duration
            if (dragInfo.isCreate) {
                showActivityInfo(dragInfo.session, this.updateSession.bind(this, dragInfo.session));
                event.stopPropagation();
                // TODO set duration to activity's current duration (some
                // default if just a click on plus) to handle both
                // click and drag cases
                return;
            }

            // Delete if duration has been reduced to zero.
            this.updateSession(dragInfo.session);
        }

        if (this.dragGoalInfo) {
            this.dragGoalInfo = null;
        }
    },
    updateSession: function(session){
        if (session.attributes.duration == 0){
            this.weekSessions.remove(session);
        }
    }
});

var FullView = Marionette.CollectionView.extend({
    childView: WeekView,
    initialize: function() {
        var sessionsByWeek = this.collection.groupBy(function(session, i) {
            return session.week();
        });
        var weeks = Object.keys(sessionsByWeek);
        weeks.sort();
        weeks.reverse();
        /*
        console.log(weeks);
        for(var w in weekSessions){
            var sessions = weekSessions[w];
            console.log('%s -> %o', w, sessions);
        }
        */
        var weekModels = _.map(weeks, function(w) {
            return new Week({sessions: new WeekSessions(sessionsByWeek[w])});
        });
        this.collection = new Backbone.Collection(weekModels);
    }
});
<|endoftext|>


<commit_before>angular.module('stream', ['core'])

.factory('StreamService', ['$http', '$httpParamSerializer', function($http, $httpParamSerializer) {

    /* get the base url */

    var baseurl = angular.element('meta[name="baseurl"]').attr('content');

    /* create the metadata service */

    var service = {
        values: {},
        errors: {}
    };

    /* define service functions */

    service.init = function(opt) {
        service.stream_url = baseurl + opt.stream_url;
        service.defaults = opt.defaults;

        if (angular.isDefined(opt.resource)) {
            service.resource = opt.resource;
        }

        service.reset();
    };

    service.reset = function() {
        console.log(service.defaults);
        angular.forEach(service.defaults, function(value, key) {
            service.values[key] = value;
        });
    };

    service.download = function() {
        // construct the url for the stream
        var url = service.stream_url + '/' + service.resource + '/?' + $httpParamSerializer(service.values);

        $http.get(url + '&download=').then(function() {
            // download the file, headers will prevent the browser reloading the page
            window.location.href = url;
        }, function(result) {
            service.errors = result.data;
        });
    }

    service.build_query_params = function() {
        service.query_params = $httpParamSerializer(service.values);
    }

    return service;
}])

.controller('StreamController', ['$scope', 'StreamService', function($scope, StreamService) {

    $scope.service = StreamService;
    $scope.$watch('service.values', $scope.service.build_query_params, true);

}]);
<commit_msg>Cleanup<commit_after>angular.module('stream', ['core'])

.factory('StreamService', ['$http', '$httpParamSerializer', function($http, $httpParamSerializer) {

    /* get the base url */

    var baseurl = angular.element('meta[name="baseurl"]').attr('content');

    /* create the metadata service */

    var service = {
        values: {},
        errors: {}
    };

    /* define service functions */

    service.init = function(opt) {
        service.stream_url = baseurl + opt.stream_url;
        service.defaults = opt.defaults;

        if (angular.isDefined(opt.resource)) {
            service.resource = opt.resource;
        }

        service.reset();
    };

    service.reset = function() {
        angular.forEach(service.defaults, function(value, key) {
            service.values[key] = value;
        });
    };

    service.download = function() {
        // construct the url for the stream
        var url = service.stream_url + '/' + service.resource + '/?' + $httpParamSerializer(service.values);

        $http.get(url + '&download=').then(function() {
            // download the file, headers will prevent the browser reloading the page
            window.location.href = url;
        }, function(result) {
            service.errors = result.data;
        });
    }

    service.build_query_params = function() {
        service.query_params = $httpParamSerializer(service.values);
    }

    return service;
}])

.controller('StreamController', ['$scope', 'StreamService', function($scope, StreamService) {

    $scope.service = StreamService;
    $scope.$watch('service.values', $scope.service.build_query_params, true);

}]);
<|endoftext|>


<commit_before>(function() {

	$('html').addClass('js');

	var currentID,
		currentPosition,
		currentDistance,
		currentLink,
		currentDirection,

		xNav = {

			config: {
				effect: 'toggle',
				speed:undefined,
				menuItems: undefined,
				contentContainer: undefined,
				defaultLink: 1,
				navHelper: false,
				isVertical: false
			},

			init: function(config) {
				$.extend(this.config, config);

				var cf = xNav.config,
					defaultLinkNumber = cf.defaultLink - 1,
				 	defaultLink = cf.menuItems.eq(defaultLinkNumber).find('a');

				// Displaying the container of the content and the default set of data.
				cf.contentContainer.show();
				cf.contentContainer.find('div').eq(defaultLinkNumber).show();
				currentID = defaultLink.attr('href');
				defaultLink.css('cursor','default');

				// Checking to see if the navHelper is on and setting the direction.
				if (cf.navHelper === true) {
					cf.isVertical === true ? currentDirection = 'top' : currentDirection = 'left';

					// Setting the data of the default set of data so the navHelper knows where it needs to go upon initialization.
					xNav.setData(xNav.config.menuItems.eq(defaultLinkNumber));
					xNav.helper.set();
					xNav.helper.control();
				}

				(this.config.menuItems).on('click', 'a', this.checkStatus);
			},

			setData: function(a) {
				currentLink = a;
				currentPosition = currentLink.offset();
				// Checks whether this is a horizontal or vertical menu and then assigns currentDistance either the height or the width of the element.
				currentDirection === 'top' ? currentDistance = currentLink.height() : currentDistance = currentLink.width();
			},

			checkStatus: function(e) {
				// Keeps the Address Bar from showing "#contentid" everytime we click a link in the menu.
				e.preventDefault();

				xNav.setData($(this));

				// Check used to see if we're clicking the same link twice. If we are, don't reload.
				if ( currentID === undefined || currentID !==  currentLink.attr('href') ) {
					xNav.newContentRequest();
				};
			},

			newContentRequest: function() {
				var cf = xNav.config,
					speed = cf.speed,
					effect = cf.effect;

				// We use the ID of the corresponding content set in the link's href attribute so we can use single page sites with JavaScript disabled (always build your sites with JS disabled first).
				currentID = currentLink.attr('href');
				currentLink.closest('li').siblings().find('a').css('cursor','auto');
				currentLink.css('cursor','default');

				cf.contentContainer.children('div')
					.siblings()
					.hide();

				cf.contentContainer
					.children('div' + currentID)[effect](speed);

				if (xNav.config.navHelper === true) {
					xNav.helper.control();
				}

			},

			helper: {

				set: function() {
					$('

')
						.insertBefore(xNav.config.contentContainer)
						.show();
				},

				control: function() {
					$('#indicator')
						.css(currentDirection, currentPosition[currentDirection] + (currentDistance / 2.5));
					}
			}
		};

		xNav.init({
			effect: 'fadeToggle',
			menuItems:$('#menu li'),
			contentContainer: $('#content-pages'),
			defaultLink: 1,
			navHelper: true,
			isVertical: false
		});

})();<commit_msg>Implemented animation. Needs some cleanup.<commit_after>(function() {

	$('html').addClass('js');

	var currentID,
		currentPosition,
		currentDistance,
		currentLink,
		currentDirection,

		xNav = {

			config: {
				effect: 'toggle',
				speed:undefined,
				menuItems: undefined,
				contentContainer: undefined,
				defaultLink: 1,
				navHelper: false,
				animatedNavHelper: false,
				animatedNavHelperSpeed: 500,
				isVertical: false
			},

			init: function(config) {
				$.extend(this.config, config);

				var cf = xNav.config,
					defaultLinkNumber = cf.defaultLink - 1,
				 	defaultLink = cf.menuItems.eq(defaultLinkNumber).find('a');

				// Displaying the container of the content and the default set of data.
				cf.contentContainer.show();
				cf.contentContainer.find('div').eq(defaultLinkNumber).show();
				currentID = defaultLink.attr('href');
				defaultLink.css('cursor','default');

				// Checking to see if the navHelper is on and setting the direction.
				if (cf.navHelper === true) {
					cf.isVertical === true ? currentDirection = 'top' : currentDirection = 'left';

					// Setting the data of the default set of data so the navHelper knows where it needs to go upon initialization.
					xNav.setData(xNav.config.menuItems.eq(defaultLinkNumber));
					xNav.helper.set();
				}

				(this.config.menuItems).on('click', 'a', this.checkStatus);
			},

			setData: function(a) {
				currentLink = a;
				currentPosition = currentLink.offset();
				// Checks whether this is a horizontal or vertical menu and then assigns currentDistance either the height or the width of the element.
				currentDirection === 'top' ? currentDistance = currentLink.height() : currentDistance = currentLink.width();
			},

			checkStatus: function(e) {
				// Keeps the Address Bar from showing "#contentid" everytime we click a link in the menu.
				e.preventDefault();

				xNav.setData($(this));

				// Check used to see if we're clicking the same link twice. If we are, don't reload.
				if ( currentID === undefined || currentID !==  currentLink.attr('href') ) {
					xNav.newContentRequest();
				};
			},

			newContentRequest: function() {
				var cf = xNav.config,
					speed = cf.speed,
					effect = cf.effect;

				// We use the ID of the corresponding content set in the link's href attribute so we can use single page sites with JavaScript disabled (always build your sites with JS disabled first).
				currentID = currentLink.attr('href');
				currentLink.closest('li').siblings().find('a').css('cursor','auto');
				currentLink.css('cursor','default');

				cf.contentContainer.children('div')
					.siblings()
					.hide();

				cf.contentContainer
					.children('div' + currentID)[effect](speed);

				if (xNav.config.navHelper === true) {
					xNav.helper.control();
				}

			},

			helper: {

				set: function() {
					$('

')
						.insertBefore(xNav.config.contentContainer)
							.show().css(currentDirection, currentPosition[currentDirection] + (currentDistance / 2.5));
				},

				control: function() {
					if ( xNav.config.animatedNavHelper === true ) {
						if ( currentDirection === 'top' ) {
							$('#indicator')
								.animate({
									top: currentPosition[currentDirection] + (currentDistance / 2.5)
								}, [xNav.config.animatedNavHelper]);
							} else {
							$('#indicator')
								.animate({
									left: currentPosition[currentDirection] + (currentDistance / 2.5)
								}, [xNav.config.animatedNavHelper]);
							}
						} else {
						$('#indicator')
							.css(currentDirection, currentPosition[currentDirection] + (currentDistance / 2.5));
						}
					}
				}
			};

		xNav.init({
			effect: 'fadeToggle',
			menuItems:$('#menu li'),
			contentContainer: $('#content-pages'),
			defaultLink: 1,
			navHelper: true,
			animatedNavHelper: true,
			isVertical: false
		});

})();<|endoftext|>


<commit_before>/*!
 * zoom.js 0.2
 * http://lab.hakim.se/zoom-js
 * MIT licensed
 *
 * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se
 */
var zoom = (function(){

	// The current zoom level (scale)
	var level = 1;

	// The current mouse position, used for panning
	var mouseX = 0,
		mouseY = 0;

	// Timeout before pan is activated
	var panEngageTimeout = -1,
		panUpdateInterval = -1;

	// Check for transform support so that we can fallback otherwise
	var supportsTransforms = 	'WebkitTransform' in document.body.style ||
								'MozTransform' in document.body.style ||
								'msTransform' in document.body.style ||
								'OTransform' in document.body.style ||
								'transform' in document.body.style;

	if( supportsTransforms ) {
		// The easing that will be applied when we zoom in/out
		document.body.style.transition = 'transform 0.8s ease';
		document.body.style.OTransition = '-o-transform 0.8s ease';
		document.body.style.msTransition = '-ms-transform 0.8s ease';
		document.body.style.MozTransition = '-moz-transform 0.8s ease';
		document.body.style.WebkitTransition = '-webkit-transform 0.8s ease';
	}

	// Zoom out if the user hits escape
	document.addEventListener( 'keyup', function( event ) {
		if( level !== 1 && event.keyCode === 27 ) {
			zoom.out();
		}
	} );

	// Monitor mouse movement for panning
	document.addEventListener( 'mousemove', function( event ) {
		if( level !== 1 ) {
			mouseX = event.clientX;
			mouseY = event.clientY;
		}
	} );

	/**
	 * Applies the CSS required to zoom in, prioritizes use of CSS3
	 * transforms but falls back on zoom for IE.
	 *
	 * @param {Number} pageOffsetX
	 * @param {Number} pageOffsetY
	 * @param {Number} elementOffsetX
	 * @param {Number} elementOffsetY
	 * @param {Number} scale
	 */
	function magnify( pageOffsetX, pageOffsetY, elementOffsetX, elementOffsetY, scale ) {

		if( supportsTransforms ) {
			var origin = pageOffsetX +'px '+ pageOffsetY +'px',
				transform = 'translate('+ -elementOffsetX +'px,'+ -elementOffsetY +'px) scale('+ scale +')';

			document.body.style.transformOrigin = origin;
			document.body.style.OTransformOrigin = origin;
			document.body.style.msTransformOrigin = origin;
			document.body.style.MozTransformOrigin = origin;
			document.body.style.WebkitTransformOrigin = origin;

			document.body.style.transform = transform;
			document.body.style.OTransform = transform;
			document.body.style.msTransform = transform;
			document.body.style.MozTransform = transform;
			document.body.style.WebkitTransform = transform;
		}
		else {
			// Reset all values
			if( scale === 1 ) {
				document.body.style.position = '';
				document.body.style.left = '';
				document.body.style.top = '';
				document.body.style.width = '';
				document.body.style.height = '';
				document.body.style.zoom = '';
			}
			// Apply scale
			else {
				document.body.style.position = 'relative';
				document.body.style.left = ( - ( pageOffsetX + elementOffsetX ) / scale ) + 'px';
				document.body.style.top = ( - ( pageOffsetY + elementOffsetY ) / scale ) + 'px';
				document.body.style.width = ( scale * 100 ) + '%';
				document.body.style.height = ( scale * 100 ) + '%';
				document.body.style.zoom = scale;
			}
		}

		level = scale;
	}

	/**
	 * Pan the document when the mosue cursor approaches the edges
	 * of the window.
	 */
	function pan() {
		var range = 0.12,
			rangeX = window.innerWidth * range,
			rangeY = window.innerHeight * range,
			scrollOffset = getScrollOffset();

		// Up
		if( mouseY < rangeY ) {
			window.scroll( scrollOffset.x, scrollOffset.y - ( 1 - ( mouseY / rangeY ) ) * ( 14 / level ) );
		}
		// Down
		else if( mouseY > window.innerHeight - rangeY ) {
			window.scroll( scrollOffset.x, scrollOffset.y + ( 1 - ( window.innerHeight - mouseY ) / rangeY ) * ( 14 / level ) );
		}

		// Left
		if( mouseX < rangeX ) {
			window.scroll( scrollOffset.x - ( 1 - ( mouseX / rangeX ) ) * ( 14 / level ), scrollOffset.y );
		}
		// Right
		else if( mouseX > window.innerWidth - rangeX ) {
			window.scroll( scrollOffset.x + ( 1 - ( window.innerWidth - mouseX ) / rangeX ) * ( 14 / level ), scrollOffset.y );
		}
	}

	function getScrollOffset() {
		return {
			x: window.scrollX !== undefined ? window.scrollX : window.pageXOffset,
			y: window.scrollY !== undefined ? window.scrollY : window.pageXYffset
		}
	}

	return {
		/**
		 * Zooms in on either a rectangle or HTML element.
		 *
		 * @param {Object} options
		 *   - element: HTML element to zoom in on
		 *   OR
		 *   - x/y: coordinates in non-transformed space to zoom in on
		 *   - width/height: the portion of the screen to zoom in on
		 *   - scale: can be used instead of width/height to explicitly set scale
		 */
		to: function( options ) {
			// Due to an implementation limitation we can't zoom in
			// to another element without zooming out first
			if( level !== 1 ) {
				zoom.out();
			}
			else {
				options.x = options.x || 0;
				options.y = options.y || 0;

				// If an element is set, that takes precedence
				if( !!options.element ) {
					// Space around the zoomed in element to leave on screen
					var padding = 20;

					options.width = options.element.getBoundingClientRect().width + ( padding * 2 );
					options.height = options.element.getBoundingClientRect().height + ( padding * 2 );
					options.x = options.element.getBoundingClientRect().left - padding;
					options.y = options.element.getBoundingClientRect().top - padding;
				}

				// If width/height values are set, calculate scale from those values
				if( options.width !== undefined && options.height !== undefined ) {
					options.scale = Math.max( Math.min( window.innerWidth / options.width, window.innerHeight / options.height ), 1 );
				}

				if( options.scale > 1 ) {
					options.x *= options.scale;
					options.y *= options.scale;

					var scrollOffset = getScrollOffset();

					magnify( scrollOffset.x, scrollOffset.y, options.x, options.y, options.scale );

					if( options.pan !== false ) {

						// Wait with engaging panning as it may conflict with the
						// zoom transition
						panEngageTimeout = setTimeout( function() {
							panUpdateInterval = setInterval( pan, 1000 / 60 );
						}, 800 );

					}
				}
			}
		},

		/**
		 * Resets the document zoom state to its default.
		 */
		out: function() {
			clearTimeout( panEngageTimeout );
			clearInterval( panUpdateInterval );

			var scrollOffset = getScrollOffset();

			magnify( scrollOffset.x, scrollOffset.y, 0, 0, 1 );

			level = 1;
		},

		// Alias
		magnify: function( options ) { this.to( options ) },
		reset: function() { this.out() },

		zoomLevel: function() {
			return level;
		}
	}

})();

<commit_msg>center zoom viewport<commit_after>/*!
 * zoom.js 0.3
 * http://lab.hakim.se/zoom-js
 * MIT licensed
 *
 * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se
 */
var zoom = (function(){

	// The current zoom level (scale)
	var level = 1;

	// The current mouse position, used for panning
	var mouseX = 0,
		mouseY = 0;

	// Timeout before pan is activated
	var panEngageTimeout = -1,
		panUpdateInterval = -1;

	// Check for transform support so that we can fallback otherwise
	var supportsTransforms = 	'WebkitTransform' in document.body.style ||
								'MozTransform' in document.body.style ||
								'msTransform' in document.body.style ||
								'OTransform' in document.body.style ||
								'transform' in document.body.style;

	if( supportsTransforms ) {
		// The easing that will be applied when we zoom in/out
		document.body.style.transition = 'transform 0.8s ease';
		document.body.style.OTransition = '-o-transform 0.8s ease';
		document.body.style.msTransition = '-ms-transform 0.8s ease';
		document.body.style.MozTransition = '-moz-transform 0.8s ease';
		document.body.style.WebkitTransition = '-webkit-transform 0.8s ease';
	}

	// Zoom out if the user hits escape
	document.addEventListener( 'keyup', function( event ) {
		if( level !== 1 && event.keyCode === 27 ) {
			zoom.out();
		}
	} );

	// Monitor mouse movement for panning
	document.addEventListener( 'mousemove', function( event ) {
		if( level !== 1 ) {
			mouseX = event.clientX;
			mouseY = event.clientY;
		}
	} );

	/**
	 * Applies the CSS required to zoom in, prioritizes use of CSS3
	 * transforms but falls back on zoom for IE.
	 *
	 * @param {Number} x
	 * @param {Number} y
	 * @param {Number} width
	 * @param {Number} height
	 * @param {Number} scale
	 */
	function magnify( x, y, width, height, scale ) {

		var scrollOffset = getScrollOffset();

		x -= ( window.innerWidth - ( width * scale ) ) / 2;
		y -= ( window.innerHeight - ( height * scale ) ) / 2;

		if( supportsTransforms ) {
			// Reset
			if( scale === 1 ) {
				document.body.style.transform = '';
				document.body.style.OTransform = '';
				document.body.style.msTransform = '';
				document.body.style.MozTransform = '';
				document.body.style.WebkitTransform = '';
			}
			// Scale
			else {
				var origin = scrollOffset.x +'px '+ scrollOffset.y +'px',
					transform = 'translate('+ -x +'px,'+ -y +'px) scale('+ scale +')';

				document.body.style.transformOrigin = origin;
				document.body.style.OTransformOrigin = origin;
				document.body.style.msTransformOrigin = origin;
				document.body.style.MozTransformOrigin = origin;
				document.body.style.WebkitTransformOrigin = origin;

				document.body.style.transform = transform;
				document.body.style.OTransform = transform;
				document.body.style.msTransform = transform;
				document.body.style.MozTransform = transform;
				document.body.style.WebkitTransform = transform;
			}
		}
		else {
			// Reset
			if( scale === 1 ) {
				document.body.style.position = '';
				document.body.style.left = '';
				document.body.style.top = '';
				document.body.style.width = '';
				document.body.style.height = '';
				document.body.style.zoom = '';
			}
			// Scale
			else {
				document.body.style.position = 'relative';
				document.body.style.left = ( - ( scrollOffset.x + x ) / scale ) + 'px';
				document.body.style.top = ( - ( scrollOffset.y + y ) / scale ) + 'px';
				document.body.style.width = ( scale * 100 ) + '%';
				document.body.style.height = ( scale * 100 ) + '%';
				document.body.style.zoom = scale;
			}
		}

		level = scale;
	}

	/**
	 * Pan the document when the mosue cursor approaches the edges
	 * of the window.
	 */
	function pan() {
		var range = 0.12,
			rangeX = window.innerWidth * range,
			rangeY = window.innerHeight * range,
			scrollOffset = getScrollOffset();

		// Up
		if( mouseY < rangeY ) {
			window.scroll( scrollOffset.x, scrollOffset.y - ( 1 - ( mouseY / rangeY ) ) * ( 14 / level ) );
		}
		// Down
		else if( mouseY > window.innerHeight - rangeY ) {
			window.scroll( scrollOffset.x, scrollOffset.y + ( 1 - ( window.innerHeight - mouseY ) / rangeY ) * ( 14 / level ) );
		}

		// Left
		if( mouseX < rangeX ) {
			window.scroll( scrollOffset.x - ( 1 - ( mouseX / rangeX ) ) * ( 14 / level ), scrollOffset.y );
		}
		// Right
		else if( mouseX > window.innerWidth - rangeX ) {
			window.scroll( scrollOffset.x + ( 1 - ( window.innerWidth - mouseX ) / rangeX ) * ( 14 / level ), scrollOffset.y );
		}
	}

	function getScrollOffset() {
		return {
			x: window.scrollX !== undefined ? window.scrollX : window.pageXOffset,
			y: window.scrollY !== undefined ? window.scrollY : window.pageXYffset
		}
	}

	return {
		/**
		 * Zooms in on either a rectangle or HTML element.
		 *
		 * @param {Object} options
		 *   - element: HTML element to zoom in on
		 *   OR
		 *   - x/y: coordinates in non-transformed space to zoom in on
		 *   - width/height: the portion of the screen to zoom in on
		 *   - scale: can be used instead of width/height to explicitly set scale
		 */
		to: function( options ) {
			// Due to an implementation limitation we can't zoom in
			// to another element without zooming out first
			if( level !== 1 ) {
				zoom.out();
			}
			else {
				options.x = options.x || 0;
				options.y = options.y || 0;

				// If an element is set, that takes precedence
				if( !!options.element ) {
					// Space around the zoomed in element to leave on screen
					var padding = 20;
					var bounds = options.element.getBoundingClientRect();

					options.x = bounds.left - padding;
					options.y = bounds.top - padding;
					options.width = bounds.width + ( padding * 2 );
					options.height = bounds.height + ( padding * 2 );
				}

				// If width/height values are set, calculate scale from those values
				if( options.width !== undefined && options.height !== undefined ) {
					options.scale = Math.max( Math.min( window.innerWidth / options.width, window.innerHeight / options.height ), 1 );
				}

				if( options.scale > 1 ) {
					options.x *= options.scale;
					options.y *= options.scale;

					magnify( options.x, options.y, options.width || 1, options.height || 1, options.scale );

					if( options.pan !== false ) {

						// Wait with engaging panning as it may conflict with the
						// zoom transition
						panEngageTimeout = setTimeout( function() {
							panUpdateInterval = setInterval( pan, 1000 / 60 );
						}, 800 );

					}
				}
			}
		},

		/**
		 * Resets the document zoom state to its default.
		 */
		out: function() {
			clearTimeout( panEngageTimeout );
			clearInterval( panUpdateInterval );

			magnify( 0, 0, 1, 1, 1 );

			level = 1;
		},

		// Alias
		magnify: function( options ) { this.to( options ) },
		reset: function() { this.out() },

		zoomLevel: function() {
			return level;
		}
	}

})();

<|endoftext|>