/* PageController */
/* */

var PageController = (function(){
  var autoPlay;
  var autoPlayFlag = 0;
  var changePageCallbacks = [];
  var $ = jQuery;
  var historyQueue = [];
  var historyTimer= null;
  var lastTrackedPageName = "";

  /**
   * Handle calls to the ajaxHistory method so that updating the browser
   * history for page and article views runs outside the normal page view.
   *
   * The historyQueue is added to in the updateBrowserHistory method, which
   * is called when a page or article is viewed.  This method then runs on
   * a timeout loop to update the ajaxHistory until the history queue is empty
   * or the timeout is cancelled.  The timeout can be cancelled when a new page is
   * added to the history queue in updateBrowserHistory.  This method will then
   * be called by updateBrowserHistory to restart the process.
   */
  function handleHistoryUpdate() {
      if (historyQueue.length > 0) {
          $.ajaxHistory.update(historyQueue.shift());
          historyTimer = setTimeout(handleHistoryUpdate, 0);
      }
  };

  /**
   * This method is used to calculate the page numbers displayed on the UI
   * if, for some reason, the folios/pages are not included in the div
   * definitions downloaded from the server.
   *
   * @param pageNumber The page number asked for
   * @return An hash: list of pages and list of folios that are displayed
   */
  function calculateDisplayedPages(pageNumber) {
      var pages = [];
      var folios = [];
      if (PageModel.pageMode == 1) {
          pages.push(pageNumber);
          folios.push(PageModel.pages[pageNumber]);
      } else if (!PageModel.twoPageCover && pageNumber == PageModel.firstPage) {
          pages.push(pageNumber);
          folios.push(PageModel.pages[pageNumber]);
      } else {
          if ((PageModel.twoPageCover && pageNumber % 2 === 0) ||
                  (!PageModel.twoPageCover && pageNumber % 2 !== 0)) {
              if (pageNumber > 1) {
                  pages.push(pageNumber - 1);
                  folios.push(PageModel.pages[pageNumber - 1]);
              }
              pages.push(pageNumber);
              folios.push(PageModel.pages[pageNumber]);
          } else {
              pages.push(pageNumber);
              folios.push(PageModel.pages[pageNumber]);
              if ((pageNumber + 1) <= PageModel.lastPage) {
                  pages.push(pageNumber + 1);
                  folios.push(PageModel.pages[pageNumber + 1]);
              }
          }
      }
      return {"pages":pages, "folios":folios};
  };

  return {
    currentPage : 1,
    nextPage : 1,
    previousPage : 1,
    folioLabels : {
        "Cover":"Front Cover",
        "IFC":"Inside Front Cover",
        "BC":"Back Cover",
        "IBC":"Inside Back Cover"
    },



    /**
     * Use friendly name for the displayed page name instead of the
     * short names from the database.
     * Note: Only add the "Page" designation to the name if
     * addPageName is true
     * 
     * @param folio Folio name
     * @param addPageName Prepend "Page " to label if true
     * @return friendly label for tracking and page name
     */
    buildFolioLabel : function(folio, addPageName) {
        addPageName = addPageName ? addPageName : false;
        var trackLabel = ""+folio;
        if (typeof PageController.folioLabels[folio] != "undefined") {
            trackLabel = PageController.folioLabels[folio];
        } else if (folio.indexOf('Insert') === 0) {
            trackLabel = folio;
        } else if (addPageName === true) {
            trackLabel = "Page " + folio;
        }
        return trackLabel;
    },

    /**
     * Called upon initial load of the Webreader directly from documentHtmlFooter.ftl
     * after everything has been initialized.  This sets up the page and calls for the
     * first/called for page to be loaded.  It then initializes the article viewer and
     * the ad viewer.
     * 
     * As part of loading the first page in the view, it checks to see if the access to
     * the issue is lookinside.  If it is, it checks to see what pages are available to
     * the lookinside subscriber id.  If the page called for is within the lookinside
     * range, then it continues on.  Otherwise, it calls the first page in the lookinside
     * range and pushes the called for page onto the requiredPages stack to load after
     * the lookinside page.  The effect of this is to open the first lookinside page in
     * the UI, then try to jump to the called for page, but display the lookinside login
     * dialog.since the user does not have access to that page.
     * 
     * @param the page (reading order) number to display
     * 
     */
    initPages : function(page) {
      PageController.currentPage = page || 1;
      PageModel.normalPagesDiv.empty();
      PageView.fitWindow();

      // Check if lookInside and whether page called for is in lookInside range
      var loadCurrentPage = true;
      if (DocumentProperties.lookInsidePages != "") {
          var liFolios = DocumentProperties.lookInsidePages.split(",");
          var firstLiPage = parseInt(PageModel.folios[liFolios[0].toLowerCase()], 10);
          var folioPage = PageModel.pages[PageController.currentPage].toLowerCase();
          var pageInRange = false;
          for (var index in liFolios) {
              if (liFolios[index].toLowerCase() == folioPage) {
                  pageInRange = true;
                  break;
              }
          }
          if (pageInRange === false) {
              // page not in lookinside range.  Call page *after* lookinside page
              PageView.requiredPages.push(PageController.currentPage);
              PageModel.getPages(firstLiPage, PageView.initPage, true);
              PageController.currentPage = firstLiPage;
              loadCurrentPage = false;
          }
      }
      if (loadCurrentPage === true) {
          // calling PageView.initPage, set to true to ensure initPage called
          PageModel.getPages(PageController.currentPage, PageView.initPage, true);
      }

      if(document.location.hash.indexOf("article_id")==-1){
          this.updateBrowserHistory();
      }
      if(PageModel.zoomIndex >= (PageModel.zoomLevels.length - 1)){
          PageModel.documentContainerDiv.addClass('max_zoom');
      }

      // Initialize ArticleViewer and start preload of articles
      if (ArticleViewer) {
          ArticleViewer.init();
      }

      // wrangle antisocial adserver ads
      var params = $("object param[name='wmode']");
      $(params).attr('value','transparent');
      $("embed").attr('wmode','transparent');
      $(params).each(function(){
        var obj = $(this).parent();
        var p = $(obj).parent();
        $(obj).wrap("<div></div>");
        var h = $(obj).parent().html();
        h = h.replace(/value="window"/i,'value="transparent"');
        $(obj).remove();
        $(p).append(h);
      });
      if ($("#leaderboard").children().length > 0) {
        $("#leaderboard").show();
      }
      if ($("#skyscraper").children().length > 0) {
        $("#skyscraper").css('visibility','visible');
      } else {
        $("#skyscraper").css('visibility','hidden');
      }
    },

    initPageStates : function() {
    },

    addChangePageCallback : function(callback) {
      // do not add duplicates
      for (var x in changePageCallbacks) {
        if (callback == changePageCallbacks[x]) {
          return;
        }
      }
      changePageCallbacks.push(callback);
    },

    clearChangePageCallbacks : function() {
      if (changePageCallbacks.length) {
        changePageCallbacks = [];
      }
    },

    applyChangePageCallbacks : function() {
      for (var x in changePageCallbacks) {
        if (typeof changePageCallbacks[x] == "string") {
          eval(changePageCallbacks[x]);
        } else if (typeof changePageCallbacks[x] == "function") {
          changePageCallbacks[x]();
        }
      }
      PageController.clearChangePageCallbacks();
    },

    handleNextPage : function() {
        clearTimeout(window.buttonFlashTimer);
        // TODO: Fix to get correct prev/next page list from server to
        // handle gatefold.
        // check if next page is stored
//      var nextPage=PageModel.normalPagesFolioDiv().attr('nextPage');
        var nextPage = null;
        // just in case... currentPage must be an integer
        PageController.currentPage = parseInt(PageController.currentPage, 10);
        if (nextPage && parseInt(nextPage,10)) {
          PageController.currentPage = parseInt(nextPage,10);
        } else if (PageController.currentPage == 1) {
          // first page could be a singleton, even in 2 page mode
          PageController.currentPage += PageModel.normalPagesPageDivs().size();
        } else {
          PageController.currentPage += PageModel.pageMode;
        }
        if (PageController.currentPage >= PageModel.lastPage) {
          PageController.currentPage = PageModel.lastPage;
        }
        PageModel.getPages(PageController.currentPage, PageView.changePage);
        handleNavigationButtons(PageController.currentPage);
    },

    handlePrevPage : function() {
        clearTimeout(window.buttonFlashTimer);
        // TODO: Fix to get correct prev/next page list from server to
        // handle gatefold.
        // check if previous page is stored
//        var prevPage = PageModel.normalPagesFolioDiv().attr('prevPage');
        var prevPage = null;
        // just in case... currentPage must be an integer
        PageController.currentPage = parseInt(PageController.currentPage, 10);
        if (prevPage && parseInt(prevPage, 10)) {
          PageController.currentPage = parseInt(prevPage, 10);
        }else {
          PageController.currentPage -= PageModel.pageMode;
        }
        if (PageController.currentPage <= 0) {
          PageController.currentPage = 1;
        }
        PageModel.getPages(PageController.currentPage, PageView.changePage);
        handleNavigationButtons(PageController.currentPage);
    },

    getPageByFolio: function(folio){
        folio = folio ? folio.toString().toLowerCase() : "";
        if (PageModel.folios.hasOwnProperty(folio)) {
            return PageModel.folios[folio];
        }

        return false;
    },

    handleGoToFolio : function(folio) {
        var page = PageController.getPageByFolio(folio);
        if(!isNaN(page)){
          PageController.handleGoToPage(page);
        }
    },

    handleGoToFirstPage : function() {
        var page=1;
        handleNavigationButtons(page);
        // just in case... currentPage must be an integer
        PageController.currentPage = parseInt(PageController.currentPage, 10);
        if (PageController.currentPage == parseInt(page, 10)){
            return;
        }
        PageController.currentPage = parseInt(page, 10);
        if(PageController.currentPage <= 1) {
            PageController.currentPage = 1;
        }
        if (PageController.currentPage >= PageModel.lastPage) {
            PageController.currentPage = PageModel.lastPage;
        }
        PageModel.getPages(PageController.currentPage, PageView.changePage);
    },

    handleGoToLastPage : function() {
        handleNavigationButtons(PageModel.lastPage);
        // just in case... currentPage must be an integer
        PageController.currentPage = parseInt(PageController.currentPage, 10);
        if (PageController.currentPage == parseInt(PageModel.lastPage, 10)){
            return;
        }
        PageController.currentPage = parseInt(PageModel.lastPage, 10);
        if(PageController.currentPage <= 1) {
            PageController.currentPage = 1;
        }
        if (PageController.currentPage >= PageModel.lastPage) {
            PageController.currentPage = PageModel.lastPage;
        }
        PageModel.getPages(PageController.currentPage,PageView.changePage);
    },

    //DT-4881:Handle Page-Flag selection
    handlePageFlagSelection : function(page,srcElem) {
          PageController.handleGoToPage(page);
       var pgFlag= $(srcElem).parent();
       $('ol.tabs li').removeClass('pageFlagSelected');
          $(pgFlag).addClass('pageFlagSelected');
    },

    handleGoToPage : function(page) {
       //DT-4743: Return to 'Fit Page' when switching to another page
       //DS-5038: For now, disable this fix until we can decide the right thing to do
//        $("#fit_view").click();
        handleNavigationButtons(page);
        // just in case... currentPage must be an integer
        PageController.currentPage = parseInt(PageController.currentPage, 10);
        if (PageController.currentPage == parseInt(page, 10)){
          return;
        }
        PageController.currentPage = parseInt(page, 10);
        if(PageController.currentPage <= 1) {
            PageController.currentPage = 1;
        }
        if (PageController.currentPage >= PageModel.lastPage) {
            PageController.currentPage = PageModel.lastPage;
        }
        PageModel.getPages(PageController.currentPage,PageView.changePage);
        if(typeof ArticleViewer != "undefined" && ArticleViewer.action_catalyst===false){
            ArticleViewer.hide();
        }
    },

    //(DT-4380) Stop AutoPlay
    stopAutoPlay : function() {
      autoPlayFlag = 0;
      window.clearInterval(autoPlay);
    },

    //(DT-4380) Start AutoPlay
    startAutoPlay : function(duration) {
      autoPlayFlag = 1;
      autoPlay = self.setInterval('PageController.handleAutoPlay()',parseInt(duration, 10));
    },

    //(DT-4380) Run AutoPlay - After last page, it goes back to the first page.
    handleAutoPlay : function() {
      if (PageController.currentPage == PageModel.lastPage) {
          this.handleGoToPage(1);
      } else {
        this.handleNextPage();
      }
    },

    handleZoomIn : function(e) {
        if (PageModel.zoomIndex < (PageModel.zoomLevels.length - 1)) {
            PageView.saveZoomPosition(e.pageX,e.pageY);
            PageView.zoomOutDrawerSize = Drawer.View.getCurrentSize();
            if(PageView.closeDrawerOnZoom &&
                    (PageView.zoomOutDrawerSize != Constants.closedDrawer)){
                Drawer.View.adjustWidth(Constants.closedDrawer,50);
            }
            PageView.isZoomedIn(true);
            if(Offline.isOffline()){
                PageView.zoomChange(PageModel.zoomIndexOf(Offline.getZoomInLevel()));
            }else{
	         /*
	           DS-3699:Calculating required Zoom Index, when user clicks to 'Zoom':
	           -Find  current Zoom Factor.
	           -Find Zoom Index which is closer to the double the value of current ZoomFactor.
	           -Return the Closest Zoom Index Or Last (max) Zoom Index available.
	         */
                var zoomFactorDiff =[];
                var finalZoomIndex = 0;
                var zoomFactorValue = PageModel.zoomLevels[PageModel.zoomIndex].zoom * 2;
                for (var i=PageModel.zoomIndex;i<=PageModel.zoomLevels.length - 1;i++){
                   if (PageModel.zoomLevels[i].zoom >= zoomFactorValue) {
                     zoomFactorDiff[i] = PageModel.zoomLevels[i].zoom  - zoomFactorValue;
                  } else {
                     zoomFactorDiff[i] = zoomFactorValue - PageModel.zoomLevels[i].zoom;
                  }
                  if ((i > 0) && (zoomFactorDiff[i] > zoomFactorDiff[i-1])) {
                     finalZoomIndex = i - 1;
                     break;
                   }
                   if (i == (PageModel.zoomLevels.length - 1)) {
                           finalZoomIndex = PageModel.zoomLevels.length - 1;
                           break;
                   }
                }
                PageView.zoomChange(finalZoomIndex);
                Navbar.ZoomControls.update();
            }
        }
    },

    handleZoomOut : function() {
        if (PageView.zoomOutDrawerSize > Drawer.View.getCurrentSize()) {
            Drawer.View.adjustWidth(PageView.zoomOutDrawerSize,50);
        }
        PageView.isZoomedIn(false);
        if(Offline.isOffline()){
            PageView.zoomChange(PageModel.zoomIndexOf(Offline.getZoomLevel()));
        } else {
            PageView.zoomChange(PageModel.preferredZoomIndex());
            Navbar.ZoomControls.update();
        }
    },

    handleTogglePageMode : function(e) {
        e.preventDefault();
        e.stopPropagation();
        ViewHelper.closeAllDialogs();
        PageView.togglePageMode();
    },

    /**
     * Handle updating browser history.  The method will create the proper
     * hash to pass to the browser history.  If nothing is passed in, it
     * is assumed that a page is being viewed, and a page hash will
     * be generated for storage in the browser history.
     * Otherwise, whatever is passed in will be stored.
     *
     * @param pageHash hash value to store in browser history
     */
    updateBrowserHistory : function(pageHash) {
        pageHash = pageHash ? pageHash : null;
        if (typeof ArticleViewer == "undefined" || ArticleViewer.active === false) {
            pageHash = pageHash ? pageHash : 'pg'+PageController.currentPage;
            if (!(PageController.currentPage && pageHash != location.hash.replace('#', ''))) {
                pageHash = null;
            }
        }
        if (pageHash !== null) {
            // if the handleHistoryUpdate timer is running,
            // cancel it so we don't start another.
            if (historyTimer) {
                clearTimeout(historyTimer);
            }
            historyQueue.push(pageHash);
            handleHistoryUpdate();
        }
    },

    /**
     * Create the page URL and page title to pass to trackers.
     * We split the url into its component pieces using the
     * Javascript location property.
     * The query string is then "cleaned" of all parameters we
     * don't want, including "folio=" and "pm=".  This list comes
     * from Tracking in Tracker.trackRemoveParams.
     * Note: The list of parameters to be cleaned can be added to
     * with a custom override - see documentSetup.ftl.
     * Then, we add back the parameters we want to track, currently
     * only "folio=" and "pm=".
     * Call tracking in timeout so that if tracking sites take a
     * long time to respond, it doesn't hold up page processing.
     * 
     * @param page The page number to track
     * @param pageId The id of the page div to get information from
     */
    trackPage : function(page, pageId) {
        if(typeof Tracker !== undefined){
            var displayedPages = PageController.getDisplayedPages(page, pageId);
            if (PageView.shortTitle === '') {
                PageView.shortTitle = document.title;
            }
            if (lastTrackedPageName === "") {
                lastTrackedPageName = document.referrer;
            }
            var pageNumbers = '';
            var numFolios = displayedPages["folios"].length;
            // add left page number...
            if (numFolios > 0) {
                pageNumbers += ' - ' + PageController.buildFolioLabel(displayedPages["folios"][0], true);
            }
            // ...and right page number if more than one page
            if (numFolios > 1) {
                pageNumbers += '-' + PageController.buildFolioLabel(displayedPages["folios"][numFolios - 1]);
            }
            // Setting document.title changes the title on the browser window.
            // Setting $("head title").html doesn't, but it seems to freeze up IE
            // document.title = pageNumbers;
            // override OpenTracker title
            document.title = $.unescapifyHTML(DocumentProperties.getTitle()) + pageNumbers;
            var trackTitle = PageView.shortTitle + pageNumbers;
            var shortTitle = PageView.shortTitle;

            // We are creating our own url to pass to the tracker, so cut out
            // anything we don't want or will add back later
            var trackingQuery = "";
            var params = "";
            // document.location.search returns url parameters starting with "?"
            if (document.location.search.length > 0) {
                params = document.location.search.substring(1).split("&");
            }
           // loop through and only keep if NOT a candidate for removal
            var trackRemoveParams = Tracker.getTrackRemoveParams();
            if (Tracker.getTrackingExclude().length > 0) {
                trackRemoveParams = trackRemoveParams.concat(Tracker.getTrackingExclude());
            }
            var addParam = true;
            for (var i = 0; i < params.length; i++) {
                addParam = true;
                for (var j = 0; j < trackRemoveParams.length; j++) {
                    if (params[i].indexOf(trackRemoveParams[j]) != -1) {
                        addParam = false;
                        break;
                    }
                }
                if (addParam === true) {
                    trackingQuery += params[i] + "&";
                }
            }
            // build href to pass to tracking
            // Note: We are ALWAYS adding a query string
            var href = document.location.protocol + "//" +
                    document.location.host +
                    document.location.pathname + "?";
            if (trackingQuery.length > 0) {
                href += trackingQuery;
            }
            href += "folio=" + displayedPages.folios[0] +
                    "&pm=" + PageModel.pageMode;

            var trackingOptions = {
                    "category":"page_viewed",
                    "lochref":document.location.href,
                    "pageName":href,
                    "lastPageName":lastTrackedPageName,
                    "folios":displayedPages.folios,
                    "pageMode":PageModel.pageMode,
                    "title":trackTitle,
                    "shortTitle":shortTitle
            };
            lastTrackedPageName = href;
            // Call to track page in a timeout, so we don't worry how long it takes
            setTimeout(function() {
//                Console.log(trackingOptions.category + "<br>" + trackingOptions.title +  "<br>" + trackingOptions.pageName + "<br>" + trackingOptions.lastPageName);
                Tracker.trackPage(trackingOptions);
            },0);
        }
    },

    getDisplayedPages : function(page, pageId) {
        pageId = pageId ? pageId :
            "p"+page+"z"+PageModel.zoomLevel+"pm"+PageModel.pageMode;

        var displayedPages = {};
        displayedPages["folios"] = [];
        displayedPages["pages"] = [];

        $("div.page", "#"+pageId).each(function(){
            var folio = $(this).attr("folio");
            displayedPages["folios"].push(folio);
            displayedPages["pages"].push(PageModel.folios[folio]);
        });
        if (displayedPages["folios"].length === 0) {
            displayedPages = calculateDisplayedPages(page);
        }
        return displayedPages;
    }

  };
})();

/* */
/* PageModel */
/* */

var PageModel = (function(){
  var zoomInLevel=0;
  var onePageZoomLevel=0;
  var twoPageZoomLevel=0;
  var preferredZoom = 0;
  var fitViewIndex=0;
  var fitWidthIndex=0;
  var callback='';
  var mruSize=2;
  var autoPlayFlag = false;
  var autoPlayDuration = 5000;
  var $ = jQuery;
  var visPages = "|";
  var stackedPages = [];
  var pageRequest = [];
  var waitPageTimer = null;
  var waitEmpty = 0;


  /**
   * Calculates the page id and page url to ask for given the page
   * number.  If the page is already downloaded, just call the
   * callback function with the page number.
   * If the page has not been downloaded, push the page onto the
   * download stack.  This allows us to control the order in which
   * pages are downloaded from the server - most recently called
   * pages get downloaded first.
   * Next the actual downloader is called to download pages in the
   * download stack.
   * If a callback function is defined wait loop is called to wait
   * for the page to be downloaded and to callback to the UI to
   * display the page.
   *
   * @param page The page number to get
   * @param zoom The zoom level for page
   * @param fn_callback Callback function once page is downloaded
   * @param zoom_request If this is a zoom request
   */
  function getPagesImpl(page, zoom, fn_callback, zoom_request) {
    var is_zoom_request = zoom_request ? zoom_request : false;
    if(is_zoom_request && page != PageController.currentPage){
        //don't go fetching zoom pages that we aren't going to use
        return;
    }
    var pageId = "p"+page+"z"+zoom+"pm"+PageModel.pageMode;
    if (page > PageModel.lastPage) {
      return;
    } else if (PageModel.cachedPages.hasOwnProperty(pageId)) {
        PageModel.doCallback(fn_callback,[page]);
    } else {
      var path = location.pathname;
      if (path.substring(path.length-1) == '/') {
        path = path.substring(0,path.length-1);
      }
      var pageUrl =path+'/Page.action?pg='+page+'&pm='+PageModel.pageMode+'&z='+zoom+'&u1='+escape(PageModel.u1);
      if (PageController.currentPage == page) {
        stackedPages.push([pageId, pageUrl]);
      } else {
        stackedPages.unshift([pageId, pageUrl]);
      }
      downloadPages();
      if (typeof fn_callback == "function") {
        waitForPage(page, zoom, fn_callback, pageId);
      }
    }
  }

  /**
   * While downloadPages is getting page from server, this method will
   * call itself in a loop, checking every 1/4 second until the page is
   * ready.  If another method calls this method, the new call will replace
   * the original, ensuring that the last page called for is the one that
   * callsback to display in the UI.
   *
   * Error Checking:
   *    1.  Check to make sure page html is cached if Ajax request is complete.
   *        If not, then manually cache page html and do callback
   *    2.  If the page html is not available and nothing seems to be happening
   *        with download, restart the download.
   *
   * @param page The page we want to view
   * @param zoom Zoom value, just in case we need to restart download
   * @param fn_callback Callback function when we are ready
   * @param pageId Page id to check for cached page
   */
  function waitForPage(page, zoom, fn_callback, pageId) {
    if(waitPageTimer) {
      clearTimeout(waitPageTimer);
    }
    // note if process buffers are empty; nothing left to process
    var buffersEmpty = (pageRequest.length === 0 && stackedPages.length == 0);

    // page is ready, use it
    if (PageModel.cachedPages.hasOwnProperty(pageId)) {
      PageModel.doCallback(fn_callback, [page]);

    // something has gone wrong...
    // the page has downloaded but not been cached...
    // use the cached XMLHttpRequest to get the HTML and cache it
    } else if (pageRequest.length > 0 &&
            pageRequest[0] == pageId &&
            pageRequest[1].readyState == 4) {
      Console.warn("Page was not cached, cacheing manually; for pageId: " + pageId);
      cacheDownloadedPage(pageRequest[1].responseText);
      PageModel.doCallback(fn_callback, [page]);

    // something has gone HORRIBLY wrong...
    // we've waited through 4 cycles with empty buffers
    // try to restart download
    } else if (buffersEmpty && waitEmpty >= 4) {
      Console.warn("Page did not download, attempting to get the page again; for pageId: " + pageId);
      getPagesImpl(page, zoom, fn_callback);

    // nothing ready, wait some more...
    // update waiting on empty buffers
    } else {
      waitEmpty = buffersEmpty ? waitEmpty++ : 0;
      waitPageTimer = setTimeout(function(){
          waitForPage(page, zoom, fn_callback, pageId);
      },250);
    }
  }

  /**
   * Process pages in stackedPages, downloading them from the server.
   * This allows us to control page downloads, getting the most recently
   * added page first, as that is the one that we are most likely to need
   * ASAP.
   * If an Ajax download is in process, pageRequest will have a pointer to
   * the XMLHttpRequest.  If this is not empty when the method is called,
   * just exit.  This allows for a single instance of this method and only
   * one download to be in progress, so that it can control the order of
   * page downloads.
   * This also allows the getPages method to keep on calling this method
   * when it needs to get pages from the server without worrying about
   * starting multiple downloads. Once the download stack is empty, the
   * pageRequest is set to empty and the loop ends.
   * If a page is in the stack, go get it from the server and cache the
   * returned html.
   */
  function downloadPages() {
  // pageRequest should only have values while page is downloading
   if (pageRequest.length > 0) {
      return;
    }
    var processDownload = true;
    var getNext = true;
    var pageUrl = "";
    // loop through stackedPages looking for a new page to download
    // if all pages are downloaded, set getNext to false and quit.
    while (getNext) {
      if (stackedPages.length > 0) {
        var pageInfo = stackedPages.pop();
        pageUrl = pageInfo[1];
        if (PageModel.cachedPages.hasOwnProperty(pageInfo[0])===false) {
          getNext = false;
        }
      } else {  // exit
        getNext = false;
        processDownload = false;
        pageRequest = [];
      }
    }
    if (processDownload === true) {
        var cache = true;
        if (PageModel.force) {
          cache = false;
        }
        pageRequest[0] = pageInfo[0];
        pageRequest[1] = DataSwitch.get({
          url: pageUrl,
          cache: cache,
          success: function(html){
            cacheDownloadedPage(html);
          }
        });
    }
  }

  /**
   * Cache the page html downloaded from the server.  Once the
   * page is cached, set pageRequest to empty indicating that a new
   * Ajax request can be made and call to download more pages.
   * 
   * @param html The string of html from the server
   */
  function cacheDownloadedPage(html) {
    var idpos = html.indexOf(" id=\"")+5;
    var htmlPageId = html.substring(idpos,html.indexOf("\"",idpos));
    PageModel.cachePages(html, htmlPageId);
    pageRequest = [];
    downloadPages();
  }

  function showPageHighlights(html) {
    var page = html.match(' pg="[0-9]+')[0];
    page = page.substring(5);
    var left = $(".page[pg="+page+"]",PageModel.normalPagesDiv).css('left');
    PageModel.normalPagesFolioDiv().append(html);
    $(".highlights[pg="+page+"]",PageModel.normalPagesFolioDiv()).css('left',left);
  }

  function getHighlightHtml(page, highlights){
    var html = '<div class="highlights" pg="' + page + '">';
    $.each(highlights, function(){
        html += '<div align="left" class="pageHighlight" style="width:'+ this.w + 'px;';
        html += 'height:' + this.h + 'px;';
        html += 'left:' + this.x +'px;';
        html += 'top:' + this.y + 'px;"></div>';

    });
    html += '</div>';
    return html;
  }

  function buildFolios() {
    for (var page in PageModel.pages) {
      PageModel.folios[(PageModel.pages[page]).toLowerCase()] = page;
    }
  }

  return {
    emptyPages: [],
    force: false,
    documentContainerDiv : null,
    popupContainerDiv : null,
    normalPagesDiv : null,
    preloadedPagesDiv : null,
    pageMode : 2,
    initPageMode : 2,
    zoomLevel : 0,
    zoomIndex : 0,
    zoomInOffset : 4,
    viewMode : 'scan',
    u1 : '',
    firstPage : '',
    lastPage : '',
    twoPageCover : false,
    fullScreen : false,
    zoomLevels : [],
    cachedPages : {},
    pages : {},
    folios : {},

    clearVisPages: function() {
      visPages = "|";
    },
    addVisPage: function(pg) {
      visPages += pg + "|";
    },
    hasVisPage: function(pg) {
      if (visPages.indexOf("|"+pg+"|") >= 0) {
         return true;
      } else {
         return false;
      }
    },
    init : function() {
       PageModel.documentContainerDiv = $("#documentContainer");
       PageModel.popupContainerDiv = $("#popupContainer");
       PageModel.normalPagesDiv = $("#normalpages");
       PageModel.preloadedPagesDiv = $("#pages");
    },

    postInit : function() {
        buildFolios();
    },

    /**
     * External method to get the page to view.  Will call internal
     * method which actually returns the page, either from the cache
     * or from the server.
     *
     * Setting the value of lockPage to true should only be done for
     * callback functions that MUST be executed to complete some setup
     * routines.
     * Currently, lockPage should only be set to true for the following
     * callback functions:
     *     PageView.initPage(...)
     *
     * @param page The page to display
     * @param fn_callback Callback function to call once page is obtained
     * @param lockPage If a callback function MUST be called, set to true
     */
    getPages : function(page, fn_callback, lockPage) {
      if (PageView.pageLock === true) {
        return;
      }
      PageView.pageLock = lockPage ? lockPage : false;
      fn_callback = fn_callback ? fn_callback : '';
      getPagesImpl(page,this.zoomLevel, fn_callback);
    },

    normalPagesFolioDiv : function() {
      return $(".folio",PageModel.normalPagesDiv);
    },

    normalPagesPageDivs : function() {
      return $(".page",PageModel.normalPagesDiv);
    },

    normalPagesZoomPageDivs : function() {
      return $(".zoomed_folio > .page",PageModel.normalPagesDiv);
    },

    zoomPagesFolioDiv : function() {
      return $(".zoomed_folio",PageModel.normalPagesDiv);
    },

    getPopupcards : function(page,numPagesViewed) {
      var path = location.pathname;
      if (path.substring(path.length-1) == '/') path = path.substring(0,path.length-1);
      var pageUrl ='/Popup.action?pg='+page+'&pm='+PageModel.pageMode+'&z='+PageModel.zoomLevel+'&numPagesViewed='+numPagesViewed+'&u1='+escape(PageModel.u1);
        DataSwitch.get({
          url: path+pageUrl,
          success: function(html){
            //if a person is paging through quickly, return - even if they don't have access they'll eventually get access denied or lookinside
            var pgpos = html.indexOf(" pg=\"")+5;
            var pg = html.substring(pgpos,html.indexOf("\"",pgpos));
            if(!PageModel.hasVisPage(pg)){
                return;
            }
            var zoompos = html.indexOf(" z=\"")+4;
            var zoom = html.substring(zoompos,html.indexOf("\"",zoompos));
            // Compare popupcard zoom with volatile current zoomLevel
            if (zoom != PageModel.zoomLevel) {
              // watch for latency with zoom slider
              return;
            }
            var pages_offset = PageModel.normalPagesDiv.offset();
            var top = pages_offset.top;
            var left = pages_offset.left;
            PageModel.popupContainerDiv.css({'top':top+'px','left':left+'px'});
            PageModel.popupContainerDiv.append(html);
            initClosedPopups();
            $(".pagePopupcards",PageModel.popupContainerDiv).each(function(){
              var pg = $(this).attr('pg');
              var left = parseInt($(".page[pg='"+pg+"']",PageModel.documentContainerDiv).css('left'), 10);
              var w = $(".page[pg='"+pg+"']",PageModel.documentContainerDiv).width();
              if(!isNaN(left)){
                  $(this).css('left',left+'px').width(w);
              }
            });
            if (Offline.isOffline()) {
                $(".pagePopupcards[external=1]",PageModel.popupContainerDiv).each(function(){
                  $(this).addClass('hidden');
                });
            }
            if($(".look_inside_help").length > 0){
                PageView.showLookInsideHelp();
            }
          }
        });
    },

    getSearchHighlights : function(search, zoom) {

      $(".highlights", PageModel.normalPagesFolioDiv()).empty();
      var offline = Offline.isOffline();
      var page_mode = PageModel.pageMode;
      PageModel.normalPagesPageDivs().each(function() {
        var page = $(this).attr('pg');

        if(offline){
            var pageHighlights = Offline.getSearchHighlights(search,page,page_mode,zoom);
            var html = getHighlightHtml(page, pageHighlights);
            showPageHighlights(html);
        } else {
            var path = location.pathname;
            if (path.substring(path.length-1) == '/') path = path.substring(0,path.length-1);
            var pageUrl ='/Search_highlights.action?search='+escape(search)+'&pg='+page+'&pm='+page_mode+'&z='+zoom;
            DataSwitch.get({
                url: path+pageUrl,
                success: showPageHighlights
            });
        }
      });
    },

    /**
     * Add the html text from a JQuery element, DOM element or
     * html string to the page cache.
     * Only cache a page if the HTML is not empty.
     * 
     * @param element Either a JQuery element, DOM element or a string
     *     of html to store in the cachedPages object
     * @param pageid The id to use as the key for the cached page
     */
    cachePages : function(element, pageid) {
        if (element == null || pageid == null ||
                (typeof(element) != "object" && typeof(element) != "string")) {
            return;
        }
        if (typeof PageModel.cachedPages[pageid] == "undefined" ||
                PageModel.cachedPages[pageid].length == 0) {
            if (typeof(element) == "string" && element.length > 0) {
                PageModel.cachedPages[pageid] = element;
            } else if (typeof(element) == "object") {
                var elementHtml = $(element).outerHtml();
                if (elementHtml.length > 0) {
                    PageModel.cachedPages[pageid] = elementHtml;
                }
            }
        }
    },

    getCachedPage : function(pageid) {
        return $(PageModel.cachedPages[pageid]).filter(".folio").attr("id",pageid);
    },

    getPageMode : function() {
      return PageModel.pageMode;
    },

    setPageMode : function(pm) {
      PageModel.pageMode = pm;
    },

    getInitPageMode : function() {
      return PageModel.initPageMode;
    },

    setInitPageMode : function(pm) {
      PageModel.initPageMode = pm;
    },

    getZoomLevel : function() {
        return PageModel.zoomLevel;
    },

    getZoomIndex : function() {
      return PageModel.zoomIndex;
    },

    getU1 : function() {
        return PageModel.u1;
    },

    setU1 : function(u) {
        PageModel.u1 = u;
    },

    setZoomIndex : function(index) {
        var zoom_levels = PageModel.zoomLevels;
        if(parseInt(index, 10) >= 0 && parseInt(index, 10) < zoom_levels.length){
            PageModel.zoomIndex = index;
            PageModel.zoomLevel = zoom_levels[index]["zoom"];
            if(PageModel.zoomIndex == PageModel.fitViewZoomIndex() || PageModel.zoomIndex == PageModel.fitWidthZoomIndex()){
                PageView.setAutoZoom(true);
            }
            if(PageView.isZoomedIn()===false){
                PageModel.setZoomInOffset();
            }
        }
    },

    setZoomLevel : function(z) {
      $.each(PageModel.zoomLevels,function(i,zl_object){
        if(zl_object["zoom"] <= z || i===0){
          PageModel.zoomIndex = i;
          PageModel.zoomLevel = zl_object["zoom"];
          if(PageModel.zoomIndex == PageModel.fitViewZoomIndex() || PageModel.zoomIndex == PageModel.fitWidthZoomIndex()){
              PageView.setAutoZoom(true);
          }
          if(PageView.isZoomedIn()===false){
              PageModel.setZoomInOffset();
          }
        }
      });
    },

    setZoomLevelFromPreference : function(z_pref,z_default) {
      var pref_arr = z_pref.split("&");
      if(pref_arr.length && pref_arr.length==2){
        var math_abs = Math.abs;
        var zoom_levels = PageModel.zoomLevels;
        var first_zoom_width = parseInt(zoom_levels[0]["width"],10);
        var first_zoom_height = parseInt(zoom_levels[0]["height"],10);
        var doc_orientation = first_zoom_width > first_zoom_height ? "landscape" : "portrait"
        var pref_width = parseInt(pref_arr[0].split(":")[1], 10);
        var pref_height = parseInt(pref_arr[1].split(":")[1], 10);
        var pref_orientation = pref_width > pref_height ? "landscape" : "portrait";
        if(doc_orientation != pref_orientation){
          var tmp_height = pref_width;
          var tmp_width = pref_height;
          pref_height = tmp_width;
          pref_width = tmp_height;
        }
        var best_fit = {
          zoom:0,
          fit_diff:(math_abs(pref_height - first_zoom_height)+math_abs(pref_width - first_zoom_width))
        };
        for(var i = 1; i < zoom_levels.length; i++){
          var this_diff = (math_abs(pref_height - zoom_levels[i]["height"])+math_abs(pref_width - zoom_levels[i]["width"]));
          if(this_diff < best_fit.fit_diff){
            best_fit = {
              index:i,
              fit_diff:this_diff
            }
          }
        }
        this.setZoomIndex(best_fit.index);
        this.preferredZoomIndex(best_fit.index);
      }else{
        this.setZoomLevel(z_default);
        this.preferredZoomIndex(z_default);
      }
    },

    getZoomInLevel : function() {
      if(PageModel.zoomLevels.length > PageModel.zoomIndex + PageModel.zoomInOffset){
          var zil = PageModel.zoomLevels[PageModel.zoomIndex + PageModel.zoomInOffset]["zoom"];
      }else{
          zil = PageModel.zoomLevels[PageModel.zoomLevels.length-1]["zoom"];
      }
      return zil;
    },

    setZoomInFactor : function(){
        var wh = $(window).height();
        switch(true){
            case wh >= 768:
                Globals.zoomInFactor = 2;
                break;
            case wh >= 600:
               Globals.zoomInFactor = 3;
                break;
            default:
               Globals.zoomInFactor = 4;
                break;
        }
    },

    setZoomInOffset : function(){
        var combined_dim_current = PageModel.zoomLevels[PageModel.zoomIndex]["width"]+PageModel.zoomLevels[PageModel.zoomIndex]["height"];
        var target_combined_dim = combined_dim_current * Globals.zoomInFactor;
        var math_abs = Math.abs;
        var best_fit = {
            offset: 0,
            fit_diff: null
        };

        for(var i = PageModel.zoomIndex; i < PageModel.zoomLevels.length; i++){
            var this_zoom = PageModel.zoomLevels[i]
            var this_height = this_zoom.height;
            var this_width = this_zoom.width;
            var this_diff = math_abs(target_combined_dim - (this_width + this_height));
            if(!best_fit.fit_diff || this_diff < best_fit.fit_diff){
                best_fit = {
                    offset: i - PageModel.zoomIndex,
                    fit_diff:this_diff
                };
            }
        }
        PageModel.zoomInOffset = best_fit.offset;
    },

    validZoomIndex : function(index){
        return parseInt(index,10) >= 0 && parseInt(index,10) < PageModel.zoomLevels.length
    },

    preferredZoomIndex : function(index){
        if(PageModel.validZoomIndex(index)){
            if(!PageView.getAutoZoom()){
                var zoom_levels = PageModel.zoomLevels;
                var preference_cookie = "width:"+zoom_levels[index]["width"]+"&height:"+zoom_levels[index]["height"];
                CookieManager.set("preference_zoom",preference_cookie);
            }
            preferredZoom = index;
        }
        return preferredZoom;
    },

    fitViewZoomIndex : function(index) {
        if(PageModel.validZoomIndex(index)){
            fitViewIndex = index;
        }
        return fitViewIndex;
    },

    fitWidthZoomIndex : function(index) {
        if(PageModel.validZoomIndex(index)){
            fitWidthIndex = index;
        }
        return fitWidthIndex;
    },

    //this is still here because of the SNT override
    setViewMode : function(vm) {
      PageModel.viewMode = vm;
    },

        //(DT-4380) Set AutoPlay parameters
        setAutoPlay : function(val,duration) {
            autoPlayFlag = val;
            autoPlayDuration = duration;
        },

        //(DT-4380) Get AutoPlay flag
        getAutoPlay : function() {
            return autoPlayFlag;
        },

        //(DT-4380) Get AutoPlay duration
        getAutoPlayDuration : function() {
            return autoPlayDuration;
        },

        doCallback : function(fn_callback,fn_arguments) {
          if (typeof fn_callback == "function"){
              fn_callback.apply(PageView,fn_arguments);
          }
        },

        zoomIndexOf : function(zoom){
            var zoom_levels = PageModel.zoomLevels;
            var zoom_index = 0;
            for(var i = 1; i < zoom_levels.length; i++){
                if(zoom_levels[i]["zoom"] == zoom){
                    zoom_index = i;
                    break;
                }
            }
            return zoom_index;
        }
  }
})();

