├── Readme.markdown ├── bookmarklet.html ├── build.sh ├── css ├── VisualEvent.css └── shCore.css └── js ├── VisualEvent.js ├── VisualEvent_Loader.js ├── jquery.js ├── parsers ├── dom0.js ├── entwine.js ├── eventi.js ├── extjs.js ├── glow.js ├── jQuery.js ├── jQuery1.3.js ├── jsBase.js ├── mootools.js ├── prototype.js └── yui2.js └── shCore.js /Readme.markdown: -------------------------------------------------------------------------------- 1 | # Visual Event - visually inspect Javascript events 2 | 3 | Visual Event is a Javascript bookmarklet which provides debugging information about events that have been attached to DOM elements. Visual Event shows: 4 | 5 | * Which elements have events attached to them 6 | * The type of events attached to an element 7 | * The code that will be run with the event is triggered 8 | * The source file and line number for where the attached function was defined (Webkit browsers and Opera only) 9 | 10 | In addition to being useful for debugging your own code, Visual Event can be used as an educational tool, showing how many web-sites have been authored. 11 | 12 | 13 | ## Make it go! 14 | 15 | As Visual Event is a bookmarklet, installing and running it on any web-page is extremely simple: 16 | 17 | * Open the [Visual Event bookmarklet page](http://sprymedia.co.uk/VisualEvent) and drag the "Visual Event" link to your bookmark bar 18 | * Load a web-page which uses one of the supported Javascript libraries 19 | * Click "Visual Event" in your bookmark bar 20 | * View the event handlers which are attached to the document elements. 21 | 22 | A demo of Visual Event that is automatically loaded is [available](http://sprymedia.co.uk/VisualEvent/demo). This demo uses [DataTables](http://datatables.net) to create a test page with a number of events attached to different elements. 23 | 24 | 25 | ## How it works 26 | 27 | It turns out that there is no standard method provided by the W3C recommended DOM interface to find out what event listeners are attached to a particular element. While this may appear to be an oversight, there was a proposal to include a property called [eventListenerList](http://www.w3.org/TR/2002/WD-DOM-Level-3-Events-20020208/changes.html) to the level 3 DOM specification, but was unfortunately been removed in later drafts. As such we are forced to looked at the individual Javascript libraries, which typically maintain a cache of attached events (so they can later be removed and perform other useful abstractions). 28 | 29 | As such, in order for Visual Event to show events, it must be able to parse the event information out of a Javascript library. Currently the following libraries are supported by Visual Event: 30 | 31 | * DOM 0 events 32 | * jQuery 1.2+ 33 | * YUI 2 34 | * MooTools 1.2+ 35 | * Prototype 1.6+ 36 | * Glow 37 | 38 | 39 | ## How to get involved 40 | 41 | Any help with improvements and suggestions for Visual Event are very welcome indeed! If you hit a specific problem, then please open an issue on GitHub for the problem you are encountering, including a link to the page where the problem occurs. Forks and pull requests are also very much encouraged! 42 | 43 | One area which may be of interest to you is how to add support for additional Javascript libraries. The key thing that is needed is access to the event cache that the library uses, since without that it is impossible to determine what nodes have events and the attached code. 44 | 45 | The VisualEvent class has a static array called `VisualEvent.parsers` which is an array of functions - to add a new parser, just push your function onto this array. The function must return a Javascript array of objects with the following parameters: 46 | 47 | ```javascript 48 | [ 49 | { 50 | "node": {element}, // The DOM element that has attached events 51 | "listeners": [ // Array of attached events 52 | { 53 | "type": {string}, // The event type - click, change, keyup etc 54 | "func": {string}, // The code that will handle the event, from Function.toString() 55 | "removed": {boolean}, // If the event has been removed or not (typically will be false, but used if the library doesn't remove the event from its cache) 56 | "source": {string} // Library name and version that attached the event (e.g. "jQuery 1.7") 57 | }, 58 | ... 59 | ] 60 | }, 61 | ... 62 | ] 63 | ``` 64 | 65 | ## Building Visual Event 66 | 67 | In order to run a local copy of Visual Event you must build it, since this process does file concatenation, which brings in the library parsers to the main file. The build process will also build the JSDoc documentation and compress the Javascript files (unless made with debug). 68 | 69 | To build Visual Event, all you need is a system which will run bash scripts and enter the command `./build.sh debug` in your terminal. This will create a new directory in the "builds" directory with the correct loader and the build Visual Event directory (note the timestamp is used to help prevent caching issues for the bookmarklet, both during development and when deployed). The following is the usage for the build script: 70 | 71 | ```text 72 | Visual Event build script - usage: 73 | ./build.sh [loader-dir] [debug] 74 | loader-dir - The web-address of the build files. Note that the build 75 | directory name is automatically appended and \"http:\\\\\" is 76 | automatically prepended. For example: 77 | localhost/VisualEvent/builds - default if no option is provided 78 | sprymedia.co.uk/VisualEvent/builds 79 | debug - Debug indicator. Will not compress the Javascript 80 | 81 | Example deploy build: 82 | ./build.sh sprymedia.co.uk/VisualEvent/builds 83 | 84 | Example debug build: 85 | ./build.sh localhost/VisualEvent/builds debug 86 | ``` 87 | 88 | The file `bookmarklet.html` is provided to build your own bookmarklet loader nice and easily. Typically you'll only need to modify the path for the bookmarklet. The link is updated on each keypress and you install it just as you would with any other bookmarklet :-). 89 | 90 | 91 | ## About the author 92 | 93 | Allan Jardine is a freelance web UI developer, broadcasting from Scotland and just loves creating usable and useful developer tools. If you'd like to work with Allan on a project, please do [get in touch](http://sprymedia.co.uk/contact)! 94 | 95 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /bookmarklet.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Visual Event javascript booklet 6 | 7 | 24 | 25 | 26 | 47 | 48 | 49 |
50 |

Visual Event bookmarklet generator

51 |

52 | Visual Event is a bookmarklet which will visually show you which elements on an HTML 53 | page have Javascript events assigned to them. This page can be used to generate the 54 | bookmarklet that you want to use for testing or deployment of Visual Event. 55 |

56 |

