221 lines
5.8 KiB
JavaScript
221 lines
5.8 KiB
JavaScript
|
var h = require('hyperscript')
|
||
|
var NativeImage = require('native-image')
|
||
|
var createHash = require('multiblob/util').createHash
|
||
|
var pull = require('pull-stream')
|
||
|
var pushable = require('pull-pushable')
|
||
|
var app = require('../app')
|
||
|
|
||
|
if (!('URL' in window) && ('webkitURL' in window))
|
||
|
window.URL = window.webkitURL
|
||
|
|
||
|
module.exports = function (opts) {
|
||
|
opts = opts || {}
|
||
|
|
||
|
// markup
|
||
|
|
||
|
var fileInput = h('input', { type: 'file', accept: 'image/*', onchange: fileChosen })
|
||
|
var canvas = h('canvas', {
|
||
|
onmousedown: onmousedown,
|
||
|
onmouseup: onmouseup,
|
||
|
onmouseout: onmouseup,
|
||
|
onmousemove: onmousemove,
|
||
|
width: 275,
|
||
|
height: 275
|
||
|
})
|
||
|
var zoomSlider = h('input', { type: 'range', value: 0, oninput: onresize })
|
||
|
var existing = h('.image-uploader-existing', opts.existing ? h('img', { src: opts.existing }) : '')
|
||
|
var viewer = h('div', existing, fileInput)
|
||
|
var editormsg = h('small', 'drag to crop')
|
||
|
var editor = h('.image-uploader-editor',
|
||
|
{ style: 'display: none' },
|
||
|
editormsg, h('br'),
|
||
|
canvas,
|
||
|
h('p', zoomSlider),
|
||
|
h('div',
|
||
|
h('button.btn.btn-3d.pull-right.savebtn', { onclick: onsave }, 'OK'),
|
||
|
h('button.btn.btn-3d', { onclick: oncancel }, 'Cancel')))
|
||
|
var el = h('.image-uploader', viewer, editor)
|
||
|
el.forceDone = forceDone.bind(el, opts)
|
||
|
|
||
|
// handlers
|
||
|
|
||
|
var img = h('img'), imgdim
|
||
|
var dragging = false, mx, my, ox=0, oy=0, zoom=1, minzoom=1
|
||
|
|
||
|
function draw () {
|
||
|
var ctx = canvas.getContext('2d')
|
||
|
ctx.globalCompositeOperation = 'source-over'
|
||
|
ctx.fillStyle = '#000'
|
||
|
ctx.fillRect(0, 0, canvas.width, canvas.height)
|
||
|
ctx.drawImage(img, ox, oy, img.width * zoom, img.height * zoom)
|
||
|
|
||
|
if (dragging)
|
||
|
drawHexagonOverlay()
|
||
|
}
|
||
|
function drawHexagonOverlay () {
|
||
|
// hexagon coords (based on the behavior of the css hexagon)
|
||
|
var left = 20
|
||
|
var right = canvas.width - 20
|
||
|
var w12 = canvas.width / 2
|
||
|
var h14 = canvas.height / 4
|
||
|
var h34 = h14 * 3
|
||
|
|
||
|
var ctx = canvas.getContext('2d')
|
||
|
ctx.save()
|
||
|
ctx.fillStyle = '#fff'
|
||
|
ctx.globalAlpha = 0.75;
|
||
|
ctx.globalCompositeOperation = 'overlay'
|
||
|
ctx.beginPath()
|
||
|
ctx.moveTo(w12, 0)
|
||
|
ctx.lineTo(right, h14)
|
||
|
ctx.lineTo(right, h34)
|
||
|
ctx.lineTo(w12, canvas.height)
|
||
|
ctx.lineTo(left, h34)
|
||
|
ctx.lineTo(left, h14)
|
||
|
ctx.lineTo(w12, 0)
|
||
|
ctx.closePath()
|
||
|
ctx.fill()
|
||
|
ctx.restore()
|
||
|
}
|
||
|
|
||
|
function fileChosen (e) {
|
||
|
editor.style.display = 'block'
|
||
|
viewer.style.display = 'none'
|
||
|
editormsg.innerText = 'loading...'
|
||
|
|
||
|
// give the html renderer a turn before loading the image
|
||
|
// if the image is large, it'll block for a sec, and we want to render "loading..." first
|
||
|
setTimeout(function () {
|
||
|
var file = fileInput.files[0]
|
||
|
var ni = NativeImage.createFromPath(file.path)
|
||
|
img.src = ni.toDataUrl()
|
||
|
|
||
|
imgdim = ni.getSize()
|
||
|
var smallest = (imgdim.width < imgdim.height) ? imgdim.width : imgdim.height
|
||
|
ox = oy = 0
|
||
|
minzoom = zoom = 275/smallest
|
||
|
zoomSlider.value = 0
|
||
|
|
||
|
editormsg.innerText = 'drag to crop'
|
||
|
draw()
|
||
|
}, 100)
|
||
|
|
||
|
/*
|
||
|
:OLD: browser method, doesnt work in electron
|
||
|
var reader = new FileReader()
|
||
|
reader.onload = function (e) {
|
||
|
ox = oy = 0
|
||
|
zoom = 1
|
||
|
zoomSlider.value = 50
|
||
|
img.src = e.target.result
|
||
|
|
||
|
draw()
|
||
|
editor.style.display = 'block'
|
||
|
viewer.style.display = 'none'
|
||
|
}
|
||
|
reader.readAsDataURL(file)
|
||
|
*/
|
||
|
}
|
||
|
|
||
|
function onmousedown (e) {
|
||
|
e.preventDefault()
|
||
|
dragging = true
|
||
|
mx = e.clientX
|
||
|
my = e.clientY
|
||
|
draw()
|
||
|
}
|
||
|
|
||
|
function onmouseup (e) {
|
||
|
e.preventDefault()
|
||
|
dragging = false
|
||
|
draw()
|
||
|
}
|
||
|
|
||
|
function onmousemove (e) {
|
||
|
e.preventDefault()
|
||
|
if (dragging) {
|
||
|
ox = Math.max(Math.min(ox + e.clientX - mx, 0), -imgdim.width * zoom + 275)
|
||
|
oy = Math.max(Math.min(oy + e.clientY - my, 0), -imgdim.height * zoom + 275)
|
||
|
draw()
|
||
|
mx = e.clientX
|
||
|
my = e.clientY
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function onresize (e) {
|
||
|
zoom = minzoom + (zoomSlider.value / 100)
|
||
|
draw()
|
||
|
}
|
||
|
|
||
|
function onsave (e) {
|
||
|
e.preventDefault()
|
||
|
if (!opts.onupload)
|
||
|
throw "onupload not specified"
|
||
|
|
||
|
var hasher = createHash('sha256')
|
||
|
var ps = pushable()
|
||
|
pull(
|
||
|
ps,
|
||
|
hasher,
|
||
|
app.ssb.blobs.add(function (err) {
|
||
|
if(err)
|
||
|
return modals.error('Failed to Upload Image to Blobstore', err)
|
||
|
|
||
|
fileInput.value = ''
|
||
|
editor.style.display = 'none'
|
||
|
viewer.style.display = 'block'
|
||
|
opts.onupload(hasher)
|
||
|
})
|
||
|
)
|
||
|
|
||
|
// Send to sbot
|
||
|
var dataUrl = canvas.toDataURL('image/png')
|
||
|
existing.querySelector('img').setAttribute('src', dataUrl)
|
||
|
ps.push(NativeImage.createFromDataUrl(dataUrl).toPng())
|
||
|
ps.end()
|
||
|
|
||
|
/*
|
||
|
:OLD: browser method, doesnt work in electron
|
||
|
canvas.toBlob(function (blob) {
|
||
|
// Send to sbot
|
||
|
var reader = new FileReader()
|
||
|
reader.onloadend = function () {
|
||
|
ps.push(new Buffer(new Uint8Array(reader.result)))
|
||
|
ps.end()
|
||
|
}
|
||
|
reader.readAsArrayBuffer(blob)
|
||
|
|
||
|
// Update "existing" img
|
||
|
var blobUrl = URL.createObjectURL(blob)
|
||
|
existing.querySelector('img').setAttribute('src', blobUrl)
|
||
|
setTimeout(function() { URL.revokeObjectURL(blobUrl) }, 50) // give 50ms to render first
|
||
|
}, 'image/png')
|
||
|
*/
|
||
|
}
|
||
|
|
||
|
function oncancel (e) {
|
||
|
e.preventDefault()
|
||
|
fileInput.value = ''
|
||
|
editor.style.display = 'none'
|
||
|
viewer.style.display = 'block'
|
||
|
}
|
||
|
|
||
|
return el
|
||
|
}
|
||
|
|
||
|
// helper to finish the edit in case the user forgets to press "OK"
|
||
|
function forceDone (opts, cb) {
|
||
|
this.forceDone = null // detach for memory cleanup
|
||
|
|
||
|
// not editing?
|
||
|
if (this.querySelector('.image-uploader-editor').style.display != 'block')
|
||
|
return cb() // we're good
|
||
|
|
||
|
// update cb to run after onupload
|
||
|
var onupload = opts.onupload
|
||
|
opts.onupload = function (hasher) {
|
||
|
onupload(hasher)
|
||
|
cb()
|
||
|
}
|
||
|
this.querySelector('.savebtn').click() // trigger upload
|
||
|
}
|