379 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			379 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | 
						|
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
 | 
						|
/* Copyright 2012 Mozilla Foundation
 | 
						|
 *
 | 
						|
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
						|
 * you may not use this file except in compliance with the License.
 | 
						|
 * You may obtain a copy of the License at
 | 
						|
 *
 | 
						|
 *     http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
 *
 | 
						|
 * Unless required by applicable law or agreed to in writing, software
 | 
						|
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
						|
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
						|
 * See the License for the specific language governing permissions and
 | 
						|
 * limitations under the License.
 | 
						|
 */
 | 
						|
/* globals PDFJS, PresentationMode */
 | 
						|
 | 
						|
'use strict';
 | 
						|
 | 
						|
var PDFHistory = {
 | 
						|
  initialized: false,
 | 
						|
  initialDestination: null,
 | 
						|
 | 
						|
  /**
 | 
						|
   * @param {string} fingerprint
 | 
						|
   * @param {IPDFLinkService} linkService
 | 
						|
   */
 | 
						|
  initialize: function pdfHistoryInitialize(fingerprint, linkService) {
 | 
						|
    this.initialized = true;
 | 
						|
    this.reInitialized = false;
 | 
						|
    this.allowHashChange = true;
 | 
						|
    this.historyUnlocked = true;
 | 
						|
 | 
						|
    this.previousHash = window.location.hash.substring(1);
 | 
						|
    this.currentBookmark = '';
 | 
						|
    this.currentPage = 0;
 | 
						|
    this.updatePreviousBookmark = false;
 | 
						|
    this.previousBookmark = '';
 | 
						|
    this.previousPage = 0;
 | 
						|
    this.nextHashParam = '';
 | 
						|
 | 
						|
    this.fingerprint = fingerprint;
 | 
						|
    this.linkService = linkService;
 | 
						|
    this.currentUid = this.uid = 0;
 | 
						|
    this.current = {};
 | 
						|
 | 
						|
    var state = window.history.state;
 | 
						|
    if (this._isStateObjectDefined(state)) {
 | 
						|
      // This corresponds to navigating back to the document
 | 
						|
      // from another page in the browser history.
 | 
						|
      if (state.target.dest) {
 | 
						|
        this.initialDestination = state.target.dest;
 | 
						|
      } else {
 | 
						|
        linkService.setHash(state.target.hash);
 | 
						|
      }
 | 
						|
      this.currentUid = state.uid;
 | 
						|
      this.uid = state.uid + 1;
 | 
						|
      this.current = state.target;
 | 
						|
    } else {
 | 
						|
      // This corresponds to the loading of a new document.
 | 
						|
      if (state && state.fingerprint &&
 | 
						|
          this.fingerprint !== state.fingerprint) {
 | 
						|
        // Reinitialize the browsing history when a new document
 | 
						|
        // is opened in the web viewer.
 | 
						|
        this.reInitialized = true;
 | 
						|
      }
 | 
						|
      this._pushOrReplaceState({ fingerprint: this.fingerprint }, true);
 | 
						|
    }
 | 
						|
 | 
						|
    var self = this;
 | 
						|
    window.addEventListener('popstate', function pdfHistoryPopstate(evt) {
 | 
						|
      evt.preventDefault();
 | 
						|
      evt.stopPropagation();
 | 
						|
 | 
						|
      if (!self.historyUnlocked) {
 | 
						|
        return;
 | 
						|
      }
 | 
						|
      if (evt.state) {
 | 
						|
        // Move back/forward in the history.
 | 
						|
        self._goTo(evt.state);
 | 
						|
      } else {
 | 
						|
        // Handle the user modifying the hash of a loaded document.
 | 
						|
        self.previousHash = window.location.hash.substring(1);
 | 
						|
 | 
						|
        // If the history is empty when the hash changes,
 | 
						|
        // update the previous entry in the browser history.
 | 
						|
        if (self.uid === 0) {
 | 
						|
          var previousParams = (self.previousHash && self.currentBookmark &&
 | 
						|
                                self.previousHash !== self.currentBookmark) ?
 | 
						|
            { hash: self.currentBookmark, page: self.currentPage } :
 | 
						|
            { page: 1 };
 | 
						|
          self.historyUnlocked = false;
 | 
						|
          self.allowHashChange = false;
 | 
						|
          window.history.back();
 | 
						|
          self._pushToHistory(previousParams, false, true);
 | 
						|
          window.history.forward();
 | 
						|
          self.historyUnlocked = true;
 | 
						|
        }
 | 
						|
        self._pushToHistory({ hash: self.previousHash }, false, true);
 | 
						|
        self._updatePreviousBookmark();
 | 
						|
      }
 | 
						|
    }, false);
 | 
						|
 | 
						|
    function pdfHistoryBeforeUnload() {
 | 
						|
      var previousParams = self._getPreviousParams(null, true);
 | 
						|
      if (previousParams) {
 | 
						|
        var replacePrevious = (!self.current.dest &&
 | 
						|
                               self.current.hash !== self.previousHash);
 | 
						|
        self._pushToHistory(previousParams, false, replacePrevious);
 | 
						|
        self._updatePreviousBookmark();
 | 
						|
      }
 | 
						|
      // Remove the event listener when navigating away from the document,
 | 
						|
      // since 'beforeunload' prevents Firefox from caching the document.
 | 
						|
      window.removeEventListener('beforeunload', pdfHistoryBeforeUnload, false);
 | 
						|
    }
 | 
						|
    window.addEventListener('beforeunload', pdfHistoryBeforeUnload, false);
 | 
						|
 | 
						|
    window.addEventListener('pageshow', function pdfHistoryPageShow(evt) {
 | 
						|
      // If the entire viewer (including the PDF file) is cached in the browser,
 | 
						|
      // we need to reattach the 'beforeunload' event listener since
 | 
						|
      // the 'DOMContentLoaded' event is not fired on 'pageshow'.
 | 
						|
      window.addEventListener('beforeunload', pdfHistoryBeforeUnload, false);
 | 
						|
    }, false);
 | 
						|
  },
 | 
						|
 | 
						|
  _isStateObjectDefined: function pdfHistory_isStateObjectDefined(state) {
 | 
						|
    return (state && state.uid >= 0 &&
 | 
						|
            state.fingerprint && this.fingerprint === state.fingerprint &&
 | 
						|
            state.target && state.target.hash) ? true : false;
 | 
						|
  },
 | 
						|
 | 
						|
  _pushOrReplaceState: function pdfHistory_pushOrReplaceState(stateObj,
 | 
						|
                                                              replace) {
 | 
						|
    if (replace) {
 | 
						|
//#if (GENERIC || CHROME)
 | 
						|
      window.history.replaceState(stateObj, '', document.URL);
 | 
						|
//#else
 | 
						|
//    window.history.replaceState(stateObj, '');
 | 
						|
//#endif
 | 
						|
    } else {
 | 
						|
//#if (GENERIC || CHROME)
 | 
						|
      window.history.pushState(stateObj, '', document.URL);
 | 
						|
//#else
 | 
						|
//    window.history.pushState(stateObj, '');
 | 
						|
//#endif
 | 
						|
//#if CHROME
 | 
						|
//    if (top === window) {
 | 
						|
//      chrome.runtime.sendMessage('showPageAction');
 | 
						|
//    }
 | 
						|
//#endif
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  get isHashChangeUnlocked() {
 | 
						|
    if (!this.initialized) {
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    // If the current hash changes when moving back/forward in the history,
 | 
						|
    // this will trigger a 'popstate' event *as well* as a 'hashchange' event.
 | 
						|
    // Since the hash generally won't correspond to the exact the position
 | 
						|
    // stored in the history's state object, triggering the 'hashchange' event
 | 
						|
    // can thus corrupt the browser history.
 | 
						|
    //
 | 
						|
    // When the hash changes during a 'popstate' event, we *only* prevent the
 | 
						|
    // first 'hashchange' event and immediately reset allowHashChange.
 | 
						|
    // If it is not reset, the user would not be able to change the hash.
 | 
						|
 | 
						|
    var temp = this.allowHashChange;
 | 
						|
    this.allowHashChange = true;
 | 
						|
    return temp;
 | 
						|
  },
 | 
						|
 | 
						|
  _updatePreviousBookmark: function pdfHistory_updatePreviousBookmark() {
 | 
						|
    if (this.updatePreviousBookmark &&
 | 
						|
        this.currentBookmark && this.currentPage) {
 | 
						|
      this.previousBookmark = this.currentBookmark;
 | 
						|
      this.previousPage = this.currentPage;
 | 
						|
      this.updatePreviousBookmark = false;
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  updateCurrentBookmark: function pdfHistoryUpdateCurrentBookmark(bookmark,
 | 
						|
                                                                  pageNum) {
 | 
						|
    if (this.initialized) {
 | 
						|
      this.currentBookmark = bookmark.substring(1);
 | 
						|
      this.currentPage = pageNum | 0;
 | 
						|
      this._updatePreviousBookmark();
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  updateNextHashParam: function pdfHistoryUpdateNextHashParam(param) {
 | 
						|
    if (this.initialized) {
 | 
						|
      this.nextHashParam = param;
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  push: function pdfHistoryPush(params, isInitialBookmark) {
 | 
						|
    if (!(this.initialized && this.historyUnlocked)) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    if (params.dest && !params.hash) {
 | 
						|
      params.hash = (this.current.hash && this.current.dest &&
 | 
						|
                     this.current.dest === params.dest) ?
 | 
						|
        this.current.hash :
 | 
						|
        this.linkService.getDestinationHash(params.dest).split('#')[1];
 | 
						|
    }
 | 
						|
    if (params.page) {
 | 
						|
      params.page |= 0;
 | 
						|
    }
 | 
						|
    if (isInitialBookmark) {
 | 
						|
      var target = window.history.state.target;
 | 
						|
      if (!target) {
 | 
						|
        // Invoked when the user specifies an initial bookmark,
 | 
						|
        // thus setting initialBookmark, when the document is loaded.
 | 
						|
        this._pushToHistory(params, false);
 | 
						|
        this.previousHash = window.location.hash.substring(1);
 | 
						|
      }
 | 
						|
      this.updatePreviousBookmark = this.nextHashParam ? false : true;
 | 
						|
      if (target) {
 | 
						|
        // If the current document is reloaded,
 | 
						|
        // avoid creating duplicate entries in the history.
 | 
						|
        this._updatePreviousBookmark();
 | 
						|
      }
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    if (this.nextHashParam) {
 | 
						|
      if (this.nextHashParam === params.hash) {
 | 
						|
        this.nextHashParam = null;
 | 
						|
        this.updatePreviousBookmark = true;
 | 
						|
        return;
 | 
						|
      } else {
 | 
						|
        this.nextHashParam = null;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    if (params.hash) {
 | 
						|
      if (this.current.hash) {
 | 
						|
        if (this.current.hash !== params.hash) {
 | 
						|
          this._pushToHistory(params, true);
 | 
						|
        } else {
 | 
						|
          if (!this.current.page && params.page) {
 | 
						|
            this._pushToHistory(params, false, true);
 | 
						|
          }
 | 
						|
          this.updatePreviousBookmark = true;
 | 
						|
        }
 | 
						|
      } else {
 | 
						|
        this._pushToHistory(params, true);
 | 
						|
      }
 | 
						|
    } else if (this.current.page && params.page &&
 | 
						|
               this.current.page !== params.page) {
 | 
						|
      this._pushToHistory(params, true);
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  _getPreviousParams: function pdfHistory_getPreviousParams(onlyCheckPage,
 | 
						|
                                                            beforeUnload) {
 | 
						|
    if (!(this.currentBookmark && this.currentPage)) {
 | 
						|
      return null;
 | 
						|
    } else if (this.updatePreviousBookmark) {
 | 
						|
      this.updatePreviousBookmark = false;
 | 
						|
    }
 | 
						|
    if (this.uid > 0 && !(this.previousBookmark && this.previousPage)) {
 | 
						|
      // Prevent the history from getting stuck in the current state,
 | 
						|
      // effectively preventing the user from going back/forward in the history.
 | 
						|
      //
 | 
						|
      // This happens if the current position in the document didn't change when
 | 
						|
      // the history was previously updated. The reasons for this are either:
 | 
						|
      // 1. The current zoom value is such that the document does not need to,
 | 
						|
      //    or cannot, be scrolled to display the destination.
 | 
						|
      // 2. The previous destination is broken, and doesn't actally point to a
 | 
						|
      //    position within the document.
 | 
						|
      //    (This is either due to a bad PDF generator, or the user making a
 | 
						|
      //     mistake when entering a destination in the hash parameters.)
 | 
						|
      return null;
 | 
						|
    }
 | 
						|
    if ((!this.current.dest && !onlyCheckPage) || beforeUnload) {
 | 
						|
      if (this.previousBookmark === this.currentBookmark) {
 | 
						|
        return null;
 | 
						|
      }
 | 
						|
    } else if (this.current.page || onlyCheckPage) {
 | 
						|
      if (this.previousPage === this.currentPage) {
 | 
						|
        return null;
 | 
						|
      }
 | 
						|
    } else {
 | 
						|
      return null;
 | 
						|
    }
 | 
						|
    var params = { hash: this.currentBookmark, page: this.currentPage };
 | 
						|
    if (PresentationMode.active) {
 | 
						|
      params.hash = null;
 | 
						|
    }
 | 
						|
    return params;
 | 
						|
  },
 | 
						|
 | 
						|
  _stateObj: function pdfHistory_stateObj(params) {
 | 
						|
    return { fingerprint: this.fingerprint, uid: this.uid, target: params };
 | 
						|
  },
 | 
						|
 | 
						|
  _pushToHistory: function pdfHistory_pushToHistory(params,
 | 
						|
                                                    addPrevious, overwrite) {
 | 
						|
    if (!this.initialized) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    if (!params.hash && params.page) {
 | 
						|
      params.hash = ('page=' + params.page);
 | 
						|
    }
 | 
						|
    if (addPrevious && !overwrite) {
 | 
						|
      var previousParams = this._getPreviousParams();
 | 
						|
      if (previousParams) {
 | 
						|
        var replacePrevious = (!this.current.dest &&
 | 
						|
                               this.current.hash !== this.previousHash);
 | 
						|
        this._pushToHistory(previousParams, false, replacePrevious);
 | 
						|
      }
 | 
						|
    }
 | 
						|
    this._pushOrReplaceState(this._stateObj(params),
 | 
						|
                             (overwrite || this.uid === 0));
 | 
						|
    this.currentUid = this.uid++;
 | 
						|
    this.current = params;
 | 
						|
    this.updatePreviousBookmark = true;
 | 
						|
  },
 | 
						|
 | 
						|
  _goTo: function pdfHistory_goTo(state) {
 | 
						|
    if (!(this.initialized && this.historyUnlocked &&
 | 
						|
          this._isStateObjectDefined(state))) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    if (!this.reInitialized && state.uid < this.currentUid) {
 | 
						|
      var previousParams = this._getPreviousParams(true);
 | 
						|
      if (previousParams) {
 | 
						|
        this._pushToHistory(this.current, false);
 | 
						|
        this._pushToHistory(previousParams, false);
 | 
						|
        this.currentUid = state.uid;
 | 
						|
        window.history.back();
 | 
						|
        return;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    this.historyUnlocked = false;
 | 
						|
 | 
						|
    if (state.target.dest) {
 | 
						|
      this.linkService.navigateTo(state.target.dest);
 | 
						|
    } else {
 | 
						|
      this.linkService.setHash(state.target.hash);
 | 
						|
    }
 | 
						|
    this.currentUid = state.uid;
 | 
						|
    if (state.uid > this.uid) {
 | 
						|
      this.uid = state.uid;
 | 
						|
    }
 | 
						|
    this.current = state.target;
 | 
						|
    this.updatePreviousBookmark = true;
 | 
						|
 | 
						|
    var currentHash = window.location.hash.substring(1);
 | 
						|
    if (this.previousHash !== currentHash) {
 | 
						|
      this.allowHashChange = false;
 | 
						|
    }
 | 
						|
    this.previousHash = currentHash;
 | 
						|
 | 
						|
    this.historyUnlocked = true;
 | 
						|
  },
 | 
						|
 | 
						|
  back: function pdfHistoryBack() {
 | 
						|
    this.go(-1);
 | 
						|
  },
 | 
						|
 | 
						|
  forward: function pdfHistoryForward() {
 | 
						|
    this.go(1);
 | 
						|
  },
 | 
						|
 | 
						|
  go: function pdfHistoryGo(direction) {
 | 
						|
    if (this.initialized && this.historyUnlocked) {
 | 
						|
      var state = window.history.state;
 | 
						|
      if (direction === -1 && state && state.uid > 0) {
 | 
						|
        window.history.back();
 | 
						|
      } else if (direction === 1 && state && state.uid < (this.uid - 1)) {
 | 
						|
        window.history.forward();
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
};
 |