[ overboard / sfw / alt / cytube] [ leftypol / b / WRK / hobby / tech / edu / ga / ent / 777 / posad / i / a / R9K / dead ] [ meta ]

/tech/ - Technology

"Technology reveals the active relation of man to nature"
Name
Email
Subject
Comment
Flag
File
Embed
Password (For file deletion.)

Matrix   IRC Chat   Mumble   Telegram   Discord


File: 1612129656526.gif ( 2.28 MB , 224x240 , 1608608621350.gif )

 No.6724[Last 50 Posts]

This thread is only for feedback related to technical issues(bug reports, suggestions). Otherwise use
>>>/leftypol/30356
Public Repo: https://github.com/towards-a-new-leftypol/leftypol_lainchan
If you have any grievances you can make a PR.

Mobile Support: https://github.com/PietroCarrara/Clover/releases/latest
Thread For Mobile Feedback: >>>/tech/6316

Onion Link: http://wz6bnwwtwckltvkvji6vvgmjrfspr3lstz66rusvtczhsgvwdcixgbyd.onion
Cytube: https://tv.leftychan.net
Matrix: https://matrix.to/#/#Leftypol:matrix.org
Once you enter, consider joining the lefty technology room.

We are currently working on improvements to the site, subject to the need of the tech team to sleep and go to their day jobs. If you need more immediate feedback please join the matrix room[s] and ask around. Feel free to leave comments, concerns, and suggestions about the tech side of the site here and we will try to get to it as soon as possible

Archived thread:
>>>/leftypol_archive/903
>>

 No.6727

File: 1612133034865.png ( 153.68 KB , 681x698 , 2021-01-31.png )

Plug this hole.
>>

 No.6730

>>6727
There was an issue with the previous fix.
It had to be rolled back, unfortunately.
The issue has been fixed and a new version is in PR.
>>

 No.6731

>>6730
Okay thanks for letting me know it's progressing. I see the PR was made Jan 30, how long do you think it will take for the given admin(s) to enable the fix?
I'm so nagging about this because the warning and Google in general gives me anxiety.

p.s. nonmakina is based af
>>

 No.6733

>>6731
nonmakina = antonious
>>

 No.6744

File: 1612183832574.png ( 8.25 KB , 461x81 , Untitled2.png )

To fix the bug of div#thread_stats not staying at the bottom of the reply list when auto-reload.js adds new replies, replace
https://github.com/towards-a-new-leftypol/leftypol_lainchan/blob/b962f6a1283b33c6dc54c78cf4545adf92e21ce9/js/thread-stats.js#L18
.after('<div id="thread_stats"></div>');

To put the "Unique IPs" on the right with the thread stats dump the span style from the element
https://github.com/towards-a-new-leftypol/leftypol_lainchan/blob/b962f6a1283b33c6dc54c78cf4545adf92e21ce9/templates/post_thread.html#L103
and add a rule to style.css:

[code]
#uniqueip > span {
display: block;
float: right;
margin: 0em 1em;
}
[/code]

The result is attached. Until a backend fix the following can be added to Options -> User JS:

[code]
(() => {
$('.clear').after($('#thread_stats'))
$('#uniqueip > span').attr('style', 'display: block; float: right; margin: 0em 1em;')
})()
[/code]

Options -> User JS quick reply spoiler fix >>>/leftypol_archive/1801
Options -> User JS catalog links in div.boardlist >>>/leftypol_archive/1803
>>

 No.6749

>>6748
lmao do it
>>

 No.6752

>404
It's not multi_image.js, it's multi-image.js.
>>

 No.6753

To fix individual post hiding in Options -> User JS:

[code]
(() => {
const pcid2tid = (board, pcid) => {
const n = document.querySelector ('[data-board="' + board + '"].thread > div#' + pcid)
return n == null ? null : n.parentNode.getAttribute ('id').replace ('thread_', '')
}
const merge = (have, posts) => {
let changed = false
for (p of posts) {
if (!have.some (e => e.post === p.post)) {
have.push (p)
changed = true
}
}
return changed
}
const fixids = () => {
let lspf = JSON.parse (localStorage.postFilter)
let changed = false
for (const [board, threads] of Object.entries (lspf.postFilter)) {
for (const [id, posts] of Object.entries (threads)) {
if (!id.startsWith ('pc')) { continue; }
const tid = pcid2tid (board, id)
if (tid == null) { continue; }
if (tid in threads) {
const have = threads [tid]
if (merge (have, posts)) { changed = true; }
} else {
threads [tid] = posts
changed = true
}
delete threads [id]
}
}
if (changed) {
lspf = JSON.stringify (lspf)
localStorage.postFilter = lspf
$(document).trigger('filter_page')
}
}
fixids ()
$(document).on('filter_page', fixids)

// post-filter.js
const removepost = function (boardId, threadId, postId) {
const list = JSON.parse (localStorage.postFilter)
const filter = list.postFilter

// thread already pruned
if (typeof filter[boardId] == 'undefined' || typeof filter[boardId][threadId] == 'undefined') return;

for (var i=0; i<filter[boardId][threadId].length; i++) {
if (filter[boardId][threadId][i].post == postId) {
filter[boardId][threadId].splice(i, 1);
break;
}
}

if ($.isEmptyObject(filter[boardId][threadId])) {
delete filter[boardId][threadId];
delete list.nextPurge[boardId][threadId];

if ($.isEmptyObject(filter[boardId])) {
delete filter[boardId];
delete list.nextPurge[boardId];
}
}

localStorage.postFilter = JSON.stringify (list)
}

Menu.onclick(function (e, $buffer) {
var ele = e.target.parentElement.parentElement;
var $ele = $(ele);
var threadId = $ele.parents('.thread').attr('id').replace('thread_', '');
var boardId = $ele.parent().data('board');
var postId = $ele.find('.post_no').not('[id]').text();

if ($ele.data('hidden')) {
$buffer.find('#filter-menu-unhide').click(function () {
removepost(boardId, threadId, postId)
$(document).trigger('filter_page')
})
}
})
})()
[/code]

The price is that the page is filtered twice, once with the wrong pcN IDs from post-filter.js, then with the correct thread IDs. The backend fix >>>/leftypol_archive/1822 doesn't have this double filtering.

Options -> User JS quick reply spoiler fix >>>/leftypol_archive/1801
Options -> User JS catalog links in div.boardlist >>>/leftypol_archive/1803
Options -> User JS thread stats and Unique IPs >>6744
>>

 No.6755

File: 1612309139952.txt ( 3.73 KB , userjs.txt )

A combined Options -> User JS with the four >>6753 fixes.
>>

 No.6756

What might it take to setup a leftypol PeerTube instance?
>>

 No.6763

>>6748
Due to their aggressive attitude, I request the first 'message' line be replaced with the second 'message' line. This will lower the change of them recognizing we're fucking with them personally.
>>

 No.6764

File: 1612359900997.png ( 49.98 KB , 877x363 , 1.png )

EASILY SOLVABLE
1. please order "OPTIONS -> [THEME SELECTION]" in ABC order.

2. please create a fuggen suboption menu for it. Currently, it's picrel, which is autistic. (picrel)
At the very least add some kind of "select look" or sg like that, lmao.

3. Please fix "Gentoo" outlook the following way:
how come the fucking hovering over message box is fucking semi-transparent? I can't see shit, because of the transparency effect. PLZ figgs!
>>

 No.6765

>>6764
$('#style-select > select').css ({float: "none"}).before ('Select theme: ')
>>

 No.6769

>>6764
Sorted, labeled style selector:

[code]
(() => {
$('#style-select > select').before ('Select theme: ')
const selected = $('div.styles > a.selected').attr ('id').replace ('style-select-', '')
const stylesSelect = $('<select></select>').css ({float: "none"})

Array.from (document.querySelectorAll ('div.styles > a')).map (a => [a.innerText.replace (/^\[(.+)\]$/, '$1'), a.getAttribute ('id').replace ('style-select-', '')]).sort ((a, b) => {
const au = a [0].toUpperCase ()
const bu = b [0].toUpperCase ()
if (au < bu) { return -1; }
if (au > bu) { return 1; }
return 0
}).forEach (([name, id]) => {
const opt = $('<option></option>').html(name).val(id)
if (id === selected) { opt.attr('selected', true); }
stylesSelect.append(opt)
})

$('#style-select > select').detach ()
stylesSelect.change(function() {
$('#style-select-' + $(this).val()).click()
})
$('#style-select').append (stylesSelect)
})()
[/code]
>>

 No.6770

File: 1612411260062.txt ( 4.67 KB , userjs.txt )

A combined Options -> User JS with the five >>6753 >>6769 fixes.
>>

 No.6771

>>6748
Rewriting to make it unambiguous.

I literally cannot make it any easier, devs. It takes ten seconds to find the file and five seconds to copy and paste and save. It will save the janitors a ton of effort over the next few years.

Append this to instance-config.php (if you actually added the version above, remove it first!)
//———————————————

$config['filters'][] = array(
'condition' => array(
// basic filter for copypasta anorectal spam
'OP' => true,
'subject' => '/Address rampant anorectal violence/' //checks subject only
),
'action' => 'reject',
'message' => 'Due to increased spam from /pol/, you must copy and paste the opening line of The Communist Manifesto into the email field in order to post.<br><br>Email [email protected] if you are unable to post.'
);

$config['filters'][] = array(
'condition' => array(
// special filter for anorectal spam
'OP' => true,
'custom' => function($post) {
// known filename
if ($post['has_file'] && preg_match("/TAKE ACTION v/", $post['files'][0]['filename']) ||
(preg_match("/Blatantly injurious/", $post['body'])) ||
(preg_match("/ustice.*paste/", $post['body'])) ){
sleep(10); # add a delay :^)
return true; # perform the action below
}
return false;
}
),
'action' => 'reject',
'message' => 'Due to increased spam from /pol/, you must copy and paste the opening line of The Communist Manifesto into the email field in order to post.<br><br>Email [email protected] if you are unable to post.'
);
>>

 No.6772

>>6756
Not a dev but:
>a decision to do so
>an hour or so to properly set up (unless it fucks up)
>occasional maintenance if users can upload
I think it would be cool but why do you want it? What benefit would a leftypol instance have that other socialist instances don't offer?
>>

 No.6773

Backend version of sorted, labeled style selector. >>6769
>>

 No.6796

In addition to image.html hardcoding the initial loop parameter to 1 >>>/leftypol_archive/1535 in the non-JS case, with JS on expand-video.js also loops by default ignoring the loop=[01] in the player.php href.
https://github.com/towards-a-new-leftypol/leftypol_lainchan/blob/b962f6a1283b33c6dc54c78cf4545adf92e21ce9/js/expand-video.js#L16
> var loop = true;
> loopControls[1].style.fontWeight = "bold";

In the JS case with expand-video.js the initial state can be switched to [play once] on all videos in a page with:

$('p.fileinfo > span').filter ((k, e) => e.innerText === '[play once]').each ((k, e) => e.click ())

Unfortunately the loopControls spans aren't tagged with a class.
>>

 No.6798

File: 1612533367083.gif ( 772.08 KB , 228x170 , ren-anxious.gif )

Is the leftypol_lainchan github maintainer okay? ~1 week has gone and they haven't updated for the fix to the Google leak.
>>6727
>>6730
https://github.com/towards-a-new-leftypol/leftypol_lainchan/pull/224
>>

 No.6800

>>

 No.6801

fix this already, pretty please
>>

 No.6803

File: 1612568307155.png ( 76.05 KB , 444x216 , Untitled.png )

>>6801
Your browser seems to be the one with the subpar handling of word-wrap:break-word, Képernyőkép dude. >>>/leftypol/69820 The break-word will not be removed, it's there for a reason. However what you can do is inspect that post, go up to div.body, locate the "div.post div.body" rules, uncheck word-wrap:break-word and post the resulting layout.
>>

 No.6814

