Chrome extension web inspector - google-chrome

I want to write a chrome extension where it is possible to mark features on a website and save it in a table, for this, I want to use the Chrome web inspector.
Unfortunately, I am new to this area (chrome plugins) and therefore I am looking for help (links, tutorials, related work etc.) to use the web inspector in my own extension.
Simple example on this website https://ieeexplore.ieee.org/document/1005630.
My idea is to mark for example the date of publication, and the plugin write the complete div to a table.

actually, I found a simple solution.
Sample
http://g.recordit.co/5CCFjXpe8J.gif
It's only a small part of my tool to keep it simple.
The main idea comes from Google Chrome Extension: highlight the div that the mouse is hovering over
'iframe' is the injected sidebar
marker.js contains the script to mark divs
manifest.json
{
"name": "Feature extractor",
"version": "1.0",
"description": "Feature extractor from website",
"permissions": ["activeTab", "declarativeContent", "storage", "contextMenus", "<all_urls>", "tabs"],
"browser_action": {},
"web_accessible_resources": ["iframe.html","iframe.js"],
"background": {
"scripts": ["background.js"],
"persistent": false
},
"content_scripts": [
{
"matches": [
"http://*/*",
"https://*/*"
],
"css": [
"marker.css"
],
"js": [
"js/jquery-1.8.3.min.js",
"marker.js"
]
}
],
"manifest_version": 2
}
background.js
'use strict';
chrome.runtime.onInstalled.addListener(function() {
chrome.declarativeContent.onPageChanged.removeRules(undefined, function() {
chrome.declarativeContent.onPageChanged.addRules([{
conditions: [new chrome.declarativeContent.PageStateMatcher({
pageUrl: {hostEquals: 'developer.chrome.com'},
})],
actions: [new chrome.declarativeContent.ShowPageAction()]
}]);
});
});
// sidebar
chrome.browserAction.onClicked.addListener(function(){
chrome.tabs.query({active: true, currentWindow: true}, function(tabs){
chrome.tabs.sendMessage(tabs[0].id,"toggle");
})
});
// message passing
chrome.runtime.onMessage.addListener(function(request, sender, callback) {
console.log(request);
callback({'request':request});
});
// context menu
var labels = ['author','date','abstract']
for(var label in labels) {
console.log(labels[label])
chrome.contextMenus.create({id: labels[label], "title": labels[label], "contexts":['all']});
}
chrome.contextMenus.onClicked.addListener(function(info, tab) {
if (info.menuItemId == labels[0]) {
chrome.tabs.query({active: true, currentWindow: true}, function(tabs){
chrome.tabs.sendMessage(tabs[0].id,labels[0]);
})
}
});
iframe.html
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="css/jquery.mobile-1.2.1.min.css" />
<script src="js/jquery-1.8.3.min.js"></script>
<script src="js/jquery.mobile-1.2.1.min.js"></script>
<script src="iframe.js"></script>
</head>
<body>
<button id="send">
send
</button>
<div id="responses">
</div>
</body>
</html>
I need the jQuery.fn.. script to identify the selected div Get unique selector of element in Jquery
iframe.js
// unique selector
jQuery.fn.extend({
getPath: function () {
var path, node = this;
while (node.length) {
var realNode = node[0], name = realNode.localName;
if (!name) break;
name = name.toLowerCase();
var parent = node.parent();
var sameTagSiblings = parent.children(name);
if (sameTagSiblings.length > 1) {
var allSiblings = parent.children();
var index = allSiblings.index(realNode) + 1;
if (index > 1) {
name += ':nth-child(' + index + ')';
}
}
path = name + (path ? '>' + path : '');
node = parent;
}
return path;
}
});
window.addEventListener('DOMContentLoaded', function () {
var callback = function (data) {
$("#responses").append("<div>" + data + "</div>");
};
var send = function () {
chrome.runtime.sendMessage(Date(), callback);
}
chrome.runtime.onMessage.addListener(function(msg, data){
if (msg.command == "append-author") {
$("#responses").append("<div>" + msg.el + "</div>")
}
})
document.getElementById('send').addEventListener('click', send);
});
Google Chrome Extension: highlight the div that the mouse is hovering over
marker.js
// Unique ID for the className.
var MOUSE_VISITED_CLASSNAME = 'crx_mouse_visited';
var MOUSE_MARKED_CLASSNAME = 'crx_mouse_marked';
// Previous dom, that we want to track, so we can remove the previous styling.
var prevDOM = null;
// Mouse listener for any move event on the current document.
document.addEventListener('mousemove', function (e) {
let srcElement = e.srcElement;
// Lets check if our underlying element is a IMG.
if (prevDOM != srcElement && srcElement.nodeName == 'DIV' ) {
// For NPE checking, we check safely. We need to remove the class name
// Since we will be styling the new one after.
if (prevDOM != null) {
prevDOM.classList.remove(MOUSE_VISITED_CLASSNAME);
}
// Add a visited class name to the element. So we can style it.
srcElement.classList.add(MOUSE_VISITED_CLASSNAME);
// The current element is now the previous. So we can remove the class
// during the next ieration.
prevDOM = srcElement;
// console.info(srcElement.currentSrc);
// console.dir(srcElement);
}
}, false);
var iframe = document.createElement('iframe');
iframe.style.background = "green";
iframe.id = "comm-test-container";
iframe.style.height = "100%";
iframe.style.width = "0px";
iframe.style.position = "fixed";
iframe.style.top = "0px";
iframe.style.right = "0px";
iframe.style.zIndex = "9000000000000000000";
iframe.frameBorder = "none";
iframe.src = chrome.extension.getURL("iframe.html")
document.body.appendChild(iframe);
function toggle(){
if(iframe.style.width == "0px") {
iframe.style.width="400px";
} else {
iframe.style.width="0px";
}
}
chrome.runtime.onMessage.addListener(function(msg, sender){
if(msg == "toggle"){
toggle();
}
if(msg == "author") {
prevDOM.classList.add(MOUSE_MARKED_CLASSNAME);
chrome.runtime.sendMessage({command:"append-author",el:prevDOM.innerHTML,selector:$(prevDOM).getPath()}, function(response) {});
}
})
// find unique selector
jQuery.fn.extend({
getPath: function () {
var path, node = this;
while (node.length) {
var realNode = node[0], name = realNode.localName;
if (!name) break;
name = name.toLowerCase();
var parent = node.parent();
var sameTagSiblings = parent.children(name);
if (sameTagSiblings.length > 1) {
var allSiblings = parent.children();
var index = allSiblings.index(realNode) + 1;
if (index > 1) {
name += ':nth-child(' + index + ')';
}
}
path = name + (path ? '>' + path : '');
node = parent;
}
return path;
}
});
marker.css
.crx_mouse_visited {
background-clip: #bcd5eb!important;
outline: 1px dashed #e9af6e!important;
z-index : 0!important;
}
.crx_mouse_marked {
background-clip: #bcd5eb!important;
outline: 5px solid #e9af6e!important;
z-index : 0!important;
}

