diff --git a/src/Posting/QR.coffee b/src/Posting/QR.coffee
index b301abe69..e66b5d594 100644
--- a/src/Posting/QR.coffee
+++ b/src/Posting/QR.coffee
@@ -338,93 +338,19 @@ QR =
@value = null
return unless files.length
QR.cleanNotifications()
- for file, i in files
- QR.handleFile file, i, files.length
+ for file in files
+ QR.handleFile file, files.length
$.addClass QR.nodes.el, 'dump' unless files.length is 1
- handleFile: (file, index, nfiles) ->
- isSingle = nfiles is 1
- if /^text\//.test file.type
- if isSingle
- post = QR.selected
- else if index isnt 0 or (post = QR.posts[QR.posts.length - 1]).com
- post = new QR.post()
- post.pasteText file
- return
- unless file.type in QR.mimeTypes
- QR.error "#{file.name}: Unsupported file type."
- return unless isSingle
- max = QR.nodes.fileInput.max
- max = Math.min(max, QR.max_size_video) if /^video\//.test file.type
- if file.size > max
- QR.error "#{file.name}: File too large (file: #{$.bytesToString file.size}, max: #{$.bytesToString max})."
- return unless isSingle
- isNewPost = false
- if isSingle
+ handleFile: (file, nfiles) ->
+ isText = /^text\//.test file.type
+ if nfiles is 1
post = QR.selected
- else if index isnt 0 or (post = QR.posts[QR.posts.length - 1]).file
- isNewPost = true
- post = new QR.post()
- QR.checkDimensions file, (pass, el) ->
- if pass or isSingle
- post.setFile file, el
- else if isNewPost
- post.rm()
- URL.revokeObjectURL el.src if el
-
- checkDimensions: (file, cb) ->
- if /^image\//.test file.type
- img = new Image()
- img.onload = ->
- {height, width} = img
- pass = true
- if height > QR.max_height or width > QR.max_width
- QR.error "#{file.name}: Image too large (image: #{height}x#{width}px, max: #{QR.max_height}x#{QR.max_width}px)"
- pass = false
- if height < QR.min_height or width < QR.min_width
- QR.error "#{file.name}: Image too small (image: #{height}x#{width}px, min: #{QR.min_height}x#{QR.min_width}px)"
- pass = false
- cb pass, img
- img.onerror = ->
- cb false, null
- img.src = URL.createObjectURL file
- else if /^video\//.test file.type
- video = $.el 'video'
- $.on video, 'loadeddata', ->
- return unless cb
- {videoHeight, videoWidth, duration} = video
- max_height = Math.min(QR.max_height, QR.max_height_video)
- max_width = Math.min(QR.max_width, QR.max_width_video)
- pass = true
- if videoHeight > max_height or videoWidth > max_width
- QR.error "#{file.name}: Video too large (video: #{videoHeight}x#{videoWidth}px, max: #{max_height}x#{max_width}px)"
- pass = false
- if videoHeight < QR.min_height or videoWidth < QR.min_width
- QR.error "#{file.name}: Video too small (video: #{videoHeight}x#{videoWidth}px, min: #{QR.min_height}x#{QR.min_width}px)"
- pass = false
- unless isFinite duration
- QR.error "#{file.name}: Video lacks duration metadata (try remuxing)"
- pass = false
- else if duration > QR.max_duration_video
- QR.error "#{file.name}: Video too long (video: #{duration}s, max: #{QR.max_duration_video}s)"
- pass = false
- if g.BOARD.ID not in ['gif', 'wsg'] and $.hasAudio video
- QR.error "#{file.name}: Audio not allowed"
- pass = false
- cb pass, video
- cb = null
- $.on video, 'error', ->
- return unless cb
- if file.type in QR.mimeTypes
- # only report error here if we should have been able to play the video
- # otherwise "unsupported type" should already have been shown
- QR.error "#{file.name}: Video appears corrupt"
- URL.revokeObjectURL file
- cb false, null
- cb = null
- video.src = URL.createObjectURL file
else
- cb true, null
+ post = QR.posts[QR.posts.length - 1]
+ if post[if isText then 'com' else 'file']
+ post = new QR.post()
+ post[if isText then 'pasteText' else 'setFile'] file
openFileInput: ->
QR.nodes.fileInput.click()
diff --git a/src/Posting/QR.post.coffee b/src/Posting/QR.post.coffee
index d32b72adc..16f36c7ed 100644
--- a/src/Posting/QR.post.coffee
+++ b/src/Posting/QR.post.coffee
@@ -75,6 +75,7 @@ QR.post = class
delete: ->
$.rm @nodes.el
URL.revokeObjectURL @URL
+ @dismissErrors()
lock: (lock=true) ->
@isLocked = lock
@@ -151,9 +152,35 @@ QR.post = class
@save node
return
- setFile: (@file, el) ->
+ @rmErrored: ->
+ for post in QR.posts by -1 when errors = post.errors
+ for error in errors when doc.contains error
+ post.rm()
+ break
+ return
+
+ error: (className, message) ->
+ div = $.el 'div', {className}
+ $.extend div, <%= html('${message}
[delete] [delete all]') %>
+ (@errors or= []).push div
+ [rm, rmAll] = $$ 'a', div
+ $.on rm, 'click', => @rm() if @ in QR.posts
+ $.on rmAll, 'click', QR.post.rmErrored
+ QR.error div
+
+ fileError: (message) ->
+ @error 'file-error', "#{@filename}: #{message}"
+
+ dismissErrors: (test = -> true) ->
+ if @errors
+ for error in @errors when doc.contains(error) and test error
+ error.parentNode.previousElementSibling.click()
+ return
+
+ setFile: (@file) ->
@filename = @file.name
@filesize = $.bytesToString @file.size
+ @checkSize()
@nodes.label.hidden = false if QR.spoiler
QR.captcha.onPostChange()
URL.revokeObjectURL @URL
@@ -161,10 +188,57 @@ QR.post = class
@showFileData()
else
@updateFilename()
- if el
+ @nodes.el.style.backgroundImage = null
+ unless @file.type in QR.mimeTypes
+ @fileError 'Unsupported file type.'
+ else if /^(image|video)\//.test @file.type
+ @readFile()
+
+ checkSize: ->
+ max = QR.nodes.fileInput.max
+ max = Math.min(max, QR.max_size_video) if /^video\//.test @file.type
+ if @file.size > max
+ @fileError "File too large (file: #{@filesize}, max: #{$.bytesToString max})."
+
+ readFile: ->
+ isVideo = /^video\//.test @file.type
+ el = $.el(if isVideo then 'video' else 'img')
+ event = if isVideo then 'loadeddata' else 'load'
+ onload = =>
+ $.off el, event, onload
+ $.off el, 'error', onerror
+ @checkDimensions el
@setThumbnail el
+ onerror = =>
+ $.off el, event, onload
+ $.off el, 'error', onerror
+ @fileError "#{if isVideo then 'Video' else 'Image'} appears corrupt"
+ URL.revokeObjectURL el.src
+ $.on el, event, onload
+ $.on el, 'error', onerror
+ el.src = URL.createObjectURL @file
+
+ checkDimensions: (el) ->
+ if el.tagName is 'IMG'
+ {height, width} = el
+ if height > QR.max_height or width > QR.max_width
+ @fileError "Image too large (image: #{height}x#{width}px, max: #{QR.max_height}x#{QR.max_width}px)"
+ if height < QR.min_height or width < QR.min_width
+ @fileError "Image too small (image: #{height}x#{width}px, min: #{QR.min_height}x#{QR.min_width}px)"
else
- @nodes.el.style.backgroundImage = null
+ {videoHeight, videoWidth, duration} = el
+ max_height = Math.min(QR.max_height, QR.max_height_video)
+ max_width = Math.min(QR.max_width, QR.max_width_video)
+ if videoHeight > max_height or videoWidth > max_width
+ @fileError "Video too large (video: #{videoHeight}x#{videoWidth}px, max: #{max_height}x#{max_width}px)"
+ if videoHeight < QR.min_height or videoWidth < QR.min_width
+ @fileError "Video too small (video: #{videoHeight}x#{videoWidth}px, min: #{QR.min_height}x#{QR.min_width}px)"
+ unless isFinite duration
+ @fileError 'Video lacks duration metadata (try remuxing)'
+ else if duration > QR.max_duration_video
+ @fileError "Video too long (video: #{duration}s, max: #{QR.max_duration_video}s)"
+ if g.BOARD.ID not in ['gif', 'wsg'] and $.hasAudio el
+ @fileError 'Audio not allowed'
setThumbnail: (el) ->
# Create a redimensioned thumbnail.
@@ -185,6 +259,7 @@ QR.post = class
@URL = el.src
@nodes.el.style.backgroundImage = "url(#{@URL})"
return
+
if height <= width
width = s / height * width
height = s
@@ -211,6 +286,7 @@ QR.post = class
@nodes.label.hidden = true if QR.spoiler
@showFileData()
URL.revokeObjectURL @URL
+ @dismissErrors (error) -> $.hasClass error, 'file-error'
updateFilename: ->
long = "#{@filename} (#{@filesize})"