diff --git a/src/Archive/Redirect.coffee b/src/Archive/Redirect.coffee index 2bb85fc75..b3c1185a3 100755 --- a/src/Archive/Redirect.coffee +++ b/src/Archive/Redirect.coffee @@ -58,6 +58,7 @@ Redirect = protocol = Redirect.protocol archive URL = new String "#{protocol}#{archive.domain}/_/api/chan/post/?board=#{boardID}&num=#{postID}" return '' unless Redirect.securityCheck URL + URL.archive = archive URL diff --git a/src/Filtering/Anonymize.coffee b/src/Filtering/Anonymize.coffee index 22914d521..c34821a7a 100755 --- a/src/Filtering/Anonymize.coffee +++ b/src/Filtering/Anonymize.coffee @@ -6,6 +6,7 @@ Anonymize = Post.callbacks.push name: 'Anonymize' cb: @node + node: -> return if @info.capcode or @isClone {name, tripcode, email} = @nodes @@ -17,6 +18,7 @@ Anonymize = if @info.email $.replace email, name delete @nodes.email + archive: -> $.ready -> name.textContent = 'Anonymous' for name in $$ '.name' diff --git a/src/Filtering/Filter.coffee b/src/Filtering/Filter.coffee index 89d71afd0..27c185f23 100755 --- a/src/Filtering/Filter.coffee +++ b/src/Filtering/Filter.coffee @@ -84,11 +84,13 @@ Filter = (value) -> regexp is value else (value) -> regexp.test value + settings = hide: !hl stub: stub class: hl top: top + (value, isReply) -> if isReply and op is 'only' or !isReply and op is 'no' return false @@ -103,10 +105,7 @@ Filter = # Continue if there's nothing to filter (no tripcode for example). continue if value is false - for filter in Filter.filters[key] - unless result = filter value, @isReply - continue - + for filter in Filter.filters[key] when result = filter value, @isReply # Hide if result.hide if @isReply diff --git a/src/Filtering/PostHiding.coffee b/src/Filtering/PostHiding.coffee index b2d2f091b..1772c13e1 100755 --- a/src/Filtering/PostHiding.coffee +++ b/src/Filtering/PostHiding.coffee @@ -12,12 +12,14 @@ PostHiding = node: -> return if !@isReply or @isClone or @isFetchedQuote + if data = PostHiding.db.get {boardID: @board.ID, threadID: @thread.ID, postID: @ID} if data.thisPost PostHiding.hide @, data.makeStub, data.hideRecursively else Recursive.apply PostHiding.hide, @, data.makeStub, true Recursive.add PostHiding.hide, @, data.makeStub, true + return unless Conf['Reply Hiding Buttons'] sideArrows = $('.sideArrows', @nodes.root) $.replace sideArrows.firstChild, PostHiding.makeButton @, 'hide' diff --git a/src/Filtering/Recursive.coffee b/src/Filtering/Recursive.coffee index 21d541b90..7567f2f61 100755 --- a/src/Filtering/Recursive.coffee +++ b/src/Filtering/Recursive.coffee @@ -1,18 +1,16 @@ Recursive = recursives: {} init: -> - return if g.VIEW not in ['index', 'thread'] - + return unless g.VIEW in ['index', 'thread'] Post.callbacks.push name: 'Recursive' cb: @node node: -> return if @isClone or @isFetchedQuote - for quote in @quotes - if obj = Recursive.recursives[quote] - for recursive, i in obj.recursives - recursive @, obj.args[i]... + for quote in @quotes when obj = Recursive.recursives[quote] + for recursive, i in obj.recursives + recursive @, obj.args[i]... return add: (recursive, post, args...) -> diff --git a/src/General/Build.coffee b/src/General/Build.coffee index 419b53428..a39df478a 100755 --- a/src/General/Build.coffee +++ b/src/General/Build.coffee @@ -334,6 +334,7 @@ Build = root = $.el 'div', className: 'catalog-thread' + $.extend root, <%= html( '' + '&{thumb}' + @@ -352,12 +353,15 @@ Build = for quote in $$ '.quotelink', root.lastElementChild href = quote.getAttribute 'href' quote.href = "/#{thread.board}/thread/#{thread.ID}" + href if href[0] is '#' + for exif in $$ '.abbr, .exif', root.lastElementChild $.rm exif + for pp in $$ '.prettyprint', root.lastElementChild cc = $.el 'span', className: 'catalog-code' $.add cc, [pp.childNodes...] $.replace pp, cc + for br in $$ 'br', root.lastElementChild when br.previousSibling?.nodeName is 'BR' $.rm br @@ -366,6 +370,7 @@ Build = src: "#{staticPath}sticky#{gifIcon}" className: 'stickyIcon' title: 'Sticky' + if thread.isClosed $.add $('.catalog-icons', root), $.el 'img', src: "#{staticPath}closed#{gifIcon}" @@ -374,6 +379,7 @@ Build = if data.bumplimit $.addClass $('.post-count', root), 'warning' + if data.imagelimit $.addClass $('.file-count', root), 'warning' diff --git a/src/General/Config.coffee b/src/General/Config.coffee index 2c5fa92de..5c5c06a25 100755 --- a/src/General/Config.coffee +++ b/src/General/Config.coffee @@ -1,7 +1,7 @@ Config = main: 'Miscellaneous': - 'JSON Navigation' : [ + 'JSON Navigation': [ true 'Replace the original board index with one supporting searching, sorting, infinite scrolling, and a catalog mode.' ] @@ -112,6 +112,7 @@ Config = true '<%= meta.name %> is NOT designed to work with the native extension.' ] + 'Linkification': 'Linkify': [ true @@ -587,69 +588,69 @@ Config = filter: name: """ -# Filter any namefags: -#/^(?!Anonymous$)/ -""" + # Filter any namefags: + #/^(?!Anonymous$)/ + """ uniqueID: """ -# Filter a specific ID: -#/Txhvk1Tl/ -""" + # Filter a specific ID: + #/Txhvk1Tl/ + """ tripcode: """ -# Filter any tripfag -#/^!/ -""" + # Filter any tripfag + #/^!/ + """ capcode: """ -# Set a custom class for mods: -#/Mod$/;highlight:mod;op:yes -# Set a custom class for moot: -#/Admin$/;highlight:moot;op:yes -""" + # Set a custom class for mods: + #/Mod$/;highlight:mod;op:yes + # Set a custom class for moot: + #/Admin$/;highlight:moot;op:yes + """ subject: """ -# Filter Generals on /v/: -#/general/i;boards:v;op:only -""" + # Filter Generals on /v/: + #/general/i;boards:v;op:only + """ comment: """ -# Filter Stallman copypasta on /g/: -#/what you\'re refer+ing to as linux/i;boards:g -""" + # Filter Stallman copypasta on /g/: + #/what you\'re refer+ing to as linux/i;boards:g + """ flag: '' filename: '' dimensions: """ -# Highlight potential wallpapers: -#/1920x1080/;op:yes;highlight;top:no;boards:w,wg -""" + # Highlight potential wallpapers: + #/1920x1080/;op:yes;highlight;top:no;boards:w,wg + """ filesize: '' MD5: '' sauces: """ -https://www.google.com/searchbyimage?image_url=%TURL -http://iqdb.org/?url=%TURL -#//tineye.com/search?url=%TURL -#//saucenao.com/search.php?url=%TURL -#http://3d.iqdb.org/?url=%TURL -#http://regex.info/exif.cgi?imgurl=%URL -# uploaders: -#//imgur.com/upload?url=%URL;text:Upload to imgur -# "View Same" in archives: -#https://archive.moe/_/search/image/%MD5/;text:View same on archive.moe -#https://archive.moe/%board/search/image/%MD5/;text:View same on archive.moe/%board/;boards:<%= - grunt.file.readJSON('src/Archive/archives.json').filter(function(x) {return x.uid === 0})[0].files.join(',') -%> -#https://rbt.asia/%board/image/%MD5;text:View same on RBT /%board/;boards:<%= - grunt.file.readJSON('src/Archive/archives.json').filter(function(x) {return x.uid === 8})[0].files.join(',') -%> -# Search with full image only for image file types: -#https://www.google.com/searchbyimage?image_url=%URL;types:gif,jpg,png -#https://www.google.com/searchbyimage?image_url=%TURL;types:webm,pdf -""" + https://www.google.com/searchbyimage?image_url=%TURL + http://iqdb.org/?url=%TURL + #//tineye.com/search?url=%TURL + #//saucenao.com/search.php?url=%TURL + #http://3d.iqdb.org/?url=%TURL + #http://regex.info/exif.cgi?imgurl=%URL + # uploaders: + #//imgur.com/upload?url=%URL;text:Upload to imgur + # "View Same" in archives: + #https://archive.moe/_/search/image/%MD5/;text:View same on archive.moe + #https://archive.moe/%board/search/image/%MD5/;text:View same on archive.moe/%board/;boards:<%= + grunt.file.readJSON('src/Archive/archives.json').filter(function(x) {return x.uid === 0})[0].files.join(',') + %> + #https://rbt.asia/%board/image/%MD5;text:View same on RBT /%board/;boards:<%= + grunt.file.readJSON('src/Archive/archives.json').filter(function(x) {return x.uid === 8})[0].files.join(',') + %> + # Search with full image only for image file types: + #https://www.google.com/searchbyimage?image_url=%URL;types:gif,jpg,png + #https://www.google.com/searchbyimage?image_url=%TURL;types:webm,pdf + """ FappeT: werk: false @@ -678,29 +679,29 @@ http://iqdb.org/?url=%TURL 'Custom Board Navigation': true boardnav: """ -[ toggle-all ] -a-replace -c-replace -g-replace -k-replace -v-replace -vg-replace -vr-replace -ck-replace -co-replace -fit-replace -jp-replace -mu-replace -sp-replace -tv-replace -vp-replace -[external-text:"FAQ","<%= meta.faq %>"] + [ toggle-all ] + a-replace + c-replace + g-replace + k-replace + v-replace + vg-replace + vr-replace + ck-replace + co-replace + fit-replace + jp-replace + mu-replace + sp-replace + tv-replace + vp-replace + [external-text:"FAQ","<%= meta.faq %>"] """ QR: 'QR.personas': """ #options:"sage";boards:jp;always - """ + """ time: '%m/%d/%y(%a)%H:%M:%S' diff --git a/src/General/Get.coffee b/src/General/Get.coffee index e8fc138a9..13186d583 100755 --- a/src/General/Get.coffee +++ b/src/General/Get.coffee @@ -51,7 +51,7 @@ Get = # get all their backlinks. posts.forEach (qPost) -> if fullID in qPost.quotes - handleQuotes qPost, 'quotelinks' + handleQuotes qPost, 'quotelinks' # Second: # If we have quote backlinks: @@ -66,10 +66,12 @@ Get = quotelinks.filter (quotelink) -> {boardID, postID} = Get.postDataFromLink quotelink boardID is post.board.ID and postID is post.ID + scriptData: -> for script in $$ 'script:not([src])', d.head return script.textContent if /\bcooldowns *=/.test script.textContent '' + postClone: (boardID, threadID, postID, root, context) -> if post = g.posts["#{boardID}.#{postID}"] Get.insert post, root, context @@ -81,6 +83,7 @@ Get = Get.fetchedPost @, boardID, threadID, postID, root, context else Get.archivedPost boardID, postID, root, context + insert: (post, root, context) -> # Stop here if the container has been removed while loading. return unless root.parentNode @@ -95,6 +98,7 @@ Get = $.rmAll root $.add root, nodes.root $.event 'PostsInserted' + fetchedPost: (req, boardID, threadID, postID, root, context) -> # In case of multiple callbacks for the same request, # don't parse the same original post more than once. @@ -127,6 +131,7 @@ Get = $.cache api, -> Get.fetchedPost @, boardID, threadID, postID, root, context return + # The post can be deleted by the time we check a quote. unless Get.archivedPost boardID, postID, root, context $.addClass root, 'warning' @@ -141,6 +146,7 @@ Get = post.isFetchedQuote = true Main.callbackNodes Post, [post] Get.insert post, root, context + archivedPost: (boardID, postID, root, context) -> return false unless Conf['Resurrect Quotes'] return false unless url = Redirect.to 'post', {boardID, postID} @@ -161,6 +167,7 @@ Get = Get.parseArchivedPost response, boardID, postID, root, context return true return false + parseArchivedPost: (data, boardID, postID, root, context) -> # In case of multiple callbacks for the same request, # don't parse the same original post more than once. diff --git a/src/General/Header.coffee b/src/General/Header.coffee index 5d3c83681..38de6d1b1 100755 --- a/src/General/Header.coffee +++ b/src/General/Header.coffee @@ -143,7 +143,10 @@ Header = $.extend boardList, <%= html( '' + ' -  ' + + '' + + ' - ' + + '' + + ' ' + '' + '' ) %> @@ -185,11 +188,13 @@ Header = nodes = boardnav.match(/[\w@]+(-(all|title|replace|full|index|catalog|archive|expired|text:"[^"]+"(,"[^"]+")?))*|[^\w@]+/g).map (t) -> if /^[^\w@]/.test t return $.tn t + text = url = null t = t.replace /-text:"([^"]+)"(?:,"([^"]+)")?/g, (m0, m1, m2) -> text = m1 url = m2 '' + if /^toggle-all/.test t a = $.el 'a', className: 'show-board-list-button' @@ -197,12 +202,14 @@ Header = href: 'javascript:;' $.on a, 'click', Header.toggleBoardList return a + if /^external/.test t a = $.el 'a', href: url or 'javascript:;' textContent: text or '+' className: 'external' return a + boardID = if /^current/.test t g.BOARD.ID else @@ -411,7 +418,6 @@ Header = hash = @location.hash[1..] return unless /^p\d+$/.test(hash) and post = $.id hash return if (Get.postFromRoot post).isHidden - Header.scrollTo post scrollTo: (root, down, needed) -> @@ -451,10 +457,12 @@ Header = headRect = Header.toggle.getBoundingClientRect() bottom -= clientHeight - headRect.bottom + headRect.height bottom + isNodeVisible: (node) -> return false if d.hidden or !doc.contains node {height} = node.getBoundingClientRect() Header.getTopOf(node) + height >= 0 and Header.getBottomOf(node) + height >= 0 + isHidden: -> {top} = Header.bar.getBoundingClientRect() if Conf['Bottom header'] diff --git a/src/General/Index.coffee b/src/General/Index.coffee index e884780b8..9d45259e2 100644 --- a/src/General/Index.coffee +++ b/src/General/Index.coffee @@ -161,6 +161,7 @@ Index = catalogNode: -> $.on @nodes.thumb.parentNode, 'click', Index.onClick + onClick: (e) -> return if e.button isnt 0 thread = g.threads[@parentNode.dataset.fullID] @@ -169,6 +170,7 @@ Index = else return e.preventDefault() + toggleHide: (thread) -> $.rm thread.catalogView.nodes.root if Index.showHiddenThreads @@ -178,6 +180,7 @@ Index = else ThreadHiding.hide thread ThreadHiding.saveHiddenState thread + cycleSortType: -> types = [Index.selectSort.options...].filter (option) -> !option.disabled for type, i in types @@ -193,15 +196,18 @@ Index = 'Show' Index.sort() Index.buildIndex() + mode: -> mode = @value unless mode is 'catalog' Conf['Previous Index Mode'] = mode $.set 'Previous Index Mode', mode Index.pageLoad Index.pushState {mode} + sort: -> Index.sort() Index.buildIndex() + size: (e) -> if Conf['Index Mode'] isnt 'catalog' $.rmClass Index.root, 'catalog-small' @@ -213,10 +219,12 @@ Index = $.addClass Index.root, 'catalog-large' $.rmClass Index.root, 'catalog-small' Index.buildIndex() if e + replies: -> Index.buildThreads() Index.sort() Index.buildIndex() + popstate: (e) -> if e?.state {search, mode} = e.state @@ -236,6 +244,7 @@ Index = scroll: true if state.command Index[if Conf['Refreshed Navigation'] then 'update' else 'pageLoad'] state + pageNav: (e) -> return if e.shiftKey or e.altKey or e.ctrlKey or e.metaKey or e.button isnt 0 switch e.target.nodeName @@ -249,6 +258,7 @@ Index = return if a.textContent is 'Catalog' e.preventDefault() Index.userPageNav +a.pathname.split('/')[2] or 1 + frontPage: (e) -> return if e.shiftKey or e.altKey or e.ctrlKey or e.metaKey or e.button isnt 0 e.preventDefault() @@ -262,12 +272,14 @@ Index = 1 else +window.location.pathname.split('/')[2] or 1 + userPageNav: (page, noRefresh) -> state = Index.pushState {page, scroll: true} if Conf['Refreshed Navigation'] and !noRefresh Index.update state else Index.pageLoad state if state.page + pushState: (state) -> {pathname, hash} = location pageBeforeSearch = history.state?.oldpage @@ -309,6 +321,7 @@ Index = oldpage: pageBeforeSearch , '', pathname + hash state + pageLoad: ({sort, search, mode, scroll}) -> if sort or search? Index.sort() @@ -318,6 +331,7 @@ Index = Index.buildIndex() Index.setPage() Index.scrollToIndex() if scroll + applyMode: -> for mode in ['paged', 'infinite', 'all pages', 'catalog'] $[if mode is Conf['Index Mode'] then 'addClass' else 'rmClass'] doc, "#{mode.replace /\ /g, '-'}-mode" @@ -331,8 +345,10 @@ Index = Math.ceil Index.sortedNodes.length / Index.threadsNumPerPage else Index.pagesNum + getMaxPageNum: -> Math.max 1, Index.getPagesNum() + buildPagelist: -> pagesRoot = $ '.pages', Index.pagelist maxPageNum = Index.getMaxPageNum() @@ -345,10 +361,12 @@ Index = nodes.push $.tn('['), a, $.tn '] ' $.rmAll pagesRoot $.add pagesRoot, nodes + setPage: -> pageNum = Index.getCurrentPage() maxPageNum = Index.getMaxPageNum() pagesRoot = $ '.pages', Index.pagelist + # Previous/Next buttons prev = pagesRoot.previousSibling.firstChild next = pagesRoot.nextSibling.firstChild @@ -358,12 +376,14 @@ Index = href = Math.min pageNum + 1, maxPageNum next.href = if href is 1 then './' else href next.firstChild.disabled = href is pageNum + # current page if strong = $ 'strong', pagesRoot return if +strong.textContent is pageNum $.replace strong, strong.firstChild else strong = $.el 'strong' + a = pagesRoot.children[pageNum - 1] $.before a, strong $.add strong, a diff --git a/src/General/Main.coffee b/src/General/Main.coffee index 263162553..57ad987e6 100755 --- a/src/General/Main.coffee +++ b/src/General/Main.coffee @@ -33,10 +33,13 @@ Main = else # string or number Conf[parent] = obj return + flatten null, Config + for db in DataBoard.keys Conf[db] = boards: {} Conf['selectedArchives'] = {} + $.get Conf, (items) -> $.extend Conf, items $.asap (-> doc = d.documentElement), Main.initFeatures @@ -71,7 +74,7 @@ Main = pathname = location.pathname.split '/' if pathname[2] isnt 'thread' or pathname.length > 4 pathname[2] = 'thread' - history.replaceState null, '', pathname.slice(0,4).join('/') + location.hash + history.replaceState null, '', pathname[0...4].join('/') + location.hash # c.time 'All initializations' for [name, feature] in Main.features diff --git a/src/General/Settings.coffee b/src/General/Settings.coffee index d1525a5d4..52c5eafb3 100755 --- a/src/General/Settings.coffee +++ b/src/General/Settings.coffee @@ -73,11 +73,13 @@ Settings = delete Settings.dialog sections: [] + addSection: (title, open) -> if typeof title isnt 'string' {title, open} = title.detail hyphenatedTitle = title.toLowerCase().replace /\s+/g, '-' Settings.sections.push {title, hyphenatedTitle, open} + openSection: -> if selected = $ '.tab-selected', Settings.dialog $.rmClass selected, 'tab-selected' @@ -142,12 +144,14 @@ Settings = localStorage.removeItem "4chan-hide-t-#{boardID}" $.delete ['hiddenThreads', 'hiddenPosts'] $.after $('input[name="Stubs"]', section).parentNode.parentNode, div + export: -> # Make sure to export the most recent data. $.get Conf, (Conf) -> # XXX don't export archives. delete Conf['archives'] Settings.downloadExport {version: g.VERSION, date: Date.now(), Conf} + downloadExport: (data) -> a = $.el 'a', download: "<%= meta.name %> v#{g.VERSION}-#{data.date}.json" @@ -160,12 +164,14 @@ Settings = a.click() import: -> $('input', @parentNode).click() + onImport: -> return unless file = @files[0] output = $('.imp-exp-result') unless confirm 'Your current settings will be entirely overwritten, are you sure?' output.textContent = 'Import aborted.' return + reader = new FileReader() reader.onload = (e) -> try @@ -176,6 +182,7 @@ Settings = output.textContent = 'Import failed due to an error.' c.error err.stack reader.readAsText file + loadSettings: (data) -> version = data.version.split '.' if version[0] is '2' @@ -259,6 +266,7 @@ Settings = data.Conf['watchedThreads'] = boards: ThreadWatcher.convert data.Conf['WatchedThreads'] delete data.Conf['WatchedThreads'] $.clear -> $.set data.Conf + reset: -> if confirm 'Your current settings will be entirely wiped, are you sure?' $.clear -> window.location.reload() if confirm 'Reset successful. Reload now?' @@ -326,9 +334,11 @@ Settings = interval = $ 'input[name="Interval"]', section customCSS = $ 'input[name="Custom CSS"]', section + interval.value = Conf['Interval'] customCSS.checked = Conf['Custom CSS'] inputs['usercss'].disabled = !Conf['Custom CSS'] + $.on interval, 'change', ThreadUpdater.cb.interval $.on customCSS, 'change', Settings.togglecss $.on $('#apply-css', section), 'click', Settings.usercss @@ -421,10 +431,13 @@ Settings = boardnav: -> Header.generateBoardList @value + time: -> @nextElementSibling.textContent = Time.format @value, new Date() + backlink: -> @nextElementSibling.textContent = @value.replace /%(?:id|%)/g, (x) -> {'%id': '123456789', '%%': '%'}[x] + fileInfo: -> data = isReply: true @@ -437,6 +450,7 @@ Settings = isImage: true isSpoiler: true FileInfo.format @value, data, @nextElementSibling + favicon: -> Favicon.switch() Unread.update() if g.VIEW is 'thread' and Conf['Unread Favicon'] @@ -445,12 +459,14 @@ Settings = img[1].src = Favicon.unreadSFW img[2].src = Favicon.unreadNSFW img[3].src = Favicon.unreadDead + togglecss: -> if $('textarea[name=usercss]', $.x 'ancestor::fieldset[1]', @).disabled = !@checked CustomCSS.rmStyle() else CustomCSS.addStyle() $.cb.checked.call @ + usercss: -> CustomCSS.update() @@ -475,6 +491,7 @@ Settings = for key, val of items inputs[key].value = val return + keybind: (e) -> return if e.keyCode is 9 # tab e.preventDefault() diff --git a/src/General/UI.coffee b/src/General/UI.coffee index b5b011db0..b0cd0a41d 100755 --- a/src/General/UI.coffee +++ b/src/General/UI.coffee @@ -328,6 +328,7 @@ UI = do -> if $.x 'ancestor::div[contains(@class,"inline")][1]', root $.on d, 'keydown', o.hoverend $.on root, 'mousemove', o.hover + <% if (type === 'userscript') { %> # Workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=674955 o.workaround = (e) -> o.hoverend(e) unless root.contains e.target diff --git a/src/General/lib/$.coffee b/src/General/lib/$.coffee index a4c9cd601..99c85c940 100755 --- a/src/General/lib/$.coffee +++ b/src/General/lib/$.coffee @@ -443,6 +443,7 @@ do -> delete $.oldValue[key] cb undefined, key $.on window, 'storage', ({key}) -> onChange key + $.forceSync = (key) -> # Storage events don't work across origins # e.g. http://boards.4chan.org and https://boards.4chan.org @@ -482,6 +483,7 @@ $.set = do -> $.oldValue[key] = val # for `storage` events localStorage.setItem key, val + (keys, val) -> if typeof keys is 'string' set keys, val diff --git a/src/General/lib/randomaccesslist.class b/src/General/lib/randomaccesslist.class index cf51715a0..b37fe246a 100644 --- a/src/General/lib/randomaccesslist.class +++ b/src/General/lib/randomaccesslist.class @@ -62,7 +62,7 @@ class RandomAccessList shift: -> @rm @first.ID - + order: -> order = [item = @first] order.push item while item = item.next diff --git a/src/General/lib/thread.class b/src/General/lib/thread.class index f083ed1f8..d68c1c75a 100755 --- a/src/General/lib/thread.class +++ b/src/General/lib/thread.class @@ -28,6 +28,7 @@ class Thread icon.title = "This thread is on page #{pageNum} in the original index." icon.textContent = "[#{pageNum}]" @catalogView.nodes.pageCount.textContent = pageNum if @catalogView + setCount: (type, count, reachedLimit) -> return unless @catalogView el = @catalogView.nodes["#{type}Count"] @@ -47,6 +48,7 @@ class Thread typeLC = type.toLowerCase() icon = $ ".#{typeLC}Icon", @OP.nodes.info return if !!icon is status + unless status $.rm icon.previousSibling $.rm icon @@ -57,6 +59,7 @@ class Thread alt: type title: type className: "#{typeLC}Icon retina" + root = if type isnt 'Sticky' and @isSticky $ '.stickyIcon', @OP.nodes.info else diff --git a/src/Images/Gallery.coffee b/src/Images/Gallery.coffee index 7b77df7c8..51d21f9c1 100644 --- a/src/Images/Gallery.coffee +++ b/src/Images/Gallery.coffee @@ -78,6 +78,7 @@ Gallery = $.on d, 'keydown', cb.keybinds $.off d, 'keydown', Keybinds.keydown if Conf['Keybinds'] + for file in $$ '.post .file' when !$ '.fileDeletedRes, .fileDeleted', file post = Get.postFromNode file Gallery.generateThumb post @@ -87,6 +88,7 @@ Gallery = if Header.getTopOf(candidate) + candidate.getBoundingClientRect().height >= 0 image = candidate $.addClass doc, 'gallery-open' + $.add d.body, dialog nodes.thumbs.scrollTop = 0 @@ -103,6 +105,7 @@ Gallery = return if post.isClone or post.isHidden return unless post.file and (post.file.isImage or post.file.isVideo or Conf['PDF in Gallery']) return if Gallery.fullIDs[post.fullID] + Gallery.fullIDs[post.fullID] = true thumb = $.el 'a', @@ -110,13 +113,14 @@ Gallery = href: post.file.URL target: '_blank' title: post.file.name - thumb.dataset.id = Gallery.images.length + + thumb.dataset.id = Gallery.images.length thumb.dataset.post = post.fullID thumbImg = post.file.thumb.cloneNode false thumbImg.style.cssText = '' $.add thumb, thumbImg - + $.on thumb, 'click', Gallery.cb.open Gallery.images.push thumb @@ -135,6 +139,7 @@ Gallery = elType = 'img' elType = 'video' if /\.webm$/.test(thumb.href) elType = 'iframe' if /\.pdf$/.test(thumb.href) + $[if elType is 'iframe' then 'addClass' else 'rmClass'] doc, 'gal-pdf' file = $.el elType, title: name.download = name.textContent = thumb.title @@ -142,7 +147,7 @@ Gallery = Gallery.error file, thumb file.src = name.href = thumb.href - $.extend file.dataset, thumb.dataset + $.extend file.dataset, thumb.dataset nodes.current.pause?() unless nodes.current.error $.replace nodes.current, file if elType is 'video' @@ -242,10 +247,12 @@ Gallery = Gallery.cb.open.call( Gallery.images[+Gallery.nodes.current.dataset.id + 1] or Gallery.images[0] ) + click: (e) -> return if ImageCommon.onControls e e.preventDefault() Gallery.cb.advance() + advance: -> if Gallery.nodes.current.paused then Gallery.nodes.current.play() else Gallery.cb.next() toggle: -> (if Gallery.nodes then Gallery.cb.close else Gallery.build)() blank: (e) -> Gallery.cb.close() if e.target is @ diff --git a/src/Images/ImageExpand.coffee b/src/Images/ImageExpand.coffee index 4793d97cf..02cc24b35 100755 --- a/src/Images/ImageExpand.coffee +++ b/src/Images/ImageExpand.coffee @@ -7,6 +7,7 @@ ImageExpand = textContent: 'EAI' title: 'Expand All Images' href: 'javascript:;' + $.on @EAI, 'click', @cb.toggleAll Header.addShortcut @EAI, 3 $.on d, 'scroll visibilitychange', @cb.playVideos @@ -18,14 +19,17 @@ ImageExpand = node: -> return unless @file and (@file.isImage or @file.isVideo) $.on @file.thumb.parentNode, 'click', ImageExpand.cb.toggle + if @isClone if @file.isExpanding # If we clone a post where the image is still loading, # make it loading in the clone too. ImageExpand.contract @ ImageExpand.expand @ + else if @file.isExpanded and @file.isVideo ImageExpand.setupVideo @, !@origin.file.fullImage?.paused or @origin.file.wasPlaying, @file.fullImage.controls + else if ImageExpand.on and !@isHidden and !@isFetchedQuote and (Conf['Expand spoilers'] or !@file.isSpoiler) and (Conf['Expand videos'] or !@file.isVideo) @@ -247,7 +251,7 @@ ImageExpand = el = $.el 'span', textContent: 'Image Expansion' - className: 'image-expansion-link' + className: 'image-expansion-link' {createSubEntry} = ImageExpand.menu subEntries = [] diff --git a/src/Images/ImageHover.coffee b/src/Images/ImageHover.coffee index 7be6c5981..cfd8beb3f 100755 --- a/src/Images/ImageHover.coffee +++ b/src/Images/ImageHover.coffee @@ -9,13 +9,16 @@ ImageHover = CatalogThread.callbacks.push name: 'Catalog Image Hover' cb: @catalogNode + node: -> return unless @file and (@file.isImage or @file.isVideo) $.on @file.thumb, 'mouseover', ImageHover.mouseover @ + catalogNode: -> {file} = @thread.OP return unless file and (file.isImage or file.isVideo) $.on @nodes.thumb, 'mouseover', ImageHover.mouseover @thread.OP + mouseover: (post) -> (e) -> return unless doc.contains @ {file} = post @@ -30,6 +33,7 @@ ImageHover = el.dataset.fullID = post.fullID $.on el, 'error', error el.src = file.URL + if Conf['Restart when Opened'] ImageCommon.rewind el ImageCommon.rewind @ @@ -46,7 +50,7 @@ ImageHover = maxWidth = Math.max left, doc.clientWidth - right maxHeight = doc.clientHeight - padding scale = Math.min 1, maxWidth / width, maxHeight / height - el.style.maxWidth = "#{scale * width}px" + el.style.maxWidth = "#{scale * width}px" el.style.maxHeight = "#{scale * height}px" UI.hover root: @ @@ -62,6 +66,7 @@ ImageHover = el.pause() if isVideo $.rm el el.removeAttribute 'style' + error: (post) -> -> return if ImageCommon.decodeError @, post ImageCommon.error @, post, 3 * $.SECOND, (URL) => diff --git a/src/Images/ImageLoader.coffee b/src/Images/ImageLoader.coffee index 0893abc81..86fffc7ca 100755 --- a/src/Images/ImageLoader.coffee +++ b/src/Images/ImageLoader.coffee @@ -75,6 +75,7 @@ ImageLoader = if !chrome? $.on thumb, 'loadeddata', -> @removeAttribute 'poster' return + el = $.el if isImage then 'img' else 'video' if replace and isImage $.on el, 'load', -> diff --git a/src/Images/Sauce.coffee b/src/Images/Sauce.coffee index b7945dcc3..a3568a3d7 100755 --- a/src/Images/Sauce.coffee +++ b/src/Images/Sauce.coffee @@ -40,7 +40,7 @@ Sauce = type ext = post.file.URL.match(/\.([^\.]*)$/)?[1] or '' return null unless !parts['boards'] or post.board.ID in parts['boards'].split ',' - return null unless !parts['types'] or ext in parts['types'].split ',' + return null unless !parts['types'] or ext in parts['types'].split ',' a = Sauce.link.cloneNode true a.href = parts['url'] a.textContent = parts['text'] @@ -49,8 +49,7 @@ Sauce = node: -> return if @isClone or !@file nodes = [] - for link in Sauce.links - if node = Sauce.createSauceLink link, @ - # \u00A0 is nbsp - nodes.push $.tn('\u00A0'), node + for link in Sauce.links when node = Sauce.createSauceLink link, @ + # \u00A0 is nbsp + nodes.push $.tn('\u00A0'), node $.add @file.text, nodes diff --git a/src/Menu/Menu.coffee b/src/Menu/Menu.coffee index e73258cbb..7f86d6f4e 100755 --- a/src/Menu/Menu.coffee +++ b/src/Menu/Menu.coffee @@ -5,12 +5,14 @@ Menu = @button = $.el 'a', className: 'menu-button' href: 'javascript:;' + $.extend @button, <%= html('') %> @menu = new UI.Menu 'post' Post.callbacks.push name: 'Menu' cb: @node + CatalogThread.callbacks.push name: 'Menu' cb: @catalogNode diff --git a/src/Miscellaneous/AnnouncementHiding.coffee b/src/Miscellaneous/AnnouncementHiding.coffee index 9eba521e2..10a1697a3 100755 --- a/src/Miscellaneous/AnnouncementHiding.coffee +++ b/src/Miscellaneous/AnnouncementHiding.coffee @@ -1,6 +1,6 @@ PSAHiding = init: -> - return if !Conf['Announcement Hiding'] + return unless Conf['Announcement Hiding'] $.addClass doc, 'hide-announcement' $.one d, '4chanXInitFinished', @setup @@ -24,7 +24,9 @@ PSAHiding = PSAHiding.btn = btn = $.el 'span', title: 'Mark announcement as read and hide.' className: 'hide-announcement' + $.extend btn, <%= html('[Dismiss]') %> + $.on btn, 'click', PSAHiding.toggle $.get 'hiddenPSA', 0, ({hiddenPSA}) -> @@ -33,6 +35,7 @@ PSAHiding = $.rmClass doc, 'hide-announcement' $.sync 'hiddenPSA', PSAHiding.sync + toggle: (e) -> if $.hasClass @, 'hide-announcement' UTC = +$.id('globalMessage').dataset.utc @@ -41,6 +44,7 @@ PSAHiding = $.event 'CloseMenu' $.delete 'hiddenPSA' PSAHiding.sync UTC + sync: (UTC) -> {psa} = PSAHiding PSAHiding.hidden = PSAHiding.btn.hidden = UTC? and UTC >= +psa.dataset.utc diff --git a/src/Miscellaneous/Banner.coffee b/src/Miscellaneous/Banner.coffee index 8a4503842..829861d40 100644 --- a/src/Miscellaneous/Banner.coffee +++ b/src/Miscellaneous/Banner.coffee @@ -55,7 +55,7 @@ Banner = i = Math.floor(Banner.choices.length * Math.random()) banner = Banner.choices.splice i, 1 $('img', @parentNode).src = "//s.4cdn.org/image/title/#{banner}" - + click: (e) -> if e.ctrlKey or e.metaKey @contentEditable = true diff --git a/src/Miscellaneous/CustomCSS.coffee b/src/Miscellaneous/CustomCSS.coffee index 289ae2a28..af3b94227 100755 --- a/src/Miscellaneous/CustomCSS.coffee +++ b/src/Miscellaneous/CustomCSS.coffee @@ -2,12 +2,15 @@ CustomCSS = init: -> return unless Conf['Custom CSS'] @addStyle() + addStyle: -> @style = $.addStyle Conf['usercss'], 'custom-css', -> $.id 'fourchanx-css' + rmStyle: -> if @style $.rm @style delete @style + update: -> unless @style @addStyle() diff --git a/src/Miscellaneous/ExpandComment.coffee b/src/Miscellaneous/ExpandComment.coffee index 32fa96156..bb62bacbe 100755 --- a/src/Miscellaneous/ExpandComment.coffee +++ b/src/Miscellaneous/ExpandComment.coffee @@ -12,10 +12,13 @@ ExpandComment = node: -> if a = $ '.abbr > a:not([onclick])', @nodes.comment $.on a, 'click', ExpandComment.cb + callbacks: [] + cb: (e) -> e.preventDefault() ExpandComment.expand Get.postFromNode @ + expand: (post) -> if post.nodes.longComment and !post.nodes.longComment.parentNode $.replace post.nodes.shortComment, post.nodes.longComment @@ -24,12 +27,14 @@ ExpandComment = return unless a = $ '.abbr > a', post.nodes.comment a.textContent = "Post No.#{post} Loading..." $.cache "//a.4cdn.org#{a.pathname.split('/').splice(0,4).join('/')}.json", -> ExpandComment.parse @, a, post + contract: (post) -> return unless post.nodes.shortComment a = $ '.abbr > a', post.nodes.shortComment a.textContent = 'here' $.replace post.nodes.longComment, post.nodes.shortComment post.nodes.comment = post.nodes.shortComment + parse: (req, a, post) -> {status} = req unless status in [200, 304] diff --git a/src/Miscellaneous/Fourchan.coffee b/src/Miscellaneous/Fourchan.coffee index 02961c833..098ce1d16 100755 --- a/src/Miscellaneous/Fourchan.coffee +++ b/src/Miscellaneous/Fourchan.coffee @@ -1,6 +1,6 @@ Fourchan = init: -> - return if g.VIEW not in ['index', 'thread'] + return unless g.VIEW in ['index', 'thread'] if g.BOARD.ID is 'g' $.globalEval ''' diff --git a/src/Miscellaneous/IDColor.coffee b/src/Miscellaneous/IDColor.coffee index 854f2e56e..32f41ced4 100755 --- a/src/Miscellaneous/IDColor.coffee +++ b/src/Miscellaneous/IDColor.coffee @@ -12,6 +12,8 @@ IDColor = span = $ '.hand', @nodes.uniqueID return unless span and span.nodeName is 'SPAN' rgb = IDColor.compute uid + + # Style the damn node. {style} = span style.color = rgb[3] style.backgroundColor = "rgb(#{rgb[0]},#{rgb[1]},#{rgb[2]})" @@ -21,16 +23,24 @@ IDColor = compute: (uid) -> return IDColor.ids[uid] if IDColor.ids[uid] + # Convert chars to integers, bitshift and math to create a larger integer + # Create a nice string of binary hash = IDColor.hash uid + + # Convert binary string to numerical values with bitshift and '&' truncation. rgb = [ (hash >> 24) & 0xFF (hash >> 16) & 0xFF (hash >> 8) & 0xFF ] + + # Weight color luminance values, assign a font color that should be readable. rgb[3] = if (rgb[0] * 0.299 + rgb[1] * 0.587 + rgb[2] * 0.114) > 125 '#000' else '#fff' + + # Cache. @ids[uid] = rgb hash: (uid) -> @@ -38,4 +48,4 @@ IDColor = i = 0 while i < 8 msg = (msg << 5) - msg + uid.charCodeAt i++ - msg \ No newline at end of file + msg diff --git a/src/Miscellaneous/IDHighlight.coffee b/src/Miscellaneous/IDHighlight.coffee index ad0e29075..4befd3112 100644 --- a/src/Miscellaneous/IDHighlight.coffee +++ b/src/Miscellaneous/IDHighlight.coffee @@ -1,6 +1,6 @@ IDHighlight = init: -> - return if g.VIEW not in ['index', 'thread'] + return unless g.VIEW in ['index', 'thread'] Post.callbacks.push name: 'Highlight by User ID' diff --git a/src/Miscellaneous/Keybinds.coffee b/src/Miscellaneous/Keybinds.coffee index 94b26432f..73ae10155 100755 --- a/src/Miscellaneous/Keybinds.coffee +++ b/src/Miscellaneous/Keybinds.coffee @@ -56,16 +56,16 @@ Keybinds = else return when Conf['Spoiler tags'] - return if target.nodeName isnt 'TEXTAREA' + return unless target.nodeName is 'TEXTAREA' Keybinds.tags 'spoiler', target when Conf['Code tags'] - return if target.nodeName isnt 'TEXTAREA' + return unless target.nodeName is 'TEXTAREA' Keybinds.tags 'code', target when Conf['Eqn tags'] - return if target.nodeName isnt 'TEXTAREA' + return unless target.nodeName is 'TEXTAREA' Keybinds.tags 'eqn', target when Conf['Math tags'] - return if target.nodeName isnt 'TEXTAREA' + return unless target.nodeName is 'TEXTAREA' Keybinds.tags 'math', target when Conf['Toggle sage'] return unless QR.nodes and !QR.nodes.el.hidden diff --git a/src/Miscellaneous/Nav.coffee b/src/Miscellaneous/Nav.coffee index fee3c09ca..71227374b 100755 --- a/src/Miscellaneous/Nav.coffee +++ b/src/Miscellaneous/Nav.coffee @@ -62,7 +62,9 @@ Nav = # Add extra space to the end of the page if necessary so that all threads can be selected by keybinds. extra = Header.getTopOf(thread) + doc.clientHeight - d.body.getBoundingClientRect().bottom d.body.style.marginBottom = "#{extra}px" if extra > 0 + Header.scrollTo thread + if extra > 0 and !Nav.haveExtra Nav.haveExtra = true $.on d, 'scroll', Nav.removeExtra diff --git a/src/Miscellaneous/RemoveSpoilers.coffee b/src/Miscellaneous/RemoveSpoilers.coffee index 2cedb7837..8295e9960 100755 --- a/src/Miscellaneous/RemoveSpoilers.coffee +++ b/src/Miscellaneous/RemoveSpoilers.coffee @@ -8,9 +8,11 @@ RemoveSpoilers = Post.callbacks.push name: 'Reveal Spoilers' cb: @node + CatalogThread.callbacks.push name: 'Reveal Spoilers' cb: @node + if g.VIEW is 'archive' $.ready -> RemoveSpoilers.unspoiler $.id 'arc-list' diff --git a/src/Miscellaneous/Time.coffee b/src/Miscellaneous/Time.coffee index ddb048633..a2a565ca8 100755 --- a/src/Miscellaneous/Time.coffee +++ b/src/Miscellaneous/Time.coffee @@ -5,6 +5,7 @@ Time = Post.callbacks.push name: 'Time Formatting' cb: @node + node: -> return if @isClone @nodes.date.textContent = Time.format Conf['time'], @info.date @@ -14,6 +15,7 @@ Time = Time.formatters[c].call(date) else s + day: [ 'Sunday' 'Monday' @@ -23,6 +25,7 @@ Time = 'Friday' 'Saturday' ] + month: [ 'January' 'February' @@ -37,7 +40,9 @@ Time = 'November' 'December' ] + zeroPad: (n) -> if n < 10 then "0#{n}" else n + formatters: a: -> Time.day[@getDay()][...3] A: -> Time.day[@getDay()] diff --git a/src/Monitoring/ThreadStats.coffee b/src/Monitoring/ThreadStats.coffee index 29a77d7de..76795bf8b 100755 --- a/src/Monitoring/ThreadStats.coffee +++ b/src/Monitoring/ThreadStats.coffee @@ -18,6 +18,7 @@ ThreadStats = $.extend sc, statsHTML $.ready -> Header.addShortcut sc + else @dialog = sc = UI.dialog 'thread-stats', 'bottom: 0px; right: 0px;', <%= html('
&{statsHTML}
') %> diff --git a/src/Monitoring/ThreadUpdater.coffee b/src/Monitoring/ThreadUpdater.coffee index eb52bd24d..cc6a9cebb 100755 --- a/src/Monitoring/ThreadUpdater.coffee +++ b/src/Monitoring/ThreadUpdater.coffee @@ -8,7 +8,7 @@ ThreadUpdater = $.extend sc, <%= html('') %> $.ready -> Header.addShortcut sc - else + else @dialog = sc = UI.dialog 'updater', 'bottom: 0px; left: 0px;', <%= html('
') %> $.addClass doc, 'float' @@ -176,7 +176,7 @@ ThreadUpdater = setInterval: -> i = ThreadUpdater.interval + 1 - + if Conf['Optional Increase'] # Lower the max refresh rate limit on visible tabs. cur = ThreadUpdater.outdateCount or 1 diff --git a/src/Monitoring/ThreadWatcher.coffee b/src/Monitoring/ThreadWatcher.coffee index a1d4503d0..c385353c6 100755 --- a/src/Monitoring/ThreadWatcher.coffee +++ b/src/Monitoring/ThreadWatcher.coffee @@ -11,10 +11,10 @@ ThreadWatcher = @db = new DataBoard 'watchedThreads', @refresh, true @dialog = UI.dialog 'thread-watcher', 'top: 50px; left: 0px;', <%= importHTML('Monitoring/ThreadWatcher') %> + @status = $ '#watcher-status', @dialog @list = @dialog.lastElementChild @refreshButton = $ '.move > .refresh', @dialog - @unreaddb = Unread.db or new DataBoard 'lastReadPosts' $.on d, 'QRPostSuccessful', @cb.post @@ -23,6 +23,7 @@ ThreadWatcher = $.on $('.move > .close', @dialog), 'click', @toggleWatcher $.on d, '4chanXInitFinished', @ready + switch g.VIEW when 'index' $.on d, 'IndexRefresh', @cb.onIndexRefresh @@ -147,6 +148,7 @@ ThreadWatcher = fetchCount: fetched: 0 fetching: 0 + fetchAuto: -> clearTimeout ThreadWatcher.timeout return unless Conf['Auto Update Thread Watcher'] @@ -158,6 +160,7 @@ ThreadWatcher = ThreadWatcher.fetchAllStatus() db.save() ThreadWatcher.timeout = setTimeout ThreadWatcher.fetchAuto, interval + fetchAllStatus: -> ThreadWatcher.db.forceSync() ThreadWatcher.unreaddb.forceSync() @@ -166,6 +169,7 @@ ThreadWatcher = for thread in threads ThreadWatcher.fetchStatus thread return + fetchStatus: (thread) -> {boardID, threadID, data} = thread return if data.isDead and !Conf['Show Unread Count'] @@ -177,6 +181,7 @@ ThreadWatcher = $.ajax "//a.4cdn.org/#{boardID}/thread/#{threadID}.json", onloadend: -> ThreadWatcher.parseStatus.call @, thread + parseStatus: ({boardID, threadID, data}) -> {fetchCount} = ThreadWatcher fetchCount.fetched++ @@ -233,6 +238,7 @@ ThreadWatcher = delete data.unread delete data.quotingYou ThreadWatcher.db.set {boardID, threadID, val: data} + ThreadWatcher.refresh() getAll: -> @@ -269,8 +275,8 @@ ThreadWatcher = div = $.el 'div' fullID = "#{boardID}.#{threadID}" div.dataset.fullID = fullID - $.addClass div, 'current' if g.VIEW is 'thread' and fullID is "#{g.BOARD}.#{g.THREADID}" - $.addClass div, 'dead-thread' if data.isDead + $.addClass div, 'current' if g.VIEW is 'thread' and fullID is "#{g.BOARD}.#{g.THREADID}" + $.addClass div, 'dead-thread' if data.isDead if Conf['Show Unread Count'] $.addClass div, 'replies-unread' if data.unread $.addClass div, 'replies-quoting-you' if data.quotingYou @@ -336,6 +342,7 @@ ThreadWatcher = ThreadWatcher.rm boardID, threadID else ThreadWatcher.add thread + add: (thread) -> data = {} boardID = thread.board.ID @@ -350,6 +357,7 @@ ThreadWatcher = ThreadWatcher.refresh() if Conf['Show Unread Count'] ThreadWatcher.fetchStatus {boardID, threadID, data} + rm: (boardID, threadID) -> ThreadWatcher.db.delete {boardID, threadID} ThreadWatcher.refresh() @@ -423,6 +431,7 @@ ThreadWatcher = @refreshers.push refresh.bind entry if refresh @menu.addEntry entry return + createSubEntry: (name, desc) -> entry = type: 'thread watcher' @@ -430,6 +439,6 @@ ThreadWatcher = entry.el.title = desc input = entry.el.firstElementChild $.on input, 'change', $.cb.checked - $.on input, 'change', ThreadWatcher.refresh if name in ['Current Board', 'Show Unread Count'] + $.on input, 'change', ThreadWatcher.refresh if name in ['Current Board', 'Show Unread Count'] $.on input, 'change', ThreadWatcher.fetchAuto if name in ['Show Unread Count', 'Auto Update Thread Watcher'] entry diff --git a/src/Monitoring/Unread.coffee b/src/Monitoring/Unread.coffee index ad8b75dcf..fb78fd62a 100755 --- a/src/Monitoring/Unread.coffee +++ b/src/Monitoring/Unread.coffee @@ -20,6 +20,7 @@ Unread = Thread.callbacks.push name: 'Unread' cb: @node + Post.callbacks.push name: 'Unread' cb: @addPost diff --git a/src/Posting/Captcha.noscript.coffee b/src/Posting/Captcha.noscript.coffee index bcaa22a2a..a7a10ac8d 100644 --- a/src/Posting/Captcha.noscript.coffee +++ b/src/Posting/Captcha.noscript.coffee @@ -1,5 +1,5 @@ Captcha.noscript = - lifetime: 2 * $.MINUTE + lifetime: 2 * $.MINUTE iframeURL: '//www.google.com/recaptcha/api/fallback?k=<%= meta.recaptchaKey %>' init: -> diff --git a/src/Posting/QR.coffee b/src/Posting/QR.coffee index da12eb6fa..0066caa4d 100644 --- a/src/Posting/QR.coffee +++ b/src/Posting/QR.coffee @@ -113,6 +113,7 @@ QR = return if Conf['QR Shortcut'] $.rmClass $('.qr-shortcut'), 'disabled' + close: -> if QR.req QR.abort() @@ -129,6 +130,7 @@ QR = QR.cooldown.auto = false QR.status() QR.captcha.destroy() + focus: -> $.queueTask -> return unless QR.nodes @@ -142,18 +144,22 @@ QR = $.on d, 'scroll', QR.scrollLock else $.off d, 'scroll', QR.scrollLock + scrollLock: (e) -> if d.activeElement and QR.nodes.el.contains(d.activeElement) and d.activeElement.nodeName is 'IFRAME' window.scroll window.scrollX, QR.scrollY else $.off d, 'scroll', QR.scrollLock + hide: -> d.activeElement.blur() $.addClass QR.nodes.el, 'autohide' QR.nodes.autohide.checked = true + unhide: -> $.rmClass QR.nodes.el, 'autohide' QR.nodes.autohide.checked = false + toggleHide: -> if @checked QR.hide() @@ -195,6 +201,7 @@ QR = <% } %> notifications: [] + cleanNotifications: -> for notification in QR.notifications notification.close() @@ -449,7 +456,8 @@ QR = dialog: -> QR.nodes = nodes = - el: dialog = UI.dialog 'qr', 'top: 50px; right: 0px;', <%= importHTML('Features/QuickReply') %> + el: dialog = UI.dialog 'qr', 'top: 50px; right: 0px;', + <%= importHTML('Features/QuickReply') %> setNode = (name, query) -> nodes[name] = $ query, dialog @@ -478,7 +486,7 @@ QR = setNode 'spoilerPar', '#qr-spoiler-label' setNode 'status', '[type=submit]' setNode 'fileInput', '[type=file]' - + rules = $('ul.rules').textContent.trim() match_min = rules.match(/.+smaller than (\d+)x(\d+).+/) match_max = rules.match(/.+greater than (\d+)x(\d+).+/) @@ -516,7 +524,9 @@ QR = nodes.spoiler.parentElement.hidden = true if g.BOARD.ID is 'f' and g.VIEW isnt 'thread' - nodes.flashTag = $.el 'select', name: 'filetag' + nodes.flashTag = $.el 'select', + name: 'filetag' + $.extend nodes.flashTag, <%= html( '' + '' + @@ -526,6 +536,7 @@ QR = '' + '' ) %> + nodes.flashTag.dataset.default = '4' $.add nodes.form, nodes.flashTag @@ -537,8 +548,8 @@ QR = $.on nodes.urlButton, 'click', QR.handleUrl $.on nodes.addPost, 'click', -> new QR.post true $.on nodes.form, 'submit', QR.submit - $.on nodes.fileRM, 'click', -> QR.selected.rmFile() - $.on nodes.fileExtras, 'click', (e) -> e.stopPropagation() + $.on nodes.fileRM, 'click', -> QR.selected.rmFile() + $.on nodes.fileExtras, 'click', (e) -> e.stopPropagation() $.on nodes.spoiler, 'change', -> QR.selected.nodes.spoiler.click() $.on nodes.fileInput, 'change', QR.handleFiles @@ -566,6 +577,7 @@ QR = QR.status() QR.cooldown.init() QR.captcha.init() + $.add d.body, dialog QR.captcha.setup() @@ -798,7 +810,7 @@ QR = QR.close() else post.rm() - QR.captcha.setup (d.activeElement is QR.nodes.status) + QR.captcha.setup(d.activeElement is QR.nodes.status) QR.cooldown.add req.uploadEndTime, threadID, postID @@ -806,6 +818,7 @@ QR = "#{window.location.origin}/#{g.BOARD}/thread/#{threadID}" else if g.VIEW is 'index' and !QR.cooldown.auto and Conf['Open Post in New Tab'] # replying from the index "#{window.location.origin}/#{g.BOARD}/thread/#{threadID}#p#{postID}" + if URL if Conf['Open Post in New Tab'] or postsCount $.open URL diff --git a/src/Posting/QR.post.coffee b/src/Posting/QR.post.coffee index a99d6339b..919c6dad7 100644 --- a/src/Posting/QR.post.coffee +++ b/src/Posting/QR.post.coffee @@ -71,6 +71,7 @@ QR.post = class (QR.posts[index-1] or QR.posts[index+1]).select() QR.posts.splice index, 1 QR.status() + delete: -> $.rm @nodes.el URL.revokeObjectURL @URL @@ -103,10 +104,13 @@ QR.post = class load: -> # Load this post's values. + for name in ['thread', 'name', 'email', 'sub', 'com', 'filename'] continue unless node = QR.nodes[name] node.value = @[name] or node.dataset.default or null + (if @thread isnt 'new' then $.addClass else $.rmClass) QR.nodes.el, 'reply-to-thread' + @showFileData() QR.characterCount() diff --git a/src/Quotelinks/QuoteThreading.coffee b/src/Quotelinks/QuoteThreading.coffee index 8c944dc52..3a897f3a5 100755 --- a/src/Quotelinks/QuoteThreading.coffee +++ b/src/Quotelinks/QuoteThreading.coffee @@ -9,6 +9,7 @@ QuoteThreading = @enabled = true @controls = $.el 'span', <%= html('') %> + @threadNewLink = $.el 'span', className: 'brackets-wrap threadnewlink' hidden: true @@ -27,6 +28,7 @@ QuoteThreading = Thread.callbacks.push name: 'Quote Threading' cb: @setThread + Post.callbacks.push name: 'Quote Threading' cb: @node diff --git a/src/Quotelinks/Quotify.coffee b/src/Quotelinks/Quotify.coffee index 6c24bed55..977c9f55c 100755 --- a/src/Quotelinks/Quotify.coffee +++ b/src/Quotelinks/Quotify.coffee @@ -55,7 +55,6 @@ Quotify = className: 'quotelink deadlink' target: '_blank' textContent: "#{quote}\u00A0(Dead)" - $.extend a.dataset, {boardID, threadID: post.thread.ID, postID} else