#20 Contact finder / complaint UI overhaul
Merged 5 years ago by quidam. Opened 5 years ago by gioma1.
gioma1/librejs feature/complain  into  master

content/contactFinder.js contact_finder.js
file renamed
+117 -86
@@ -2,6 +2,7 @@ 

  * GNU LibreJS - A browser add-on to block nonfree nontrivial JavaScript.

  * *

  * Copyright (C) 2017 Nathan Nichols, Loic J. Duros, Nik Nyby

+ * Copyright (C) 2018 Giorgio Maone

  *

  * This file is part of GNU LibreJS.

  *
@@ -23,8 +24,12 @@ 

  // - open the manifest.json

  // - add a comma after the closing bracket of the key "background"

  // - Copy and paste this after it:

- /* 

- 	"content_scripts": [{"matches": ["<all_urls>"],"js": ["contact_finder.js"]}]

+ /*

+ 	"content_scripts": [{

+       "matches": ["<all_urls>"],

+       "js": ["/content/contactFinder.js"],

+       "css": ["/content/contactFinder.css"]

+     }]

  */

  // Now, the contact finder will load on every page and you can test it where ever you want.

  
@@ -34,7 +39,13 @@ 

  //Copyright (C) 2011, 2012, 2014 Loic J. Duros

  //Copyright (C) 2014, 2015 Nik Nyby

  

- console.log("contact_finder.js");

+ function debug(format, ...args) {

+   console.debug(`LibreJS - ${format}`, ...args);

+ }

+ 

+ var myPort;

+ 

+ debug("Injecting contact finder in %s", document.URL);

  

  // email address regexp

  var reEmail = /^mailto\:(admin|feedback|webmaster|info|contact|support|comments|team|help)\@[a-z0-9.\-]+\.[a-z]{2,4}$/i;
@@ -121,33 +132,14 @@ 

  var prefs;

  

  /**

- *

- *	Creates a transparent floating button from a name string and a callback

- *

- *

- */

- function new_debug_button(name_text,callback){

- 	if(document.getElementById("abc123_main_div") !== null){

- 		document.getElementById("abc123_main_div").remove();

- 	}

- 	console.log("adding button");

- 	if(document.getElementById("abc123_main_div") === null){

- 		var to_insert = '<div style="opacity: 0.5; font-size: small; z-index: 2147483647; position: fixed; right: 1%; top: 4%;" id="abc123_main_div"></div>';

- 		document.body.insertAdjacentHTML('afterbegin', to_insert);

- 	}

- 	var button_html = '<input id="abc123_button_complain" value="' + name_text +'" type="button"></input><br>';

- 	document.getElementById("abc123_main_div").insertAdjacentHTML('afterbegin', button_html);	document.getElementById("abc123_button_complain").addEventListener("click",callback);

- 

- }

- /**

  *	returns input with all elements not of type string removed

  */

  function remove_not_str(a){

- 	var new_a = [];	

+ 	var new_a = [];

  	for(var i in a){

  		if(typeof(a[i]) == "string"){

  			new_a.push(a[i])

- 		}	

+ 		}

  	}

  	return new_a;

  }
@@ -157,7 +149,7 @@ 

  *	Will return either the first regex match from the selected certainty level or all regexes that

  *	match on that certainty level.

  *

- *	certainty_lvl can be "certain" > "probable" > "uncertain" 