Related

Adding nodes to bootstrap treeview dynamically

I currently have a huge JSON file (over 15k lines, size might increase) with which I want to construct a bootstrap-treeview. Adding all the nodes would make loading of page really slow, so I plan to create a service to fetch JSON of selected nodes and populate the treeview accordingly. This is what I have right now.
<!DOCTYPE html>
<html>
<head>
<title>Bootstrap Tree View</title>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<link href="./TreeView_files/bootstrap-treeview.css" rel="stylesheet">
</head>
<body>
<div class="container">
<h1>Bootstrap Tree View - DOM Tree</h1>
<br/>
<div class="row">
<div class="col-sm-12">
<label for="treeview"></label>
<div id="treeview"/>
</div>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script src="./TreeView_files/bootstrap-treeview.js"></script>
<script type="text/javascript">
function buildDomTree() {
var tree = [
{
text: "Parent 1",
nodes: [
{
text: "Child 1",
nodes: [
{
text: "Grandchild 1"
},
{
text: "Grandchild 2"
}
]
},
{
text: "Child 2"
}
]
},
{
text: "Parent 2"
},
{
text: "Parent 3"
},
{
text: "Parent 4"
},
{
text: "Parent 5"
}
];
return tree;
}
$(function() {
var options = {
bootstrap2: false,
showTags: true,
levels: 5,
data: buildDomTree()
};
$('#treeview').treeview(options);
});
</script>
</body>
Treeview can go 4 levels deep, and I want to fetch next level JSON each time a node is clicked. So, if I click on "Parent 5", it should pop out sub nodes.
I have no clue how to add nodes dynamically. Any help would be really appreciated.
Answer on request: I have found a way to do it, although, it is a tad inefficient. What I've done is, I keep a state of all expanded nodes, and when I click on a node to expand it, I make an HTTP request, add the new nodes to the old one, redraw the entire tree and re-expand all the previously expanded nodes. I know this is inefficient, but it is the only way I could find without going into the nitty-gritty and essentially recreating the entire tree myself (which is just a glorified recursion application).
Here's the code I ran with when I posted the question. There is obviously room for improvement.
var expandedNodes = [];
var tree = [];
$(function()
{
$.post("http://localhost:8000/getLevel1", function( data )
{
var JSObject = JSON.parse(data);
for (j in JSObject)
tree.push(JSObject[j]);
createTree();
});
});
function createTree(){
var options = {
bootstrap2: false,
showTags: true,
levels: 0,
data: tree,
expandIcon: 'fa fa-chevron-right',
collapseIcon: 'fa fa-chevron-down',
onNodeExpanded: nodeExpand,
onNodeCollapsed: nodeCollapse,
onNodeSelected: nodeSelect,
onNodeUnselected: nodeUnselect
}
$('#treeview').treeview(options);
for (node in expandedNodes)
$('#treeview').treeview('expandNode', [ expandedNodes[node], { levels: 0, silent: true } ]);
$('#treeview').treeview('expandNode', 0, { silent: true } );
};
function nodeExpand(event, data)
{
expandedNodes.push(data.nodeId);
var requestObject = []
requestObject.push(data.text);
var parent, dummy = data;
while ((parent = $('#treeview').treeview('getParent', dummy.nodeId))["nodeId"] != undefined)
{
requestObject.push(parent.text);
dummy = parent;
}
$.post("http://localhost:8000/getNode?param=" + JSON.stringify(requestObject), function(retVal)
{
var JSObject = JSON.parse(retVal);
var node = findNode(requestObject);
node.nodes = JSObject;
createTree();
});
}
function nodeCollapse(event, data)
{
var index = expandedNodes.indexOf(data.nodeId);
if (index > -1)
expandedNodes.splice(index, 1);
}
function nodeSelect(event, data)
{
if (data.state.expanded == true)
$('#treeview').treeview('collapseNode', data.nodeId);
else
$('#treeview').treeview('expandNode', data.nodeId);
//$('#treeview').treeview('unselectNode', [ data.nodeId, { silent: true } ]);
}
function nodeUnselect(event, data)
{
}
function findNode(array)
{
var searchIn = tree; //array
var lastFound = tree;
for (var i = array.length - 1; i >= 0; i--)
{
var obj = searchInObject(searchIn, array[i]);
searchIn = obj.nodes;
lastFound = obj;
}
return lastFound;
}
function searchInObject(objectArray, string)
{
for (var index in objectArray)
if (objectArray[index].text == string)
return objectArray[index];
}
$(document).ready(function () {
var trigger = $('.hamburger'),
overlay = $('.overlay'),
isClosed = false;
hamburger_cross();
$('#wrapper').toggleClass('toggled');
trigger.click(function () {
hamburger_cross();
});
function hamburger_cross() {
if (isClosed == true) {
overlay.hide();
trigger.removeClass('is-open');
trigger.addClass('is-closed');
isClosed = false;
$('#open_arrow').removeClass('fa-chevron-circle-left').addClass('fa-chevron-circle-right');
} else {
overlay.show();
trigger.removeClass('is-closed');
trigger.addClass('is-open');
isClosed = true;
$('#open_arrow').removeClass('fa-chevron-circle-right').addClass('fa-chevron-circle-left');
}
}
$('[data-toggle="offcanvas"]').click(function () {
$('#wrapper').toggleClass('toggled');
});
});
Point of interest would be nodeExpand and createTree methods.