hiding individual posts doesn't seem to work, also, there's a lag when the hidden OPs show up in the catalog view like i can see it for half a second and then it disappears
>>

 No.6816

Batch loop/once setting >>6796 for Options -> WebM, manual mode only for now.

Test thread: >>>/leftypol/32459
>>

 No.6818

Batch loop/once setting >>6796 for Options -> WebM, with auto set on page load but not on new posts yet.

Test thread: >>>/leftypol/32459
>>

 No.6819

File: 1612658459705.txt ( 2.21 KB , videoloop.txt )

Batch loop/once setting >>6796 >>>/leftypol_archive/1521 in Options -> WebM, with auto set on page load and on new posts. Test thread: >>>/leftypol/32459 The issue with new posts was that expand-video.js uses a MutationObserver instead of reacting to the 'new_post' event like everyone else, and the observer runs after the 'new_post' trigger.
https://github.com/towards-a-new-leftypol/leftypol_lainchan/blob/b962f6a1283b33c6dc54c78cf4545adf92e21ce9/js/expand-video.js#L230

Options -> User JS quick reply spoiler fix >>>/leftypol_archive/1801
Options -> User JS catalog links in div.boardlist >>>/leftypol_archive/1803
Options -> User JS thread stats and Unique IPs fix >>6744
Options -> User JS individual post hiding >>6753
Options -> User JS sorted, labeled style selector >>6769
>>

 No.6820

File: 1612660369892.txt ( 6.91 KB , userjs.txt )

A combined Options -> User JS with the six >>6819 fixes.
>>

 No.6824

File: 1612757728808.png ( 148.76 KB , 400x416 , 318c1999bf9d690a6af5324fac….png )

>>6814
HELLLO. JANNIES. WHY CANT I HIDE INDIVIDUAL POSTS? ONLY THE OP OF THREADS WHICH IS GREAT BUT I WANT TO HIDE INDIVIDUAL POSTS IN THREADS
>>

 No.6835

Go to top/bottom navlinks in the top bar for Options -> User JS:

[code]
(() => {
const makea = (href, title, html) => {
const a = document.createElement ("a")
a.setAttribute ('href', href)
a.setAttribute ('title', title)
a.innerHTML = html
return a
}
if (document.querySelectorAll ('div.post.op').length == 1) {
const span = document.createElement ("span")
span.setAttribute ('id', 'topbottom-boardlist-navlinks')
span.setAttribute ('style', 'float:right;')
span.appendChild (makea ('#top', 'Go to top', '&#x25B2;'))
span.appendChild (makea ('#bottom', 'Go to bottom', '&#x25BC;'))
document.querySelector ('div.boardlist').appendChild (span)
}
})()
[/code]
>>

 No.6837

File: 1612796305571.txt ( 7.56 KB , userjs.txt )

A combined Options -> User JS with the seven >>6819 >>6835 fixes.
>>

 No.6841

File: 1612845805652.jpg ( 608.72 KB , 1500x1960 , 3b6eea79dbce5c4d42443f9089….jpg )

1. Spoiler and deleted thumbs work in the catalog after >>>/leftypol_archive/1446 #124, but generic thumbs fail with e.g. src="/tech/thumb/file".
- spoiler: https://leftypol.org/meta/catalog.html spoiler OP test
- deleted: https://leftypol.org/meta/catalog.html Posting doesn't work for me
- generic: https://leftypol.org/tech/catalog.html Kickstarter workers unionize
The reason is that generateRecentPosts doesn't have any 'file' thumb handling:
https://github.com/towards-a-new-leftypol/leftypol_lainchan/blob/b962f6a1283b33c6dc54c78cf4545adf92e21ce9/templates/themes/catalog/theme.php#L421
To add 'file' thumb handling, fix the fallback loop and add a missing 'else' branch:

[code]
if (isset($post['files']) && $post['files']) {
$files = json_decode($post['files']);

if (isset($files[0]) && $files[0]) {
$foundone = false;
foreach ($files as $file) {
if ($file->file != 'deleted') {
$post['file'] = $this->filepathForThumb($file->thumb, $file->file);
$foundone = true;
break;
}
}
if (!$foundone) {
$post['file'] = $this->filepathForThumb('deleted', null);
}
} else {
$post['file'] = $this->filepathForThumb('deleted', null);
}
} else {
$post['file'] = $this->filepathForThumb('deleted', null);
}
[/code]

Before or after generateRecentPosts:

[code]
private function filepathForThumb($thumb_or_special, $path_when_file) {
global $config;

if ($thumb_or_special === 'deleted') {
return $config['root'] . $config['image_deleted'];
} else if ($thumb_or_special === 'spoiler') {
return $config['root'] . $config['spoiler_image'];
} else if ($thumb_or_special === 'file') {
// see twig_extension_filter
$ext = mb_strtolower(mb_substr($path_when_file, mb_strrpos($path_when_file, '.') + 1));
$icons = $config['file_icons'];
// see templates/post/image.html
if (isset($icons[$ext])) {
return $config['root'] . sprintf($config['file_thumb'], $icons[$ext]);
} else {
return $config['root'] . sprintf($config['file_thumb'], $icons['default']);
}
} else {
return $config['uri_thumb'] . $thumb_or_special;
}
}
[/code]

2. There is what might be considered an UI bug, whereby the banner images cause a layout shift on nearly every page load. To fix it:
https://github.com/towards-a-new-leftypol/leftypol_lainchan/blob/b962f6a1283b33c6dc54c78cf4545adf92e21ce9/inc/config.php#L937
> // Banner dimensions are also optional. As the banner loads after the rest of the page, everything may be
> // shifted down a few pixels when it does. Making the banner a fixed size will prevent this.
> // $config['banner_width'] = 300;
> // $config['banner_height'] = 100;
https://github.com/towards-a-new-leftypol/leftypol_lainchan/blob/b962f6a1283b33c6dc54c78cf4545adf92e21ce9/inc/instance-config.php#L105

3. The "sticky bug in the overboard catalog view" #229 is caused by this line:
https://github.com/towards-a-new-leftypol/leftypol_lainchan/blob/b962f6a1283b33c6dc54c78cf4545adf92e21ce9/js/catalog.js#L11
> $('#Grid').mixItUp('sort', (value == "random" ? value : "sticky:desc " + value));
To fix it change it to:
> $('#Grid').mixItUp('sort', (((value "random") || (board_name "overboard")) ? value : "sticky:desc " + value));
and update the test if the overboard ever moves from /overboard/.

4. For future reference, should a similar issue come up again, the clone_wrapped_with_exist_check fix to #225
https://github.com/towards-a-new-leftypol/leftypol_lainchan/commit/43a6a67844e2b806945e67cd34c593009f54c460
is almost the same as the @ error suppression from
>>>/leftypol_archive/1839 + >>>/leftypol/63272
and treats the symptoms. The explanation of the cause and of a few other problems with mod_move is still available in the archive.

+ catalog post form for non-JS users >>>/leftypol_archive/1833
+ thread stats and unique IPs fix >>6744
+ sorted, labeled style selector >>6773
in issues but not resolved yet:
+ track order #63 webm error 2 >>>/leftypol_archive/1337 [in PR]
+ arabic characters #125 >>>/leftypol_archive/1517
+ original file name downloads for non-JS users #128 >>>/leftypol_archive/1499
+ txt thumbnails #129 >>>/leftypol_archive/1593 >>>/leftypol_archive/1673
+ reply limit nobump markers in index and thread view #147 >>>/leftypol_archive/1775
+ UTF-8 troubles #193 >>>/leftypol_archive/1738 >>>/leftypol_archive/1744
+ posts below the line and related #207 >>>/leftypol_archive/1759
+ quick reply spoiler #213 >>>/leftypol_archive/1801
+ individual post hiding #215 >>>/leftypol_archive/1822
+ catalog links in div.boardlist #216 >>>/leftypol_archive/1807
+ zombie threads in catalog #219 >>>/leftypol_archive/1691
+ post quote ending an orange quote #220 >>>/leftypol_archive/1722
+ ICC profile error #221 >>>/leftypol_archive/1787
>>

 No.6842

> $('#Grid').mixItUp('sort', (((value == "random") || (board_name == "overboard")) ? value : "sticky:desc " + value));
>>

 No.6843

Client-side fix for generic file thumbs >>6841 in catalog:

Array.from (document.getElementsByTagName ('img')).filter (e => e.hasAttribute ('src')).map (e => [e, e.getAttribute ('src').match (/^\/([^\/]+)\/thumb\/file$/)]).filter (([e, m]) => m != null).forEach (([e, m]) => e.setAttribute ('src', '/static/file.png'))
>>

 No.6847

I cannot post images via mobile. Is there any way around this?
>>

 No.6848

>>6847
Post a screenshot of the error message in >>4951
>>

 No.6849

Is the matrix server down for anyone else?
>>

 No.6851

when do we get 24 hour unique IP calculation?
>>

 No.6859

The "sticky bug in the overboard catalog view" #229 can also be fixed >>6842 manually on the client from the console after the page has finished loading.

[code]
(() => {
const sortby = (value) => { console.log (value); $('#Grid').mixItUp('sort', value); }
if ((active_page 'catalog') && (board_name "overboard")) {
$("#sort_by").change((e) => {
const v = e.target.value
if (v != "random") { sortby (v); }
})
if (localStorage && localStorage.catalog) {
const catalog = JSON.parse(localStorage.catalog)
const sb = catalog.sort_by
if (sb && (sb != "random")) { sortby (sb); }
}
}
})()
[/code]

This version won't work from Options -> User JS because both mixitup and catalog.js run after options/user-js.js.
https://github.com/towards-a-new-leftypol/leftypol_lainchan/blob/b962f6a1283b33c6dc54c78cf4545adf92e21ce9/templates/themes/catalog/theme.php#L465
>>

 No.6860

[code]
(() => {
const sortby = (value) => { console.log (value); $('#Grid').mixItUp('sort', value); }
if ((active_page == 'catalog') && (board_name == "overboard")) {
$("#sort_by").change((e) => {
const v = e.target.value
if (v != "random") { sortby (v); }
})
if (localStorage && localStorage.catalog) {
const catalog = JSON.parse(localStorage.catalog)
const sb = catalog.sort_by
if (sb && (sb != "random")) { sortby (sb); }
}
}
})()
[/code]
>>

 No.6864

File: 1613008585135.txt ( 7.84 KB , userjs.txt )

A combined Options -> User JS with the eight >>6837 >>6843 fixes.
>>

 No.6867

>>6727
>>6730
Why can't towards-a-new-leftypol just log in and merge this?
>>

 No.6871

Options -> User JS fix for "sticky bug in the overboard catalog view" #229 using a mixEnd callback:
https://github.com/patrickkunka/mixitup/blob/v2/docs/events.md

[code]
(() => {
if ((active_page != 'catalog') || (board_name != "overboard")) { return; }
const state = { added: false }
$("#sort_by").change((e) => {
if (state.added) { return; }
state.added = true
$('#Grid').on('mixEnd', (ev, st) => {
as = st.activeSort
if ((typeof as === 'string') && as.startsWith ('sticky:desc ')) {
$('#Grid').mixItUp('sort', as.replace ('sticky:desc ', ""))
}
})
})
})()
[/code]

As with the individual post hiding #215 User JS fix >>6753 the price is double sorting, once with the sticky:desc forced in by catalog.js and again without. The backend fix >>6842 >>6841 doesn't have this double sorting.
>>

 No.6878

