⬆️ ⬇️

See the hidden or how good to do even better

All-seeing eye A long time ago, even at the very beginning of my acquaintance with jQuery, I wanted to have a tool showing what elements on the page were attached to events and what these events are. At that moment I could not find anything like it. Not to say that I was very much looking for, but neither FireBug nor the later appeared Dragonfly had such functionality.



Periodically, I remembered this idea, but a quick glance at Google did not give anything interesting. Until recently.



When in January I again wanted to see if there was anything interesting on this subject, I found the wonderful Visual Event 2 bookmark written by Allan Jardine . The bookmarklet worked like a clock, but there were a couple of fly in the ointment, small but nasty.

')

I use Opera and all the bookmarklets on the toolbar have the same icon: Bookmarklet Icon . It is worth adding a couple of these bookmarks and navigate among them to become very uncomfortable. The second unpleasant moment is that the bookmarklet loaded all of its code from the developer’s site, which resulted in even minor delays.



Therefore, it was decided to issue a bookmarklet in the form of an extension for the Opera browser. In addition, I have long wanted to get acquainted with the Opera Extension API. Here is a link to the expansion page for the most impatient. The rest I ask under the cat, which describes all the stages of converting a bookmarklet into an extension for Opera, the problems that I encountered and the methods for solving them.



What is it all for



Expansion button

First I want to introduce you to what eventually happened. After installing the extension

Visual Event on the Opera toolbar will be a new button. With it, you can enable and disable the VisualEvent mode.



Attention! The button will act only on pages loaded after the extension is installed!



After activating the button, the open HTML page will be darkened and all elements on which any event handlers are hung will be highlighted with colored rectangles:

VisualEvent mode



After pointing the cursor to any of the rectangles, you will see the code of the function-handler, the file name and the string in which it was declared:

Output of the function handler code



A second click on the eye button will turn off VisualEvent. On this, in fact, nothing more than the extension does not. Interested, can install it from the directory Opera extensions . Those who are interested in the details of converting a bookmarklet into an extension, I invite you to the next section.



Parse the bookmarklet



Before porting something, it would be good to figure out how it still works. To this end, let's look at the code of the bookmarklet itself.



The bookmarklet consists of the code itself added to the link and a few additional files with the style sheet and the implementation of all the functionality. The code of the bookmarlet is extremely simple:



(function() { var url = 'http://example.com/VisualEvent/builds/VisualEvent_Loader.js'; if( typeof VisualEvent!='undefined' ) { if ( VisualEvent.instance !== null ) { VisualEvent.close(); } else { new VisualEvent(); } } else { var n=document.createElement('script'); n.setAttribute('language','JavaScript'); n.setAttribute('src',url+'?rand='+new Date().getTime()); document.body.appendChild(n); } })(); 


The url variable contains the address of the script loader, which provides activation of all logic. From the listing it is clear that the code checks for the presence of the VisualEvent object and, if it is not detected, initiates its loading. If the object is detected, it is checked whether the VisualEvent mode is activated or not. And, depending on the results of the test, it either turns it off or on.



In fact, this script implements the functionality of the on / off button of the VisualEvent mode, and all the logic is stored separately and loaded when first activated. But, since we want to make an extension, we will need to embed all the VisualEvent code into it, thereby completely eliminating access to third-party sites and reducing the time of first activation.



All bookmarklet code is divided into three files:



VisualEvent_Loader.js is loaded first. it checks again whether VisualEvent was loaded before and, if that was the case, activates or deactivates the debug mode. In case, if VisualEvent has not previously loaded, it checks whether the page uses jQuery . If jQuery is used, then the VisualEvent.js file with the code of the bookmarklet itself is loaded. If jQuery is not loaded, then the VisualEvent-jQuery.js file is loaded, containing both the VisualEvent code and the jQuery library code.



It is clear that we will have to implement both VisualEvent activation options in the extension: with and without jQuery. And we will not be able to spread the code across multiple files.



In addition to the JS files, the bookmark uses its own style sheet from the external VisualEvent.css file.



Making a button on the panel



The easiest way to start developing an extension is to add a button to the toolbar. Firstly, it is much easier to debug the extension by clicking on the button, and secondly, the presence of the button in sight is an eye-catching and does not allow the wave of enthusiasm to quickly subside.



How to create the basic structure of the extension, how to fill in the description file config.xml and how to add a button to the toolbar I will not tell, it is all in the official manual , and it was described on Habré.