Quill strips out simple html when dangerouslyPasteHTML into editor

<style>
#editor-container {
height: 375px;
}
.link {
color:blue;
}
</style>
<div id="editor-container">
This is a test
</div>
<script type="text/javascript">
var quill = new Quill('#editor-container', {
modules: {
toolbar: [
[{ header: [1, 2, false] }],
['bold', 'italic', 'underline'],
['image', 'code-block']
]
},
placeholder: 'Compose an epic...',
theme: 'bubble' // or 'bubble'
});
quill.clipboard.dangerouslyPasteHTML(5, "<span class=\"link\" data-test=\"test\">testing</span>", "silent");
</script>
MVCE - https://codepen.io/anon/pen/QMQMee
The HTML get stripped out despite being pretty harmless (this will be handled better later).
My current plan, due to the way Quill does not allow pasted html, is (As part of a click action on the mentioned person's name):
$("#tag-selectable-users-list li").on("click",
function() {
var $this = $(this);
var startIndex = $this.data("data-start-index");
var userName = $this.data("data-user-name");
var userId = $this.data("data-user-id");
var taggedUserIds = $("#hiddenTaggedUsers");
taggedUserIds.val((taggedUserIds.val()||"") + ";" + userId);
var delta = [];
if (startIndex > 0) {
//retain up to the tag start
delta.push({ retain: parseInt(startIndex) });
}
//delete the junk
delta.push({ delete: tagStatus.Total.length });
//insert the new characters
delta.push({
insert: "##" + userName,
attributes: {
color: "blue",
underline: "true"
}
});
//insert a blank space to end the span
delta.push({ insert: " " });
quill.updateContents(delta,
'api');
});
}