+ *	certainty_lvl can be "certain" > "probable" > "uncertain"

  */

  function attempt(certainty_lvl, first=true){

  	// There needs to be some kind of max so that people can't troll by for example leaving a comment with a bunch of emails
@@ -165,8 +157,8 @@ 

  	var fail_flag = true;

  	var flag;

  	var matches = [];

- 	var result = [];	

- 	var str_under_test = "";	

+ 	var result = [];

+ 	var str_under_test = "";

  	for(var i in document.links){

  		if( typeof(document.links[i].innerText) != "string" || typeof(document.links[i].href) != "string"){

  			continue;
@@ -176,10 +168,10 @@ 

  		for(var j in contactStr){

  			for(var k in contactStr[j][certainty_lvl]){

  				if(flag){

- 					result = [];				

+ 					result = [];

  					result = str_under_test.match(new RegExp(contactStr[j][certainty_lvl][k],"g"));

  					result = remove_not_str(result);

- 					if(result !== undefined && typeof(result[0]) == "string" ){				

+ 					if(result !== undefined && typeof(result[0]) == "string" ){

  						if(first){

  							return {"fail":false,"result":document.links[i]};

  						} else{
@@ -197,7 +189,7 @@ 

  }

  

  /**

- *	"LibreJS detects contact pages, email addresses that are likely to be owned by the 

+ *	"LibreJS detects contact pages, email addresses that are likely to be owned by the

  *	maintainer of the site, Twitter and identi.ca links, and phone numbers."

  */

  function find_contacts(){
@@ -207,19 +199,19 @@ 

  	var identi = [];

  	var contact_pages = [];

  	var res = attempt("certain");

- 	var flag = true;	

+ 	var flag = true;

  	var type = "";

  	if(res["fail"] == false){

  		type = "certain";

  		res = res["result"];

- 		flag = false;	

+ 		flag = false;

  	}

  	if(flag){

  		res = attempt("probable");

  		if(res["fail"] == false){

  			type = "probable";

  			res = res["result"];

- 			flag = false;	

+ 			flag = false;

  		}

  	}

  	if(flag){
@@ -227,7 +219,7 @@ 

  		if(res["fail"] == false){

  			type = "uncertain";

  			res = res["result"];

- 			flag = false;		

+ 			flag = false;

  		}

  	}

  	if(flag){
@@ -235,63 +227,102 @@ 

  	}

  	return [type,res];

  }

- // need to have this so the handler doesn't take too long

- function handler(){

- 	var res = find_contacts();

- 	if(document.getElementById("librejs_contactfinder") != null){

- 		document.getElementById("librejs_contactfinder").remove();

- 	}

- 	var to_insert;

- 	if("fail" in res){

- 		to_insert = '<div style="font-size: small; z-index: 2147483647; background-color: #eeeeee;'+

- 						'position: fixed; display:inline-block; border: 3px solid #990000; width: 50%;"'+

- 						' id="librejs_contactfinder">'+

- 						"Contact finder failed.";

- 	} else{

- 		if(typeof(res[1]) == "string"){

- 			to_insert = '<div style="font-size: small; z-index: 2147483647; background-color: #eeeeee;'+

- 							'position: fixed; display:inline-block; border: 3px solid #990000; width: 50%;"'+

- 							' id="librejs_contactfinder"><b>Result:</b><br>'+

- 							res[0] + ": " + '<a href="' + res[1] + '>'+res[1]+'</a>';

- 		}

- 		if(typeof(res[1]) == "object"){

- 			to_insert = '<div style="font-size: small; z-index: 2147483647; background-color: #eeeeee;'+

- 							'position: fixed; display:inline-block; border: 3px solid #990000; width: 50%;"'+

- 							' id="librejs_contactfinder"><b>Result:</b><br>'+

- 							res[0]+": "+res[1].outerHTML;

  

- 		}

- 	}

  

- 	var email = document.documentElement.innerText.match(email_regex);

- 	if(email != null){

- 		var max_i = 0;

- 		if(email.length >= 10){

- 			max_i = 10;

- 		} else{

- 			max_i = email.length;

- 		}

- 		for(var i = 0; i < max_i; i++){

- 			var mailto = "mailto:"+email[i]+"?subject="+encodeURI(prefs["pref_subject"])+"&body="+encodeURI(prefs["pref_body"]);

- 			to_insert += '<br>' + 'Possible email: <a href="' + mailto + '">'+email[i]+'</a>';

- 		}

- 	}

+ function createWidget(id, tag, parent = document.body) {

+   let widget = document.getElementById(id);

+   if (widget)  widget.remove();

+   widget = parent.appendChild(document.createElement(tag));

+   widget.id = id;

+   return widget;

+ }

  

- 	to_insert += '</div>';

+ /**

+ *

+ *	Creates the contact finder / complain UI as a semi-transparent overlay

+ *

+ */

  

- 	setTimeout(function(){document.getElementById("librejs_contactfinder").remove()}, 7500);

- 	document.body.insertAdjacentHTML("afterbegin",to_insert);

- 	return 0;

+ function main() {

+   let overlay = createWidget("_LibreJS_overlay", "div");

+   let frame = createWidget("_LibreJS_frame", "iframe");

  

- }

+   let close = () => {

+     frame.remove();

+     overlay.remove();

+   };

  

- function main(){

- 	new_debug_button("Complain to website",handler);

- }

+   let closeListener = e => {

+     let t = e.currentTarget;

+     if (t.href) { // link navigation

+       if (t.href !== document.URL) {

+         if (t.href.includes("#")) {

+           window.addEventListener("hashchange", close);

+         }

+         return;

+       }

+     }

+     close();

+   };

+   let makeCloser = clickable => clickable.addEventListener("click", closeListener);

+ 

+   makeCloser(overlay);

+ 

+   let initFrame = () => {

+     debug("initFrame");

+     let res = find_contacts();

+     let contentDoc = frame.contentWindow.document;

+     let {body} = contentDoc;

+     body.id = "_LibreJS_dialog";

+     body.innerHTML = `<h1>LibreJS Complaint</h1><button class='close'>x</button>`;

+     contentDoc.documentElement.appendChild(contentDoc.createElement("base")).target = "_top";

+     let content = body.appendChild(contentDoc.createElement("div"));

+     content.id = "content";

+     let addHTML = s => content.insertAdjacentHTML("beforeend", s);

+     if ("fail" in res) {

+       content.classList.toggle("_LibreJS_fail", true)

+   		addHTML("<div>Could not guess any contact page for this site.</div>");

+   	} else {

+       addHTML("<h3>Contact info guessed for this site</h3>");

+   		if(typeof(res[1]) === "string") {

+         let a = contentDoc.createElement("a");

+         a.href = a.textContent = res[1];

+         content.appendChild(a);

+   		} else if (typeof(res[1]) === "object"){

+   			 addHTML(`${res[0]}: ${res[1].outerHTML}`);

+   		}

+   	}

  

- var myPort = browser.runtime.connect({name:"contact_finder"});

+   	let emails = document.documentElement.textContent.match(email_regex);

+   	if (emails  && (emails = Array.filter(emails, e => !!e)).length) {

+       addHTML("<h5>Possible email addresses:</h5>");

+       let list = contentDoc.createElement("ul");

+   		for (let i = 0, max = Math.min(emails.length, 10); i < max; i++) {

+         let recipient = emails[i];

+         let a = contentDoc.createElement("a");

+         a.href = `mailto:${recipient}?subject${

+             encodeURIComponent(prefs["pref_subject"])

+           }&body=${

+             encodeURIComponent(prefs["pref_body"])

+           }`;

+         a.textContent = recipient;

+         list.appendChild(contentDoc.createElement("li")).appendChild(a);

+   		}

+       content.appendChild(list);

+   	}

+     Array.forEach(contentDoc.querySelectorAll(".close, a"), makeCloser);

+     debug("frame initialized");

+   }

+ 

+ 

+ 

+   frame.addEventListener("load", e => {

+     debug("frame loaded");

+     myPort = browser.runtime.connect({name: "contact_finder"}).onMessage.addListener(m => {

+     	prefs = m;

+     	initFrame();

+     });

+   });

+ }

  

- myPort.onMessage.addListener(function(m) {

- 	prefs = m;

- 	main();

- });

+ main();

file added
+91
@@ -0,0 +1,91 @@ 

+ #_LibreJS_dialog {

+   font-family: sans-serif;

+   font-size: 1.2em;

+   color: #000;

+   background-color: #eee;

+   margin: 0;

+   padding: 0;

+   text-align: center;

+ }

+ #_LibreJS_dialog .close {

+   -moz-appearance: none;

+   border: 0;

+   position: fixed;

+   top: .2em;

+   right: .2em;

+   font-size: 1em;

+   font-weight: bold;

+   text-transform: uppercase;

+   color: white;

+   background: black;

+   border-radius: 1em;

+   cursor: pointer;

+   display: block;

+   width: 1.5em;

+   height: 1.5em;

+   text-align: center;

+   vertical-align: middle;

+   padding: 0;

+ }

+ #_LibreJS_dialog .close:hover {

+   background: #800;

+ }

+ 

+  #_LibreJS_dialog h1, #_LibreJS_dialog h2, #_LibreJS_dialog h3 {

+   font-family: sans-serif;

+   color: #000;

+   font-weight: bold;

+   text-align: center;

+   border: none;

+   padding: .2em;

+   margin: .2em;

+   display: block;

+ }

+ 

+ #_LibreJS_dialog a {

+   text-decoration: none;

+   color: #048;

+   border: none;

+ }

+ 

+ #_LibreJS_dialog a:hover {

+   text-decoration: none;

+   color: #06c;

+   border-bottom: 1px solid #048;

+ }

+ 

+ #_LibreJS_dialog h1 {

+   font-size: 1.5em;

+   background: #666;

+   color: white;

+   position: fixed;

+   top: 0;

+   left: 0;

+   width: 100%;

+   margin: 0;

+   padding: .2em;

+ }

+ 

+ #_LibreJS_dialog h3 {

+   font-size: 1.2em !important

+ }

