Better file input interface.

This commit is contained in:
Nicolas Stepien 2013-02-25 05:06:14 +01:00
parent 0c4eaed642
commit 1dcdd2c20a
3 changed files with 130 additions and 75 deletions

File diff suppressed because one or more lines are too long

View File

@ -511,10 +511,10 @@ a[href="javascript:;"] {
background: -webkit-linear-gradient(#CCC, #DDD); background: -webkit-linear-gradient(#CCC, #DDD);
background: linear-gradient(#CCC, #DDD); background: linear-gradient(#CCC, #DDD);
} }
#qr:not(.dump) #dump-list, .dump #spoilerLabel { #qr:not(.dump) #dump-list-container {
display: none; display: none;
} }
#dump-list { #dump-list-container {
height: 100px; height: 100px;
position: relative; position: relative;
-webkit-user-select: none; -webkit-user-select: none;
@ -522,14 +522,14 @@ a[href="javascript:;"] {
-o-user-select: none; -o-user-select: none;
user-select: none; user-select: none;
} }
#dump-list-container { #dump-list {
counter-reset: qrpreviews; counter-reset: qrpreviews;
top: 0; right: 0; bottom: 0; left: 0; top: 0; right: 0; bottom: 0; left: 0;
overflow: hidden; overflow: hidden;
position: absolute; position: absolute;
white-space: pre; white-space: pre;
} }
#dump-list-container:hover { #dump-list:hover {
bottom: -10px; bottom: -10px;
overflow-x: auto; overflow-x: auto;
z-index: 1; z-index: 1;
@ -611,7 +611,7 @@ a[href="javascript:;"] {
.textarea { .textarea {
position: relative; position: relative;
} }
#charCount { #char-count {
color: #000; color: #000;
background: hsla(0, 0%, 100%, .5); background: hsla(0, 0%, 100%, .5);
font-size: 8pt; font-size: 8pt;
@ -620,7 +620,7 @@ a[href="javascript:;"] {
right: 1px; right: 1px;
pointer-events: none; pointer-events: none;
} }
#charCount.warning { #char-count.warning {
color: red; color: red;
} }
.captcha-img { .captcha-img {
@ -633,17 +633,47 @@ a[href="javascript:;"] {
height: 57px; height: 57px;
width: 300px; width: 300px;
} }
#qr [type='file'] { #file-n-submit.has-file > #qr-no-file,
margin: 1px 0; #file-n-submit:not(.has-file) > #qr-filename-container,
width: 70%; #file-n-submit:not(.has-file) > #qr-file-spoiler,
#file-n-submit:not(.has-file) > #qr-filerm {
display: none;
} }
#qr [type='submit'] { #qr-no-file,
margin: 1px 0; #qr-filename-container,
padding: 1px; /* not Gecko */ #qr-file-spoiler,
width: 30%; #qr-filerm {
line-height: 2;
} }
.gecko #qr [type='submit'] { #file-n-submit {
padding: 0 1px; /* Gecko does not respect box-sizing: border-box */ display: -webkit-flex;
display: flex;
-webkit-flex-direction: row;
flex-direction: row;
}
#qr-no-file, #qr-filename-container {
-webkit-flex: 1;
flex: 1;
}
#qr-filename-container {
position: relative;
}
#qr-filename {
position: absolute;
top: 0; right: 0; bottom: 0; left: 0;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
#qr-filerm {
padding: 0 2px;
}
#qr-file-spoiler {
margin: 6px 1px;
}
#qr input[type='file'] {
position: absolute;
visibility: hidden;
} }
/* Menu */ /* Menu */

View File