Apple tvOS Javascript stop Video AutoPlay upon selection

Using MenuBar template and have menu working. However, say you hover over a top level menu items like videos. The Video page automatically loads within the presentation area. BUT when you go to select a video it begins to autoplay before a user click thus inhibiting users from selecting other videos but the first video. I simply want the videos not to autoplay and await an input from the user. I tried an eventListener but it was ignored. I am at a loss of what to do.
Presenter.js
var Presenter = {
defaultPresenter: function(xml) {
if(this.loadingIndicatorVisible) {
navigationDocument.replaceDocument(xml, this.loadingIndicator);
this.loadingIndicatorVisible = false;
} else {
navigationDocument.pushDocument(xml);
}
},
modalDialogPresenter: function(xml) {
navigationDocument.presentModal(xml);
},
menuBarItemPresenter: function(xml, ele) {
var feature = ele.parentNode.getFeature("MenuBarDocument");
if (feature) {
var currentDoc = feature.getDocument(ele);
if (!currentDoc) {
feature.setDocument(xml, ele);
}
}
},
load: function(event) {
console.log(event);
var self = this,
ele = event.target,
templateURL = ele.getAttribute("template"),
presentation = ele.getAttribute("presentation");
videoURL = ele.getAttribute("videoURL");
if(videoURL) {
var player = new Player();
var playlist = new Playlist();
var mediaItem = new MediaItem("video", videoURL);
player.playlist = playlist;
player.playlist.push(mediaItem);
player.present();
}
if (templateURL) {
self.showLoadingIndicator(presentation);
resourceLoader.loadResource(templateURL,
function(resource) {
if (resource) {
var doc = self.makeDocument(resource);
doc.addEventListener("select", self.load.bind(self));
doc.addEventListener("highlight", self.load.bind(self));
if (self[presentation] instanceof Function) {
self[presentation].call(self, doc, ele);
} else {
self.defaultPresenter.call(self, doc);
}
}
}
);
}
},
makeDocument: function(resource) {
if (!Presenter.parser) {
Presenter.parser = new DOMParser();
}
var doc = Presenter.parser.parseFromString(resource, "application/xml");
return doc;
},
showLoadingIndicator: function(presentation) {
if (!this.loadingIndicator) {
this.loadingIndicator = this.makeDocument(this.loadingTemplate);
}
if (!this.loadingIndicatorVisible && presentation != "modalDialogPresenter" && presentation != "menuBarItemPresenter") {
navigationDocument.pushDocument(this.loadingIndicator);
this.loadingIndicatorVisible = true;
}
},
removeLoadingIndicator: function() {
if (this.loadingIndicatorVisible) {
navigationDocument.removeDocument(this.loadingIndicator);
this.loadingIndicatorVisible = false;
}
},
loadingTemplate: `<?xml version="1.0" encoding="UTF-8" ?>
<document>
<loadingTemplate>
<activityIndicator>
<text>Loading...</text>
</activityIndicator>
</loadingTemplate>
</document>`
}
--- application.js ---
var resourceLoader;
App.onLaunch = function(options) {
var javascriptFiles = [
`${options.BASEURL}js/ResourceLoader.js`,
`${options.BASEURL}js/Presenter.js`
];
evaluateScripts(javascriptFiles, function(success) {
if (success) {
resourceLoader = new ResourceLoader(options.BASEURL);
var index = resourceLoader.loadResource(`${options.BASEURL}templates/CalvaryTVMenuBar.xml.js`,
function(resource) {
var doc = Presenter.makeDocument(resource);
doc.addEventListener("select", Presenter.load.bind(Presenter));
navigationDocument.pushDocument(doc);
});
} else {
var alert = createAlert("Evaluate Scripts Error", "There was an error attempting to evaluate the external JavaScript files.\n\n Please check your network connection and try again later.");
navigationDocument.presentModal(alert);
throw ("Playback Example: unable to evaluate scripts.");
}
});
}
var createAlert = function(title, description) {
var alertString = `<?xml version="1.0" encoding="UTF-8" ?>
<document>
<alertTemplate>
<title>${title}</title>
<description>${description}</description>
</alertTemplate>
</document>`
var parser = new DOMParser();
var alertDoc = parser.parseFromString(alertString, "application/xml");
return alertDoc
}
Hi Josh,
Try removing the highlight event that you are attaching while loading the document using resourceLoader.loadResource method.
It seems, you are attaching both the events
doc.addEventListener("select", self.load.bind(self));
doc.addEventListener("highlight", self.load.bind(self));
Try removing the second one.

