From 36e9240e9b42db6ce15e3b812e73cdafb422f9f0 Mon Sep 17 00:00:00 2001 From: Bala Clark Date: Tue, 6 Sep 2011 09:12:33 +0200 Subject: [PATCH] Makefile now creates a combined js with pixastic dependencies and makes the minified version from that --- Makefile | 19 +- examples/basic.html | 4 - lib/ComicBook.combined.js | 1494 +++++++++++++++++++++++++++++++++++++ lib/ComicBook.js | 1 - lib/ComicBook.min.js | 46 +- 5 files changed, 1543 insertions(+), 21 deletions(-) create mode 100644 lib/ComicBook.combined.js diff --git a/Makefile b/Makefile index a15a90f..8deec33 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,18 @@ -lib/ComicBook.min.js : lib/ComicBook.js - java -jar bin/closure-complier/compiler.jar --compilation_level SIMPLE_OPTIMIZATIONS --js $< --js_output_file $@ \ No newline at end of file +SOURCES = lib/pixastic/pixastic.core.js \ + lib/pixastic/actions/brightness.js \ + lib/pixastic/actions/desaturate.js \ + lib/pixastic/actions/sharpen.js \ + lib/ComicBook.js + +all: reset lib/ComicBook.combined.js lib/ComicBook.min.js + +lib/ComicBook.combined.js: ${SOURCES} + cat > $@ $^ + +lib/ComicBook.min.js: lib/ComicBook.combined.js + java -jar bin/closure-complier/compiler.jar --compilation_level SIMPLE_OPTIMIZATIONS --js $< --js_output_file $@ + +reset: + rm lib/ComicBook.min.js + rm lib/ComicBook.combined.js diff --git a/examples/basic.html b/examples/basic.html index 25f7450..682480d 100755 --- a/examples/basic.html +++ b/examples/basic.html @@ -6,10 +6,6 @@ - - - - diff --git a/lib/ComicBook.combined.js b/lib/ComicBook.combined.js new file mode 100644 index 0000000..0c46b40 --- /dev/null +++ b/lib/ComicBook.combined.js @@ -0,0 +1,1494 @@ +/* + * Pixastic Lib - Core Functions - v0.1.3 + * Copyright (c) 2008 Jacob Seidelin, jseidelin@nihilogic.dk, http://blog.nihilogic.dk/ + * License: [http://www.pixastic.com/lib/license.txt] + */ + +var Pixastic = (function() { + + + function addEvent(el, event, handler) { + if (el.addEventListener) + el.addEventListener(event, handler, false); + else if (el.attachEvent) + el.attachEvent("on" + event, handler); + } + + function onready(handler) { + var handlerDone = false; + var execHandler = function() { + if (!handlerDone) { + handlerDone = true; + handler(); + } + } + document.write("<"+"script defer src=\"//:\" id=\"__onload_ie_pixastic__\">"); + var script = document.getElementById("__onload_ie_pixastic__"); + script.onreadystatechange = function() { + if (script.readyState == "complete") { + script.parentNode.removeChild(script); + execHandler(); + } + } + if (document.addEventListener) + document.addEventListener("DOMContentLoaded", execHandler, false); + addEvent(window, "load", execHandler); + } + + function init() { + var imgEls = getElementsByClass("pixastic", null, "img"); + var canvasEls = getElementsByClass("pixastic", null, "canvas"); + var elements = imgEls.concat(canvasEls); + for (var i=0;i -1) { + var tmp = actionName; + actionName = tmp.substr(0, tmp.indexOf("(")); + var arg = tmp.match(/\((.*?)\)/); + if (arg[1]) { + arg = arg[1].split(";"); + for (var a=0;a 255 ) + data[pix] = 255; + else if (r < 0) + data[pix] = 0; + else + data[pix] = r; + + if ((g = data[pix1=pix+1] * mul + add) > 255 ) + data[pix1] = 255; + else if (g < 0) + data[pix1] = 0; + else + data[pix1] = g; + + if ((b = data[pix2=pix+2] * mul + add) > 255 ) + data[pix2] = 255; + else if (b < 0) + data[pix2] = 0; + else + data[pix2] = b; + } + return true; + } + }, + checkSupport : function() { + return Pixastic.Client.hasCanvasImageData(); + } +} + +/* + * Pixastic Lib - Desaturation filter - v0.1.1 + * Copyright (c) 2008 Jacob Seidelin, jseidelin@nihilogic.dk, http://blog.nihilogic.dk/ + * License: [http://www.pixastic.com/lib/license.txt] + */ + +Pixastic.Actions.desaturate = { + + process : function(params) { + var useAverage = !!(params.options.average && params.options.average != "false"); + + if (Pixastic.Client.hasCanvasImageData()) { + var data = Pixastic.prepareData(params); + var rect = params.options.rect; + var w = rect.width; + var h = rect.height; + + var p = w*h; + var pix = p*4, pix1, pix2; + + if (useAverage) { + while (p--) + data[pix-=4] = data[pix1=pix+1] = data[pix2=pix+2] = (data[pix]+data[pix1]+data[pix2])/3 + } else { + while (p--) + data[pix-=4] = data[pix1=pix+1] = data[pix2=pix+2] = (data[pix]*0.3 + data[pix1]*0.59 + data[pix2]*0.11); + } + return true; + } else if (Pixastic.Client.isIE()) { + params.image.style.filter += " gray"; + return true; + } + }, + checkSupport : function() { + return (Pixastic.Client.hasCanvasImageData() || Pixastic.Client.isIE()); + } +}/* + * Pixastic Lib - Sharpen filter - v0.1.0 + * Copyright (c) 2008 Jacob Seidelin, jseidelin@nihilogic.dk, http://blog.nihilogic.dk/ + * License: [http://www.pixastic.com/lib/license.txt] + */ + +Pixastic.Actions.sharpen = { + process : function(params) { + + var strength = 0; + if (typeof params.options.amount != "undefined") + strength = parseFloat(params.options.amount)||0; + + if (strength < 0) strength = 0; + if (strength > 1) strength = 1; + + if (Pixastic.Client.hasCanvasImageData()) { + var data = Pixastic.prepareData(params); + var dataCopy = Pixastic.prepareData(params, true) + + var mul = 15; + var mulOther = 1 + 3*strength; + + var kernel = [ + [0, -mulOther, 0], + [-mulOther, mul, -mulOther], + [0, -mulOther, 0] + ]; + + var weight = 0; + for (var i=0;i<3;i++) { + for (var j=0;j<3;j++) { + weight += kernel[i][j]; + } + } + + weight = 1 / weight; + + var rect = params.options.rect; + var w = rect.width; + var h = rect.height; + + mul *= weight; + mulOther *= weight; + + var w4 = w*4; + var y = h; + do { + var offsetY = (y-1)*w4; + + var nextY = (y == h) ? y - 1 : y; + var prevY = (y == 1) ? 0 : y-2; + + var offsetYPrev = prevY*w4; + var offsetYNext = nextY*w4; + + var x = w; + do { + var offset = offsetY + (x*4-4); + + var offsetPrev = offsetYPrev + ((x == 1) ? 0 : x-2) * 4; + var offsetNext = offsetYNext + ((x == w) ? x-1 : x) * 4; + + var r = (( + - dataCopy[offsetPrev] + - dataCopy[offset-4] + - dataCopy[offset+4] + - dataCopy[offsetNext]) * mulOther + + dataCopy[offset] * mul + ); + + var g = (( + - dataCopy[offsetPrev+1] + - dataCopy[offset-3] + - dataCopy[offset+5] + - dataCopy[offsetNext+1]) * mulOther + + dataCopy[offset+1] * mul + ); + + var b = (( + - dataCopy[offsetPrev+2] + - dataCopy[offset-2] + - dataCopy[offset+6] + - dataCopy[offsetNext+2]) * mulOther + + dataCopy[offset+2] * mul + ); + + + if (r < 0 ) r = 0; + if (g < 0 ) g = 0; + if (b < 0 ) b = 0; + if (r > 255 ) r = 255; + if (g > 255 ) g = 255; + if (b > 255 ) b = 255; + + data[offset] = r; + data[offset+1] = g; + data[offset+2] = b; + + } while (--x); + } while (--y); + + return true; + + } + }, + checkSupport : function() { + return Pixastic.Client.hasCanvasImageData(); + } +} +/*jslint browser: true, on: true, eqeqeq: true, newcap: true, immed: true */ + +/* + TODOs: + + Fo sho: + - fix last page loading bug + - disable the strech button if in an auto zoom mode + - improve prev/next buttons, only show them when they can possibly work (not at beginning/end) + - check for html5 feature support where used: diveintohtml5.org/everything.html or www.modernizr.com + - write bin scripts to minify & join all js + + Nice 2 have: + - jump to page? + - enable menu items via config, allow for custom items + - decouple controls from reader api + - split out classes into seperate files + - offline access + - thumbnail browser + - remove jquery dependency in favour of straight js + - chrome frame / ExplorerCanvas / non canvas version? + - really need to speed up enhancements, try to use webworkers +*/ + +/** + * 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 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; +} + +/** + * Exception class. Always throw an instance of this when throwing exceptions. + * + * @param {String} type + * @param {Object} object + * @returns {ComicBookException} + */ +function ComicBookException(type, object) { + + this.type = type; + this.object = object; + + this.INVALID_PAGE = "invalid page"; + this.INVALID_PAGE_TYPE = "invalid page type"; + this.UNDEFINED_CONTROL = "undefined control"; + this.INVALID_ZOOM_MODE = "invalid zoom mode"; + this.INVALID_NAVIGATION_EVENT = "invalid navigation event"; +}; + +function ComicBook(id, srcs, opts) { + + 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 + } + }; + + var options = merge(defaults, opts); // options array for internal use + + 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 buffer = 1; // image preload buffer level + 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; + + // the current page, can pass a default as a url hash + var pointer = (parseInt(location.hash.substring(1),10) - 1) || 0; + + /** + * 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(); + } + + /** + * enables the back button + */ + function checkHash() { + + var hash = getHash(); + + if (hash !== pointer && loaded.indexOf(hash) > -1) { + pointer = hash; + ComicBook.prototype.draw(); + } + } + + function getHash() { + return parseInt(location.hash.substring(1),10) - 1 || 0; + } + + function setHash(pageNo) { + location.hash = pageNo; + } + + /** + * 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) { + ComicBook.prototype.renderControls(); + controlsRendered = true; + } + + // add page controls + // TODO: add IE event listeners too. + canvas.addEventListener("click", ComicBook.prototype.navigation, false); + window.addEventListener("keydown", ComicBook.prototype.navigation, false); + window.addEventListener("hashchange", checkHash, false); + //setInterval(function() { checkHash(); }, 300); // TODO: enable this when there is no onhashchange event + } + + /** + * User controls + * + * TODO: add reset links, + * TODO: style + * TODO: don't allow draggable controls to leave the visible window + * TODO: remember draggable position + * TODO: show/hide controls + * TODO: save current values + */ + ComicBook.prototype.control = { + + status: $(document.createElement("p")) + .attr("id", "cb-status") + .addClass("cb-control cb-always-on"), + + toolbar: $(document.createElement("div")) + .hide() + .attr("id", "cb-toolbar") + .addClass("cb-control") + .append( + $(document.createElement("button")) + .attr("title", "close the toolbar") + .addClass("cb-close") + .click(function(){ + ComicBook.prototype.toggleToolbar(); + }) + ) + .append( + $(document.createElement("button")) + .attr("title", "switch between dual and single page modes") + .addClass("cb-layout " + options.displayMode) + .click(function(){ + ComicBook.prototype.toggleLayout(); + }) + ) + .append( + $(document.createElement("button")) + .attr("title", "tweak the page colors") + .addClass("cb-color cb-menu-button") + .click(function(){ + ComicBook.prototype.toggleControl("color"); + }) + ) + .append( + $(document.createElement("button")) + .attr("title", "zoom out") + .addClass("cb-zoom-out") + .click(function(){ + ComicBook.prototype.zoom(scale - 0.1); + }) + ) + .append( + $(document.createElement("button")) + .attr("title", "zoom in") + .addClass("cb-zoom-in") + .click(function(){ + ComicBook.prototype.zoom(scale + 0.1); + }) + ) + .append( + $(document.createElement("button")) + .attr("title", "fit to page width") + .addClass("cb-fit-width") + .click(function(){ + options.zoomMode = "fitWidth" + ComicBook.prototype.drawPage(); + }) + ) + .append( + $(document.createElement("p")) + .attr("id", "cb-comic-info") + .append(" / " + srcs.length) + ), + + /** + * Image enhancements + * TODO: split out brightness / contrast controls? + */ + color: $(document.createElement("div")) + .attr("id", "cb-color") + .addClass("cb-control") + .append("") + .append( + $("
").slider({ + value: 0, + step: 10, + min: -1000, + max: 1000, + slide: function(event, ui) { + ComicBook.prototype.enhance.brightness({ brightness: ui.value }); + } + }) + ) + .append("") + .append( + $("
").slider({ + value: 0, + step: 0.1, + min: 0, + max: 1, + slide: function(event, ui) { + ComicBook.prototype.enhance.brightness({ contrast: ui.value }); + } + }) + ) + .append("") + .append( + $("
").slider({ + value: 0, + step: 0.1, + min: 0, + max: 1, + slide: function(event, ui) { + ComicBook.prototype.enhance.sharpen({ amount: ui.value }); + } + }) + ) + .append( + $(document.createElement("div")).addClass("cb-option") + .append(" ") + .append("") + ), + + /** + * Page navigation + */ + navigation: { + + left: $(document.createElement("div")) + .addClass("cb-control cb-navigate cb-always-on left") + .click(function(e){ + ComicBook.prototype.drawPrevPage(); + }), + + right: $(document.createElement("div")) + .addClass("cb-control cb-navigate cb-always-on right") + .click(function(e) { + ComicBook.prototype.drawNextPage(); + }) + }, + + loadingOverlay: $(document.createElement("div")) + .attr("id", "cb-loading-overlay") + .addClass("cb-control") + }; + + /** + * TODO: center, make sure they never leave the visible portion of the screen + */ + ComicBook.prototype.renderControls = function() { + + $(canvas) + .before(this.getControl("loadingOverlay")) + .before(this.getControl("status")) + .after(this.getControl("toolbar")) + .after(this.getControl("navigation").left) + .after(this.getControl("navigation").right) + .after(this.getControl("color").hide()); + + $(".cb-menu-button").click(function(e) { + $(this).toggleClass("active"); + }); + + $("#cb-desaturate").click(function(){ + if ($(this).is(":checked")) { + ComicBook.prototype.enhance.desaturate(); + } else { + ComicBook.prototype.enhance.resaturate(); + } + }); + + $("#cb-reset").click(function() { + // TODO: improve performance here. + $("#cb-brightness").slider("value", 0); + $("#cb-contrast").slider("value", 0); + $("#cb-saturation").slider("value", 0); + $("#cb-sharpen").slider("value", 0); + var desaturate = $("#cb-desaturate"); + desaturate.attr("checked", false); + ComicBook.prototype.enhance.reset(); + }); + }; + + ComicBook.prototype.getControl = function(control) { + + if (typeof this.control[control] === "undefined") { + throw new ComicBookException(ComicBookException.UNDEFINED_CONTROL, control); + } + + return this.control[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.toggleToolbar = function() { + if ($("#cb-toolbar").is(":visible")) { + $(".cb-control").not(".cb-always-on").hide(); + } else { + $("#cb-toolbar, .cb-control.open").show(); + } + }; + + ComicBook.prototype.toggleLayout = function() { + if (options.displayMode === "double") { + $("#cb-toolbar .cb-layout").removeClass("double"); + options.displayMode = "single"; + } else { + $("#cb-toolbar .cb-layout").removeClass("single"); + options.displayMode = "double"; + } + $("#cb-toolbar .cb-layout").addClass(options.displayMode); + ComicBook.prototype.drawPage(); + }; + + /** + * Get the image for a given page. + * + * @return Image + */ + ComicBook.prototype.getPage = function(i) { + + if (i < 0 || i > srcs.length) { + throw new ComicBookException(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 + $(".cb-control.cb-navigate").outerHeight(window.innerHeight); + $("#cb-toolbar").outerWidth(windowWidth()); + $("#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(); } + }; + + /** + * Preload all images, draw the page only after a given number have been loaded. + * + * @see #drawPage + */ + ComicBook.prototype.preload = function () { + + this.showControl("loadingOverlay"); + + //var srcs = this.srcs; + + if (no_pages < buffer) { buffer = no_pages; } // don't get stuck if the buffer level is higher than the number of pages + + var i = pointer; // the current page counter for this method + //if (i - buffer >= 0) { i = i - buffer; } // start loading from the first requested page - buffer + + // I am using recursion instead of a forEach loop so that the next image is + // only loaded when the previous one has completely finished + function preload(i) { + + var page = new Image(); + var padding; + + $("#cb-status").text("loading page " + (i + 1) + " of " + no_pages); + + page.src = srcs[i]; + + page.onload = function () { + + pages[i] = this; + loaded.push(i); + + // start to load from the beginning if loading started midway + if (i === no_pages-1 && loaded.length !== no_pages) { + i = -1; + } + + // there are still more pages to load, do it + if (loaded.length < no_pages) { + i++; + preload(i); + } + + //console.log(loaded[loaded.length-1]); + + // double page mode needs an extra page added to the buffer + padding = (options.displayMode === "double") ? 1 : 0; + + // start rendering the comic when the buffer level has been reached (FIXME: buggy, fails if trying to load the last couple of pages) + if (loaded[loaded.length-1] === pointer + buffer + padding || loaded[loaded.length-1] === page_requested) { + + // 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; + } + + ComicBook.prototype.drawPage(); + ComicBook.prototype.hideControl("loadingOverlay"); + } + if (loaded.length === no_pages) { ComicBook.prototype.hideControl("status") } + }; + } + + // manually trigger the first load + preload(i); + }; + + 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) { + + // 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) { + pointer = page_no-1; + if (!this.pageLoaded(page_no)) { + this.showControl("loadingOverlay"); + return; + } + } + + var zoom_scale; + var offsetW = 0, offsetH = 0; + + var page = ComicBook.prototype.getPage(pointer); + var page2 = ComicBook.prototype.getPage(pointer + 1); + + if (typeof page !== "object") { + throw new ComicBookException(ComicBookException.INVALID_PAGE_TYPE, typeof page); + } + + var width = page.width; + + // reset the canvas to stop duplicate pages showing + canvas.width = 0; + canvas.height = 0; + + // show double page spreads on a single page + is_double_page_spread = ((page.width > page.height || page2.width > page2.height) && options.displayMode === "double"); + if (is_double_page_spread) { options.displayMode = "single"; } + + if (options.displayMode === "double") { + + // for double page spreads, factor in the width of both pages + if (typeof page2 === "object") { width += page2.width; } + + // if this is the last page and there is no page2, still keep the canvas wide + else { width += width; } + } + + // update the page scale if a non manual mode has been chosen + switch(options.zoomMode) { + + case "manual": + document.body.style.overflowX = "auto"; + zoom_scale = (options.displayMode === "double") ? scale * 2 : scale; + break; + + case "fitWidth": + document.body.style.overflowX = "hidden"; + + 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 + + // update the interal scale var so switching zoomModes while zooming will be smooth + scale = zoom_scale + break; + + default: + throw new ComicBookException(ComicBookException.INVALID_ZOOM_MODE, options.zoomMode); + } + + var canvas_width = page.width * zoom_scale; + var canvas_height = page.height * zoom_scale; + + var page_width = (options.zoomMode === "manual") ? page.width * scale : canvas_width; + var page_height = (options.zoomMode === "manual") ? page.height * scale : canvas_height; + + canvas_height = page_height; + + // 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; + + // 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; } + } + + // 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; + } + + // in manga double page mode reverse the page(s) + if (options.manga && options.displayMode === "double") { + var tmpPage = page; + var tmpPage2 = page2; // FIXME: check this exists before using + page = tmpPage2; + page2 = tmpPage; + } + + // 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); } + + // apply any image enhancements previously defined + $.each(options.enhance, function(action, options) { + ComicBook.prototype.enhance[action](options); + }); + + var current_page = (options.displayMode === "double") ? (pointer+1) + "-" + (pointer+2) : pointer+1 + $("#cb-current-page").text(current_page); + + // revert page mode back to double if it was auto switched for a double page spread + if (is_double_page_spread) { options.displayMode = "double"; } + + // user callback + if (typeof options.afterDrawPage === "function") { + options.afterDrawPage(pointer + 1); + } + + // update hash location + if (getHash() !== pointer) { + setHash(pointer + 1); + } + + // make sure the top of the page is in view + window.scroll(0, 0); + }; + + /** + * Increment the counter and draw the page in the canvas + * + * @see #drawPage + */ + ComicBook.prototype.drawNextPage = function () { + + if (!this.getPage(pointer+1)) { return false; } + + if (pointer + 1 < pages.length) { + pointer += (options.displayMode === "single" || is_double_page_spread) ? 1 : 2; + this.drawPage(); + } + }; + + /** + * Decrement the counter and draw the page in the canvas + * + * @see #drawPage + */ + ComicBook.prototype.drawPrevPage = function () { + + var page = this.getPage(pointer-1); + 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; + this.drawPage(); + } + }; + + /** + * 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 = { + + /** + * 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]; + } + ComicBook.prototype.drawPage(); + }, + + /** + * 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 + Pixastic.process(canvas, "brightness", { + brightness: opts.brightness, + contrast: opts.contrast, + legacy: true + }); + + init(); + }, + + /** + * Force black and white + */ + desaturate: function () { + + options.enhance.desaturate = {}; + + Pixastic.process(canvas, "desaturate", { average : false }); + + init(); + }, + + /** + * Undo desaturate + */ + resaturate: function() { + delete options.enhance.desaturate; + ComicBook.prototype.drawPage(); + }, + + /** + * Sharpen + * + * options: + * amount: number (-1 to infinity) + * + * @param {Object} options + */ + sharpen: function (params) { + + this.desharpen(); + + var opts = merge({ amount: 0 }, params); + + options.enhance.sharpen = opts; + + Pixastic.process(canvas, "sharpen", { + amount: opts.amount + }); + + init(); + }, + + desharpen: function() { + delete options.enhance.sharpen; + ComicBook.prototype.drawPage(); + } + }; + + 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 "click": + ComicBook.prototype.toggleToolbar(); + break; + 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) { + ComicBook.prototype.toggleToolbar(); + } + if (e.keyCode === options.keyboard.toggleLayout) { + ComicBook.prototype.toggleLayout(); + } + break; + default: + throw new ComicBookException( + ComicBookException.INVALID_NAVIGATION_EVENT, e.type + ); + } + + if (side) { + + e.stopPropagation(); + + // western style (left to right) + if (!options.manga) { + if (side === "left") { ComicBook.prototype.drawPrevPage(); } + if (side === "right") { ComicBook.prototype.drawNextPage(); } + } + // manga style (right to left) + else { + if (side === "left") { ComicBook.prototype.drawNextPage(); } + if (side === "right") { ComicBook.prototype.drawPrevPage(); } + } + + return false; + } + }; + +} diff --git a/lib/ComicBook.js b/lib/ComicBook.js index ef51e2b..6efc955 100755 --- a/lib/ComicBook.js +++ b/lib/ComicBook.js @@ -7,7 +7,6 @@ - fix manga mode - trigger preload if requesting valid but not loaded images (can happen if network was interupted) - check for html5 feature support where used: diveintohtml5.org/everything.html or www.modernizr.com - - write bin scripts to minify & join all js - when applying enhancements reading position gets lost - loading bar - full browser test - IE9 / FF3.6+ / Chrome / Safari / Opera diff --git a/lib/ComicBook.min.js b/lib/ComicBook.min.js index 5e1baf0..2464326 100644 --- a/lib/ComicBook.min.js +++ b/lib/ComicBook.min.js @@ -1,20 +1,38 @@ -function merge(j,g){var k;typeof g==="undefined"&&(g={});for(k in j)j.hasOwnProperty(k)&&!(k in g)&&(g[k]=j[k]);return g}function ComicBookException(j,g){this.type=j;this.object=g;this.INVALID_PAGE="invalid page";this.INVALID_PAGE_TYPE="invalid page type";this.UNDEFINED_CONTROL="undefined control";this.INVALID_ZOOM_MODE="invalid zoom mode";this.INVALID_NAVIGATION_EVENT="invalid navigation event"} -function ComicBook(j,g,k){function m(){var a=window.innerHeight+1;o===!1&&(o=$(document.createElement("div")).attr("id","cb-width-shiv").css({width:"100%",position:"absolute",top:0,zIndex:"-1000"}),$("body").append(o));o.height(a);return o.innerWidth()}function w(){var a=parseInt(location.hash.substring(1),10)-1||0;a!==d&&h.indexOf(a)>-1&&(d=a,ComicBook.prototype.draw())}function s(){i=document.getElementById(x);t=i.getContext("2d");v===!1&&(ComicBook.prototype.renderControls(),v=!0);i.addEventListener("click", -ComicBook.prototype.navigation,!1);window.addEventListener("keydown",ComicBook.prototype.navigation,!1);window.addEventListener("hashchange",w,!1)}var x=j;this.srcs=g;var b=merge({displayMode:"double",zoomMode:"fitWidth",manga:!1,enhance:{},keyboard:{next:78,previous:80,toolbar:84,toggleLayout:76}},k),e=g.length,p=[],i,t,u=1,h=[],n=1,q=!1,v=!1,r=!1,o=!1,d=parseInt(location.hash.substring(1),10)-1||0;ComicBook.prototype.control={status:$(document.createElement("p")).attr("id","cb-status").addClass("cb-control cb-always-on"), +var Pixastic=function(){function c(a,b,e){a.addEventListener?a.addEventListener(b,e,!1):a.attachEvent&&a.attachEvent("on"+b,e)}function k(a){var b=!1,e=function(){b||(b=!0,a())};document.write('