- // ==UserScript==
- // name and namespace cannot be changed - it would break the update mechanism, that's why we will leave the name at Extra Flags for int
- // @name Extra Flags for int
- // @namespace com.whatisthisimnotgoodwithcomputers.extraflagsforint
- // @description Extra Flags for int v2 "City flags were a mistake" edition
- // @include http*://boards.4chan.org/int/*
- // @include http*://boards.4chan.org/sp/*
- // @include http*://boards.4chan.org/pol/*
- // @include http*://boards.4chan.org/bant/*
- // @exclude http*://boards.4chan.org/int/catalog
- // @exclude http*://boards.4chan.org/sp/catalog
- // @exclude http*://boards.4chan.org/pol/catalog
- // @exclude http*://boards.4chan.org/bant/catalog
- // @version 0.29
- // @grant GM_xmlhttpRequest
- // @grant GM_registerMenuCommand
- // @grant GM_getValue
- // @grant GM_setValue
- // @grant GM_addStyle
- // @run-at document-end
- // ==/UserScript==
-
- // DO NOT EDIT ANYTHING IN THIS SCRIPT DIRECTLY - YOUR REGION SHOULD BE CONFIGURED BY USING THE CONFIGURATION BOXES (see install webms for help)
-
- /** JSLint excludes */
- /*jslint browser: true*/
- /*global document, console, GM_addStyle, GM_setValue, GM_getValue, GM_registerMenuCommand, GM_xmlhttpRequest, cloneInto, unsafeWindow*/
-
- /* WebStorm JSLint ticked:
- - uncapitalized constructors
- - missing 'use strict' pragma
- - many var statements
- */
-
- /* Right margin: 160 */
-
- // DO NOT EDIT ANYTHING IN THIS SCRIPT DIRECTLY - YOUR REGION SHOULD BE CONFIGURED BY USING THE CONFIGURATION BOXES (see install webms for help)
- var regions = [];
- var radio = "all";
- var lastRegion = ""; //used for back button
- var regionVariable = 'regionVariableAPI2';
- var radioVariable = 'radioVariableAPI2';
- var allPostsOnPage = [];
- var postNrs = [];
- var postRemoveCounter = 60;
- var requestRetryInterval = 5000;
- var flegsBaseUrl = 'https://raw.githubusercontent.com/flaghunters/Extra-Flags-for-int-/master/flags/';
- // remove comment and change link to add country flag icons into selection menu var countryFlegsBaseUrl = 'https://raw.githubusercontent.com/flagzzzz/Extra-Flags-for-4chan/master/flags/';
- var flagListFile = 'flag_list.txt';
- var backendBaseUrl = 'https://whatisthisimnotgoodwithcomputers.com/';
- var postUrl = 'int/post_flag_api2.php';
- var getUrl = 'int/get_flags_api2.php';
- var shortId = 'witingwc.ef.';
- var regionDivider = "||";
-
- /** Setup, preferences */
- var setup = {
- namespace: 'com.whatisthisimnotgoodwithcomputers.extraflagsforint.',
- id: "ExtraFlags-setup",
- html: function () {
-
- var htmlFixedStart = '<div>Extra Flags for 4chan v2</div><br/>';
- var htmlBackButton = '<button name="back">Back</button>';
- var htmlNextButton = '<button name="forward">Next</button>';
- var htmlBackNextButtons = '<div>' + htmlBackButton + htmlNextButton + '</div>';
- var htmlSaveButton = '<div><button name="save" title="Pressing "Save Regions" will set your regions to the ones current displayed below.">' +
- 'Save Regions</button></div><br/>';
- var htmlHelpText = '<label name="' + shortId + 'label"> You can go as deep as you like, regions stack.<br/>' +
- 'For example; United States, California, Los Angeles<br/></label>' +
- '<label>Country must match your flag! Your flag not here? Open issue here:<br/>' +
- '<a href="https://github.com/flaghunters/Extra-Flags-for-4chan/issues" style="color:blue">' +
- 'https://github.com/flaghunters/Extra-Flags-for-4chan/issues</a></label>';
- var filterRadio = '<br/><br/><form id="filterRadio">' +
- '<input type="radio" name="filterRadio" id="filterRadioall" style="display: inline !important;" value="all"><label>Show country + ALL regions.</label>' +
- '<br/><input type="radio" name="filterRadio" id="filterRadiofirst" style="display: inline !important;" value="first"><label>Only show country + FIRST region.</label>' +
- '<br/><input type="radio" name="filterRadio" id="filterRadiolast" style="display: inline !important;" value="last"><label>Only show country + LAST region. (v1/old format)</label>' +
- '</form>';
-
- if (regions.length > 1) {
- var selectMenuFlags = "Regional flags selected: ";
- var path = flegsBaseUrl + "/" + regions[0];
- for (var i = 1; i < regions.length; i++) {
- path += "/" + regions[i];
- selectMenuFlags += "<img src='" + path + ".png'" + " title='" + regions[i] + "'> ";
- }
- selectMenuFlags += "<br/>";
- return htmlFixedStart + '<div>Region: <br/><select id="' + shortId + 'countrySelect">' +
- '</select></div><br/>' + htmlBackNextButtons +
- '<br/>' + htmlSaveButton + '</div>' + selectMenuFlags + htmlHelpText + filterRadio;
- }
-
- if (regions.length == 1) {
- var selectMenuFlags = "<br/>";
- return htmlFixedStart + '<div>Region: <br/><select id="' + shortId + 'countrySelect">' +
- '</select></div><br/>' + htmlBackNextButtons +
- '<br/>' + '</div><br/><br/>' + selectMenuFlags + htmlHelpText + filterRadio;
- }
-
- return htmlFixedStart + '<div>Country: <br/><select id="' + shortId + 'countrySelect">' +
- '</select></div><br/>' + htmlBackNextButtons + '<br/>' + htmlHelpText + filterRadio;
-
- },
- fillHtml: function (path1) {
- if (path1 === "") { //normal call
- var path = flegsBaseUrl + "/";
- var oldPath = path;
- if (regions.length > 0) {
- for (var i = 0; i < regions.length; i++) {
- oldPath = path;
- path += regions[i] + "/";
- }
- }
- var pathNoFlagList = path;
- } else { // end of folder line call
- path = path1;
- oldPath = "";
- var pathNoFlagList = path;
- }
-
- /* resolve countries which we support */
- GM_xmlhttpRequest({
- method: "GET",
- url: path + flagListFile,
- headers: {
- "Content-Type": "application/x-www-form-urlencoded"
- },
- onload: function (response) {
- if (response.status == 404) { // detect if there are no more folders
- setup.fillHtml(oldPath);
- setup.q('forward').disabled = true; // disable next button
- } else {
- //hide spam, debug purposes only
- //console.log(response.responseText);
- var countrySelect = document.getElementById(shortId + 'countrySelect'),
- countriesAvailable = response.responseText.split('\n');
-
- for (var countriesCounter = 0; countriesCounter < countriesAvailable.length - 1; countriesCounter++) {
- var opt = document.createElement('option');
- opt.value = countriesAvailable[countriesCounter];
-
- if (regions.length > 0) {
- opt.innerHTML = countriesAvailable[countriesCounter] + " " + "<img src=\"" + flegsBaseUrl + pathNoFlagList + countriesAvailable[countriesCounter] + ".png\"" + " title=\"" + countriesAvailable[countriesCounter] + "\">";
- } else {
- opt.innerHTML = countriesAvailable[countriesCounter]; // remove comment to enable country flags in the selection menu + " " + "<img src=\"" + countryFlegsBaseUrl + countriesAvailable[countriesCounter] + ".png\"" + " title=\"" + countriesAvailable[countriesCounter] + "\">";
- }
-
-
- if (lastRegion != "" && countriesAvailable[countriesCounter] === lastRegion) { // automatically select last selected when going up a folder
- opt.selected = "selected";
- } else if (oldPath == "" && countriesAvailable[countriesCounter] === regions[regions.length - 1]) { // show final selected when no more
- // folders detected
- opt.selected = "selected";
- }
- countrySelect.appendChild(opt);
- }
- }
-
- }
- });
- },
- setRadio: function() {
- var radioStatus = setup.load(radioVariable);
- if (!radioStatus || radioStatus === "" || radioStatus === "undefined") {
- radioStatus = "all";
- }
- var radioButton = document.getElementById("filterRadio" + radioStatus);
- radioButton.checked = true;
- },
- q: function (n) {
- return document.querySelector('#' + this.id + ' *[name="' + n + '"]');
- },
- removeExtra: function () {
- if (regions.length > 0) {
- lastRegion = regions[regions.length - 1];
- regions.pop();
- }
- setup.show();
- },
- show: function () {
- /* remove setup window if existing */
- var setup_el = document.getElementById(setup.id);
- if (setup_el) {
- setup_el.parentNode.removeChild(setup_el);
- }
- /* create new setup window */
- GM_addStyle('\
- #' + setup.id + ' { position:fixed;z-index:10001;top:40px;right:40px;padding:20px 30px;background-color:white;width:auto;border:1px solid black }\
- #' + setup.id + ' * { color:black;text-align:left;line-height:normal;font-size:12px }\
- #' + setup.id + ' div { text-align:center;font-weight:bold;font-size:14px }'
- );
- setup_el = document.createElement('div');
- setup_el.id = setup.id;
- setup_el.innerHTML = setup.html();
- setup.fillHtml("", "");
-
- document.body.appendChild(setup_el);
-
- setup.setRadio();
-
- /* button listeners */
- setup.q('back').addEventListener('click', function () {
- if (regions.length > 0) {
- if (setup.q('forward').disabled == true) {
- setup.q('forward').disabled = false; // reenable next button
- }
- lastRegion = regions[regions.length - 1];
- regions.pop();
- setup.show();
- }
- }, false);
-
- setup.q('forward').addEventListener('click', function () {
- var e = document.getElementById(shortId + "countrySelect");
- var temp = e.options[e.selectedIndex].value;
- lastRegion = "";
- if (temp != "" && regions[regions.length - 1] != temp) {
- this.disabled = true;
- this.innerHTML = 'Saving...';
-
- lastRegion = regions[regions.length - 1];
- regions.push(temp);
- setup.show();
- }
-
- }, false);
-
- setup.q('save').addEventListener('click', function () {
- var e = document.getElementById(shortId + "countrySelect");
- var temp = e.options[e.selectedIndex].value;
-
- if (regions[regions.length - 1] === "") { //prevent last spot from being blank
- regions.pop();
- }
- lastRegion = "";
-
- radio = document.querySelector('input[name="filterRadio"]:checked').value;
- setup.save(radioVariable, radio);
-
- alert('Flags set: ' + regions + '\n\n' + 'Refresh all your 4chan tabs and be sure to post using the quick reply window!');
-
- this.disabled = true;
- this.innerHTML = 'Saving...';
- setup_el.parentNode.removeChild(setup_el);
- setup.save(regionVariable, regions);
-
- }, false);
- },
- save: function (k, v) {
- GM_setValue(setup.namespace + k, v);
- },
- load: function (k) {
- return GM_getValue(setup.namespace + k);
- },
- init: function () {
- //GM_registerMenuCommand('Extra Flags setup', setup.show;
- GM_registerMenuCommand('Extra Flags setup', setup.show);
- }
- };
-
- /** Prompt to set region if regionVariable is empty */
- regions = setup.load(regionVariable);
- radio = setup.load(radioVariable);
- if (!regions) {
- regions = [];
- setTimeout(function () {
- if (window.confirm("Extra Flags: No region detected, set it up now?") === true) {
- setup.show();
- }
- }, 2000);
- }
- if (!radio || radio === "" || radio === "undefined") {
- radio = "all";
- }
-
- /** parse the posts already on the page before thread updater kicks in */
- function parseOriginalPosts() {
- var tempAllPostsOnPage = document.getElementsByClassName('postContainer');
- allPostsOnPage = Array.prototype.slice.call(tempAllPostsOnPage); //convert from element list to javascript array
- postNrs = allPostsOnPage.map(function (p) {
- return p.id.replace("pc", "");
- });
- }
-
- /** the function to get the flags from the db uses postNrs
- * member variable might not be very nice but it's the easiest approach here */
- function onFlagsLoad(response) {
- //exit on error
- if (response.status !== 200) {
- console.log("Could not fetch flags, status: " + response.status);
- console.log(response.statusText);
- setTimeout(resolveRefFlags, requestRetryInterval);
- return;
- }
-
- var jsonData = JSON.parse(response.responseText);
-
- jsonData.forEach(function (post) {
- var postToAddFlagTo = document.getElementById("pc" + post.post_nr),
- postInfo = postToAddFlagTo.getElementsByClassName('postInfo')[0],
- nameBlock = postInfo.getElementsByClassName('nameBlock')[0],
- currentFlag = nameBlock.getElementsByClassName('flag')[0],
- postedRegions = post.region.split(regionDivider);
-
- if (postedRegions.length > 0 && !(currentFlag === undefined)) {
- var path = currentFlag.title;
- for (var i = 0; i < postedRegions.length; i++) {
- path += "/" + postedRegions[i];
-
- // this is probably quite a dirty fix, but it's fast
- if ((radio === "all") || (radio === "first" && i === 0) || (radio === "last" && i === (postedRegions.length - 1))) {
- var newFlag = document.createElement('a');
- nameBlock.appendChild(newFlag);
-
- var lastI = i;
- if (radio === 'last') {
- lastI = 0;
- }
-
- var newFlagImgOpts = 'onerror="(function () {var extraFlagsImgEl = document.getElementById(\'pc' + post.post_nr +
- '\').getElementsByClassName(\'extraFlag\')[' + lastI +
- '].firstElementChild; if (!/\\/empty\\.png$/.test(extraFlagsImgEl.src)) {extraFlagsImgEl.src = \'' +
- flegsBaseUrl + 'empty.png\';}})();"';
-
- newFlag.innerHTML = "<img src=\"" + flegsBaseUrl + path + ".png\"" + newFlagImgOpts + " title=\"" + postedRegions[i] + "\">";
- newFlag.className = "extraFlag";
-
- if (i > 0) {
- newFlag.href = "https://www.google.com/search?q=" + postedRegions[i] + ", " + postedRegions[i - 1];
- } else {
- newFlag.href = "https://www.google.com/search?q=" + postedRegions[i] + ", " + currentFlag.title;
- }
-
- newFlag.target = '_blank';
- //padding format: TOP x RIGHT_OF x BOTTOM x LEFT_OF
- newFlag.style = "padding: 0px 0px 0px 5px; vertical-align:;display: inline-block; width: 16px; height: 11px; position: relative;";
-
- console.log("resolved " + postedRegions[i]);
- }
- }
- }
-
- //postNrs are resolved and should be removed from this variable
- var index = postNrs.indexOf(post.post_nr);
- if (index > -1) {
- postNrs.splice(index, 1);
- }
- });
-
- //removing posts older than the time limit (they likely won't resolve)
- var timestampMinusPostRemoveCounter = Math.round(+new Date() / 1000) - postRemoveCounter;
-
- postNrs.forEach(function (post_nr) {
- var postToAddFlagTo = document.getElementById("pc" + post_nr),
- postInfo = postToAddFlagTo.getElementsByClassName('postInfo')[0],
- dateTime = postInfo.getElementsByClassName('dateTime')[0];
-
- if (dateTime.getAttribute("data-utc") < timestampMinusPostRemoveCounter) {
- var index = postNrs.indexOf(post_nr);
- if (index > -1) {
- postNrs.splice(index, 1);
- }
- }
- });
- }
-
- /** fetch flags from db */
- function resolveRefFlags() {
- var boardID = window.location.pathname.split('/')[1];
- if (boardID === "int" || boardID === "sp" || boardID === "pol" || boardID === "bant") {
-
- GM_xmlhttpRequest({
- method: "POST",
- url: backendBaseUrl + getUrl,
- data: "post_nrs=" + encodeURIComponent(postNrs) + "&" + "board=" + encodeURIComponent(boardID),
- headers: {
- "Content-Type": "application/x-www-form-urlencoded"
- },
- onload: onFlagsLoad
- });
- }
- }
-
- /** send flag to system on 4chan x (v2, loadletter, v3 untested) post
- * handy comment to save by ccd0
- * console.log(e.detail.boardID); // board name (string)
- * console.log(e.detail.threadID); // thread number (integer in ccd0, string in loadletter)
- * console.log(e.detail.postID); // post number (integer in ccd0, string in loadletter) */
- document.addEventListener('QRPostSuccessful', function (e) {
- //setTimeout to support greasemonkey 1.x
- setTimeout(function () {
- GM_xmlhttpRequest({
- method: "POST",
- url: backendBaseUrl + postUrl,
- data: "post_nr=" + encodeURIComponent(e.detail.postID) + "&" + "board=" + encodeURIComponent(e.detail.boardID) + "&" + "regions=" +
- encodeURIComponent(regions.slice(1).join(regionDivider)),
- headers: {
- "Content-Type": "application/x-www-form-urlencoded"
- },
- onload: function (response) {
- //hide spam, debug purposes only
- //console.log(response.responseText);
- }
- });
- }, 0);
- }, false);
-
- /** send flag to system on 4chan inline post */
- document.addEventListener('4chanQRPostSuccess', function (e) {
- var boardID = window.location.pathname.split('/')[1];
- var evDetail = e.detail || e.wrappedJSObject.detail;
- //setTimeout to support greasemonkey 1.x
- setTimeout(function () {
- GM_xmlhttpRequest({
- method: "POST",
- url: backendBaseUrl + postUrl,
- data: "post_nr=" + encodeURIComponent(evDetail.postId) + "&" + "board=" + encodeURIComponent(boardID) + "&" + "regions=" +
- encodeURIComponent(regions.slice(1).join(regionDivider)),
- headers: {
- "Content-Type": "application/x-www-form-urlencoded"
- },
- onload: function (response) {
- //hide spam, debug only
- //console.log(response.responseText);
- }
- });
- }, 0);
- }, false);
-
- /** Listen to post updates from the thread updater for 4chan x v2 (loadletter) and v3 (ccd0 + ?) */
- document.addEventListener('ThreadUpdate', function (e) {
- var evDetail = e.detail || e.wrappedJSObject.detail;
- var evDetailClone = typeof cloneInto === 'function' ? cloneInto(evDetail, unsafeWindow) : evDetail;
-
- //ignore if 404 event
- if (evDetail[404] === true) {
- return;
- }
-
- setTimeout(function () {
- //add to temp posts and the DOM element to allPostsOnPage
- evDetailClone.newPosts.forEach(function (post_board_nr) {
- var post_nr = post_board_nr.split('.')[1];
- postNrs.push(post_nr);
- var newPostDomElement = document.getElementById("pc" + post_nr);
- allPostsOnPage.push(newPostDomElement);
- });
-
- }, 0);
- //setTimeout to support greasemonkey 1.x
- setTimeout(resolveRefFlags, 0);
- }, false);
-
- /** Listen to post updates from the thread updater for inline extension */
- document.addEventListener('4chanThreadUpdated', function (e) {
- var evDetail = e.detail || e.wrappedJSObject.detail;
-
- var threadID = window.location.pathname.split('/')[3];
- var postsContainer = Array.prototype.slice.call(document.getElementById('t' + threadID).childNodes);
- var lastPosts = postsContainer.slice(Math.max(postsContainer.length - evDetail.count, 1)); //get the last n elements (where n is evDetail.count)
-
- //add to temp posts and the DOM element to allPostsOnPage
- lastPosts.forEach(function (post_container) {
- var post_nr = post_container.id.replace("pc", "");
- postNrs.push(post_nr);
- allPostsOnPage.push(post_container);
- });
- //setTimeout to support greasemonkey 1.x
- setTimeout(resolveRefFlags, 0);
- }, false);
-
- /** START fix flag alignment on chrome */
- function addGlobalStyle(css) {
- var head, style;
- head = document.getElementsByTagName('head')[0];
- if (!head) {
- return;
- }
- style = document.createElement('style');
- style.type = 'text/css';
- style.innerHTML = css;
- head.appendChild(style);
- }
-
- if (navigator.userAgent.toLowerCase().indexOf('webkit') > -1) {
- addGlobalStyle('.flag{top: 0px !important;left: -1px !important}');
- }
- /** END fix flag alignment on chrome */
-
- /** setup init and start first calls */
- setup.init();
- parseOriginalPosts();
- resolveRefFlags();