> https://github.com/towards-a-new-leftypol/leftypol_lainchan/issues/231
> https://github.com/towards-a-new-leftypol/leftypol_lainchan/issues/230
Github mangled some code portions of the pasted comment.

> https://github.com/towards-a-new-leftypol/leftypol_lainchan/issues/229

The bugfix line in >>6841 point 3 has heading markup interference which ate the two pairs of ==.
The same line with the two pairs of == present is in >>6842.
>>

 No.6880

File: 1613133932787.txt ( 8.29 KB , userjs.txt )

A combined Options -> User JS with the nine fixes.

① quick reply spoiler >>>/leftypol_archive/1801
② catalog links in div.boardlist >>>/leftypol_archive/1803
③ thread stats and Unique IPs >>6744
④ individual post hiding >>6753
⑤ sorted, labeled style selector >>6769
⑥ batch loop/once WebM setting >>6819
⑦ top/bottom navlinks in the top bar >>6835
⑧ generic file thumbs in catalog >>6843
⑨ sticky bug in the overboard catalog >>6871
>>

 No.6881

File: 1613160362672.png ( 15.02 KB , 460x143 , from-speedy-activity-to-no….png )

Can mods confirm that they have heard from the maintainer (towards-a-new-leftypol) within the past two weeks to that they're ok?
Or have Biden's admin taken advantage of Trumps new anti-Antifa laws?
>>

 No.6884

File: 1613212562675.png ( 21.22 KB , 491x258 , 2021،02،13-13:31:42.png )

Using easy privacy list blocks cloudflare scripts that is required to pass the browser verification process. If the cloudflare check loops forever for you this might be the problem.
>>

 No.6890

>>6881
Yes, they are alive and well.
Remember that anyone with an account can start a PR on github and also review PRs.
It also helps to attach patches (as .txt) so that they're easier to apply.
>>

 No.6892

>>6890
>It also helps to attach patches (as .txt) so that they're easier to apply.
>>

 No.6893

>>6890
Please tell them to log in and merge this which was pull-requested half a month ago:
https://github.com/towards-a-new-leftypol/leftypol_lainchan/pull/224
>>

 No.6914

Put a link to the catalog at the top, as well. Right next to go to bottom so it looks like:

[ Return / Go to bottom ] [ Catalog ] [ Home ]
>>

 No.6916

>>6914
Options -> User JS

[code]
(() => {
if (active_page !== "thread") { return; }
const added = ' [ <span class="threadlink"><a href="/' + board_name + '/catalog.html">Catalog</a></span> / <span class="threadlink"><a href="/">Home</a></span> ]'
document.querySelector ('div.threadlinks-noup').innerHTML += added
})()
[/code]
>>

 No.6917

>>6893
Why is this not progressing / getting ignored?
>>

 No.6923

>>6917
will checkin with tech team on this
>>

 No.6928

File: 1613819005764.txt ( 8.71 KB , userjs.txt )

A combined Options -> User JS with the ten >>6880 >>6916 fixes. Added tooltips to div.boardlist catalog links.
>>

 No.6933

To fix >>>/meta/3737
https://github.com/towards-a-new-leftypol/leftypol_lainchan/blob/6885c1977834ec93b594494a963df4ef6c588c71/inc/functions.php#L2199

$code = "<pre class='code lang-$code_lang'>".str_replace(array("\n","\t"), array("&#10;","&#9;"), htmlspecialchars($code, ENT_COMPAT, "UTF-8", false))."</pre>";

https://www.php.net/manual/en/function.htmlspecialchars.php
>>

 No.6934

Frontend workaround until the backend is fixed >>6933

Array.from (document.querySelectorAll ('pre.code')).forEach (e => { e.innerHTML = e.innerHTML.replace (/&amp;(amp|lt|gt);/g, '&$1;'); })
>>

 No.6952

what happened to youtube embed thumbnails?
>>

 No.6953

>>

 No.6974

File: 1614632455671.txt ( 8.87 KB , userjs.txt )

A combined Options -> User JS with the eleven >>6928 >>6934 fixes.
>>

 No.6988

Suggestion: load the [watchlist] element in the site navigation bar faster or earlier, or don't incorporate it in the centered container at all. Your use of Javascript to generate it is causing it to load much later than all the other elements. Alternatively, you could could delay the generation of the entire list until your slow-ass javashit is ready. Tired of misclicking a board I want to visit because the watchlist loads too late and shifts all the entries over!
>>

 No.6991

when are u going to fix the youtube embeds already
>>

 No.6992

>>6991
>>6952
They got disabled because the images were being loaded from youtube and causing a call to the site which impacted tor users especially. Someone could write code to download the images directly on the server from youtube.
Hop on the matrix chat if you're interested.
>>

 No.7012

When you merge threads, moved posts still link back to the old thread that doesn't exist anymore.
This causes a 404 to occur if you click on a post-link in a post from the old thread
>>

 No.7026

Some minor nitpicks/ideas about the onion:

On the index page for the news sections etc, leftypol.org links are not replaced with the onion.

Also with the index page and site in general, it might be worthwhile having a specific filter to replace links that have known onions for when accessing leftypol from its onion. Example would be marxists.org and archive.is both have onions, respectively: http://www.marxist7mbr3mbaj.onion http://archivecaslytosk.onion

Font Awesome and other icon fonts do not display in Tor Browser when its set at the safest security levels. SVGs are disabled as well

Continuing the idea of link replacements, might be good idea for reddit, twitter, and youtube links to be replaced on the fly to respective random instances for their privacy-oriented frontend alternatives: https://codeberg.org/teddit/teddit https://github.com/zedeus/nitter/wiki/Instances https://github.com/iv-org/documentation/blob/master/Invidious-Instances.md All three have onions listed.
>>

 No.7050

>>7026
V2 onions are about to expire.
>>

 No.7066

My suggestion is that we make it so it previews the flag you choose, so we lesson the chance we pick the wrong flag and it tells the new anons watch each flag looks like.
>>

 No.7071

>>6890
> It also helps to attach patches (as .txt) so that they're easier to apply.
https://github.com/towards-a-new-leftypol/leftypol_lainchan/pull/238
> Commit the patch from issue 229 #238

Here's #125 >>>/leftypol_archive/1517
>>

 No.7096

would it be complicated to add mathjax support? I want to start more technical thread and simply attaching latex rendered image seems unwieldy.
>>

 No.7106

File: 1616527912107.png ( 555.04 KB , 633x550 , 13a504621c2edd4fe7ec52dcc4….png )

> Failed to resize image! Details: gm convert: iCCP: profile 'ICC Profile': 'RGB ': RGB color space not permitted on grayscale PNG (/tmp/phparr3Fy).
When posting this: https://files.catbox.moe/0mhv8x.png
>>

 No.7107

The https://files.catbox.moe/0mhv8x.png >>7096 image is a type 0 grayscale png with an iCCP chunk holding 'RGB '.
$ identify rgb.png
rgb.png PNG 609x282 609x282+0+0 8-bit sRGB 256c 93.8KB 0.000u 0:00.020
identify-im6.q16: iCCP: profile 'ICC Profile': 'RGB ': RGB color space not permitted on grayscale PNG `rgb.png' @ warning/png.c/MagickPNGWarningHandler/1654.

Without the iCCP the image passes >>>/meta/3992. In IM this is only a warning which doesn't prevent thumbnailing.
$ convert rgb.png -thumbnail 200x200 rgbthumb.png
convert-im6.q16: iCCP: profile 'ICC Profile': 'RGB ': RGB color space not permitted on grayscale PNG `rgb.png' @ warning/png.c/MagickPNGWarningHandler/1654.
$ identify rgbthumb.png 
rgbthumb.png PNG 200x93 200x93+0+0 8-bit sRGB 15.2KB 0.000u 0:00.000

The entire >>>/leftypol_archive/1787 #221 applies with the additional exception string "'RGB ': RGB color space not permitted on grayscale PNG".
>>

 No.7372

test
>>

 No.7374

I wrote a small script to filter posts by flag. just posting it here for personal convenience so I can use it between devices. if you want to add or remove flags, just alter the "blocklist" and add or remove the strings that you see when you hover over the flags in question. might expand on it in the future to make it gui-based.
blocklist = ["Sandinista","Sabo-Tabby"]

x = document.getElementsByClassName("flag")
for(var i = 0; i &lt; x.length; i++) {
  flag = x[i]
  if (blocklist.find(element =&gt; element == flag.title)) {
    flag.parentNode.parentNode.parentNode.style.display = "none";
  }
}
>>

 No.7375

>>7374
does the great-than symbol not work in code?
blocklist = ["Sandinista","Sabo-Tabby"]

x = document.getElementsByClassName("flag")
for(var i = 0; i &amp;lt; x.length; i++) {
  flag = x[i]
  if (blocklist.find(element =&gt; element == flag.title)) {
    flag.parentNode.parentNode.parentNode.style.display = "none";
  }
}
>>

 No.7376

x = document.getElementsByClassName("flag")
for(var i = 0; i < x.length; i++) {
flag = x[i]
if (blocklist.find(element => element == flag.title)) {
flag.parentNode.parentNode.parentNode.style.display = "none";
}
}
>>

 No.7382

>>7375
>does the great-than symbol not work in code?
see >>6933 >>6934
>>

 No.7390

>Could we have one spoiler checkbox per file instead of it spoilering all files in a post?
>Could the Embed field be on the quick reply box too?

Also
>>>4015
>>

 No.7391

>>7390

>>>/meta/4015
>>

 No.7435

>>7390
>Could we have one spoiler checkbox per file instead of it spoilering all files in a post?
The storage model supports this with spoilers consisting of a per-file thumb value of 'spoiler'. >>>/anime/4442 The use of $_POST['spoiler'] in post.php for every file is relatively easy to upgrade.
https://github.com/towards-a-new-leftypol/leftypol_lainchan/blob/1a6b26d2be19e430add273af00a0718fd41ef016/post.php#L1057
But after that every file with posting responsibilities would also have to be upgraded to allow for individual spoilers, among them multi-image.js, file-selector.js and quick-reply.js. This is only likely to be done by someone who needs that feature and regularly uploads multiple images, since multiple image upload is only available to begin with to those who regularly browse with remote code execution enabled.
>>

 No.7464

File: 1617038832156.png ( 134.01 KB , 1002x674 , "no".png )

Is this really necessary?
I doubt it.
>>

 No.7465

Don't make a /mu/ board without fixing youtube embeds first
>>

 No.7521

>>6727
This issue has recurred. It's leaking on catalog, but not in overboard or in-thread. Pretty sure it's still to do with embeds bugging out.
>>

 No.7525

>>6824
>HELLLO. JANNIES. WHY CANT I HIDE INDIVIDUAL POSTS? ONLY THE OP OF THREADS WHICH IS GREAT BUT I WANT TO HIDE INDIVIDUAL POSTS IN THREADS
this still isn't fixed
>>

 No.7526

comrades, jump on the matrix and help out contributing code.

>>7525
there's a [-] button to hide the entire thread.
>>

 No.7528

>>

 No.7632

>>7465
>>6727
<done I think
>>

 No.7633

File: 1617493199624.png ( Spoiler Image, 8.59 KB , 493x402 , rl.png )

>>7632
Hey you
Yeah, you
I love you
<3
>>

 No.7634

Bugged link found (radio link to .ogg as JS?)
>>>/music/1174
>>

 No.7635

>>7634
Works ok for me, you realize it's a stream right?
>>

 No.7637

>>7635
Yes I know it's a stream, I'm saying how it was added to the site caused a bug, similar to >>6727
>>

 No.7641

(but obv has nothing to do with youtube this time)
maybe it's an underlying thing?
>>

 No.7651

Requesting a peertube instance with increased maximum tag count to be used as a booru. The webm thread sucks for searching.
It will also be fun to see how long it takes to get blocked by 'safe space' socialist instances
>>

 No.7683

