Difference between revisions of "Team:Tianjin/Resources/JS:jquerytouchPDF"

(Created page with "/* * @fileOverview TouchPDF - jQuery Plugin * @version 0.4 * * @author Loic Minghetti http://www.loicminghetti.net * @see https://github.com/loicminghetti/TouchPDF-Jquery-Plug...")
 
 
Line 7: Line 7:
 
* @see http://plugins.jquery.com/project/touchPDF
 
* @see http://plugins.jquery.com/project/touchPDF
 
*
 
*
 
+
* Copyright (c) 2014 Loic Minghetti
 
* Dual licensed under the MIT or GPL Version 2 licenses.
 
* Dual licensed under the MIT or GPL Version 2 licenses.
 
*
 
*
Line 19: Line 19:
 
  * documents the function and classes that are added to jQuery by this plug-in.
 
  * documents the function and classes that are added to jQuery by this plug-in.
 
  */
 
  */
+
 
 
/**
 
/**
 
  * See (http://jquery.com/)
 
  * See (http://jquery.com/)
Line 29: Line 29:
 
  */
 
  */
  
 +
(function($) {
 +
        "use strict";
  
(function ($) {
+
        //Constants
"use strict";
+
        var EMPTY = "empty",
 +
        INIT = "init",
 +
        LOADING = "loading",
 +
        LOADED = "loaded",
 +
        ZOOMEDIN = "zoomedin",
 +
        DRAGGING = "dragging",
 +
        RENDERING = "rendering",
  
//Constants
+
        PLUGIN_NS = 'TouchPDF',
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;
+
  
 +
        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.
 
* 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.
 
* You can set the default values by updating any of the properties prior to instantiation.
Line 74: Line 72:
 
* @property {function} [loadingWidth=595] Width 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 = {
+
        var defaults = {
source: null,
+
                source: null,
title: "TouchPDF",
+
                title: "TouchPDF",
tabs: [],
+
                tabs: [],
tabsColor: "beige",
+
                tabsColor: "beige",
disableZoom: false,
+
                disableZoom: false,
disableSwipe: false,
+
                disableSwipe: false,
disableLinks: false,
+
                disableLinks: false,
disableKeys: false,
+
                disableKeys: false,
pdfScale: 1,
+
                pdfScale: 1,
quality: 2,
+
                quality: 2,
redrawOnWindowResize: true,
+
                redrawOnWindowResize: true,
showToolbar: true,
+
                showToolbar: true,
loaded: null,
+
                loaded: null,
changed: null,
+
                changed: null,
loadingHeight: 841,
+
                loadingHeight: 841,
loadingWidth: 595,
+
                loadingWidth: 595,
loadingHTML: "Loading PDF"
+
                loadingHTML: "Loading PDF"
};
+
        };
  
 
+
        /**
 
+
/**
+
 
* Load a PDF file in a div
 
* Load a PDF file in a div
 
* The TouchPDF plugin can be instantiated via this method, or methods within  
 
* The TouchPDF plugin can be instantiated via this method, or methods within  
Line 106: Line 102:
 
* configuration properties defined in the object. See TouchPDF
 
* configuration properties defined in the object. See TouchPDF
 
*/
 
*/
$.fn.pdf = function (method) {
+
        $.fn.pdf = function(method) {
var $this = $(this),
+
                var $this = $(this),
plugin = $this.data(PLUGIN_NS);
+
                plugin = $this.data(PLUGIN_NS);
  
//Check if we are already instantiated and trying to execute a method
+
                //Check if we are already instantiated and trying to execute a method
if (plugin && typeof method === 'string') {
+
                if (plugin && typeof method === 'string') {
if (plugin[method]) {
+
                        if (plugin[method]) {
return plugin[method].apply(this, Array.prototype.slice.call(arguments, 1));
+
                                return plugin[method].apply(this, Array.prototype.slice.call(arguments, 1));
} else {
+
                        } else {
$.error('Method ' + method + ' does not exist on jQuery.pdf');
+
                                $.error('Method ' + method + ' does not exist on jQuery.pdf');
}
+
                        }
}
+
                }
//Else not instantiated and trying to pass init object (or nothing)
+
                //Else not instantiated and trying to pass init object (or nothing)
else if (!plugin && (typeof method === 'object' || !method)) {
+
                else if (!plugin && (typeof method === 'object' || !method)) {
return init.apply(this, arguments);
+
                        return init.apply(this, arguments);
}
+
                }
  
return $this;
+
                return $this;
};
+
        };
  
//Expose our defaults so a user could override the plugin defaults
+
        //Expose our defaults so a user could override the plugin defaults
$.fn.pdf.defaults = defaults;
+
        $.fn.pdf.defaults = defaults;
  
 
+
        /**
/**
+
 
* Initialise the plugin for each DOM element matched
 
* Initialise the plugin for each DOM element matched
 
* This creates a new instance of the main TouchPDF class for each DOM element, and then
 
* This creates a new instance of the main TouchPDF class for each DOM element, and then
Line 136: Line 131:
 
* @internal
 
* @internal
 
*/
 
*/
function init(options) {
+
        function init(options) {
//Prep and extend the options
+
                //Prep and extend the options
if (!options) {
+
                if (!options) {
options = {};
+
                        options = {};
}
+
                }
options = $.extend({}, $.fn.pdf.defaults, options);
+
                options = $.extend({},
 +
                $.fn.pdf.defaults, options);
  
//For each element instantiate the plugin
+
                //For each element instantiate the plugin
return this.each(function () {
+
                return this.each(function() {
var $this = $(this);
+
                        var $this = $(this);
  
//Check we havent already initialised the plugin
+
                        //Check we havent already initialised the plugin
var plugin = $this.data(PLUGIN_NS);
+
                        var plugin = $this.data(PLUGIN_NS);
if (!plugin) {
+
                        if (!plugin) {
plugin = new TouchPDF(this, options);
+
                                plugin = new TouchPDF(this, options);
$this.data(PLUGIN_NS, plugin);
+
                                $this.data(PLUGIN_NS, plugin);
}
+
                        }
});
+
                });
}
+
        }
  
/**
+
        /**
 
* Main TouchPDF Plugin Class.
 
* Main TouchPDF Plugin Class.
 
* Do not use this to construct your TouchPDF object, use the jQuery plugin method $.fn.pdf(); {@link $.fn.pdf}
 
* Do not use this to construct your TouchPDF object, use the jQuery plugin method $.fn.pdf(); {@link $.fn.pdf}
Line 167: Line 163:
 
     * @class
 
     * @class
 
*/
 
*/
function TouchPDF(element, options) {
+
        function TouchPDF(element, options) {
  
+
                // Current phase of pdf loading
// Current phase of pdf loading
+
                var state = EMPTY;
var state = EMPTY;
+
                // Number of pages
// Number of pages
+
                var totalPages = 0;
var totalPages = 0;
+
                // Page to be displayed
// Page to be displayed
+
                var pageNum = 0;
var pageNum = 0;
+
                // Page currently rendering
// Page currently rendering
+
                var pageNumRendering = 0;
var pageNumRendering = 0;
+
                // jQuery wrapped element for this instance
// jQuery wrapped element for this instance
+
                var $element = $(element);
var $element = $(element);
+
                // PDF canvas
// PDF canvas
+
                var canvas = null;
var canvas = null;
+
                // jQuery wrapped PDF annotation layer
// jQuery wrapped PDF annotation layer
+
                var $annotations = null;
var $annotations = null;
+
                // PDF.JS object
// PDF.JS object
+
                var pdfDoc = null;
var pdfDoc = null;
+
  
var scale = 1;
+
                var scale = 1;
  
var ctx = false;
+
                var ctx = false;
+
var pagesRefMap = [];
+
+
var plugin = this;
+
+
var tabWidth = 0;
+
var $drag = null, $viewer = null;
+
  
var linksDisabled = false;
+
                var pagesRefMap = [];
+
initDom();
+
load();
+
  
+
                var plugin = this;
//
+
//Public methods
+
//
+
  
/**
+
                var tabWidth = 0;
 +
                var $drag = null,
 +
                $viewer = null;
 +
 
 +
                var linksDisabled = false;
 +
 
 +
                initDom();
 +
                load();
 +
 
 +
                //
 +
                //Public methods
 +
                //
 +
                /**
 
* Go to specific page of PDF file
 
* Go to specific page of PDF file
 
* @function
 
* @function
Line 215: Line 209:
 
* @example $("#element").pdf("goto", 10);
 
* @example $("#element").pdf("goto", 10);
 
*/
 
*/
this.goto = function (number) {
+
                this.goto = function(number) {
goto(number);
+
                        goto(number);
return $element;
+
                        return $element;
};
+
                };
  
/**
+
                /**
 
* Go to previous page of PDF file, until first page
 
* Go to previous page of PDF file, until first page
 
* @function
 
* @function
Line 227: Line 221:
 
* @example $("#element").pdf("previous");
 
* @example $("#element").pdf("previous");
 
*/
 
*/
this.previous = function () {
+
                this.previous = function() {
goto(pageNum-1);
+
                        goto(pageNum - 1);
return $element;
+
                        return $element;
};
+
                };
  
/**
+
                /**
 
* Go to next page of PDF file, until end of pdf
 
* Go to next page of PDF file, until end of pdf
 
* @function
 
* @function
Line 239: Line 233:
 
* @example $("#element").pdf("next");
 
* @example $("#element").pdf("next");
 
*/
 
*/
this.next = function () {
+
                this.next = function() {
goto(pageNum+1);
+
                        goto(pageNum + 1);
return $element;
+
                        return $element;
};
+
                };
  
/**
+
                /**
 
* Force redraw of pdf (height, width and zoom)
 
* Force redraw of pdf (height, width and zoom)
 
* @function
 
* @function
Line 251: Line 245:
 
* @example $("#element").pdf("redraw");
 
* @example $("#element").pdf("redraw");
 
*/
 
*/
this.redraw = function () {
+
                this.redraw = function() {
return $element;
+
                        return $element;
};
+
                };
  
/**
+
                /**
 
* Destroy the pdf container completely.
 
* Destroy the pdf container completely.
 
* @function
 
* @function
Line 261: Line 255:
 
* @example $("#element").pdf("destroy");
 
* @example $("#element").pdf("destroy");
 
*/
 
*/
this.destroy = function () {
+
                this.destroy = function() {
$element.empty().removeClass("touchPDF");
+
                        $element.empty().removeClass("touchPDF");
+
};
+
  
/**
+
                };
 +
 
 +
                /**
 
* Get the current page number (may not be rendered yet)
 
* Get the current page number (may not be rendered yet)
 
* @function
 
* @function
Line 273: Line 267:
 
* @example $("#element").pdf("getPageNumber");
 
* @example $("#element").pdf("getPageNumber");
 
*/
 
*/
this.getPageNumber = function () {
+
                this.getPageNumber = function() {
return pageNum;
+
                        return pageNum;
};
+
                };
  
/**
+
                /**
 
* Get the total number of pages of loaded PDF
 
* Get the total number of pages of loaded PDF
 
* @function
 
* @function
Line 284: Line 278:
 
* @example $("#element").pdf("getTotalPages");
 
* @example $("#element").pdf("getTotalPages");
 
*/
 
*/
this.getTotalPages = function () {
+
                this.getTotalPages = function() {
return totalPages;
+
                        return totalPages;
};
+
                };
+
+
+
  
//
+
                //
// Private methods
+
                // 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
+
                function goto(number) {
var z = 1;
+
                        if (state == EMPTY || state == INIT) return;
$element.find(".pdf-tabs .tab").each(function(i, a) {
+
                        if (number < 1) number = 1;
var $a = $(a);
+
                        if (number > totalPages) number = totalPages;
var aPageNum = $a.data("page");
+
                        if (number == 0) return;
if ( aPageNum < number) {
+
                        pageNum = number;
$a.removeClass("right");
+
                        renderPage();
$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(
+
'<div class="pdf-outerdiv">'
+
+ '<div class="pdf-tabs"></div>'
+
+ '<div class="pdf-toolbar"></div>'
+
+ '<div class="pdf-viewer">'
+
+ '<div class="pdf-loading">'+options.loadingHTML+'</div>'
+
+ '<div class="pdf-drag">'
+
+ '<div class="pdf-canvas">'
+
+ '<canvas></canvas>'
+
+ '<div class="pdf-annotations"></div>'
+
+ '</div>'
+
+ '</div>'
+
+ '</div>'
+
+ '</div>');
+
+
if (options.showToolbar) {
+
+
$element.find(".pdf-toolbar").html(
+
'<div class="pdf-title">'+options.title+'</div>'
+
+ '<div class="pdf-button"><button class="pdf-prev">&lt;</button></div>'
+
+ '<div class="pdf-button"><span class="pdf-page-count"></span></div>'
+
+ '<div class="pdf-button"><button class="pdf-next">&gt;</button></div>'
+
+ (options.disableZoom? '':'<div class="pdf-button"><button class="pdf-zoomin">+</button></div>'
+
+ '<div class="pdf-button"><button class="pdf-zoomout">-</button></div>')
+
);
+
+
$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");
+
                        // update tabs
$viewer = $element.find(".pdf-viewer");
+
                        var z = 1;
+
                        $element.find(".pdf-tabs .tab").each(function(i, a) {
+
                                var $a = $(a);
if (!options.disableKeys) {
+
                                var aPageNum = $a.data("page");
$(window).keydown(function(event) {
+
                                if (aPageNum < number) {
if (event.keyCode == 37) goto(pageNum-1);
+
                                        $a.removeClass("right");
else if (event.keyCode == 39) goto(pageNum+1);
+
                                        $a.css("z-index", 1000 + z++);
});
+
                                } else if (aPageNum == number) {
}
+
                                        $a.removeClass("right");
+
                                        $a.css("z-index", 1000 + z++);
if (options.redrawOnWindowResize) {
+
                                } else {
var windowResizeTimeout = false;
+
                                        $a.addClass("right");
$( window ).resize(function() {
+
                                        $a.css("z-index", 1000 - z++);
clearTimeout(windowResizeTimeout);
+
                                }
windowResizeTimeout = setTimeout(function() {
+
                        });
redraw();
+
                }
}, 100);
+
});
+
}
+
  
if (!options.disableZoom) {
+
                function initDom() {
 +
                        if (state != EMPTY) return;
 +
                        $element.addClass("touchPDF").html('<div class="pdf-outerdiv">' + '<div class="pdf-tabs"></div>' + '<div class="pdf-toolbar"></div>' + '<div class="pdf-viewer">' + '<div class="pdf-loading">' + options.loadingHTML + '</div>' + '<div class="pdf-drag">' + '<div class="pdf-canvas">' + '<canvas></canvas>' + '<div class="pdf-annotations"></div>' + '</div>' + '</div>' + '</div>' + '</div>');
  
          $drag.panzoom({
+
                        if (options.showToolbar) {
          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);
+
          }
+
  
    });
+
                                $element.find(".pdf-toolbar").html('<div class="pdf-title">' + options.title + '</div>' + '<div class="pdf-button"><button class="pdf-prev">&lt;</button></div>' + '<div class="pdf-button"><span class="pdf-page-count"></span></div>' + '<div class="pdf-button"><button class="pdf-next">&gt;</button></div>' + (options.disableZoom ? '': '<div class="pdf-button"><button class="pdf-zoomin">+</button></div>' + '<div class="pdf-button"><button class="pdf-zoomout">-</button></div>'));
          $drag.panzoom('enable');
+
  
$drag.parent().on('mousewheel.focal', function( e ) {
+
                                $element.find(".pdf-toolbar > .pdf-title").on("click",
e.preventDefault();
+
                                function() {
var delta = e.delta || e.originalEvent.wheelDelta;
+
                                        goto(1);
var direction = delta ? delta < 0 : e.originalEvent.deltaY > 0;
+
                                });
if (direction) zoomOut(e);
+
                                $element.find(".pdf-toolbar > .pdf-button > .pdf-prev").on("click",
else zoomIn(e);
+
                                function() {
});
+
                                        goto(pageNum - 1);
 +
                                });
 +
                                $element.find(".pdf-toolbar > .pdf-button > .pdf-next").on("click",
 +
                                function() {
 +
                                        goto(pageNum + 1);
 +
                                });
 +
                        }
  
if (options.showToolbar) {
+
                        $drag = $element.find(".pdf-drag");
$element.find(".pdf-toolbar > .pdf-button > .pdf-zoomin").on("click", function() {
+
                        $viewer = $element.find(".pdf-viewer");
zoomIn();
+
});
+
$element.find(".pdf-toolbar > .pdf-button > .pdf-zoomout").on("click", function() {
+
zoomOut();
+
});
+
}
+
  
if (!options.disableLinks) {
+
                        if (!options.disableKeys) {
// enable links while zoomed in
+
                                $(window).keydown(function(event) {
var touchlink = null;
+
                                        if (event.keyCode == 37) goto(pageNum - 1);
$drag.on('touchstart', "a", function( e ) {
+
                                        else if (event.keyCode == 39) goto(pageNum + 1);
touchlink = this;
+
                                });
setTimeout(function() {
+
                        }
touchlink = null;
+
}, 100);
+
});
+
$drag.on('touchend', "a", function( e ) {
+
if (this == touchlink) {
+
  e.stopImmediatePropagation();
+
this.click();
+
}
+
});
+
}
+
}
+
+
  
if (!options.disableSwipe) {
+
                        if (options.redrawOnWindowResize) {
$viewer.swipe( {
+
                                var windowResizeTimeout = false;
swipe:function(event, direction, distance, duration, fingerCount, fingerData) {
+
                                $(window).resize(function() {
if (state != LOADED) return;
+
                                        clearTimeout(windowResizeTimeout);
linksDisabled = true;
+
                                        windowResizeTimeout = setTimeout(function() {
      setTimeout(function() {linksDisabled = false;}, 1);
+
                                                redraw();
if (direction == "right") goto(pageNum-1);
+
                                        },
else if (direction == "left") goto(pageNum+1);
+
                                        100);
},
+
                                });
threshold:50,
+
                        }
excludedElements: ".noSwipe"
+
});
+
}
+
  
canvas = $element.find("canvas")[0];
+
                        if (!options.disableZoom) {
ctx = canvas.getContext('2d');
+
+
$annotations = $element.find(".pdf-annotations");
+
+
state = INIT;
+
  
redraw();
+
                                $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);
 +
                                        }
  
function zoomIn (focal) {
+
                                });
if (options.disableZoom) return;
+
                                $drag.panzoom('enable');
if (state != ZOOMEDIN && state != LOADED) return;
+
state = ZOOMEDIN;
+
$drag.panzoom('zoom', false, {
+
increment: 0.25,
+
animate: true,
+
focal: focal
+
});
+
linksDisabled = false;
+
}
+
  
function zoomOut(focal) {
+
                                $drag.parent().on('mousewheel.focal',
if (options.disableZoom) return;
+
                                function(e) {
if (state != ZOOMEDIN) return;
+
                                        e.preventDefault();
$drag.panzoom('zoom', true, {
+
                                        var delta = e.delta || e.originalEvent.wheelDelta;
increment: 0.25,
+
                                        var direction = delta ? delta < 0 : e.originalEvent.deltaY > 0;
animate: true,
+
                                        if (direction) zoomOut(e);
focal: focal
+
                                        else zoomIn(e);
});
+
                                });
linksDisabled = false;
+
  
if ($drag.panzoom("getMatrix")[0] == 1) zoomReset();
+
                                if (options.showToolbar) {
}
+
                                        $element.find(".pdf-toolbar > .pdf-button > .pdf-zoomin").on("click",
function zoomReset() {
+
                                        function() {
if (options.disableZoom) return;
+
                                                zoomIn();
$drag.panzoom('reset');
+
                                        });
linksDisabled = false;
+
                                        $element.find(".pdf-toolbar > .pdf-button > .pdf-zoomout").on("click",
$drag.panzoom("option", "disablePan", true);
+
                                        function() {
state = LOADED;
+
                                                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.
 
* Asynchronously downloads PDF.
 
*/
 
*/
function load () {
+
                function load() {
if (state != INIT) return;
+
                        if (state != INIT) return;
state = LOADING;
+
                        state = LOADING;
  
PDFJS.getDocument(options.source).then(function (pdfDoc_) {
+
                        PDFJS.getDocument(options.source).then(function(pdfDoc_) {
pdfDoc = pdfDoc_;
+
                                pdfDoc = pdfDoc_;
totalPages = pdfDoc.numPages;
+
                                totalPages = pdfDoc.numPages;
if (totalPages < 1) return;
+
                                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) {
+
                                state = LOADED;
var offset = tab.offset || 0;
+
                                if (options.loaded) options.loaded() goto(1);
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 = $("<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;
+
});
+
}
+
}
+
  
/**
+
                        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 = $("<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.
 
* Get page info from document, resize canvas accordingly, and render page.
 
* @param num Page number.
 
* @param num Page number.
 
*/
 
*/
function renderPage () {
+
                function renderPage() {
if (state != LOADED && state != ZOOMEDIN) return;
+
                        if (state != LOADED && state != ZOOMEDIN) return;
if (pageNum == pageNumRendering) 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();
 +
                                        }
 +
                                });
  
zoomReset();
+
                                redraw();
state = RENDERING;
+
                                $element.find(".pdf-loading").hide();
pageNumRendering = pageNum;
+
                                $element.find(".pdf-tabs").css("visibility", "visible");
updatePageCount();
+
                                $element.find("canvas").css("visibility", "visible");
  
// Using promise to fetch the page
+
                                if (options.changed) options.changed();
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) {
+
                function redraw() {
renderAnnotations(page, viewport);
+
}
+
  
// Wait for rendering to finish
+
                        if (state == INIT) {
renderTask.promise.then(function () {
+
                                var pdfHeight = options.loadingHeight;
state = LOADED;
+
                                var pdfWidth = options.loadingWidth;
if (pageNumRendering != pageNum) {
+
// New page rendering is pending
+
renderPage();
+
}
+
});
+
  
redraw();
+
                        } else {
$element.find(".pdf-loading").hide();
+
                                var pdfHeight = canvas.height / options.quality;
$element.find(".pdf-tabs").css("visibility", "visible");
+
                                var pdfWidth = canvas.width / options.quality;
$element.find("canvas").css("visibility", "visible");
+
                        }
 +
                        var winHeight = $element.height();
 +
                        var winWidth = $element.width();
  
if (options.changed) options.changed();
+
                        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);
  
function redraw() {
+
                        $viewer.css("width", pdfWidth).css("height", pdfHeight).css("left", tabWidth).css("top", TOOLBAR_HEIGHT).css("border-width", BORDER_WIDTH);
+
if (state == INIT) {
+
var pdfHeight = options.loadingHeight;
+
var pdfWidth = options.loadingWidth;
+
  
} else {
+
                        $drag.css("width", pdfWidth).css("height", pdfHeight);
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 (!options.disableZoom) {
if (scale > 1) scale = 1;
+
                                $drag.panzoom('resetDimensions');
 +
                        }
  
$element.find(".pdf-outerdiv")
+
                        if (!options.disableSwipe) {
.css("transform", "scale("+scale+")")
+
                                $viewer.swipe("option", "threshold", 75 * 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) {
+
                function updatePageCount() {
$drag.panzoom('resetDimensions');
+
                        if (state == EMPTY || state == INIT) return;
}
+
                        $element.find(".pdf-page-count").html(pageNum + " / " + totalPages);
 +
                }
  
if (!options.disableSwipe) {
+
                function getPageIndex(destRef) {
$viewer.swipe("option", "threshold", 75*scale);
+
                        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 updatePageCount() {
+
                function renderAnnotations(page, viewport) {
if (state == EMPTY || state == INIT) return;
+
                        if (state != RENDERING) return;
$element.find(".pdf-page-count").html(pageNum + " / " + totalPages);
+
}
+
  
function getPageIndex(destRef) {
+
                        $annotations.empty();
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) {
+
                        page.getAnnotations().then(function(annotationsData) {
if (state != RENDERING) return;
+
  
$annotations.empty();
+
                                viewport = viewport.clone({
 +
                                        dontFlip: true
 +
                                });
  
page.getAnnotations().then(function (annotationsData) {
+
                                $.each(annotationsData,
+
                                function(i, data) {
viewport = viewport.clone({dontFlip: true});
+
                                        if (!data || !data.hasHtml || data.subtype !== 'Link' || (!data.dest && !data.url)) return;
+
$.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 $el = $(PDFJS.AnnotationUtils.getHtmlElement(data, page.commonObjs));
var rect = data.rect;
+
                                        var rect = data.rect;
var view = page.view;
+
                                        var view = page.view;
rect = PDFJS.Util.normalizeRect([
+
                                        rect = PDFJS.Util.normalizeRect([rect[0], view[3] - rect[1] + view[1], rect[2], view[3] - rect[3] + view[1]]);
rect[0],
+
                                        $el.css("left", rect[0] + 'px').css("top", rect[1] + 'px').css("position", 'absolute');
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 transform = viewport.transform;
var transformStr = 'matrix(' + transform.join(',') + ')';
+
                                        var transformStr = 'matrix(' + transform.join(',') + ')';
$el.css('transform', transformStr);
+
                                        $el.css('transform', transformStr);
var transformOriginStr = -rect[0] + 'px ' + -rect[1] + 'px';
+
                                        var transformOriginStr = -rect[0] + 'px ' + -rect[1] + 'px';
$el.css('transformOrigin', transformOriginStr);
+
                                        $el.css('transformOrigin', transformOriginStr);
  
var link = $el.find("a")
+
                                        var link = $el.find("a").on('mousedown',
.on('mousedown', function(e) {
+
                                        function(e) {
e.preventDefault();
+
                                                e.preventDefault();
});
+
                                        });
  
if (data.url) {
+
                                        if (data.url) {
  
link.addClass("externalLink")
+
                                                link.addClass("externalLink").attr("href", data.url).attr("target", "_blank");
.attr("href", data.url)
+
.attr("target", "_blank");
+
  
} else if (data.dest) {
+
                                        } else if (data.dest) {
  
link.addClass("internalLink")
+
                                                link.addClass("internalLink").data("dest", data.dest).on('click',
.data("dest", data.dest)
+
                                                function(e) {
.on('click', function(e) {
+
                                                        if (state != LOADED && state != ZOOMEDIN) return false;
if (state != LOADED && state != ZOOMEDIN) return false;
+
                                                        if (linksDisabled) return false;
if (linksDisabled) return false;
+
                                                        var dest = $(this).data("dest");
var dest = $(this).data("dest");
+
  
if (dest instanceof Array) {
+
                                                        if (dest instanceof Array) {
getPageIndex(dest[0]).then( function ( num ) {
+
                                                                getPageIndex(dest[0]).then(function(num) {
if (state != LOADED && state != ZOOMEDIN) return;
+
                                                                        if (state != LOADED && state != ZOOMEDIN) return;
goto(num);
+
                                                                        goto(num);
});
+
                                                                });
} else {
+
                                                        } else {
pdfDoc.getDestination($(this).data("dest")).then(function(destRefs) {
+
                                                                pdfDoc.getDestination($(this).data("dest")).then(function(destRefs) {
if (!(destRefs instanceof Array)) return; // invalid destination
+
                                                                        if (! (destRefs instanceof Array)) return; // invalid destination
getPageIndex(destRefs[0]).then( function ( num ) {
+
                                                                        getPageIndex(destRefs[0]).then(function(num) {
if (state != LOADED && state != ZOOMEDIN) return;
+
                                                                                if (state != LOADED && state != ZOOMEDIN) return;
if (linksDisabled) return;
+
                                                                                if (linksDisabled) return;
goto(num);
+
                                                                                goto(num);
});
+
                                                                        });
});
+
                                                                });
}
+
                                                        }
return false;
+
                                                        return false;
});
+
                                                });
}
+
                                        }
  
$annotations.append($el);
+
                                        $annotations.append($el);
});
+
                                });
  
});
+
                        });
  
}
+
                }
+
+
}
+
  
 +
        }
  
})(jQuery );
+
})(jQuery);

Latest revision as of 09:11, 29 August 2017

/*

/**

* 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);