Merge branch 'persona' into v3

This commit is contained in:
Mayhem 2013-05-01 23:00:54 +02:00
commit 2a11b1633d
9 changed files with 143 additions and 34 deletions

View File

@ -1,3 +1,9 @@
- **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.
### 3.2.3 - *2013-04-30* ### 3.2.3 - *2013-04-30*
- Update archive redirection for /c/, /d/, /v/, /vg/, /w/ and /wg/. - Update archive redirection for /c/, /d/, /v/, /vg/, /w/ and /wg/.

View File

@ -8,7 +8,7 @@ Reporting bugs:
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 sensible 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.

View File

@ -335,15 +335,20 @@ a[href="javascript:;"] {
.section-main label { .section-main label {
text-decoration: underline; text-decoration: underline;
} }
.section-filter ul { .section-filter ul,
.section-qr ul {
padding: 0; padding: 0;
} }
.section-filter li { .section-filter li,
.section-qr li {
margin: 10px 40px; margin: 10px 40px;
} }
.section-filter textarea { .section-filter textarea {
height: 500px; height: 500px;
} }
.section-qr textarea {
height: 200px;
}
.section-sauce textarea { .section-sauce textarea {
height: 350px; height: 350px;
} }

View File

@ -0,0 +1,16 @@
<fieldset>
<legend>Quick Reply Personas <span class="warning" #{if Conf['Quick Reply'] then 'hidden' else ''}>is disabled.</span></legend>
<textarea 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>

View File

@ -8,22 +8,22 @@
</div> </div>
<form> <form>
<div class="persona"> <div class="persona">
<input id="dump-button" type="button" title="Dump list" value="+"> <input type="button" id="dump-button" title="Dump list" value="+">
<input name="name" data-name="name" title="Name" placeholder="Name" class="field" size="1"> <input data-name="name" list="list-name" placeholder="Name" class="field" size="1">
<input name="email" data-name="email" title="E-mail" placeholder="E-mail" class="field" size="1"> <input data-name="email" list="list-email" placeholder="E-mail" class="field" size="1">
<input name="sub" data-name="sub" title="Subject" placeholder="Subject" class="field" size="1"> <input data-name="sub" list="list-sub" placeholder="Subject" class="field" size="1">
</div> </div>
<div id="dump-list-container"> <div id="dump-list-container">
<div id="dump-list"></div> <div id="dump-list"></div>
<a id="add-post" href="javascript:;" title="Add a post">+</a> <a id="add-post" href="javascript:;" title="Add a post">+</a>
</div> </div>
<div class="textarea"> <div class="textarea">
<textarea data-name="com" title="Comment" placeholder="Comment" class="field"></textarea> <textarea data-name="com" placeholder="Comment" class="field"></textarea>
<span id="char-count"></span> <span id="char-count"></span>
</div> </div>
<div id="file-n-submit"> <div id="file-n-submit">
<input type="submit"> <input type="submit">
<input id="qr-file-button" type="button" value="Choose files"> <input type="button" id="qr-file-button" value="Choose files">
<span id="qr-filename-container"> <span id="qr-filename-container">
<span id="qr-no-file">No selected file</span> <span id="qr-no-file">No selected file</span>
<span id="qr-filename"></span> <span id="qr-filename"></span>
@ -33,3 +33,6 @@
</div> </div>
<input type="file" multiple> <input type="file" multiple>
</form> </form>
<datalist id="list-name"></datalist>
<datalist id="list-email"></datalist>
<datalist id="list-sub"></datalist>

View File

@ -147,6 +147,11 @@ Config =
'Top Board List': false 'Top Board List': false
'Bottom Board List': false 'Bottom Board List': false
'Custom Board Navigation': true 'Custom Board Navigation': true
QR:
'QR.personas': [
'#email:"sage";boards:jp;always'
'email:"sage"'
].join '\n'
boardnav: '[current-title / toggle-all]' boardnav: '[current-title / toggle-all]'
time: '%m/%d/%y(%a)%H:%M:%S' time: '%m/%d/%y(%a)%H:%M:%S'
backlink: '>>%id' backlink: '>>%id'

View File

@ -42,6 +42,7 @@ Settings =
Settings.addSection 'Main', Settings.main Settings.addSection 'Main', Settings.main
Settings.addSection 'Filter', Settings.filter Settings.addSection 'Filter', Settings.filter
Settings.addSection 'QR', Settings.qr
Settings.addSection 'Sauce', Settings.sauce Settings.addSection 'Sauce', Settings.sauce
Settings.addSection 'Rice', Settings.rice Settings.addSection 'Rice', Settings.rice
Settings.addSection 'Keybinds', Settings.keybinds Settings.addSection 'Keybinds', Settings.keybinds
@ -320,14 +321,23 @@ Settings =
<%= grunt.file.read('html/General/Settings-section-Filter-guide.html').replace(/>\s+</g, '><').trim() %> <%= grunt.file.read('html/General/Settings-section-Filter-guide.html').replace(/>\s+</g, '><').trim() %>
""" """
qr: (section) ->
section.innerHTML = """
<%= grunt.file.read('html/General/Settings-section-QR.html').replace(/>\s+</g, '><').trim() %>
"""
ta = $ 'textarea', section
$.get 'QR.personas', Conf['QR.personas'], (item) ->
ta.value = item['QR.personas']
$.on ta, 'change', $.cb.value
sauce: (section) -> sauce: (section) ->
section.innerHTML = """ section.innerHTML = """
<%= grunt.file.read('html/General/Settings-section-Sauce.html').replace(/>\s+</g, '><').trim() %> <%= grunt.file.read('html/General/Settings-section-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
rice: (section) -> rice: (section) ->
section.innerHTML = """ section.innerHTML = """

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 = @

View File

@ -134,6 +134,73 @@ QR =
value value
status.disabled = disabled or false 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: cooldown:
init: -> init: ->
return unless Conf['Cooldown'] return unless Conf['Cooldown']
@ -403,18 +470,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()
@ -818,8 +894,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()
@ -899,7 +975,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
@ -1002,13 +1078,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