QR doesn't need its own settings section

This commit is contained in:
Jordan Bates 2013-05-01 02:46:29 -07:00
commit 81d65cc3ca
16 changed files with 565 additions and 190 deletions

View File

@ -1,3 +1,16 @@
seaweedchan:
- Fix Gist links if no username is specificed
MayhemYDG:
- **New feature**: `Quick Reply Personas`
- Add custom auto-completion for the name, e-mail and subject inputs.
- Always use a specific persona.
- Per-board configuration.
- Access it in the `QR` tab of the Settings window.
zixaphir:
- Add Gist link titles
### 1.1.5 - 2013-04-30 ### 1.1.5 - 2013-04-30
seaweedchan: seaweedchan:
- Fix various embedding issues - Fix various embedding issues

View File

@ -1,5 +1,5 @@
/* /*
* 4chan X - Version 1.1.5 - 2013-04-30 * 4chan X - Version 1.1.5 - 2013-05-01
* *
* Licensed under the MIT license. * Licensed under the MIT license.
* https://github.com/seaweedchan/4chan-x/blob/master/LICENSE * https://github.com/seaweedchan/4chan-x/blob/master/LICENSE

View File

@ -8,7 +8,7 @@
1. Precise steps to reproduce the problem, with the expected and actual results. 1. Precise steps to reproduce the problem, with the expected and actual results.
2. Console errors, if any. 2. Console errors, if any.
3. Browser version. 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: Open your console with:
- `Ctrl + Shift + J` on Chrome. - `Ctrl + Shift + J` on Chrome.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -19,12 +19,7 @@ ThreadHiding =
hiddenThreads = ThreadHiding.db.get hiddenThreads = ThreadHiding.db.get
boardID: g.BOARD.ID boardID: g.BOARD.ID
defaultValue: {} defaultValue: {}
# XXX tmp fix hiddenThreadsOnCatalog = JSON.parse(localStorage.getItem "4chan-hide-t-#{g.BOARD}") or {}
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()
# Add threads that were hidden in the catalog. # Add threads that were hidden in the catalog.
for threadID of hiddenThreadsOnCatalog for threadID of hiddenThreadsOnCatalog
@ -130,10 +125,10 @@ ThreadHiding =
return if thread.isHidden return if thread.isHidden
{OP} = thread {OP} = thread
threadRoot = OP.nodes.root.parentNode threadRoot = OP.nodes.root.parentNode
threadRoot.hidden = thread.isHidden = true thread.isHidden = true
unless makeStub unless makeStub
threadRoot.nextElementSibling.hidden = true # <hr> threadRoot.hidden = threadRoot.nextElementSibling.hidden = true # <hr>
return return
numReplies = 0 numReplies = 0
@ -154,7 +149,7 @@ ThreadHiding =
$.add thread.stub, a $.add thread.stub, a
if Conf['Menu'] if Conf['Menu']
$.add thread.stub, [$.tn(' '), Menu.makeButton OP] $.add thread.stub, [$.tn(' '), Menu.makeButton OP]
$.before threadRoot, thread.stub $.prepend threadRoot, thread.stub
show: (thread) -> show: (thread) ->
if thread.stub if thread.stub
@ -162,4 +157,4 @@ ThreadHiding =
delete thread.stub delete thread.stub
threadRoot = thread.OP.nodes.root.parentNode threadRoot = thread.OP.nodes.root.parentNode
threadRoot.nextElementSibling.hidden = threadRoot.nextElementSibling.hidden =
threadRoot.hidden = thread.isHidden = false threadRoot.hidden = thread.isHidden = false

View File

@ -425,6 +425,11 @@ http://iqdb.org/?url=%TURL
boardnav: '[ toggle-all ] [current-title]' boardnav: '[ toggle-all ] [current-title]'
QR:
'QR.personas': [
'#email:"sage";boards:jp;always'
].join '\n'
time: '%m/%d/%y(%a)%H:%M:%S' time: '%m/%d/%y(%a)%H:%M:%S'
backlink: '>>%id' backlink: '>>%id'

View File

@ -8,6 +8,8 @@ Get =
if excerpt.length > 70 if excerpt.length > 70
excerpt = "#{excerpt[...67]}..." excerpt = "#{excerpt[...67]}..."
"/#{thread.board}/ - #{excerpt}" "/#{thread.board}/ - #{excerpt}"
threadFromRoot: (root) ->
g.threads["#{g.BOARD}.#{root.id[1..]}"]
postFromRoot: (root) -> postFromRoot: (root) ->
link = $ 'a[title="Highlight this post"]', root link = $ 'a[title="Highlight this post"]', root
boardID = link.pathname.split('/')[1] boardID = link.pathname.split('/')[1]
@ -226,4 +228,4 @@ Get =
post = new Post Build.post(o, true), thread, board, post = new Post Build.post(o, true), thread, board,
isArchived: true isArchived: true
Main.callbackNodes Post, [post] Main.callbackNodes Post, [post]
Get.insert post, root, context Get.insert post, root, context

View File

@ -310,10 +310,10 @@ Settings =
section.innerHTML = """ section.innerHTML = """
<%= grunt.file.read('src/General/html/Settings/Sauce.html').replace(/>\s+</g, '><').trim() %> <%= grunt.file.read('src/General/html/Settings/Sauce.html').replace(/>\s+</g, '><').trim() %>
""" """
sauce = $ 'textarea', section ta = $ 'textarea', section
$.get 'sauces', Conf['sauces'], (item) -> $.get 'sauces', Conf['sauces'], (item) ->
sauce.value = item['sauces'] ta.value = item['sauces']
$.on sauce, 'change', $.cb.value $.on ta, 'change', $.cb.value
advanced: (section) -> advanced: (section) ->
section.innerHTML = """ section.innerHTML = """
@ -331,6 +331,12 @@ Settings =
'input' 'input'
$.on input, event, $.cb.value $.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
archiver = $ 'select[name=archiver]', section archiver = $ 'select[name=archiver]', section
toSelect = Redirect.select g.BOARD.ID toSelect = Redirect.select g.BOARD.ID

View File

@ -321,8 +321,13 @@ a[href="javascript:;"] {
.section-advanced ul { .section-advanced ul {
list-style: none; list-style: none;
margin: 0; margin: 0;
}
.section-sauce ul {
padding: 8px; padding: 8px;
} }
.section-advanced ul {
padding: 0px;
}
.section-sauce li, .section-sauce li,
.section-advanced li { .section-advanced li {
padding-left: 4px; padding-left: 4px;
@ -570,9 +575,7 @@ a.hide-announcement {
float: left; float: left;
margin-right: 2px; margin-right: 2px;
} }
.stub ~ .sideArrows, .stub ~ * {
.stub ~ .hide-reply-button,
.stub ~ .post {
display: none !important; display: none !important;
} }
.stub input { .stub input {
@ -927,4 +930,4 @@ a:only-of-type > .remove {
.linkify.gist { .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; 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; padding-left: 18px;
} }

View File

@ -11,12 +11,12 @@
<form> <form>
<div class=persona> <div class=persona>
<input id=dump-button type=button title='Dump list' value=+ tabindex=0> <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=name data-name=name list="list-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=email data-name=email list="list-email" 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=sub data-name=sub list="list-sub" placeholder=Subject class=field size=1 tabindex=30>
</div> </div>
<div class=textarea> <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> <span id=char-count></span>
</div> </div>
<div id=dump-list-container> <div id=dump-list-container>
@ -35,4 +35,7 @@
<label id=qr-spoiler-label> <label id=qr-spoiler-label>
<input type=checkbox id=qr-file-spoiler title='Spoiler image' tabindex=90>Spoiler? <input type=checkbox id=qr-file-spoiler title='Spoiler image' tabindex=90>Spoiler?
</label> </label>
</form> </form>
<datalist id="list-name"></datalist>
<datalist id="list-email"></datalist>
<datalist id="list-sub"></datalist>

View File

@ -52,6 +52,23 @@
<div>Resolution: <code>%r</code> (Displays 'PDF' for PDF files)</div> <div>Resolution: <code>%r</code> (Displays 'PDF' for PDF files)</div>
</fieldset> </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> <fieldset>
<legend>Unread Favicon <span class=warning #{if Conf['Unread Favicon'] then 'hidden' else ''}>is disabled.</span></legend> <legend>Unread Favicon <span class=warning #{if Conf['Unread Favicon'] then 'hidden' else ''}>is disabled.</span></legend>
<select name=favicon> <select name=favicon>

View File

@ -46,18 +46,12 @@ DeleteLink =
$.off @, 'click', DeleteLink.delete $.off @, 'click', DeleteLink.delete
@textContent = "Deleting #{@textContent}..." @textContent = "Deleting #{@textContent}..."
pwd =
if m = d.cookie.match /4chan_pass=([^;]+)/
decodeURIComponent m[1]
else
$.id('delPassword').value
fileOnly = $.hasClass @, 'delete-file' fileOnly = $.hasClass @, 'delete-file'
form = form =
mode: 'usrdel' mode: 'usrdel'
onlyimgdel: fileOnly onlyimgdel: fileOnly
pwd: pwd pwd: QR.persona.getPassword()
form[post.ID] = 'delete' form[post.ID] = 'delete'
link = @ link = @
@ -106,4 +100,4 @@ DeleteLink =
delete DeleteLink.cooldown.counting delete DeleteLink.cooldown.counting
return return
setTimeout DeleteLink.cooldown.count, 1000, post, seconds - 1, length, node setTimeout DeleteLink.cooldown.count, 1000, post, seconds - 1, length, node
node.textContent = "Delete (#{seconds})" node.textContent = "Delete (#{seconds})"

View File

@ -44,7 +44,9 @@ Nav =
else else
headRect = Header.bar.getBoundingClientRect() headRect = Header.bar.getBoundingClientRect()
topMargin = headRect.top + headRect.height 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 for thread, i in threads
rect = thread.getBoundingClientRect() rect = thread.getBoundingClientRect()
if rect.bottom > topMargin # not scrolled past if rect.bottom > topMargin # not scrolled past
@ -62,4 +64,4 @@ Nav =
i += delta i += delta
top = threads[i]?.getBoundingClientRect().top - topMargin top = threads[i]?.getBoundingClientRect().top - topMargin
window.scrollBy 0, top window.scrollBy 0, top

View File

@ -157,6 +157,72 @@ QR =
value value
status.disabled = disabled or false status.disabled = disabled or false
persona:
name: []
email: []
sub: []
pwd: ''
always: {}
init: ->
QR.persona.getPassword()
$.get 'QR.personas', Conf['QR.personas'], ({'QR.personas': personas}) ->
for item in personas.split '\n'
QR.persona.parseItem item.trim()
for type in ['name', 'email', 'sub']
QR.persona.loadPersonas type
return
parseItem: (item) ->
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 QR.persona[type]
QR.persona[type].push val
loadPersonas: (type) ->
list = $ "#list-#{type}", QR.nodes.el
for val in QR.persona[type]
$.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: cooldown:
init: -> init: ->
return unless Conf['Cooldown'] return unless Conf['Cooldown']
@ -430,18 +496,27 @@ QR =
prev.spoiler prev.spoiler
else else
false false
$.get 'QR.persona', {}, (item) => QR.persona.get (persona) =>
persona = item['QR.persona'] @name = if 'name' of QR.persona.always
@name = if prev QR.persona.always.name
else if prev
prev.name prev.name
else else
persona.name 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 prev.email
else else
persona.email 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 @load() if QR.selected is @ # load persona
@select() if select @select() if select
@unlock() @unlock()
@ -847,8 +922,8 @@ QR =
$.set 'QR Size', @style.cssText $.set 'QR Size', @style.cssText
<% } %> <% } %>
QR.persona.init()
new QR.post true new QR.post true
QR.status() QR.status()
QR.cooldown.init() QR.cooldown.init()
QR.captcha.init() QR.captcha.init()
@ -928,7 +1003,7 @@ QR =
spoiler: post.spoiler spoiler: post.spoiler
textonly: textOnly textonly: textOnly
mode: 'regist' 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_challenge_field: challenge
recaptcha_response_field: response recaptcha_response_field: response
@ -1031,13 +1106,7 @@ QR =
QR.cleanNotifications() QR.cleanNotifications()
QR.notifications.push new Notification 'success', h1.textContent, 5 QR.notifications.push new Notification 'success', h1.textContent, 5
$.get 'QR.persona', {}, (item) -> QR.persona.set post
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
[_, threadID, postID] = h1.nextSibling.textContent.match /thread:(\d+),no:(\d+)/ [_, threadID, postID] = h1.nextSibling.textContent.match /thread:(\d+),no:(\d+)/
postID = +postID postID = +postID