Skip to content

Instantly share code, notes, and snippets.

@qqueue
Last active December 12, 2015 18:38
Show Gist options
  • Save qqueue/0bd42b745e4eb5b0b7ca to your computer and use it in GitHub Desktop.
Save qqueue/0bd42b745e4eb5b0b7ca to your computer and use it in GitHub Desktop.
streaming comic book zip reader
<!doctype html>
<meta charset=utf-8>
<title>zip stream</title>
<style>
</style>
<div id=thumbs></div>
<div id=full></div>
<script src=pako.js></script>
<script src=DataStream.js></script>
<script src=zipstream.js></script>
<!doctype html>
<meta charset=utf-8>
<title>progressive</title>
<style>
</style>
<body>
<script src=progressive.js></script>
img = document.body.append-child new Image
xhr = new XMLHttpRequest
..open \GET \052.png
..response-type = \moz-blob
last = 0
..onprogress = !->
if @response
return if (Date.now! - last) < 1000ms
last := Date.now!
if img.src
URL.revokeObjectURL img.src
img.src = URL.createObjectURL @response
..send!
body {
background-color: black;
margin: 0;
overflow: hidden;
}
#scrollcontainer {
position: absolute;
left: 0;
top: 0;
right: -18px;
bottom: 0;
overflow-x: hidden;
overflow-y: scroll;
}
#pages {
direction: rtl;
text-align: center;
line-height: 100vh;
width: 100vw;
}
a {
display: inline-block;
height: 100vh;
}
.page {
max-height:calc(100vh - 1px);
max-width: calc(100vw - 1px);
margin: 0.5px;
vertical-align: middle;
}
<!doctype html>
<meta charset=utf-8>
<title>reader</title>
<link rel=stylesheet href=style.css>
<body>
<div id=scrollcontainer>
<div id=pages></div>
<div id=scrollhandle></div>
</div>
<script src=pako.js></script>
<script src=DataStream.js></script>
<script src=ui.js></script>
"use strict"
url = location.search.substring 1 # minus '?'
get-range = (bytes, cb) !->
xhr = new XMLHttpRequest
..open \GET url
..response-type = \arraybuffer
..set-request-header \Range "bytes=#bytes"
..onload = !->
cb new DataStream @response
..send!
ds <-! get-range \-22
s = ds.readStruct [
\magic \string:4
\diskno \uint16
\centralDirectoryDisk \uint16
\totalEntriesOnDisk \uint16
\totalEntries \uint16
\cdSize \uint32
\cdOffset \uint32
# don't care about comment length or comment
# since we're explicitly reading 22 bytes back
# it has to be empty anyway
]
console.log s
if not (s.magic is \PK\x05\x06 or s.magic is \PK\x03\x04)
throw new Error \invalid!
central <-! get-range "#{s.cdOffset}-#{s.cdOffset + s.cdSize}"
{headers} = central.readStruct [
\headers [
'[]'
[
\sig \string:4
\madeBy \uint16
\versionNeeded \uint16
\bitFlags \uint16
\compression \uint16
\lastModTime \uint16
\lastModDate \uint16
\crc32 \uint32
\compressedSize \uint32
\uncompressedSize \uint32
\filenameLength \uint16
\extraFieldLength \uint16
\fileCommentLength \uint16
\diskNoStart \uint16
\internalAttrs \uint16
\externalAttrs \uint32
\lhOffset \uint32
\filename \string:filenameLength
\extraField ['[]' \uint8 \extraFieldLength]
\comment ['[]' \uint8 \fileCommentLength]
]
s.totalEntriesOnDisk
]
# don't care about signature
]
console.log headers
headers = headers.sort (a,b) ->
if a.filename > b.filename
1
else if b.filename > a.filename
-1
else
0
headers = headers.filter ->
/\.png$/.test it.filename or /\.jpg$/.test it.filename
for header, i in headers
header.start = header.lhOffset + 30 + header.filenameLength
header.end = header.start + header.compressedSize - 1
header.i = i
imgs = []
container = document.get-element-by-id \scrollcontainer
just-scrolled = false
document.body.add-event-listener \keydown !->
if it.key is \Left or it.key is \ArrowLeft or it.keyIdentifier is \Left
container.scrollTop -= window.innerHeight
it.preventDefault!
else if it.key is \Right or it.key is \ArrowRight or it.keyIdentifier is \Right
container.scrollTop += window.innerHeight
it.preventDefault!
else if it.key is \Enter or it.keyIdentifier is \Enter
document.documentElement.mozRequestFullScreen?!
document.documentElement.webkitRequestFullscreen?(Element.ALLOW_KEYBOARD_INPUT)
todo = new Set headers
pages = document.get-element-by-id \pages
xhr = new XMLHttpRequest
..open \GET url
..response-type = \moz-blob
..onloadend = !-> document.title = 'reader'
..onprogress = !->
document.title = "#{Math.round(it.loaded / it.total * 100)}%"
res = @response
nu-todo = new Set
todo.for-each (header) !->
if res.size > header.end
new FileReader
..onload = !->
if header.compression is 8
data = pako.inflate-raw new Uint8Array @result
else
data = new Uint8Array @result
type =
if /\.png$/.test header.filename
\image/png
else
\image/jpeg
blob = new Blob [data], {type}
url = URL.createObjectURL blob
img = new Image
..src = url
..title = header.filename
..class-name = \page
..onclick = !->
{bottom, top} = this.get-bounding-client-rect!
console.log top, bottom
i = header.i
imgs[i] = img
a = document.create-element \a
..name = i
..href = "\##i"
a.append-child img
# assume we get them in order (pretty safe)
pages.append-child a
..read-as-array-buffer res.slice header.start, header.end + 1
else
nu-todo.add header
todo := nu-todo
..send!
in-zone = false
container
..add-event-listener \mousemove !->
if window.innerWidth - it.clientX < 100
if not in-zone
container.style.right = \0
in-zone := true
else
if in-zone
container.style.right = \-18px
in-zone := false
var timeout
container.add-event-listener \scroll !->
if just-scrolled
just-scrolled := false
return
container.style.right = \0
clearTimeout timeout
timeout := setTimeout do
!->
if not in-zone
container.style.right = \-18px
1000
"use strict"
#url = \http://localhost:8888
url = location.hash.substring 1
get-range = (bytes, cb) !->
xhr = new XMLHttpRequest
..open \GET url
..response-type = \arraybuffer
..set-request-header \Range "bytes=#bytes"
..onload = !->
cb new DataStream @response
..send!
# prefetch test
fetch = (url, cb) !->
console.log \fetching
xhr = new XMLHttpRequest
..open \GET url
..response-type = \arraybuffer
..onloadend = !->
console.log \fetched...
cb!
..send!
console.log \fetched?
#<-! fetch url
console.log \loaded?
ds <-! get-range \-22
s = ds.readStruct [
\magic \string:4
\diskno \uint16
\centralDirectoryDisk \uint16
\totalEntriesOnDisk \uint16
\totalEntries \uint16
\cdSize \uint32
\cdOffset \uint32
# don't care about comment length or comment
# since we're explicitly reading 22 bytes back
# it has to be empty anyway
]
if s.magic is not \PK\x05\x06
throw new Error \invalid!
console.log s
central <-! get-range "#{s.cdOffset}-#{s.cdOffset + s.cdSize}"
{headers} = central.readStruct [
\headers [
'[]'
[
\sig \string:4
\madeBy \uint16
\versionNeeded \uint16
\bitFlags \uint16
\compression \uint16
\lastModTime \uint16
\lastModDate \uint16
\crc32 \uint32
\compressedSize \uint32
\uncompressedSize \uint32
\filenameLength \uint16
\extraFieldLength \uint16
\fileCommentLength \uint16
\diskNoStart \uint16
\internalAttrs \uint16
\externalAttrs \uint32
\lhOffset \uint32
\filename \string:filenameLength
\extraField ['[]' \uint8 \extraFieldLength]
\comment ['[]' \uint8 \fileCommentLength]
]
s.totalEntriesOnDisk
]
# don't care about signature
]
thumbs = document.get-element-by-id \thumbs
full = document.get-element-by-id \full
headers = headers.sort (a,b) ->
if a.filename > b.filename
1
else if b.filename > a.filename
-1
else
0
queue = []
overall = document.body.append-child document.create-element \progress
for let header in headers
console.log header
start = header.lhOffset + 30 + header.filenameLength
end = start + header.compressedSize - 1
fimg = full.append-child document.create-element \img
span = thumbs.append-child document.create-element \span
progress = span.append-child document.create-element \progress
xhr = new XMLHttpRequest
..open \GET url
..response-type = \arraybuffer
..set-request-header \Range "bytes=#start-#end"
..onload = !->
if header.compression is 8
data = pako.inflate-raw new Uint8Array @response
else
data = new Uint8Array @response
type =
if /\.png$/.test header.filename
\image/png
else
\image/jpeg
console.log type
blob = new Blob [data], {type}
url = URL.createObjectURL blob
timg = new Image
..style.max-width = \50px
..src = url
fimg
..style.max-width = \100%
..src = url
span.append-child timg
span.remove-child progress
..onprogress = !->
if it.length-computable
progress.value = it.loaded / it.total
overall.value = it.loaded / it.total
..onloadend = !->
if queue.shift!
that.send!
else
document.body.remove-child overall
queue.push xhr
queue.shift!send!
file = document.get-element-by-id \file
f = new FileReader
..onload = !->
js = new JSZip it.target.result
console.log js
f.readAsArrayBuffer file.files.0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment