// ==UserScript==
// @name Language Switcher for Google™
// @author gb.ianni shunf4
// @namespace gb.ianni
// @version 2.3.4
// @copyright Copyright 2015-2022 gb.ianni shunf4
// @description a port of Language Switcher for Google™ Chome extension (https://chrome.google.com/webstore/detail/language-switcher-for-goo/jbbappjmafnnhhalfbhdhiemchcgejcp) to userscript.js
// @include *://www.google.com/*
// @include *://www.google.ad/*
// @include *://www.google.ae/*
// @include *://www.google.com.af/*
// @include *://www.google.com.ag/*
// @include *://www.google.com.ai/*
// @include *://www.google.am/*
// @include *://www.google.co.ao/*
// @include *://www.google.com.ar/*
// @include *://www.google.as/*
// @include *://www.google.at/*
// @include *://www.google.com.au/*
// @include *://www.google.az/*
// @include *://www.google.ba/*
// @include *://www.google.com.bd/*
// @include *://www.google.be/*
// @include *://www.google.bf/*
// @include *://www.google.bg/*
// @include *://www.google.com.bh/*
// @include *://www.google.bi/*
// @include *://www.google.bj/*
// @include *://www.google.com.bn/*
// @include *://www.google.com.bo/*
// @include *://www.google.com.br/*
// @include *://www.google.bs/*
// @include *://www.google.co.bw/*
// @include *://www.google.by/*
// @include *://www.google.com.bz/*
// @include *://www.google.ca/*
// @include *://www.google.cd/*
// @include *://www.google.cf/*
// @include *://www.google.cg/*
// @include *://www.google.ch/*
// @include *://www.google.ci/*
// @include *://www.google.co.ck/*
// @include *://www.google.cl/*
// @include *://www.google.cm/*
// @include *://www.google.cn/*
// @include *://www.google.com.co/*
// @include *://www.google.co.cr/*
// @include *://www.google.com.cu/*
// @include *://www.google.cz/*
// @include *://www.google.de/*
// @include *://www.google.dj/*
// @include *://www.google.dk/*
// @include *://www.google.dm/*
// @include *://www.google.com.do/*
// @include *://www.google.dz/*
// @include *://www.google.com.ec/*
// @include *://www.google.ee/*
// @include *://www.google.com.eg/*
// @include *://www.google.es/*
// @include *://www.google.com.et/*
// @include *://www.google.fi/*
// @include *://www.google.com.fj/*
// @include *://www.google.fm/*
// @include *://www.google.fr/*
// @include *://www.google.ga/*
// @include *://www.google.ge/*
// @include *://www.google.gg/*
// @include *://www.google.com.gh/*
// @include *://www.google.com.gi/*
// @include *://www.google.gl/*
// @include *://www.google.gm/*
// @include *://www.google.gp/*
// @include *://www.google.gr/*
// @include *://www.google.com.gt/*
// @include *://www.google.gy/*
// @include *://www.google.com.hk/*
// @include *://www.google.hn/*
// @include *://www.google.hr/*
// @include *://www.google.ht/*
// @include *://www.google.hu/*
// @include *://www.google.co.id/*
// @include *://www.google.ie/*
// @include *://www.google.co.il/*
// @include *://www.google.im/*
// @include *://www.google.co.in/*
// @include *://www.google.iq/*
// @include *://www.google.is/*
// @include *://www.google.it/*
// @include *://www.google.je/*
// @include *://www.google.com.jm/*
// @include *://www.google.jo/*
// @include *://www.google.co.jp/*
// @include *://www.google.co.ke/*
// @include *://www.google.com.kh/*
// @include *://www.google.ki/*
// @include *://www.google.kg/*
// @include *://www.google.co.kr/*
// @include *://www.google.com.kw/*
// @include *://www.google.kz/*
// @include *://www.google.la/*
// @include *://www.google.com.lb/*
// @include *://www.google.li/*
// @include *://www.google.lk/*
// @include *://www.google.co.ls/*
// @include *://www.google.lt/*
// @include *://www.google.lu/*
// @include *://www.google.lv/*
// @include *://www.google.com.ly/*
// @include *://www.google.co.ma/*
// @include *://www.google.md/*
// @include *://www.google.me/*
// @include *://www.google.mg/*
// @include *://www.google.mk/*
// @include *://www.google.ml/*
// @include *://www.google.mn/*
// @include *://www.google.ms/*
// @include *://www.google.com.mt/*
// @include *://www.google.mu/*
// @include *://www.google.mv/*
// @include *://www.google.mw/*
// @include *://www.google.com.mx/*
// @include *://www.google.com.my/*
// @include *://www.google.co.mz/*
// @include *://www.google.com.na/*
// @include *://www.google.com.nf/*
// @include *://www.google.com.ng/*
// @include *://www.google.com.ni/*
// @include *://www.google.ne/*
// @include *://www.google.nl/*
// @include *://www.google.no/*
// @include *://www.google.com.np/*
// @include *://www.google.nr/*
// @include *://www.google.nu/*
// @include *://www.google.co.nz/*
// @include *://www.google.com.om/*
// @include *://www.google.com.pa/*
// @include *://www.google.com.pe/*
// @include *://www.google.com.ph/*
// @include *://www.google.com.pk/*
// @include *://www.google.pl/*
// @include *://www.google.pn/*
// @include *://www.google.com.pr/*
// @include *://www.google.ps/*
// @include *://www.google.pt/*
// @include *://www.google.com.py/*
// @include *://www.google.com.qa/*
// @include *://www.google.ro/*
// @include *://www.google.ru/*
// @include *://www.google.rw/*
// @include *://www.google.com.sa/*
// @include *://www.google.com.sb/*
// @include *://www.google.sc/*
// @include *://www.google.se/*
// @include *://www.google.com.sg/*
// @include *://www.google.sh/*
// @include *://www.google.si/*
// @include *://www.google.sk/*
// @include *://www.google.com.sl/*
// @include *://www.google.sn/*
// @include *://www.google.sm/*
// @include *://www.google.st/*
// @include *://www.google.com.sv/*
// @include *://www.google.td/*
// @include *://www.google.tg/*
// @include *://www.google.co.th/*
// @include *://www.google.com.tj/*
// @include *://www.google.tk/*
// @include *://www.google.tl/*
// @include *://www.google.tm/*
// @include *://www.google.tn/*
// @include *://www.google.to/*
// @include *://www.google.com.tr/*
// @include *://www.google.tt/*
// @include *://www.google.com.tw/*
// @include *://www.google.co.tz/*
// @include *://www.google.com.ua/*
// @include *://www.google.co.ug/*
// @include *://www.google.co.uk/*
// @include *://www.google.com.uy/*
// @include *://www.google.co.uz/*
// @include *://www.google.com.vc/*
// @include *://www.google.co.ve/*
// @include *://www.google.vg/*
// @include *://www.google.co.vi/*
// @include *://www.google.com.vn/*
// @include *://www.google.vu/*
// @include *://www.google.ws/*
// @include *://www.google.rs/*
// @include *://www.google.co.za/*
// @include *://www.google.co.zm/*
// @include *://www.google.co.zw/*
// ==/UserScript==
var debug = false;
cLog("Start");
//
// Parses and puts in a hash map CGI URL parameters.
//
cLog("Window href:" + window.location.href);
//debugLayer = { 'FORM3' : 1, 'PROF' : 1 };
debugLayer = { 'INJ': 1 };
var vars = null;
function getUrlVars() {
if (!vars) {
vars = [];
var hash;
var positionQ = window.location.href.indexOf('?');
var positionS = window.location.href.indexOf('#');
if (positionQ > 0 && positionS > 0)
position = Math.min(positionQ, positionS);
else if (positionQ > 0)
position = positionQ;
else position = positionS;
var hashes = window.location.href.slice(position + 1).split(/&|#/);
for (var i = 0; i < hashes.length; i++) {
cLog('VALUE:' + hashes[i]);
hash = hashes[i].split('=');
//vars.push(hash[0]);
vars[hash[0]] = hash[1];
}
// Possibly missing hl value is filled with languages value corresponding to TLD value
if (!vars['hl'])
vars['hl'] = domainsToLang[getTLD()];
// Possibly missing lr value is filled with languages value corresponding to TLD value
if (!vars['lr'])
vars['lr'] = "lang_" + domainsToLang[getTLD()];
}
return vars;
}
function getTLD() {
var tld = window.location.href.match(/\.(\w+?)(\/|$)/)[1];
cLog('TLD=' + tld);
return tld;
}
cLog('TLD:' + getTLD());
//
// Main
//
//var addPath = "/html/body/div/*/div/div[2]/div";
//var addPath = "/html/body/div/div/div[2]/div[@id='resultStats']";
//var addPath = "/html/body/div[@id='main']/div[@id='cnt']/div[@id='subform_ctrl']";
//var addSelector = '#resultStats';
var addPaths = ["//*[@id='resultStats']", "//*[@id='gbx1']"];
//
// Add Possible Injection points below
//
// Places where to try injection of Language switcher box
// var addSelectors = { "#resultStats": 1, "#slim_appbar": 1, "#extabar": 1, "#topabar": 1, ".med": 1, ".g": 1, ".appbar": 1 };
var addSelectors = [ ["#rhs > div:first-of-type", "addBeforeElem", ""], ["#appbar", "addAfterElem", "float:right;"], ];
var languageVars = ["hl", "#hl"];
var documentFormName = 'gbqf';
/*
* Injected Code for querying as a different language
*/
var labels = ["en-US", "it", "fr", "de", "es", "cn/hk", "jp", "ru", "nl"];
//,"nl","ru","gr","jp","tr","cn","arb","il");
var languages = ["en", "it", "fr", "de", "es", "zh-CN", "ja", "ru", "nl"];
//,"nl","ru","el","ja","tr","zh-CN","ar","iw");
var domains = ["com", "it", "fr", "de", "es", "com.hk", "co.jp", "ru", "nl"];
var tlds = ["com", "it", "fr", "de", "es", "cn", "jp", "ru", "nl"];
var wikipediaDomains = ["en", "it", "fr", "de", "es", "zh", "ja", "ru", "nl"];
var domainsToLang = [];
for (var i = 0; i < domains.length; i++) {
domainsToLang[tlds[i]] = languages[i];
}
var injectedObjectName = 'AGB_language_switcher';
var status = new Array();
var radioOn = "checked=\"checked\"";
var radioOff = "";
for (var i = 0; i < labels.length; i++) {
if ("lang_" + languages[i] != getUrlVars()["lr"])
status[languages[i]] = radioOff;
else
status[languages[i]] = radioOn;
}
cLog("GetURlVars=" + getUrlVars());
cLog("n_lang is=" + getUrlVars()["n_lang"]);
cLog("hl is=" + getUrlVars()["hl"]);
cLog("lr is=" + getUrlVars()["lr"]);
cLog("q is=" + getUrlVars()["q"]);
cLog("Languages[n_lang] is=" + status[getUrlVars()["hl"]]);
var inject = "<div id='" + injectedObjectName + "' class='q qs'><br>Ask again on another local version: \
<table width='100%' border='0' cellpadding='0' cellspacing='0' style='position:static'><td> \
<table border='0' cellpadding='0' cellspacing='0'> \
<tbody><tr><th scope='row' align='left'>Google:</th>";
//
// Prepare code for radio buttons to be injected in Google page results
//
var googleInjectFuncMap = {};
for (let i = 0; i < languages.length; i++) {
googleInjectFuncMap["label_g_" + languages[i]] = function() {
var a = window.location.href.slice(0);
a = a.replace(/google\.(.*?)\//, 'google.' + domains[i] + '/');
if (!a.match(/(\?|\&)hl=/)) {
a = a + '&hl=';
}
if (!a.match(/(\?|\&)lr=/)) {
a = a + '&lr=';
}
a = a.replace(/hl=(.*?)(&|$)/, 'hl=' + languages[i] + '$2');
a = a.replace(/lr=(.*?)(&|$)/, 'lr=lang_' + languages[i] + '$2');
window.location.href = a;
}
//var clicInject = "document."+documentFormName+".hl.value ='"+languages[i]+"'; document."+documentFormName+".submit(); ";
cLog("Injecting " + domains[i] + " code");
var checked = ("lang_" + languages[i] == getUrlVars()["lr"]) && (tlds[i] == getTLD() || true);
inject += "<td nowrap valign='middle'><label id='label_g_" + languages[i] + "' for=il \
style=\"font-size:large;\"><input id='g_" + languages[i] + "' " + (checked ? ' checked ' : '') + "type=radio name=n_lang value='" + languages[i] + "'>" + labels[i] + "</label></td>";
}
inject += "<td> </td>";
cLog("This is what I'm injecting:" + inject);
/*
function getClicFunction(i)
{
return function() { document[documentFormName].hl.value = languages[i]; document[documentFormName].submit(); };
}
for (var i = 0; i < languages.length; i++)
{
inject.append($('<td>').attr({ 'valign' : 'middle' })
.append($('<input>').attr( { id : 'g_'+languages[i], type : 'radio', name : 'n_lang', value : languages[i]}
)
));
}
*/
//
// Adds Wikipedia radio buttons. Constructs xx.wikipedia.org for xx taken from wikipediaDomains[]
//
inject += "</tr><tr><th scope='row' align='left'>Wikipedia:</th>";
var query = getUrlVars()["q"];
for (var i = 0; i < languages.length; i++) {
var clicWikiInject = "window.location = 'http://" + wikipediaDomains[i] + ".wikipedia.org/?search=" + query + "';";
inject += "<td nowrap valign='middle'><input id=g_w" + languages[i] + " type=radio name=n_lang value='w" + languages[i] + "' \
onClick=\""+ clicWikiInject + "\"><label for=il>" + labels[i] + "</label></td>";
}
inject += "<td> </td></tr></tbody></table></td></table>";
tryInjection();
document.addEventListener("DOMNodeInserted", function (event) {
var element = event.target;
//cLog("Node INSERTED "+element.name);
//if (element.className == 'med') {
tryInjection();
//}
});
var alreadyInjected = false;
function tryInjection() {
if (alreadyInjected) return;
var placeToInject = null;
if (!document.getElementById(injectedObjectName)) {
lLog("Starting injection", "INJ");
var selector;
var insertMethod;
var extraStyles;
for (var pair of addSelectors) {
selector = pair[0];
insertMethod = pair[1];
extraStyles = pair[2];
lLog("Attempting injection point: " + selector, "INJ");
if (placeToInject = document.querySelectorAll(selector)[0]) {
lLog("Matched Selector:" + selector, "INJ"); break;
}
}
lLog("Place to inject:" + placeToInject, "INJ");
if (placeToInject) {
//placeToInject.innerHTML = "<td>"+inject+"</td></div>" + placeToInject.innerHTML;
// document.querySelectorAll("#appbar").forEach(el => { el.style.zIndex = 999 });
document.querySelectorAll(selector).forEach(el => {
var mainElem = new DOMParser().parseFromString(inject, "text/html").body.firstChild;
if (insertMethod === "addBeforeElem") {
el.before(mainElem);
} else {
el.after(mainElem);
}
mainElem.setAttribute("style", extraStyles);
});
for (var labelId in googleInjectFuncMap) {
document.getElementById(labelId).onclick = googleInjectFuncMap[labelId];
}
alreadyInjected = true;
}
lLog("Ending injection", "INJ");
}
else lLog("Looks like there is already a language_switcher here.", "INJ2");
}
function cLog(str) {
if (debug) console.log(str);
}
/**
* main logging function
* @param str String to be printed
* @param level debugLayer triggering the printout. Will be printed only if level is in the currently active debugLayers
*/
function lLog(str, level) {
//console.log("Logging:"+str);
if (!isNaN(level))
str += " USING NUMBERS FOR DEBUG LEVELS IS DEPRECATED - ";
else if (level.match(/PROF/)) {
str = this.debug.getPace() + "\n" + str;
};
// console.log("mid Logging:"+this.debugLayer+" "+this.localLog);
if (debugLayer == level || debugLayer == "ALL" || (debugLayer instanceof Object && debugLayer[level])) { cLog(str); }
//console.log("end Logging:"+str);
};
//
// XPath evaluation wrapping function.
//
function xpath(query, node) {
return document.evaluate(query, node, null,
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
}
//
// Returns the first element of a given XPath query
//
function fXPath(query, node) {
var elements = xpath(query, node);
return elements.snapshotItem(0);
}
//
// Returns the last element of a given XPath query
//
function lXPath(query, node) {
cLog("Asking for " + query);
var elements = xpath(query, node);
cLog("Elements are " + elements.snapshotLength);
return elements.snapshotItem(elements.snapshotLength - 1);
}
//
// 0-ize NaN and non-number strings
//
function normalize(str) {
var temp = (str != null) ? parseInt(str) : 0;
return (isNaN(temp)) ? 0 : temp;
}