>>7651
Does PeerTube even allow for more than 5 tags?
>>

 No.7855

>>6724
Is there a way to go to the Cytube on the .onion domain? I just tried butchering the URL together with a tv. prefix and it didn't seem to work. Not really a big deal but it would be preferable to stay on the Tor network than to shoot out an exit node.
>>

 No.7857

>>7855
Well the cytube has embedded content, I don't think that will work over tor anyway, I'm not sure it can be supported.
>>

 No.8225

https://github.com/towards-a-new-leftypol/leftypol_lainchan/issues/251
>Certain Posts with images do not stretch enough to fit the text #251
see >>6801 >>6803
>>

 No.8386

I don't see the update button at bottom of threads anymore ?
>>

 No.8406

>>8386
I do. What are you using?
Do you have any script blockers? Is your window larger than your screen?
>>

 No.8425

test
>>

 No.8772

>>8631
amogus
>>

 No.9096

File: 1623357174519.png ( 4.97 KB , 194x60 , pls-do-this.png )

(reminder)
Pls do this
Thnks
>>

 No.9214

File: 1623700986087.png ( 74.49 KB , 1316x358 , 000.png )

Please fix cytube config to allow direct streaming of videos, change picrel to true and avprobe respectively. Join https://cytu.be/r/ipfstest for an example of how it is supposed to work.
>>

 No.9253

>>6756
PeerTube now supports livestreaming. Even if we don't have our own instance, can cytube support PeerTube streams?
>>

 No.9478

File: 1624214509975.png ( 175.69 KB , 1291x637 , ClipboardImage.png )

Very small thing but that I think would make a better user experience:
Put the title of the thread as the title of the page. It would make having multiple tab open much better.
>>

 No.9481

>>9478
(() =&gt; {
  const ops = document.querySelectorAll ("div.thread &gt; div.op")
  if (ops.length !== 1) return
  const sub = ops [0].querySelector ("span.subject")
  if (!sub) return
  document.title = sub.innerText
})()


>>6933 >>6934
>>

 No.9482

Also:
>// Show thread subject in page title.
>$config['thread_subject_in_title'] = false;
https://github.com/towards-a-new-leftypol/leftypol_lainchan/blob/9acdacbb352f6f515f454f851bab581baef8fe3c/inc/config.php#L929
>>

 No.9483

Update of catalog links in div.boardlist >>>/leftypol_archive/1803 for the new overboards:
Array.from (document.querySelectorAll ("div.boardlist a")).filter (a =&gt; a.hasAttribute ("href")).forEach (a =&gt; {
  const href = a.getAttribute ("href")
  const ma   = href.match (/^(\/[^\/]+\/)(index.html)?$/)
  let   cata = null
  if (ma != null) {
    cata = ma [1] + "catalog.html"
  }
  if (cata != null) {
    const e = document.createElement ("a")
    e.setAttribute ("href", cata)
    e.innerText = "+"
    if (a.hasAttribute ("title")) {
      e.setAttribute ("title", a.getAttribute ("title") + " - Catalog")
    }
    a.insertAdjacentElement ("afterend", e)
  }
})


>>6933 >>6934
>>

 No.9485

Update of
https://github.com/towards-a-new-leftypol/leftypol_lainchan/issues/229
https://github.com/towards-a-new-leftypol/leftypol_lainchan/commit/237ad41fc02dded98ccde72c34ed3f87fb17186d
for the new overboards:
$('#Grid').mixItUp('sort', (((value == "random") || /^(overboard|sfw|alt)$/.test (board_name)) ? value : "sticky:desc " + value));

Or you might add a new JS var to signal an overboard.
>>

 No.9565

>>9478
>>9482
Sorry for not seeing this earlier. Incidentally, I was thinking of doing the same thing yesterday for both user experience and SEO purposes.
>>

 No.9567

>>9565
If, like the rest of us, you find it easy to miss things, see >>7071 for #125.
>>

 No.9568

Maybe the wrong place, but is there a way to make the Kuroba app download the full non compressed image automatically?
>>

 No.9570

>>9568>>4951

>This Thread Has Been Re-appropriated for leftypol.org Usage.

>General thread meant for the discussion of the mobile app for browsing leftypol.org, known as clover.
>>

 No.9591

>>6849
I can't join matrix server
>>

 No.9596

>>9591
Which link? In OP?
Try this one:
https://matrix.to/#/#Leftypol:matrix.org
Once you're in, find the /tech/ chat if you want.
>>

 No.9796

File: 1625394656509.txt ( 7.44 KB , userjs.txt )

Updated Options -> User JS >>6974 for the recent backend changes.
① quick reply spoiler >>>/leftypol_archive/1801
② catalog links in div.boardlist >>9483
③ thread stats and Unique IPs >>6744
④ individual post hiding >>6753
⑤ batch loop/once WebM setting >>6819
⑥ top/bottom navlinks in the top bar >>6835
⑦ generic file thumbs in catalog >>6843
⑧ catalog link above OP >>6916
⑨ code double escaping >>6934
>>

 No.9797

>>9796
One of the devs here, I've got a bit of spare time to solve some of these on the main site now that more critical changes are resolved.
Thanks for compiling and sharing these with everyone.
>>

 No.9808

File: 1625408005880.jpg ( 1.84 MB , 1497x2500 , 1128b1276672288a8725beaf4d….jpg )

>>9797
>One of the devs here, I've got a bit of spare time to solve some of these on the main site now that more critical changes are resolved.
Thanks for all your hard work.

>Thanks for compiling and sharing these with everyone.

No problem. The last list for the backend is in >>6841. The backend-relevant updates since that list:
+ code double escaping >>6933
+ issue-125-patch.txt >>7071
+ more iCCP profiles >>7107
+ embed in quick reply >>7392
+ sticky overboard catalog sort >>9485
>>

 No.9816

please pay attention to meeeeeeee
>>

 No.9820

Is it just me or are all the new image thumbnails broken over the last day or so?
>>

 No.9823

>>9820
Oh, I see what happened… Whose cocksucking idea was it to start making all thumbnails in Google's gay webp format?
>>

 No.9824

>>9823
What browser are you using that can't handle them, mobile app?
>>

 No.9825

>>9824
An older version of Palemoon. It's not easy for me to upgrade at the moment.
>>

 No.9828

>>9825
Now I'm curious, you must be on a five+ year old version. Is there a breaking change? Increased requirements? Excessive customization?
>>

 No.9829

>>9828
They adopted some new library versions a few months ago that aren't supported on *buntu 16.04. The PPA maintainer seems to have given up supporting that version and it doesn't seem like this will be easy to fix without a full system upgrade (a much more complicated problem for me).
>>

 No.9830

File: 1625436363651.png ( 15.78 KB , 729x90 , ooboontoo.png )

>>9829
>a few
I wouldn't call over 260 months a few, v26 introduced support. 16.04 LTS is EOL, so I suspect an OS upgrade is increasingly inevitable when things start breaking. Have you tried a live USB on the newer LTS versions?
>>

 No.9831

>>9830
lol typo with the months, meant 60
>>

 No.9836

File: 1625443500796.jpg ( 1.67 MB , 1552x2500 , 38dce130c3f5aff33b2a66f4fc….jpg )

To fix >>>/meta/7789 stripped file sizes for the current strip_exif+use_exiftool case:
https://github.com/towards-a-new-leftypol/leftypol_lainchan/blob/e0ff3ea33ba2c81acdeb6bbc1b72ecd15a734be2/post.php#L1094
if($error = shell_exec_error('exiftool -overwrite_original -ignoreMinorErrors -q -q -all= ' .
    escapeshellarg($file['tmp_name']))) {
    error(_('Could not strip EXIF metadata!'), null, $error);
} else {
    clearstatcache(true, $file['tmp_name']);
    if (($newfilesize = filesize($file['tmp_name'])) !== false)
        $file['size'] = $newfilesize;
}

More generally, operations that modify an uploaded file should reread its size.

https://www.php.net/manual/en/function.filesize.php
https://www.php.net/manual/en/function.clearstatcache.php
>>

 No.9851

>>9830
>v26 introduced support
Oh? I'm on v28.15 and webp rendering is broken.
>>

 No.9854

Maybe add some escaping >>>/leftypol_archive/1499 to #289 for >>>/meta/7808.
>>

 No.9874

File: 1625529717281.png ( 476.67 KB , 1234x705 , ClipboardImage.png )