Having safely created the config.xml file, finding the icon and adding a button to the toolbar, I moved on to the next step - adapting the scripts to work in the extension mode. And here I was waiting for the first surprise. It turns out the code that handles pressing the button on the toolbar does not have access to the page over which it was pressed. At first, I was discouraged by this behavior and even tried to find some workaround, but after a couple of experiments, I realized that this problem could not be solved.



And then I remembered about the remarkable cleanPages extension, which, at the press of a button on the toolbar, selects only significant content from the page and draws it up in a readable form. Those. by pressing a button, something is done with the page, scripts are loaded, code is modified, etc. And this is exactly what we need!



Lyrical digression The extension file is actually a ZIP archive with all the scripts, style sheets and pictures used. Therefore, having an extension file is not at all difficult to unzip and see how it works inside.


An autopsy revealed that the extension should consist of two scripts : a button handler on the toolbar and a background script that automatically connects to all loaded pages in all tabs. The button handler simply sends a broadcast message to all tabs with the ID of the tab on which the button was pressed, and the background script listens to all messages and, when received, activates the extension logic.



As a result, the function of processing the button presses took the form:



 function buttonClicked() { var tab = opera.extension.tabs.getFocused(); if ( tab ) { opera.extension.broadcastMessage( JSON.stringify(opera.extension.tabs.getFocused().url)+ '|' + 'VisualEvent-1.0'); } }; 


Where the call opera.extension.broadcastMessage () just sends the broadcast message. And the background script got this logic:



 opera.extension.onmessage = function(e) { var dataArray = e.data.split('|'); if ( window.top == window.self && e.data && JSON.parse(dataArray[0]) == window.decodeURI(window.location.href.replace(window.location.hash, '')) ) { if ( 'VisualEvent-1.0' == dataArray[1] ) { enableVisualEvent(); } } }; 


Here you can see that after receiving a message, its format is checked and the URL of the page on which the button was pressed. Yes, it is the URL that is being checked, so if you have several identical tabs open, then VisualEvent will be activated on all at the same time .



The mechanism is not without flaws, so if anyone knows a simpler way of interacting buttons and pages, I will be glad to read about it in the comments.



We write a script to process the page



We have already started writing a script to process the page and added a message handler to it from the toolbar button. And this script calls the function enableVisualEvent () . I put the bookmarklet code in this function with the only difference that instead of loading the loader file, it simply calls its code, which is designed as a separate function.



The fact is that I couldn’t find a way to connect another JavaScript file from the backup script that belongs to the same extension, so I had to write the code for each of the three source bookmarklet files ( VisualEvent , VisualEvent_loader and jQuery ) into a separate function and single background script.



Adapt JavaScript


As stated in the guide for converting UserJS to extensions .



Sometimes the UserJS doesn’t work as an extension, for various reasons. ( Sometimes UserJS does not work in extension mode, for various reasons )


And this is true, at least it did not work for me :). But the same guide also contains recipes for how to deal with this disgrace. The main reason is that the extension works in its sandbox and has access to the global scope of the page only through the explicit indication of the window object. Those. Calling jQuery as “ $ ” or “j Query ” will not work; instead, use “ window. $ ” or “ window.jQuery ”. And such corrections must be made for all global variables used in scripts.



After all replacements have been made the extension has fully earned. But the style sheets are not connected.



We connect CSS


The source code used its own style sheet and loaded it from an external file. As I said, it was not possible to connect additional files from the extension directory to the page. As a workaround, I simply included all the CSS code in the background script and at the first initialization I create a <style> tag in <head>, writing all the CSS into it -regulations:



 function initCss() { var css = '< CSS->'; var n = document.createElement('style'); n.type = 'text/css'; var rules = document.createTextNode(css); if ( n.styleSheet ) { n.styleSheet.cssText = rules.nodeValue; } else { n.appendChild(rules); } document.getElementsByTagName('head')[0].appendChild(n); }; 


As a result, the stylesheets worked fine. The only disadvantage is that all the CSS code has to be included in one line and manually take care of escaping quotes.



findings



Thanks to everyone who read it! :)



I would like a couple of sentences to summarize the article. Learning new is always useful, and sometimes even interesting. People familiar with JavaScript will not encounter any special difficulties when developing extensions. When developing extensions, you may encounter some of their unobvious features, but a convenient developer guide and code for existing extensions will easily allow you to deal with everything.



By the way, Chrome users can find a similar extension for it.

Source: https://habr.com/ru/post/140576/



All Articles