From 9fdcb272d000e4d98ae071ec2ef694dd1892b904 Mon Sep 17 00:00:00 2001 From: Bala Clark Date: Mon, 6 May 2013 21:42:15 +0200 Subject: [PATCH] jshint, use strict --- Makefile | 2 + lib/.jshintrc | 20 + lib/ComicBook.js | 1356 +++++++++++++++++++++++----------------------- package.json | 3 +- 4 files changed, 707 insertions(+), 674 deletions(-) create mode 100644 lib/.jshintrc diff --git a/Makefile b/Makefile index 848affb..f303448 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,7 @@ build: + @echo "Running jshint..." + @./node_modules/.bin/jshint lib/ComicBook.js --config lib/.jshintrc @echo "Compiling Handlebars templates..." @./node_modules/.bin/handlebars templates/*.handlebars -f lib/templates.js @echo "Compiling and minifying javascript..." diff --git a/lib/.jshintrc b/lib/.jshintrc new file mode 100644 index 0000000..7543422 --- /dev/null +++ b/lib/.jshintrc @@ -0,0 +1,20 @@ +{ + "predef": [ + "jQuery", + "Handlebars", + "Pixastic" + ], + "forin": true, + "noarg": true, + "noempty": true, + "eqeqeq": true, + "bitwise": true, + "strict": true, + "undef": true, + "unused": true, + "curly": true, + "browser": true, + "maxerr": 50, + "quotmark": "double", + "trailing": false +} diff --git a/lib/ComicBook.js b/lib/ComicBook.js index 0e2ccad..d5a7235 100755 --- a/lib/ComicBook.js +++ b/lib/ComicBook.js @@ -1,4 +1,4 @@ -/*jslint browser: true, on: true, eqeqeq: true, newcap: true, immed: true */ +/* exported ComicBook */ /* TODOs: @@ -13,7 +13,6 @@ - full browser test - IE9 / FF3.6+ / Chrome / Safari / Opera Nice 2 have: - - lint - jump to page? - make page draggable with the cursor - enable menu items via config, allow for custom items @@ -26,785 +25,796 @@ - unit test / integrate with travis CI */ -/** - * Merge two arrays. Any properties in b will replace the same properties in - * a. New properties from b will be added to a. - * - * @param a {Object} - * @param b {Object} - */ -function merge(a, b) { +var ComicBook = (function ($) { - var prop; + "use strict"; - if (typeof b === "undefined") { b = {}; } + /** + * Merge two arrays. Any properties in b will replace the same properties in + * a. New properties from b will be added to a. + * + * @param a {Object} + * @param b {Object} + */ + function merge(a, b) { - for (prop in a) { - if (a.hasOwnProperty(prop)) { - if (prop in b) { continue; } - b[prop] = a[prop]; + var prop; + + if (typeof b === "undefined") { b = {}; } + + for (prop in a) { + if (a.hasOwnProperty(prop)) { + if (prop in b) { continue; } + b[prop] = a[prop]; + } } + + return b; } - return b; -} - -/** - * Exception class. Always throw an instance of this when throwing exceptions. - * - * @param {String} type - * @param {Object} object - * @returns {ComicBookException} - */ -var ComicBookException = { - INVALID_ACTION: "invalid action", - INVALID_PAGE: "invalid page", - INVALID_PAGE_TYPE: "invalid page type", - UNDEFINED_CONTROL: "undefined control", - INVALID_ZOOM_MODE: "invalid zoom mode", - INVALID_NAVIGATION_EVENT: "invalid navigation event" -}; - -function ComicBook(id, srcs, opts) { - - var self = this; - var canvas_id = id; // canvas element id - this.srcs = srcs; // array of image srcs for pages - - var defaults = { - displayMode: "double", // single / double - zoomMode: "fitWidth", // manual / fitWidth - manga: false, // true / false - enhance: {}, - keyboard: { - next: 78, - previous: 80, - toolbar: 84, - toggleLayout: 76 - }, - libPath: "/lib/" + /** + * Exception class. Always throw an instance of this when throwing exceptions. + * + * @param {String} type + * @param {Object} object + * @returns {ComicBookException} + */ + var ComicBookException = { + INVALID_ACTION: "invalid action", + INVALID_PAGE: "invalid page", + INVALID_PAGE_TYPE: "invalid page type", + UNDEFINED_CONTROL: "undefined control", + INVALID_ZOOM_MODE: "invalid zoom mode", + INVALID_NAVIGATION_EVENT: "invalid navigation event" }; - var options = merge(defaults, opts); // options array for internal use + function ComicBook(id, srcs, opts) { - var no_pages = srcs.length; - var pages = []; // array of preloaded Image objects - var canvas; // the HTML5 canvas object - var context; // the 2d drawing context - var loaded = []; // the images that have been loaded so far - var scale = 1; // page zoom scale, 1 = 100% - var is_double_page_spread = false; - var controlsRendered = false; // have the user controls been inserted into the dom yet? - var page_requested = false; // used to request non preloaded pages - var shiv = false; + var self = this; + var canvas_id = id; // canvas element id + this.srcs = srcs; // array of image srcs for pages - /** - * Gets the window.innerWidth - scrollbars - */ - function windowWidth() { + var defaults = { + displayMode: "double", // single / double + zoomMode: "fitWidth", // manual / fitWidth + manga: false, // true / false + enhance: {}, + keyboard: { + next: 78, + previous: 80, + toolbar: 84, + toggleLayout: 76 + }, + libPath: "/lib/" + }; - var height = window.innerHeight + 1; + var options = merge(defaults, opts); // options array for internal use - if (shiv === false) { - shiv = $(document.createElement("div")) - .attr("id", "cb-width-shiv") - .css({ - width: "100%", - position: "absolute", - top: 0, - zIndex: "-1000" - }); + var no_pages = srcs.length; + var pages = []; // array of preloaded Image objects + var canvas; // the HTML5 canvas object + var context; // the 2d drawing context + var loaded = []; // the images that have been loaded so far + var scale = 1; // page zoom scale, 1 = 100% + var is_double_page_spread = false; + var controlsRendered = false; // have the user controls been inserted into the dom yet? + var page_requested = false; // used to request non preloaded pages + var shiv = false; - $("body").append(shiv); + /** + * Gets the window.innerWidth - scrollbars + */ + function windowWidth() { + + var height = window.innerHeight + 1; + + if (shiv === false) { + shiv = $(document.createElement("div")) + .attr("id", "cb-width-shiv") + .css({ + width: "100%", + position: "absolute", + top: 0, + zIndex: "-1000" + }); + + $("body").append(shiv); + } + + shiv.height(height); + + return shiv.innerWidth(); } - shiv.height(height); + /** + * enables the back button + */ + function checkHash() { - return shiv.innerWidth(); - } + var hash = getHash(); - /** - * enables the back button - */ - function checkHash() { + if (hash !== pointer && loaded.indexOf(hash) > -1) { + pointer = hash; + self.draw(); + } + } + function getHash() { + var hash = parseInt(location.hash.substring(1),10) - 1 || 0; + if (hash < 0) { + setHash(0); + hash = 0; + } + return hash; + } + + function setHash(pageNo) { + location.hash = pageNo; + } + + // page hash on first load var hash = getHash(); - if (hash !== pointer && loaded.indexOf(hash) > -1) { - pointer = hash; - self.draw(); - } - } + // the current page, can pass a default as a url hash + var pointer = (hash < srcs.length) ? hash : 0; - function getHash() { - var hash = parseInt(location.hash.substring(1),10) - 1 || 0; - if (hash < 0) { - setHash(0); - hash = 0; - } - return hash; - } + /** + * Setup the canvas element for use throughout the class. + * + * @see #ComicBook.prototype.draw + * @see #ComicBook.prototype.enhance + */ + function init() { - function setHash(pageNo) { - location.hash = pageNo; - } + // setup canvas + canvas = document.getElementById(canvas_id); + context = canvas.getContext("2d"); - // page hash on first load - var hash = getHash(); + // render user controls + if (controlsRendered === false) { + self.renderControls(); + controlsRendered = true; + } - // the current page, can pass a default as a url hash - var pointer = (hash < srcs.length) ? hash : 0; - - /** - * Setup the canvas element for use throughout the class. - * - * @see #ComicBook.prototype.draw - * @see #ComicBook.prototype.enhance - */ - function init() { - - // setup canvas - canvas = document.getElementById(canvas_id); - context = canvas.getContext("2d"); - - // render user controls - if (controlsRendered === false) { - self.renderControls(); - controlsRendered = true; + // add page controls + window.addEventListener("keydown", self.navigation, false); + window.addEventListener("hashchange", checkHash, false); } - // add page controls - window.addEventListener("keydown", self.navigation, false); - window.addEventListener("hashchange", checkHash, false); - } + /** + * Render Handlebars templates. Templates with data-trigger & data-action will + * have the specified events bound. + */ + ComicBook.prototype.renderControls = function () { - /** - * Render Handlebars templates. Templates with data-trigger & data-action will - * have the specified events bound. - */ - ComicBook.prototype.renderControls = function () { + var controls = {}; - var controls = {}; + $.each(Handlebars.templates, function (name, template) { - $.each(Handlebars.templates, function (name, template) { + var $template = $(template().trim()); + controls[name] = $template; - var $template = $(template().trim()); - controls[name] = $template; + // add event listeners to controls that specify callbacks + $template.find("*").andSelf().filter("[data-action][data-trigger]").each(function () { - // add event listeners to controls that specify callbacks - $template.find('*').andSelf().filter("[data-action][data-trigger]").each(function () { + var $this = $(this); + var trigger = $this.data("trigger"); + var action = $this.data("action"); - var $this = $(this); - var trigger = $this.data('trigger'); - var action = $this.data('action'); - - // trigger a direct method if exists - if (typeof self[$this.data('action')] === "function") { - $this.on(trigger, self[action]); - } - - // throw an event to be caught outside if the app code - $this.on(trigger, function (e) { - $(self).trigger(trigger, e); - }); - }); - - $(canvas).before($template); - }); - - this.controls = controls; - }; - - ComicBook.prototype.getControl = function(control) { - if (typeof this.controls[control] !== "object") { - throw ComicBookException.UNDEFINED_CONTROL + ' ' + control; - } - return this.controls[control]; - }; - - ComicBook.prototype.showControl = function(control) { - this.getControl(control).show().addClass("open"); - }; - - ComicBook.prototype.hideControl = function(control) { - this.getControl(control).removeClass("open").hide(); - }; - - ComicBook.prototype.toggleControl = function(control) { - this.getControl(control).toggle().toggleClass("open"); - }; - - ComicBook.prototype.toggleLayout = function() { - - var $control = self.getControl("toolbar").find("[data-action=toggleLayout]"); - var displayMode = (options.displayMode === "single") ? "double" : "single"; - - $control.removeClass(options.displayMode); - $control.addClass(displayMode); - - options.displayMode = displayMode; - - self.drawPage(); - }; - - /** - * Get the image for a given page. - * - * @return Image - */ - ComicBook.prototype.getPage = function(i) { - - if (i < 0 || i > srcs.length-1) { - throw ComicBookException.INVALID_PAGE+' '+i; - } - - if (typeof pages[i] === "object") { - return pages[i]; - } else { - page_requested = i; - this.showControl("loadingOverlay"); - } - }; - - /** - * @see #preload - */ - ComicBook.prototype.draw = function () { - - init(); - - // resize navigation controls - $(".navigate").outerHeight(window.innerHeight); - $("#cb-loading-overlay").outerWidth(windowWidth()).height(window.innerHeight); - - // preload images if needed - if (pages.length !== no_pages) { - this.preload(); - } else { - this.drawPage(); - } - }; - - /** - * Zoom the canvas - * - * @param new_scale {Number} Scale the canvas to this ratio - */ - ComicBook.prototype.zoom = function (new_scale) { - options.zoomMode = "manual"; - scale = new_scale; - if (typeof this.getPage(pointer) === "object") { this.drawPage(); } - }; - - ComicBook.prototype.zoomIn = function () { - self.zoom(scale + 0.1); - }; - - ComicBook.prototype.zoomOut = function () { - self.zoom(scale - 0.1); - }; - - ComicBook.prototype.fitWidth = function () { - options.zoomMode = "fitWidth" - ComicBook.prototype.drawPage(); - }; - - /** - * Preload all images, draw the page only after a given number have been loaded. - * - * @see #drawPage - */ - ComicBook.prototype.preload = function () { - - var i = pointer; // the current page counter for this method - var rendered = false; - var queue = []; - - this.showControl("loadingOverlay"); - - function loadImage(i) { - - var page = new Image(); - page.src = srcs[i]; - - page.onload = function () { - - pages[i] = this; - loaded.push(i); - - $("#cb-progress-bar .progressbar-value").css("width", Math.floor((loaded.length / no_pages) * 100) + "%"); - - // double page mode needs an extra page added - var buffer = (options.displayMode === "double" && pointer < srcs.length-1) ? 1 : 0; - - // start rendering the comic when the requested page is ready - if ((rendered === false && ($.inArray(pointer + buffer, loaded) !== -1) - || - (typeof page_requested === "number" && $.inArray(page_requested, loaded) !== -1)) - ) { - // if the user is waiting for a page to be loaded, render that one instead of the default pointer - if (typeof page_requested === "number") { - pointer = page_requested-1; - page_requested = false; + // trigger a direct method if exists + if (typeof self[$this.data("action")] === "function") { + $this.on(trigger, self[action]); } - self.drawPage(); - self.hideControl("loadingOverlay"); - rendered = true; + // throw an event to be caught outside if the app code + $this.on(trigger, function (e) { + $(self).trigger(trigger, e); + }); + }); + + $(canvas).before($template); + }); + + this.controls = controls; + }; + + ComicBook.prototype.getControl = function(control) { + if (typeof this.controls[control] !== "object") { + throw ComicBookException.UNDEFINED_CONTROL + " " + control; + } + return this.controls[control]; + }; + + ComicBook.prototype.showControl = function(control) { + this.getControl(control).show().addClass("open"); + }; + + ComicBook.prototype.hideControl = function(control) { + this.getControl(control).removeClass("open").hide(); + }; + + ComicBook.prototype.toggleControl = function(control) { + this.getControl(control).toggle().toggleClass("open"); + }; + + ComicBook.prototype.toggleLayout = function() { + + var $control = self.getControl("toolbar").find("[data-action=toggleLayout]"); + var displayMode = (options.displayMode === "single") ? "double" : "single"; + + $control.removeClass(options.displayMode); + $control.addClass(displayMode); + + options.displayMode = displayMode; + + self.drawPage(); + }; + + /** + * Get the image for a given page. + * + * @return Image + */ + ComicBook.prototype.getPage = function(i) { + + if (i < 0 || i > srcs.length-1) { + throw ComicBookException.INVALID_PAGE + " " + i; + } + + if (typeof pages[i] === "object") { + return pages[i]; + } else { + page_requested = i; + this.showControl("loadingOverlay"); + } + }; + + /** + * @see #preload + */ + ComicBook.prototype.draw = function () { + + init(); + + // resize navigation controls + $(".navigate").outerHeight(window.innerHeight); + $("#cb-loading-overlay").outerWidth(windowWidth()).height(window.innerHeight); + + // preload images if needed + if (pages.length !== no_pages) { + this.preload(); + } else { + this.drawPage(); + } + }; + + /** + * Zoom the canvas + * + * @param new_scale {Number} Scale the canvas to this ratio + */ + ComicBook.prototype.zoom = function (new_scale) { + options.zoomMode = "manual"; + scale = new_scale; + if (typeof this.getPage(pointer) === "object") { this.drawPage(); } + }; + + ComicBook.prototype.zoomIn = function () { + self.zoom(scale + 0.1); + }; + + ComicBook.prototype.zoomOut = function () { + self.zoom(scale - 0.1); + }; + + ComicBook.prototype.fitWidth = function () { + options.zoomMode = "fitWidth"; + ComicBook.prototype.drawPage(); + }; + + /** + * Preload all images, draw the page only after a given number have been loaded. + * + * @see #drawPage + */ + ComicBook.prototype.preload = function () { + + var i = pointer; // the current page counter for this method + var rendered = false; + var queue = []; + + this.showControl("loadingOverlay"); + + function loadImage(i) { + + var page = new Image(); + page.src = srcs[i]; + + page.onload = function () { + + pages[i] = this; + loaded.push(i); + + $("#cb-progress-bar .progressbar-value").css("width", Math.floor((loaded.length / no_pages) * 100) + "%"); + + // double page mode needs an extra page added + var buffer = (options.displayMode === "double" && pointer < srcs.length-1) ? 1 : 0; + + // start rendering the comic when the requested page is ready + if ((rendered === false && ($.inArray(pointer + buffer, loaded) !== -1) || + (typeof page_requested === "number" && $.inArray(page_requested, loaded) !== -1)) + ) { + // if the user is waiting for a page to be loaded, render that one instead of the default pointer + if (typeof page_requested === "number") { + pointer = page_requested-1; + page_requested = false; + } + + self.drawPage(); + self.hideControl("loadingOverlay"); + rendered = true; + } + + if (queue.length) { + loadImage(queue[0]); + queue.splice(0,1); + } else { + $("#cb-status").delay(500).fadeOut(); + } + }; + } + + // loads pages in both directions so you don't have to wait for all pages + // to be loaded before you can scroll backwards + function preload(start, stop) { + + var forward_buffer = 3; // TODO: make this into a config option? + + var j = 0; + var count = 1; + var forward = start; + var backward = start-1; + + while (forward <= stop) { + + if (count > forward_buffer && backward > -1) { + queue.push(backward); + backward--; + count = 0; + } else { + queue.push(forward); + forward++; + } + count++; } - if (queue.length) { - loadImage(queue[0]); - queue.splice(0,1); - } else { - $("#cb-status").delay(500).fadeOut(); - } - }; - } - - // loads pages in both directions so you don't have to wait for all pages - // to be loaded before you can scroll backwards - function preload(start, stop) { - - var forward_buffer = 3; // TODO: make this into a config option? - - var j = 0; - var count = 1; - var forward = start; - var backward = start-1; - - while (forward <= stop) { - - if (count > forward_buffer && backward > -1) { + while (backward > -1) { queue.push(backward); backward--; - count = 0; - } else { - queue.push(forward); - forward++; } - count++; + + loadImage(queue[j]); } - while (backward > -1) { - queue.push(backward); - backward--; + preload(i, srcs.length-1); + }; + + ComicBook.prototype.pageLoaded = function (page_no) { + return (typeof loaded[page_no-1] !== "undefined"); + }; + + /** + * Draw the current page in the canvas + */ + ComicBook.prototype.drawPage = function(page_no, reset_scroll) { + + var scrollY; + + reset_scroll = (typeof reset_scroll !== "undefined") ? reset_scroll : true; + scrollY = reset_scroll ? 0 : window.scrollY; + + // if a specific page is given try to render it, if not bail and wait for preload() to render it + if (typeof page_no === "number" && page_no < srcs.length && page_no > 0) { + pointer = page_no-1; + if (!this.pageLoaded(page_no)) { + this.showControl("loadingOverlay"); + return; + } } - loadImage(queue[j]); - } + if (pointer < 0) { pointer = 0; } - preload(i, srcs.length-1); - }; + var zoom_scale; + var offsetW = 0, offsetH = 0; - ComicBook.prototype.pageLoaded = function (page_no) { - return (typeof loaded[page_no-1] !== "undefined"); - }; + var page = self.getPage(pointer); + var page2 = false; - /** - * Draw the current page in the canvas - */ - ComicBook.prototype.drawPage = function(page_no, reset_scroll) { - - var reset_scroll = (typeof reset_scroll !== "undefined") ? reset_scroll : true; - var scrollY = reset_scroll ? 0 : window.scrollY; - - // if a specific page is given try to render it, if not bail and wait for preload() to render it - if (typeof page_no === "number" && page_no < srcs.length && page_no > 0) { - pointer = page_no-1; - if (!this.pageLoaded(page_no)) { - this.showControl("loadingOverlay"); - return; + if (options.displayMode === "double" && pointer < srcs.length-1) { + page2 = self.getPage(pointer + 1); } - } - if (pointer < 0) { pointer = 0; } + if (typeof page !== "object") { + throw ComicBookException.INVALID_PAGE_TYPE + " " + typeof page; + } - var zoom_scale; - var offsetW = 0, offsetH = 0; + var width = page.width; - var page = self.getPage(pointer); - var page2 = false; + // reset the canvas to stop duplicate pages showing + canvas.width = 0; + canvas.height = 0; - if (options.displayMode === "double" && pointer < srcs.length-1) { - page2 = self.getPage(pointer + 1); - } + // show double page spreads on a single page + is_double_page_spread = ( + typeof page2 === "object" && + (page.width > page.height || page2.width > page2.height) && + options.displayMode === "double" + ); + if (is_double_page_spread) { options.displayMode = "single"; } - if (typeof page !== "object") { - throw ComicBookException.INVALID_PAGE_TYPE+' '+typeof page; - } + if (options.displayMode === "double") { - var width = page.width; + // for double page spreads, factor in the width of both pages + if (typeof page2 === "object") { width += page2.width; } - // reset the canvas to stop duplicate pages showing - canvas.width = 0; - canvas.height = 0; + // if this is the last page and there is no page2, still keep the canvas wide + else { width += width; } + } - // show double page spreads on a single page - is_double_page_spread = ( - typeof page2 === "object" - && (page.width > page.height || page2.width > page2.height) - && options.displayMode === "double" - ); - if (is_double_page_spread) { options.displayMode = "single"; } + // update the page scale if a non manual mode has been chosen + switch(options.zoomMode) { - if (options.displayMode === "double") { + case "manual": + document.body.style.overflowX = "auto"; + zoom_scale = (options.displayMode === "double") ? scale * 2 : scale; + break; - // for double page spreads, factor in the width of both pages - if (typeof page2 === "object") { width += page2.width; } + case "fitWidth": + document.body.style.overflowX = "hidden"; - // if this is the last page and there is no page2, still keep the canvas wide - else { width += width; } - } + // scale up if the window is wider than the page, scale down if the window + // is narrower than the page + zoom_scale = (windowWidth() > width) ? ((windowWidth() - width) / windowWidth()) + 1 : windowWidth() / width; - // update the page scale if a non manual mode has been chosen - switch(options.zoomMode) { + // update the interal scale var so switching zoomModes while zooming will be smooth + scale = zoom_scale; + break; - case "manual": - document.body.style.overflowX = "auto"; - zoom_scale = (options.displayMode === "double") ? scale * 2 : scale; - break; + default: + throw ComicBookException.INVALID_ZOOM_MODE + " " + options.zoomMode; + } - case "fitWidth": - document.body.style.overflowX = "hidden"; + var canvas_width = page.width * zoom_scale; + var canvas_height = page.height * zoom_scale; - zoom_scale = (windowWidth() > width) - ? ((windowWidth() - width) / windowWidth()) + 1 // scale up if the window is wider than the page - : windowWidth() / width; // scale down if the window is narrower than the page + var page_width = (options.zoomMode === "manual") ? page.width * scale : canvas_width; + var page_height = (options.zoomMode === "manual") ? page.height * scale : canvas_height; - // update the interal scale var so switching zoomModes while zooming will be smooth - scale = zoom_scale - break; + canvas_height = page_height; - default: - throw ComicBookException.INVALID_ZOOM_MODE+' '+options.zoomMode; - } + // make sure the canvas is always at least full screen, even if the page is more narrow than the screen + canvas.width = (canvas_width < windowWidth()) ? windowWidth() : canvas_width; + canvas.height = (canvas_height < window.innerHeight) ? window.innerHeight : canvas_height; - var canvas_width = page.width * zoom_scale; - var canvas_height = page.height * zoom_scale; + // work out a horizontal position that will keep the pages always centred + if (canvas_width < windowWidth() && options.zoomMode === "manual") { + offsetW = (windowWidth() - page_width) / 2; + if (options.displayMode === "double") { offsetW = offsetW - page_width / 2; } + } - var page_width = (options.zoomMode === "manual") ? page.width * scale : canvas_width; - var page_height = (options.zoomMode === "manual") ? page.height * scale : canvas_height; + // work out a vertical position that will keep the pages always centred + if (canvas_height < window.innerHeight && options.zoomMode === "manual") { + offsetH = (window.innerHeight - page_height) / 2; + } - canvas_height = page_height; + // in manga double page mode reverse the page(s) + if (options.manga && options.displayMode === "double" && typeof page2 === "object") { + var tmpPage = page; + var tmpPage2 = page2; + page = tmpPage2; + page2 = tmpPage; + } - // make sure the canvas is always at least full screen, even if the page is more narrow than the screen - canvas.width = (canvas_width < windowWidth()) ? windowWidth() : canvas_width; - canvas.height = (canvas_height < window.innerHeight) ? window.innerHeight : canvas_height; + // draw the page(s) + context.drawImage(page, offsetW, offsetH, page_width, page_height); + if (options.displayMode === "double" && typeof page2 === "object") { + context.drawImage(page2, page_width + offsetW, offsetH, page_width, page_height); + } - // work out a horizontal position that will keep the pages always centred - if (canvas_width < windowWidth() && options.zoomMode === "manual") { - offsetW = (windowWidth() - page_width) / 2; - if (options.displayMode === "double") { offsetW = offsetW - page_width / 2; } - } + this.pixastic = new Pixastic(context, options.libPath + "pixastic/"); - // work out a vertical position that will keep the pages always centred - if (canvas_height < window.innerHeight && options.zoomMode === "manual") { - offsetH = (window.innerHeight - page_height) / 2; - } + // apply any image enhancements previously defined + $.each(options.enhance, function(action, options) { + self.enhance[action](options); + }); - // in manga double page mode reverse the page(s) - if (options.manga && options.displayMode === "double" && typeof page2 === "object") { - var tmpPage = page; - var tmpPage2 = page2; - page = tmpPage2; - page2 = tmpPage; - } + var current_page = + (options.displayMode === "double" && + pointer + 2 <= srcs.length) ? (pointer + 1) + "-" + (pointer + 2) : pointer + 1; - // draw the page(s) - context.drawImage(page, offsetW, offsetH, page_width, page_height); - if (options.displayMode === "double" && typeof page2 === "object") { - context.drawImage(page2, page_width + offsetW, offsetH, page_width, page_height); - } + this.getControl("toolbar") + .find("#current-page").text(current_page) + .end() + .find("#page-count").text(srcs.length); - this.pixastic = new Pixastic(context, options.libPath + "pixastic/"); + // revert page mode back to double if it was auto switched for a double page spread + if (is_double_page_spread) { options.displayMode = "double"; } - // apply any image enhancements previously defined - $.each(options.enhance, function(action, options) { - self.enhance[action](options); - }); + // disable the fit width button if needed + $("button.cb-fit-width").attr("disabled", (options.zoomMode === "fitWidth")); - var current_page = (options.displayMode === "double" && pointer+2 <= srcs.length) - ? (pointer+1) + "-" + (pointer+2) : pointer+1 + // disable prev/next buttons if not needed + $(".navigate").show(); + if (pointer === 0) { + $(".navigate-left").hide(); + $(".navigate-right").show(); + } - this.getControl('toolbar') - .find("#current-page").text(current_page) - .end() - .find("#page-count").text(srcs.length); + if (pointer === srcs.length-1 || (typeof page2 === "object" && pointer === srcs.length-2)) { + $(".navigate-left").show(); + $(".navigate-right").hide(); + } - // revert page mode back to double if it was auto switched for a double page spread - if (is_double_page_spread) { options.displayMode = "double"; } + $(this).trigger("navigate"); - // disable the fit width button if needed - $("button.cb-fit-width").attr("disabled", (options.zoomMode === "fitWidth")); + // update hash location + if (getHash() !== pointer) { + setHash(pointer + 1); + } - // disable prev/next buttons if not needed - $(".navigate").show(); - if (pointer === 0) { - $(".navigate-left").hide(); - $(".navigate-right").show(); - } + // make sure the top of the page is in view + window.scroll(0, scrollY); + }; - if (pointer === srcs.length-1 || (typeof page2 === "object" && pointer === srcs.length-2)) { - $(".navigate-left").show(); - $(".navigate-right").hide(); - } + /** + * Increment the counter and draw the page in the canvas + * + * @see #drawPage + */ + ComicBook.prototype.drawNextPage = function () { - $(this).trigger("navigate"); + var page; - // update hash location - if (getHash() !== pointer) { - setHash(pointer + 1); - } - - // make sure the top of the page is in view - window.scroll(0, scrollY); - }; - - /** - * Increment the counter and draw the page in the canvas - * - * @see #drawPage - */ - ComicBook.prototype.drawNextPage = function () { - - var page; - - try { - page = self.getPage(pointer+1); - } catch (e) {} - - if (!page) { return false; } - - if (pointer + 1 < pages.length) { - pointer += (options.displayMode === "single" || is_double_page_spread) ? 1 : 2; try { - self.drawPage(); + page = self.getPage(pointer+1); } catch (e) {} - } - }; - /** - * Decrement the counter and draw the page in the canvas - * - * @see #drawPage - */ - ComicBook.prototype.drawPrevPage = function () { + if (!page) { return false; } - var page; - - try { - page = self.getPage(pointer-1); - } catch (e) {} - - if (!page) { return false; } - - is_double_page_spread = (page.width > page.height); // need to run double page check again here as we are going backwards - - if (pointer > 0) { - pointer -= (options.displayMode === "single" || is_double_page_spread) ? 1 : 2; - self.drawPage(); - } - }; - - - ComicBook.prototype.brightness = function () { - self.enhance.brightness({ brightness: $(this).val() }); - } - - ComicBook.prototype.contrast = function () { - self.enhance.brightness({ contrast: $(this).val() }); - } - - ComicBook.prototype.sharpen = function () { - self.enhance.sharpen({ strength: $(this).val() }); - } - - ComicBook.prototype.desaturate = function () { - if ($(this).is(":checked")) { - self.enhance.desaturate(); - } else { - self.enhance.resaturate(); - } - }; - - ComicBook.prototype.resetEnhancements = function () { - self.enhance.reset(); - }; - - /** - * Apply image enhancements to the canvas. - * - * Powered by the awesome Pixastic: http://www.pixastic.com/ - * - * TODO: reset & apply all image enhancements each time before applying new one - * TODO: abstract this into an "Enhance" object, separate from ComicBook? - */ - ComicBook.prototype.enhance = { + if (pointer + 1 < pages.length) { + pointer += (options.displayMode === "single" || is_double_page_spread) ? 1 : 2; + try { + self.drawPage(); + } catch (e) {} + } + }; /** - * Reset enhancements. - * This can reset a specific enhancement if the method name is passed, or - * it will reset all. + * Decrement the counter and draw the page in the canvas * - * @param method {string} the specific enhancement to reset + * @see #drawPage */ - reset: function (method) { - if (!method) { - options.enhance = {}; + ComicBook.prototype.drawPrevPage = function () { + + var page; + + try { + page = self.getPage(pointer-1); + } catch (e) {} + + if (!page) { return false; } + + is_double_page_spread = (page.width > page.height); // need to run double page check again here as we are going backwards + + if (pointer > 0) { + pointer -= (options.displayMode === "single" || is_double_page_spread) ? 1 : 2; + self.drawPage(); + } + }; + + + ComicBook.prototype.brightness = function () { + self.enhance.brightness({ brightness: $(this).val() }); + }; + + ComicBook.prototype.contrast = function () { + self.enhance.brightness({ contrast: $(this).val() }); + }; + + ComicBook.prototype.sharpen = function () { + self.enhance.sharpen({ strength: $(this).val() }); + }; + + ComicBook.prototype.desaturate = function () { + if ($(this).is(":checked")) { + self.enhance.desaturate(); } else { - delete options.enhance[method]; + self.enhance.resaturate(); } - self.drawPage(null, false); - }, + }; + + ComicBook.prototype.resetEnhancements = function () { + self.enhance.reset(); + }; /** - * Pixastic progress callback - * @param {float} progress - */ - progress: function (progress) { - // console.info(Math.floor(progress * 100)); - }, - - /** - * Pixastic on complete callback - */ - done: function () { - - }, - - /** - * Adjust brightness / contrast + * Apply image enhancements to the canvas. * - * params - * brightness (int) -150 to 150 - * contrast: (float) -1 to infinity + * Powered by the awesome Pixastic: http://www.pixastic.com/ * - * @param {Object} params Brightness & contrast levels - * @param {Boolean} reset Reset before applying more enhancements? + * TODO: reset & apply all image enhancements each time before applying new one + * TODO: abstract this into an "Enhance" object, separate from ComicBook? */ - brightness: function (params, reset) { + ComicBook.prototype.enhance = { - if (reset !== false) { this.reset("brightness"); } - - // merge user options with defaults - var opts = merge({ brightness: 0, contrast: 0 }, params); - - // remember options for later - options.enhance.brightness = opts; - - // run the enhancement - self.pixastic.brightness({ - brightness: opts.brightness, - contrast: opts.contrast - }).done(this.done, this.progress); - }, - - /** - * Force black and white - */ - desaturate: function () { - options.enhance.desaturate = {}; - self.pixastic.desaturate().done(this.done, this.progress); - }, - - /** - * Undo desaturate - */ - resaturate: function() { - delete options.enhance.desaturate; - self.drawPage(null, false); - }, - - /** - * Sharpen - * - * options: - * strength: number (-1 to infinity) - * - * @param {Object} options - */ - sharpen: function (params) { - - this.desharpen(); - - var opts = merge({ strength: 0 }, params); - - options.enhance.sharpen = opts; - - self.pixastic.sharpen3x3({ - strength: opts.strength - }).done(this.done, this.progress); - }, - - desharpen: function() { - delete options.enhance.sharpen; - self.drawPage(null, false); - } - }; - - ComicBook.prototype.navigation = function (e) { - - // disable navigation when the overlay is showing - if ($("#cb-loading-overlay").is(":visible")) { return false; } - - var side = false; - - switch (e.type) { - - case "keydown": - - // navigation - if (e.keyCode === options.keyboard.previous) { side = "left"; } - if (e.keyCode === options.keyboard.next) { side = "right"; } - - // display controls - if (e.keyCode === options.keyboard.toolbar) { - self.toggleToolbar(); + /** + * Reset enhancements. + * This can reset a specific enhancement if the method name is passed, or + * it will reset all. + * + * @param method {string} the specific enhancement to reset + */ + reset: function (method) { + if (!method) { + options.enhance = {}; + } else { + delete options.enhance[method]; } - if (e.keyCode === options.keyboard.toggleLayout) { - self.toggleLayout(); + self.drawPage(null, false); + }, + + /** + * Pixastic progress callback + * @param {float} progress + */ + // progress: function (progress) { + progress: function () { + // console.info(Math.floor(progress * 100)); + }, + + /** + * Pixastic on complete callback + */ + done: function () { + + }, + + /** + * Adjust brightness / contrast + * + * params + * brightness (int) -150 to 150 + * contrast: (float) -1 to infinity + * + * @param {Object} params Brightness & contrast levels + * @param {Boolean} reset Reset before applying more enhancements? + */ + brightness: function (params, reset) { + + if (reset !== false) { this.reset("brightness"); } + + // merge user options with defaults + var opts = merge({ brightness: 0, contrast: 0 }, params); + + // remember options for later + options.enhance.brightness = opts; + + // run the enhancement + self.pixastic.brightness({ + brightness: opts.brightness, + contrast: opts.contrast + }).done(this.done, this.progress); + }, + + /** + * Force black and white + */ + desaturate: function () { + options.enhance.desaturate = {}; + self.pixastic.desaturate().done(this.done, this.progress); + }, + + /** + * Undo desaturate + */ + resaturate: function() { + delete options.enhance.desaturate; + self.drawPage(null, false); + }, + + /** + * Sharpen + * + * options: + * strength: number (-1 to infinity) + * + * @param {Object} options + */ + sharpen: function (params) { + + this.desharpen(); + + var opts = merge({ strength: 0 }, params); + + options.enhance.sharpen = opts; + + self.pixastic.sharpen3x3({ + strength: opts.strength + }).done(this.done, this.progress); + }, + + desharpen: function() { + delete options.enhance.sharpen; + self.drawPage(null, false); + } + }; + + ComicBook.prototype.navigation = function (e) { + + // disable navigation when the overlay is showing + if ($("#cb-loading-overlay").is(":visible")) { return false; } + + var side = false; + + switch (e.type) { + + case "keydown": + + // navigation + if (e.keyCode === options.keyboard.previous) { side = "left"; } + if (e.keyCode === options.keyboard.next) { side = "right"; } + + // display controls + if (e.keyCode === options.keyboard.toolbar) { + self.toggleToolbar(); + } + if (e.keyCode === options.keyboard.toggleLayout) { + self.toggleLayout(); + } + break; + + default: + throw ComicBookException.INVALID_NAVIGATION_EVENT + " " + e.type; + } + + if (side) { + + e.stopPropagation(); + + // western style (left to right) + if (!options.manga) { + if (side === "left") { self.drawPrevPage(); } + if (side === "right") { self.drawNextPage(); } + } + // manga style (right to left) + else { + if (side === "left") { self.drawNextPage(); } + if (side === "right") { self.drawPrevPage(); } } - break; - default: - throw ComicBookException.INVALID_NAVIGATION_EVENT+' '+e.type; - } - - if (side) { - - e.stopPropagation(); - - // western style (left to right) - if (!options.manga) { - if (side === "left") { self.drawPrevPage(); } - if (side === "right") { self.drawNextPage(); } - } - // manga style (right to left) - else { - if (side === "left") { self.drawNextPage(); } - if (side === "right") { self.drawPrevPage(); } + return false; } + }; - return false; - } - }; + ComicBook.prototype.destroy = function () { - ComicBook.prototype.destroy = function () { + $.each(this.controls, function (name, $control) { + $control.remove(); + }); - $.each(book.controls, function (name, $control) { - $control.remove(); - }); + canvas.width = 0; + canvas.height = 0; - canvas.width = 0; - canvas.height = 0; + window.removeEventListener("keydown", this.navigation, false); + window.removeEventListener("hashchange", checkHash, false); - window.removeEventListener("keydown", self.navigation, false); - window.removeEventListener("hashchange", checkHash, false); + setHash(""); - setHash(''); + // $(this).trigger("destroy"); + }; - // $(this).trigger("destroy"); } -} + return ComicBook; + +})(jQuery); diff --git a/package.json b/package.json index 8a881bf..e7f0507 100755 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ ], "devDependencies": { "handlebars": "1.0.10", - "uglify-js": "1.3.4" + "uglify-js": "1.3.4", + "jshint": "1.1.0" } }