Skip to content

Commit

Permalink
- Upstream came back, so I had to port those new features.
Browse files Browse the repository at this point in the history
  - Fix for unwanted sorting of catalog under certain settings. [ccd0#3212](ccd0#3212), 7dfba22
  - Turn JS Whitelist functionality off by default. 419e90c
    - Better way of turning off JS Whitelist. 7df2750
    - Update documentation. 62e4ccf
  - Fallback when XPCNativeWrapper is unavailable [ccd0#3430](ccd0#3430)
  - Add ability to clear whole thread watcher [ccd0#2926](ccd0#2926)
  • Loading branch information
TuxedoTako committed Nov 22, 2024
1 parent 6c322a7 commit e900a0c
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 98 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,17 @@
4chan XT uses a different user script namespace than 4chan X, so to migrate you need to export settings from 4chan X,
and import them in XT.

### Not released yet

- Upstream came back, so I had to port those new features.
- Fix for unwanted sorting of catalog under certain settings. [ccd0#3212](https://github.com/ccd0/4chan-x/issues/3212),
7dfba22042d01fde1e762af68e92109d80d0164d
- Turn JS Whitelist functionality off by default. 419e90c38eddc65a5a32e4a17a8211b3157ae61e
- Better way of turning off JS Whitelist. 7df2750fadffe0b5cc441b21034563c95c8500bd
- Update documentation. 62e4ccf1e869ab4757fa2b9107d1a52b1890a1fc
- Fallback when XPCNativeWrapper is unavailable [ccd0#3430](https://github.com/ccd0/4chan-x/pull/3430)
- Add ability to clear whole thread watcher [ccd0#2926](https://github.com/ccd0/4chan-x/pull/2926)

### 2.18.0 (2024-11-20)

- The quick reply now supports mp4. [#124](https://github.com/TuxedoTako/4chan-xt/pull/124)
Expand Down
4 changes: 2 additions & 2 deletions src/General/Settings/Advanced.html
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
if you are on /g/.
</div>
<div class="note">
For custom styling, you can wrap groups or individual links in <code>{{</code> and <code>}}</code>, to wrap them in
For custom styling, you can wrap groups or individual links in <code>{{</div>code> and <code>}}</code>, to wrap them in
a span. You can also add classes in double quotes right after the {{. For example: <br />
<code>[g-title] {{"favorites"[a-title / jp-title]}}</code><br />
Results in:<br />
Expand Down Expand Up @@ -187,7 +187,7 @@
<legend>Javascript Whitelist</legend>
<div>
Sources from which Javascript is allowed to be loaded by <a href="http://content-security-policy.com/#source_list" target="_blank">Content Security Policy</a>.<br>
Lines starting with a <code>#</code> will be ignored.
Lines starting with a <code>#</code> will be ignored. Remove or comment out all lines to allow everything.
</div>
<textarea hidden name="jsWhitelist" class="field" spellcheck="false"></textarea>
</fieldset>
Expand Down
169 changes: 93 additions & 76 deletions src/Monitoring/ThreadWatcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ var ThreadWatcher = {
if (this.cb) { $.off(this.el, 'click', this.cb); }
this.cb = function() {
$.event('CloseMenu');
return ThreadWatcher.toggle(thread);
return ThreadWatcher.toggle(thread, true);
};
$.on(this.el, 'click', this.cb);
return true;
Expand Down Expand Up @@ -167,8 +167,8 @@ var ThreadWatcher = {
catalogNode() {
if (ThreadWatcher.isWatched(this.thread)) { $.addClass(this.nodes.root, 'watched'); }
return $.on(this.nodes.root, 'mousedown click', e => {
if ((e.button !== 0) || !e.altKey) { return; }
if (e.type === 'click') { ThreadWatcher.toggle(this.thread); }
if ((e.button !== 0) || !e.altKey) return;
if (e.type === 'click') ThreadWatcher.toggle(this.thread, true);
return e.preventDefault();
});
}, // Also on mousedown to prevent highlighting thumbnail in Firefox.
Expand Down Expand Up @@ -206,14 +206,24 @@ var ThreadWatcher = {
}
$.event('CloseMenu');
},
clear() {
if (!confirm("Delete ALL threads from watcher?")) return;
const ref = ThreadWatcher.getAll();
for (let i = 0, len = ref.length; i < len; i++) {
const { siteID, boardID, threadID } = ref[i];
ThreadWatcher.db.delete({ siteID, boardID, threadID });
}
ThreadWatcher.refresh(true);
$.event('CloseMenu');
},
pruneDeads() {
if ($.hasClass(this, 'disabled')) return;
for (var {siteID, boardID, threadID, data} of ThreadWatcher.getAll()) {
if (data.isDead) {
ThreadWatcher.db.delete({siteID, boardID, threadID});
}
}
ThreadWatcher.refresh();
ThreadWatcher.refresh(true);
$.event('CloseMenu');
},
pruneReadDeads() {
Expand All @@ -223,7 +233,7 @@ var ThreadWatcher = {
ThreadWatcher.db.delete({ siteID, boardID, threadID });
}
}
ThreadWatcher.refresh();
ThreadWatcher.refresh(true);
$.event('CloseMenu');
},
dismiss() {
Expand All @@ -236,22 +246,24 @@ var ThreadWatcher = {
},
toggle() {
const {thread} = Get.postFromNode(this);
ThreadWatcher.toggle(thread);
ThreadWatcher.toggle(thread, true);
},
rm() {
const {siteID} = this.parentNode.dataset;
const [boardID, threadID] = this.parentNode.dataset.fullID.split('.');
ThreadWatcher.rm(siteID, boardID, +threadID);
ThreadWatcher.rm(siteID, boardID, +threadID, undefined, true);
},
post(e) {
const {boardID, threadID, postID} = e.detail;
const cb = PostRedirect.delay();
if (postID === threadID) {
if (Conf['Auto Watch']) {
ThreadWatcher.addRaw(boardID, threadID, {}, cb);
ThreadWatcher.addRaw(boardID, threadID, {}, cb, true);
}
} else if (Conf['Auto Watch Reply']) {
ThreadWatcher.add((g.threads.get(boardID + '.' + threadID) || new Thread(threadID, g.boards[boardID] || new Board(boardID))), cb);
ThreadWatcher.add(
(g.threads.get(boardID + '.' + threadID) || new Thread(threadID, g.boards[boardID] || new Board(boardID))),
cb, true);
}
},
onIndexUpdate(e) {
Expand Down Expand Up @@ -684,7 +696,7 @@ var ThreadWatcher = {
return ThreadWatcher.refreshIcon();
},

refresh() {
refresh(manual) {
ThreadWatcher.build();

g.threads.forEach(function(thread) {
Expand All @@ -701,7 +713,7 @@ var ThreadWatcher = {
});

if (Conf['Pin Watched Threads']) {
return $.event('SortIndex', {deferred: Conf['Index Mode'] !== 'catalog'});
return $.event('SortIndex', {deferred: !(manual && Conf['Index Mode'] === 'catalog')});
}
},

Expand Down Expand Up @@ -752,18 +764,18 @@ var ThreadWatcher = {
return ThreadWatcher.db.extend({boardID, threadID, val: {isDead: true, isArchived: undefined, page: undefined, lastPage: undefined, unread: undefined, quotingYou: undefined}}, cb);
},

toggle(thread) {
toggle(thread, manual) {
const siteID = g.SITE.ID;
const boardID = thread.board.ID;
const threadID = thread.ID;
if (ThreadWatcher.db.get({boardID, threadID})) {
return ThreadWatcher.rm(siteID, boardID, threadID);
return ThreadWatcher.rm(siteID, boardID, threadID, undefined, manual);
} else {
return ThreadWatcher.add(thread);
return ThreadWatcher.add(thread, undefined, manual);
}
},

add(thread, cb) {
add(thread, cb, manual) {
const data = {};
const siteID = g.SITE.ID;
const boardID = thread.board.ID;
Expand All @@ -776,16 +788,16 @@ var ThreadWatcher = {
data.isDead = true;
}
if (thread.OP) { data.excerpt = Get.threadExcerpt(thread); }
return ThreadWatcher.addRaw(boardID, threadID, data, cb);
return ThreadWatcher.addRaw(boardID, threadID, data, cb, manual);
},

addRaw(boardID, threadID, data, cb) {
addRaw(boardID, threadID, data, cb, manual) {
const oldData = ThreadWatcher.db.get({ boardID, threadID, defaultValue: dict() });
delete oldData.last;
delete oldData.modified;
$.extend(oldData, data);
ThreadWatcher.db.set({boardID, threadID, val: oldData}, cb);
ThreadWatcher.refresh();
ThreadWatcher.refresh(manual);
const thread = {siteID: g.SITE.ID, boardID, threadID, data, force: true};
if (Conf['Show Page'] && !data.isDead) {
return ThreadWatcher.fetchBoard([thread]);
Expand All @@ -794,9 +806,9 @@ var ThreadWatcher = {
}
},

rm(siteID, boardID, threadID, cb) {
rm(siteID, boardID, threadID, cb, manual) {
ThreadWatcher.db.delete({siteID, boardID, threadID}, cb);
return ThreadWatcher.refresh();
return ThreadWatcher.refresh(manual);
},

menu: {
Expand Down Expand Up @@ -827,68 +839,71 @@ var ThreadWatcher = {
return true;
}
});
return $.on(entryEl, 'click', () => ThreadWatcher.toggle(g.threads.get(`${g.BOARD}.${g.THREADID}`)));
return $.on(entryEl, 'click', () => ThreadWatcher.toggle(g.threads.get(`${g.BOARD}.${g.THREADID}`), true));
},

addMenuEntries() {
const entries = [];

// `Open all` entry
entries.push({
text: 'Open all threads',
cb: ThreadWatcher.cb.openAll,
open() {
this.el.classList.toggle('disabled', !ThreadWatcher.list.firstElementChild);
return true;
}
});

// `Open Unread` entry
entries.push({
text: 'Open unread threads',
cb: ThreadWatcher.cb.openUnread,
open() {
this.el.classList.toggle('disabled', !$('.replies-unread', ThreadWatcher.list));
return true;
}
});

const toggleDisabledDead = function () {
this.el.classList.toggle('disabled', !$('.dead-thread', ThreadWatcher.list));
return true;
};

// `Open unread dead threads` entry
entries.push({
text: 'Open unread dead threads',
cb: ThreadWatcher.cb.openDeads,
open: toggleDisabledDead,
});

// `Prune all dead threads` entry
entries.push({
text: 'Prune all dead threads',
cb: ThreadWatcher.cb.pruneDeads,
open: toggleDisabledDead,
});

// `Prune read dead threads` entry
entries.push({
text: 'Prune read dead threads',
cb: ThreadWatcher.cb.pruneReadDeads,
open: toggleDisabledDead,
});

// `Dismiss posts quoting you` entry
entries.push({
text: 'Dismiss posts quoting you',
title: 'Unhighlight the thread watcher icon and threads until there are new replies quoting you.',
cb: ThreadWatcher.cb.dismiss,
open() {
this.el.classList.toggle('disabled', !$.hasClass(ThreadWatcher.shortcut, 'replies-quoting-you'));
return true;
}
});
const entries = [
// `Open all` entry
{
text: 'Open all threads',
cb: ThreadWatcher.cb.openAll,
open() {
this.el.classList.toggle('disabled', !ThreadWatcher.list.firstElementChild);
return true;
}
},
{
text: 'Clear all threads',
cb: ThreadWatcher.cb.clear,
open() {
this.el.classList.toggle('disabled', !ThreadWatcher.list.firstElementChild);
return true;
}
},
// `Open Unread` entry
{
text: 'Open unread threads',
cb: ThreadWatcher.cb.openUnread,
open() {
this.el.classList.toggle('disabled', !$('.replies-unread', ThreadWatcher.list));
return true;
}
},
// `Open unread dead threads` entry
{
text: 'Open unread dead threads',
cb: ThreadWatcher.cb.openDeads,
open: toggleDisabledDead,
},
// `Prune all dead threads` entry
{
text: 'Prune all dead threads',
cb: ThreadWatcher.cb.pruneDeads,
open: toggleDisabledDead,
},
// `Prune read dead threads` entry
{
text: 'Prune read dead threads',
cb: ThreadWatcher.cb.pruneReadDeads,
open: toggleDisabledDead,
},
// `Dismiss posts quoting you` entry
{
text: 'Dismiss posts quoting you',
title: 'Unhighlight the thread watcher icon and threads until there are new replies quoting you.',
cb: ThreadWatcher.cb.dismiss,
open() {
this.el.classList.toggle('disabled', !$.hasClass(ThreadWatcher.shortcut, 'replies-quoting-you'));
return true;
}
},
];

for (var {text, title, cb, open} of entries) {
var entry = {
Expand Down Expand Up @@ -924,8 +939,10 @@ var ThreadWatcher = {
entry.el.title += '\n[Remember Last Read Post is disabled.]';
}
$.on(input, 'change', $.cb.checked);
if (['Current Board', 'Show Page', 'Show Unread Count', 'Show Site Prefix'].includes(name)) { $.on(input, 'change', ThreadWatcher.refresh); }
if (['Show Page', 'Show Unread Count', 'Auto Update Thread Watcher'].includes(name)) { $.on(input, 'change', ThreadWatcher.fetchAuto); }
if (['Current Board', 'Show Page', 'Show Unread Count', 'Show Site Prefix'].includes(name))
$.on(input, 'change', () => ThreadWatcher.refresh());
if (['Show Page', 'Show Unread Count', 'Auto Update Thread Watcher'].includes(name))
$.on(input, 'change', ThreadWatcher.fetchAuto);
return this.menu.addEntry(entry);
}
}
Expand Down
16 changes: 1 addition & 15 deletions src/config/Config.js
Original file line number Diff line number Diff line change
Expand Up @@ -892,21 +892,7 @@ current-archive-text:"Archive"]
sjisPreview: false
},

jsWhitelist: `\
http://s.4cdn.org
https://s.4cdn.org
http://www.google.com
https://www.google.com
https://www.gstatic.com
http://cdn.mathjax.org
https://cdn.mathjax.org
https://cdnjs.cloudflare.com
https://hcaptcha.com
https://*.hcaptcha.com
'self'
'unsafe-inline'
'unsafe-eval'\
`,
jsWhitelist: '',

captchaLanguage: '',

Expand Down
5 changes: 4 additions & 1 deletion src/main/Main.js
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,10 @@ var Main = {
!SW.yotsuba.regexp.captcha.test(location.href) &&
!$$('script:not([src])', d).filter(s => /this\[/.test(s.textContent)).length
) {
($.getSync || $.get)({'jsWhitelist': Conf['jsWhitelist']}, ({jsWhitelist}) => $.addCSP(`script-src ${jsWhitelist.replace(/^#.*$/mg, '').replace(/[\s;]+/g, ' ').trim()}`));
($.getSync || $.get)({'jsWhitelist': Conf['jsWhitelist']}, ({jsWhitelist}) => {
const parsedList = jsWhitelist.replace(/^#.*$/mg, '').replace(/[\s;]+/g, ' ').trim();
if (/\S/.test(parsedList)) $.addCSP(`script-src ${parsedList}`);
});
}

// Get saved values as items
Expand Down
8 changes: 4 additions & 4 deletions src/platform/$.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,11 @@ $.getOwn = function(obj, key) {
};

$.ajax = (function() {
let pageXHR;
let pageXHR = XMLHttpRequest;
if (window.wrappedJSObject && !XMLHttpRequest.wrappedJSObject) {
pageXHR = XPCNativeWrapper(window.wrappedJSObject.XMLHttpRequest);
} else {
pageXHR = XMLHttpRequest;
try {
pageXHR = XPCNativeWrapper(window.wrappedJSObject.XMLHttpRequest);
} catch (e) {}
}

return function (url, options={}) {
Expand Down

0 comments on commit e900a0c

Please sign in to comment.