>>9851
Huh, that's weird then, my mistake. There have been no WebP fixes since 28.15 in updates, and support was introduced in 26.
I downloaded the 29.2.1 binary for 16.04 *ubuntu and it works, but I realize that is different.
Do either of these work?
https://developers.google.com/speed/webp/gallery1
https://developers.google.com/speed/webp/gallery2
(If you don't want to touch Google, you can search for some in https://commons.wikimedia.org/w/index.php?search=webp instead)
>>

 No.9891

>>9874
If you don't want to or can't fix the webp thumbnails, go to Options (top right) and add this script to User JS to replace the thumbnails with the source image.

ext = ["png","jpg", "jpeg"];
$('.file').each(function(file){
   children = this.childNodes;
   if (this.tagName == 'DIV'){
   link=this.childNodes[1].href;
   if (ext.includes(link.split('.').pop())){
      this.getElementsByClassName('post-image')[0].src = link;
   }}
});


(If someone wants to critique this skiddie hack, please do.)
>>

 No.9952

saving as original filename suddenly doesn't work
tried two browsers on both desktop and mobile
>>

 No.9960

Any chance kuroba can work with Orbot?
>>

 No.9965

>>9952
Thanks for reporting.
We did a change last week that deferred JS loading, which broke a few features (hopefully fixed this weekend).
At the same time, I replaced the download with filename funciton with one that didn't need Javascript, but the way I did it was flawed and lead to a security issue so we quickly removed it.
So we're currently running the deferred one that appears to be broken until enough devs are online to push the new changes.
>>

 No.10011

New update. [code­] tags double-encoding should be fixed.

<test> && "test"
>>

 No.10013

Javascript is fucked, backlinks don't work, the autoupdate and the [Watch Thread] appear twice.
>>

 No.10014

>>10013
The dev team is aware and a fix should be deployed soon.
>>

 No.10015

File: 1626019078747.png ( 19.33 KB , 488x367 , Screenshot_2021-07-11_17-5….png )

>>10014
Also whatever the fuck is going on here
>>

 No.10016

>>10015
Yeah, that was trippy. On the dev env I got a quad one once.
Issue should now be resolved.
>>

 No.10018

>>9891
Here's a better version:
[code]
$('div.file a').each(function(index){
let ext = ["png","jpg", "jpeg"];
let file = $(this);
let fullSize = file.prop("href");
if (ext.includes(fullSize.split('.').pop())){
file.find('img').prop("src", fullSize);
}
});
[\code]
the indentation is fucked.
>>

 No.10079

File: 1626124420264.png ( 17.04 KB , 317x398 , Screenshot_2021-07-12_23-1….png )

>>10016
Still happens if you open the thread to reply, like using this link: https://leftypol.org/tech/res/6724.html#q10016
>>

 No.10082

>>9986
wtf it does work, nvm
>>

 No.10101

File: 1626170899444.txt ( 6.83 KB , userjs.txt )

Trimmed Options -> User JS >>9796 for the recent backend changes.
① catalog links in div.boardlist >>9483
② thread stats and Unique IPs >>6744
③ individual post hiding >>6753
④ batch loop/once WebM setting >>6819
⑤ top/bottom navlinks in the top bar >>6835
⑥ generic file thumbs in catalog >>6843
⑦ catalog link above OP >>6916
>>

 No.10109

const callonce_factory = target => {
  let   called = false
  const once   = (...args) => {
    if (!called) {
      called = true
      return target (...args)
    }
  }
  return once
}

const init_file_selector_once = callonce_factory (init_file_selector)

This way init_file_selector_once can be harmlessly and cheaply called any number of times, such as at $(document).ready and before show_quick_reply → clone, without losing any defer-like benefits for the most common case where there's no q fragment.
>>

 No.10226

>>6724
is there a way to automatically decline all legitimate interests cookies from all websites using firefox
>>

 No.10247

When the reply count drops and a recent version of the thread is available, the ids can be diffed:
>> Array.from (document.querySelectorAll ("div.post.reply")).map (e => e.getAttribute ('id').replace (/^reply_/, '')).join (' ')
"6727 ..."

$ diffids () { diff <(echo "$1" | tr ' ' '\n') <(echo "$2" | tr ' ' '\n'); }
$ diffids "6727 ..." "6727 ..."
140d139
< 9986
151a151
> 10226

In this case the logs https://leftypol.org/log.php?board=tech suggest 9986 was probably in "Deleted all posts by IP address".
>>

 No.10389

File: 1626955991857.jpg ( 1.7 MB , 1493x2500 , 04c5e4db7974bb4ced191f0085….jpg )

Duplicate scripts:
>> console.log (Object.entries (Array.from (document.getElementsByTagName ("script")).filter (e => e.hasAttribute ("src")).map (e => e.getAttribute ("src")).reduce ((acc, name, idx) => {
  if (name in acc) {
    acc [name].push (idx)
  } else {
    acc [name] = [idx]
  }
  return acc
}, {})).filter (([name, indices]) => indices.length > 1).sort ((a, b) => a [1] [0] - b [1] [0]).map (([name, indices]) => name + ' ' + indices.toString ()).join ('\n'))

/js/jquery.min.js 1,3
/js/inline-expanding.js 2,5

The source of the problem is this duplication:
https://github.com/towards-a-new-leftypol/leftypol_lainchan/blob/5faa622303f7a3d9568a7c89728c3095a664b5c7/inc/config.php#L1039
https://github.com/towards-a-new-leftypol/leftypol_lainchan/blob/5faa622303f7a3d9568a7c89728c3095a664b5c7/inc/instance-config.php#L375

While two jquery.min only slow page loads, the two inline-expanding cause double registration of listeners and duplicate options gui spans.
>>

 No.10393

https://github.com/towards-a-new-leftypol/leftypol_lainchan/issues/326
> Multiple features in the Options dialog appear to be broken #326
> The first three options appear to have no effect and do not store a value when activated.
> Show relative time

After the page has loaded with scripts enabled the inspector can be used to verify that '#show-relative-time>input' has no listener. The listener is added in:
https://github.com/towards-a-new-leftypol/leftypol_lainchan/blob/5faa622303f7a3d9568a7c89728c3095a664b5c7/js/local-time.js#L91
> $('#show-relative-time>input').on('change', function() {

Setting a breakpoint on that line shows that $('#show-relative-time>input') is empty, so the listener is added to nothing. One way to fix this is to retrieve the input from the options tab instead of the document:
< Options.get_tab ('general').content.find ('#show-relative-time>input').on('change', function() {

I don't know whether this is the only fix needed for js/local-time.js, that needs to be retested after the listener is in place. A similar consideration applies to every script that has been moved from onready to $(document).ready and attempts to retrieve options gui elements after an Options.extend_tab or equivalent call.

https://github.com/towards-a-new-leftypol/leftypol_lainchan/commit/88f6088a429fb73d12805377b43c6b567d03a5db
> fix Relative Time and Image Throttler
> Author: marktaiwan <[email protected]>
> Date: Fri Jan 23 00:03:37 2015 +0800
> marktaiwan authored and czaks committed May 5, 2016
>>

 No.10407

The reason $('#show-relative-time>input') is empty >>10393 is that js/options.js adds its gui to the document at $(document).ready
https://github.com/towards-a-new-leftypol/leftypol_lainchan/blob/5faa622303f7a3d9568a7c89728c3095a664b5c7/js/options.js#L105
but it's placed after some scripts that add options in $config['additional_javascript']
https://github.com/towards-a-new-leftypol/leftypol_lainchan/blob/5faa622303f7a3d9568a7c89728c3095a664b5c7/inc/instance-config.php#L393

The only reason js/local-time.js and its friends even find a general tab is that js/options/general.js adds it at the time of head:
https://github.com/towards-a-new-leftypol/leftypol_lainchan/blob/5faa622303f7a3d9568a7c89728c3095a664b5c7/js/options/general.js#L15
>>

 No.10414

https://github.com/towards-a-new-leftypol/leftypol_lainchan/issues/326
> Number of simultaneous image downloads (0 to disable):

Besides being run >>10389 twice, js/inline-expanding.js has the same no listener issue >>10393 because $('#inline-expand-max input') is empty.
https://github.com/towards-a-new-leftypol/leftypol_lainchan/blob/5faa622303f7a3d9568a7c89728c3095a664b5c7/js/inline-expanding.js#L196

One fix is:
< Options.get_tab ('general').content.find ('#inline-expand-max input')
>>

 No.10420

File: 1627085901011.jpg ( 589.83 KB , 720x1080 , 9a51c2ff7b9880dbcfd1af649e….jpg )

When testing with scripts enabled, cloudflare's injected malware can be avoided by blocking leftypol URLs that contain "/cdn-cgi/".
>>

 No.10421

Thanks for all your help with this, I'm going to start making pull requests over the next few days.

>>10109
Would you consider this an ideal solution for us to implement, or a simple and effective bandaid for our short-sighted $(document).ready changes? Is it sane for us to be in this situation where it is called multiple times?
>>

 No.10429

>>10421
That is certainly only a quickfix, but a very cheap one because after the first call it will immediately return on a boolean test. A more pleasing long-term solution would be for the rememberStuff chain not to trigger 'cite' that early and instead cause a delayed 'cite' to fire at the time of $(document).ready, but this way great care must be taken with the order in which things run.

Also, I see that PR #330 has been approved and its commit merged but the duplicate scripts are still being served as I'm writing this.
>>

 No.10430

>>10429
>>10429
We're testing bb. One sec.
>>

 No.10432

File: 1627126740264.png ( 9.16 KB , 454x122 , ClipboardImage.png )

>>10429
>Also, I see that PR #330 has been approved and its commit merged but the duplicate scripts are still being served as I'm writing this.
Yep, merge happens before final testing, before they go live.
I can confirm it removes the duplication in the Option form.

>That is certainly only a quickfix [snip]

I'm happy with adding that, it's safe and effective. I might aim for the better solution once we've finished fixing the fallout from our speed-improvement changes and downstreaming the years of vichan changes that lainchan ignored.
>>

 No.10441

File: 1627172785657.jpg ( 566.96 KB , 1605x2500 , 0f92b8330c700a6981361b0b7d….jpg )

https://github.com/towards-a-new-leftypol/leftypol_lainchan/issues/318
> Syncronize spoiler button state on quick reply and main post form #318

While looking into this the synchronization section of js/quick-reply.js turns out to be quite peculiar.
https://github.com/towards-a-new-leftypol/leftypol_lainchan/blob/dcf92dfef5f5ab900dc879d7dc4c3f04fab0aed8/js/quick-reply.js#L296
Every time the quick reply form is manually closed and reopened, new listeners are installed on the forms and the window, but the old listeners are never cleared in the .close-btn click handler.
https://github.com/towards-a-new-leftypol/leftypol_lainchan/blob/dcf92dfef5f5ab900dc879d7dc4c3f04fab0aed8/js/quick-reply.js#L348

The effect can be observed on elements of the top form, such as the textarea, for which the inspector shows an ever increasing number of listeners. These keep the closed quick reply forms reachable and therefore uncollectable, leaking memory. They also transfer the top form text to each closed quick reply form, and after a sufficient number typing becomes sluggish. The .close-btn click handler should deregister all listeners that only served the closed quick reply form.

Keeping in mind that the current way of doing things leaks memory with abandon, the spoiler checkboxes can be synced by extending the synchronization section:
const cbsync = (form1, form2, name) => {
  const sel = 'input[type="checkbox"][name="' + name + '"]'
  const cb1 = form1.find (sel)
  const cb2 = form2.find (sel)
  cb1.on ('change', () => {
    cb2.prop ('checked', cb1.prop ('checked'))
  })
  cb2.on ('change', () => {
    cb1.prop ('checked', cb2.prop ('checked'))
  })
}

cbsync ($origPostForm, $postForm, 'spoiler')
>>

 No.10444

>>10441
>Every time the quick reply form is manually closed and reopened, new listeners are installed on the forms and the window, but the old listeners are never cleared in the .close-btn click handler.
It looks adding these two .off() lines fixes it correctly:
		$postForm.find('th .close-btn').click(function() {
			// Remove origPostForm listeners
			$origPostForm.find('textarea[name="body"]').off('change input propertychange focus');
			$origPostForm.find('input[type="text"],select').off('change input propertychange');
>>

 No.10455

>>10444
That removes every listener for those events from those elements, not just those added by js/quick-reply.js. Currently there are no others, but in the future that might remove some other script's listeners. To avoid this the events can be namespaced with something like .quickreply in both on and off calls.
https://api.jquery.com/on/#event-names

> > new listeners are installed on the forms and the window >>10441

This scroll handler on the window
https://github.com/towards-a-new-leftypol/leftypol_lainchan/blob/dcf92dfef5f5ab900dc879d7dc4c3f04fab0aed8/js/quick-reply.js#L363
needs to be deregistered as well on .close-btn click because it keeps the old $postForm reachable and therefore uncollectable.

This stylesheet handler on the window
https://github.com/towards-a-new-leftypol/leftypol_lainchan/blob/dcf92dfef5f5ab900dc879d7dc4c3f04fab0aed8/js/quick-reply.js#L375
needs the same treatment. Alternatively it could be added exactly once >>10109, since it doesn't depend on the $postForm, but in that case it must be moved out of show_quick_reply.

This 'quick-reply' handler on the window
https://github.com/towards-a-new-leftypol/leftypol_lainchan/blob/dcf92dfef5f5ab900dc879d7dc4c3f04fab0aed8/js/quick-reply.js#L414
is added anew on every floating_link call, which occurs on every .close-btn click. It needs to be added at most once. >>10109
>>

 No.10478

Here's a cleanup_jquery_listeners utility function:
const cleanup_jquery_listeners = cleanupspec => {
  // cleanupspec is a list of triples of
  // [jquery sets, selector or null, event string]
  // example: [
  //   [[$origPostForm, $postForm], 'input[type="text"],select', '.quickreply'],
  //   [[$(window)], null, 'scroll.quickreply']
  // ]
  for (const [sets, sel, events] of cleanupspec) {
    for (let oneset of sets) {
      if (sel != null) {
        oneset = oneset.find (sel)
      }
      oneset.off (events)
    }
  }
}

Then in .close-btn click:
const spec = [
  [[$origPostForm, $postForm], 'textarea[name="body"]', '.quickreply'],
  [[$origPostForm, $postForm], 'input[type="text"],select', '.quickreply'],
  [[$(window)], null, 'scroll.quickreply'],
  [[$postForm], 'th .close-btn', 'click.quickreply'],
]

cleanup_jquery_listeners (spec)

The $postForm is cleaned up for completeness. When spoilers are synced >>10441 they can be added to the spec list in the obvious way. All on calls need .quickreply on their events. The click, focus and scroll calls become .on('click.quickreply', …) and equivalent. The exceptions are the last two listeners of >>10455 which can be dealt with using callonce_factory >>10109.
>>

 No.10486

>>10109
Should the callonce factory be placed in main.js?
>>

 No.10487

For the last two listeners of >>10455, inside the outermost function of js/quick-reply.js:
const stylesheet_handler_once = callonce_factory (() => {
  $(window).on('stylesheet', function() {
    do_css();
    if ($('link#stylesheet').attr('href')) {
      $('link#stylesheet')[0].onload = do_css;
    }
  });
})

const qr_handler_once = callonce_factory (() => {
  $(window).on('quick-reply', function() {
    $('.quick-reply-btn').remove();
  });
})

Then replace
https://github.com/towards-a-new-leftypol/leftypol_lainchan/blob/dcf92dfef5f5ab900dc879d7dc4c3f04fab0aed8/js/quick-reply.js#L375
with
< stylesheet_handler_once ()
and replace
https://github.com/towards-a-new-leftypol/leftypol_lainchan/blob/dcf92dfef5f5ab900dc879d7dc4c3f04fab0aed8/js/quick-reply.js#L414
with
< qr_handler_once ()

>>10486
Either templates/main.js or a new js/functools.js, entirely at the techs' option.
>>

 No.10493

File: 1627341198437.jpg ( 5.79 MB , 3002x4988 , ebe6d32cbc673dba0151ea29f3….jpg )

Temporary fix for spoiler sync #318 in Options -> User JS until the backend is fixed >>10441.
(() => {
  const cbsync = (form1, form2, name) => {
    const sel = 'input[type="checkbox"][name="' + name + '"]'
    const cb1 = form1.find (sel)
    const cb2 = form2.find (sel)
    cb1.on ('change', () => {
      cb2.prop ('checked', cb1.prop ('checked'))
    })
    cb2.on ('change', () => {
      cb1.prop ('checked', cb2.prop ('checked'))
    })
  }
  const spoilersync = () => {
    const ftop   = $('form[name="post"]:first')
    const fquick = $('#quick-reply')
    if ((ftop.length != 1) || (fquick.length != 1)) { return; }
    if (fquick.attr ('data-userjs-spoilersync') == 'spoilersync') { return; }
    cbsync (ftop, fquick, 'spoiler')
    fquick.attr ('data-userjs-spoilersync', 'spoilersync')
  }
  spoilersync ()
  $(window).on('quick-reply', spoilersync)
})()
>>

 No.10494

Temporary fix for >>10079 in Options -> User JS until the backend is fixed >>10104 >>10109.
$('#quick-reply input[name="file"]').remove ()
>>

 No.10495

File: 1627344936998.txt ( 7.71 KB , userjs.txt )

Updated Options -> User JS >>10101 with the current fixes.
① catalog links in div.boardlist >>9483
② thread stats and Unique IPs >>6744
③ individual post hiding >>6753
④ batch loop/once WebM setting >>6819
⑤ top/bottom navlinks in the top bar >>6835
⑥ generic file thumbs in catalog >>6843
⑦ catalog link above OP >>6916
⑧ quick reply spoiler sync >>10493
⑨ quick reply before init_file_selector >>10494
>>

 No.10656

links like https://leftychan.net/leftypol don't work anymore
>>

 No.10664

>extremely basic pull requests in queue for weeks
>one of three devs remaining
._.
>>

 No.10698

>>10664
Zer0 is aware of the backlog of Pr's
>>

 No.10728

https://github.com/towards-a-new-leftypol/leftypol_lainchan/issues/353
>Embeding can only be done at the top of the page. #353
see >>7392
>>

 No.10767

File: 1630180285468.jpg ( 797.3 KB , 720x1080 , 6ae8c364d9d95333de9fc023e0….jpg )

autism scoredb links, preferring thumbnails to full images.
((getthumb, getfull, getdest, getthumb2, getfull2, realthumb, formatok, speclist, showft) => Array.from (document.querySelectorAll ("div.files > div.file")).map (e => [getthumb (e), getfull (e), getdest (e)]).map (([ethumb, efull, edest]) => [ethumb ? getthumb2 (ethumb) : null, efull ? getfull2 (efull) : null, edest]).map (([thumb, full, edest]) => [thumb, full, edest, thumb && realthumb (thumb) && formatok (thumb), full && formatok (full)]).filter (([thumb, full, edest, thumbok, fullok]) => thumbok || fullok).forEach (([thumb, full, edest, thumbok, fullok]) => {
  const url  = "https://leftypol.org" + (thumbok ? thumb : full)
  const span = document.createElement ("span")
  span.setAttribute ("class", "iqdb")
  span.innerHTML = ' ' + (showft ? ((thumbok ? 'T' : 'F') + ':') : "") + speclist.map (([label, urlfun]) => '<a href="' + urlfun (url) + '" target="_blank">' + label + '</a>').join (' ')
  edest.appendChild (span)
})) (
  e => e.querySelector ("img.post-image"),
  e => e.querySelector ('p.fileinfo a[target="_blank"][href*="/src/"]'),
  e => e.querySelector ("span.details"),
  e => e.getAttribute ("src"),
  e => e.getAttribute ("href"),
  s => /^\/[^\/]+\/thumb\//.test (s),
  s => /[.](jpe?g|png|gif)$/i.test (s),
  [
    ["iqdb", s => "https://iqdb.org/?url=" + s],
 // ["nao",  s => "https://saucenao.com/search.php?db=999&dbmaski=32768&url=" + s]
  ],
  false
)
>>

 No.10768

>>

 No.10769

Forgot to updste >>10767 the site URL, my mistake.
((getthumb, getfull, getdest, getthumb2, getfull2, realthumb, formatok, speclist, showft) => Array.from (document.querySelectorAll ("div.files > div.file")).map (e => [getthumb (e), getfull (e), getdest (e)]).map (([ethumb, efull, edest]) => [ethumb ? getthumb2 (ethumb) : null, efull ? getfull2 (efull) : null, edest]).map (([thumb, full, edest]) => [thumb, full, edest, thumb && realthumb (thumb) && formatok (thumb), full && formatok (full)]).filter (([thumb, full, edest, thumbok, fullok]) => thumbok || fullok).forEach (([thumb, full, edest, thumbok, fullok]) => {
  const url  = "https://leftychan.net" + (thumbok ? thumb : full)
  const span = document.createElement ("span")
  span.setAttribute ("class", "iqdb")
  span.innerHTML = ' ' + (showft ? ((thumbok ? 'T' : 'F') + ':') : "") + speclist.map (([label, urlfun]) => '<a href="' + urlfun (url) + '" target="_blank">' + label + '</a>').join (' ')
  edest.appendChild (span)
})) (
  e => e.querySelector ("img.post-image"),
  e => e.querySelector ('p.fileinfo a[target="_blank"][href*="/src/"]'),
  e => e.querySelector ("span.details"),
  e => e.getAttribute ("src"),
  e => e.getAttribute ("href"),
  s => /^\/[^\/]+\/thumb\//.test (s),
  s => /[.](jpe?g|png|gif)$/i.test (s),
  [
    ["iqdb", s => "https://iqdb.org/?url=" + s],
 // ["nao",  s => "https://saucenao.com/search.php?db=999&dbmaski=32768&url=" + s]
  ],
  false
)
>>

 No.10775

Multiupload with the site's scripts disabled but JS enabled in the dev tools console:
(count => {
  const dest = document.querySelector ("tr#upload > td.upload-area")
  if (dest == null) { return; }
  const have = dest.querySelectorAll ('input[type="file"]').length
  if (have >= count) { return; }
  const make = n => '<br class="file_separator"/><input type="file" name="file' + n + '" id="upload_file' + n + '">'
  const add  = []
  for (let k = 2; k <= count; k++) {
    add.push (make (k))
  }
  dest.innerHTML += add.join ("")
}) (5)
>>

 No.10787

Some harmless fun with the original filenames. If the filename looks like a timestamp from a board download >>6724 the UTC date is provided. If it looks like an md5 hash >>6841 an md5 search link is provided to r34. Other matchers might be added later. This is merely a demo, not anything serious.
((getfilesbody, getorig, decorate, providers) => getfilesbody ().forEach (([files, body]) => {
  const infolist = Array.from (files.querySelectorAll ("div.file")).map ((f, index) => [getorig (f), index + 1]).filter (p => p [0] != null).map (([name, index]) => providers.map (p => p (name, index)).filter (s => s != null)).flat (1)
  if (infolist.length > 0) {
    decorate (body, infolist)
  }
})) (
  () => Array.from (document.querySelectorAll ("div.thread > div.files")).map (f => [f, f.parentNode.querySelector ("div.post.op > div.body")]).concat (Array.from (document.querySelectorAll ("div.post.reply")).map (r => [r.querySelector ("div.files"), r.querySelector ("div.body")]).filter (p => p [0] != null)),
  f => {
    const s = f.querySelector ('span.details > span.postfilename')
    return s == null ? null : (s.hasAttribute ("title") ? s.getAttribute ("title") : s.innerText)
  },
  (body, infolist) => {
    const p = document.createElement ("p")
    p.setAttribute ("class", "miscfilesbodyinfo")
    p.innerHTML = infolist.join ("<br/>")
    if (body.firstChild) {
       body.insertBefore (p, body.firstChild)
       const hr = document.createElement ("hr")
       hr.setAttribute ("style", "clear: none;")
       body.insertBefore (hr, p.nextSibling)
    } else {
       body.appendChild (p)
    }
  },
  [
 // (name, index) => "file " + index + " name " + name,
    (name, index) => {
      const m = name.match (/^([0-9a-fA-F]{32})[.]/)
      if (m == null) { return null; }
      const hash = m [1]
      return "file " + index + " md5 " + hash + ' search <a href="https://rule34.xxx/index.php?page=post&s=list&tags=md5%3a' + hash + '" target="_blank">r34</a>'
    },
    (name, index) => {
      const m = name.match (/^([0-9]{13})[.]/)
      if (m == null) { return null; }
      const time = parseInt (m [1], 10)
      return "file " + index + " timestamp " + time + " date " + new Date (time).toUTCString ()
    }
  ]
)
>>

 No.10799

To move towards per-file spoilers >>7390 >>7435 exemptions for spoiler1 through spoiler9 should be added to $config['spam']['valid_inputs'], to allow per-file booleans to pass through the post form. Their number is simply for consistency with the existing file_url9.
https://github.com/towards-a-new-leftypol/leftypol_lainchan/blob/d5bbcc205d4e357de180fa19d8d20e688d6153cb/inc/config.php#L279

The names of the file fields of the post form are file, file2, file3 and so on.
https://github.com/towards-a-new-leftypol/leftypol_lainchan/blob/d5bbcc205d4e357de180fa19d8d20e688d6153cb/js/file-selector.js#L73

Those names can be used to select corresponding booleans sent by checkboxes, and are available as $_FILES keys when processing uploads. The $key can be temporarily stored in $file, for example as $file['formparametername'].
https://github.com/towards-a-new-leftypol/leftypol_lainchan/blob/d5bbcc205d4e357de180fa19d8d20e688d6153cb/post.php#L793
https://www.php.net/manual/en/features.file-upload.post-method.php

The spoiler test
https://github.com/towards-a-new-leftypol/leftypol_lainchan/blob/d5bbcc205d4e357de180fa19d8d20e688d6153cb/post.php#L1064
can then be modified to check per-file booleans:
< if ($config['spoiler_images'] && isset($_POST[str_replace('file', 'spoiler', $file['formparametername'])])) {
>>

 No.10800

File: 1630800257046.png ( 33.66 KB , 447x420 , image.png )

For per-file spoilers for the non-JS case the way to have multiple file controls can be found in multi-image.js:
https://github.com/towards-a-new-leftypol/leftypol_lainchan/blob/d5bbcc205d4e357de180fa19d8d20e688d6153cb/js/multi-image.js#L21
> var new_file = '<br class="file_separator"/><input type="file" name="file'+(images_len+1)+'" id="upload_file'+(images_len+1)+'">';

The original spoiler checkbox is in post_form.html:
https://github.com/towards-a-new-leftypol/leftypol_lainchan/blob/d5bbcc205d4e357de180fa19d8d20e688d6153cb/templates/post_form.html#L51
> {% if config.spoiler_images %}<div id="spoilercontainer"> <input id="spoiler" name="spoiler" type="checkbox"> <label for="spoiler">{% trans %}Spoiler Image{% endtrans %}</label></div>{% endif %}

as is the file input:
https://github.com/towards-a-new-leftypol/leftypol_lainchan/blob/d5bbcc205d4e357de180fa19d8d20e688d6153cb/templates/post_form.html#L149
> <input type="file" name="file" id="upload_file">

The div#spoilercontainer can be dropped and the file input replaced with:
{% for counter in 1..config.max_images %}
    {% if counter > 1 %}<br class="file_separator"/>{% endif %}
    {% set countersuffix = counter == 1 ? '' : counter %}
    {% if config.spoiler_images %}<span class="spoilercontainer"><input id="spoiler{{ countersuffix }}" name="spoiler{{ countersuffix }}" type="checkbox"><label for="spoiler{{ countersuffix }}">S{{ counter }}</label></span>{% endif %}
    <input type="file" name="file{{ countersuffix }}" id="upload_file{{ countersuffix }}">
{% endfor %}


A sample result is attached. With >>10799 this will enable per-file spoilers for the non-JS case. I'll leave the necessary frontend changes for the JS case to those who regularly browse with remote code execution enabled.
>>

 No.10803

Upgrade of >>10787 to unify handling of the two sites. It'll work on some other vichan-based boards as well if their URLs are added.
(regexlisttest => ((sitespec, providers) => {
  const site = sitespec.find (spec => spec ["test"] ())
  if (!site) { return; }
  const postfiles = site ["postfiles"]
  const original  = site ["original" ]
  const decorate  = site ["decorate" ]
  site ["filesbody"] ().forEach (([files, body]) => {
    const infolist = postfiles (files).map ((f, index) => [original (f), index + 1]).filter (p => p [0] != null).map (([name, index]) => providers.map (p => p (name, index)).filter (s => s != null)).flat (1)
    if (infolist.length > 0) {
      decorate (body, infolist)
    }
  })
}) (
  [
    {
      test:      regexlisttest ([
     // /^https?:\/\//,
        /^https?:\/\/leftychan\.net\//,
        /^https?:\/\/leftypol\.org\//,
      ]),
      filesbody: () => Array.from (document.querySelectorAll ("div.thread > div.files")).map (f => [f, f.parentNode.querySelector ("div.post.op > div.body")]).concat (Array.from (document.querySelectorAll ("div.post.reply")).map (r => [r.querySelector ("div.files"), r.querySelector ("div.body")]).filter (p => p [0] != null)),
      postfiles: files => Array.from (files.querySelectorAll ("div.file")),
      original:  f => {
        let e = f.querySelector ('span.postfilename')
        if (e != null) { return e.hasAttribute ("title") ? e.getAttribute ("title") : e.innerText; }
        e = f.querySelector ('span.details > a[download][title*="original filename"]')
        if (e != null) { return e.getAttribute ("download"); }
        return null
      },
      decorate:  (body, infolist) => {
        const p = document.createElement ("p")
        p.setAttribute ("class", "miscfilesbodyinfo")
        p.innerHTML = infolist.join ("<br/>")
        if (body.firstChild) {
          body.insertBefore (p, body.firstChild)
          const hr = document.createElement ("hr")
          hr.setAttribute ("style", "clear: none;")
          body.insertBefore (hr, p.nextSibling)
        } else {
          body.appendChild (p)
        }
      }
    }
  ], [
 // (name, index) => "file " + index + " name " + name,
    ((link, speclist) => (name, index) => {
      const m = name.match (/^([0-9a-fA-F]{32})[.]/)
      if (m == null) { return null; }
      const hash = m [1]
      return "file " + index + " md5 " + hash + ' search ' + speclist.map (e => link (e ["href"] (hash), e ["name"])).join (" ")
    }) (
      (h, t) => '<a href="' + h + '" target="_blank">' + t + '</a>',
      [
        {
          name: "r3",
          href: s => "https://rule34.xxx/index.php?page=post&s=list&tags=md5%3a" + s,
        }
      ]
    ),
    (name, index) => {
      const m = name.match (/^([0-9]{13})[-.]/)
      if (m == null) { return null; }
      const time = parseInt (m [1], 10)
      return "file " + index + " timestamp " + time + " date " + new Date (time).toUTCString ()
    }
  ]
)) (
  regexlist => () => (url => regexlist.some (rx => rx.test (url))) (document.location.href)
)
>>

 No.10864

A backend version of >>6835.

The thread view's top/bottom targets are #top and #bottom:
https://github.com/towards-a-new-leftypol/leftypol_lainchan/blob/bd737669957948b72deb1ef974f8132a7cd99b13/templates/thread.html#L61
> <span class="threadlink"><a href="#bottom" style="padding-left: 10px"> {% trans %}Go to bottom{% endtrans %}</a> ]</span>
https://github.com/towards-a-new-leftypol/leftypol_lainchan/blob/bd737669957948b72deb1ef974f8132a7cd99b13/templates/thread.html#L73
> <a id="thread-top" href="#top">[{% trans %}Go to top{% endtrans %}]</a>

The corresponding anchors are placed just after the boardlist and just before the end of the body:
https://github.com/towards-a-new-leftypol/leftypol_lainchan/blob/bd737669957948b72deb1ef974f8132a7cd99b13/templates/thread.html#L32
> <a name="top"></a>
https://github.com/towards-a-new-leftypol/leftypol_lainchan/blob/bd737669957948b72deb1ef974f8132a7cd99b13/templates/thread.html#L117
> <a href="#" id="bottom"></a>

The boardlist assembly:
https://github.com/towards-a-new-leftypol/leftypol_lainchan/blob/bd737669957948b72deb1ef974f8132a7cd99b13/inc/display.php#L108
> 'top' => '<div class="boardlist">' . $body . '</div>' . $top,
This could host a span with two links with titles, some classes and some default styling such as floating in style.css. Inclusion could be controlled by a new optional boolean passed to createBoardlist that defaults to false.
// after trim
if ($topbottomlinks) {
    $body .= ' <span class="topbottomlinks"><a href="#top" title="' . _('Go to top') . '">&#x25B2;</a> <a href="#bottom" title="' . _('Go to bottom') . '">&#x25BC;</a></span>';
}

// in style.css
.topbottomlinks {
    ... style to taste ...
}
.topbottomlinks a {
    ... style to taste ...
}


The createBoardlist invocations that would need the new boolean set to true:
https://github.com/towards-a-new-leftypol/leftypol_lainchan/blob/bd737669957948b72deb1ef974f8132a7cd99b13/inc/functions.php#L1405
https://github.com/towards-a-new-leftypol/leftypol_lainchan/blob/bd737669957948b72deb1ef974f8132a7cd99b13/inc/functions.php#L2316
https://github.com/towards-a-new-leftypol/leftypol_lainchan/blob/bd737669957948b72deb1ef974f8132a7cd99b13/inc/functions.php#L2419
https://github.com/towards-a-new-leftypol/leftypol_lainchan/blob/bd737669957948b72deb1ef974f8132a7cd99b13/templates/themes/catalog/theme.php#L460
https://github.com/towards-a-new-leftypol/leftypol_lainchan/blob/bd737669957948b72deb1ef974f8132a7cd99b13/templates/themes/overboards/theme.php#L216

Plenty of views have boardlists, most of them lacking top/bottom anchors:
$ grep -e '{{ *boardlist\.top *}}' -r .
Out of those the ones that matter and need top/bottom anchors are:
templates/index.html
templates/themes/catalog/catalog.html

The unicode arrows used above:
▲=&#x25B2;
▼=&#x25BC;
>>

 No.10877

File: 1633954221730.png ( 113.83 KB , 1059x840 , lcimage.png )

Here's a graph of relative PPD for the last 30 days for thread views.
((pagetest, install, getdata, draw) => {
  const back   = 'rgba(  0,   0,  60, 1)'
  const bars   = 'rgba(  0,   0, 255, 1)'
  const grid   = 'rgba(128, 128, 128, 1)'
  const count  = 30, step = 15
  const width  = count * step
  const height = 100

  if (!pagetest ()) { return; }
  context = install ({
    width:  width,
    height: height,
  })
  if (!context.ok) { return; }

  context.back   = back
  context.bars   = bars
  context.grid   = grid
  context.count  = count
  context.step   = step
  context.width  = width
  context.height = height
  context.data   = getdata (count)
  draw (context)
}) (
  () => (document.getElementById ('uniqueip') != null) && (document.querySelectorAll ('div.post.op').length == 1),
  config => {
    const holder = document.createElement ("div")
    holder.innerHTML = '<div style="text-align: center;"><canvas id="canvasid" width="' + config.width + '" height="' + config.height + '" style="border: 1px solid; z-index: 100; position: relative;">canvas</canvas></div>'
    const target = document.getElementById ('thread-interactions')
    if (target == null) { return { ok: false }; }
    target.parentNode.insertBefore (holder, target)
    const canvas = document.getElementById ('canvasid')

    if (canvas.getContext) {
      const ctx = canvas.getContext ('2d')
      return {
        ok:      true,
        holder:  holder,
        canvas:  canvas,
        context: ctx,
      }
    } else {
      holder.remove ()
      return { ok: false }
    }
  },
  count => {
    const all = Array.from (document.querySelectorAll ('p.intro time')).map (e => e.innerText.match (/^\d{4}-\d{2}-\d{2}/)).filter (m => m != null).reduce ((acc, m) => {
      const key = m [0]
      if (key in acc) {
        acc [key] += 1
      } else {
        acc [key]  = 1
      }
      return acc
    }, {})
    const now = new Date ()
    const key = k => {
      const when = new Date (now)
      when.setUTCDate (now.getUTCDate () - k)
      return when.getUTCFullYear ().toString ().padStart (4, '0') + '-' + (when.getUTCMonth () + 1).toString ().padStart (2, '0') + '-' + when.getUTCDate ().toString ().padStart (2, '0')
    }
    const ret = Array.from ({length: count}, (x, k) => all [key (k)] ?? 0)
    return ret
  },
  context => {
    const ctx   = context.context
    const data  = context.data
    const max   = data.reduce ((a, b) => Math.max (a, b), 0) || 1
    const w     = context.width
    const h     = context.height
    const count = context.count
    const step  = context.step

    ctx.fillStyle = context.back
    ctx.fillRect (0, 0, w, h)

    ctx.strokeStyle = context.grid
    for (let k = 0; k < count; k++) {
      ctx.beginPath ()
      ctx.moveTo (step * k, 0)
      ctx.lineTo (step * k, h)
      ctx.stroke ()
    }

    ctx.fillStyle = context.bars
    for (let k = 0; k < count; k++) {
      const y = data [k] * h / max
      ctx.fillRect (w - step - k * step, h - y, step, y)
    }
  }
)

Just a demo.
>>

 No.10880

File: 1634121485828.png ( 326.99 KB , 3177x800 , lcimage.png )

Here's a plot for catalog views that shows the relative age of threads, growing to the right, the relative reply counts, growing to the top, and the relative bump freshness, active in red, dormant in blue. The sample images are for /tech/, /leftypol/ and /b/.
(tools => ((install, sitespec) => {
  const site   = sitespec.find (spec => spec.pagetest ())
  if (!site) { return; }
  const config = site.getconfig ()
  if (!config.ok) { return; }
  install (config)
  if (!config.ok) { return; }
  site.getdata (config)
  if (!config.ok) {
    config.holder.remove ()
    return
  }
  site.draw (config)
}) (
  config => {
    // > width height insertmode inserttarget
    // < holder canvas context
    const modes = {
      after:  (holder, target) => { target.parentNode.insertBefore (holder, target.nextSibling); },
      before: (holder, target) => { target.parentNode.insertBefore (holder, target); },
      first:  (holder, target) => { target.insertBefore (holder, target.firstChild); },
      last:   (holder, target) => { target.appendChild (holder); },
    }

    const holder = document.createElement ("div")
    holder.innerHTML = '<div style="text-align: center;"><canvas id="canvasid" width="' + config.width + '" height="' + config.height + '" style="border: 1px solid; z-index: 100; position: relative;">canvas</canvas></div>'
    modes [config.insertmode] (holder, config.inserttarget)
    const canvas = document.getElementById ('canvasid')

    if (canvas.getContext) {
      config.holder  = holder
      config.canvas  = canvas
      config.context = canvas.getContext ('2d')
    } else {
      holder.remove ()
      config.ok = false
    }
  }, [{
    pagetest:  () => /^https?:\/\/(leftypol\.org|leftychan\.net)\//.test (document.location.href) && (document.getElementById ('uniqueip') != null) && (document.querySelectorAll ('div.post.op').length == 1),
    getconfig: () => {
      const target = document.getElementById ('thread-interactions')
      if (target == null) { return { ok: false }; }
      const count  = 60, step = 10

      return {
        ok:     true,
        width:  count * step,
        height: 100,
        count:  count,
        step:   step,
        back:   'rgba(  0,   0,  60, 1)',
        bars:   'rgba(  0,   0, 255, 1)',
        grid:   'rgba(128, 128, 128, 1)',
        insertmode:   'before',
        inserttarget: target,
      }
    },
    getdata: config => {
      const dates = Array.from (document.querySelectorAll ('p.intro time')).map (e => e.innerText.match (/^\d{4}-\d{2}-\d{2}/)).filter (m => m != null).map (m => m [0])
      tools.getdatadatecount (config, dates)
    },
    draw: tools.drawrelative
  }, {
    pagetest:  () => /^https?:\/\/(leftypol\.org|leftychan\.net)\//.test (document.location.href) && (document.getElementById ('Grid') != null),
    getconfig: () => {
      const target = document.getElementById ('Grid')
      if (target == null) { return { ok: false }; }

      return {
        ok:     true,
        width:  400,
        height: 400,
        radius: 8,
        insertmode:   'after',
        inserttarget: target,
      }
    },
    getdata: config => {
      const now  = Date.now () / 1000
      const data = Array.from (document.querySelectorAll ('div.mix[data-bump][data-reply][data-time]')).map (e => ["data-bump", "data-reply", "data-time"].map (s => parseInt (e.getAttribute (s), 10))).map (([b, r, t]) => [now - b, r, now - t])
      config.data = data
    },
    draw: config => {
      const ctx    = config.context
      const w      = config.width
      const h      = config.height
      const radius = config.radius
      const data   = config.data
      const [maxb, maxr, maxt] = Array.from ({length: 3}, (x, k) => data.reduce ((a, b) => Math.max (a, b [k]), 0) || 1)

      data.reverse ()
      for (const [b, r, t] of data) {
        const x = t * w / maxt
        const y = h - r * h / maxr
        const c = Math.floor (b * 255 / maxb)
        const f = 'rgba(' + (255 - c) + ', 0, ' + c + ', 1)'

        ctx.fillStyle = f
        ctx.beginPath ()
        ctx.arc (x, y, radius, 0, 2 * Math.PI, true)
        ctx.fill ()
      }
    }
  }]
)) ({
  drawrelative: config => {
    const ctx   = config.context
    const data  = config.data
    const max   = data.reduce ((a, b) => Math.max (a, b), 0) || 1
    const w     = config.width
    const h     = config.height
    const count = config.count
    const step  = config.step

    ctx.fillStyle = config.back
    ctx.fillRect (0, 0, w, h)

    ctx.strokeStyle = config.grid
    for (let k = 0; k < count; k++) {
      ctx.beginPath ()
      ctx.moveTo (step * k, 0)
      ctx.lineTo (step * k, h)
      ctx.stroke ()
    }

    ctx.fillStyle = config.bars
    for (let k = 0; k < count; k++) {
      const y = data [k] * h / max
      ctx.fillRect (w - step - k * step, h - y, step, y)
    }
  },
  getdatadatecount: (config, datestrings) => {
    // YYYY-MM-DD
    const all = datestrings.reduce ((acc, key) => {
      if (key in acc) {
        acc [key] += 1
      } else {
        acc [key]  = 1
      }
      return acc
    }, {})
    const now = new Date ()
    const key = k => {
      const when = new Date (now)
      when.setUTCDate (now.getUTCDate () - k)
      return when.getUTCFullYear ().toString ().padStart (4, '0') + '-' + (when.getUTCMonth () + 1).toString ().padStart (2, '0') + '-' + when.getUTCDate ().toString ().padStart (2, '0')
    }
    const ret = Array.from ({length: config.count}, (x, k) => all [key (k)] ?? 0)
    config.data = ret
  }
})

Just a demo.
>>

 No.10904

Last50 links on catalog pages:
((count, min) => Array.from (document.querySelectorAll ("div#Grid div.thread")).map (t => [t.querySelector ('a[href*="/res/"]'), t.querySelector ("div.replies > strong")]).filter (([a, s]) => parseInt (s.innerText.replace (/^R: (\d+) .+$/, "$1"), 10) >= min).forEach (([a, s]) => { s.innerHTML += ' <a href="' + a.getAttribute ("href").replace (/^(.+)([.]html)$/, "$1+50$2") + '">L' + count + '</a>'; })) (50, 100)
>>

 No.10933

Update of js/show-own-posts.js to allow users to opt out of (You) tracking.
>>

 No.10934

>>

 No.10937

Here's some harmless fun with localStorage.own_posts:
((sortkeys, link, divide, cites, install, slice, groups) => {
  const own    = JSON.parse (localStorage.own_posts || '{}')
  const boards = sortkeys (Object.keys (own))
  const count  = boards.reduce ((acc, item) => acc + own [item].length, 0)
  const html   = ['own posts: ' + count + ' boards: ' + boards.length]
  boards.forEach (b => {
    const ownb = own [b].reverse ()
    const have = ownb.length
    const [div, mod, left] = divide (have, slice, groups)
    html.push ('board: ' + link ('/' + b + '/index.html', '/' + b + '/') + ' ' + link ('/' + b + '/catalog.html', '&copy;') + ' count: ' + have + ' = ' + div + ' * ' + slice + ' + ' + mod + ' + ' + left)
    for (let k = 0; k < div; k++) {
      html.push (cites (b, ownb, slice, k, slice))
    }
    if (mod) {
      html.push (cites (b, ownb, slice, div, mod))
    }
  })
  install (html)
}) (
  arr => arr.map (s => [s, s.toUpperCase ()]).sort ((a, b) => a [1] < b [1] ? -1 : a [1] > b [1] ? 1 : 0).map (p => p [0]),
  (href, text) => '<a href="' + href + '">' + text + '</a>',
  (have, slice, groups) => {
    const take = Math.min (have, slice * groups)
    const mod  = take % slice
    return [(take - mod) / slice, mod, have - take]
  },
  (board, arr, slice, skip, now) => arr.slice (skip * slice, skip * slice + now).map (s => '>>>/' + board + '/' + s).join (' '),
  html => {
    const old  = document.getElementById ("ownposts-report")
    if (old) { old.remove (); }
    const node = document.createElement ("div")
    node.setAttribute ("id", "ownposts-report")
    node.setAttribute ("style", "border: thin solid;")
    node.innerHTML = html.map (s => '<p>' + s + '</p>').join ("")
    const foot = document.querySelector ('footer')
    if (foot) {
      foot.parentNode.insertBefore (node, foot)
    } else {
      document.body.appendChild (node)
    }
  },
  45, 2
)
>>

 No.10966

to deal with tripiots:
Array.from (document.querySelectorAll ("span.trip")).forEach (e => { e.parentNode.parentNode.parentNode.querySelector ("div.body").innerHTML = "I am a child seeking attention."; });

idea from: https://textboard.org/prog/34#t34p105
>>

 No.11019

Fix for
> Undefined index: id
when reporting a post that gets deleted just before the report is submitted, without discarding the user's reporting effort on the remaining posts of a multipost report.
>>

 No.11035

Here's a patch to make auto-reload.js mark deleted posts. Theme maintainers can customize the marking using the div.post.reply.auto-reload-removed selector. JS users can do the same in User CSS. Deleted posts are not considered important enough to reset the update timer, but if this is desired the removed_posts counter is available for this purpose. To test, inspect and edit a reply id to a non-existent one. Keep in mind that this is narrowly aimed at marking deleted posts, and auto-reload.js has other bugs >>>/leftypol_archive/1759 with which this patch does not concern itself.
>>

 No.11139

File: 1662638998597.jpg ( 638.47 KB , 848x1200 , 969ba5628d2f34bf1575d4be64….jpg )

… U+2026 Po HORIZONTAL ELLIPSIS &hellip;

space >>11090
no space >>11090…

See >>11090 for cite markup.

To fix add the colon and the ampersand to the character class.
< ((?=[\s,.:)?!&])|$)
>>

 No.11140

>>11139
Thank you for your work anon.
>>

 No.11151

File: 1663581149363.jpg ( 366.99 KB , 1392x2048 , nitter.jpg )

User JS snippet for twitter → nitter link conversion

Array.from (document.querySelectorAll ('a[href^="https://twitter.com/"]')).forEach (e => { e.outerHTML = e.outerHTML + ' <a target="_blank" href="' + e.getAttribute ("href").replace (/^https:\/\/twitter\.com\//, "https://nitter.net/") + '">[nitter]</a>'; })
>>

 No.11270

>>11139:

Test
>>

 No.11283

>>11139
A pull request has been submitted for this bug.
>>

 No.11303

>>11283
>A pull request has been submitted for this bug.
I must be looking in the wrong place because I can only find one for the colon which would fix >>11090 but not for the ampersand which would fix >>11139

colon test >>11283 : versus >>11283:
ellipsis test >>11283 … versus >>11283…
>>

 No.12030

File: 1678937328590.png ( 1.88 MB , 1536x1024 , 608bfd6724eb6e62e9ae5dc04b….png )

>>>/meta/11075
>We don't really know where it is located in the source code because lainchan is spaghetti code
The bug is caused by instantiating the index template
https://github.com/towards-a-new-leftypol/leftypol_lainchan/blob/3396999a17b4c67473a5b1739329a9d08b992c84/templates/themes/overboards/theme.php#L204
using the $config of the openBoard call of the buildOne call of whichever thread happens to be last in the $top_threads loop.

Unfortunately there doesn't seem to be any clean way to access the board-independent global config once openBoard has been run at least once. So a general solution may need to save a reference to the board-independent global config into a new global variable as soon as it is available
https://github.com/towards-a-new-leftypol/leftypol_lainchan/blob/3396999a17b4c67473a5b1739329a9d08b992c84/inc/functions.php#L38
and pass that to the instantiation of the index template.

Unique IPs: 49

[Return][Catalog][Top][Home][Post a Reply]
Delete Post [ ]
[ overboard / sfw / alt / cytube] [ leftypol / b / WRK / hobby / tech / edu / ga / ent / 777 / posad / i / a / R9K / dead ] [ meta ]
ReturnCatalogTopBottomHome