Automatic text detection on contenteditable div using angular js

what is the best way to do following using angular js
when writing text on a contenteditable div need to detect special word like ' {{FULL_NAME}} ' and covert to tag with pre-deifined constant words
example - if some one write
' His name is {{FULL_NAME}} '
should be instantly convert to ' His name is john smith '
Here is a demo plunker: http://plnkr.co/edit/GKYxXiDKv7fBeaE7rrZA?p=preview
Service:
app.factory('interpolator', function(){
var dict = { .... };
return function(str){
return (str || "").replace(/\{\{([^\}]+)\}\}/g, function(all, match){
return dict[match.trim().toLowerCase()] || all;
});
};
});
Directive:
app.directive('edit',[ 'interpolator', function(interpolator){
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, element, attrs, ngModel) {
element.on('blur', function(e) {
scope.$apply(function() {
var content = interpolator(element.text());
element.text(content);
ngModel.$setViewValue(content);
});
});
ngModel.$formatters.push(interpolator);
ngModel.$render = function() {
element.text(ngModel.$viewValue);
ngModel.$setViewValue(ngModel.$viewValue);
};
}
};
}]);
Here's an example using simple DOM methods, based on other answers I've provided on Stack Overflow. It also uses Rangy to save and restore the selection while doing the substitutions so that the caret does not move.
Demo:
http://jsbin.com/zuvevocu/2
Code:
var editorEl = document.getElementById("editor");
var keyTimer = null, keyDelay = 100;
function createKeyword(matchedTextNode) {
var el = document.createElement("b");
el.style.backgroundColor = "yellow";
el.style.padding = "2px";
el.contentEditable = false;
var matchedKeyword = matchedTextNode.data.slice(1, -1); // Remove the curly brackets
matchedTextNode.data = (matchedKeyword.toLowerCase() == "name") ? "John Smith" : "REPLACEMENT FOR " + matchedKeyword;
el.appendChild(matchedTextNode);
return el;
}
function surroundInElement(el, regex, surrounderCreateFunc) {
// script and style elements are left alone
if (!/^(script|style)$/.test(el.tagName)) {
var child = el.lastChild;
while (child) {
if (child.nodeType == 1) {
surroundInElement(child, regex, surrounderCreateFunc);
} else if (child.nodeType == 3) {
surroundMatchingText(child, regex, surrounderCreateFunc);
}
child = child.previousSibling;
}
}
}
function surroundMatchingText(textNode, regex, surrounderCreateFunc) {
var parent = textNode.parentNode;
var result, surroundingNode, matchedTextNode, matchLength, matchedText;
while ( textNode && (result = regex.exec(textNode.data)) ) {
matchedTextNode = textNode.splitText(result.index);
matchedText = result[0];
matchLength = matchedText.length;
textNode = (matchedTextNode.length > matchLength) ?
matchedTextNode.splitText(matchLength) : null;
surroundingNode = surrounderCreateFunc(matchedTextNode.cloneNode(true));
parent.insertBefore(surroundingNode, matchedTextNode);
parent.removeChild(matchedTextNode);
}
}
function updateKeywords() {
var savedSelection = rangy.saveSelection();
surroundInElement(editorEl, /\{\w+\}/, createKeyword);
rangy.restoreSelection(savedSelection);
}
function keyUpHandler() {
if (keyTimer) {
window.clearTimeout(keyTimer);
}
keyTimer = window.setTimeout(function() {
updateKeywords();
keyTimer = null;
}, keyDelay);
}
editorEl.onkeyup = keyUpHandler;
Related:
https://stackoverflow.com/a/5905413/96100
https://stackoverflow.com/a/4026684/96100
https://stackoverflow.com/a/4045531/96100