/* */
/* PageView */
/* */

var PageView = (function(){

  var isZoomed = false;
  var m_transition = slide;
  var autoZoom = true;
  var autoZoomMode = '';  //"fit_view";
  var prevPageMargin = null;
  var prevPageHover = false;
  var nextPageMargin = null;
  var nextPageHover = false;
  var tmpScrollLeft = 0;
  var tmpScrollTop = 0;
  var animationDuration = 350;
  var togglePageModeFlag = false;
  var zoomTimer = null;
  var $ = jQuery;
  var pageChanged = null;

   function access_denied(pageElement){
      var access_denied_div = $(".access_denied",pageElement);
      var url = access_denied_div.attr('url');
      if (url) {
        location.href = url;
        return;
      }
      if(access_denied_div.html() && PageElements.dialogs.hasOwnProperty('access_denied')===false){
        $(ViewHelper.shadowWrap(access_denied_div.parent(),"black")).dialog({
            height:365,
            width:480,
            modal:true,
            close: function(){
//                $("div[id^=p"+PageController.currentPage+"z]", PageModel.preloadedPagesDiv).empty();
                PageModel.preloadedPagesDiv.empty();
                // If access to page is denied, put the PageController.currentPage back
                // to the original viewed page.
                PageController.currentPage = PageView.currentDisplayedPage;
                delete PageElements.dialogs['access_denied'];
                $(this).dialog('destroy').remove();
            },
            open: function(){
              PageElements.dialogs["access_denied"] = {
                'id':'access_denied',
                'link_active':true,
                'dialog_active':true
              };
            },
            resizable:false
        });
      }
      PageController.clearChangePageCallbacks();
    }

    function look_inside_help(){
      var look_inside_elem = $(".look_inside_help");
      // don't show look_inside popup in full screen mode, doesn't apply for CDS payment reminder
      if ((PageModel.fullScreen)&&(look_inside_elem.html() && PageElements.dialogs.hasOwnProperty('look_inside_help')===false)) {
          look_inside_elem.each(function(){
                  $(this).parent().remove();
          });
          return;
      }
      // check to see if multiple pages were returned with look_inside_help divs.
      // if so, delete
      if(look_inside_elem.length > 1){
          look_inside_elem.each(function(i){
              if(i != (look_inside_elem.length - 1)){
                  $(this).parent().remove();
              }
          })
          return;
      }
      // check to see if the parent is for Look Inside or Publisher Payment.
      // Look Inside: only display dialog if not from portal and only once
      // Publisher Payment: only display if content is filled in
      var from_portal = DocumentProperties.portalDomain;
      var parentId = look_inside_elem.parent()[0].id;
      var showLookInside = Boolean(look_inside_elem.html() &&
                                   parentId.indexOf('look_inside_help') != -1 &&
                                   PageElements.dialogs.hasOwnProperty('look_inside_help') === false &&
                                   !from_portal);
      var showPaymentPage = Boolean(look_inside_elem.html() &&
                                    parentId.indexOf('publisher_payment_page') != -1 &&
                                    $("div:first div",look_inside_elem).html());

      if (showLookInside || showPaymentPage) {
          $(ViewHelper.shadowWrap(look_inside_elem,"black")).dialog({
            height:400,
            width:500,
            modal:true,
            close: function(){
              //delete PageElements.dialogs['look_inside_help'];
                $(this).dialog('destroy').remove();
            },
            open: function(){
              look_inside_elem.parent().remove();
              PageElements.dialogs["look_inside_help"] = {
                  'id':'look_inside_help',
                  'link_active':true,
                  'dialog_active':true
              };
            },
            resizable:false
        });
      }else{
          look_inside_elem.parent().remove();
      }
    }

  function slide(delta,callback) {
    var pageList = PageModel.normalPagesFolioDiv();
    PageModel.normalPagesFolioDiv().animate(
      { "left": delta },{
        duration:animationDuration,
        easing:"easeOutCubic",
        complete:PageView.finish
      });
  }

  function flip(delta,callback) {
    if (delta.indexOf("-=") == 0) {
      $(".folio:last",PageModel.normalPagesDiv).css({ "left": '0', "z-index" : '1' });
    } else {
      $(".folio:first",PageModel.normalPagesDiv).css({ "left": '0', "z-index" : '1' });
    }
    PageView.finish();
  }

  function toggle(delta,callback) {
    if (delta.indexOf("-=") == 0) {
      $(".folio:last",PageModel.normalPagesDiv).css({ "left": '0', "z-index" : '1' }).hide();
    } else {
      $(".folio:first",PageModel.normalPagesDiv).css({ "left": '0', "z-index" : '1' }).hide();
    }
    $(".folio").toggle(animationDuration,PageView.finish);
  }

  function slidetoggle(delta,callback) {
    if (delta.indexOf("-=") == 0) {
      $(".folio:last",PageModel.normalPagesDiv).css({ "left": '0', "z-index" : '1' }).hide();
    } else {
      $(".folio:first",PageModel.normalPagesDiv).css({ "left": '0', "z-index" : '1' }).hide();
    }
    $(".folio",PageModel.normalPagesDiv).slideToggle(animationDuration,PageView.finish);
  }

  function disable_buttons(){
    $(".pageChanger a").addClass("hidden");
  }

  function enable_buttons(){
      var page_changers = $(".pageChanger");
      var page_changer_links = page_changers.find('a');
      page_changer_links.removeClass('hidden');
      if($.browser.msie){
          page_changers.hover(
              function(){
                  $(this).find("a").css('display','block');
              },
              function(){
                  $(this).find("a").css('display','');
              }
          );
      }
  }

  function handle_next_page_click(e){
      e.preventDefault();
      e.stopPropagation();

      ArticleViewer.hide();
      PageController.handleNextPage();
      return false;
  }

  function handle_prev_page_click(e){
      e.preventDefault();
      e.stopPropagation();

      ArticleViewer.hide();
      PageController.handlePrevPage();
      return false;
  }

  function handle_first_page_click(e){
      e.preventDefault();
      e.stopPropagation();

      ArticleViewer.hide();
      PageController.handleGoToFirstPage();
      return false;
  }

  function handle_last_page_click(e){
      e.preventDefault();
      e.stopPropagation();

      ArticleViewer.hide();
      PageController.handleGoToLastPage();
      return false;
  }

  function initialize_buttons(){
      nextPageMargin = $("#nextPageMargin",PageModel.documentContainerDiv);
      nextPageMargin.data('img',$("img:first",nextPageMargin));
      prevPageMargin = $("#prevPageMargin",PageModel.documentContainerDiv);
      prevPageMargin.data('img',$("img:first",prevPageMargin));
      enable_buttons();
    }

    function mouse_move_callback(e){
        var dx = e.pageX - PageModel.normalPagesDiv.data('dragx');
        var dy = e.pageY - PageModel.normalPagesDiv.data('dragy');
        var scroll_target = // isZoomed
          //? PageModel.normalPagesDiv :
            $.browser.safari ? $("body") : $("html");
            if(dx != 0 || dy != 0){
          PageView.inDrag=true;
        }
        scroll_target
          .scrollLeft(scroll_target.scrollLeft()-dx)
          .scrollTop(scroll_target.scrollTop()-dy)
          .data('dragx',e.pageX)
          .data('dragy',e.pageY);

        return false;
    }

  function mouse_drag_callback(e){
    if(PageView.inDrag){
      PageView.inDrag = false;
      $(".fg",PageModel.normalPagesDiv).trigger('mouseup');
    }
  }

  function check_for_max_zoom(idx){
      var z_index = idx || PageModel.zoomIndex;
      var z_levels_len = PageModel.zoomLevels.length - 1;
      if(z_index >= z_levels_len){
          z_index = z_levels_len;
          if(PageView.isZoomedIn()===false){
              PageModel.documentContainerDiv.addClass('max_zoom');
          }
      }else{
          PageModel.documentContainerDiv.removeClass('max_zoom');
      }
  }

  function handle_page_click(e){
      if(e.button == 2){
       return;
      }
      if (PageView.inDrag === true) {
          PageView.inDrag = false;
          return true;
      }else{
          if($(e.target).parents('.zoomed_folio').length === 0){
              PageController.handleZoomIn(e);
          }else{
              PageController.handleZoomOut(e);
          }
          return false;
      }
  }

  function handle_page_mousedown(e){
      window.mouseisdown = true;
      PageModel.normalPagesDiv.data('dragx', e.pageX);
      PageModel.normalPagesDiv.data('dragy', e.pageY);

      $(".fg",PageModel.normalPagesDiv)
         .addClass('mousedown')
         .mousemove(mouse_move_callback);

      // this prevents the window from getting stuck in drag mode
      $(window).bind('mouseup',mouse_drag_callback);

      //e.button == 2 means it is a right click...returning false would kill the right click menu
      if( e.button == 2 ){
          //trigger a mouse up so that the page does not follow the mouse around
          $(".fg",PageModel.normalPagesDiv).trigger('mouseup');
      }else{
       return false;
      }
  }

  function handle_page_mouseup(e){
      $(".fg",PageModel.normalPagesDiv)
          .removeClass('mousedown')
          .unbind('mousemove',mouse_move_callback);
      $(window).unbind('mouseup',mouse_drag_callback);
      return false;
  }

  return {
      inDrag : false,
      currentDisplayedPage : 0,  // set to 0 initially - not a real page
      numPagesViewed : 1,
      flashingDisabled : false,
      zoomOutDrawerSize : 0,
      pageLock : false,
      zoomLock : 0,
      newZoomIndex : 0,
      closeDrawerOnZoom: true,
      shortTitle : "",
      requiredPages : [],
      
      initPageBehaviors : function() {
          $(".fg",PageModel.normalPagesDiv)
              .live('click',handle_page_click)
              .live('mousedown',handle_page_mousedown)
              .live('mouseup',handle_page_mouseup);

          PageView.initPageNavigationButtonBehaviors();
          initialize_buttons();
          var resizeTimer = null;
          $(window).unbind('resize.positionPages').bind('resize.positionPages',function(){
              if(resizeTimer) clearTimeout(resizeTimer);
              resizeTimer = setTimeout(function(){
                  PageView.positionPages(PageModel.viewMode);
                  PageModel.setZoomInFactor();
              },50);
          });
      },

      initPageNavigationButtonBehaviors : function() {
          $(".nextPage").unbind('click').bind('click',handle_next_page_click);
          $(".prevPage").unbind('click').bind('click',handle_prev_page_click);
          $(".firstPage").unbind('click').bind('click',handle_first_page_click);
          $(".lastPage").unbind('click').bind('click',handle_last_page_click);
      },

      disableButtons : function (){
          disable_buttons();
      },

      enableButtons : function () {
          enable_buttons();
      },

      getAutoZoom : function () {
          return autoZoom;
      },

      setAutoZoom : function (state) {
          if(ViewHelper.trueTypeOf(state) == "boolean"){
              autoZoom = state;
              if(autoZoom===true){
                  CookieManager.remove("preference_zoom");
              }
          }
      },

      getAutoZoomMode : function () {
          return autoZoomMode;
      },

      setAutoZoomMode : function (state) {
          autoZoomMode = state;
      },

      showLookInsideHelp : look_inside_help,

      /**
       * This method sets up the look of the page, centering the image
       * correctly on the page and making sure everything looks OK for the
       * image size.
       * This method MUST be called whenever the layout of the page changes.
       * For this reason, PageView.pageLock must be set to true before this
       * method is referenced as a callback with the call to
       * PageModel.getPages(...)
       * The page lock is taken off once this method is called, no matter the
       * outcome of processing the page.
       *
       * @param page The page to be viewed.
       */
      initPage : function(page) {
          PageView.pageLock = false;  // unlock access to other pages
          if (PageView.newZoomIndex) {
            var zoom_index = PageView.newZoomIndex;
            PageView.newZoomIndex = 0;
            PageView.zoomLock = 0;
            PageView.zoomChange(zoom_index);
            return;
          }
          var page_id = "p"+page+"z"+PageModel.zoomLevel+"pm"+PageModel.pageMode;
          var pageElement = PageModel.getCachedPage(page_id);
          if($(".access_denied",pageElement).length > 0){
            access_denied(pageElement);
            return;
          }

          var afterInit = [];
          var w = ViewHelper.width(pageElement) || 0;
          var h = ViewHelper.height(pageElement) || 0;

          // cache current pages if not cached, then remove
          var current_folio = PageModel.normalPagesFolioDiv();
          current_folio.each(function(i, element) {
              PageModel.cachePages(element, element.id);
              $(element).remove();
          });
          PageModel.normalPagesDiv.append(pageElement);
          PageView.centerPages(true);

          var track = false;
          if (PageView.currentDisplayedPage != page) {
            PageView.currentDisplayedPage = parseInt(page, 10);
            track = true;
          }
          // call to preload next page
          var nextPage = PageView.currentDisplayedPage + PageModel.pageMode;
          PageModel.getPages(nextPage, PageView.preloadPage);
          PageView.finish();
          afterInit.push(ViewHelper.ContextMenu.Page.init);
          afterInit.push(handleNavigationButtons);
          if (track) {
              PageController.trackPage(page, page_id);
          }
          ViewHelper.schedule(afterInit,this);
      },

      removePopupCards : function(){
          $(".pagePopupcards",PageModel.popupContainerDiv).remove();
      },


      /**
       * Handle the display of the new page after it has been downloaded
       * from the server.  Load it into the normal page div and resize
       * the div if necessary to accomodate the images.
       * Call to transition from the old image to the new.
       * Call to preload the next page, either going forward or going
       * backward.
       * Call to track the page as it is now displayed in the view.
       *
       * @param page The page to display.
       */
      changePage : function(page) {
          var that = this;
          var afterChangePage = [];
          var originalPage = PageView.currentDisplayedPage;
          if (isZoomed) {
            PageController.handleZoomOut();
          }

          var pageId = "p"+page+"z"+PageModel.zoomLevel+"pm"+PageModel.pageMode;
          var pageElement = PageModel.cachedPages.hasOwnProperty(pageId) ? PageModel.getCachedPage(pageId) : false;

          if (pageElement===false) {
            return;
          } else if ($(".access_denied",pageElement).length > 0) {
            access_denied(pageElement);
            return;
          }

          PageView.removePopupCards();

          var w = ViewHelper.width(pageElement) || 0;
          var h = ViewHelper.height(pageElement) || 0;
          PageModel.normalPagesDiv.css({'width' : w + 'px', 'height' : h + 'px'});

          var track = true;
          var nextPage = null;
          var transitionWidth = 0;
          // Going Forward
          if (page > PageView.currentDisplayedPage) {
            closedPopups = [];
            nextPage = page + PageModel.pageMode;
            PageView.numPagesViewed++;
            transitionWidth = ViewHelper.width(PageModel.normalPagesDiv);
            pageElement.css('left', transitionWidth + 'px');
            PageModel.normalPagesDiv.append(pageElement);
            this.transition("-=" + transitionWidth + "px");

            // Going Backward
          } else if (page < PageView.currentDisplayedPage) {
            closedPopups = [];
            nextPage = page - PageModel.pageMode;
            PageView.numPagesViewed++;
            transitionWidth = ViewHelper.width(pageElement) || 0;
            pageElement.css('left', (-transitionWidth) + 'px');
            PageModel.normalPagesDiv.append(pageElement)
            this.transition("+=" + transitionWidth + "px");

            // Confused...  Viewing the same page again????
          } else {
            // If there are any pages in the view, remove them all,
            // then add the new page.  Preload page going forward as
            // potentially the most common direction.
            // Don't track, since this may be the same page.
            // We are not transitioning here, just adding the new page
            if (PageModel.normalPagesFolioDiv().length > 0) {
                var pageList = PageModel.normalPagesFolioDiv();
                for (var i = 0; i < pageList.length; i++) {
                    $(pageList[i], PageModel.normalPagesDiv).empty();
                }
            }
            pageElement.css('left', '0px');
            PageModel.normalPagesDiv.append(pageElement);
            PageView.finish();
            track = false;
            nextPage = page + PageModel.pageMode;
          }

          PageView.currentDisplayedPage = parseInt(page, 10);
          PageController.updateBrowserHistory();
          afterChangePage.push(ViewHelper.ContextMenu.Page.init);

          // call to preload next page
          if (PageView.currentDisplayedPage <= PageModel.firstPage) {
              nextPage = PageView.currentDisplayedPage + PageModel.pageMode;
          } else if (PageView.currentDisplayedPage >= PageModel.lastPage){
              nextPage = PageView.currentDisplayedPage - PageModel.pageMode;
          }
          if(nextPage !== null){
              PageModel.getPages(nextPage, PageView.preloadPage);
          }

          if (track) {
              PageController.trackPage(page, pageId);
          }
          ViewHelper.schedule(afterChangePage,that,5000);
      },

      /**
       * Preload pages into a hidden (visibility: hidden) div on the page.
       * This pre-loading is strictly to pre-cache images in the browser
       * so that the images will already be pre-loaded for the next page
       * which will serve to make transitions smoother.
       * Note: We only pre-load a page in one direction, as we assume that
       * the images from the previous page are already cached by the browser.
       *
       * @param page The page to pre-load.
       */
      preloadPage : function(page) {
          var pageId = "p"+page+"z"+PageModel.zoomLevel+"pm"+PageModel.pageMode;
          var pageElement = PageModel.cachedPages.hasOwnProperty(pageId) ? PageModel.getCachedPage(pageId) : false;
          if (pageElement === false || $(".access_denied",pageElement).length > 0) {
            return;
          }
          // Remove any preloaded elements before adding new.
          PageModel.preloadedPagesDiv.empty();
          var w = ViewHelper.width(pageElement) || 0;
          var h = ViewHelper.height(pageElement) || 0;
          PageModel.preloadedPagesDiv.css({'width' : w + 'px', 'height' : h + 'px'});
          PageModel.preloadedPagesDiv.append(pageElement);
      },

      /* removed zoomIn and zoomOut, reconciled click-to-zoom with zoom slider */

      setTransition : function(trans) {
          if (trans == 'slide') {
            m_transition = slide;
          } else if (trans == 'flip') {
            m_transition = flip;
          } else if (trans == 'toggle') {
            m_transition = toggle;
          } else if (trans == 'slidetoggle') {
            m_transition = slidetoggle;
          }
      },

      transition : function(delta,callback) {
              m_transition(delta,callback);
    },

    /**
     * Called to complete a series of processing that leads to the
     * display or update of a page.  It may be called in several
     * different ways.
     */
    finish : function() {
        // Show the article flag tab if the page has an article
        if(typeof ArticleViewer != "undefined" && PageView.currentDisplayedPage) {
            ArticleViewer.showArticleFlag(PageView.currentDisplayedPage);
        }
        // One element, make sure it is setup right on UI
        if (PageModel.normalPagesFolioDiv().length == 1) {
            // position buttons and center page
              PageView.centerPages(false);

            if (isZoomed) {
              var popupCardDelay = 600;
              PageModel.normalPagesFolioDiv().addClass('zoomed_folio');
              PageView.setZoomPosition();
            } else {
              popupCardDelay = 0;
              PageModel.normalPagesFolioDiv().removeClass('zoomed_folio')
                .find(".fg").css('cursor','');
              PageView.setZoomPosition();
            }
            // apply search highlights
            if (Search.Model.getSearchValue()) {
              PageModel.getSearchHighlights(Search.Model.getSearchValue(), PageModel.zoomLevel);
            }

            PageModel.clearVisPages();
            PageModel.normalPagesPageDivs().each(function(i){
              var pg = parseInt($(this).attr('pg'), 10);
              PageModel.addVisPage(pg);
              // update popupcards
                    // temporary block the offline Popup.action for performance reason
                    //if(!Offline.isOffline()){
                      setTimeout(function(){
                          PageModel.getPopupcards(pg,(i != 0 || Offline.isOffline()) ? -1 : PageView.numPagesViewed);
                      },popupCardDelay);
                    //}
            });
            // update page selector
            $("#current_page_input").val($("#page_list li a[pg="+PageController.currentPage+"]").html());
            // ad refresh
            $("iframe").each(function(){
              this.src = this.src;
            });
            // trigger callbacks
            PageController.applyChangePageCallbacks();
        } else if (PageModel.normalPagesFolioDiv().length == 0) {
            Console.warn("DOM not loaded with page, trying again...");
            PageModel.getPages(PageController.currentPage, PageView.initPage, true);
        } else {
            // transition, more than one page; try to cache old page, then remove
            PageModel.normalPagesPageDivs().css({ "z-index" : '' });
            var element = this;
            PageModel.cachePages(element, element.id);
            $(element).remove();
        }
        check_for_max_zoom();
        PageView.zoomLock = 0;
        if (PageView.requiredPages.length > 0) {
            PageModel.getPages(PageView.requiredPages.pop(), PageView.initPage, true);
        }
	//In case of failed login, directing back to originally requested page.
	var rfa = CookieManager.get("rfa");
        rfa = rfa ? rfa.toString().toLowerCase() : "";
  	var isFailedLoginAttempt = rfa.split('failedloginattempt');
  	var isShow = rfa.split('show');
  	var isGoto = rfa.split('goto');
  	
  	if(isFailedLoginAttempt.length==2 && isGoto.length==2){
                if($.browser.safari){
		 setTimeout(function(){
          		PageController.handleGoToPage(isGoto[1]);
			},250);
                }else{
                  PageController.handleGoToPage(isGoto[1]);
                }
		//CookieManager.setWithPath("rfa", "failedloginattemptgoneto",1,DocumentProperties.getCollectionUrl());
  	} 
    },


    togglePageMode : function() {
        var current_pages = PageModel.normalPagesPageDivs();

        function handle_zoom_in_mode(){
            if (PageView.zoomOutDrawerSize > Drawer.View.getCurrentSize()) {
                Drawer.View.adjustWidth(PageView.zoomOutDrawerSize);
            }
            PageView.isZoomedIn(false);
        }

        /**
         * Check to see if switching from one page to two page mode.  If so,
         * check to see if the page # is even and switch currentPage to odd
         * page # (subtract 1) as pages display odd|even in two page mode.
         */
        function update_page_dropdown(){
          if(PageController.currentPage % 2 == (PageModel.twoPageCover ? 0 : 1)
            && PageController.currentPage > 1){
              PageController.currentPage = PageController.currentPage - 1;
              PageView.currentDisplayedPage = PageView.currentDisplayedPage - 1;
              $("#current_page_input").val(PageModel.pages[PageView.currentDisplayedPage]);
          }
        }

        togglePageModeFlag = true;
        Navbar.Model.pageTogglers.toggleClass("hidden");

        if(PageModel.pageMode == 2){
            PageModel.pageMode=1;
        }else{
            PageModel.pageMode=2;
        }

        if(isZoomed){
            handle_zoom_in_mode();
        }

        update_page_dropdown();
        PageView.positionPages(PageModel.viewMode);
        togglePageModeFlag = false;


    },

    positionButtons : function() {
      $("#imgnext,#imgprev").css('display','block');
      var w = parseInt(PageModel.normalPagesDiv.css('width'), 10);
      var h = parseInt(PageModel.normalPagesDiv.css('height'), 10);

      var pagesDiv = PageModel.normalPagesDiv.get(0);
      var top = pagesDiv.offsetTop;
      var left = pagesDiv.offsetLeft;
      var buttonDelta = 35;
          if (PageModel.fullScreen) buttonDelta = 0;
      var lprev = left - buttonDelta + 1;
      var lnext = left + w + 4 + buttonDelta - 35;

      prevPageMargin.css({'left' : lprev,'top':top,'height' : h + 'px'}).find("a").css("top",(h / 2 - 25) + 'px');
      nextPageMargin.css({'left' : lnext,'top':top,'height' : h + 'px'}).find("a").css("top",(h / 2 - 25) + 'px');

      var check = null;
    },

    isZoomedIn : function(value) {
        if(ViewHelper.trueTypeOf(value) == "boolean"){
            isZoomed = value;
        }
        return isZoomed;
    },

    testFit : function(zw,vw,zh,vh) {
      return this.testFitView(zw,vw,zh,vh);
    },

    testFitView : function(zw,vw,zh,vh) {
      if (zw <= vw && zh <= vh)
        return true;
      return false;
    },

    testFitWidth : function(zw,vw,zh,vh) {
      if (zw <= vw)
        return true;
      return false;
    },

    testFitHeight : function(zw,vw,zh,vh) {
      if (zh <= vh)
        return true;
      return false;
    },

    testFitScroll : function(zw,vw,zh,vh) {
      if (zw <= vw || zh <= vh)
        return true;
      return false;
    },

    zoomChange : function(z_index) {
        if (PageView.zoomLock){
            PageView.newZoomIndex = z_index;
            return;
        }
        PageView.zoomLock = 1;
        // check_for_max_zoom(z_index);
        if(PageView.isZoomedIn() && z_index <= PageModel.preferredZoomIndex()){
            PageView.isZoomedIn(false);
        }
        PageModel.setZoomIndex(z_index);
        PageView.removePopupCards();
        // calling PageView.initPage, set to true to lock pages
        PageModel.getPages(PageController.currentPage, PageView.initPage, true);
        return 0;
    },

    saveZoomPosition : function(pageX,pageY) {
      var scroll_target = $.browser.safari ? $("body") : $("html");
      scroll_target.scrollLeft();

      var dx = (scroll_target.scrollLeft() + pageX - PageModel.normalPagesDiv.offset().left) / PageModel.normalPagesDiv.width();
      var dy = (scroll_target.scrollTop() + pageY - PageModel.documentContainerDiv.offset().top) / PageModel.normalPagesDiv.height();
      PageModel.normalPagesDiv.data('zoom_position',{'pageX':pageX,'pageY':pageY,'dx':dx,'dy':dy});
    },

    setZoomPosition : function() {
      var zoomPosition =  PageModel.normalPagesDiv.data('zoom_position');
      if (zoomPosition) {
        var pageX = PageModel.normalPagesDiv.data('zoom_position').pageX;
        var pageY = PageModel.normalPagesDiv.data('zoom_position').pageY;
        var dx = PageModel.normalPagesDiv.data('zoom_position').dx;
        var dy = PageModel.normalPagesDiv.data('zoom_position').dy;
        var posX = PageModel.normalPagesDiv.offset().left + (dx * PageModel.normalPagesDiv.width());
        var posY = PageModel.documentContainerDiv.offset().top + (dy * PageModel.normalPagesDiv.height());
        window.scrollTo(posX-pageX,posY-pageY);

        if (!isZoomed) {
          PageModel.normalPagesDiv.data('zoom_position','');
        }
      }
    },

    fitWindow : function(viewMode) {
    	var win_h = $(window).height();
        var win_w = $(window).width();
        var zoom_levels = PageModel.zoomLevels;
        var button_delta = nextPageMargin ? ViewHelper.width(nextPageMargin) : ViewHelper.width($("#nextPageMargin",PageModel.documentContainerDiv));
        var right_margin = button_delta;
        var left_margin = Constants.standardDrawer + button_delta;
        var top_margin = ViewHelper.height($("#navbar"));
        var page_mode = PageModel.pageMode;

        if(viewMode == 'read'){
            left_margin = Constants.closedDrawer + button_delta;
        }
        if ($("#skyscraper").html()) {
          var skyRight = (parseInt("0"+$("#skyscraper").css('right'), 10) || 50) + 25;
            right_margin = parseInt($("#skyscraper").css('width'), 10) + skyRight + button_delta;
        }
        if ($("#leaderboard").html()) {
            top_margin += parseInt($("#leaderboard").height(), 10);
        }
        if ($("#pageflags").length) {
            top_margin += parseInt($("#pageflags").height(), 10);
        }
        if ($("#documentArticleFlagDiv").length) {
            top_margin += parseInt($("#documentArticleFlagDiv").height(), 10);
        }


        PageModel.fitViewZoomIndex(0);
        PageModel.fitWidthZoomIndex(0);

        var vw = win_w - left_margin - right_margin;
        var vh = win_h - top_margin;

        for(var i = 0; i < zoom_levels.length; i++){
            var zw = zoom_levels[i]['width'];
            var zh = zoom_levels[i]['height'];
            if(PageView.testFitWidth(zw * page_mode,vw,zh,vh)){
                PageModel.fitWidthZoomIndex(i);
            }
            if(PageView.testFitView(zw * page_mode,vw,zh,vh)){
                PageModel.fitViewZoomIndex(i);
            }
        }

        if ($("#leaderboard").html()) {
            var lbWidth = parseInt($("#leaderboard").width(), 10);
            var lbLeft = (win_w - lbWidth)/2;
      if (lbLeft < Constants.standardDrawer) lbLeft = Constants.standardDrawer;
            $("#leaderboard").css({'top':ViewHelper.height($("#navbar"))+'px','left':lbLeft+'px'});
        }

        return 0;
    },

    centerPages : function (vertical) {
      // get width of the window
      var vw=$(window).width();
      var vh=$(window).height();
      var zw = 0;
      var zh = 0;

      // margins
      var leftMargin = 35;
      var rightMargin = 35;
      var buttonDelta = PageModel.fullScreen? 0:35;

      $(".folio",PageModel.normalPagesDiv).each(function(){
          zw += ViewHelper.width($(this));
          var th = ViewHelper.height($(this));
          if(th > zh){
              zh = th;
          }
      });

      if(PageModel.viewMode == 'read'){
          leftMargin = Constants.closedDrawer + buttonDelta;
      }else{
          leftMargin = Constants.standardDrawer + buttonDelta;
      }

      var skyscraper = $("#skyscraper");
      if (skyscraper.html()) {
        var skyRight = (parseInt("0"+$("#skyscraper").css('right'), 10) || 50) + 25;
          rightMargin = parseInt(skyscraper.css('width'), 10) + skyRight + buttonDelta;
          if($.browser.msie && /MSIE 6.0/.test(navigator.userAgent)){
            skyscraper.css('left',(leftMargin+zw+buttonDelta)+'px');
          }

      }

      var centerLeft = Math.round((vw - zw) / 2);
      if (centerLeft > leftMargin && centerLeft > rightMargin) {
          // set left margin to center the page, if there is room
          leftMargin = centerLeft;
      }

      var pageflags = $("#pageflags");
      var articleflags = $("#documentArticleFlag");
      var flag_params = {left:leftMargin+'px'};
      var page_params = {
          width:zw+'px',
          left:leftMargin+'px',
          height:zh+'px'
      };
      var popTop = null;

      if(vertical){
          var navbar_h = 31;
          var leaderboard_h = 0;
          var pageflags_h = 0;
          var articleflags_h = 0;

          var leaderboard = $("#leaderboard");
          if (leaderboard.html()) {
              leaderboard_h = parseInt(leaderboard.height(), 10);
          }

          if (pageflags.length > 0) {
              pageflags_h = parseInt(pageflags.height(), 10);
          }

          if (articleflags.length > 0) {
              articleflags_h = parseInt(articleflags.height(), 10);
          }

          var space = Math.round((vh - zh - articleflags_h - pageflags_h - leaderboard_h - navbar_h) / 2);
          if(space < 0){
              space = 0;
          }

          //adjust page flags
          if (pageflags.length > 0) {
              flag_params["top"] = (navbar_h + leaderboard_h + space + 22) +'px';
          }

            // adjust page
          var topVal = navbar_h + leaderboard_h + space + pageflags_h;
          page_params['top'] = topVal + 'px';
          popTop = topVal + parseInt($("#documentContainer").css("padding-top"), 10) + "px";

          // adjust skyscraper
          if (skyscraper.html()) {
              skyscraper.css("top",(navbar_h + leaderboard_h + space + pageflags_h)+"px");
          }
      }

      if (pageflags.length > 0){
          pageflags.css(flag_params);
      }
      PageModel.documentContainerDiv.css(page_params);
      if (popTop !== null) {
          PageModel.popupContainerDiv.css({top:popTop});
      }
      PageModel.popupContainerDiv.css({left:page_params.left});
      PageModel.normalPagesDiv.css({width:zw+'px', height:zh+'px'});
      PageModel.normalPagesFolioDiv().css("left","0px");

      // added to prevent negative margin when toggling between 1/2 page mode when page
      // had been previously viewed and moved off to the side
      PageView.positionButtons();

      // reset page scroll
      if(tmpScrollTop > 0 || tmpScrollLeft > 0){
          $(window).scrollTop(tmpScrollTop).scrollLeft(tmpScrollLeft);
          tmpScrollTop = 0;
          tmpScrollLeft = 0;
      }
    },

    positionPages : function(viewMode) {
        if (isZoomed){
            return;
        }


        PageView.fitWindow(viewMode);
        if (!PageView.getAutoZoom() && togglePageModeFlag === false) {
            PageView.centerPages(true);
            return;
        }else if (togglePageModeFlag === true){
            PageView.zoomChange(
                PageModel.pageMode==2
                    ? PageModel.fitViewZoomIndex()
                    : PageModel.fitWidthZoomIndex()
            );
            Navbar.ZoomControls.update();
            return;
        }
        var orig_view_mode = PageModel.viewMode;
        var orig_zoom_index = PageModel.zoomIndex;
        var orig_zoom_level = PageModel.zoomLevel;
        var orig_auto_mode = PageView.getAutoZoomMode() == "fit_view"
              ? PageModel.fitViewZoomIndex
              : PageModel.fitWidthZoomIndex;

        if (viewMode == 'scan' || viewMode == 'read') PageModel.viewMode=viewMode;
        else viewMode = PageModel.viewMode;

        if(Offline.isOffline()===false){
            var new_size = orig_auto_mode.call();
            if (!pageChanged && (orig_zoom_index != new_size || PageModel.viewMode != orig_view_mode || togglePageModeFlag === true)){

                pageChanged = true;
                PageModel.preferredZoomIndex(new_size);
                PageModel.setZoomIndex(new_size);
                if(togglePageModeFlag === true){
                    PageModel.normalPagesDiv.empty();
                    PageModel.cachedPages = {};
                }else{
                    PageModel.cachePages(PageModel.normalPagesFolioDiv(),"p"+PageController.currentPage+"z"+orig_zoom_level+"pm"+PageModel.pageMode);
                }
                PageView.removePopupCards();
                // calling PageView.initPage, set to true to lock pages
                PageModel.getPages(PageController.currentPage, PageView.initPage, true);
                Navbar.ZoomControls.update();
                setTimeout(function (){pageChanged = null; },3000);
            }else{
                PageView.centerPages(true);
            }
        }
    }
  }
})();


var PageLoader = (function(){

  var pageQueue = [];
  var currentFolio = "";

  function getNextFromQueue() {
    return pageQueue.shift();
  }

  function getEndFromQueue() {
    return pageQueue.pop();
  }

  function processNext() {
    currentFolio = PageModel.pages[PageController.currentPage];
  }

  function endProcessNext(data) {

    processNext();
  }

  // PUBLIC VARIABLES AND FUNCTIONS
  return {
    addToQueue : function(folio) {
      pageQueue.push(folio);
    },

    priorityAddToQueue : function(folio) {
      pageQueue.unshift(folio);
    }

  }

})();


