Team:Tianjin/Resources/JS:jquerytouchPDF

/*

  • Dual licensed under the MIT or GPL Version 2 licenses.
  • /

/**

* See (http://jquery.com/).
* @name $
* @class 
* See the jQuery Library  (http://jquery.com/) for full details.  This just
* documents the function and classes that are added to jQuery by this plug-in.
*/

/**

* See (http://jquery.com/)
* @name fn
* @class 
* See the jQuery Library  (http://jquery.com/) for full details.  This just
* documents the function and classes that are added to jQuery by this plug-in.
* @memberOf $
*/


(function ($) { "use strict";

//Constants var EMPTY = "empty", INIT = "init", LOADING = "loading", LOADED = "loaded", ZOOMEDIN = "zoomedin", DRAGGING = "dragging", RENDERING = "rendering",

PLUGIN_NS = 'TouchPDF',

TOOLBAR_HEIGHT = 30, BORDER_WIDTH = 1, TAB_SPACING = -2, TAB_WIDTH = 41, TAB_OFFSET_WIDTH = 10;


/** * The default configuration, and available options to configure touchPDF with. * You can set the default values by updating any of the properties prior to instantiation. * @name $.fn.pdf.defaults * @namespace * @property {string} [source=""] Path of PDF file to display * @property {string} [title="TouchPDF"] Title of the PDF to be displayed in the toolbar * @property {array} [tabs=[]] Array of tabs to display on the side. See doc for syntax. * @property {string} [tabsColor="beige" Default background color for all tabs. Available colors are "green", "yellow", "orange", "brown", "blue", "white", "black" and you can define your own colors with CSS. * @property {boolean} [disableZoom=false] Disable zooming of PDF document. By default, PDF can be zoomed using scroll, two fingers pinch, +/- keys, and toolbar buttons * @property {boolean} [disableSwipe=false] Disable swipe to next/prev page of PDF document. By default, PDF can be swiped using one finger * @property {boolean} [disableLinks=false] Disable all internal and external links on PDF document * @property {boolean} [disableKeys=false] Disable the arrow keys for next/previous page and +/- for zooming (if zooming is enabled) * @property {boolean} [redrawOnWindowResize=true] Force resize of PDF viewer on window resize * @property {float} [pdfScale=1] Defines the ratio between your PDF page size and the tabs size * @property {float} [quality=2] Set quality ratio for loaded PDF pages. Set at 2 for sharp display when user zooms up to 200% * @property {boolean} [showToolbar=true] Show a toolbar on top of the document with title, page number and buttons for next/prev pages and zooming * @property {function} [loaded=null] A handler triggered when PDF document is loaded (before display of first page) * @property {function} [changed=null] A handler triggered each time a new page is displayed * @property {string} [loadingHTML="Loading PDF"] Text or HTML displayed on white page shown before document is loaded * @property {function} [loadingHeight=841] Height in px of white page shown before document is loaded * @property {function} [loadingWidth=595] Width in px of white page shown before document is loaded */ var defaults = { source: null, title: "TouchPDF", tabs: [], tabsColor: "beige", disableZoom: false, disableSwipe: false, disableLinks: false, disableKeys: false, pdfScale: 1, quality: 2, redrawOnWindowResize: true, showToolbar: true, loaded: null, changed: null, loadingHeight: 841, loadingWidth: 595, loadingHTML: "Loading PDF" };


/** * Load a PDF file in a div * The TouchPDF plugin can be instantiated via this method, or methods within * @see TouchPDF * @class * @param {Mixed} method If the current DOMNode is a TouchPDF object, and method is a TouchPDF method, then * the method is executed, and any following arguments are passed to the TouchPDF method. * If method is an object, then the TouchPDF class is instantiated on the current DOMNode, passing the * configuration properties defined in the object. See TouchPDF */ $.fn.pdf = function (method) { var $this = $(this), plugin = $this.data(PLUGIN_NS);

//Check if we are already instantiated and trying to execute a method if (plugin && typeof method === 'string') { if (plugin[method]) { return plugin[method].apply(this, Array.prototype.slice.call(arguments, 1)); } else { $.error('Method ' + method + ' does not exist on jQuery.pdf'); } } //Else not instantiated and trying to pass init object (or nothing) else if (!plugin && (typeof method === 'object' || !method)) { return init.apply(this, arguments); }

return $this; };

//Expose our defaults so a user could override the plugin defaults $.fn.pdf.defaults = defaults;


/** * Initialise the plugin for each DOM element matched * This creates a new instance of the main TouchPDF class for each DOM element, and then * saves a reference to that instance in the elements data property. * @internal */ function init(options) { //Prep and extend the options if (!options) { options = {}; } options = $.extend({}, $.fn.pdf.defaults, options);

//For each element instantiate the plugin return this.each(function () { var $this = $(this);

//Check we havent already initialised the plugin var plugin = $this.data(PLUGIN_NS); if (!plugin) { plugin = new TouchPDF(this, options); $this.data(PLUGIN_NS, plugin); } }); }

/** * Main TouchPDF Plugin Class. * Do not use this to construct your TouchPDF object, use the jQuery plugin method $.fn.pdf(); {@link $.fn.pdf} * @private * @name TouchPDF * @param {DOMNode} element The HTML DOM object to apply to plugin to * @param {Object} options The options to configure the plugin with. @link {$.fn.pdf.defaults} * @see $.fn.pdf.defaults * @see $.fn.pdf

   * @class

*/ function TouchPDF(element, options) {


// Current phase of pdf loading var state = EMPTY; // Number of pages var totalPages = 0; // Page to be displayed var pageNum = 0; // Page currently rendering var pageNumRendering = 0; // jQuery wrapped element for this instance var $element = $(element); // PDF canvas var canvas = null; // jQuery wrapped PDF annotation layer var $annotations = null; // PDF.JS object var pdfDoc = null;

var scale = 1;

var ctx = false;

var pagesRefMap = [];

var plugin = this;

var tabWidth = 0; var $drag = null, $viewer = null;

var linksDisabled = false;

initDom(); load();


// //Public methods //

/** * Go to specific page of PDF file * @function * @name $.fn.pdf#goto * @return {DOMNode} The Dom element that was registered with TouchPDF * @example $("#element").pdf("goto", 10); */ this.goto = function (number) { goto(number); return $element; };

/** * Go to previous page of PDF file, until first page * @function * @name $.fn.pdf#previous * @return {DOMNode} The Dom element that was registered with TouchPDF * @example $("#element").pdf("previous"); */ this.previous = function () { goto(pageNum-1); return $element; };

/** * Go to next page of PDF file, until end of pdf * @function * @name $.fn.pdf#next * @return {DOMNode} The Dom element that was registered with TouchPDF * @example $("#element").pdf("next"); */ this.next = function () { goto(pageNum+1); return $element; };

/** * Force redraw of pdf (height, width and zoom) * @function * @name $.fn.pdf#redraw * @return {DOMNode} The Dom element that was registered with TouchPDF * @example $("#element").pdf("redraw"); */ this.redraw = function () { return $element; };

/** * Destroy the pdf container completely. * @function * @name $.fn.pdf#destroy * @example $("#element").pdf("destroy"); */ this.destroy = function () { $element.empty().removeClass("touchPDF");

};

/** * Get the current page number (may not be rendered yet) * @function * @name $.fn.pdf#getPageNumber * @return {int} Current page number, 0 if PDF is not loaded * @example $("#element").pdf("getPageNumber"); */ this.getPageNumber = function () { return pageNum; };

/** * Get the total number of pages of loaded PDF * @function * @name $.fn.pdf#getTotalPages * @return {int} The number of pages, 0 if PDF is not loaded * @example $("#element").pdf("getTotalPages"); */ this.getTotalPages = function () { return totalPages; };



// // Private methods //


function goto(number) { if (state == EMPTY || state == INIT) return; if (number < 1) number = 1; if (number > totalPages) number = totalPages; if (number == 0) return; pageNum = number; renderPage();

// update tabs var z = 1; $element.find(".pdf-tabs .tab").each(function(i, a) { var $a = $(a); var aPageNum = $a.data("page"); if ( aPageNum < number) { $a.removeClass("right"); $a.css("z-index", 1000+z++); } else if (aPageNum == number) { $a.removeClass("right"); $a.css("z-index", 1000+z++); } else { $a.addClass("right"); $a.css("z-index", 1000-z++); } }); }

function initDom() { if (state != EMPTY) return; $element.addClass("touchPDF").html(

'
' + '
' + '
' + '
' + '
'+options.loadingHTML+'
' + '
' + '
'

+ '<canvas></canvas>'

+ '
' + '
' + '
' + '
' + '
');

if (options.showToolbar) {

$element.find(".pdf-toolbar").html(

'
'+options.title+'
' + '
<button class="pdf-prev"><</button>
' + '
' + '
<button class="pdf-next">></button>
' + (options.disableZoom? :'
<button class="pdf-zoomin">+</button>
'
+ '
<button class="pdf-zoomout">-</button>
')

);

$element.find(".pdf-toolbar > .pdf-title").on("click", function() { goto(1); }); $element.find(".pdf-toolbar > .pdf-button > .pdf-prev").on("click", function() { goto(pageNum-1); }); $element.find(".pdf-toolbar > .pdf-button > .pdf-next").on("click", function() { goto(pageNum+1); }); }

$drag = $element.find(".pdf-drag"); $viewer = $element.find(".pdf-viewer");


if (!options.disableKeys) { $(window).keydown(function(event) { if (event.keyCode == 37) goto(pageNum-1); else if (event.keyCode == 39) goto(pageNum+1); }); }

if (options.redrawOnWindowResize) { var windowResizeTimeout = false; $( window ).resize(function() { clearTimeout(windowResizeTimeout); windowResizeTimeout = setTimeout(function() { redraw(); }, 100); }); }

if (!options.disableZoom) {

         		$drag.panzoom({
         			contain: 'invert',
         			minScale: 1, 
         			disablePan: true,
         			increment: 0.25,
         			maxScale: 2,
         			onChange: function() {
         				linksDisabled = true;
         				$drag.panzoom("option", "disablePan", false);

state = ZOOMEDIN;

         			},
         			onEnd: function() {
         				setTimeout(function() {
         					linksDisabled = false;

if ($drag.panzoom("getMatrix")[0] == 1) zoomReset();

         				}, 1);
         			}
   			});
         		$drag.panzoom('enable');

$drag.parent().on('mousewheel.focal', function( e ) { e.preventDefault(); var delta = e.delta || e.originalEvent.wheelDelta; var direction = delta ? delta < 0 : e.originalEvent.deltaY > 0; if (direction) zoomOut(e); else zoomIn(e); });

if (options.showToolbar) { $element.find(".pdf-toolbar > .pdf-button > .pdf-zoomin").on("click", function() { zoomIn(); }); $element.find(".pdf-toolbar > .pdf-button > .pdf-zoomout").on("click", function() { zoomOut(); }); }

if (!options.disableLinks) { // enable links while zoomed in var touchlink = null; $drag.on('touchstart', "a", function( e ) { touchlink = this; setTimeout(function() { touchlink = null; }, 100); }); $drag.on('touchend', "a", function( e ) { if (this == touchlink) { e.stopImmediatePropagation(); this.click(); } }); } }


if (!options.disableSwipe) { $viewer.swipe( { swipe:function(event, direction, distance, duration, fingerCount, fingerData) { if (state != LOADED) return; linksDisabled = true; setTimeout(function() {linksDisabled = false;}, 1); if (direction == "right") goto(pageNum-1); else if (direction == "left") goto(pageNum+1); }, threshold:50, excludedElements: ".noSwipe" }); }

canvas = $element.find("canvas")[0]; ctx = canvas.getContext('2d');

$annotations = $element.find(".pdf-annotations");

state = INIT;

redraw(); }

function zoomIn (focal) { if (options.disableZoom) return; if (state != ZOOMEDIN && state != LOADED) return; state = ZOOMEDIN; $drag.panzoom('zoom', false, { increment: 0.25, animate: true, focal: focal }); linksDisabled = false; }

function zoomOut(focal) { if (options.disableZoom) return; if (state != ZOOMEDIN) return; $drag.panzoom('zoom', true, { increment: 0.25, animate: true, focal: focal }); linksDisabled = false;

if ($drag.panzoom("getMatrix")[0] == 1) zoomReset(); } function zoomReset() { if (options.disableZoom) return; $drag.panzoom('reset'); linksDisabled = false; $drag.panzoom("option", "disablePan", true); state = LOADED; }

/** * Asynchronously downloads PDF. */ function load () { if (state != INIT) return; state = LOADING;

PDFJS.getDocument(options.source).then(function (pdfDoc_) { pdfDoc = pdfDoc_; totalPages = pdfDoc.numPages; if (totalPages < 1) return;

state = LOADED; if (options.loaded) options.loaded() goto(1); });

if (options.tabs && $.isArray(options.tabs)) {

var top = []; var maxOffset = 0;

$.each(options.tabs, function(i, tab) { if(tab.offset && tab.offset > maxOffset) maxOffset = tab.offset; });

tabWidth = TAB_WIDTH + TAB_OFFSET_WIDTH * maxOffset;

$.each(options.tabs, function(i, tab) { var offset = tab.offset || 0; if (top[offset] === undefined) { top[offset] = 5 + TOOLBAR_HEIGHT; }

var $a = $("<a>") .addClass("tab") .data("page", tab.page) .css("margin-left", offset*TAB_OFFSET_WIDTH+"px") .css("margin-right", (maxOffset-offset)*TAB_OFFSET_WIDTH+"px") .click(function() { if (tab.page == pageNumRendering) goto(tab.page-1); else goto(tab.page) }); var $span = $("") .html(tab.title) .appendTo($a);

if (tab.bottom !== undefined) { $a.css("bottom", tab.bottom); } else { if (tab.top !== undefined) top[offset] = tab.top + TOOLBAR_HEIGHT; $a.css("top", top[offset]); }

if (tab.title.length > 2) $a.addClass("large"); if (!tab.color) tab.color = options.tabsColor; $a.addClass(tab.color); if (tab.height) { $a.css("height", tab.height); $span.css("width", tab.height); }

$element.find(".pdf-tabs").append($a);

if (tab.bottom === undefined) top[offset] += $a.height() + TAB_SPACING; }); } }

/** * Get page info from document, resize canvas accordingly, and render page. * @param num Page number. */ function renderPage () { if (state != LOADED && state != ZOOMEDIN) return; if (pageNum == pageNumRendering) return;

zoomReset(); state = RENDERING; pageNumRendering = pageNum; updatePageCount();

// Using promise to fetch the page pdfDoc.getPage(pageNumRendering).then(function(page) { var viewport = page.getViewport(options.pdfScale*options.quality); canvas.height = viewport.height; canvas.width = viewport.width; $(".pdf-canvas").css("transform", "scale("+(1/options.quality)+")").css("transform-origin", "top left");

// Render PDF page into canvas context var renderTask = page.render({ canvasContext: ctx, viewport: viewport });

if (!options.disableLinks) { renderAnnotations(page, viewport); }

// Wait for rendering to finish renderTask.promise.then(function () { state = LOADED; if (pageNumRendering != pageNum) { // New page rendering is pending renderPage(); } });

redraw(); $element.find(".pdf-loading").hide(); $element.find(".pdf-tabs").css("visibility", "visible"); $element.find("canvas").css("visibility", "visible");

if (options.changed) options.changed();

}); }


function redraw() {

if (state == INIT) { var pdfHeight = options.loadingHeight; var pdfWidth = options.loadingWidth;

} else { var pdfHeight = canvas.height / options.quality; var pdfWidth = canvas.width / options.quality; } var winHeight = $element.height(); var winWidth = $element.width();


scale = Math.min( winHeight / (pdfHeight + TOOLBAR_HEIGHT + BORDER_WIDTH*2), winWidth / (pdfWidth + tabWidth*2 + BORDER_WIDTH*2) ); if (scale > 1) scale = 1;

$element.find(".pdf-outerdiv") .css("transform", "scale("+scale+")") .css("width", pdfWidth + BORDER_WIDTH*2) .css("height", pdfHeight + TOOLBAR_HEIGHT + BORDER_WIDTH*2) .css("padding", "0 "+tabWidth+"px") .css("left", (winWidth - scale*(pdfWidth + tabWidth*2 + BORDER_WIDTH*2))/2);

$element.find(".pdf-toolbar") .css("width", pdfWidth) .css("height", TOOLBAR_HEIGHT) .css("left", tabWidth + BORDER_WIDTH);

$viewer .css("width", pdfWidth) .css("height", pdfHeight) .css("left", tabWidth) .css("top", TOOLBAR_HEIGHT) .css("border-width", BORDER_WIDTH);

$drag .css("width", pdfWidth) .css("height", pdfHeight);

if (!options.disableZoom) { $drag.panzoom('resetDimensions'); }

if (!options.disableSwipe) { $viewer.swipe("option", "threshold", 75*scale); }


}


function updatePageCount() { if (state == EMPTY || state == INIT) return; $element.find(".pdf-page-count").html(pageNum + " / " + totalPages); }

function getPageIndex(destRef) { var defer = $.Deferred();

if (pagesRefMap[destRef.num + ' ' + destRef.gen + ' R']) { defer.resolve(pagesRefMap[destRef.num + ' ' + destRef.gen + ' R']);

} else { pdfDoc.getPageIndex(destRef).then(function (pageIndex) { pagesRefMap[destRef.num + ' ' + destRef.gen + ' R'] = pageIndex + 1; defer.resolve(pageIndex + 1); }); } return defer.promise(); }

function renderAnnotations(page, viewport) { if (state != RENDERING) return;

$annotations.empty();

page.getAnnotations().then(function (annotationsData) {

viewport = viewport.clone({dontFlip: true});

$.each(annotationsData, function(i, data) { if (!data || !data.hasHtml || data.subtype !== 'Link' || (!data.dest && !data.url)) return;

var $el = $(PDFJS.AnnotationUtils.getHtmlElement(data, page.commonObjs)); var rect = data.rect; var view = page.view; rect = PDFJS.Util.normalizeRect([ rect[0], view[3] - rect[1] + view[1], rect[2], view[3] - rect[3] + view[1] ]); $el.css("left", rect[0] + 'px') .css("top", rect[1] + 'px') .css("position", 'absolute');

var transform = viewport.transform; var transformStr = 'matrix(' + transform.join(',') + ')'; $el.css('transform', transformStr); var transformOriginStr = -rect[0] + 'px ' + -rect[1] + 'px'; $el.css('transformOrigin', transformOriginStr);

var link = $el.find("a") .on('mousedown', function(e) { e.preventDefault(); });

if (data.url) {

link.addClass("externalLink") .attr("href", data.url) .attr("target", "_blank");

} else if (data.dest) {

link.addClass("internalLink") .data("dest", data.dest) .on('click', function(e) { if (state != LOADED && state != ZOOMEDIN) return false; if (linksDisabled) return false; var dest = $(this).data("dest");

if (dest instanceof Array) { getPageIndex(dest[0]).then( function ( num ) { if (state != LOADED && state != ZOOMEDIN) return; goto(num); }); } else { pdfDoc.getDestination($(this).data("dest")).then(function(destRefs) { if (!(destRefs instanceof Array)) return; // invalid destination getPageIndex(destRefs[0]).then( function ( num ) { if (state != LOADED && state != ZOOMEDIN) return; if (linksDisabled) return; goto(num); }); }); } return false; }); }

$annotations.append($el); });

});

}


}


})(jQuery );