Skip to content

Commit

Permalink
Initial commit: notification for event page about selected element
Browse files Browse the repository at this point in the history
  • Loading branch information
lushchick committed Oct 11, 2012
0 parents commit df2c0d5
Show file tree
Hide file tree
Showing 20 changed files with 972 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.idea
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
Chrome DOM Picker
=================

This Chrome extension allows you to pick DOM nodes on any webpage and send selection to your client script for further processing.

Installation
------------
*@todo*

Usage
-----
*@todo*

API
---
*@todo*
Binary file added icon-128.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added icon.psd
Binary file not shown.
34 changes: 34 additions & 0 deletions manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"name": "ChromeDOMPicker",
"version": "0.0.1",
"manifest_version": 2,
"description": "This Chrome extension allows you to pick DOM nodes on any webpage and send selection to your client script for further processing.",
"icons": {
"128": "icon-128.png"
},
"browser_action": {
"default_icon": "icon-128.png",
"default_title": "Chrome DOM Picker",
"default_popup": "src/popup.html"
},
"background": {
"scripts": [
"thirdparty/js/jquery.min.js",
"src/event_page.js"
],
"persistent": false
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"css": ["src/content_style.css"],
"js": [
"src/content_script.js"
]
}
],
"permissions": [
"tabs", "<all_urls>"
],
"content_security_policy": "script-src 'self'; object-src 'self'"
}
88 changes: 88 additions & 0 deletions src/content_script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
console.log('Content script loaded');
(function() {
var messageEventPrefix = 'cdp',
messageEvents = (function() {
var eventNames = ['launch'],
events = {};

for (var i = -1, l = eventNames.length; ++i < l; ) {
events[eventNames[i]] = true;
}

return events;
})(),
isKnownMessageEvent = function(name) {
for (var i in messageEvents) {
if (messageEvents.hasOwnProperty(i)) {
return true;
}
}
return false;
},
tooltip = function(options) {
var $container, $alert,
closeTooltip = function() {
// check if our tooltip is not hovered, and if yes - postpone closing it a bit
$alert.is(':hover') ? setTimeout(closeTooltip, 5e3) : $alert.alert('close');
},
markup = ['<div class="cdp-tooltip-item alert alert-block alert-info fade in">',
'<button type="button" class="close" data-dismiss="alert">&times;</button>'];

if (options.title) {
markup.push('<h4 class="alert-heading">' + options.title + '</h4>');
}

if (options.text) {
markup.push(options.text);
}

markup.push('</div>');

// create tooltip wrap container if not yet exist
if (!($container = $('.cdp-tooltip')).length) {
$container = $('<div class="cdp-tooltip"></div>').appendTo('body');
}

$alert = $(markup.join('')).prependTo($container).alert();

setTimeout(closeTooltip, 5e3);
},
onMessage = function(event) {
var container, handler, parts, type;
if (event instanceof MessageEvent) {
parts = event.data.type ? event.data.type.split('.') : [];
if (event.source !== window
|| parts.length < 2
|| parts[0] !== messageEventPrefix
|| !isKnownMessageEvent(type = parts[1])
) {
return;
}
event.data.type = type;
container = messageEventHandlers;
} else {
type = event.type;
container = eventPageMessageHandlers;
}

handler = (typeof container[type] === 'function') ? type : 'def';
container[handler].apply(this, arguments);
}

this.messageEventHandlers = {
def: function(event) {
console.log('Default messageEventHandler', arguments);
// just proxy received message to background script
chrome.extension.sendMessage(null, event.data || {});
}
};

this.eventPageMessageHandlers = {
def: function() {
console.log('Default eventPageMessageHandler', arguments);
}
}

window.addEventListener('message', onMessage, false);
chrome.extension.onMessage.addListener(onMessage);
})();
2 changes: 2 additions & 0 deletions src/content_style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.cdp-tooltip { position: absolute; right: 10px; top: 10px; width: 300px; z-index: 99999; }
.cdp-tooltip-item { margin-bottom: 10px; }
130 changes: 130 additions & 0 deletions src/dom_picker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
(function($) {
/*
* Gets XPATH string representation for given DOM element, stolen from firebug_lite.
* example: /html/body/div[2]/div[3]/div[4]/div/div[2]/div/div/div/div/div/div[2]/p
*
* To keep paths short, the path generated tries to find a parent element with a unique
* id, eg: //[@id="dom-element-with-id"]/div[2]/p
*/
var getElementXPath = function(element) {
var paths = [], index, nodeName, tagName, sibling, pathIndex;

for (; element && element.nodeType == 1; element = element.parentNode) {
index = 0,
nodeName = element.nodeName;

if (element.id) {
tagName = element.nodeName.toLowerCase();
paths.splice(0, 0, '*[@id="' + element.id + '"]');
return "//" + paths.join("/");
}

for (sibling = element.previousSibling; sibling; sibling = sibling.previousSibling) {
if (sibling.nodeType != 1)
continue;

if (sibling.nodeName == nodeName)
++index;
}

tagName = element.nodeName.toLowerCase();
pathIndex = (index ? "[" + (index + 1) + "]" : "");
paths.splice(0, 0, tagName + pathIndex);
}

return paths.length ? "/" + paths.join("/") : null;
};

/**
* Simple plugin for handling elements selection
* @param event
* @param options
* @return jQuery
*/
$.fn.dompicker = function(event, options) {
var $this = $(this),
defaults = {
hoverStyles: {
backgroundColor: 'lightblue',
opacity: .7
},
selectedStyles: {
backgroundColor: 'lightskyblue'
}
},
data = (function() {
var ns = 'cdp';
if (!$this.data(ns)) {
$this.data(ns, {});
}
return $this.data(ns);
})(),
events = {
show: function() {
if ($.isEmptyObject(data.css)) {
data.css = {};
$.each(options.hoverStyles, function(key) {
data.css[key] = $this.css(key);
});
}

if (!data.selected) {
$this.css(options.hoverStyles);
}
},
hide: function() {
if (data.css && !data.selected) {
$this.css(data.css);
}
},
click: function() {
data.selected = !data.selected;
var messageType = 'onNode' + (data.selected ? 'Select' : 'Deselect'),
styles = data.selected ? options.selectedStyles : data.css;

$this.css(styles);
chrome.extension.sendMessage(null, {
type: messageType,
node: {
id: $this.prop($.expando),
tagName: $this.prop('tagName').toLowerCase(),
xPath: getElementXPath($this[0])
}
});
}
};

options = $.extend({}, defaults, options || {});

if (event && events[event]) {
events[event]();
}

return this;
};

$('*')

.on('mouseover mouseout mouseup mousedown click', function(e) {
if (this !== e.target) {
return;
}

switch (e.type) {
case 'mouseover':
$(this).dompicker('show');
break;
case 'mouseout':
$(this).dompicker('hide');
break;
case 'click':
$(this).dompicker('click');
// no break here
case 'mousedown':
case 'mouseup':
default:
e.preventDefault();
e.stopPropagation();
}
});
})(jQuery);
99 changes: 99 additions & 0 deletions src/event_page.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
(function() {
var loadScripts = (function() {
var loaded = {};

return function(tab, scripts, injectDetails) {
// fired when all scripts loaded. This could actually be a callback queue, but there's no need for it
var onLoadCallback;

if (loaded[tab.id])
return;

injectDetails = injectDetails || {};

for (var i = -1, l = scripts.length; ++i < l; ) {
injectDetails.file = scripts[i];
chrome.tabs.executeScript(tab.id, injectDetails, (function(i) {
return function() {
if (i + 1 === l && onLoadCallback) {
// last script was loaded, fire onLoadCallback
onLoadCallback();
}

(loaded[tab.id] = loaded[tab.id] || []).push(scripts[i]);
}
}(i)));
}

return {
then: function(cb) {
if (loaded[tab.id]) {
onLoadCallback();
} else {
onLoadCallback = cb;
}
}
};
}
})(),
/**
* Fired when a connection is made from either an extension process or a content script.
* @param port {Port} http://developer.chrome.com/extensions/extension.html#type-Port
*/
messageHandlers = {
launch: function(req) {
if (!req.data.url) {
return;
}

var url = req.data.url.match(/^https?/i) ? req.data.url : 'http://' + req.data.url,
parentTab,
onWindowCreated = function(window) {
// load dom picker scripts into the newly created window
loadScripts(window.tabs[0], [
'thirdparty/js/jquery.min.js',
'src/dom_picker.js'
], {
allFrames: true
})
.then(function() {
chrome.tabs.sendMessage(parentTab.id, {data: 'foobar'});
chrome.tabs.sendMessage(window.tabs[0].id, {type: 'ready'});
});
},
onSelectedTab = function(tab) {
parentTab = tab;
loadScripts(tab, [
'thirdparty/js/jquery.min.js',
'thirdparty/js/bootstrap.min.js'
], { allFrames: true });

chrome.windows.create({
url: url,
focused: true,
type: 'popup'
}, onWindowCreated);
};

chrome.tabs.getSelected(null, onSelectedTab);
},
onNodeSelect: function() {
console.log('node selected', arguments);
},
onNodeDeselect: function() {
console.log('node DEselected', arguments);
}
},
onMessage = function(req, sender, callback) {
if (req.type && typeof messageHandlers[req.type] === 'function') {
messageHandlers[req.type].apply(messageHandlers, arguments);
}
if (typeof callback === 'function') {
// say that we do want to fire callback
return true;
}
};

// register onMessage handler
chrome.extension.onMessage.addListener(onMessage);
})();
9 changes: 9 additions & 0 deletions src/popup.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script src="popup.js"></script>
</head>
<body>
<div>Hello, world!</div>
</body>
</html>
3 changes: 3 additions & 0 deletions src/popup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
$(function() {
console.log(jQuery().jquery);
})
5 changes: 5 additions & 0 deletions src/shared.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/**
* Code in this file is shared between content script and event page. Defining variables here will make them appear
* in global scope
*/
console.log('shared script loaded');
Loading

0 comments on commit df2c0d5

Please sign in to comment.