+ 

+ #_LibreJS_dialog h4 {

+   font-size: 1em;

+ }

+ 

+ #_LibreJS_dialog #content {

+   position: fixed;

+   overflow: auto;

+   top: 2.5em;

+   bottom: 0;

+   left: 0;

+   right: 0;

+   width: 100%;

+ }

+ 

+ #_LibreJS_dialog #content ul {

+   list-style: none;

+   margin: 0;

+   padding: 0;"

+ }

file added
+28
@@ -0,0 +1,28 @@ 

+ #_LibreJS_overlay {

+   z-index: 2147483647 !important;

+   background-color: #000;

+   opacity: 0.7 !important;

+   display: block !important;

+   position: fixed !important;

+   margin: 0 !important;

+   padding: 0 !important;

+   top: 0 !important;

+   bottom: 0 !important;

+   left: 0 !important;

+   right: 0 !important;

+ }

+ 

+ #_LibreJS_frame {

+   width: 60% !important;

+   height: 80% !important;

+   top: 10% !important;

+   bottom: 10% !important;

+   left: 20% !important;

+   right: 20% !important;

+   overflow: auto !important;

+   position: fixed !important;

+   display:inline-block !important;

+   border: 2px solid #444 !important;

+   z-index: 2147483647 !important;

+   background: #eee;

