Merge branch 'v3' into Av2
Conflicts: LICENSE builds/4chan-X.js builds/4chan-X.meta.js builds/4chan-X.user.js builds/crx/manifest.json builds/crx/script.js package.json src/General/Main.coffee src/General/css/style.css src/General/html/Features/QuickReply.html src/Posting/QuickReply.coffee
95
CHANGELOG.md
@ -1,11 +1,75 @@
|
||||
### 1.1.9 - 2013-05-02
|
||||
seaweedchan
|
||||
- Fix boards with previously deleted archives not switching to new archives
|
||||
|
||||
ihavenoface:
|
||||
- 4chan Pass link by the style selector
|
||||
|
||||
zixaphir:
|
||||
- Make Allow False Positives option more efficient
|
||||
|
||||
### 1.1.8 - 2013-05-01
|
||||
seaweedchan:
|
||||
- Fix QR not clearing on submit with Posting Success Notifications disabled
|
||||
- New archives for /h/, /v/, and /vg/
|
||||
|
||||
### 1.1.7 - 2013-05-01
|
||||
seaweedchan:
|
||||
- External image embedding
|
||||
- Account for time options in youtube links for embedding
|
||||
- Once again remove /v/ and /vg/ archiving... ;_;
|
||||
- Add paste.installgentoo.com embedding
|
||||
- Added `Posting Success Notifications` option to make "Post Successful!" and "_____ uploaded" notifications optional
|
||||
- Added `Allow False Positives` option under Linkification, giving the user more control over what's linkified.
|
||||
- Fix URL for update checking
|
||||
|
||||
### 1.1.6 - 2013-05-01
|
||||
seaweedchan:
|
||||
- Fix Gist links if no username is specificed
|
||||
|
||||
MayhemYDG:
|
||||
- Access it in the `Advanced` tab of the Settings window.
|
||||
|
||||
zixaphir:
|
||||
- Add Gist link titles
|
||||
|
||||
### 1.1.5 - 2013-04-30
|
||||
seaweedchan:
|
||||
- Fix various embedding issues
|
||||
- Fix Link Title depending on Embedding
|
||||
- Added favicons to links that can be embedded
|
||||
- Add gist embedding
|
||||
|
||||
### 1.1.4 - 2013-04-29
|
||||
seaweedchan:
|
||||
- Change ESC functionality in QR to autohide if Persistent QR is enabled
|
||||
- Add /v/ and /vg/ archiving to archive.nihil-ad-rem.net, and make sure Archiver Selection settings actually switch to it
|
||||
- Add option to toggle between updater and stats fixed in header or floating
|
||||
|
||||
MayhemYDG:
|
||||
- Add nyafuu archiving for /w/
|
||||
- Add /d/ archive
|
||||
|
||||
### 1.1.3 - 2013-04-28
|
||||
seaweedchan:
|
||||
- Chrome doesn't get .null, so don't style it
|
||||
- Fix count when auto update is disabled and set updater text to "Update"
|
||||
- Remove /v/ and /vg/ redirection. See https://archive.foolz.us/foolz/thread/509388/ for news and how you can donate to bring /v/ and /vg/ archiving back.
|
||||
- Remove /v/ and /vg/ redirection from Foolz.
|
||||
- Toggle keybind for header auto-hiding
|
||||
|
||||
MayhemYDG:
|
||||
=======
|
||||
- Access it in the `QR` tab of the Settings window.
|
||||
- Updated archive redirection for /h/, /v/ and /vg/.
|
||||
|
||||
### 3.2.3 - *2013-04-30*
|
||||
|
||||
- Update archive redirection for /c/, /d/, /v/, /vg/, /w/ and /wg/.
|
||||
- Minor fixes.
|
||||
|
||||
### 3.2.2 - *2013-04-27*
|
||||
|
||||
>>>>>>> b74e0c92fdf2d755d996cb574dddb3c8d964e91a
|
||||
- Fix Unread Count taking into account hidden posts.
|
||||
|
||||
### 1.1.2 - 2013-04-26
|
||||
@ -22,7 +86,7 @@ zixaphir:
|
||||
- Fix preview with favicons and emoji
|
||||
- Fix NaN error on Thread Updater Interval
|
||||
- Draggable UI can no longer overlap the Header.
|
||||
-- Setting the header to Autohide also increases its z-index to overlap other UI
|
||||
- Setting the header to Autohide also increases its z-index to overlap other UI
|
||||
|
||||
### 1.1.1 - 2013-04-26
|
||||
zixaphir:
|
||||
@ -35,29 +99,30 @@ MayhemYDG:
|
||||
seaweedchan:
|
||||
- Allow thread watcher to load on catalog
|
||||
|
||||
### 1.0.10:
|
||||
### 1.0.10 - 2013-04-23
|
||||
- Add message pertaining to rewrite
|
||||
|
||||
### 1.0.9:
|
||||
### 1.0.9 - 2013-04-17
|
||||
ihavenoface:
|
||||
- Implement Announcement Hiding
|
||||
seaweedchan:
|
||||
- Change #options back to inheriting colors from replies
|
||||
- Fix script breaking when disabling image expansion
|
||||
|
||||
### 1.0.8:
|
||||
### 1.0.8 - 2013-04-15
|
||||
seaweedchan:
|
||||
- Redo settings menu styling
|
||||
- Move Export/Import buttons and dialog
|
||||
- Update license and use banner.js for license
|
||||
|
||||
### 1.0.7:
|
||||
### 1.0.7 - 2013-04-14
|
||||
qqueue:
|
||||
- Relative post dates
|
||||
|
||||
MayhemYDG:
|
||||
- Exporting/importing settings
|
||||
|
||||
### 1.0.6
|
||||
### 1.0.6 - 2013-04-13
|
||||
seaweedchan:
|
||||
- Update supported boards for archive redirection and custom navigation
|
||||
- Point to github.io instead of github.com for pages
|
||||
@ -65,40 +130,44 @@ seaweedchan:
|
||||
- Make InstallGentoo default for /g/
|
||||
- Fix embedding issues
|
||||
|
||||
### 1.0.5:
|
||||
### 1.0.5 - 2013-04-09
|
||||
seaweedchan:
|
||||
- Added keybind to toggle Fappe Tyme
|
||||
- Fix code tag keybind
|
||||
|
||||
Zixaphir:
|
||||
- Add 'yourPost' class to own replies
|
||||
|
||||
### 1.0.4:
|
||||
### 1.0.4 - 2013-04-08
|
||||
seaweedchan:
|
||||
- Fix Fappe Tyme
|
||||
- Re- add label for image expanding
|
||||
- Move restore button to left side as per RiDeag
|
||||
|
||||
### 1.0.3
|
||||
### 1.0.3 - 2013-03-23
|
||||
seaweedchan:
|
||||
- Add ad- blocking CSS into Custom CSS examples
|
||||
|
||||
Zixaphir:
|
||||
- Fix ctrl+s bringing up save dialog
|
||||
- Fix issues with soundcloud embedding
|
||||
|
||||
### 1.0.2:
|
||||
### 1.0.2 - 2013-03-14
|
||||
seaweedchan:
|
||||
- New Rice option: Emoji Position
|
||||
- New layout for Rice tab
|
||||
- No more Yotsuba / Yotsuba B in options
|
||||
|
||||
### 1.0.1:
|
||||
### 1.0.1 - 2013-03-14
|
||||
- New option: Emoji
|
||||
- New Rice option: Sage Emoji
|
||||
|
||||
seaweedchan:
|
||||
- Prettier error messages
|
||||
|
||||
### 1.0.0
|
||||
### 1.0.0 - 2013-03-13
|
||||
- Initial release
|
||||
|
||||
zixaphir:
|
||||
- Fix unread post count for filtered posts
|
||||
- Fix issues when switching from ihavenoface's fork
|
||||
|
||||
@ -39,10 +39,11 @@ module.exports = (grunt) ->
|
||||
]
|
||||
dest: 'tmp-<%= pkg.type %>/script.coffee'
|
||||
|
||||
license:
|
||||
meta:
|
||||
options: concatOptions
|
||||
files:
|
||||
'LICENSE': 'src/General/meta/banner.js'
|
||||
'LICENSE': 'src/General/meta/banner.js',
|
||||
'builds/version': 'src/General/meta/version.js'
|
||||
|
||||
crx:
|
||||
options: concatOptions
|
||||
@ -86,6 +87,7 @@ module.exports = (grunt) ->
|
||||
|
||||
concurrent:
|
||||
build: [
|
||||
'concat:meta'
|
||||
'build-crx'
|
||||
'build-userjs'
|
||||
'build-userscript'
|
||||
@ -153,7 +155,6 @@ module.exports = (grunt) ->
|
||||
|
||||
grunt.registerTask 'build', [
|
||||
'concurrent:build'
|
||||
'concat:license'
|
||||
]
|
||||
|
||||
grunt.registerTask 'build-crx', [
|
||||
|
||||
2
LICENSE
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* appchan x - Version 2.0.0 - 2013-04-28
|
||||
* appchan x - Version 2.0.0 - 2013-05-03
|
||||
*
|
||||
* Licensed under the MIT license.
|
||||
* https://github.com/zixaphir/appchan-x/blob/master/LICENSE
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
1. Precise steps to reproduce the problem, with the expected and actual results.
|
||||
2. Console errors, if any.
|
||||
3. Browser version.
|
||||
4. Your exported settings.
|
||||
4. Your exported settings. If your settings contains sensitive information (e.g. personas), edit the text file manually.
|
||||
|
||||
Open your console with:
|
||||
- `Ctrl + Shift + J` on Chrome.
|
||||
|
||||
10166
builds/4chan-X.js
Normal file
19
builds/4chan-X.meta.js
Normal file
@ -0,0 +1,19 @@
|
||||
// ==UserScript==
|
||||
// @name 4chan X
|
||||
// @version 1.1.9
|
||||
// @namespace 4chan-X
|
||||
// @description Cross-browser userscript for maximum lurking on 4chan.
|
||||
// @license MIT; https://github.com/seaweedchan/4chan-x/blob/master/LICENSE
|
||||
// @match *://api.4chan.org/*
|
||||
// @match *://boards.4chan.org/*
|
||||
// @match *://images.4chan.org/*
|
||||
// @match *://sys.4chan.org/*
|
||||
// @grant GM_getValue
|
||||
// @grant GM_setValue
|
||||
// @grant GM_deleteValue
|
||||
// @grant GM_openInTab
|
||||
// @run-at document-start
|
||||
// @updateURL https://github.com/seaweedchan/4chan-x/raw/stable/builds/4chan_X.meta.js
|
||||
// @downloadURL https://github.com/seaweedchan/4chan-x/raw/stable/builds/4chan_X.user.js
|
||||
// @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwAgMAAAAqbBEUAAAACVBMVEUAAGcAAABmzDNZt9VtAAAAAXRSTlMAQObYZgAAAHFJREFUKFOt0LENACEIBdBv4Qju4wgWanEj3D6OcIVMKaitYHEU/jwTCQj8W75kiVCSBvdQ5/AvfVHBin11BgdRq3ysBgfwBDRrj3MCIA+oAQaku/Q1cNctrAmyDl577tOThYt/Y1RBM4DgOHzM0HFTAyLukH/cmRnqAAAAAElFTkSuQmCC
|
||||
// ==/UserScript==
|
||||
10189
builds/4chan-X.user.js
Normal file
1
builds/version
Normal file
@ -0,0 +1 @@
|
||||
2.0.0
|
||||
@ -1,30 +1,39 @@
|
||||
Redirect =
|
||||
init: ->
|
||||
$.sync 'archs', @updateArchives
|
||||
$.sync 'archivers', @updateArchives
|
||||
|
||||
updateArchives: ->
|
||||
$.get 'archivers', {}, ({archivers}) ->
|
||||
Conf['archivers'] = archivers
|
||||
|
||||
imageArchives: do ->
|
||||
o =
|
||||
a: "//archive.foolz.us/"
|
||||
ck: "//fuuka.warosu.org/"
|
||||
an: "http://archive.heinessen.com/"
|
||||
cgl: "//rbt.asia/"
|
||||
c: "//archive.nyafuu.org/"
|
||||
d: "//loveisover.me/"
|
||||
hr: "http://archive.4plebs.org/"
|
||||
u: "//nsfw.foolz.us/"
|
||||
po: "//archive.thedarkcave.org/"
|
||||
vg: "http://nth.pensivenonsen.se/"
|
||||
c: "//archive.nyafuu.org/"
|
||||
|
||||
o.gd = o.jp = o.m = o.q = o.tg = o.vp = o.vr = o.wsg = o.a
|
||||
o.fa = o.lit = o.s4s = o.ck
|
||||
o.k = o.toy = o.x = o.an
|
||||
o.g = o.mu = o.cgl
|
||||
o.w = o.wg = o.c
|
||||
o.h = o.v = o.d
|
||||
o.tv = o.hr
|
||||
|
||||
return o
|
||||
|
||||
image: (boardID, filename) ->
|
||||
# Do not use g.BOARD, the image url can originate from a cross-quote.
|
||||
switch boardID
|
||||
when 'a', 'gd', 'jp', 'm', 'q', 'tg', 'vp', 'vr', 'wsg'
|
||||
"//archive.foolz.us/#{boardID}/full_image/#{filename}"
|
||||
when 'u'
|
||||
"//nsfw.foolz.us/#{boardID}/full_image/#{filename}"
|
||||
when 'po'
|
||||
"//archive.thedarkcave.org/#{boardID}/full_image/#{filename}"
|
||||
when 'hr', 'tv'
|
||||
"http://archive.4plebs.org/#{boardID}/full_image/#{filename}"
|
||||
when 'ck', 'fa', 'lit', 's4s'
|
||||
"//fuuka.warosu.org/#{boardID}/full_image/#{filename}"
|
||||
when 'cgl', 'g', 'mu', 'w'
|
||||
"//rbt.asia/#{boardID}/full_image/#{filename}"
|
||||
when 'an', 'k', 'toy', 'x'
|
||||
"http://archive.heinessen.com/#{boardID}/full_image/#{filename}"
|
||||
when 'c'
|
||||
"//archive.nyafuu.org/#{boardID}/full_image/#{filename}"
|
||||
# Fuck. Your. Shit.
|
||||
"#{Redirect.imageArchives[boardID]}#{boardID}/full_image/#{filename}"
|
||||
|
||||
post: (boardID, postID) ->
|
||||
unless Redirect.post[boardID]?
|
||||
@ -58,6 +67,9 @@ Redirect =
|
||||
else
|
||||
null)
|
||||
|
||||
unless archive.boards.contains g.BOARD.ID
|
||||
Conf['archivers'] = archive
|
||||
|
||||
archiver:
|
||||
'Foolz':
|
||||
base: 'https://archive.foolz.us'
|
||||
@ -75,9 +87,21 @@ Redirect =
|
||||
base: 'http://archive.4plebs.org'
|
||||
boards: ['hr', 'tg', 'tv', 'x']
|
||||
base: 'foolfuuka'
|
||||
'NyaFuu':
|
||||
base: '//archive.nyafuu.org'
|
||||
boards: ['c', 'w', 'wg']
|
||||
type: 'foolfuuka'
|
||||
'LoveIsOver':
|
||||
base: '//loveisover.me'
|
||||
boards: ['d', 'h', 'v']
|
||||
type: 'foolfuuka'
|
||||
'PensiveNonsen':
|
||||
base: 'http://nth.pensivenonsen.se'
|
||||
boards: ['vg']
|
||||
type: 'foolfuuka'
|
||||
'Warosu':
|
||||
base: '//fuuka.warosu.org'
|
||||
boards: ['cgl', 'ck', 'fa', 'jp', 'lit', 's4s', 'q', 'tg']
|
||||
boards: ['cgl', 'ck', 'fa', 'jp', 'lit', 's4s', 'q', 'tg', 'vr']
|
||||
type: 'fuuka'
|
||||
'InstallGentoo':
|
||||
base: '//archive.installgentoo.net'
|
||||
@ -85,7 +109,7 @@ Redirect =
|
||||
type: 'fuuka'
|
||||
'RebeccaBlackTech':
|
||||
base: '//rbt.asia'
|
||||
boards: ['an', 'cgl', 'g', 'mu', 'w']
|
||||
boards: ['cgl', 'g', 'mu', 'w']
|
||||
type: 'fuuka_mail'
|
||||
'Heinessen':
|
||||
base: 'http://archive.heinessen.com'
|
||||
@ -95,10 +119,6 @@ Redirect =
|
||||
base: '//www.cliché.net/4chan/cgi-board.pl'
|
||||
boards: ['e']
|
||||
type: 'fuuka'
|
||||
'NyaFuu':
|
||||
base: '//archive.nyafuu.org'
|
||||
boards: ['c', 'w']
|
||||
type: 'fuuka'
|
||||
|
||||
path: (base, archiver, data) ->
|
||||
if data.isSearch
|
||||
|
||||
@ -19,12 +19,7 @@ ThreadHiding =
|
||||
hiddenThreads = ThreadHiding.db.get
|
||||
boardID: g.BOARD.ID
|
||||
defaultValue: {}
|
||||
# XXX tmp fix
|
||||
try
|
||||
hiddenThreadsOnCatalog = JSON.parse(localStorage.getItem "4chan-hide-t-#{g.BOARD}") or {}
|
||||
catch e
|
||||
localStorage.setItem "4chan-hide-t-#{g.BOARD}", JSON.stringify {}
|
||||
return ThreadHiding.syncCatalog()
|
||||
hiddenThreadsOnCatalog = JSON.parse(localStorage.getItem "4chan-hide-t-#{g.BOARD}") or {}
|
||||
|
||||
# Add threads that were hidden in the catalog.
|
||||
for threadID of hiddenThreadsOnCatalog
|
||||
@ -130,10 +125,10 @@ ThreadHiding =
|
||||
return if thread.isHidden
|
||||
{OP} = thread
|
||||
threadRoot = OP.nodes.root.parentNode
|
||||
threadRoot.hidden = thread.isHidden = true
|
||||
thread.isHidden = true
|
||||
|
||||
unless makeStub
|
||||
threadRoot.nextElementSibling.hidden = true # <hr>
|
||||
threadRoot.hidden = threadRoot.nextElementSibling.hidden = true # <hr>
|
||||
return
|
||||
|
||||
numReplies = 0
|
||||
@ -154,7 +149,7 @@ ThreadHiding =
|
||||
$.add thread.stub, a
|
||||
if Conf['Menu']
|
||||
$.add thread.stub, [$.tn(' '), Menu.makeButton OP]
|
||||
$.before threadRoot, thread.stub
|
||||
$.prepend threadRoot, thread.stub
|
||||
|
||||
show: (thread) ->
|
||||
if thread.stub
|
||||
@ -162,4 +157,4 @@ ThreadHiding =
|
||||
delete thread.stub
|
||||
threadRoot = thread.OP.nodes.root.parentNode
|
||||
threadRoot.nextElementSibling.hidden =
|
||||
threadRoot.hidden = thread.isHidden = false
|
||||
threadRoot.hidden = thread.isHidden = false
|
||||
|
||||
@ -79,6 +79,10 @@ Config =
|
||||
true
|
||||
'Convert text into links where applicable.'
|
||||
]
|
||||
'Allow False Positives': [
|
||||
false
|
||||
'Linkify everything, allowing more false positives but reducing missed links'
|
||||
]
|
||||
'Embedding': [
|
||||
true
|
||||
'Embed supported services.'
|
||||
@ -89,7 +93,7 @@ Config =
|
||||
]
|
||||
'Link Title': [
|
||||
true
|
||||
'Replace the link of a supported site with its actual title. Currently Supported: YouTube, Vimeo, SoundCloud'
|
||||
'Replace the link of a supported site with its actual title. Currently Supported: YouTube, Vimeo, SoundCloud, and Github gists'
|
||||
]
|
||||
|
||||
'Filtering':
|
||||
@ -217,6 +221,10 @@ Config =
|
||||
true
|
||||
'Display reply and image count.'
|
||||
]
|
||||
'Updater and Stats in Header': [
|
||||
true,
|
||||
'Places the thread updater and thread stats in the header instead of floating them.'
|
||||
]
|
||||
'Thread Watcher': [
|
||||
true
|
||||
'Bookmark threads.'
|
||||
@ -277,6 +285,10 @@ Config =
|
||||
true
|
||||
'Decrease the cooldown time by taking into account upload speed. Disable it if it\'s inaccurate for you.'
|
||||
]
|
||||
'Posting Success Notifications': [
|
||||
true
|
||||
'Show notifications on successful post creation or file uploading.'
|
||||
]
|
||||
|
||||
'Quote Links':
|
||||
'Quote Backlinks': [
|
||||
@ -766,6 +778,11 @@ http://iqdb.org/?url=%TURL
|
||||
|
||||
boardnav: '[ toggle-all ] [current-title]'
|
||||
|
||||
QR:
|
||||
'QR.personas': [
|
||||
'#email:"sage";boards:jp;always'
|
||||
].join '\n'
|
||||
|
||||
time: '%m/%d/%y(%a)%H:%M:%S'
|
||||
|
||||
backlink: '>>%id'
|
||||
|
||||
@ -8,6 +8,8 @@ Get =
|
||||
if excerpt.length > 70
|
||||
excerpt = "#{excerpt[...67]}..."
|
||||
"/#{thread.board}/ - #{excerpt}"
|
||||
threadFromRoot: (root) ->
|
||||
g.threads["#{g.BOARD}.#{root.id[1..]}"]
|
||||
postFromRoot: (root) ->
|
||||
link = $ 'a[title="Highlight this post"]', root
|
||||
boardID = link.pathname.split('/')[1]
|
||||
@ -226,4 +228,4 @@ Get =
|
||||
post = new Post Build.post(o, true), thread, board,
|
||||
isArchived: true
|
||||
Main.callbackNodes Post, [post]
|
||||
Get.insert post, root, context
|
||||
Get.insert post, root, context
|
||||
|
||||
@ -135,22 +135,22 @@ Header =
|
||||
for a in as
|
||||
if a.textContent is board
|
||||
a = a.cloneNode true
|
||||
if /-title/.test t
|
||||
a.textContent = a.title
|
||||
else if /-replace/.test t
|
||||
if $.hasClass a, 'current'
|
||||
a.textContent = a.title
|
||||
|
||||
a.textContent = if /-title/.test(t) or /-replace/.test(t) and $.hasClass a, 'current'
|
||||
a.title
|
||||
else if /-full/.test t
|
||||
a.textContent = "/#{board}/ - #{a.title}"
|
||||
else if /-(index|catalog|text)/.test t
|
||||
if m = t.match /-(index|catalog)/
|
||||
a.setAttribute 'data-only', m[1]
|
||||
a.href = "//boards.4chan.org/#{board}/"
|
||||
a.href += 'catalog' if m[1] is 'catalog'
|
||||
if m = t.match /-text:"(.+)"/
|
||||
a.textContent = m[1]
|
||||
else if board is '@'
|
||||
$.addClass a, 'navSmall'
|
||||
"/#{board}/ - #{a.title}"
|
||||
else if m = t.match /-text:"(.+)"/
|
||||
m[1]
|
||||
else
|
||||
a.textContent
|
||||
|
||||
if m = t.match /-(index|catalog)/
|
||||
a.setAttribute 'data-only', m[1]
|
||||
a.href = "//boards.4chan.org/#{board}/"
|
||||
a.href += 'catalog' if m[1] is 'catalog'
|
||||
|
||||
$.addClass a, 'navSmall' if board is '@'
|
||||
return a
|
||||
$.tn t
|
||||
$.add list, nodes
|
||||
|
||||
@ -93,7 +93,7 @@ Main =
|
||||
'Rice': Rice
|
||||
'Banner': Banner
|
||||
'Announcements': GlobalMessage
|
||||
'Redirection': Redirect
|
||||
'Archive Redirection': Redirect
|
||||
'Header': Header
|
||||
'Catalog Links': CatalogLinks
|
||||
'Settings': Settings
|
||||
@ -190,8 +190,19 @@ Main =
|
||||
$.event '4chanXInitFinished'
|
||||
Main.checkUpdate()
|
||||
|
||||
if styleSelector = $.id 'styleSelector'
|
||||
passLink = $.el 'a',
|
||||
textContent: '4chan Pass'
|
||||
href: 'javascript:;'
|
||||
$.on passLink, 'click', ->
|
||||
window.open '//sys.4chan.org/auth',
|
||||
'This will steal your data.'
|
||||
'left=0,top=0,width=500,height=255,toolbar=0,resizable=0'
|
||||
$.before styleSelector.previousSibling, [$.tn '['; passLink, $.tn ']\u00A0\u00A0']
|
||||
|
||||
return
|
||||
|
||||
|
||||
$.event '4chanXInitFinished'
|
||||
Main.checkUpdate()
|
||||
|
||||
@ -280,7 +291,7 @@ Main =
|
||||
$.get items, (items) ->
|
||||
if items.lastupdate > now - freq or items.lastchecked > now - $.DAY
|
||||
return
|
||||
$.ajax '<%= meta.page %><%= meta.buildsPath %>version', onload: ->
|
||||
$.ajax '<%= meta.repo %>raw/<%= meta.mainBranch %>/<%= meta.buildsPath %>version', onload: ->
|
||||
return unless @status is 200
|
||||
version = @response
|
||||
return unless /^\d\.\d+\.\d+$/.test version
|
||||
|
||||
@ -334,10 +334,10 @@ Settings =
|
||||
section.innerHTML = """
|
||||
<%= grunt.file.read('src/General/html/Settings/Sauce.html').replace(/>\s+</g, '><').trim() %>
|
||||
"""
|
||||
sauce = $ 'textarea', section
|
||||
ta = $ 'textarea', section
|
||||
$.get 'sauces', Conf['sauces'], (item) ->
|
||||
sauce.value = item['sauces']
|
||||
$.on sauce, 'change', $.cb.value
|
||||
ta.value = item['sauces']
|
||||
$.on ta, 'change', $.cb.value
|
||||
|
||||
advanced: (section) ->
|
||||
section.innerHTML = """<%= grunt.file.read('src/General/html/Settings/Advanced.html').replace(/>\s+</g, '><').trim() %>"""
|
||||
@ -353,6 +353,12 @@ Settings =
|
||||
'input'
|
||||
$.on input, event, $.cb.value
|
||||
|
||||
# Quick Reply Personas
|
||||
ta = $ '.personafield', section
|
||||
$.get 'QR.personas', Conf['QR.personas'], (item) ->
|
||||
ta.value = item['QR.personas']
|
||||
$.on ta, 'change', $.cb.value
|
||||
|
||||
# Archiver
|
||||
archiver = $ 'select[name=archiver]', section
|
||||
toSelect = Redirect.select g.BOARD.ID
|
||||
@ -985,4 +991,4 @@ Settings =
|
||||
userThemes = item["userThemes"]
|
||||
userThemes[@id] = Themes[@id]
|
||||
$.set 'userThemes', userThemes
|
||||
$.rm @
|
||||
$.rm @
|
||||
|
||||
941
src/General/css/style.css
Normal file
@ -0,0 +1,941 @@
|
||||
/* General */
|
||||
.dialog {
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, .15);
|
||||
border: 1px solid;
|
||||
display: block;
|
||||
padding: 0;
|
||||
}
|
||||
.captcha-img,
|
||||
.field {
|
||||
background-color: #FFF;
|
||||
border: 1px solid #CCC;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
color: #333;
|
||||
font: 13px sans-serif;
|
||||
outline: none;
|
||||
transition: color .25s, border-color .25s;
|
||||
transition: color .25s, border-color .25s;
|
||||
}
|
||||
.field::-moz-placeholder,
|
||||
.field:hover::-moz-placeholder {
|
||||
color: #AAA !important;
|
||||
font-size: 13px !important;
|
||||
opacity: 1.0 !important;
|
||||
}
|
||||
.captch-img:hover,
|
||||
.field:hover {
|
||||
border-color: #999;
|
||||
}
|
||||
.field:hover, .field:focus {
|
||||
color: #000;
|
||||
}
|
||||
.field[disabled] {
|
||||
background-color: #F2F2F2;
|
||||
color: #888;
|
||||
}
|
||||
.move {
|
||||
cursor: move;
|
||||
overflow: hidden;
|
||||
}
|
||||
label, .favicon {
|
||||
cursor: pointer;
|
||||
}
|
||||
a[href="javascript:;"] {
|
||||
text-decoration: none;
|
||||
}
|
||||
.warning {
|
||||
color: red;
|
||||
}
|
||||
#boardNavDesktop {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* 4chan style fixes */
|
||||
.opContainer, .op {
|
||||
display: block !important;
|
||||
overflow: visible !important;
|
||||
}
|
||||
[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* fixed, z-index */
|
||||
#overlay,
|
||||
#fourchanx-settings,
|
||||
#qp, #ihover,
|
||||
#navlinks, .fixed #header-bar,
|
||||
#watcher,
|
||||
:root.float #updater,
|
||||
:root.float #thread-stats,
|
||||
#qr {
|
||||
position: fixed;
|
||||
}
|
||||
#fourchanx-settings {
|
||||
z-index: 999;
|
||||
}
|
||||
#overlay {
|
||||
z-index: 900;
|
||||
}
|
||||
#notifications {
|
||||
z-index: 70;
|
||||
}
|
||||
#qp, #ihover {
|
||||
z-index: 60;
|
||||
}
|
||||
#menu {
|
||||
z-index: 50;
|
||||
}
|
||||
#navlinks, #updater, #thread-stats {
|
||||
z-index: 40;
|
||||
}
|
||||
.fixed #header-bar.autohide {
|
||||
z-index: 35;
|
||||
}
|
||||
#qr {
|
||||
z-index: 30;
|
||||
}
|
||||
#watcher {
|
||||
z-index: 20;
|
||||
}
|
||||
.fixed #header-bar {
|
||||
z-index: 10;
|
||||
}
|
||||
/* Header */
|
||||
.fixed.top body {
|
||||
padding-top: 2em;
|
||||
}
|
||||
.fixed.bottom body {
|
||||
padding-bottom: 2em;
|
||||
}
|
||||
.fixed #header-bar {
|
||||
right: 0;
|
||||
left: 0;
|
||||
padding: 3px 4px 4px;
|
||||
}
|
||||
.fixed.top #header-bar {
|
||||
top: 0;
|
||||
}
|
||||
.fixed.bottom #header-bar {
|
||||
bottom: 0;
|
||||
}
|
||||
#header-bar {
|
||||
border-width: 0;
|
||||
transition: all .1s .05s ease-in-out;
|
||||
}
|
||||
.fixed.top #header-bar {
|
||||
border-bottom-width: 1px;
|
||||
}
|
||||
.fixed.bottom #header-bar {
|
||||
box-shadow: 0 -1px 2px rgba(0, 0, 0, .15);
|
||||
border-top-width: 1px;
|
||||
}
|
||||
.fixed.bottom #header-bar .menu-button i {
|
||||
border-top: none;
|
||||
border-bottom: 6px solid;
|
||||
}
|
||||
#board-list {
|
||||
text-align: center;
|
||||
}
|
||||
.fixed #header-bar.autohide:not(:hover) {
|
||||
box-shadow: none;
|
||||
transition: all .8s .6s cubic-bezier(.55, .055, .675, .19);
|
||||
}
|
||||
.fixed.top #header-bar.autohide:not(:hover) {
|
||||
margin-bottom: -1em;
|
||||
-webkit-transform: translateY(-100%);
|
||||
transform: translateY(-100%);
|
||||
}
|
||||
.fixed.bottom #header-bar.autohide:not(:hover) {
|
||||
-webkit-transform: translateY(100%);
|
||||
transform: translateY(100%);
|
||||
}
|
||||
#scroll-marker {
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 10px;
|
||||
position: absolute;
|
||||
}
|
||||
#header-bar #scroll-marker {
|
||||
display: none;
|
||||
}
|
||||
.fixed #header-bar #scroll-marker {
|
||||
display: block;
|
||||
}
|
||||
.fixed.top #header-bar #scroll-marker {
|
||||
top: 100%;
|
||||
}
|
||||
.fixed.bottom #header-bar #scroll-marker {
|
||||
bottom: 100%;
|
||||
}
|
||||
#header-bar a:not(.entry):not(.close) {
|
||||
text-decoration: none;
|
||||
padding: 1px;
|
||||
}
|
||||
#header-bar input {
|
||||
margin: 0;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
#shortcuts:empty {
|
||||
display: none;
|
||||
}
|
||||
.brackets-wrap::before {
|
||||
content: "\\00a0[";
|
||||
}
|
||||
.brackets-wrap::after {
|
||||
content: "]\\00a0";
|
||||
}
|
||||
.disabled,
|
||||
.expand-all-shortcut {
|
||||
opacity: .45;
|
||||
}
|
||||
#shortcuts {
|
||||
float: right;
|
||||
}
|
||||
#navbotright,
|
||||
#navtopright {
|
||||
display: none;
|
||||
}
|
||||
#toggleMsgBtn {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Notifications */
|
||||
#notifications {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
height: 0;
|
||||
text-align: center;
|
||||
right: 0;
|
||||
left: 0;
|
||||
transition: all .8s .6s cubic-bezier(.55, .055, .675, .19);
|
||||
}
|
||||
.fixed.top #header-bar #notifications {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
}
|
||||
.notification {
|
||||
color: #FFF;
|
||||
font-weight: 700;
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, .5);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, .15);
|
||||
border-radius: 2px;
|
||||
margin: 1px auto;
|
||||
width: 500px;
|
||||
max-width: 100%;
|
||||
position: relative;
|
||||
transition: all .25s ease-in-out;
|
||||
}
|
||||
.notification.error {
|
||||
background-color: hsla(0, 100%, 38%, .9);
|
||||
}
|
||||
.notification.warning {
|
||||
background-color: hsla(36, 100%, 38%, .9);
|
||||
}
|
||||
.notification.info {
|
||||
background-color: hsla(200, 100%, 38%, .9);
|
||||
}
|
||||
.notification.success {
|
||||
background-color: hsla(104, 100%, 38%, .9);
|
||||
}
|
||||
.notification a {
|
||||
color: white;
|
||||
}
|
||||
.notification > .close {
|
||||
padding: 6px;
|
||||
top: 0;
|
||||
right: 5px;
|
||||
position: absolute;
|
||||
}
|
||||
.message {
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
padding: 6px 20px;
|
||||
max-height: 200px;
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/* Settings */
|
||||
:root.fourchan-x body {
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
#overlay {
|
||||
background-color: rgba(0, 0, 0, .5);
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
#fourchanx-settings {
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0 0 15px rgba(0, 0, 0, .15);
|
||||
height: 600px;
|
||||
min-height: 0;
|
||||
max-height: 100%;
|
||||
width: 900px;
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
margin: auto;
|
||||
padding: 3px;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
-moz-transform: translate(-50%, -50%);
|
||||
-webkit-transform: translate(-50%, -50%);
|
||||
-o-transform: translate(-50%, -50%);
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
#fourchanx-settings > nav {
|
||||
padding: 2px 2px 0;
|
||||
height: 15px;
|
||||
}
|
||||
#fourchanx-settings > nav a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
#fourchanx-settings > nav a.close {
|
||||
text-decoration: none;
|
||||
padding: 2px;
|
||||
}
|
||||
.section-container {
|
||||
overflow: auto;
|
||||
position: absolute;
|
||||
top: 2.1em;
|
||||
right: 5px;
|
||||
bottom: 5px;
|
||||
left: 5px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
.sections-list {
|
||||
padding: 0 3px;
|
||||
float: left;
|
||||
}
|
||||
.credits {
|
||||
float: right;
|
||||
}
|
||||
.tab-selected {
|
||||
font-weight: 700;
|
||||
}
|
||||
.section-sauce ul,
|
||||
.section-advanced ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
}
|
||||
.section-sauce ul {
|
||||
padding: 8px;
|
||||
}
|
||||
.section-advanced ul {
|
||||
padding: 0px;
|
||||
}
|
||||
.section-sauce li,
|
||||
.section-advanced li {
|
||||
padding-left: 4px;
|
||||
}
|
||||
.section-main label {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.section-filter ul {
|
||||
padding: 0;
|
||||
}
|
||||
.section-filter li {
|
||||
margin: 10px 40px;
|
||||
}
|
||||
.section-filter textarea {
|
||||
height: 500px;
|
||||
}
|
||||
.section-sauce textarea {
|
||||
height: 350px;
|
||||
}
|
||||
.section-advanced .field[name="boardnav"] {
|
||||
width: 100%;
|
||||
}
|
||||
.section-advanced textarea {
|
||||
height: 150px;
|
||||
}
|
||||
#fourchanx-settings fieldset {
|
||||
border: 1px solid;
|
||||
border-radius: 3px;
|
||||
}
|
||||
#fourchanx-settings legend {
|
||||
font-weight: 700;
|
||||
}
|
||||
#fourchanx-settings textarea {
|
||||
font-family: monospace;
|
||||
min-width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
#fourchanx-settings code {
|
||||
color: #000;
|
||||
background-color: #FFF;
|
||||
padding: 0 2px;
|
||||
}
|
||||
.unscroll {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Announcement Hiding */
|
||||
:root.hide-announcement #globalMessage {
|
||||
display: none;
|
||||
}
|
||||
a.hide-announcement {
|
||||
float: left;
|
||||
}
|
||||
|
||||
/* Unread */
|
||||
#unread-line {
|
||||
margin: 0;
|
||||
border-color: rgb(255,0,0);
|
||||
}
|
||||
|
||||
/* Thread Updater */
|
||||
#updater {
|
||||
background: none;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
#updater > .move {
|
||||
padding: 5px 3px 0px;
|
||||
margin-bottom: -3px;
|
||||
}
|
||||
#updater > div:last-child {
|
||||
text-align: center;
|
||||
}
|
||||
#updater input[type=number] {
|
||||
width: 4em;
|
||||
}
|
||||
:root.float #updater {
|
||||
padding: 0px 3px;
|
||||
}
|
||||
.new {
|
||||
color: limegreen;
|
||||
}
|
||||
#update-status.warning, #update-status.new {
|
||||
margin-right: 5px;
|
||||
}
|
||||
#update-timer {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Thread Watcher */
|
||||
#watcher {
|
||||
padding-bottom: 3px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
#watcher:not(:hover) {
|
||||
max-height: 220px;
|
||||
}
|
||||
#watcher > .move {
|
||||
padding-top: 3px;
|
||||
}
|
||||
#watcher > div {
|
||||
max-width: 200px;
|
||||
overflow: hidden;
|
||||
padding-left: 3px;
|
||||
padding-right: 3px;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
#watcher a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* Thread Stats */
|
||||
#thread-stats {
|
||||
background: none;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
:root.float #post-count, :root.float #file-count {
|
||||
pointer-events: none;
|
||||
}
|
||||
:root.float #thread-stats {
|
||||
padding: 0px 3px;
|
||||
}
|
||||
|
||||
/* Quote */
|
||||
.deadlink {
|
||||
text-decoration: none !important;
|
||||
}
|
||||
.backlink.deadlink:not(.forwardlink), .quotelink.deadlink:not(.forwardlink) {
|
||||
text-decoration: underline !important;
|
||||
}
|
||||
.inlined {
|
||||
opacity: .5;
|
||||
}
|
||||
#qp input, .forwarded {
|
||||
display: none;
|
||||
}
|
||||
.quotelink.forwardlink,
|
||||
.backlink.forwardlink {
|
||||
text-decoration: none;
|
||||
border-bottom: 1px dashed;
|
||||
}
|
||||
.filtered {
|
||||
text-decoration: underline line-through;
|
||||
}
|
||||
.inline {
|
||||
border: 1px solid;
|
||||
display: table;
|
||||
margin: 2px 0;
|
||||
}
|
||||
.inline .post {
|
||||
border: 0 !important;
|
||||
background-color: transparent !important;
|
||||
display: table !important;
|
||||
margin: 0 !important;
|
||||
padding: 1px 2px !important;
|
||||
}
|
||||
#qp > .opContainer::after {
|
||||
content: '';
|
||||
clear: both;
|
||||
display: table;
|
||||
}
|
||||
#qp .post {
|
||||
border: none;
|
||||
margin: 0;
|
||||
padding: 2px 2px 5px;
|
||||
}
|
||||
#qp img {
|
||||
max-height: 300px;
|
||||
max-width: 500px;
|
||||
max-height: 80vh;
|
||||
max-width: 50vw;
|
||||
}
|
||||
.qphl {
|
||||
outline: 2px solid rgba(216, 94, 49, .7);
|
||||
}
|
||||
.highlight-own .yourPost>.reply {
|
||||
border-left: 2px solid rgba(221,0,0,.5);
|
||||
}
|
||||
/* Quote Threading */
|
||||
.threadContainer {
|
||||
margin-left: 20px;
|
||||
border-left: 1px solid rgba(128,128,128,.3);
|
||||
}
|
||||
.threadOP {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
/* File */
|
||||
.fileText:hover .fntrunc,
|
||||
.fileText:not(:hover) .fnfull,
|
||||
.expanded-image > .post > .file > .fileThumb > img[data-md5],
|
||||
:not(.expanded-image) > .post > .file > .fileThumb > .full-image {
|
||||
display: none;
|
||||
}
|
||||
.expanding {
|
||||
opacity: .5;
|
||||
}
|
||||
.expanded-image {
|
||||
clear: both;
|
||||
}
|
||||
.expanded-image > .op > .file::after {
|
||||
content: '';
|
||||
clear: both;
|
||||
display: table;
|
||||
}
|
||||
:root.fit-width .full-image {
|
||||
max-width: 100%;
|
||||
}
|
||||
:root.gecko.fit-width .full-image,
|
||||
:root.presto.fit-width .full-image {
|
||||
width: 100%;
|
||||
}
|
||||
#ihover {
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
max-height: 100%;
|
||||
max-width: 75%;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
.fappeTyme .thread > .noFile,
|
||||
.fappeTyme .threadContainer > .noFile {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Index/Reply Navigation */
|
||||
#navlinks {
|
||||
font-size: 16px;
|
||||
top: 25px;
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
/* Filter */
|
||||
.opContainer.filter-highlight {
|
||||
box-shadow: inset 5px 0 rgba(255, 0, 0, .5);
|
||||
}
|
||||
.filter-highlight > .reply {
|
||||
box-shadow: -5px 0 rgba(255, 0, 0, .5);
|
||||
}
|
||||
|
||||
/* Thread & Reply Hiding */
|
||||
.hide-thread-button,
|
||||
.hide-reply-button {
|
||||
float: left;
|
||||
margin-right: 2px;
|
||||
}
|
||||
.stub ~ * {
|
||||
display: none !important;
|
||||
}
|
||||
.stub input {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/* QR */
|
||||
:root.hide-original-post-form #postForm,
|
||||
:root.hide-original-post-form .postingMode,
|
||||
:root.hide-original-post-form #togglePostForm,
|
||||
#qr.autohide:not(.has-focus):not(:hover) > form,
|
||||
.postingMode ~ #qr select,
|
||||
#file-n-submit:not(.has-file) #qr-filerm {
|
||||
display: none;
|
||||
}
|
||||
#qr select, #dump-button, .remove, .captcha-img {
|
||||
cursor: pointer;
|
||||
}
|
||||
#qr {
|
||||
z-index: 20;
|
||||
position: fixed;
|
||||
padding: 1px;
|
||||
border: 1px solid transparent;
|
||||
min-width: 248px;
|
||||
border-radius: 3px 3px 0 0;
|
||||
}
|
||||
#qrtab {
|
||||
border-radius: 3px 3px 0 0;
|
||||
}
|
||||
#qrtab {
|
||||
margin-bottom: 1px;
|
||||
}
|
||||
#qr .close {
|
||||
float: right;
|
||||
padding: 0 3px;
|
||||
}
|
||||
#qr .warning {
|
||||
min-height: 1.6em;
|
||||
vertical-align: middle;
|
||||
padding: 0 1px;
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
}
|
||||
.qr-link {
|
||||
text-align: center;
|
||||
}
|
||||
.persona {
|
||||
width: 248px;
|
||||
max-width: 100%;
|
||||
min-width: 100%;
|
||||
}
|
||||
#dump-button {
|
||||
background: linear-gradient(#EEE, #CCC);
|
||||
border: 1px solid #CCC;
|
||||
width: 10%;
|
||||
margin: 0;
|
||||
font: 13px sans-serif;
|
||||
padding: 1px 0px 2px;
|
||||
}
|
||||
.persona .field:not(#dump) {
|
||||
width: 95px;
|
||||
min-width: 30%;
|
||||
max-width: 30%;
|
||||
}
|
||||
#qr textarea.field {
|
||||
height: 14.8em;
|
||||
min-height: 9em;
|
||||
}
|
||||
#qr.has-captcha textarea.field {
|
||||
height: 9em;
|
||||
}
|
||||
input.field.tripped:not(:hover):not(:focus) {
|
||||
color: transparent !important; text-shadow: none !important;
|
||||
}
|
||||
#qr textarea {
|
||||
resize: both;
|
||||
}
|
||||
.captcha-img {
|
||||
margin: 0px;
|
||||
text-align: center;
|
||||
background-image: #fff;
|
||||
font-size: 0px;
|
||||
min-height: 59px;
|
||||
min-width: 302px;
|
||||
}
|
||||
.captcha-input {
|
||||
width: 100%;
|
||||
margin: 1px 0 0;
|
||||
}
|
||||
.field {
|
||||
-moz-box-sizing: border-box;
|
||||
margin: 0px;
|
||||
padding: 2px 4px 3px;
|
||||
}
|
||||
#qr textarea {
|
||||
min-width: 100%;
|
||||
}
|
||||
#qr [type='submit'] {
|
||||
width: 25%;
|
||||
vertical-align: top;
|
||||
}
|
||||
/* Fake File Input */
|
||||
#qr-filename,
|
||||
.has-file #qr-no-file {
|
||||
display: none;
|
||||
}
|
||||
#qr-no-file,
|
||||
.has-file #qr-filename {
|
||||
display: block;
|
||||
padding: 0px 4px;
|
||||
margin-bottom: 2px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
#qr-no-file {
|
||||
color: #AAA;
|
||||
}
|
||||
#qr-filename-container {
|
||||
-moz-box-sizing: border-box;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
width: 100px;
|
||||
min-width: 74.6%;
|
||||
max-width: 74.6%;
|
||||
margin-right: 0.4%;
|
||||
margin-top: 1px;
|
||||
overflow: hidden;
|
||||
padding: 2px 1px 0;
|
||||
height: 22px;
|
||||
}
|
||||
#qr-filename-container:hover {
|
||||
cursor: text;
|
||||
}
|
||||
#qr-filerm {
|
||||
position: relative;
|
||||
right: 14px;
|
||||
bottom: 6px;
|
||||
margin-right: -8px;
|
||||
z-index: 2;
|
||||
}
|
||||
#file-n-submit {
|
||||
height: 23px;
|
||||
}
|
||||
#qr input[type=file] {
|
||||
display: none;
|
||||
}
|
||||
/* Thread Select / Spoiler Label */
|
||||
#qr select {
|
||||
float: right;
|
||||
}
|
||||
/* Dumping UI */
|
||||
.dump #dump-list-container {
|
||||
display: block;
|
||||
}
|
||||
#dump-list-container {
|
||||
display: none;
|
||||
position: relative;
|
||||
overflow-y: hidden;
|
||||
margin-top: 1px;
|
||||
}
|
||||
#dump-list {
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
white-space: pre;
|
||||
width: 248px;
|
||||
max-width: 100%;
|
||||
min-width: 100%;
|
||||
}
|
||||
#dump-list:hover {
|
||||
overflow-x: auto;
|
||||
}
|
||||
.qr-preview {
|
||||
-moz-box-sizing: border-box;
|
||||
counter-increment: thumbnails;
|
||||
cursor: move;
|
||||
display: inline-block;
|
||||
height: 90px;
|
||||
width: 90px;
|
||||
padding: 2px;
|
||||
opacity: .5;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
text-shadow: 0 1px 1px #000;
|
||||
-moz-transition: opacity .25s ease-in-out;
|
||||
vertical-align: top;
|
||||
}
|
||||
.qr-preview:hover,
|
||||
.qr-preview:focus {
|
||||
opacity: .9;
|
||||
}
|
||||
.qr-preview::before {
|
||||
content: counter(thumbnails);
|
||||
color: #fff;
|
||||
position: absolute;
|
||||
top: 3px;
|
||||
right: 3px;
|
||||
text-shadow: 0 0 3px #000, 0 0 8px #000;
|
||||
}
|
||||
.qr-preview#selected {
|
||||
opacity: 1;
|
||||
}
|
||||
.qr-preview.drag {
|
||||
box-shadow: 0 0 10px rgba(0,0,0,.5);
|
||||
}
|
||||
.qr-preview.over {
|
||||
border-color: #fff;
|
||||
}
|
||||
.qr-preview > span {
|
||||
color: #fff;
|
||||
}
|
||||
.remove {
|
||||
background: none;
|
||||
color: #e00;
|
||||
font-weight: 700;
|
||||
padding: 3px;
|
||||
}
|
||||
a:only-of-type > .remove {
|
||||
display: none;
|
||||
}
|
||||
.remove:hover::after {
|
||||
content: " Remove";
|
||||
}
|
||||
.qr-preview > label {
|
||||
background: rgba(0,0,0,.5);
|
||||
color: #fff;
|
||||
right: 0; bottom: 0; left: 0;
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
}
|
||||
.qr-preview > label > input {
|
||||
margin: 0;
|
||||
}
|
||||
#add-post {
|
||||
cursor: pointer;
|
||||
font-size: 2em;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 10px;
|
||||
-moz-transform: translateY(-50%);
|
||||
}
|
||||
.textarea {
|
||||
position: relative;
|
||||
}
|
||||
#char-count {
|
||||
color: #000;
|
||||
background: hsla(0, 0%, 100%, .5);
|
||||
font-size: 8pt;
|
||||
position: absolute;
|
||||
bottom: 1px;
|
||||
right: 1px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Menu */
|
||||
.menu-button {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
}
|
||||
.menu-button i {
|
||||
border-top: 6px solid;
|
||||
border-right: 4px solid transparent;
|
||||
border-left: 4px solid transparent;
|
||||
display: inline-block;
|
||||
margin: 2px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
#menu {
|
||||
position: fixed;
|
||||
outline: none;
|
||||
}
|
||||
.entry {
|
||||
border-bottom: 1px solid rgba(0,0,0,.25);
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
outline: none;
|
||||
padding: 3px 7px;
|
||||
position: relative;
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.left>.entry.has-submenu {
|
||||
padding-right: 17px !important;
|
||||
}
|
||||
.entry:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
.has-submenu::after {
|
||||
content: "";
|
||||
border-left: .5em solid;
|
||||
border-top: .3em solid transparent;
|
||||
border-bottom: .3em solid transparent;
|
||||
display: inline-block;
|
||||
margin: .3em;
|
||||
position: absolute;
|
||||
right: 3px;
|
||||
}
|
||||
.left .has-submenu::after {
|
||||
border-left: 0;
|
||||
border-right: .5em solid;
|
||||
}
|
||||
.submenu {
|
||||
display: none;
|
||||
position: absolute;
|
||||
left: 100%;
|
||||
top: -1px;
|
||||
}
|
||||
.focused .submenu {
|
||||
display: block;
|
||||
}
|
||||
.imp-exp-result {
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
margin: auto;
|
||||
right: 0px;
|
||||
left: 0px;
|
||||
width: 200px;
|
||||
}
|
||||
.export, .import {
|
||||
cursor: pointer;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
/* Link Title Favicons */
|
||||
.linkify.YouTube {
|
||||
background: transparent url('data:image/png;base64,<%= grunt.file.read("src/General/img/links/youtube.png", {encoding: "base64"}) %>') center left no-repeat!important;
|
||||
padding-left: 18px;
|
||||
}
|
||||
.linkify.Vimeo {
|
||||
background: transparent url('data:image/png;base64,<%= grunt.file.read("src/General/img/links/vimeo.png", {encoding: "base64"}) %>') center left no-repeat!important;
|
||||
padding-left: 18px;
|
||||
}
|
||||
.linkify.SoundCloud {
|
||||
background: transparent url('data:image/png;base64,<%= grunt.file.read("src/General/img/links/soundcloud.png", {encoding: "base64"}) %>') center left no-repeat!important;
|
||||
padding-left: 18px;
|
||||
}
|
||||
.linkify.audio {
|
||||
background: transparent url('data:image/png;base64,<%= grunt.file.read("src/General/img/links/audio.png", {encoding: "base64"}) %>') center left no-repeat!important;
|
||||
padding-left: 18px;
|
||||
}
|
||||
.linkify.LiveLeak {
|
||||
background: transparent url('data:image/png;base64,<%= grunt.file.read("src/General/img/links/liveleak.png", {encoding: "base64"}) %>') center left no-repeat!important;
|
||||
padding-left: 18px;
|
||||
}
|
||||
.linkify.Vocaroo {
|
||||
background: transparent url('data:image/png;base64,<%= grunt.file.read("src/General/img/links/vocaroo.png", {encoding: "base64"}) %>') center left no-repeat!important;
|
||||
padding-left: 18px;
|
||||
}
|
||||
.linkify.pastebin {
|
||||
background: transparent url('data:image/png;base64,<%= grunt.file.read("src/General/img/links/pastebin.png", {encoding: "base64"}) %>') center left no-repeat!important;
|
||||
padding-left: 18px;
|
||||
}
|
||||
.linkify.gist {
|
||||
background: transparent url('data:image/png;base64,<%= grunt.file.read("src/General/img/links/gist.png", {encoding: "base64"}) %>') center left no-repeat!important;
|
||||
padding-left: 18px;
|
||||
}
|
||||
.linkify.image {
|
||||
background: transparent url('data:image/png;base64,<%= grunt.file.read("src/General/img/links/image.png", {encoding: "base64"}) %>') center left no-repeat!important;
|
||||
padding-left: 18px;
|
||||
}
|
||||
.linkify.InstallGentoo {
|
||||
background: transparent url('data:image/png;base64,<%= grunt.file.read("src/General/img/links/installgentoo.png", {encoding: "base64"}) %>') center left no-repeat!important;
|
||||
padding-left: 18px;
|
||||
}
|
||||
@ -5,12 +5,12 @@
|
||||
<form>
|
||||
<div class=persona>
|
||||
<input id=dump-button type=button title='Dump list' value=+ tabindex=0>
|
||||
<input name=name data-name=name title=Name placeholder=Name class=field size=1 tabindex=10>
|
||||
<input name=email data-name=email title=E-mail placeholder=E-mail class=field size=1 tabindex=20>
|
||||
<input name=sub data-name=sub title=Subject placeholder=Subject class=field size=1 tabindex=30>
|
||||
<input name=name data-name=name list="list-name" placeholder=Name class=field size=1 tabindex=10>
|
||||
<input name=email data-name=email list="list-email" placeholder=E-mail class=field size=1 tabindex=20>
|
||||
<input name=sub data-name=sub list="list-sub" placeholder=Subject class=field size=1 tabindex=30>
|
||||
</div>
|
||||
<div class=textarea>
|
||||
<textarea data-name=com title=Comment placeholder=Comment class=field tabindex=40></textarea>
|
||||
<textarea data-name=com placeholder=Comment class=field tabindex=40></textarea>
|
||||
<span id=char-count></span>
|
||||
</div>
|
||||
<div id=dump-list-container>
|
||||
@ -35,3 +35,6 @@
|
||||
<input type=checkbox id=qr-file-spoiler title='Spoiler image' tabindex=90>Spoiler?
|
||||
</label>
|
||||
</form>
|
||||
<datalist id="list-name"></datalist>
|
||||
<datalist id="list-email"></datalist>
|
||||
<datalist id="list-sub"></datalist>
|
||||
@ -54,6 +54,23 @@
|
||||
<div>Resolution: <code>%r</code> (Displays 'PDF' for PDF files)</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend>Quick Reply Personas <span class="warning" #{if Conf['Quick Reply'] then 'hidden' else ''}>is disabled.</span></legend>
|
||||
<textarea class=personafield name="QR.personas" class="field" spellcheck="false"></textarea>
|
||||
<p>
|
||||
One item per line.<br>
|
||||
Items will be added in the relevant input's auto-completion list.<br>
|
||||
Password items will always be used, since there is no password input.<br>
|
||||
Lines starting with a <code>#</code> will be ignored.
|
||||
</p>
|
||||
<ul>You can use these settings with each item, separate them with semicolons:
|
||||
<li>Possible items are: <code>name</code>, <code>email</code>, <code>subject</code> and <code>password</code>.</li>
|
||||
<li>Wrap values of items with quotes, like this: <code>email:"sage"</code>.</li>
|
||||
<li>Force values as defaults with the <code>always</code> keyword, for example: <code>email:"sage";always</code>.</li>
|
||||
<li>Select specific boards for an item, separated with commas, for example: <code>email:"sage";boards:jp;always</code>.</li>
|
||||
</ul>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend>Unread Favicon <span class=warning #{if Conf['Unread Favicon'] then 'hidden' else ''}>is disabled.</span></legend>
|
||||
<div>
|
||||
|
||||
BIN
src/General/img/links/audio.png
Normal file
|
After Width: | Height: | Size: 700 B |
BIN
src/General/img/links/gist.png
Normal file
|
After Width: | Height: | Size: 683 B |
BIN
src/General/img/links/image.png
Normal file
|
After Width: | Height: | Size: 863 B |
BIN
src/General/img/links/installgentoo.png
Normal file
|
After Width: | Height: | Size: 730 B |
BIN
src/General/img/links/liveleak.png
Normal file
|
After Width: | Height: | Size: 952 B |
BIN
src/General/img/links/pastebin.png
Normal file
|
After Width: | Height: | Size: 871 B |
BIN
src/General/img/links/soundcloud.png
Normal file
|
After Width: | Height: | Size: 491 B |
BIN
src/General/img/links/vimeo.png
Normal file
|
After Width: | Height: | Size: 435 B |
BIN
src/General/img/links/vocaroo.png
Normal file
|
After Width: | Height: | Size: 928 B |
BIN
src/General/img/links/youtube.png
Normal file
|
After Width: | Height: | Size: 347 B |
@ -16,14 +16,14 @@ Array::contains = (object) ->
|
||||
Array::indexOf = (object) ->
|
||||
i = @length
|
||||
while i--
|
||||
break if @[i] is object
|
||||
return i if @[i] is object
|
||||
return i
|
||||
|
||||
Array::pushArrays = ->
|
||||
args = arguments
|
||||
for arg in args
|
||||
@push.apply @, arg
|
||||
return
|
||||
return @
|
||||
|
||||
Array::remove = (object) ->
|
||||
if (index = @indexOf object) > -1
|
||||
@ -133,7 +133,8 @@ $.x = (path, root) ->
|
||||
|
||||
$.X = (path, root) ->
|
||||
root or= d.body
|
||||
d.evaluate path, root, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null
|
||||
# XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE === 6
|
||||
d.evaluate path, root, null, 6, null
|
||||
|
||||
$.addClass = (el, className) ->
|
||||
el.classList.add className
|
||||
@ -148,7 +149,7 @@ $.hasClass = (el, className) ->
|
||||
el.classList.contains className
|
||||
|
||||
$.rm = do ->
|
||||
if 'remove' of Element.prototype
|
||||
if 'remove' of Element::
|
||||
(el) -> el.remove()
|
||||
else
|
||||
(el) -> el.parentNode?.removeChild el
|
||||
@ -373,7 +374,7 @@ $.set = do ->
|
||||
items = {}
|
||||
localItems = {}
|
||||
catch err
|
||||
c.error err
|
||||
c.error err.stack
|
||||
|
||||
(key, val) ->
|
||||
if typeof key is 'string'
|
||||
|
||||
1
src/General/meta/version.js
Normal file
@ -0,0 +1 @@
|
||||
<%= version %>
|
||||
@ -2,6 +2,26 @@ Linkify =
|
||||
init: ->
|
||||
return if g.VIEW is 'catalog' or not Conf['Linkify']
|
||||
|
||||
@regString = if Conf['Allow False Positives']
|
||||
///(
|
||||
\b(
|
||||
[a-z]+://
|
||||
|
|
||||
[a-z]{3,}\.[-a-z0-9]+\.[a-z]+
|
||||
|
|
||||
[-a-z0-9]+\.[a-z]
|
||||
|
|
||||
[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+
|
||||
|
|
||||
[a-z]{3,}:[a-z0-9?]
|
||||
|
|
||||
[a-z0-9._%+-:]+@[a-z0-9.-]+\.[a-z0-9]
|
||||
)
|
||||
[^\s'"]+
|
||||
)///gi
|
||||
else
|
||||
/(((magnet|mailto)\:|(www\.)|(news|(ht|f)tp(s?))\:\/\/){1}\S+)/gi
|
||||
|
||||
if Conf['Comment Expansion']
|
||||
ExpandComment.callbacks.push @node
|
||||
|
||||
@ -9,23 +29,6 @@ Linkify =
|
||||
name: 'Linkify'
|
||||
cb: @node
|
||||
|
||||
regString: ///(
|
||||
\b(
|
||||
[a-z]+://
|
||||
|
|
||||
[a-z]{3,}\.[-a-z0-9]+\.[a-z]+
|
||||
|
|
||||
[-a-z0-9]+\.[a-z]
|
||||
|
|
||||
[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+
|
||||
|
|
||||
[a-z]{3,}:[a-z0-9?]
|
||||
|
|
||||
[a-z0-9._%+-:]+@[a-z0-9.-]+\.[a-z0-9]
|
||||
)
|
||||
[^\s'"]+
|
||||
)///gi
|
||||
|
||||
cypher: $.el 'div'
|
||||
|
||||
node: ->
|
||||
@ -44,6 +47,7 @@ Linkify =
|
||||
data = node.data
|
||||
|
||||
# Test for valid links
|
||||
|
||||
continue unless node.parentNode and Linkify.regString.test data
|
||||
|
||||
Linkify.regString.lastIndex = 0
|
||||
@ -125,21 +129,17 @@ Linkify =
|
||||
textContent: @getAttribute("data-title") or url
|
||||
|
||||
@textContent = '(embed)'
|
||||
$.addClass el, "#{@getAttribute 'data-service'}"
|
||||
|
||||
# Embed
|
||||
else
|
||||
# We create an element to embed
|
||||
el = (type = Linkify.types[@getAttribute("data-service")]).el.call @
|
||||
|
||||
# Set style values.
|
||||
if style = type.style
|
||||
el.style.cssText = style
|
||||
el.style.cssText = if style = type.style
|
||||
style
|
||||
else
|
||||
items =
|
||||
'embedWidth': Config['embedWidth']
|
||||
'embedHeight': Config['embedHeight']
|
||||
$.get items, (items) ->
|
||||
el.style.cssText = "border: 0; width: #{items['embedWidth']}px; height: #{items['embedHeight']}px"
|
||||
"border: 0; width: 640px; height: 390px"
|
||||
|
||||
@textContent = '(unembed)'
|
||||
|
||||
@ -148,10 +148,10 @@ Linkify =
|
||||
|
||||
types:
|
||||
YouTube:
|
||||
regExp: /.*(?:youtu.be\/|youtube.*v=|youtube.*\/embed\/|youtube.*\/v\/|youtube.*videos\/)([^#\&\?]*).*/
|
||||
regExp: /.*(?:youtu.be\/|youtube.*v=|youtube.*\/embed\/|youtube.*\/v\/|youtube.*videos\/)([^#\&\?]*)\??(t\=.*)?/
|
||||
el: ->
|
||||
$.el 'iframe',
|
||||
src: "//www.youtube.com/embed/#{@name}"
|
||||
src: "//www.youtube.com/embed/#{@name}#{if @option then '#' + @option else ''}"
|
||||
title:
|
||||
api: -> "https://gdata.youtube.com/feeds/api/videos/#{@name}?alt=json&fields=title/text(),yt:noembed,app:control/yt:state/@reasonCode"
|
||||
text: -> JSON.parse(@responseText).entry.title.$t
|
||||
@ -186,33 +186,63 @@ Linkify =
|
||||
preload: 'auto'
|
||||
src: @name
|
||||
|
||||
image:
|
||||
regExp: /(http|www).*\.(gif|png|jpg|jpeg|bmp)$/
|
||||
style: 'border: 0; width: auto; height: auto;'
|
||||
el: ->
|
||||
$.el 'div',
|
||||
innerHTML: "<a target=_blank href='#{@getAttribute 'data-originalURL'}'><img src='#{@getAttribute 'data-originalURL'}'></a>"
|
||||
|
||||
SoundCloud:
|
||||
regExp: /.*(?:soundcloud.com\/|snd.sc\/)([^#\&\?]*).*/
|
||||
regExp: /.*(?:soundcloud.com\/|snd.sc\/)([^#\&\?]*).*/
|
||||
style: 'height: auto; width: 500px; display: inline-block;'
|
||||
el: ->
|
||||
div = $.el 'div',
|
||||
className: "soundcloud"
|
||||
name: "soundcloud"
|
||||
name: "soundcloud"
|
||||
$.ajax(
|
||||
"//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=#{@getAttribute 'data-originalURL'}&color=#{Style.colorToHex Themes[Conf['theme']]['Background Color']}"
|
||||
"//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=https://www.soundcloud.com/#{@name}"
|
||||
div: div
|
||||
onloadend: ->
|
||||
@div.innerHTML = JSON.parse(@responseText).html
|
||||
false)
|
||||
div
|
||||
title:
|
||||
api: -> "//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=https://www.soundcloud.com/#{@name}"
|
||||
text: -> JSON.parse(@responseText).title
|
||||
|
||||
pastebin:
|
||||
regExp: /.*(?:pastebin.com\/)([^#\&\?]*).*/
|
||||
regExp: /.*(?:pastebin.com\/(?!u\/))([^#\&\?]*).*/
|
||||
el: ->
|
||||
div = $.el 'iframe',
|
||||
src: "http://pastebin.com/embed_iframe.php?i=#{@name}"
|
||||
|
||||
gist:
|
||||
regExp: /.*(?:gist.github.com.*\/)([^\/][^\/]*)$/
|
||||
el: ->
|
||||
div = $.el 'iframe',
|
||||
# Github doesn't allow embedding straight from the site, so we use an external site to bypass that.
|
||||
src: "http://www.purplegene.com/script?url=https://gist.github.com/#{@name}.js"
|
||||
title:
|
||||
api: -> "https://api.github.com/gists/#{@name}"
|
||||
text: ->
|
||||
response = JSON.parse(@responseText).files
|
||||
return file for file of response when response.hasOwnProperty file
|
||||
|
||||
InstallGentoo:
|
||||
regExp: /.*(?:paste.installgentoo.com\/view\/)([0-9a-z_]+)/
|
||||
el: ->
|
||||
$.el 'iframe',
|
||||
src: "http://paste.installgentoo.com/view/embed/#{@name}"
|
||||
|
||||
embedder: (a) ->
|
||||
return [a] unless Conf['Embedding']
|
||||
return [a] unless Conf['Link Title']
|
||||
titles = {}
|
||||
|
||||
callbacks = ->
|
||||
a.textContent = switch @status
|
||||
when 200, 304
|
||||
title = "[#{embed.getAttribute 'data-service'}] #{service.text.call @}"
|
||||
title = "#{service.text.call @}"
|
||||
embed.setAttribute 'data-title', title
|
||||
titles[embed.name] = [title, Date.now()]
|
||||
$.set 'CachedTitles', titles
|
||||
@ -229,19 +259,23 @@ Linkify =
|
||||
|
||||
embed = $.el 'a',
|
||||
name: (a.name = match[1])
|
||||
option: match[2]
|
||||
className: 'embedder'
|
||||
href: 'javascript:;'
|
||||
textContent: '(embed)'
|
||||
|
||||
embed.setAttribute 'data-service', key
|
||||
embed.setAttribute 'data-originalURL', a.href
|
||||
$.addClass a, "#{embed.getAttribute 'data-service'}"
|
||||
|
||||
$.on embed, 'click', Linkify.toggle
|
||||
|
||||
unless Conf['Embedding']
|
||||
embed.hidden = true
|
||||
|
||||
if Conf['Link Title'] and (service = type.title)
|
||||
$.get 'CachedTitles', {}, (item) ->
|
||||
titles = item['CachedTitles']
|
||||
|
||||
if title = titles[match[1]]
|
||||
a.textContent = title[0]
|
||||
embed.setAttribute 'data-title', title[0]
|
||||
|
||||
@ -46,18 +46,12 @@ DeleteLink =
|
||||
$.off @, 'click', DeleteLink.delete
|
||||
@textContent = "Deleting #{@textContent}..."
|
||||
|
||||
pwd =
|
||||
if m = d.cookie.match /4chan_pass=([^;]+)/
|
||||
decodeURIComponent m[1]
|
||||
else
|
||||
$.id('delPassword').value
|
||||
|
||||
fileOnly = $.hasClass @, 'delete-file'
|
||||
|
||||
form =
|
||||
mode: 'usrdel'
|
||||
onlyimgdel: fileOnly
|
||||
pwd: pwd
|
||||
pwd: QR.persona.getPassword()
|
||||
form[post.ID] = 'delete'
|
||||
|
||||
link = @
|
||||
@ -106,4 +100,4 @@ DeleteLink =
|
||||
delete DeleteLink.cooldown.counting
|
||||
return
|
||||
setTimeout DeleteLink.cooldown.count, 1000, post, seconds - 1, length, node
|
||||
node.textContent = "Delete (#{seconds})"
|
||||
node.textContent = "Delete (#{seconds})"
|
||||
|
||||
@ -12,7 +12,7 @@ ExpandComment =
|
||||
cb: @node
|
||||
|
||||
node: ->
|
||||
if a = $ '.abbr > a', @nodes.comment
|
||||
if a = $ '.abbr > a:not([onclick])', @nodes.comment
|
||||
$.on a, 'click', ExpandComment.cb
|
||||
|
||||
callbacks: []
|
||||
@ -69,4 +69,4 @@ ExpandComment =
|
||||
|
||||
for callback in ExpandComment.callbacks
|
||||
callback.call post
|
||||
return
|
||||
return
|
||||
|
||||
@ -41,7 +41,10 @@ Keybinds =
|
||||
for notification in notifications
|
||||
$('.close', notification).click()
|
||||
else if QR.nodes
|
||||
QR.close()
|
||||
if Conf['Persistent QR']
|
||||
QR.hide()
|
||||
else
|
||||
QR.close()
|
||||
when Conf['Spoiler tags']
|
||||
return if target.nodeName isnt 'TEXTAREA'
|
||||
Keybinds.tags 'spoiler', target
|
||||
|
||||
@ -42,7 +42,9 @@ Nav =
|
||||
else
|
||||
headRect = Header.bar.getBoundingClientRect()
|
||||
topMargin = headRect.top + headRect.height
|
||||
threads = $$ '.thread:not([hidden])'
|
||||
threads = $$('.thread').filter (thread) ->
|
||||
thread = Get.threadFromRoot thread
|
||||
!(thread.isHidden and !thread.stub)
|
||||
for thread, i in threads
|
||||
rect = thread.getBoundingClientRect()
|
||||
if rect.bottom > topMargin # not scrolled past
|
||||
@ -60,4 +62,4 @@ Nav =
|
||||
i += delta
|
||||
|
||||
top = threads[i]?.getBoundingClientRect().top - topMargin
|
||||
window.scrollBy 0, top
|
||||
window.scrollBy 0, top
|
||||
|
||||
@ -1,14 +1,20 @@
|
||||
ThreadStats =
|
||||
init: ->
|
||||
return if g.VIEW isnt 'thread' or !Conf['Thread Stats']
|
||||
@dialog = sc = $.el 'span',
|
||||
innerHTML: "<span id=post-count>0</span> / <span id=file-count>0</span></div>"
|
||||
id: 'thread-stats'
|
||||
|
||||
if Conf['Updater and Stats in Header']
|
||||
@dialog = sc = $.el 'span',
|
||||
innerHTML: "<span id=post-count>0</span> / <span id=file-count>0</span>"
|
||||
id: 'thread-stats'
|
||||
Header.addShortcut sc
|
||||
else
|
||||
@dialog = sc = UI.dialog 'thread-stats', 'bottom: 0px; right: 0px;',
|
||||
"<div class=move><span id=post-count>0</span> / <span id=file-count>0</span></div>"
|
||||
$.ready =>
|
||||
$.add d.body, sc
|
||||
|
||||
@postCountEl = $ '#post-count', sc
|
||||
@fileCountEl = $ '#file-count', sc
|
||||
|
||||
Header.addShortcut sc
|
||||
|
||||
Thread::callbacks.push
|
||||
name: 'Thread Stats'
|
||||
|
||||
@ -3,20 +3,28 @@ ThreadUpdater =
|
||||
return if g.VIEW isnt 'thread' or !Conf['Thread Updater']
|
||||
|
||||
checked = if Conf['Auto Update'] then 'checked' else ''
|
||||
@dialog = sc = $.el 'span',
|
||||
innerHTML: "<span id=update-status></span><span id=update-timer title='Update now'></span>"
|
||||
id: 'updater'
|
||||
|
||||
@timer = $ '#update-timer', sc
|
||||
if Conf['Updater and Stats in Header']
|
||||
@dialog = sc = $.el 'span',
|
||||
innerHTML: "<span id=update-status></span><span id=update-timer title='Update now'></span>"
|
||||
id: 'updater'
|
||||
Header.addShortcut sc
|
||||
else
|
||||
@dialog = sc = UI.dialog 'updater', 'bottom: 0px; left: 0px;',
|
||||
"<div class=move></div><span id=update-status></span><span id=update-timer title='Update now'></span>"
|
||||
$.addClass doc, 'float'
|
||||
$.ready =>
|
||||
$.addClass doc, 'float'
|
||||
$.add d.body, sc
|
||||
|
||||
@checkPostCount = 0
|
||||
|
||||
@timer = $ '#update-timer', sc
|
||||
@status = $ '#update-status', sc
|
||||
|
||||
$.on @timer, 'click', ThreadUpdater.update
|
||||
$.on @status, 'click', ThreadUpdater.update
|
||||
|
||||
@checkPostCount = 0
|
||||
|
||||
Header.addShortcut sc
|
||||
|
||||
subEntries = []
|
||||
for name, conf of Config.updater.checkbox
|
||||
checked = if Conf[name] then 'checked' else ''
|
||||
|
||||
@ -39,6 +39,7 @@ ThreadWatcher =
|
||||
for id, props of watched[board]
|
||||
x = $.el 'a',
|
||||
textContent: '×'
|
||||
className: 'close'
|
||||
href: 'javascript:;'
|
||||
$.on x, 'click', ThreadWatcher.cb.x
|
||||
link = $.el 'a', props
|
||||
|
||||
@ -149,6 +149,73 @@ QR =
|
||||
value
|
||||
status.disabled = disabled or false
|
||||
|
||||
persona:
|
||||
pwd: ''
|
||||
always: {}
|
||||
init: ->
|
||||
QR.persona.getPassword()
|
||||
$.get 'QR.personas', Conf['QR.personas'], ({'QR.personas': personas}) ->
|
||||
types =
|
||||
name: []
|
||||
email: []
|
||||
sub: []
|
||||
for item in personas.split '\n'
|
||||
QR.persona.parseItem item.trim(), types
|
||||
for type, arr of types
|
||||
QR.persona.loadPersonas type, arr
|
||||
return
|
||||
parseItem: (item, types) ->
|
||||
return if item[0] is '#'
|
||||
return unless match = item.match /(name|email|subject|password):"(.*)"/i
|
||||
[match, type, val] = match
|
||||
|
||||
# Don't mix up item settings with val.
|
||||
item = item.replace match, ''
|
||||
|
||||
boards = item.match(/boards:([^;]+)/i)?[1].toLowerCase() or 'global'
|
||||
if boards isnt 'global' and not (g.BOARD.ID in boards.split ',')
|
||||
return
|
||||
|
||||
if type is 'password'
|
||||
QR.persona.pwd = val
|
||||
return
|
||||
|
||||
type = 'sub' if type is 'subject'
|
||||
|
||||
if /always/i.test item
|
||||
QR.persona.always[type] = val
|
||||
|
||||
unless val in types[type]
|
||||
types[type].push val
|
||||
loadPersonas: (type, arr) ->
|
||||
list = $ "#list-#{type}", QR.nodes.el
|
||||
for val in arr
|
||||
$.add list, $.el 'option',
|
||||
textContent: val
|
||||
return
|
||||
getPassword: ->
|
||||
unless QR.persona.pwd
|
||||
QR.persona.pwd = if m = d.cookie.match /4chan_pass=([^;]+)/
|
||||
decodeURIComponent m[1]
|
||||
else if input = $.id 'postPassword'
|
||||
input.value
|
||||
else
|
||||
# If we're in a closed thread, #postPassword isn't available.
|
||||
# And since #delPassword.value is only filled on window.onload
|
||||
# we'd rather use #postPassword when we can.
|
||||
$.id('delPassword').value
|
||||
return QR.persona.pwd
|
||||
get: (cb) ->
|
||||
$.get 'QR.persona', {}, ({'QR.persona': persona}) ->
|
||||
cb persona
|
||||
set: (post) ->
|
||||
$.get 'QR.persona', {}, ({'QR.persona': persona}) ->
|
||||
persona =
|
||||
name: post.name
|
||||
email: if /^sage$/.test post.email then persona.email else post.email
|
||||
sub: if Conf['Remember Subject'] then post.sub else undefined
|
||||
$.set 'QR.persona', persona
|
||||
|
||||
cooldown:
|
||||
init: ->
|
||||
return unless Conf['Cooldown']
|
||||
@ -428,18 +495,27 @@ QR =
|
||||
prev.spoiler
|
||||
else
|
||||
false
|
||||
$.get 'QR.persona', {}, (item) =>
|
||||
persona = item['QR.persona']
|
||||
@name = if prev
|
||||
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 prev and !/^sage$/.test prev.email
|
||||
|
||||
@email = if 'email' of QR.persona.always
|
||||
QR.persona.always.email
|
||||
else if prev and !/^sage$/.test prev.email
|
||||
prev.email
|
||||
else
|
||||
persona.email
|
||||
if Conf['Remember Subject']
|
||||
@sub = if prev then prev.sub else persona.sub
|
||||
|
||||
@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
|
||||
''
|
||||
@load() if QR.selected is @ # load persona
|
||||
@select() if select
|
||||
@unlock()
|
||||
@ -878,8 +954,9 @@ QR =
|
||||
return if e.button isnt 0
|
||||
$.set 'QR Size', @style.cssText
|
||||
<% } %>
|
||||
new QR.post true
|
||||
|
||||
QR.persona.init()
|
||||
new QR.post true
|
||||
QR.status()
|
||||
QR.cooldown.init()
|
||||
QR.captcha.init()
|
||||
@ -970,7 +1047,7 @@ QR =
|
||||
spoiler: post.spoiler
|
||||
textonly: textOnly
|
||||
mode: 'regist'
|
||||
pwd: if m = d.cookie.match(/4chan_pass=([^;]+)/) then decodeURIComponent m[1] else $.id('postPassword').value
|
||||
pwd: QR.persona.pwd
|
||||
recaptcha_challenge_field: challenge
|
||||
recaptcha_response_field: response
|
||||
|
||||
@ -1068,18 +1145,15 @@ QR =
|
||||
QR.status()
|
||||
QR.error err
|
||||
return
|
||||
|
||||
|
||||
h1 = $ 'h1', tmpDoc
|
||||
QR.cleanNotifications()
|
||||
QR.notifications.push new Notification 'success', h1.textContent, 5
|
||||
h1 = $ 'h1', tmpDoc
|
||||
|
||||
if Conf['Posting Success Notifications']
|
||||
QR.notifications.push new Notification 'success', h1.textContent, 5
|
||||
|
||||
$.get 'QR.persona', {}, (item) ->
|
||||
persona = item['QR.persona']
|
||||
persona =
|
||||
name: post.name
|
||||
email: if /^sage$/.test post.email then persona.email else post.email
|
||||
sub: if Conf['Remember Subject'] then post.sub else null
|
||||
$.set 'QR.persona', persona
|
||||
QR.persona.set post
|
||||
|
||||
[_, threadID, postID] = h1.nextSibling.textContent.match /thread:(\d+),no:(\d+)/
|
||||
postID = +postID
|
||||
|
||||