@ -11,7 +11,7 @@
// @exclude http*://archive.nyafuu.org/bant/statistics/
// @exclude http*://archived.moe/bant/statistics/
// @exclude http*://thebarchive.com/bant/statistics/
// @version 2. 0. 1
// @version 2. 1.0
// @grant GM_xmlhttpRequest
// @grant GM_getValue
// @grant GM_setValue
@ -29,7 +29,7 @@
// /bant/ Flags is licensed under the GNU AGPL Version 3.0 or later.
// see the LICENSE file or <https://www.gnu.org/licenses/>
// Change this if you want verbose debuging information in the console.
// This will print a load of shit to the console
const debugMode = false ;
const isGM4 = typeof GM _setValue === 'undefined' ;
@ -37,9 +37,6 @@ const setValue = isGM4 ? GM.setValue : GM_setValue;
const getValue = isGM4 ? GM . getValue : GM _getValue ;
const xmlHttpRequest = isGM4 ? GM . xmlHttpRequest : GM _xmlhttpRequest ;
//
// DO NOT EDIT ANYTHING IN THIS SCRIPT DIRECTLY - YOUR FLAGS SHOULD BE CONFIGURED USING THE FLAG SELECT
//
const version = encodeURIComponent ( 2 ) ; // Breaking changes.
const back _end = 'https://flags.plum.moe/' ;
const api _flags = back _end + 'api/flags' ;
@ -55,9 +52,6 @@ let regions = []; // The flags we have selected.
let postNrs = [ ] ; // all post numbers in the thread.
let board _id = "" ; // The board we get flags for.
let flagsLoaded = false ;
//
// DO NOT EDIT ANYTHING IN THIS SCRIPT DIRECTLY - YOUR FLAGS SHOULD BE CONFIGURED USING THE FLAG SELECT
//
const debug = text => {
if ( debugMode )
@ -72,19 +66,12 @@ const software = {
} ;
const makeElement = ( tag , options ) => Object . assign ( document . createElement ( tag ) , options ) ;
const toggleFlagButton = state => document . getElementById ( 'append_flag_button' ) . disabled = state === 'off' ? true : false ;
const flagSource = flag => flag _dir + flag + ".png" ;
const flagSource = flag => flag _dir + flag + '.png' ;
/** Add styles to the <head> */
const add Global Style = css => document . head . appendChild ( makeElement ( 'style' , { innerHTML : css } ) ) ;
const add Style = css => document . head . appendChild ( makeElement ( 'style' , { innerHTML : css } ) ) ;
/ * * W r a p p e r a r o u n d G M _ x m l h t t p R e q u e s t .
* @ param { string } method - The HTTP method ( GET , POST ) .
* @ param { string } url - The URL of the request .
* @ param { string } data - text for the form body .
* @ param { Function } func - The function run when we recieve a response . Response data is sent directly to it . * /
const makeRequest = ( ( method , url , data , func ) => {
xmlHttpRequest ( {
method : method ,
@ -100,56 +87,51 @@ function saveFlags() {
regions = [ ] ;
const selectedFlags = document . querySelectorAll ( ".bantflags_flag" ) ;
for ( var i = 0 ; i < selectedFlags . length ; i ++ ) {
for ( let i = 0 ; i < selectedFlags . length ; i ++ ) {
regions [ i ] = selectedFlags [ i ] . title ;
}
setValue ( namespace , regions ) ;
}
/ * * A d d a f l a g t o o u r s e l e c t i o n .
* @ param { string } flag - The flag to add to our selection . Either passed from saved flags or the current value of flagSelect * /
/** Add a flag to our selection. */
function setFlag ( flag ) {
let UID = Math . random ( ) . toString ( 36 ) . substring ( 7 ) ;
let flagName = flag ? flag : document . querySelector ( '#flagSelect input' ) . value ;
let flagContainer = document . getElementById ( 'bantflags_container' ) ;
const flagName = flag ? flag : document . querySelector ( '#flagSelect input' ) . value ;
const flagContainer = document . getElementById ( 'bantflags_container' ) ;
flagContainer . appendChild ( makeElement ( 'img' , {
title : flagName ,
src : flagSource ( flagName ) ,
id : UID ,
className : 'bantflags_flag'
className : 'bantflags_flag' ,
onclick : function ( ) {
flagContainer . removeChild ( this ) ;
if ( flagsLoaded )
toggleFlagButton ( 'on' ) ;
saveFlags ( ) ;
}
} ) ) ;
if ( flagContainer . children . length >= max _flags )
toggleFlagButton ( 'off' ) ;
document . getElementById ( UID ) . addEventListener ( "click" , e => {
flagContainer . removeChild ( e . target ) ;
toggleFlagButton ( 'on' ) ;
saveFlags ( ) ;
} ) ;
if ( ! flag ) // We've added a new flag to our selection
saveFlags ( ) ;
}
function init ( ) {
le t flagsForm = makeElement ( 'div' , {
const flagsForm = makeElement ( 'div' , {
className : 'flagsForm' ,
innerHTML : '<span id="bantflags_container"></span><button type="button" id="append_flag_button" title="Click to add selected flag to your flags. Click on flags to remove them. Saving happens automatically, you only need to refresh the pages that have an outdated flaglist on the page. "><<</button><button id="flagLoad" type="button">Click to load flags.</button><div id="flagSelect" ><ul class="hide"></ul><input type="button" value="(You)" onclick=""></div>'
innerHTML : '<span id="bantflags_container"></span><button type="button" id="append_flag_button" title="Click to add selected flag to your flags. Click on flags to remove them. " disabled="true "><<</button><button id="flagLoad" type="button">Click to load flags.</button><div id="flagSelect" ><ul class="hide"></ul><input type="button" value="(You)" onclick=""></div>'
} ) ;
// Where do we append the flagsForm to?
if ( software . yotsuba ) { document . getElementById ( 'delform' ) . appendChild ( flagsForm ) ; }
else if ( software . nodegucaDoushio ) { document . querySelector ( 'section' ) . append( flagsForm ) ; } // As posts are added the flagForm moves up the page. Could we append this after .section?
else if ( software . nodegucaDoushio ) { document . querySelector ( 'section' ) . insertAdjacentElement( 'afterEnd' , flagsForm ) ; }
for ( let i = 0 ; i < regions . length ; i ++ ) {
setFlag ( regions [ i ] ) ;
}
document . getElementById ( 'append_flag_button' ) . addEventListener ( 'click' , ( ) => flagsLoaded ? setFlag ( ) : alert ( 'Load flags before adding them.' ) ) ;
document . getElementById ( 'flagLoad' ) . addEventListener ( 'click' , makeFlagSelect , { once : true } ) ;
}
@ -168,19 +150,19 @@ function makeFlagSelect() {
}
let flagSelect = document . getElementById ( 'flagSelect' ) ;
let flagList = flagSelect . querySelector ( 'ul' ) ;
let flagInput = flagSelect . querySelector ( 'input' ) ;
let flag s = resp . responseText . split ( '\n ') ;
let flag List = flagSelect . querySelector ( 'ul ') ;
for ( var i = 0 ; i < flags . length ; i ++ ) {
let flag = flags [ i ] ;
let flags = resp . responseText . split ( '\n' ) ;
for ( let i = 0 ; i < flags . length ; i ++ ) {
const flag = flags [ i ] ;
flagList . appendChild ( makeElement ( 'li' , {
innerHTML : ` <img src=" ${ flagSource ( flag ) } " title=" ${ flag } "><span> ${ flag } </span> `
} ) ) ;
}
flagSelect . addEventListener ( 'click' , function ( e ) {
// So it works if we click the flag image
flagSelect . addEventListener ( 'click' , e => {
// Maybe we clicked the flag image
const node = e . target . nodeName === 'LI' ? e . target : e . target . parentNode ;
if ( node . nodeName === 'LI' )
flagInput . value = node . querySelector ( 'span' ) . innerHTML ;
@ -188,6 +170,10 @@ function makeFlagSelect() {
flagList . classList . toggle ( 'hide' ) ;
} ) ;
const flagButton = document . getElementById ( 'append_flag_button' ) ;
flagButton . addEventListener ( 'click' , ( ) => setFlag ( ) ) ;
flagButton . disabled = false ;
document . getElementById ( 'flagLoad' ) . style . display = 'none' ;
document . querySelector ( '.flagsForm' ) . style . marginRight = "200px" ; // flagsForm has position: absolute and is ~200px long.
flagSelect . style . display = 'inline-block' ;
@ -226,10 +212,9 @@ function resolveFlags() {
debug ( ` JSON: ${ resp . responseText } ` ) ;
Object . keys ( jsonData ) . forEach ( post => {
le t flags = jsonData [ post ] ;
cons t flags = jsonData [ post ] ;
if ( flags . length <= 0 )
return ;
if ( flags . length <= 0 ) return ;
debug ( ` Resolving flags for >> ${ post } ` ) ;
@ -241,14 +226,12 @@ function resolveFlags() {
for ( let i = 0 ; i < flags . length ; i ++ ) {
const flag = flags [ i ] ;
const newFlag = makeElement ( 'a' , {
flagContainer . append ( makeElement ( 'a' , {
innerHTML : ` <img src=" ${ flagSource ( flag ) } " title=" ${ flag } "> ` ,
className : 'bantFlag' ,
target : '_blank' ,
title : flag
} ) ;
flagContainer . append ( newFlag ) ;
} ) ) ;
debug ( ` \t -> ${ flag } ` ) ;
}
@ -259,46 +242,45 @@ function resolveFlags() {
}
function main ( ) {
if ( ! regions ) {
regions = [ ] ;
}
// See Docs/styles.css
add Global Style( '.bantFlag{padding: 0px 0px 0px 5px; display: inline-block; width: 16px; height: 11px; position: relative;} .bantflags_flag{padding: 1px;} [title^="Romania"]{ position: relative; animation: shakeAnim 0.1s linear infinite;} @keyframes shakeAnim{ 0% {left: 1px;} 25% {top: 2px;} 50% {left: 1px;} 75% {left: 0px;} 100% {left: 2px;}}.flagsForm{float: right; clear: right; margin: 20px 10px;} #flagSelect{display:none;} #flagSelect ul{list-style-type: none;padding: 0;margin-bottom: 0;cursor: pointer;bottom: 100%;height: 200px;overflow: auto;position: absolute;width:200px;background-color:#fff} #flagSelect ul li {display: block;} #flagSelect ul li:hover {background-color: #ddd;}#flagSelect {position: absolute;}#flagSelect input {width: 200px;} #flagSelect .hide {display: none;}#flagSelect img {margin-left: 2px;}' )
add Style( '.bantFlag{padding: 0px 0px 0px 5px; display: inline-block; width: 16px; height: 11px; position: relative;} .bantflags_flag{padding: 1px;} [title^="Romania"]{ position: relative; animation: shakeAnim 0.1s linear infinite;} @keyframes shakeAnim{ 0% {left: 1px;} 25% {top: 2px;} 50% {left: 1px;} 75% {left: 0px;} 100% {left: 2px;}}.flagsForm{float: right; clear: right; margin: 20px 10px;} #flagSelect{display:none;} #flagSelect ul{list-style-type: none;padding: 0;margin-bottom: 0;cursor: pointer;bottom: 100%;height: 200px;overflow: auto;position: absolute;width:200px;background-color:#fff} #flagSelect ul li {display: block;} #flagSelect ul li:hover {background-color: #ddd;}#flagSelect {position: absolute;}#flagSelect input {width: 200px;} #flagSelect .hide {display: none;}#flagSelect img {margin-left: 2px;}' )
if ( software . yotsuba ) {
getPosts ( '.postContainer' ) ;
add Global Style( '.flag{top: 0px;left: -1px}' ) ;
add Style( '.flag{top: 0px;left: -1px}' ) ;
init ( ) ;
}
if ( software . nodegucaDoushio ) {
else if ( software . nodegucaDoushio ) {
getPosts ( 'section[id], article[id]' ) ;
add Global Style( '.bantFlag {cursor: default} .bantFlag img {pointer-events: none;}' ) ;
add Style( '.bantFlag {cursor: default} .bantFlag img {pointer-events: none;}' ) ;
init ( ) ;
}
if ( software . foolfuuka ) {
else if ( software . foolfuuka ) {
getPosts ( 'article[id]' ) ;
add Global Style( '.bantFlag{top: -2px !important;left: -1px !important}' ) ;
add Style( '.bantFlag{top: -2px !important;left: -1px !important}' ) ;
}
board _id = window . location . pathname . split ( '/' ) [ 1 ] ;
debug ( board _id ) ;
debug ( ` board: ${ board _id } ` ) ;
try {
resolveFlags ( ) ;
}
catch ( fuckywucky ) {
console . log ( ` Wah! Manx fucked something up ;~; \n Poke him somewhere this this:\n ${ fuckywucky } ` )
console . log ( ` Wah! Manx fucked something up ;~; \n Poke him somewhere wi th this:\n ${ fuckywucky } ` )
}
}
if ( isGM4 ) { // Fuck you G M4
if ( isGM4 ) { // Fuck you G reaseMonkey
( async ( ) => {
regions = await getValue ( namespace ) ;
main ( ) ;
@ -316,48 +298,35 @@ const postFlags = (post_nr, func = resp => debug(resp.responseText)) => makeRequ
func ) ;
if ( software . yotsuba ) {
const GetEvDetail = e => e . detail || e . wrappedJSObject . detail ;
// 4chanX and native extension respectively
document . addEventListener ( 'QRPostSuccessful' , e => postFlags ( e . detail . postID ) ) ;
document . addEventListener ( '4chanQRPostSuccess' , e => postFlags ( GetEvDetail ( e ) . postId ) ) ;
// I need to look at these.
document . addEventListener ( 'ThreadUpdate' , function ( e ) {
var evDetail = GetEvDetail ( e ) ;
var evDetailClone = typeof cloneInto === 'function' ? cloneInto ( evDetail , unsafeWindow ) : evDetail ;
const e _detail = e => e . detail || e . wrappedJSObject . detail // what?
document . addEventListener ( 'QRPostSuccessful' , e => postFlags ( e _detail ( e ) . postID ) ) ;
document . addEventListener ( '4chanQRPostSuccess' , e => postFlags ( e _detail ( e ) . postId ) ) ;
//ignore if 404 event
if ( evDetail [ 404 ] === true ) {
return ;
}
document . addEventListener ( 'ThreadUpdate' , e => {
const d = e _detail ( e )
if ( d [ 404 ] ) return ;
evDetailClone . newPosts . forEach ( function ( post _board _nr ) {
var post _nr = post _board _nr . split ( '.' ) [ 1 ] ;
postNrs . push ( post _nr ) ;
} ) ;
d . newPosts . forEach ( post => postNrs . push ( post . split ( '.' ) [ 1 ] ) ) ;
resolveFlags ( ) ;
} , false );
} ) ;
document . addEventListener ( '4chanThreadUpdated' , function ( e ) {
var evDetail = GetEvDetail ( e ) ;
let threadID = window . location . pathname . split ( '/' ) [ 3 ] ;
let postsContainer = Array . prototype . slice . call ( document . getElementById ( 't' + threadID ) . childNodes ) ;
let lastPosts = postsContainer . slice ( Math . max ( postsContainer . length - evDetail . count , 1 ) ) ; //get the last n elements (where n is evDetail.count)
document . addEventListener ( '4chanThreadUpdated' , e => {
const d = e _detail ( e ) ;
if ( d . count <= 0 ) return ;
lastPosts . forEach ( function ( post _container ) {
var post _nr = post _container . id . replace ( 'pc' , '' ) ;
postNrs . push ( post _nr ) ;
} ) ;
// Get the added posts in reverse order, take post numbers from ID
const posts = document . querySelectorAll ( '.postContainer' ) ;
for ( let i = 0 ; i < d . count ; i ++ ) {
postNrs . push ( posts [ posts . length - 1 - i ] . id . substr ( 2 ) ) ;
}
resolveFlags ( ) ;
} , false );
} );
}
if ( software . nodegucaDoushio ) {
const postFunc = function ( ) {
const postFunc = ( ) => {
postNrs . push ( mutation . target . id ) ;
resolveFlags ( ) ;
}
@ -369,7 +338,7 @@ if (software.nodegucaDoushio) {
if ( mutation . addedNodes . length <= 0 )
return ; // We only care if something post was added
var firstAddedNode = mutation . addedNodes [ 0 ] . nodeName ;
const firstAddedNode = mutation . addedNodes [ 0 ] . nodeName ;
// Enter a thread / change boards
if ( mutation . target . nodeName === 'THREADS' ) {