Start working on tightening our control of the Navigation context

Currently breaks posting.

TODO:
* Fix style switching from NSFW to SFW (SFW to NSFW works)
  - It is worth noting this used to work.
* Fix post form not updating its currently selected thread.
* Fix navigation between threads in different boards (same boards
  works? Maybe?)
* Handle race conditions due to pop states.
  - I'm having a lot of trouble wrapping my mind around this one.
    Mostly due to the fact that I have no idea where to begin with
    it. But this isn't a big issue unless you pop state multiple
    times within seconds.

    I just need some exceptions when we try to disconnect features
    that haven't even finished connecting due to threads not being
    available yet.

Most of the early issues, like double-backlinks, incorrect
thumbnails, etc, have been fixed, I think. Or at least I'm no
longer running into them all the time.
This commit is contained in:
Zixaphir 2014-01-18 02:33:54 -07:00
parent c8514d1029
commit 8ddf2b4e5f
7 changed files with 209 additions and 71 deletions

View File

@ -1,5 +1,5 @@
/*
* 4chan X - Version 1.3.2 - 2014-01-17
* 4chan X - Version 1.3.2 - 2014-01-18
*
* Licensed under the MIT license.
* https://github.com/seaweedchan/4chan-x/blob/master/LICENSE

View File

@ -22,7 +22,7 @@
// ==/UserScript==
/*
* 4chan X - Version 1.3.2 - 2014-01-17
* 4chan X - Version 1.3.2 - 2014-01-18
*
* Licensed under the MIT license.
* https://github.com/seaweedchan/4chan-x/blob/master/LICENSE
@ -1164,7 +1164,7 @@
if (!(!$.hasClass(quotelink, 'deadlink'))) {
continue;
}
$.add(quotelink, $.tn('\u00A0(Dead)'));
quotelink.textContent = quotelink.textContent + '\u00A0(Dead)';
$.addClass(quotelink, 'deadlink');
}
};
@ -4854,7 +4854,7 @@
if (Conf['Quote Inlining']) {
$.on(link, 'click', QuoteInline.toggle);
if (Conf['Quote Hash Navigation']) {
nodes.push.apply(nodes, QuoteInline.qiQuote(link, $.hasClass(link, 'filtered')));
nodes.push(QuoteInline.qiQuote(link, $.hasClass(link, 'filtered')));
}
}
$.add(container, nodes);
@ -5632,12 +5632,14 @@
$.on(d, 'dragover', QR.dragOver);
$.on(d, 'drop', QR.dropFile);
$.on(d, 'dragstart dragend', QR.drag);
switch (g.VIEW) {
case 'index':
return {
index: function() {
return $.on(d, 'IndexRefresh', QR.generatePostableThreadsList);
case 'thread':
},
thread: function() {
return $.on(d, 'ThreadUpdate', QR.statusCheck);
}
}
}[g.VIEW]();
},
statusCheck: function() {
if (g.DEAD) {
@ -5972,7 +5974,7 @@
return list.value = g.VIEW === 'thread' ? g.THREADID : 'new';
},
dialog: function() {
var check, dialog, elm, event, flagSelector, i, items, key, mimeTypes, name, node, nodes, save, value, _ref;
var check, dialog, elm, event, i, items, key, mimeTypes, name, node, nodes, save, value, _ref;
QR.nodes = nodes = {
el: dialog = UI.dialog('qr', 'top:0;right:0;', "<div class=move><label><input type=checkbox id=autohide title=Auto-hide>Quick Reply</label><a href=javascript:; class=close title=Close>×</a><select data-name=thread title='Create a new thread / Reply'><option value=new>New thread</option></select></div><form><div class=persona><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 placeholder=Comment class=field tabindex=40></textarea><span id=char-count></span></div><div id=dump-list-container><div id=dump-list></div><a id=add-post href=javascript:; title=\"Add a post\" tabindex=50>+</a></div><div id=file-n-submit><span id=qr-filename-container class=field tabindex=60><span id=qr-no-file>No selected file</span><input id=\"qr-filename\" data-name=\"filename\" spellcheck=\"false\"><span id=qr-extras-container><a id=qr-filerm href=javascript:; title='Remove file'>×</a><a id=dump-button title='Dump list'>+</a></span></span><label id=qr-spoiler-label><input type=checkbox id=qr-file-spoiler title='Spoiler image' tabindex=70></label><input type=submit tabindex=80></div><input type=file multiple></form><datalist id=\"list-name\"></datalist><datalist id=\"list-email\"></datalist><datalist id=\"list-sub\"></datalist> ")
};
@ -6030,12 +6032,7 @@
nodes.flashTag.dataset["default"] = '4';
$.add(nodes.form, nodes.flashTag);
}
if (flagSelector = $('.flagSelector')) {
nodes.flag = flagSelector.cloneNode(true);
nodes.flag.dataset.name = 'flag';
nodes.flag.dataset["default"] = '0';
$.add(nodes.form, nodes.flag);
}
QR.flagsInput();
$.on(nodes.filename.parentNode, 'click keydown', QR.openFileInput);
items = $$('*', QR.nodes.el);
i = 0;
@ -6096,6 +6093,40 @@
$.add(d.body, dialog);
return $.event('QRDialogCreation', null, dialog);
},
flags: function() {
var flag, fn, select, _i, _len, _ref;
fn = function(val) {
return $.el('option', {
value: val[0],
textContent: val[1]
});
};
select = $.el('select', {
name: 'flag',
className: 'flagSelector'
});
_ref = [['0', 'None'], ['US', 'American'], ['KP', 'Best Korean'], ['BL', 'Black Nationalist'], ['CM', 'Communist'], ['CF', 'Confederate'], ['RE', 'Conservative'], ['EU', 'European'], ['GY', 'Gay'], ['PC', 'Hippie'], ['IL', 'Israeli'], ['DM', 'Liberal'], ['RP', 'Libertarian'], ['MF', 'Muslim'], ['NZ', 'Nazi'], ['OB', 'Obama'], ['PR', 'Pirate'], ['RB', 'Rebel'], ['TP', 'Tea Partier'], ['TX', 'Texan'], ['TR', 'Tree Hugger'], ['WP', 'White Supremacist']];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
flag = _ref[_i];
$.add(select, fn(flag));
}
return select;
},
flagsInput: function() {
var flag, nodes;
nodes = QR.nodes;
if (nodes.flagSelector) {
$.rm(nodes.flagSelector);
delete nodes.flagSelector;
}
if (g.BOARD.ID === 'pol') {
flag = QR.flags();
flag.dataset.name = 'flag';
flag.dataset["default"] = '0';
nodes.flag = flag;
return $.add(nodes.form, flag);
}
},
preSubmitHooks: [],
submit: function(e) {
var challenge, err, extra, filetag, formData, hook, options, post, response, textOnly, thread, threadID, _i, _len, _ref, _ref1;
@ -12125,21 +12156,34 @@
return QR.generatePostableThreadsList();
},
updateContext: function(view) {
var oldView;
if (view === g.VIEW) {
return;
}
$.rmClass(doc, g.VIEW);
$.addClass(doc, view);
oldView = g.VIEW;
g.VIEW = view;
switch (view) {
case 'index':
return {
index: function() {
if (oldView === g.VIEW) {
return;
}
delete g.THREADID;
QR.link.textContent = 'Start a Thread';
$.off(d, 'ThreadUpdate', QR.statusCheck);
return $.on(d, 'IndexRefresh', QR.generatePostableThreadsList);
case 'thread':
},
thread: function() {
g.THREADID = +window.location.pathname.split('/')[3];
if (oldView === g.VIEW) {
return;
}
QR.link.textContent = 'Reply to Thread';
$.on(d, 'ThreadUpdate', QR.statusCheck);
return $.off(d, 'IndexRefresh', QR.generatePostableThreadsList);
}
}
}[g.VIEW]();
},
updateBoard: function(boardID) {
var fullBoardList, onload, req;
@ -12148,6 +12192,7 @@
$.rmClass($('.current', fullBoardList), 'current');
$.addClass($("a[href*='/" + boardID + "/']", fullBoardList), 'current');
Header.generateBoardList(Conf['boardnav'].replace(/(\r\n|\n|\r)/g, ' '));
QR.flagsInput();
onload = function(e) {
var aboard, board, err, _i, _len, _ref;
if (e.type === 'abort') {
@ -12250,23 +12295,25 @@
pageNum = view;
view = 'index';
}
if (view !== g.VIEW) {
Navigate.updateContext(view);
if (!(view === g.VIEW && boardID === g.BOARD.ID)) {
Navigate.disconnect();
Navigate.clean();
Navigate.updateContext(view);
Navigate.reconnect();
}
if (view === 'index') {
if (boardID === g.BOARD.ID) {
Navigate.title = function() {
if (boardID === g.BOARD.ID) {
Navigate.title = function() {
if (view === 'index') {
return d.title = $('.boardTitle').textContent;
};
} else {
g.BOARD = new Board(boardID);
Navigate.title = function() {
return Navigate.updateBoard(boardID);
};
}
}
};
} else {
g.BOARD = new Board(boardID);
Navigate.title = function() {
return Navigate.updateBoard(boardID);
};
}
if (view === 'index') {
return Index.update(pageNum);
} else {
Navigate.updateFavicon(Favicon.SFW);
@ -12296,6 +12343,7 @@
new Notice('warning', "Failed to load thread." + (req.status ? " " + req.status : ''));
return;
}
Navigate.title();
try {
return Navigate.parse(JSON.parse(req.response).posts);
} catch (_error) {

View File

@ -1,6 +1,6 @@
// Generated by CoffeeScript
/*
* 4chan X - Version 1.3.2 - 2014-01-17
* 4chan X - Version 1.3.2 - 2014-01-18
*
* Licensed under the MIT license.
* https://github.com/seaweedchan/4chan-x/blob/master/LICENSE
@ -1170,7 +1170,7 @@
if (!(!$.hasClass(quotelink, 'deadlink'))) {
continue;
}
$.add(quotelink, $.tn('\u00A0(Dead)'));
quotelink.textContent = quotelink.textContent + '\u00A0(Dead)';
$.addClass(quotelink, 'deadlink');
}
};
@ -4857,7 +4857,7 @@
if (Conf['Quote Inlining']) {
$.on(link, 'click', QuoteInline.toggle);
if (Conf['Quote Hash Navigation']) {
nodes.push.apply(nodes, QuoteInline.qiQuote(link, $.hasClass(link, 'filtered')));
nodes.push(QuoteInline.qiQuote(link, $.hasClass(link, 'filtered')));
}
}
$.add(container, nodes);
@ -5636,12 +5636,14 @@
$.on(d, 'dragover', QR.dragOver);
$.on(d, 'drop', QR.dropFile);
$.on(d, 'dragstart dragend', QR.drag);
switch (g.VIEW) {
case 'index':
return {
index: function() {
return $.on(d, 'IndexRefresh', QR.generatePostableThreadsList);
case 'thread':
},
thread: function() {
return $.on(d, 'ThreadUpdate', QR.statusCheck);
}
}
}[g.VIEW]();
},
statusCheck: function() {
if (g.DEAD) {
@ -5980,7 +5982,7 @@
return list.value = g.VIEW === 'thread' ? g.THREADID : 'new';
},
dialog: function() {
var check, dialog, event, flagSelector, i, items, key, mimeTypes, name, node, nodes, save, value, _ref;
var check, dialog, event, i, items, key, mimeTypes, name, node, nodes, save, value, _ref;
QR.nodes = nodes = {
el: dialog = UI.dialog('qr', 'top:0;right:0;', "<div class=move><label><input type=checkbox id=autohide title=Auto-hide>Quick Reply</label><a href=javascript:; class=close title=Close>×</a><select data-name=thread title='Create a new thread / Reply'><option value=new>New thread</option></select></div><form><div class=persona><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 placeholder=Comment class=field tabindex=40></textarea><span id=char-count></span></div><div id=dump-list-container><div id=dump-list></div><a id=add-post href=javascript:; title=\"Add a post\" tabindex=50>+</a></div><div id=file-n-submit><span id=qr-filename-container class=field tabindex=60><span id=qr-no-file>No selected file</span><input id=\"qr-filename\" data-name=\"filename\" spellcheck=\"false\"><span id=qr-extras-container><a id=qr-filerm href=javascript:; title='Remove file'>×</a><a id=dump-button title='Dump list'>+</a></span></span><label id=qr-spoiler-label><input type=checkbox id=qr-file-spoiler title='Spoiler image' tabindex=70></label><input type=submit tabindex=80></div><input type=file multiple></form><datalist id=\"list-name\"></datalist><datalist id=\"list-email\"></datalist><datalist id=\"list-sub\"></datalist> ")
};
@ -6038,12 +6040,7 @@
nodes.flashTag.dataset["default"] = '4';
$.add(nodes.form, nodes.flashTag);
}
if (flagSelector = $('.flagSelector')) {
nodes.flag = flagSelector.cloneNode(true);
nodes.flag.dataset.name = 'flag';
nodes.flag.dataset["default"] = '0';
$.add(nodes.form, nodes.flag);
}
QR.flagsInput();
$.on(nodes.filename.parentNode, 'click keydown', QR.openFileInput);
$.on(dialog, 'focusin', QR.focusin);
$.on(dialog, 'focusout', QR.focusout);
@ -6087,6 +6084,40 @@
$.add(d.body, dialog);
return $.event('QRDialogCreation', null, dialog);
},
flags: function() {
var flag, fn, select, _i, _len, _ref;
fn = function(val) {
return $.el('option', {
value: val[0],
textContent: val[1]
});
};
select = $.el('select', {
name: 'flag',
className: 'flagSelector'
});
_ref = [['0', 'None'], ['US', 'American'], ['KP', 'Best Korean'], ['BL', 'Black Nationalist'], ['CM', 'Communist'], ['CF', 'Confederate'], ['RE', 'Conservative'], ['EU', 'European'], ['GY', 'Gay'], ['PC', 'Hippie'], ['IL', 'Israeli'], ['DM', 'Liberal'], ['RP', 'Libertarian'], ['MF', 'Muslim'], ['NZ', 'Nazi'], ['OB', 'Obama'], ['PR', 'Pirate'], ['RB', 'Rebel'], ['TP', 'Tea Partier'], ['TX', 'Texan'], ['TR', 'Tree Hugger'], ['WP', 'White Supremacist']];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
flag = _ref[_i];
$.add(select, fn(flag));
}
return select;
},
flagsInput: function() {
var flag, nodes;
nodes = QR.nodes;
if (nodes.flagSelector) {
$.rm(nodes.flagSelector);
delete nodes.flagSelector;
}
if (g.BOARD.ID === 'pol') {
flag = QR.flags();
flag.dataset.name = 'flag';
flag.dataset["default"] = '0';
nodes.flag = flag;
return $.add(nodes.form, flag);
}
},
preSubmitHooks: [],
submit: function(e) {
var challenge, err, extra, filetag, formData, hook, options, post, response, textOnly, thread, threadID, _i, _len, _ref, _ref1;
@ -12117,18 +12148,20 @@
$.rmClass(doc, g.VIEW);
$.addClass(doc, view);
g.VIEW = view;
switch (view) {
case 'index':
return {
index: function() {
delete g.THREADID;
QR.link.textContent = 'Start a Thread';
$.off(d, 'ThreadUpdate', QR.statusCheck);
return $.on(d, 'IndexRefresh', QR.generatePostableThreadsList);
case 'thread':
},
thread: function() {
g.THREADID = +window.location.pathname.split('/')[3];
QR.link.textContent = 'Reply to Thread';
$.on(d, 'ThreadUpdate', QR.statusCheck);
return $.off(d, 'IndexRefresh', QR.generatePostableThreadsList);
}
}
}[g.VIEW]();
},
updateBoard: function(boardID) {
var fullBoardList, onload, req;
@ -12137,6 +12170,7 @@
$.rmClass($('.current', fullBoardList), 'current');
$.addClass($("a[href*='/" + boardID + "/']", fullBoardList), 'current');
Header.generateBoardList(Conf['boardnav'].replace(/(\r\n|\n|\r)/g, ' '));
QR.flagsInput();
onload = function(e) {
var aboard, board, err, _i, _len, _ref;
if (e.type === 'abort') {

View File

@ -90,21 +90,26 @@ Navigate =
QR.generatePostableThreadsList()
updateContext: (view) ->
return if view is g.VIEW
$.rmClass doc, g.VIEW
$.addClass doc, view
oldView = g.VIEW
g.VIEW = view
switch view
when 'index'
{
index: ->
return if oldView is g.VIEW
delete g.THREADID
QR.link.textContent = 'Start a Thread'
$.off d, 'ThreadUpdate', QR.statusCheck
$.on d, 'IndexRefresh', QR.generatePostableThreadsList
when 'thread'
thread: ->
g.THREADID = +window.location.pathname.split('/')[3]
return if oldView is g.VIEW
QR.link.textContent = 'Reply to Thread'
$.on d, 'ThreadUpdate', QR.statusCheck
$.off d, 'IndexRefresh', QR.generatePostableThreadsList
}[g.VIEW]()
updateBoard: (boardID) ->
req = null
@ -114,6 +119,8 @@ Navigate =
$.addClass $("a[href*='/#{boardID}/']", fullBoardList), 'current'
Header.generateBoardList Conf['boardnav'].replace /(\r\n|\n|\r)/g, ' '
QR.flagsInput()
onload = (e) ->
if e.type is 'abort'
req.onloadend = null
@ -200,19 +207,20 @@ Navigate =
pageNum = view
view = 'index' # path is "/boardID/". See the problem?
if view isnt g.VIEW
Navigate.updateContext view
unless view is g.VIEW and boardID is g.BOARD.ID # We've navigated somewhere we weren't before!
Navigate.disconnect()
Navigate.clean()
Navigate.updateContext view
Navigate.reconnect()
if view is 'index'
if boardID is g.BOARD.ID
Navigate.title = -> d.title = $('.boardTitle').textContent
else
g.BOARD = new Board boardID
Navigate.title = -> Navigate.updateBoard boardID
if boardID is g.BOARD.ID
Navigate.title = -> d.title = $('.boardTitle').textContent if view is 'index'
else
g.BOARD = new Board boardID
Navigate.title = -> Navigate.updateBoard boardID
if view is 'index'
Index.update pageNum
# Moving from index to thread or thread to thread
@ -240,6 +248,8 @@ Navigate =
new Notice 'warning', "Failed to load thread.#{if req.status then " #{req.status}" else ''}"
return
Navigate.title()
try
Navigate.parse JSON.parse(req.response).posts
catch err

View File

@ -180,7 +180,7 @@ class Post
# Get quotelinks/backlinks to this post
# and paint them (Dead).
for quotelink in Get.allQuotelinksLinkingTo @ when not $.hasClass quotelink, 'deadlink'
$.add quotelink, $.tn '\u00A0(Dead)'
quotelink.textContent = quotelink.textContent + '\u00A0(Dead)'
$.addClass quotelink, 'deadlink'
return
# XXX tmp fix for 4chan's racing condition

View File

@ -68,11 +68,12 @@ QR =
$.on d, 'dragover', QR.dragOver
$.on d, 'drop', QR.dropFile
$.on d, 'dragstart dragend', QR.drag
switch g.VIEW
when 'index'
{
index: ->
$.on d, 'IndexRefresh', QR.generatePostableThreadsList
when 'thread'
thread: ->
$.on d, 'ThreadUpdate', QR.statusCheck
}[g.VIEW]()
statusCheck: ->
if g.DEAD
@ -405,11 +406,8 @@ QR =
"""
nodes.flashTag.dataset.default = '4'
$.add nodes.form, nodes.flashTag
if flagSelector = $ '.flagSelector'
nodes.flag = flagSelector.cloneNode true
nodes.flag.dataset.name = 'flag'
nodes.flag.dataset.default = '0'
$.add nodes.form, nodes.flag
QR.flagsInput()
$.on nodes.filename.parentNode, 'click keydown', QR.openFileInput
@ -464,6 +462,54 @@ QR =
# Use it to extend the QR's functionalities, or for XTRM RICE.
$.event 'QRDialogCreation', null, dialog
flags: ->
fn = (val) -> $.el 'option',
value: val[0]
textContent: val[1]
select = $.el 'select',
name: 'flag'
className: 'flagSelector'
$.add select, fn flag for flag in [
['0', 'None']
['US', 'American']
['KP', 'Best Korean']
['BL', 'Black Nationalist']
['CM', 'Communist']
['CF', 'Confederate']
['RE', 'Conservative']
['EU', 'European']
['GY', 'Gay']
['PC', 'Hippie']
['IL', 'Israeli']
['DM', 'Liberal']
['RP', 'Libertarian']
['MF', 'Muslim']
['NZ', 'Nazi']
['OB', 'Obama']
['PR', 'Pirate']
['RB', 'Rebel']
['TP', 'Tea Partier']
['TX', 'Texan']
['TR', 'Tree Hugger']
['WP', 'White Supremacist']
]
select
flagsInput: ->
{nodes} = QR
if nodes.flagSelector
$.rm nodes.flagSelector
delete nodes.flagSelector
if g.BOARD.ID is 'pol'
flag = QR.flags()
flag.dataset.name = 'flag'
flag.dataset.default = '0'
nodes.flag = flag
$.add nodes.form, flag
preSubmitHooks: []
submit: (e) ->

View File

@ -41,7 +41,7 @@ QuoteBacklink =
$.on link, 'mouseover', QuotePreview.mouseover
if Conf['Quote Inlining']
$.on link, 'click', QuoteInline.toggle
nodes.push.apply nodes, QuoteInline.qiQuote link, $.hasClass link, 'filtered' if Conf['Quote Hash Navigation']
nodes.push QuoteInline.qiQuote link, $.hasClass link, 'filtered' if Conf['Quote Hash Navigation']
$.add container, nodes
return
secondNode: ->