@ -67,7 +67,6 @@ QR =
QR.replies[0].rm() QR.replies[0].rm()
QR.cooldown.auto = false QR.cooldown.auto = false
QR.status() QR.status()
QR.resetFileInput()
if !Conf['Remember Spoiler'] and QR.nodes.spoiler.checked if !Conf['Remember Spoiler'] and QR.nodes.spoiler.checked
QR.nodes.spoiler.click() QR.nodes.spoiler.click()
QR.cleanNotifications() QR.cleanNotifications()
@ -292,7 +291,8 @@ QR =
$.addClass QR.nodes.el, 'dump' $.addClass QR.nodes.el, 'dump'
fileInput: (files) -> fileInput: (files) ->
unless files instanceof FileList unless files instanceof FileList
files = @files files = Array::slice.call @files
QR.nodes.fileInput.value = null # Don't hold the files from being modified on windows
{length} = files {length} = files
return unless length return unless length
max = QR.nodes.fileInput.max max = QR.nodes.fileInput.max
@ -302,10 +302,8 @@ QR =
file = files[0] file = files[0]
if file.size > max if file.size > max
QR.error "File too large (file: #{$.bytesToString file.size}, max: #{$.bytesToString max})." QR.error "File too large (file: #{$.bytesToString file.size}, max: #{$.bytesToString max})."
QR.resetFileInput()
else unless file.type in QR.mimeTypes else unless file.type in QR.mimeTypes
QR.error 'Unsupported file type.' QR.error 'Unsupported file type.'
QR.resetFileInput()
else else
QR.selected.setFile file QR.selected.setFile file
return return
@ -321,9 +319,6 @@ QR =
else else
new QR.reply().setFile file new QR.reply().setFile file
$.addClass QR.nodes.el, 'dump' $.addClass QR.nodes.el, 'dump'
QR.resetFileInput() # reset input
resetFileInput: ->
QR.nodes.fileInput.value = null
resetThreadSelector: -> resetThreadSelector: ->
if g.BOARD.ID is 'f' if g.BOARD.ID is 'f'
if g.VIEW is 'index' if g.VIEW is 'index'
@ -373,8 +368,10 @@ QR =
QR.replies.push @ QR.replies.push @
setFile: (@file) -> setFile: (@file) ->
@nodes.el.title = "#{file.name} (#{$.bytesToString file.size})" @filename = "#{file.name} (#{$.bytesToString file.size})"
@nodes.el.title = @filename
@nodes.label.hidden = false if QR.spoiler @nodes.label.hidden = false if QR.spoiler
@showFileData()
unless /^image/.test file.type unless /^image/.test file.type
@el.style.backgroundImage = null @el.style.backgroundImage = null
return return
@ -425,13 +422,22 @@ QR =
img.src = fileURL img.src = fileURL
rmFile: -> rmFile: ->
QR.resetFileInput()
delete @file delete @file
delete @filename
@nodes.el.title = null @nodes.el.title = null
@nodes.el.style.backgroundImage = null @nodes.el.style.backgroundImage = null
@nodes.label.hidden = true if QR.spoiler @nodes.label.hidden = true if QR.spoiler
@showFileData()
return unless window.URL return unless window.URL
URL.revokeObjectURL @url URL.revokeObjectURL @url
showFileData: (hide) ->
if @file
QR.nodes.filename.textContent = @filename
QR.nodes.filename.title = @filename
QR.nodes.spoiler.checked = @spoiler if QR.spoiler
$.addClass QR.nodes.fileSubmit, 'has-file'
else
$.rmClass QR.nodes.fileSubmit, 'has-file'
select: -> select: ->
if QR.selected if QR.selected
QR.selected.nodes.el.id = null QR.selected.nodes.el.id = null
@ -445,8 +451,8 @@ QR =
# Load this reply's values. # Load this reply's values.
for name in ['name', 'email', 'sub', 'com'] for name in ['name', 'email', 'sub', 'com']
QR.nodes[name].value = @[name] QR.nodes[name].value = @[name]
@showFileData()
QR.characterCount() QR.characterCount()
QR.nodes.spoiler.checked = @spoiler
save: (input) -> save: (input) ->
{value} = input {value} = input
@[input.dataset.name] = value @[input.dataset.name] = value
@ -488,7 +494,6 @@ QR =
if el = $ '.over', @parentNode if el = $ '.over', @parentNode
$.rmClass el, 'over' $.rmClass el, 'over'
rm: -> rm: ->
QR.resetFileInput()
$.rm @nodes.el $.rm @nodes.el
index = QR.replies.indexOf @ index = QR.replies.indexOf @
if QR.replies.length is 1 if QR.replies.length is 1
@ -614,20 +619,24 @@ QR =
<input data-name=email title=E-mail placeholder=E-mail class=field size=1> <input data-name=email title=E-mail placeholder=E-mail class=field size=1>
<input data-name=sub title=Subject placeholder=Subject class=field size=1> <input data-name=sub title=Subject placeholder=Subject class=field size=1>
</div> </div>
<div id=dump-list> <div id=dump-list-container>
<div id=dump-list-container> <div id=dump-list>
<a id=addReply href=javascript:; title="Add a reply">+</a> <a id=addReply href=javascript:; title="Add a reply">+</a>
</div> </div>
</div> </div>
<div class=textarea> <div class=textarea>
<textarea data-name=com title=Comment placeholder=Comment class=field></textarea> <textarea data-name=com title=Comment placeholder=Comment class=field></textarea>
<span id=charCount></span> <span id=char-count></span>
</div> </div>
<div> <div id=file-n-submit>
<input type=file title="Shift+Click to remove the selected file." multiple size=16> <input id=qr-file-button type=button value='Choose files'>
<span id=qr-no-file>No selected file</span>
<span id=qr-filename-container><span id=qr-filename></span></span>
<a id=qr-filerm href=javascript:; title='Remove file'>×</a>
<input type=checkbox id=qr-file-spoiler title='Spoiler image'>
<input type=submit> <input type=submit>
</div> </div>
<label id=spoilerLabel><input type=checkbox id=spoiler> Spoiler Image</label> <input type=file multiple>
</form> </form>
""".replace />\s+</g, '><' # get rid of spaces between elements """.replace />\s+</g, '><' # get rid of spaces between elements
@ -643,10 +652,14 @@ QR =
sub: $ '[data-name=sub]', dialog sub: $ '[data-name=sub]', dialog
com: $ '[data-name=com]', dialog com: $ '[data-name=com]', dialog
addReply: $ '#addReply', dialog addReply: $ '#addReply', dialog
charCount: $ '#charCount', dialog charCount: $ '#char-count', dialog
fileInput: $ '[type=file]', dialog fileSubmit: $ '#file-n-submit', dialog
spoiler: $ '#spoiler', dialog fileButton: $ '#qr-file-button', dialog
filename: $ '#qr-filename', dialog
fileRM: $ '#qr-filerm', dialog
spoiler: $ '#qr-file-spoiler', dialog
status: $ '[type=submit]', dialog status: $ '[type=submit]', dialog
fileInput: $ '[type=file]', dialog
# Allow only this board's supported files. # Allow only this board's supported files.
mimeTypes = $('ul.rules > li').textContent.trim().match(/: (.+)/)[1].toLowerCase().replace /\w+/g, (type) -> mimeTypes = $('ul.rules > li').textContent.trim().match(/: (.+)/)[1].toLowerCase().replace /\w+/g, (type) ->
@ -666,12 +679,12 @@ QR =
nodes.fileInput.accept = mimeTypes if $.engine isnt 'presto' # Opera's accept attribute is fucked up nodes.fileInput.accept = mimeTypes if $.engine isnt 'presto' # Opera's accept attribute is fucked up
QR.spoiler = !!$ 'input[name=spoiler]' QR.spoiler = !!$ 'input[name=spoiler]'
nodes.spoiler.parentNode.hidden = !QR.spoiler nodes.spoiler.hidden = !QR.spoiler
if g.BOARD.ID is 'f' if g.BOARD.ID is 'f'
if g.VIEW is 'index' if g.VIEW is 'index'
nodes.flashTag = $('select[name=filetag]').cloneNode true nodes.flashTag = $('select[name=filetag]').cloneNode true
$.after QR.nodes.autohide, nodes.flashTag $.after nodes.autohide, nodes.flashTag
else # Make a list of visible threads. else # Make a list of visible threads.
nodes.thread = $.el 'select', nodes.thread = $.el 'select',
title: 'Create a new thread / Reply' title: 'Create a new thread / Reply'
@ -679,17 +692,18 @@ QR =
for key, thread of g.BOARD.threads for key, thread of g.BOARD.threads
threads += "<option value=#{thread.ID}>Thread No.#{thread.ID}</option>" threads += "<option value=#{thread.ID}>Thread No.#{thread.ID}</option>"
nodes.thread.innerHTML = threads nodes.thread.innerHTML = threads
$.after QR.nodes.autohide, nodes.thread $.after nodes.autohide, nodes.thread
QR.resetThreadSelector() QR.resetThreadSelector()
$.on nodes.autohide, 'change', QR.toggleHide $.on nodes.autohide, 'change', QR.toggleHide
$.on nodes.close, 'click', QR.close $.on nodes.close, 'click', QR.close
$.on nodes.dumpButton, 'click', -> QR.nodes.el.classList.toggle 'dump' $.on nodes.dumpButton, 'click', -> nodes.el.classList.toggle 'dump'
$.on nodes.addReply, 'click', -> new QR.reply().select() $.on nodes.addReply, 'click', -> new QR.reply().select()
$.on nodes.form, 'submit', QR.submit $.on nodes.form, 'submit', QR.submit
$.on nodes.fileButton, 'click', -> QR.nodes.fileInput.click()
$.on nodes.fileRM, 'click', -> QR.selected.rmFile()
$.on nodes.spoiler, 'change', -> QR.selected.nodes.spoiler.click()
$.on nodes.fileInput, 'change', QR.fileInput $.on nodes.fileInput, 'change', QR.fileInput
$.on nodes.fileInput, 'click', (e) -> if e.shiftKey then QR.selected.rmFile(); e.preventDefault()
$.on nodes.spoiler, 'change', -> $('input', QR.selected.el).click()
new QR.reply().select() new QR.reply().select()
# save selected reply's data # save selected reply's data
@ -894,7 +908,6 @@ QR =
QR.close() QR.close()
QR.status() QR.status()
QR.resetFileInput()
abort: -> abort: ->
if QR.ajax if QR.ajax