From 458527a62a22ec4b013fa3fd2766fa4f802bff0f Mon Sep 17 00:00:00 2001 From: Bala Clark Date: Wed, 14 Sep 2011 18:03:35 +0200 Subject: [PATCH 01/16] starting to preload in both directions --- lib/ComicBook.js | 81 +++++++++++++++++++++++------------------------- 1 file changed, 39 insertions(+), 42 deletions(-) diff --git a/lib/ComicBook.js b/lib/ComicBook.js index a1f1962..fb06147 100755 --- a/lib/ComicBook.js +++ b/lib/ComicBook.js @@ -455,58 +455,55 @@ function ComicBook(id, srcs, opts) { this.showControl("loadingOverlay"); var i = pointer; // the current page counter for this method - var rendered = false; - - // 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) { + function loadImage(i) { +console.log(i); var page = new Image(); - - $("#cb-status").text("loading page " + (i + 1) + " of " + no_pages); - page.src = srcs[i]; page.onload = function () { - pages[i] = this; loaded.push(i); + $("#cb-status").text("loaded page " + (i + 1) + " of " + no_pages); - // 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); - } - - // 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; - } - - ComicBook.prototype.drawPage(); - ComicBook.prototype.hideControl("loadingOverlay"); - rendered = true; - } - if (loaded.length === no_pages) { ComicBook.prototype.hideControl("status") } + // TODO: put back in the page render code, page request code }; } - // manually trigger the first load - preload(i); + // Loads the pages at a rate of 3 forward, 1 back. This should make scrolling + // pages in both directions faster. + function preload(start, stop) { + + var forward_buffer = 3; + + var count = 2; + var forward = start+1; + var backward = start-1; + + // always load the first requested image first + // TODO: remove the need for this? + loadImage(start); + + while (forward <= stop) { + + if (count > forward_buffer && backward > -1) { + loadImage(backward); + backward--; + count = 0; + } else { + loadImage(forward); + } + forward++; + count++; + } + + while (backward > -1) { + loadImage(backward); + backward--; + } + } + + preload(i, srcs.length-1); }; ComicBook.prototype.pageLoaded = function (page_no) { @@ -631,7 +628,7 @@ function ComicBook(id, srcs, opts) { ComicBook.prototype.enhance[action](options); }); - var current_page = (options.displayMode === "double" && pointer+2 <= srcs.length) + var current_page = (options.displayMode === "double" && pointer+2 <= srcs.length) ? (pointer+1) + "-" + (pointer+2) : pointer+1 $("#cb-current-page").text(current_page); From dc46a6bf4d9e366faca3c51fae203814fc353e5e Mon Sep 17 00:00:00 2001 From: Bala Clark Date: Thu, 15 Sep 2011 09:39:05 +0200 Subject: [PATCH 02/16] preload bugfix --- lib/ComicBook.js | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/lib/ComicBook.js b/lib/ComicBook.js index fb06147..a58c3ef 100755 --- a/lib/ComicBook.js +++ b/lib/ComicBook.js @@ -457,7 +457,7 @@ function ComicBook(id, srcs, opts) { var i = pointer; // the current page counter for this method function loadImage(i) { -console.log(i); + var page = new Image(); page.src = srcs[i]; @@ -474,16 +474,12 @@ console.log(i); // pages in both directions faster. function preload(start, stop) { - var forward_buffer = 3; + var forward_buffer = 3; // TODO: make this into a config option? - var count = 2; - var forward = start+1; + var count = 1; + var forward = start; var backward = start-1; - // always load the first requested image first - // TODO: remove the need for this? - loadImage(start); - while (forward <= stop) { if (count > forward_buffer && backward > -1) { @@ -492,8 +488,8 @@ console.log(i); count = 0; } else { loadImage(forward); + forward++; } - forward++; count++; } From 4d38b42d2eed239ef18bbb9a4d352e5714687ec0 Mon Sep 17 00:00:00 2001 From: Bala Clark Date: Thu, 15 Sep 2011 11:23:24 +0200 Subject: [PATCH 03/16] show loading percentage --- lib/ComicBook.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/ComicBook.js b/lib/ComicBook.js index a58c3ef..6aa30f1 100755 --- a/lib/ComicBook.js +++ b/lib/ComicBook.js @@ -10,7 +10,7 @@ - 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 - - use a loading bar / percentage indicator instead of explictly showing which pages are loading + - loading bar - full browser test - IE9 / FF3.6+ / Chrome / Safari / Opera Nice 2 have: @@ -464,7 +464,8 @@ function ComicBook(id, srcs, opts) { page.onload = function () { pages[i] = this; loaded.push(i); - $("#cb-status").text("loaded page " + (i + 1) + " of " + no_pages); + + $("#cb-status").text("loading " + Math.floor((loaded.length / no_pages) * 100) + "%"); // TODO: put back in the page render code, page request code }; From c29c8b8275af688bffbe48fd149f7880c703e29c Mon Sep 17 00:00:00 2001 From: Bala Clark Date: Fri, 16 Sep 2011 15:22:08 +0200 Subject: [PATCH 04/16] put back page rendering code into the preload method --- lib/ComicBook.js | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/lib/ComicBook.js b/lib/ComicBook.js index 6aa30f1..6ee288d 100755 --- a/lib/ComicBook.js +++ b/lib/ComicBook.js @@ -452,9 +452,10 @@ function ComicBook(id, srcs, opts) { */ ComicBook.prototype.preload = function () { - this.showControl("loadingOverlay"); - var i = pointer; // the current page counter for this method + var rendered = false; + + this.showControl("loadingOverlay"); function loadImage(i) { @@ -462,17 +463,41 @@ function ComicBook(id, srcs, opts) { page.src = srcs[i]; page.onload = function () { + pages[i] = this; loaded.push(i); - $("#cb-status").text("loading " + Math.floor((loaded.length / no_pages) * 100) + "%"); + var percentage_loaded = Math.floor((loaded.length / no_pages) * 100); - // TODO: put back in the page render code, page request code + $("#cb-status").text("loading " + percentage_loaded + "%"); + + // 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; + } + + ComicBook.prototype.drawPage(); + ComicBook.prototype.hideControl("loadingOverlay"); + rendered = true; + } + + if (percentage_loaded === 100) { + $("#cb-status").delay(500).fadeOut(); + } }; } - // Loads the pages at a rate of 3 forward, 1 back. This should make scrolling - // pages in both directions faster. + // 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? From a18b08f728b83924346ed6340ab1f4542ed0ad8c Mon Sep 17 00:00:00 2001 From: Bala Clark Date: Fri, 16 Sep 2011 15:39:54 +0200 Subject: [PATCH 05/16] now using 'golden boy' as the example comic --- examples/basic.html | 38 +++++++++++++++++++--------- examples/goldenboy/goldenboy_00.jpg | Bin 0 -> 101432 bytes examples/goldenboy/goldenboy_01.jpg | Bin 0 -> 68358 bytes examples/goldenboy/goldenboy_02.jpg | Bin 0 -> 24819 bytes examples/goldenboy/goldenboy_03.jpg | Bin 0 -> 157204 bytes examples/goldenboy/goldenboy_04.jpg | Bin 0 -> 161879 bytes examples/goldenboy/goldenboy_05.jpg | Bin 0 -> 141351 bytes examples/goldenboy/goldenboy_06.jpg | Bin 0 -> 176926 bytes examples/goldenboy/goldenboy_07.jpg | Bin 0 -> 180860 bytes examples/goldenboy/goldenboy_08.jpg | Bin 0 -> 184574 bytes examples/goldenboy/goldenboy_09.jpg | Bin 0 -> 161488 bytes examples/goldenboy/goldenboy_10.jpg | Bin 0 -> 162851 bytes examples/goldenboy/goldenboy_11.jpg | Bin 0 -> 184555 bytes examples/goldenboy/goldenboy_12.jpg | Bin 0 -> 168920 bytes examples/goldenboy/goldenboy_13.jpg | Bin 0 -> 186227 bytes examples/goldenboy/goldenboy_14.jpg | Bin 0 -> 141125 bytes examples/goldenboy/goldenboy_15.jpg | Bin 0 -> 136046 bytes examples/goldenboy/goldenboy_16.jpg | Bin 0 -> 171236 bytes examples/goldenboy/goldenboy_17.jpg | Bin 0 -> 107617 bytes examples/goldenboy/goldenboy_18.jpg | Bin 0 -> 28800 bytes examples/goldenboy/goldenboy_19.jpg | Bin 0 -> 117718 bytes examples/goldenboy/goldenboy_20.jpg | Bin 0 -> 128299 bytes examples/goldenboy/goldenboy_21.jpg | Bin 0 -> 149191 bytes examples/goldenboy/goldenboy_22.jpg | Bin 0 -> 131986 bytes examples/goldenboy/goldenboy_23.jpg | Bin 0 -> 155376 bytes examples/goldenboy/goldenboy_24.jpg | Bin 0 -> 142688 bytes examples/goldenboy/goldenboy_25.jpg | Bin 0 -> 177371 bytes 27 files changed, 26 insertions(+), 12 deletions(-) create mode 100644 examples/goldenboy/goldenboy_00.jpg create mode 100644 examples/goldenboy/goldenboy_01.jpg create mode 100644 examples/goldenboy/goldenboy_02.jpg create mode 100644 examples/goldenboy/goldenboy_03.jpg create mode 100644 examples/goldenboy/goldenboy_04.jpg create mode 100644 examples/goldenboy/goldenboy_05.jpg create mode 100644 examples/goldenboy/goldenboy_06.jpg create mode 100644 examples/goldenboy/goldenboy_07.jpg create mode 100644 examples/goldenboy/goldenboy_08.jpg create mode 100644 examples/goldenboy/goldenboy_09.jpg create mode 100644 examples/goldenboy/goldenboy_10.jpg create mode 100644 examples/goldenboy/goldenboy_11.jpg create mode 100644 examples/goldenboy/goldenboy_12.jpg create mode 100644 examples/goldenboy/goldenboy_13.jpg create mode 100644 examples/goldenboy/goldenboy_14.jpg create mode 100644 examples/goldenboy/goldenboy_15.jpg create mode 100644 examples/goldenboy/goldenboy_16.jpg create mode 100644 examples/goldenboy/goldenboy_17.jpg create mode 100644 examples/goldenboy/goldenboy_18.jpg create mode 100644 examples/goldenboy/goldenboy_19.jpg create mode 100644 examples/goldenboy/goldenboy_20.jpg create mode 100644 examples/goldenboy/goldenboy_21.jpg create mode 100644 examples/goldenboy/goldenboy_22.jpg create mode 100644 examples/goldenboy/goldenboy_23.jpg create mode 100644 examples/goldenboy/goldenboy_24.jpg create mode 100644 examples/goldenboy/goldenboy_25.jpg diff --git a/examples/basic.html b/examples/basic.html index b2ed035..6347720 100755 --- a/examples/basic.html +++ b/examples/basic.html @@ -22,18 +22,32 @@ - + diff --git a/lib/ComicBook.min.js b/lib/ComicBook.min.js new file mode 100644 index 0000000..5e1baf0 --- /dev/null +++ b/lib/ComicBook.min.js @@ -0,0 +1,20 @@ +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"), +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 "+b.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(n-0.1)})).append($(document.createElement("button")).attr("title","zoom in").addClass("cb-zoom-in").click(function(){ComicBook.prototype.zoom(n+0.1)})).append($(document.createElement("button")).attr("title","fit to page width").addClass("cb-fit-width").click(function(){b.zoomMode="fitWidth";ComicBook.prototype.drawPage()})).append($(document.createElement("p")).attr("id","cb-comic-info").append(" / "+ +g.length)),color:$(document.createElement("div")).attr("id","cb-color").addClass("cb-control").append("").append($("
").slider({value:0,step:10,min:-1E3,max:1E3,slide:function(a,b){ComicBook.prototype.enhance.brightness({brightness:b.value})}})).append("").append($("
").slider({value:0,step:0.1,min:0,max:1,slide:function(a,b){ComicBook.prototype.enhance.brightness({contrast:b.value})}})).append("").append($("
").slider({value:0, +step:0.1,min:0,max:1,slide:function(a,b){ComicBook.prototype.enhance.sharpen({amount:b.value})}})).append($(document.createElement("div")).addClass("cb-option").append(" ").append("")),navigation:{left:$(document.createElement("div")).addClass("cb-control cb-navigate cb-always-on left").click(function(){ComicBook.prototype.drawPrevPage()}),right:$(document.createElement("div")).addClass("cb-control cb-navigate cb-always-on right").click(function(){ComicBook.prototype.drawNextPage()})}, +loadingOverlay:$(document.createElement("div")).attr("id","cb-loading-overlay").addClass("cb-control")};ComicBook.prototype.renderControls=function(){$(i).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(){$(this).toggleClass("active")});$("#cb-desaturate").click(function(){$(this).is(":checked")? +ComicBook.prototype.enhance.desaturate():ComicBook.prototype.enhance.resaturate()});$("#cb-reset").click(function(){$("#cb-brightness").slider("value",0);$("#cb-contrast").slider("value",0);$("#cb-saturation").slider("value",0);$("#cb-sharpen").slider("value",0);$("#cb-desaturate").attr("checked",!1);ComicBook.prototype.enhance.reset()})};ComicBook.prototype.getControl=function(a){if(typeof this.control[a]==="undefined")throw new ComicBookException(ComicBookException.UNDEFINED_CONTROL,a);return this.control[a]}; +ComicBook.prototype.showControl=function(a){this.getControl(a).show().addClass("open")};ComicBook.prototype.hideControl=function(a){this.getControl(a).removeClass("open").hide()};ComicBook.prototype.toggleControl=function(a){this.getControl(a).toggle().toggleClass("open")};ComicBook.prototype.toggleToolbar=function(){$("#cb-toolbar").is(":visible")?$(".cb-control").not(".cb-always-on").hide():$("#cb-toolbar, .cb-control.open").show()};ComicBook.prototype.toggleLayout=function(){b.displayMode==="double"? +($("#cb-toolbar .cb-layout").removeClass("double"),b.displayMode="single"):($("#cb-toolbar .cb-layout").removeClass("single"),b.displayMode="double");$("#cb-toolbar .cb-layout").addClass(b.displayMode);ComicBook.prototype.drawPage()};ComicBook.prototype.getPage=function(a){if(a<0||a>g.length)throw new ComicBookException(ComicBookException.INVALID_PAGE,a);if(typeof p[a]==="object")return p[a];else r=a,this.showControl("loadingOverlay")};ComicBook.prototype.draw=function(){s();$(".cb-control.cb-navigate").outerHeight(window.innerHeight); +$("#cb-toolbar").outerWidth(m());$("#cb-loading-overlay").outerWidth(m()).height(window.innerHeight);p.length!==e?this.preload():this.drawPage()};ComicBook.prototype.zoom=function(a){b.zoomMode="manual";n=a;typeof this.getPage(d)==="object"&&this.drawPage()};ComicBook.prototype.preload=function(){function a(c){var i=new Image,f;$("#cb-status").text("loading page "+(c+1)+" of "+e);i.src=g[c];i.onload=function(){p[c]=this;h.push(c);c===e-1&&h.length!==e&&(c=-1);h.lengthf.height||e.width>e.height)&&b.displayMode==="double")b.displayMode="single";b.displayMode==="double"&&(l+=typeof e==="object"?e.width:l);switch(b.zoomMode){case "manual":document.body.style.overflowX="auto";c=b.displayMode==="double"?n*2:n;break;case "fitWidth":document.body.style.overflowX= +"hidden";n=c=m()>l?(m()-l)/m()+1:m()/l;break;default:throw new ComicBookException(ComicBookException.INVALID_ZOOM_MODE,b.zoomMode);}var l=f.width*c,j=f.height*c;c=b.zoomMode==="manual"?f.width*n:l;var k=b.zoomMode==="manual"?f.height*n:j,j=k;i.width=la.height;d>0&&(d-=b.displayMode==="single"||q?1:2,this.drawPage())};ComicBook.prototype.enhance={reset:function(a){a?delete b.enhance[a]:b.enhance={};ComicBook.prototype.drawPage()},brightness:function(a,c){c!==!1&&this.reset("brightness");var d=merge({brightness:0,contrast:0},a);b.enhance.brightness=d;Pixastic.process(i,"brightness",{brightness:d.brightness, +contrast:d.contrast,legacy:!0});s()},desaturate:function(){b.enhance.desaturate={};Pixastic.process(i,"desaturate",{average:!1});s()},resaturate:function(){delete b.enhance.desaturate;ComicBook.prototype.drawPage()},sharpen:function(a){this.desharpen();a=merge({amount:0},a);b.enhance.sharpen=a;Pixastic.process(i,"sharpen",{amount:a.amount});s()},desharpen:function(){delete b.enhance.sharpen;ComicBook.prototype.drawPage()}};ComicBook.prototype.navigation=function(a){if($("#cb-loading-overlay").is(":visible"))return!1; +var c=!1;switch(a.type){case "click":ComicBook.prototype.toggleToolbar();break;case "keydown":a.keyCode===b.keyboard.previous&&(c="left");a.keyCode===b.keyboard.next&&(c="right");a.keyCode===b.keyboard.toolbar&&ComicBook.prototype.toggleToolbar();a.keyCode===b.keyboard.toggleLayout&&ComicBook.prototype.toggleLayout();break;default:throw new ComicBookException(ComicBookException.INVALID_NAVIGATION_EVENT,a.type);}if(c)return a.stopPropagation(),b.manga?(c==="left"&&ComicBook.prototype.drawNextPage(), +c==="right"&&ComicBook.prototype.drawPrevPage()):(c==="left"&&ComicBook.prototype.drawPrevPage(),c==="right"&&ComicBook.prototype.drawNextPage()),!1}}; From 36e9240e9b42db6ce15e3b812e73cdafb422f9f0 Mon Sep 17 00:00:00 2001 From: Bala Clark Date: Tue, 6 Sep 2011 09:12:33 +0200 Subject: [PATCH 11/16] 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('