Hidden-Field Switcher Icon Button Option

I've found this great extension which does exactly what I want in terms of functionality e.g it switches the hidden input fields and makes them visible.
The only thing I've been trying to figure out is how to make it so it will work as an extension icon button and not as a shortcut key combination?.
Extension:
https://chrome.google.com/webstore/detail/hidden-field-switcher/gfkfklknjmlpldiggcjgdgkonoicfngp
Thanks
My Current Setup:
I've been able to add the button but on click it doesn't do anything:
Manifest.json
{
"name": "Display Hidden",
"version": "1.0",
"manifest_version": 2,
"description": "Display hidden",
"background": {"page": "background.html", "persistent": false},
"browser_action": {"default_icon": "icon.png"},
"content_scripts":[{"js": ["background.js"], "matches": ["https://*/*"]}],
"permissions": ["tabs", "http://*/*"]
}
background.html
<html>
<head>
<script src="background.js">
chrome.browserAction.onClicked.addListener(function(tab) {alert('icon clicked')});
</script>
</head>
</html>
background.js Source
var allFields = new Array();
var visible = false;
function switchHidden() {
if(visible == true){
hideHidden();
visible = false;
}
else {
showHidden();
visible = true;
}
}
function showHidden() {
var allHidden = document.getElementsByTagName("input");
for (var Key in allHidden) {
if(allHidden[Key].type == "hidden") {
allFields.push(allHidden[Key]);
allHidden[Key].type = "text";
/*for (var SettingsKey in Settings) {
var subSection = SettingsKey.substring(7,0);
var subKey = SettingsKey.substring(7);
if(subSection == "styles_") {
allHidden[Key].style.setProperty(subKey,Settings[SettingsKey]);
}
}*/
if(allHidden[Key].name)
allHidden[Key].title = allHidden[Key].name;
if(!allHidden[Key].name && allHidden[Key].id)
allHidden[Key].title = allHidden[Key].id;
allHidden[Key].style.setProperty("background-color","#CCFFFF");
}
}
}
function hideHidden() {
for (var Key in allFields) {
allFields[Key].type = "hidden";
}
allFields = new Array();
}