QR.post = class constructor: (select) -> el = $.el 'a', className: 'qr-preview' draggable: true href: 'javascript:;' innerHTML: '' @nodes = el: el rm: el.firstChild label: $ 'label', el spoiler: $ 'input', el span: el.lastChild <% if (type === 'userscript') { %> # XXX Firefox lacks focusin/focusout support. for elm in $$ '*', el $.on elm, 'blur', QR.focusout $.on elm, 'focus', QR.focusin <% } %> $.on el, 'click', @select $.on @nodes.rm, 'click', (e) => e.stopPropagation(); @rm() $.on @nodes.label, 'click', (e) => e.stopPropagation() $.on @nodes.spoiler, 'change', (e) => @spoiler = e.target.checked QR.nodes.spoiler.checked = @spoiler if @ is QR.selected $.add QR.nodes.dumpList, el for event in ['dragStart', 'dragEnter', 'dragLeave', 'dragOver', 'dragEnd', 'drop'] $.on el, event.toLowerCase(), @[event] @thread = if g.VIEW is 'thread' g.THREADID else 'new' prev = QR.posts[QR.posts.length - 1] QR.posts.push @ @nodes.spoiler.checked = @spoiler = if prev and Conf['Remember Spoiler'] prev.spoiler else false QR.persona.get (persona) => @name = if 'name' of QR.persona.always QR.persona.always.name else if prev prev.name else persona.name @email = if 'email' of QR.persona.always QR.persona.always.email else if prev and !/^sage$/.test prev.email prev.email else persona.email @sub = if 'sub' of QR.persona.always QR.persona.always.sub else if Conf['Remember Subject'] if prev then prev.sub else persona.sub else '' if QR.nodes.flag @flag = if prev prev.flag else persona.flag @load() if QR.selected is @ # load persona @select() if select @unlock() rm: -> @delete() index = QR.posts.indexOf @ if QR.posts.length is 1 new QR.post true $.rmClass QR.nodes.el, 'dump' else if @ is QR.selected (QR.posts[index-1] or QR.posts[index+1]).select() QR.posts.splice index, 1 QR.status() delete: -> $.rm @nodes.el URL.revokeObjectURL @URL lock: (lock=true) -> @isLocked = lock return unless @ is QR.selected for name in ['thread', 'name', 'email', 'sub', 'com', 'fileButton', 'filename', 'spoiler', 'flag'] when node = QR.nodes[name] node.disabled = lock @nodes.rm.style.visibility = if lock then 'hidden' else '' (if lock then $.off else $.on) QR.nodes.filename.previousElementSibling, 'click', QR.openFileInput @nodes.spoiler.disabled = lock @nodes.el.draggable = !lock unlock: -> @lock false select: => if QR.selected QR.selected.nodes.el.id = null QR.selected.forceSave() QR.selected = @ @lock @isLocked @nodes.el.id = 'selected' # Scroll the list to center the focused post. rectEl = @nodes.el.getBoundingClientRect() rectList = @nodes.el.parentNode.getBoundingClientRect() @nodes.el.parentNode.scrollLeft += rectEl.left + rectEl.width/2 - rectList.left - rectList.width/2 @load() $.event 'QRPostSelection', @ load: -> # Load this post's values. for name in ['thread', 'name', 'email', 'sub', 'com', 'filename', 'flag'] continue unless node = QR.nodes[name] node.value = @[name] or node.dataset.default or null @showFileData() QR.characterCount() save: (input) -> if input.type is 'checkbox' @spoiler = input.checked return {name} = input.dataset @[name] = input.value or input.dataset.default or null switch name when 'thread' QR.status() when 'com' @nodes.span.textContent = @com QR.characterCount() # Disable auto-posting if you're typing in the first post # during the last 5 seconds of the cooldown. if QR.cooldown.auto and @ is QR.posts[0] and 0 < QR.cooldown.seconds <= 5 QR.cooldown.auto = false when 'filename' return unless @file @file.newName = @filename.replace /[/\\]/g, '-' unless /\.(jpe?g|png|gif|pdf|swf)$/i.test @filename # 4chan will truncate the filename if it has no extension, # but it will always replace the extension by the correct one, # so we suffix it with '.jpg' when needed. @file.newName += '.jpg' @updateFilename() forceSave: -> return unless @ is QR.selected # Do this in case people use extensions # that do not trigger the `input` event. for name in ['thread', 'name', 'email', 'sub', 'com', 'filename', 'spoiler', 'flag'] continue unless node = QR.nodes[name] @save node return setFile: (@file) -> @filename = file.name @filesize = $.bytesToString file.size @nodes.label.hidden = false if QR.spoiler URL.revokeObjectURL @URL @showFileData() if @ is QR.selected unless /^image/.test file.type @nodes.el.style.backgroundImage = null return @setThumbnail() setThumbnail: -> # Create a redimensioned thumbnail. img = $.el 'img' img.onload = => # Generate thumbnails only if they're really big. # Resized pictures through canvases look like ass, # so we generate thumbnails `s` times bigger then expected # to avoid crappy resized quality. s = 90 * 2 * window.devicePixelRatio s *= 3 if @file.type is 'image/gif' # let them animate {height, width} = img if height < s or width < s @URL = fileURL @nodes.el.style.backgroundImage = "url(#{@URL})" return if height <= width width = s / height * width height = s else height = s / width * height width = s cv = $.el 'canvas' cv.height = img.height = height cv.width = img.width = width cv.getContext('2d').drawImage img, 0, 0, width, height URL.revokeObjectURL fileURL cv.toBlob (blob) => @URL = URL.createObjectURL blob @nodes.el.style.backgroundImage = "url(#{@URL})" fileURL = URL.createObjectURL @file img.src = fileURL rmFile: -> return if @isLocked delete @file delete @filename delete @filesize @nodes.el.title = null QR.nodes.fileContainer.title = '' @nodes.el.style.backgroundImage = null @nodes.label.hidden = true if QR.spoiler @showFileData() URL.revokeObjectURL @URL updateFilename: -> long = "#{@filename} (#{@filesize})\nCtrl+click to edit filename. Shift+click to clear." @nodes.el.title = long return unless @ is QR.selected QR.nodes.fileContainer.title = long showFileData: -> if @file @updateFilename() QR.nodes.filename.value = @filename QR.nodes.spoiler.checked = @spoiler $.addClass QR.nodes.fileSubmit, 'has-file' else $.rmClass QR.nodes.fileSubmit, 'has-file' pasteText: (file) -> reader = new FileReader() reader.onload = (e) => text = e.target.result if @com @com += "\n#{text}" else @com = text if QR.selected is @ QR.nodes.com.value = @com @nodes.span.textContent = @com reader.readAsText file dragStart: (e) -> e.dataTransfer.setDragImage @, e.layerX, e.layerY $.addClass @, 'drag' dragEnd: -> $.rmClass @, 'drag' dragEnter: -> $.addClass @, 'over' dragLeave: -> $.rmClass @, 'over' dragOver: (e) -> e.preventDefault() e.dataTransfer.dropEffect = 'move' drop: -> $.rmClass @, 'over' return unless @draggable el = $ '.drag', @parentNode index = (el) -> [el.parentNode.children...].indexOf el oldIndex = index el newIndex = index @ (if oldIndex < newIndex then $.after else $.before) @, el post = QR.posts.splice(oldIndex, 1)[0] QR.posts.splice newIndex, 0, post QR.status()