57 | To generate a bookmarklet, modify the Javascript in the textarea below, the 58 | bookmarklet link will be updated as you type (typically you will only need to 59 | change the URL to load Visual Event) and then click and drag the 60 | Visual Event link to your bookmarklet bar. 61 |

62 | 63 |

64 | Visual Event 65 |

66 | 67 | 88 |
89 | 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "" 4 | if [ "$1" = "-h" ]; then 5 | echo " Visual Event build script - usage: 6 | ./build.sh [loader-dir] [debug] 7 | loader-dir - The web-address of the build files. Note that the build 8 | directory name is automatically appended. For example: 9 | localhost/VisualEvent/builds - default if no option is provided 10 | sprymedia.co.uk/VisualEvent/builds 11 | debug - Debug indicator. Will not compress the Javascript 12 | 13 | Example deploy build: 14 | ./build.sh sprymedia.co.uk/VisualEvent/builds 15 | 16 | Example debug build: 17 | ./build.sh localhost/VisualEvent/builds debug 18 | " 19 | exit 20 | elif [ "$1" = "clean" ]; then 21 | echo "Cleaning Visual Event builds" 22 | rm -Rf $(pwd)/builds/VisualEvent* 23 | exit 24 | fi 25 | 26 | # OPTIONS 27 | SCRIPT_LOC=$1 28 | DEBUG=$2 29 | 30 | if [ "$SCRIPT_LOC" = "debug" ]; then 31 | DEBUG="debug" 32 | SCRIPT_LOC="localhost/VisualEvent/builds" 33 | fi 34 | 35 | if [ -z "$1" ]; then 36 | SCRIPT_LOC="localhost/VisualEvent/builds" 37 | fi 38 | 39 | # DEFAULTS 40 | UGLIFYJS=/Users/allan/node_modules/uglify-js/bin/uglifyjs 41 | JSDOC3=/usr/local/jsdoc/jsdoc 42 | 43 | BUILD_DIR=VisualEvent-$(date +%s) 44 | BUILD_BASE=$(pwd)/builds 45 | BUILD=$(pwd)/builds/${BUILD_DIR} 46 | BUILD_JS=${BUILD}/js 47 | BUILD_CSS=${BUILD}/css 48 | BUILD_DOCS=${BUILD}/docs 49 | 50 | JS=$(pwd)/js 51 | CSS=$(pwd)/css 52 | 53 | echo "Building VisualEvent" 54 | echo " Creating media directory ${BUILD}" 55 | 56 | mkdir -p "$BUILD" 57 | 58 | 59 | # JAVASCRIPT 60 | echo " Javascript" 61 | mkdir "$BUILD_JS" 62 | 63 | echo " Combining Javascript files" 64 | cp "$JS/VisualEvent_Loader.js" "$BUILD_BASE/VisualEvent_Loader.js" 65 | cat "$JS/jquery.js" "$JS/shCore.js" "$JS/VisualEvent.js" "$JS"/parsers/*.js > "$BUILD_JS/VisualEvent-jQuery.js" 66 | cat "$JS/shCore.js" "$JS/VisualEvent.js" "$JS"/parsers/*.js > "$BUILD_JS/VisualEvent.js" 67 | 68 | if [ "$DEBUG" != "debug" -a -e $UGLIFYJS ]; then 69 | echo " Compressing Javascript" 70 | $UGLIFYJS $BUILD_BASE/VisualEvent_Loader.js > $BUILD_BASE/VisualEvent_Loader.min.js 71 | $UGLIFYJS $BUILD_JS/VisualEvent-jQuery.js > $BUILD_JS/VisualEvent-jQuery.min.js 72 | $UGLIFYJS $BUILD_JS/VisualEvent.js > $BUILD_JS/VisualEvent.min.js 73 | 74 | mv "$BUILD_BASE/VisualEvent_Loader.min.js" "$BUILD_BASE/VisualEvent_Loader.js" 75 | mv "$BUILD_JS/VisualEvent-jQuery.min.js" "$BUILD_JS/VisualEvent-jQuery.js" 76 | mv "$BUILD_JS/VisualEvent.min.js" "$BUILD_JS/VisualEvent.js" 77 | fi 78 | 79 | sed "s#__BUILD_URL__#//${SCRIPT_LOC}/${BUILD_DIR}#g" "$BUILD_BASE/VisualEvent_Loader.js" > "$BUILD_BASE/VisualEvent_Loader.tmp.js" 80 | mv "$BUILD_BASE/VisualEvent_Loader.tmp.js" "$BUILD_BASE/VisualEvent_Loader.js" 81 | 82 | 83 | # CSS 84 | echo " Combining CSS files" 85 | mkdir "$BUILD_CSS" 86 | cat "$CSS/VisualEvent.css" "$CSS/shCore.css" > "$BUILD_CSS/VisualEvent.css" 87 | 88 | 89 | # Docs 90 | if [ -e $JSDOC3 -a "$DEBUG" != "debug" ]; then 91 | echo " Documentation" 92 | $JSDOC3 -d $BUILD_DOCS -t JSDoc-DataTables $JS/VisualEvent.js $JS/VisualEvent_Loader.js 93 | fi 94 | 95 | echo "Done :-)" 96 | echo "" 97 | 98 | -------------------------------------------------------------------------------- /css/VisualEvent.css: -------------------------------------------------------------------------------- 1 | /** 2 | * @summary Visual Event CSS 3 | * @file VisualEvent.css 4 | * @author Allan Jardine (www.sprymedia.co.uk) 5 | * @license GPL v2 6 | * @contact www.sprymedia.co.uk/contact 7 | * 8 | * @copyright Copyright 2007-2011 Allan Jardine. 9 | * 10 | * This source file is free software, under the GPL v2 license: 11 | * http://www.gnu.org/licenses/gpl-2.0.html 12 | */ 13 | 14 | 15 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 16 | * Label 17 | */ 18 | 19 | #Event_Label { 20 | position: fixed; 21 | bottom: 0; 22 | left: 0; 23 | color: white !important; 24 | padding: 5px 10px 5px 5px; 25 | font-size: 11px; 26 | font-family: "Lucida Grande", Verdana, Arial, Helvetica, sans-serif; 27 | z-index: 55999; 28 | background-color: #7C94C0; 29 | z-index: 55002; 30 | } 31 | 32 | #Event_Label>span { 33 | color: white !important; 34 | } 35 | 36 | #Event_Label span.Event_LabelClose, 37 | #Event_Label span.Event_LabelHelp { 38 | display: inline-block; 39 | width: 17px; 40 | text-align: center; 41 | border: 1px solid #627ba9; 42 | background-color: #93a8cf; 43 | color: #4b6698; 44 | margin-right: 5px; 45 | cursor: pointer; 46 | *cursor: hand; 47 | } 48 | 49 | #Event_Label span.Event_LabelClose:hover, 50 | #Event_Label span.Event_LabelHelp:hover { 51 | background-color: #aebfdd; 52 | } 53 | 54 | #Event_Label span.Event_LabelHelp { 55 | margin-right: 10px; 56 | } 57 | 58 | #Event_Label span.Event_LabelBy { 59 | display: inline-block; 60 | font-size: 10px; 61 | margin-right: 10px; 62 | } 63 | 64 | #Event_Label span.Event_LabelBy a { 65 | color: #eee; 66 | } 67 | 68 | table.Event_LabelColorInfo td { 69 | text-align: center; 70 | font-size: 12px; 71 | } 72 | 73 | div.Event_LabelColour { 74 | margin: 0 auto; 75 | height: 20px; 76 | width: 20px; 77 | } 78 | 79 | 80 | 81 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 82 | * Help 83 | */ 84 | 85 | #Event_Help { 86 | position: fixed; 87 | top: 0; 88 | right: 0; 89 | bottom: 0; 90 | left: 0; 91 | z-index: 55001; 92 | background-color: #ddd; 93 | color: #222; 94 | font-size: 14px; 95 | font-family: "Lucida Grande", Verdana, Arial, Helvetica, sans-serif; 96 | } 97 | 98 | #Event_Help div.Event_HelpInner { 99 | width: 800px; 100 | margin: 30px auto 0 auto; 101 | } 102 | 103 | #Event_Help h1 { 104 | font-size: 18px; 105 | border-bottom: 1px solid #ccc; 106 | color: #222; 107 | } 108 | 109 | #Event_Help p { 110 | margin: 1em 0; 111 | color: #222; 112 | } 113 | 114 | #Event_Help table { 115 | width: 90%; 116 | margin: 0 auto; 117 | } 118 | 119 | #Event_Help p.Event_HelpClose { 120 | text-align: center; 121 | margin-top: 2em; 122 | } 123 | 124 | 125 | 126 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 127 | * Lightbox 128 | */ 129 | 130 | #Event_Lightbox { 131 | position: absolute; 132 | display: none; 133 | color: #111; 134 | width: 660px; 135 | z-index: 55003; 136 | text-align: left; 137 | border-radius: 10px; 138 | -moz-border-radius: 10px; 139 | -webkit-border-radius: 10px; 140 | padding: 5px; 141 | font-size: 12px; 142 | direction : ltr; 143 | background-color: #F8F8F8; 144 | border: 4px solid #7C94C0; 145 | -moz-box-shadow: 3px 3px 5px #111; 146 | -webkit-box-shadow: 3px 3px 5px #111; 147 | box-shadow: 3px 3px 5px #111; 148 | font-family: "Lucida Grande", Verdana, Arial, Helvetica, sans-serif; 149 | box-sizing: content-box; 150 | } 151 | 152 | #Event_Lightbox * { 153 | box-sizing: content-box !important; 154 | } 155 | 156 | #Event_Lightbox ul { 157 | list-style-type: none; 158 | margin: 0; 159 | padding: 0; 160 | } 161 | 162 | #Event_Lightbox ul li { 163 | display: block; 164 | width: 90px; 165 | padding: 5px; 166 | cursor: pointer; 167 | *cursor: hand; 168 | } 169 | 170 | #Event_Lightbox ul li.Event_EventSelected { 171 | background-color: #eee; 172 | } 173 | 174 | #Event_Lightbox div.Event_NodeRemove { 175 | float: right; 176 | font-size: 10px; 177 | color: #4E6CA3; 178 | cursor: pointer; 179 | *cursor: hand; 180 | } 181 | 182 | #Event_Lightbox div.Event_NodeRemove:hover { 183 | text-decoration: underline; 184 | } 185 | 186 | #Event_Lightbox #Event_Trigger { 187 | color: #4E6CA3; 188 | cursor: pointer; 189 | *cursor: hand; 190 | } 191 | 192 | #Event_Lightbox #Event_Trigger:hover { 193 | text-decoration: underline; 194 | } 195 | 196 | #Event_Lightbox div.Event_Nav { 197 | float: left; 198 | width: 100px; 199 | } 200 | 201 | #Event_Lightbox div.Event_Code { 202 | float: left; 203 | width: 550px; 204 | background-color: #eee; 205 | padding: 5px; 206 | } 207 | 208 | #Event_Lightbox div.Event_Code p { 209 | padding: 0 0 12px 0; 210 | margin: 0; 211 | } 212 | 213 | #Event_Lightbox div.Event_NodeName { 214 | padding: 5px; 215 | } 216 | 217 | 218 | 219 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 220 | * Display 221 | */ 222 | 223 | #Event_Display { 224 | position: absolute; 225 | top: 0; 226 | left: 0; 227 | height: 100%; 228 | width: 100%; 229 | z-index: 55001; 230 | background-color: rgba(0, 0, 0, 0.2); 231 | } 232 | 233 | 234 | #Event_Display div.Event_info { 235 | float: left; 236 | width: 15px; 237 | height: 15px; 238 | z-index: 55002; 239 | border: transparent 2px solid; 240 | } 241 | 242 | #Event_Help div.Event_bg_blue, 243 | #Event_Display div.Event_bg_blue { 244 | background-color: rgba(0, 0, 255, 0.3); 245 | border: rgba(0, 0, 255, 0.5) 2px solid; 246 | } 247 | 248 | #Event_Display div.Event_bg_blue:hover { 249 | border: rgba(0, 0, 255, 1) 2px solid; 250 | } 251 | 252 | #Event_Help div.Event_bg_red, 253 | #Event_Display div.Event_bg_red { 254 | background-color: rgba(255, 0, 0, 0.3); 255 | border: rgba(255, 0, 0, 0.5) 2px solid; 256 | } 257 | 258 | #Event_Display div.Event_bg_red:hover { 259 | border: rgba(255, 0, 0, 1) 2px solid; 260 | } 261 | 262 | #Event_Help div.Event_bg_yellow, 263 | #Event_Display div.Event_bg_yellow { 264 | background-color: rgba(255, 204, 51, 0.3); 265 | border: rgba(245, 184, 0, 0.5) 2px solid; 266 | } 267 | 268 | #Event_Display div.Event_bg_yellow:hover { 269 | border: rgba(255, 204, 51, 1) 2px solid; 270 | } 271 | 272 | #Event_Help div.Event_bg_green, 273 | #Event_Display div.Event_bg_green { 274 | background-color: rgba(0, 167, 0, 0.3); 275 | border: rgba(0, 167, 0, 0.5) 2px solid; 276 | } 277 | 278 | #Event_Display div.Event_bg_green:hover { 279 | border: rgba(0, 167, 0, 1) 2px solid; 280 | } 281 | 282 | #Event_Help div.Event_bg_purple, 283 | #Event_Display div.Event_bg_purple { 284 | background-color: rgba(167, 0, 145, 0.3); 285 | border: rgba(167, 0, 145, 0.5) 2px solid; 286 | } 287 | 288 | #Event_Display div.Event_bg_purple:hover { 289 | border: rgba(167, 0, 145, 1) 2px solid; 290 | } 291 | 292 | #Event_Help div.Event_bg_orange, 293 | #Event_Display div.Event_bg_orange { 294 | background-color: rgba(201, 145, 35, 0.3); 295 | border: rgba(201, 145, 35, 0.5) 2px solid; 296 | } 297 | 298 | #Event_Display div.Event_bg_orange:hover { 299 | border: rgba(201, 145, 35, 1) 2px solid; 300 | } 301 | 302 | #Event_Help div.Event_bg_black, 303 | #Event_Display div.Event_bg_black { 304 | background-color: rgba(190, 190, 190, 0.3); 305 | border: rgba(190, 190, 190, 0.5) 2px solid; 306 | } 307 | 308 | #Event_Display div.Event_bg_black:hover { 309 | border: rgba(190, 190, 190, 1) 2px solid; 310 | } 311 | -------------------------------------------------------------------------------- /css/shCore.css: -------------------------------------------------------------------------------- 1 | /** 2 | * SyntaxHighlighter 3 | * http://alexgorbatchev.com/SyntaxHighlighter 4 | * 5 | * SyntaxHighlighter is donationware. If you are using it, please donate. 6 | * http://alexgorbatchev.com/SyntaxHighlighter/donate.html 7 | * 8 | * @version 9 | * 3.0.83 (July 02 2010) 10 | * 11 | * @copyright 12 | * Copyright (C) 2004-2010 Alex Gorbatchev. 13 | * 14 | * @license 15 | * Dual licensed under the MIT and GPL licenses. 16 | */ 17 | .Event_syntaxHighlighter a, 18 | .Event_syntaxHighlighter div, 19 | .Event_syntaxHighlighter code, 20 | .Event_syntaxHighlighter table, 21 | .Event_syntaxHighlighter table td, 22 | .Event_syntaxHighlighter table tr, 23 | .Event_syntaxHighlighter table tbody, 24 | .Event_syntaxHighlighter table thead, 25 | .Event_syntaxHighlighter table caption, 26 | .Event_syntaxHighlighter textarea { 27 | -moz-border-radius: 0 0 0 0 !important; 28 | -webkit-border-radius: 0 0 0 0 !important; 29 | background: none !important; 30 | border: 0 !important; 31 | bottom: auto !important; 32 | float: none !important; 33 | height: auto !important; 34 | left: auto !important; 35 | line-height: 1.1em !important; 36 | margin: 0 !important; 37 | outline: 0 !important; 38 | overflow: visible !important; 39 | padding: 0 !important; 40 | position: static !important; 41 | right: auto !important; 42 | text-align: left !important; 43 | top: auto !important; 44 | vertical-align: baseline !important; 45 | width: auto !important; 46 | box-sizing: content-box !important; 47 | font-family: "Source Code Pro","Consolas","Monaco","Bitstream Vera Sans Mono","Courier New",Courier,monospace !important; 48 | font-weight: normal !important; 49 | font-style: normal !important; 50 | font-size: 1em !important; 51 | min-height: inherit !important; 52 | min-height: auto !important; 53 | } 54 | 55 | .Event_syntaxHighlighter { 56 | width: 100% !important; 57 | margin: 1em 0 1em 0 !important; 58 | position: relative !important; 59 | overflow: auto !important; 60 | font-size: 1em !important; 61 | } 62 | .Event_syntaxHighlighter.source { 63 | overflow: hidden !important; 64 | } 65 | .Event_syntaxHighlighter .bold { 66 | font-weight: bold !important; 67 | } 68 | .Event_syntaxHighlighter .italic { 69 | font-style: italic !important; 70 | } 71 | .Event_syntaxHighlighter .line { 72 | white-space: pre !important; 73 | } 74 | .Event_syntaxHighlighter table { 75 | width: 100% !important; 76 | } 77 | .Event_syntaxHighlighter table caption { 78 | text-align: left !important; 79 | padding: .5em 0 0.5em 1em !important; 80 | } 81 | .Event_syntaxHighlighter table td.code { 82 | width: 100% !important; 83 | } 84 | .Event_syntaxHighlighter table td.code .container { 85 | position: relative !important; 86 | } 87 | .Event_syntaxHighlighter table td.code .container textarea { 88 | box-sizing: border-box !important; 89 | position: absolute !important; 90 | left: 0 !important; 91 | top: 0 !important; 92 | width: 100% !important; 93 | height: 100% !important; 94 | border: none !important; 95 | background: white !important; 96 | padding-left: 1em !important; 97 | overflow: hidden !important; 98 | white-space: pre !important; 99 | } 100 | .Event_syntaxHighlighter table td.gutter .line { 101 | text-align: right !important; 102 | padding: 2px 0.5em 2px 1em !important; 103 | } 104 | .Event_syntaxHighlighter table td.code .line { 105 | padding: 2px 1em !important; 106 | } 107 | .Event_syntaxHighlighter.nogutter td.code .container textarea, .Event_syntaxHighlighter.nogutter td.code .line { 108 | padding-left: 0em !important; 109 | } 110 | .Event_syntaxHighlighter.show { 111 | display: block !important; 112 | } 113 | .Event_syntaxHighlighter.collapsed table { 114 | display: none !important; 115 | } 116 | .Event_syntaxHighlighter.collapsed .toolbar { 117 | padding: 0.1em 0.8em 0em 0.8em !important; 118 | font-size: 1em !important; 119 | position: static !important; 120 | width: auto !important; 121 | height: auto !important; 122 | } 123 | .Event_syntaxHighlighter.collapsed .toolbar span { 124 | display: inline !important; 125 | margin-right: 1em !important; 126 | } 127 | .Event_syntaxHighlighter.collapsed .toolbar span a { 128 | padding: 0 !important; 129 | display: none !important; 130 | } 131 | .Event_syntaxHighlighter.collapsed .toolbar span a.expandSource { 132 | display: inline !important; 133 | } 134 | .Event_syntaxHighlighter .toolbar { 135 | position: absolute !important; 136 | right: 1px !important; 137 | top: 1px !important; 138 | font-size: 10px !important; 139 | z-index: 10 !important; 140 | } 141 | .Event_syntaxHighlighter .toolbar span.title { 142 | display: inline !important; 143 | } 144 | .Event_syntaxHighlighter .toolbar a { 145 | display: block !important; 146 | text-align: center !important; 147 | text-decoration: none !important; 148 | padding-top: 1px !important; 149 | } 150 | .Event_syntaxHighlighter .toolbar a.expandSource { 151 | display: none !important; 152 | } 153 | .Event_syntaxHighlighter.ie { 154 | font-size: .9em !important; 155 | padding: 1px 0 1px 0 !important; 156 | } 157 | .Event_syntaxHighlighter.ie .toolbar { 158 | line-height: 8px !important; 159 | } 160 | .Event_syntaxHighlighter.ie .toolbar a { 161 | padding-top: 0px !important; 162 | } 163 | .Event_syntaxHighlighter.printing .line.alt1 .content, 164 | .Event_syntaxHighlighter.printing .line.alt2 .content, 165 | .Event_syntaxHighlighter.printing .line.highlighted .number, 166 | .Event_syntaxHighlighter.printing .line.highlighted.alt1 .content, 167 | .Event_syntaxHighlighter.printing .line.highlighted.alt2 .content { 168 | background: none !important; 169 | } 170 | .Event_syntaxHighlighter.printing .line .number { 171 | color: #bbbbbb !important; 172 | } 173 | .Event_syntaxHighlighter.printing .line .content { 174 | color: black !important; 175 | } 176 | .Event_syntaxHighlighter.printing .toolbar { 177 | display: none !important; 178 | } 179 | .Event_syntaxHighlighter.printing a { 180 | text-decoration: none !important; 181 | } 182 | .Event_syntaxHighlighter.printing .plain, .Event_syntaxHighlighter.printing .plain a { 183 | color: black !important; 184 | } 185 | .Event_syntaxHighlighter.printing .comments, .Event_syntaxHighlighter.printing .comments a { 186 | color: #008200 !important; 187 | } 188 | .Event_syntaxHighlighter.printing .string, .Event_syntaxHighlighter.printing .string a { 189 | color: blue !important; 190 | } 191 | .Event_syntaxHighlighter.printing .keyword { 192 | color: #006699 !important; 193 | font-weight: bold !important; 194 | } 195 | .Event_syntaxHighlighter.printing .preprocessor { 196 | color: gray !important; 197 | } 198 | .Event_syntaxHighlighter.printing .variable { 199 | color: #aa7700 !important; 200 | } 201 | .Event_syntaxHighlighter.printing .value { 202 | color: #009900 !important; 203 | } 204 | .Event_syntaxHighlighter.printing .functions { 205 | color: #ff1493 !important; 206 | } 207 | .Event_syntaxHighlighter.printing .constants { 208 | color: #0066cc !important; 209 | } 210 | .Event_syntaxHighlighter.printing .script { 211 | font-weight: bold !important; 212 | } 213 | .Event_syntaxHighlighter.printing .color1, .Event_syntaxHighlighter.printing .color1 a { 214 | color: gray !important; 215 | } 216 | .Event_syntaxHighlighter.printing .color2, .Event_syntaxHighlighter.printing .color2 a { 217 | color: #ff1493 !important; 218 | } 219 | .Event_syntaxHighlighter.printing .color3, .Event_syntaxHighlighter.printing .color3 a { 220 | color: red !important; 221 | } 222 | .Event_syntaxHighlighter.printing .break, .Event_syntaxHighlighter.printing .break a { 223 | color: black !important; 224 | } 225 | 226 | 227 | 228 | /** 229 | * SyntaxHighlighter 230 | * http://alexgorbatchev.com/SyntaxHighlighter 231 | * 232 | * SyntaxHighlighter is donationware. If you are using it, please donate. 233 | * http://alexgorbatchev.com/SyntaxHighlighter/donate.html 234 | * 235 | * @version 236 | * 3.0.83 (July 02 2010) 237 | * 238 | * @copyright 239 | * Copyright (C) 2004-2010 Alex Gorbatchev. 240 | * 241 | * @license 242 | * Dual licensed under the MIT and GPL licenses. 243 | */ 244 | .Event_syntaxHighlighter { 245 | background-color: white !important; 246 | font-size: 13px !important; 247 | overflow: visible !important; 248 | } 249 | .Event_syntaxHighlighter .line.alt1 { 250 | background-color: white !important; 251 | } 252 | .Event_syntaxHighlighter .line.alt2 { 253 | background-color: #F8F8F8 !important; 254 | } 255 | .Event_syntaxHighlighter .line.highlighted.alt1, .Event_syntaxHighlighter .line.highlighted.alt2 { 256 | background-color: #e0e0e0 !important; 257 | } 258 | .Event_syntaxHighlighter .line.highlighted.number { 259 | color: black !important; 260 | } 261 | .Event_syntaxHighlighter table caption { 262 | color: black !important; 263 | } 264 | .Event_syntaxHighlighter .gutter { 265 | } 266 | .Event_syntaxHighlighter .gutter div { 267 | color: #5C5C5C !important; 268 | } 269 | .Event_syntaxHighlighter .gutter .line.alt1, .Event_syntaxHighlighter .gutter .line.alt2 { 270 | background-color: white !important; 271 | } 272 | .odd .Event_syntaxHighlighter .gutter .line.alt1, .odd .Event_syntaxHighlighter .gutter .line.alt2 { 273 | background-color: #F2F2F2 !important; 274 | } 275 | .Event_syntaxHighlighter .gutter .line { 276 | border-right: 3px solid #4E6CA3 !important; 277 | } 278 | .Event_syntaxHighlighter .gutter .line.highlighted { 279 | background-color: #4E6CA3 !important; 280 | color: white !important; 281 | } 282 | .Event_syntaxHighlighter.printing .line .content { 283 | border: none !important; 284 | } 285 | .Event_syntaxHighlighter.collapsed { 286 | overflow: visible !important; 287 | } 288 | .Event_syntaxHighlighter.collapsed .toolbar { 289 | color: blue !important; 290 | background: white !important; 291 | border: 1px solid #4E6CA3 !important; 292 | } 293 | .Event_syntaxHighlighter.collapsed .toolbar a { 294 | color: blue !important; 295 | } 296 | .Event_syntaxHighlighter.collapsed .toolbar a:hover { 297 | color: red !important; 298 | } 299 | .Event_syntaxHighlighter .toolbar { 300 | color: white !important; 301 | border: none !important; 302 | } 303 | .Event_syntaxHighlighter .toolbar a { 304 | font: 100%/1.45em "Lucida Grande", Verdana, Arial, Helvetica, sans-serif !important; 305 | color: white !important; 306 | background: #4E6CA3 !important; 307 | float: right !important; 308 | padding: 2px 5px !important; 309 | clear: both; 310 | } 311 | .Event_syntaxHighlighter .toolbar a:hover { 312 | color: #b7c5df !important; 313 | background: #39568b !important; 314 | } 315 | .Event_syntaxHighlighter .plain, .Event_syntaxHighlighter .plain a { 316 | color: black !important; 317 | } 318 | .Event_syntaxHighlighter .comments, .Event_syntaxHighlighter .comments a { 319 | color: #008200 !important; 320 | } 321 | .Event_syntaxHighlighter .string, .Event_syntaxHighlighter .string a { 322 | color: blue !important; 323 | } 324 | .Event_syntaxHighlighter .keyword { 325 | color: #006699 !important; 326 | } 327 | .Event_syntaxHighlighter .preprocessor { 328 | color: gray !important; 329 | } 330 | .Event_syntaxHighlighter .variable { 331 | color: #aa7700 !important; 332 | } 333 | .Event_syntaxHighlighter .value { 334 | color: #009900 !important; 335 | } 336 | .Event_syntaxHighlighter .functions { 337 | color: #ff1493 !important; 338 | } 339 | .Event_syntaxHighlighter .constants { 340 | color: #0066cc !important; 341 | } 342 | .Event_syntaxHighlighter .script { 343 | font-weight: bold !important; 344 | color: #006699 !important; 345 | background-color: none !important; 346 | } 347 | .Event_syntaxHighlighter .color1, .Event_syntaxHighlighter .color1 a { 348 | color: gray !important; 349 | } 350 | .Event_syntaxHighlighter .color2, .Event_syntaxHighlighter .color2 a { 351 | color: #ff1493 !important; 352 | } 353 | .Event_syntaxHighlighter .color3, .Event_syntaxHighlighter .color3 a { 354 | color: red !important; 355 | } 356 | 357 | .Event_syntaxHighlighter .keyword { 358 | font-weight: bold !important; 359 | } 360 | 361 | .datatables_ref:hover { 362 | text-decoration: underline; 363 | cursor: pointer; 364 | *cursor: hand; 365 | } 366 | 367 | .Event_syntaxHighlighter .dtapi { 368 | color: #069; 369 | } 370 | 371 | .Event_syntaxHighlighter .dtapi:hover { 372 | text-decoration: underline; 373 | cursor: pointer; 374 | *cursor: hand; 375 | } 376 | 377 | .Event_syntaxHighlighter table { 378 | table-layout: fixed !important; 379 | } 380 | 381 | .Event_syntaxHighlighter table td.gutter { 382 | width: 46px !important; /* enough for three digits */ 383 | } 384 | 385 | .Event_syntaxHighlighter table td.code { 386 | width: auto !important; 387 | overflow: auto !important; 388 | } 389 | 390 | -------------------------------------------------------------------------------- /js/VisualEvent.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @summary Visual Event 3 | * @description Visual Event - show Javascript events which have been attached to objects, and 4 | * the event's associated function code, visually. 5 | * @file VisualEvent_Loader.js 6 | * @author Allan Jardine (www.sprymedia.co.uk) 7 | * @license GPL v2 8 | * @contact www.sprymedia.co.uk/contact 9 | * 10 | * @copyright Copyright 2007-2013 Allan Jardine. 11 | * 12 | * This source file is free software, under the GPL v2 license: 13 | * http://www.gnu.org/licenses/gpl-2.0.html 14 | */ 15 | 16 | (function(window, document, $){ 17 | 18 | /*global VisualEvent,VisualEvent_Loader,VisualEvents,VisualEventSyntaxHighlighter*/ 19 | 20 | 21 | /** 22 | * Visual Event will show, visually, which DOM elements on a web-page have events attached to 23 | * them, information about those events and the code accossiated with each handler for the 24 | * event. It does this by parsing through the cache of Javascript libraries (as there is no DOM 25 | * method to get the information required), thus a major part of Visual Event are the library 26 | * parsers. A result of this is that universal display of events is not possible - there must 27 | * be a parser available. 28 | * 29 | * Visual Event's display is broken into four major areas: 30 | * - Label - The information toolbar at the bottom of the window (fixed) showing Visual Event 31 | * controls (close and help), the name of the program and information about the events that have 32 | * been found on the page. 33 | * 34 | * - Help - The help view is a completely blocking layer which shows information about Visual 35 | * Event and how to use it. A single click will remove the help layer and restore the standard 36 | * Visual Event view. 37 | * 38 | * - Display - A layer which provides a background to Visual Event (thus when Visual Event is 39 | * active is it blocking to the web-page below it) and acts as a container for the boxes (DIVs) 40 | * which serve as a visual indicator that there is an event attached to the element below it 41 | * (sized to match the element with the event attached). 42 | * 43 | * - Lightbox - The event information and code display of attached events. 44 | * 45 | * Note that currently there can only be one instance of Visual Event at a time, due to 46 | * practicality (no point in showing the same thing twice, at the same time) and the use of 47 | * element IDs in the script. 48 | * 49 | * @class VisualEvent 50 | * @constructor 51 | * 52 | * @example 53 | * new VisualEvent(); 54 | */ 55 | window.VisualEvent = function () 56 | { 57 | // Sanity check 58 | if ( ! this instanceof VisualEvent ) { 59 | alert( "VisualEvent warning: Must be initialised with the 'new' keyword." ); 60 | return; 61 | } 62 | 63 | // Only one instance of VisualEvent at a time, in the current running mode. So if there is a 64 | // current instance, shut it down first 65 | if ( VisualEvent.instance !== null ) { 66 | VisualEvent.instance.close(); 67 | } 68 | VisualEvent.instance = this; 69 | 70 | 71 | /** 72 | * Settings object containing customisable information for the class instance 73 | * @namespace 74 | */ 75 | this.s = { 76 | /** 77 | * Array of objects containing information about the nodes which have been found to have 78 | * events attached to them. Each object contains the following parameters: 79 | * {element} node The DOM element in question 80 | * {array} listeners Array of objects which with details about each of the events on this node 81 | * {string} func Source of the event handler (from Function.toString()) 82 | * {string} source Library name / version 83 | * {string} type Type of event (click, change, keyup etc) 84 | * {boolean} removed Flag to indicate if the event has been removed (for API) 85 | * @type array 86 | * @default null 87 | */ 88 | "elements": null, 89 | 90 | /** 91 | * setTimeout reference for delayed hiding of the lightbox layer 92 | * @type int 93 | * @default null 94 | * @private 95 | */ 96 | "mouseTimer": null, 97 | 98 | /** 99 | * Counter for the number of events which have been found from a JS library's cache, but 100 | * are not currently available in the document's DOM 101 | * @type int 102 | * @default null 103 | * @private 104 | */ 105 | "nonDomEvents": 0, 106 | 107 | /** 108 | * Array of objects holding information about each SCRIPT tag that is found in the DOM. Each 109 | * object contains the parameters: 110 | * {string} src The URL of the script source (or inline string if no src attribute) 111 | * {string} code The code (.text) from the script 112 | * @type array 113 | * @default [] 114 | * @private 115 | */ 116 | "scripts": [] 117 | }; 118 | 119 | /** 120 | * DOM elements used by the class instance 121 | * @namespace 122 | */ 123 | this.dom = { 124 | /** 125 | * Label layer - for showing that Visual Event is currently running and information and 126 | * controls, about and for this instance 127 | * @type element 128 | * @default See code 129 | */ 130 | "label": $( 131 | '
'+ 132 | 'x'+ 133 | '?'+ 134 | 'Visual Event by Allan Jardine.'+ 135 | ' events were found attached to '+ 136 | ' nodes. '+ 137 | ' events were attached to elements not currently in the document.'+ 138 | '
')[0], 139 | 140 | /** 141 | * Display layer - background layer and container for Visual Event visual node indicators 142 | * @type element 143 | * @default See code 144 | */ 145 | "display": $('
')[0], 146 | 147 | /** 148 | * Lightbox layer - Template for information display about a given node, and the code for 149 | * a given event handler 150 | * @type element 151 | * @default See code 152 | */ 153 | "lightbox": $( 154 | '
'+ 155 | '
Node: '+ 156 | '
Remove from display
'+ 157 | '
'+ 158 | '
'+ 159 | '
'+ 160 | '
    '+ 161 | '
    '+ 162 | '
    '+ 163 | '
    '+ 164 | '
    ')[0], 165 | 166 | /** 167 | * Help layer - information about Visual Event and how to use it 168 | * @type element 169 | * @default See code 170 | */ 171 | "help": $( 172 | '
    '+ 173 | '
    '+ 174 | '

    Visual Event help

    '+ 175 | '

    Visual Event will show which elements on any given page have Javascript events attached '+ 176 | 'to them, what those events are and the code associated with the events. In Webkit '+ 177 | 'browsers and Opera, Visual Event will also indicate where the code in question was '+ 178 | 'defined.

    '+ 179 | '

    Note that Visual Event is only able to show events for Javascript libraries for which '+ 180 | 'it has a parser. This is currently: DOM0 events, Glow, jQuery, MooTools, Prototype and YUI2.

    '+ 181 | '

    Commands:

    '+ 182 | ''+ 183 | ''+ 184 | ''+ 185 | ''+ 186 | ''+ 187 | ''+ 188 | ''+ 189 | ''+ 190 | ''+ 191 | ''+ 192 | ''+ 193 | ''+ 194 | ''+ 195 | ''+ 196 | ''+ 197 | ''+ 198 | ''+ 199 | ''+ 200 | ''+ 201 | ''+ 202 | ''+ 203 | '
    Double click element with eventHide event indicator. Allows access to nodes underneath
    Key: spaceRestore all events to be visible
    Key: escQuit out of Visual Event
    Key: hShow / hide this help box
    Key: rReload and display events on page
    '+ 204 | '

    The colour of the elements that have been detected to have an event reflect the type of '+ 205 | 'events that are attached to the element:

    '+ 206 | ''+ 207 | ''+ 208 | ''+ 209 | ''+ 210 | ''+ 211 | ''+ 212 | ''+ 213 | ''+ 214 | ''+ 215 | ''+ 216 | ''+ 217 | ''+ 218 | ''+ 219 | ''+ 220 | ''+ 221 | ''+ 222 | ''+ 223 | ''+ 224 | ''+ 225 | '
    Mouse eventUI eventHTML eventMouse + HTMLMouse + UIHTML + UIMouse + HTML + UI
    '+ 226 | '

    Visual Event is open source software (GPLv2). If you would like to contribute an '+ 227 | 'enhancement, please fork the project on '+ 228 | 'Github!

    '+ 229 | '

    Click anywhere to close this help box.

    '+ 230 | '
    '+ 231 | '
    ')[0], 232 | 233 | 234 | /** 235 | * Reference to the visual event node indicator - so we have a reference to what node we are 236 | * showing the lightbox information about 237 | * @type element 238 | * @default See code 239 | */ 240 | "activeEventNode": null 241 | }; 242 | 243 | this._construct(); 244 | }; 245 | 246 | 247 | VisualEvent.prototype = { 248 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 249 | * API methods 250 | */ 251 | 252 | /** 253 | * Shutdown Visual Event and return to the original page 254 | * @param {event} e Event object 255 | */ 256 | "close": function ( e ) 257 | { 258 | // Remove all events that we've added 259 | $('*').unbind('.VisualEvent'); 260 | $(document).unbind( 'keydown.VisualEvent' ); 261 | 262 | $(this.dom.display).remove(); 263 | $(this.dom.lightbox).remove(); 264 | $(this.dom.label).remove(); 265 | $(this.dom.help).remove(); 266 | 267 | if ( typeof VisualEvent_Loader !== 'undefined' && !VisualEvent_Loader.jQueryPreLoaded ) { 268 | $.noConflict(); 269 | } 270 | 271 | VisualEvent.instance = null; 272 | }, 273 | 274 | 275 | /** 276 | * Reinitialise Visual Event (i.e. bring it up-to-date with any new events which might have 277 | * been added 278 | */ 279 | "reInit": function () 280 | { 281 | $('*').unbind('.VisualEvent'); 282 | $(document).unbind( 'keydown.VisualEvent' ); 283 | 284 | $(this.dom.display).empty(); 285 | $(this.dom.display).remove(); 286 | $(this.dom.lightbox).remove(); 287 | $(this.dom.label).remove(); 288 | $(this.dom.help).remove(); 289 | 290 | this.s.elements.splice(0, this.s.elements.length); 291 | this.s.nonDomEvents = 0; 292 | 293 | this._construct(); 294 | }, 295 | 296 | 297 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 298 | * Private methods 299 | */ 300 | 301 | /** 302 | * Visual Event constructor 303 | * @private 304 | */ 305 | "_construct": function () 306 | { 307 | var that = this; 308 | var i, iLen; 309 | var windowHeight = $(document).height(); 310 | var windowWidth = $(document).width(); 311 | 312 | /* Prep the DOM */ 313 | this.dom.display.style.width = windowWidth+'px'; 314 | this.dom.display.style.height = windowHeight+'px'; 315 | 316 | document.body.appendChild( this.dom.display ); 317 | document.body.appendChild( this.dom.lightbox ); 318 | document.body.appendChild( this.dom.label ); 319 | 320 | /* Event handlers */ 321 | $(this.dom.lightbox).bind('mouseover.VisualEvent', function (e) { 322 | that._timerClear( e ); 323 | } ).bind( 'mousemove.VisualEvent', function (e) { 324 | that._timerClear( e ); 325 | } ).bind( 'mouseout.VisualEvent', function (e) { 326 | that._lightboxHide(); 327 | } ); 328 | 329 | $('div.Event_NodeRemove', this.dom.lightbox).bind('click.VisualEvent', function (e) { 330 | that.dom.activeEventNode.style.display = "none"; 331 | that.dom.lightbox.style.display = "none"; 332 | } ); 333 | 334 | $(document).bind( 'keydown.VisualEvent', function( e ) { 335 | if ( e.which === 0 || e.which === 27 ) { // esc 336 | that.close(); 337 | } 338 | if ( e.which === 72 ) { // 'h' 339 | if ( $(that.dom.help).filter(':visible').length === 0 ) { 340 | that._help(); 341 | } 342 | else { 343 | that._hideHelp(); 344 | } 345 | } 346 | else if ( e.which === 32 ) { // space 347 | $('div.EventLabel').css('display', 'block'); 348 | e.preventDefault(); 349 | } 350 | else if ( e.which === 82 ) { // r 351 | that.reInit(); 352 | } 353 | } ); 354 | 355 | /* Build the events list and display */ 356 | this.s.elements = this._eventsLoad(); 357 | for ( i=0, iLen=this.s.elements.length ; i 0 ) { 404 | return; 405 | } 406 | 407 | var loadQueue = []; 408 | var scripts = document.getElementsByTagName('script'); 409 | for ( var i=0, iLen=scripts.length ; i'+this._scriptName(srcFiles[0].src)+':'+ srcFiles[0].line + "
    "; 498 | } else { 499 | origin += srcFiles[0].src+"
    "; 500 | } 501 | } else { 502 | origin = "Function could originate in multiple locations:
    "; 503 | for ( i=0, iLen=srcFiles.length ; i'+this._scriptName(srcFiles[i].src)+':'+srcFiles[i].line+'
    '; 506 | } 507 | } 508 | 509 | return origin; 510 | }, 511 | 512 | 513 | /** 514 | * Get the name of a file from a URL (i.e. the last part in a slash seperated string) 515 | * @param {string} src URL to get the file name from 516 | * @returns {string} File name 517 | * @private 518 | */ 519 | "_scriptName": function ( src ) 520 | { 521 | var a = src.split('/'); 522 | return a[ a.length-1 ]; 523 | }, 524 | 525 | 526 | 527 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 528 | * Display 529 | */ 530 | 531 | /** 532 | * Build the list of nodes that have events attached to them by going through all installed 533 | * parsers 534 | * @returns {array} List of nodes with their associated events 535 | * @private 536 | */ 537 | "_eventsLoad": function () 538 | { 539 | var i, iLen; 540 | var elements=[], libraryListeners; 541 | 542 | /* Gather the events from the supported libraries */ 543 | for ( i=0, iLen=VisualEvent.parsers.length ; i