RGraph.common.context.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594
  1. // version: 2014-06-26
  2. /**
  3. * o--------------------------------------------------------------------------------o
  4. * | This file is part of the RGraph package. RGraph is Free Software, licensed |
  5. * | under the MIT license - so it's free to use for all purposes. If you want to |
  6. * | donate to help keep the project going then you can do so here: |
  7. * | |
  8. * | http://www.rgraph.net/donate |
  9. * o--------------------------------------------------------------------------------o
  10. */
  11. RGraph = window.RGraph || {isRGraph: true};
  12. // Module pattern
  13. (function (win, doc, undefined)
  14. {
  15. var RG = RGraph,
  16. ua = navigator.userAgent,
  17. ma = Math;
  18. /**
  19. * This gunction shows a context menu containing the parameters
  20. * provided to it
  21. *
  22. * @param object canvas The canvas object
  23. * @param array menuitems The context menu menuitems
  24. * @param object e The event object
  25. */
  26. RG.contextmenu =
  27. RG.Contextmenu = function (obj, menuitems, e)
  28. {
  29. var canvas = obj.canvas;
  30. e = RG.FixEventObject(e);
  31. /**
  32. * Fire the custom RGraph event onbeforecontextmenu
  33. */
  34. RG.FireCustomEvent(obj, 'onbeforecontextmenu');
  35. /**
  36. * Hide any existing menu
  37. */
  38. if (RG.Registry.Get('chart.contextmenu')) {
  39. RG.HideContext();
  40. }
  41. // Hide any zoomed canvas
  42. RG.HideZoomedCanvas();
  43. /**
  44. * Hide the palette if necessary
  45. */
  46. RG.HidePalette();
  47. /**
  48. * This is here to ensure annotating is OFF
  49. */
  50. obj.Set('chart.mousedown', false);
  51. var x = e.pageX;
  52. var y = e.pageY;
  53. var div = document.createElement('div');
  54. var bg = document.createElement('div');
  55. div.className = 'RGraph_contextmenu';
  56. div.__canvas__ = canvas; /* Store a reference to the canvas on the contextmenu object */
  57. div.style.position = 'absolute';
  58. div.style.left = 0;
  59. div.style.top = 0;
  60. div.style.border = '1px solid black';
  61. div.style.backgroundColor = 'white';
  62. div.style.boxShadow = '3px 3px 3px rgba(96,96,96,0.5)';
  63. div.style.MozBoxShadow = '3px 3px 3px rgba(96,96,96,0.5)';
  64. div.style.WebkitBoxShadow = '3px 3px 3px rgba(96,96,96,0.5)';
  65. div.style.filter = 'progid:DXImageTransform.Microsoft.Shadow(color=#aaaaaa,direction=135)';
  66. div.style.opacity = 0;
  67. bg.className = 'RGraph_contextmenu_background';
  68. bg.style.position = 'absolute';
  69. bg.style.backgroundColor = '#ccc';
  70. bg.style.borderRight = '1px solid #aaa';
  71. bg.style.top = 0;
  72. bg.style.left = 0;
  73. bg.style.width = '18px';
  74. bg.style.height = '100%';
  75. bg.style.opacity = 0;
  76. div = document.body.appendChild(div);
  77. bg = div.appendChild(bg);
  78. /**
  79. * Now add the context menu items
  80. */
  81. for (i=0; i<menuitems.length; ++i) {
  82. var menuitem = document.createElement('div');
  83. menuitem.__object__ = obj;
  84. menuitem.__canvas__ = canvas;
  85. menuitem.__contextmenu__ = div;
  86. menuitem.className = 'RGraph_contextmenu_item';
  87. if (menuitems[i]) {
  88. menuitem.style.padding = '2px 5px 2px 23px';
  89. menuitem.style.fontFamily = 'Arial';
  90. menuitem.style.fontSize = '10pt';
  91. menuitem.style.fontWeight = 'normal';
  92. menuitem.innerHTML = menuitems[i][0];
  93. if (RG.is_array(menuitems[i][1])) {
  94. menuitem.style.backgroundImage = 'url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAYAAADEUlfTAAAAQUlEQVQImY3NoQ2AMABE0ZewABMyGQ6mqWODzlAclBSFO8HZl8uf0FFxCHtwYkt4Y6ChYE44cGH9/fyae2p2LAleW9oVTQuVf6gAAAAASUVORK5CYII=)';
  95. menuitem.style.backgroundRepeat = 'no-repeat';
  96. menuitem.style.backgroundPosition = '97% center';
  97. }
  98. // Add the mouseover event
  99. if (menuitems[i][1]) {
  100. if (menuitem.addEventListener) {
  101. menuitem.addEventListener("mouseover", function (e) {RG.HideContextSubmenu(); e.target.style.backgroundColor = 'rgba(0,0,0,0.2)'; e.target.style.cursor = 'pointer';}, false);
  102. menuitem.addEventListener("mouseout", function (e) {e.target.style.backgroundColor = 'inherit'; e.target.style.cursor = 'default';}, false);
  103. } else {
  104. menuitem.attachEvent("onmouseover", function () {RG.HideContextSubmenu();event.srcElement.style.backgroundColor = '#eee';event.srcElement.style.cursor = 'pointer';}
  105. , false);
  106. menuitem.attachEvent("onmouseout", function () {event.srcElement.style.backgroundColor = 'inherit'; event.srcElement.style.cursor = 'default';}, false);
  107. }
  108. } else {
  109. if (menuitem.addEventListener) {
  110. menuitem.addEventListener("mouseover", function (e) {e.target.style.cursor = 'default';}, false);
  111. menuitem.addEventListener("mouseout", function (e) {e.target.style.cursor = 'default';}, false);
  112. } else {
  113. menuitem.attachEvent("onmouseover", function () {event.srcElement.style.cursor = 'default'}, false);
  114. menuitem.attachEvent("onmouseout", function () {event.srcElement.style.cursor = 'default';}, false);
  115. }
  116. }
  117. } else {
  118. menuitem.style.borderBottom = '1px solid #ddd';
  119. menuitem.style.marginLeft = '25px';
  120. }
  121. div.appendChild(menuitem);
  122. /**
  123. * Install the event handler that calls the menuitem
  124. */
  125. if (menuitems[i] && menuitems[i][1] && typeof(menuitems[i][1]) == 'function') {
  126. menuitem.addEventListener('click', menuitems[i][1], false);
  127. // Submenu
  128. } else if (menuitems[i] && menuitems[i][1] && RG.is_array(menuitems[i][1])) {
  129. (function ()
  130. {
  131. var tmp = menuitems[i][1]; // This is here because of "references vs primitives" and how they're passed around in Javascript
  132. // TODO This may need attention
  133. menuitem.addEventListener('mouseover', function (e) {RG.Contextmenu_submenu(obj, tmp, e.target);}, false);
  134. })();
  135. }
  136. }
  137. /**
  138. * Now all the menu items have been added, set the shadow width
  139. * Shadow now handled by CSS3
  140. */
  141. div.style.width = (div.offsetWidth + 10) + 'px';
  142. div.style.height = (div.offsetHeight - 2) + 'px';
  143. // Show the menu to the left or the right (normal) of the cursor?
  144. if (x + div.offsetWidth > document.body.offsetWidth) {
  145. x -= div.offsetWidth;
  146. }
  147. // Reposition the menu (now we have the real offsetWidth)
  148. div.style.left = x + 'px';
  149. div.style.top = y + 'px';
  150. /**
  151. * Do a little fade in effect
  152. */
  153. setTimeout("if (obj = RGraph.Registry.Get('chart.contextmenu')) obj.style.opacity = 0.2", 50);
  154. setTimeout("if (obj = RGraph.Registry.Get('chart.contextmenu')) obj.style.opacity = 0.4", 100);
  155. setTimeout("if (obj = RGraph.Registry.Get('chart.contextmenu')) obj.style.opacity = 0.6", 150);
  156. setTimeout("if (obj = RGraph.Registry.Get('chart.contextmenu')) obj.style.opacity = 0.8", 200);
  157. setTimeout("if (obj = RGraph.Registry.Get('chart.contextmenu')) obj.style.opacity = 1", 250);
  158. // The fade in effect on the left gray bar
  159. setTimeout("if (obj = RGraph.Registry.Get('chart.contextmenu.bg')) obj.style.opacity = 0.2", 50);
  160. setTimeout("if (obj = RGraph.Registry.Get('chart.contextmenu.bg')) obj.style.opacity = 0.4", 100);
  161. setTimeout("if (obj = RGraph.Registry.Get('chart.contextmenu.bg')) obj.style.opacity = 0.6", 150);
  162. setTimeout("if (obj = RGraph.Registry.Get('chart.contextmenu.bg')) obj.style.opacity = 0.8", 200);
  163. setTimeout("if (obj = RGraph.Registry.Get('chart.contextmenu.bg')) obj.style.opacity = 1", 250);
  164. // Store the context menu in the registry
  165. RG.Registry.Set('chart.contextmenu', div);
  166. RG.Registry.Set('chart.contextmenu.bg', bg);
  167. RG.Registry.Get('chart.contextmenu').oncontextmenu = function () {return false;};
  168. RG.Registry.Get('chart.contextmenu.bg').oncontextmenu = function () {return false;};
  169. /**
  170. * Install the event handlers that hide the context menu
  171. */
  172. canvas.addEventListener('click', function () {RG.HideContext();}, false);
  173. window.addEventListener('click', function ()
  174. {
  175. RG.HideContext();
  176. }, false);
  177. window.addEventListener('resize', function ()
  178. {
  179. RG.HideContext();
  180. }, false);
  181. /**
  182. * Add the __shape__ object to the context menu
  183. */
  184. /**
  185. * Set the shape coords from the .getShape() method
  186. */
  187. if (typeof(obj.getShape) == 'function') {
  188. RG.Registry.Get('chart.contextmenu').__shape__ = obj.getShape(e);
  189. }
  190. e.stopPropagation();
  191. /**
  192. * Fire the (RGraph) oncontextmenu event
  193. */
  194. RG.FireCustomEvent(obj, 'oncontextmenu');
  195. return false;
  196. };
  197. /**
  198. * Hides the context menu if it's currently visible
  199. */
  200. RG.hideContext =
  201. RG.HideContext = function ()
  202. {
  203. var cm = RG.Registry.Get('chart.contextmenu');
  204. var cmbg = RG.Registry.Get('chart.contextmenu.bg');
  205. //Hide any submenu currently being displayed
  206. RG.HideContextSubmenu();
  207. if (cm) {
  208. cm.parentNode.removeChild(cm);
  209. cmbg.parentNode.removeChild(cmbg);
  210. cm.style.visibility = 'hidden';
  211. cm.style.display = 'none';
  212. RG.Registry.Set('chart.contextmenu', null);
  213. cmbg.style.visibility = 'hidden';
  214. cmbg.style.display = 'none';
  215. RG.Registry.Set('chart.contextmenu.bg', null);
  216. }
  217. };
  218. /**
  219. * Hides the context menus SUBMENU if it's currently visible
  220. */
  221. RG.hideContextSubmenu =
  222. RG.HideContextSubmenu = function ()
  223. {
  224. var sub = RG.Registry.Get('chart.contextmenu.submenu');
  225. if (sub) {
  226. sub.style.visibility = 'none';
  227. sub.style.display = 'none';
  228. RG.Registry.Set('chart.contextmenu.submenu', null);
  229. }
  230. };
  231. /**
  232. * Shows the context menu after making a few checks - not opera (doesn't support oncontextmenu,
  233. * not safari (tempermentality), not chrome (hmmm)
  234. */
  235. RG.showContext =
  236. RG.ShowContext = function (obj)
  237. {
  238. RG.HidePalette();
  239. if (obj.Get('chart.contextmenu') && obj.Get('chart.contextmenu').length) {
  240. var isOpera = navigator.userAgent.indexOf('Opera') >= 0;
  241. var isSafari = navigator.userAgent.indexOf('Safari') >= 0;
  242. var isChrome = navigator.userAgent.indexOf('Chrome') >= 0;
  243. var isMacFirefox = navigator.userAgent.indexOf('Firefox') > 0 && navigator.userAgent.indexOf('Mac') > 0;
  244. var isIE9 = navigator.userAgent.indexOf('MSIE 9') >= 0;
  245. if (((!isOpera && !isSafari) || isChrome) && !isMacFirefox) {
  246. obj.canvas.oncontextmenu = function (e)
  247. {
  248. e = RG.FixEventObject(e);
  249. if (e.ctrlKey) return true;
  250. RG.Contextmenu(obj, obj.Get('chart.contextmenu'), e);
  251. return false;
  252. }
  253. // Accomodate Opera and Safari - use double click event
  254. } else {
  255. obj.canvas.addEventListener('dblclick', function (e)
  256. {
  257. if (e.ctrlKey) return true;
  258. if (!RG.Registry.Get('chart.contextmenu')) {
  259. RG.Contextmenu(obj, obj.Get('chart.contextmenu'), e);
  260. }
  261. }, false);
  262. }
  263. }
  264. };
  265. /**
  266. * This draws a submenu should it be necessary
  267. *
  268. * @param object obj The graph object
  269. * @param object menu The context menu
  270. */
  271. RG.contextmenu_submenu =
  272. RG.Contextmenu_submenu = function (obj, menuitems, parentMenuItem)
  273. {
  274. RG.HideContextSubmenu();
  275. var canvas = obj.canvas;
  276. var context = obj.context;
  277. var menu = parentMenuItem.parentNode;
  278. var subMenu = document.createElement('DIV');
  279. subMenu.style.position = 'absolute';
  280. subMenu.style.width = '100px';
  281. subMenu.style.top = menu.offsetTop + parentMenuItem.offsetTop + 'px';
  282. subMenu.style.left = (menu.offsetLeft + menu.offsetWidth - (RG.ISOLD ? 9 : 0)) + 'px';
  283. subMenu.style.backgroundColor = 'white';
  284. subMenu.style.border = '1px solid black';
  285. subMenu.className = 'RGraph_contextmenu';
  286. subMenu.__contextmenu__ = menu;
  287. subMenu.style.boxShadow = '3px 3px 3px rgba(96,96,96,0.5)';
  288. subMenu.style.MozBoxShadow = '3px 3px 3px rgba(96,96,96,0.5)';
  289. subMenu.style.WebkitBoxShadow = '3px 3px 3px rgba(96,96,96,0.5)';
  290. subMenu.style.filter = 'progid:DXImageTransform.Microsoft.Shadow(color=#aaaaaa,direction=135)';
  291. document.body.appendChild(subMenu);
  292. for (var i=0; i<menuitems.length; ++i) {
  293. var menuitem = document.createElement('DIV');
  294. menuitem.__canvas__ = canvas;
  295. menuitem.__contextmenu__ = menu;
  296. menuitem.className = 'RGraph_contextmenu_item';
  297. if (menuitems[i]) {
  298. menuitem.style.padding = '2px 5px 2px 23px';
  299. menuitem.style.fontFamily = 'Arial';
  300. menuitem.style.fontSize = '10pt';
  301. menuitem.style.fontWeight = 'normal';
  302. menuitem.innerHTML = menuitems[i][0];
  303. if (menuitems[i][1]) {
  304. if (menuitem.addEventListener) {
  305. menuitem.addEventListener("mouseover", function (e) {e.target.style.backgroundColor = 'rgba(0,0,0,0.2)'; e.target.style.cursor = 'pointer';}, false);
  306. menuitem.addEventListener("mouseout", function (e) {e.target.style.backgroundColor = 'inherit'; e.target.style.cursor = 'default';}, false);
  307. } else {
  308. menuitem.attachEvent("onmouseover", function () {event.srcElement.style.backgroundColor = 'rgba(0,0,0,0.2)'; event.srcElement.style.cursor = 'pointer'}, false);
  309. menuitem.attachEvent("onmouseout", function () {event.srcElement.style.backgroundColor = 'inherit'; event.srcElement.style.cursor = 'default';}, false);
  310. }
  311. } else {
  312. if (menuitem.addEventListener) {
  313. menuitem.addEventListener("mouseover", function (e) {e.target.style.cursor = 'default';}, false);
  314. menuitem.addEventListener("mouseout", function (e) {e.target.style.cursor = 'default';}, false);
  315. } else {
  316. menuitem.attachEvent("onmouseover", function () {event.srcElement.style.cursor = 'default'}, false);
  317. menuitem.attachEvent("onmouseout", function () {event.srcElement.style.cursor = 'default';}, false);
  318. }
  319. }
  320. } else {
  321. menuitem.style.borderBottom = '1px solid #ddd';
  322. menuitem.style.marginLeft = '25px';
  323. }
  324. subMenu.appendChild(menuitem);
  325. if (menuitems[i] && menuitems[i][1]) {
  326. if (document.all) {
  327. menuitem.attachEvent('onclick', menuitems[i][1]);
  328. } else {
  329. menuitem.addEventListener('click', menuitems[i][1], false);
  330. }
  331. }
  332. }
  333. var bg = document.createElement('DIV');
  334. bg.className = 'RGraph_contextmenu_background';
  335. bg.style.position = 'absolute';
  336. bg.style.backgroundColor = '#ccc';
  337. bg.style.borderRight = '1px solid #aaa';
  338. bg.style.top = 0;
  339. bg.style.left = 0;
  340. bg.style.width = '18px';
  341. bg.style.height = '100%';
  342. bg = subMenu.appendChild(bg);
  343. RG.Registry.Set('chart.contextmenu.submenu', subMenu);
  344. };
  345. /**
  346. * A function designed to be used in conjunction with thed context menu
  347. * to allow people to get image versions of canvases.
  348. *
  349. * @param canvas Optionally you can pass in the canvas, which will be used
  350. */
  351. RG.showPNG = function ()
  352. {
  353. if (RG.ISIE8) {
  354. alert('[RGRAPH PNG] Sorry, showing a PNG is not supported on MSIE8.');
  355. return;
  356. }
  357. if (arguments[0] && arguments[0].id) {
  358. var canvas = arguments[0];
  359. var event = arguments[1];
  360. } else if (RG.Registry.Get('chart.contextmenu')) {
  361. var canvas = RG.Registry.Get('chart.contextmenu').__canvas__;
  362. } else {
  363. alert('[RGRAPH SHOWPNG] Could not find canvas!');
  364. }
  365. var obj = canvas.__object__;
  366. /**
  367. * Create the gray background DIV to cover the page
  368. */
  369. var bg = document.createElement('DIV');
  370. bg.id = '__rgraph_image_bg__';
  371. bg.style.position = 'fixed';
  372. bg.style.top = '-10px';
  373. bg.style.left = '-10px';
  374. bg.style.width = '5000px';
  375. bg.style.height = '5000px';
  376. bg.style.backgroundColor = 'rgb(204,204,204)';
  377. bg.style.opacity = 0;
  378. document.body.appendChild(bg);
  379. /**
  380. * Create the div that the graph sits in
  381. */
  382. var div = document.createElement('DIV');
  383. div.style.backgroundColor = 'white';
  384. div.style.opacity = 0;
  385. div.style.border = '1px solid black';
  386. div.style.position = 'fixed';
  387. div.style.top = '20%';
  388. div.style.width = canvas.width + 'px';
  389. div.style.height = canvas.height + 35 + 'px';
  390. div.style.left = (document.body.clientWidth / 2) - (canvas.width / 2) + 'px';
  391. div.style.padding = '5px';
  392. div.style.borderRadius = '10px';
  393. div.style.MozBorderRadius = '10px';
  394. div.style.WebkitBorderRadius = '10px';
  395. div.style.boxShadow = '0 0 15px rgba(96,96,96,0.5)';
  396. div.style.MozBoxShadow = '0 0 15px rgba(96,96,96,0.5)';
  397. div.style.WebkitBoxShadow = 'rgba(96,96,96,0.5) 0 0 15px';
  398. div.__canvas__ = canvas;
  399. div.__object__ = obj;
  400. div.id = '__rgraph_image_div__';
  401. document.body.appendChild(div);
  402. /**
  403. * Add the HTML text inputs
  404. */
  405. div.innerHTML += '<div style="position: absolute; margin-left: 10px; top: ' + canvas.height + 'px; width: ' + (canvas.width - 50) + 'px; height: 25px"><span style="font-size: 12pt;display: inline; display: inline-block; width: 65px; text-align: right">URL:</span><textarea style="float: right; overflow: hidden; height: 20px; width: ' + (canvas.width - obj.gutterLeft - obj.gutterRight - 80) + 'px" onclick="this.select()" readonly="readonly" id="__rgraph_dataurl__">' + canvas.toDataURL() + '</textarea></div>';
  406. div.innerHTML += '<div style="position: absolute; top: ' + (canvas.height + 25) + 'px; left: ' + (obj.gutterLeft - 65 + (canvas.width / 2)) + 'px; width: ' + (canvas.width - obj.gutterRight) + 'px; font-size: 65%">A link using the URL: <a href="' + canvas.toDataURL() + '">View</a></div>'
  407. /**
  408. * Create the image rendition of the graph
  409. */
  410. var img = document.createElement('IMG');
  411. RG.Registry.Set('chart.png', img);
  412. img.__canvas__ = canvas;
  413. img.__object__ = obj;
  414. img.id = '__rgraph_image_img__';
  415. img.className = 'RGraph_png';
  416. img.src = canvas.toDataURL();
  417. div.appendChild(img);
  418. setTimeout(function () {document.getElementById("__rgraph_dataurl__").select();}, 50);
  419. window.addEventListener('resize', function (e){var img = RG.Registry.Get('chart.png');img.style.left = (document.body.clientWidth / 2) - (img.width / 2) + 'px';}, false);
  420. bg.onclick = function (e)
  421. {
  422. var div = document.getElementById("__rgraph_image_div__");
  423. var bg = document.getElementById("__rgraph_image_bg__");
  424. if (div) {
  425. div.style.opacity = 0;
  426. div.parentNode.removeChild(div);
  427. div.id = '';
  428. div.style.display = 'none';
  429. div = null;
  430. }
  431. if (bg) {
  432. bg.style.opacity = 0;
  433. bg.id = '';
  434. bg.style.display = 'none';
  435. bg = null;
  436. }
  437. }
  438. window.addEventListener('resize', function (e) {bg.onclick(e);}, false)
  439. /**
  440. * This sets the image BG and the DIV as global variables, circumventing repeated calls to document.getElementById()
  441. */
  442. RG.showpng_image_bg = bg;
  443. RG.showpng_image_div = div;
  444. setTimeout('RGraph.showpng_image_div.style.opacity = 0.2', 50);
  445. setTimeout('RGraph.showpng_image_div.style.opacity = 0.4', 100);
  446. setTimeout('RGraph.showpng_image_div.style.opacity = 0.6', 150);
  447. setTimeout('RGraph.showpng_image_div.style.opacity = 0.8', 200);
  448. setTimeout('RGraph.showpng_image_div.style.opacity = 1', 250);
  449. setTimeout('RGraph.showpng_image_bg.style.opacity = 0.1', 50);
  450. setTimeout('RGraph.showpng_image_bg.style.opacity = 0.2', 100);
  451. setTimeout('RGraph.showpng_image_bg.style.opacity = 0.3', 150);
  452. setTimeout('RGraph.showpng_image_bg.style.opacity = 0.4', 200);
  453. setTimeout('RGraph.showpng_image_bg.style.opacity = 0.5', 250);
  454. img.onclick = function (e)
  455. {
  456. if (e.stopPropagation) e.stopPropagation();
  457. else event.cancelBubble = true;
  458. }
  459. if (event && event.stopPropagation) {
  460. event.stopPropagation();
  461. }
  462. };
  463. // End module pattern
  464. })(window, document);