+ }

@@ -149,9 +149,3 @@ 

    width: 100%;

    text-align: center;

  }

- 

- 

- 

- #complain {

-   display: none; /* TODO: Complaint to owner UI */

- }

@@ -71,15 +71,9 @@ 

  

        <fieldset id="section-complaint"><legend>Complaint email defaults</legend>

          <label for="pref_subject">Subject</label>

-         <input id="pref_subject" type="text"

-           value="Issues with Javascript on your website"

-           />

+         <input id="pref_subject" type="text"/>

          <label for="pref_body">Body</label>

-         <textarea id="pref_body" rows="5"

- >Please consider using a free license for the Javascript on your website.

- 

- [Message generated by LibreJS. See https://www.gnu.org/software/librejs/ for more information]

- </textarea>

+         <textarea id="pref_body" rows="5"></textarea>

        </fieldset>

      </div>

    </body>

@@ -85,7 +85,3 @@ 

    background: #ffe;

    color: #800;

  }

- 

- #section-complaint {

-   display: none; /* TODO: Complaint to owner UI */

- }

file modified
+42 -13
@@ -339,13 +339,18 @@ 

  *

  */

  var portFromCS;

- function connected(p) {

- 	if(p["name"] == "contact_finder"){

+ async function connected(p) {

+ 	if(p.name === "contact_finder"){

+ 		// style the contact finder panel

+ 		await browser.tabs.insertCSS(p.sender.tab.id, {

+ 			file: "/content/dialog.css",

+ 			cssOrigin: "user",

+ 			matchAboutBlank: true,

+ 			allFrames: true

+ 		});

+ 

  		// Send a message back with the relevant settings

- 		function cb(items){

- 			p.postMessage(items);

- 		}

- 		browser.storage.local.get(cb);

+ 		p.postMessage(await browser.storage.local.get(["prefs_subject", "prefs_body"]));

  		return;

  	}

  	p.onMessage.addListener(async function(m) {
@@ -372,7 +377,7 @@ 

  		// invoke_contact_finder

  		if(m["invoke_contact_finder"] !== undefined){

  			contact_finder = true;

- 			inject_contact_finder();

+ 			await injectContactFinder();

  		}

  		// a debug feature (maybe give the user an option to do this?)

  		if(m["deletelocalstorage"] !== undefined){
@@ -1120,11 +1125,35 @@ 

  			.map(script => script.hash)

  	);

  

+ 

+ async function initDefaults() {

+ 	let defaults = {

+ 		pref_subject: "Issues with Javascript on your website",

+ 		pref_body: `Please consider using a free license for the Javascript on your website.

+ 

+ [Message generated by LibreJS. See https://www.gnu.org/software/librejs/ for more information]

+ `

+ 	};

+ 	let keys = Object.keys(defaults);

+ 	let prefs = await browser.storage.local.get(keys);

+ 	let changed = false;

+ 	for (let k of keys) {

+ 		if (!(k in prefs)) {

+ 			prefs[k] = defaults[k];

+ 			changed = true;

+ 		}

+ 	}

+ 	if (changed) {

+ 		await browser.storage.local.set(prefs);

+ 	}

+ }

+ 

  /**

  *	Initializes various add-on functions

  *	only meant to be called once when the script starts

  */

- async function init_addon(){

+ async function init_addon() {

+ 	await initDefaults();

  	await whitelist.load();

  	browser.runtime.onConnect.addListener(connected);

  	browser.storage.onChanged.addListener(options_listener);
@@ -1154,11 +1183,11 @@ 

  /**

  *	Loads the contact finder on the given tab ID.

  */

- function inject_contact_finder(tab_id){

- 	function executed(result) {

- 	  dbg_print("[TABID:"+tab_id+"]"+"finished executing contact finder: " + result);

- 	}

- 	var executing = browser.tabs.executeScript(tab_id, {file: "/contact_finder.js"}, executed);

+ async function injectContactFinder(tabId){

+ 	await Promise.all([

+ 		browser.tabs.insertCSS(tabId, {file: "/content/overlay.css", cssOrigin: "user"}),

+ 		browser.tabs.executeScript(tabId, {file: "/content/contactFinder.js"}),

+  ]);

  }

  

  init_addon();

Notice: since it could not be developed without compiling against latest Acorn, this PR includes and supersedes PR #19, whose branch has been merged while the work was in progress.

Here we refactored the complaint UI to use a separate CSS, safer and faster HTML building and less clicks to be operated (by removing the intermediate "Complain to website" tab/button and going straight to the floating panel).
Nevertheless we kept around a branch preserving the "Complain to website" side tab/button, just in case there's a rationale to reintroduce it.

rebased onto 475a6b9

5 years ago

1 new commit added

  • Improved and partially refactored floating dialog.
5 years ago

3 new commits added

  • Improved and partially refactored floating dialog.
  • Contact finder / complaint UI overhaul.
  • Initial refactoring and unhiding of contact finder / complaint UI.
5 years ago

Pull-Request has been merged by quidam

5 years ago