Firefox for desktop - list fixed bugs in Mercurial as sortable table

Lists (as sortable table) fixed bugs related to Firefox for desktop in Mozilla Mercurial pushlogs

  1. // ==UserScript==
  2. // @name Firefox for desktop - list fixed bugs in Mercurial as sortable table
  3. // @namespace darkred
  4. // @version 5.5.9.2
  5. // @date 2020.8.25
  6. // @description Lists (as sortable table) fixed bugs related to Firefox for desktop in Mozilla Mercurial pushlogs
  7. // @author darkred, johnp
  8. // @license MIT
  9. // @include /^https?:\/\/hg\.mozilla\.org.*pushloghtml.*/
  10. // @grant GM_getResourceURL
  11. // @grant GM_getResourceText
  12. // @grant GM_addStyle
  13. // @grant GM_xmlhttpRequest
  14. // @require https://code.jquery.com/jquery-2.1.4.min.js
  15. // @require https://code.jquery.com/ui/1.11.4/jquery-ui.min.js
  16. // @require https://cdnjs.cloudflare.com/ajax/libs/jquery.tablesorter/2.24.3/js/jquery.tablesorter.min.js
  17. // @require https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.10.6/moment-with-locales.min.js
  18. // @require https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.4.1/moment-timezone-with-data.min.js
  19. // @require https://cdnjs.cloudflare.com/ajax/libs/jstimezonedetect/1.0.6/jstz.min.js
  20. // @require https://cdnjs.cloudflare.com/ajax/libs/datejs/1.0/date.min.js
  21. // @require https://cdnjs.cloudflare.com/ajax/libs/keypress/2.1.3/keypress.min.js
  22. // @resource jqUI_CSS http://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/themes/redmond/jquery-ui.min.css
  23. // @resource IconSet1 https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/themes/redmond/images/ui-bg_glass_75_d0e5f5_1x400.png
  24. // @resource IconSet2 https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/themes/redmond/images/ui-bg_glass_85_dfeffc_1x400.png
  25. // @resource IconSet3 https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/themes/redmond/images/ui-bg_gloss-wave_55_5c9ccc_500x100.png
  26. // @resource IconSet4 https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/themes/redmond/images/ui-bg_inset-hard_100_fcfdfd_1x100.png
  27. // @resource IconSet5 https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/themes/redmond/images/ui-icons_217bc0_256x240.png
  28. // @resource IconSet6 https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/themes/redmond/images/ui-icons_469bdd_256x240.png
  29. // @resource IconSet7 https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/themes/redmond/images/ui-icons_6da8d5_256x240.png
  30. // Thanks a lot to: johnp (your contribution is most appreciated!), wOxxOm and Brock Adams.
  31. // @supportURL https://github.com/darkred/Userscripts/issues
  32. // ==/UserScript==
  33.  
  34.  
  35. /* eslint-disable no-console, complexity */
  36. /* global jstz, moment */
  37.  
  38.  
  39. var silent = false;
  40. var debug = false;
  41.  
  42. time('MozillaMercurial');
  43.  
  44.  
  45.  
  46.  
  47.  
  48. // CSS rules in order to show 'up' and 'down' arrows in each table header
  49. var stylesheet = `
  50. <style>
  51. thead th {
  52. background-repeat: no-repeat;
  53. background-position: right center;
  54. }
  55. thead th.up {
  56. padding-right: 20px;
  57. background-image: url(data:image/gif;base64,R0lGODlhFQAEAIAAACMtMP///yH5BAEAAAEALAAAAAAVAAQAAAINjI8Bya2wnINUMopZAQA7);
  58. }
  59. thead th.down {
  60. padding-right: 20px;
  61. background-image: url(data:image/gif;base64,R0lGODlhFQAEAIAAACMtMP///yH5BAEAAAEALAAAAAAVAAQAAAINjB+gC+jP2ptn0WskLQA7);
  62. }
  63. </style>`;
  64.  
  65. $('head').append(stylesheet);
  66.  
  67.  
  68.  
  69. var stylesheet2 =
  70. `<style>
  71.  
  72.  
  73. /* in order to highlight hovered table row */
  74. #tbl tr:hover{ background:#F6E6C6 !important;}
  75.  
  76. /* in order the table headers to be larger and bold */
  77. #tbl th {text-align: -moz-center !important; font-size: larger; font-weight: bold; }
  78.  
  79. /* in order to remove unnecessairy space between rows */
  80. #dialog > div > table > tbody {line-height: 14px;}
  81.  
  82.  
  83. #tbl > thead > tr > th {border-bottom: solid 1px};}
  84.  
  85.  
  86. #tbl td:nth-child(1) {text-align: -moz-right;}
  87.  
  88.  
  89. /* in order the 'product/component' to be aligned to the left */
  90. #tbl td:nth-child(2) {text-align: -moz-left;}
  91.  
  92. /* in order the 'Product/Component' cells to appear truncated */
  93. #tbl td:nth-child(2) {white-space: nowrap; text-overflow:ellipsis; overflow: hidden; max-width:280px;} /* initially it was max-width:1px; */
  94.  
  95. /* in order the 'Modified' cells to have fixed width (via being displayed truncated but with max-width:100px) (in order to avoid having to display "Modified__") */
  96. #tbl td:nth-child(3) {white-space: nowrap; text-overflow:ellipsis; overflow: hidden; max-width:700px;} /* initially it was max-width:1px; */
  97.  
  98. /* in order the 'Modified' cells to have fixed width (via being displayed truncated but with max-width:100px) (in order to avoid having to display "Modified__") */
  99. #tbl td:nth-child(4) {white-space: nowrap; text-overflow:ellipsis; overflow: hidden; max-width:100px;} /* initially it was max-width:1px; */
  100.  
  101. /* in order the bug list to have width 1500px */
  102. .ui-dialog {width:1200px !important;}
  103.  
  104. </style>`;
  105. $('head').append(stylesheet2);
  106.  
  107.  
  108.  
  109.  
  110.  
  111.  
  112.  
  113. // the dialog will only be opened after all these promises have finished
  114. var requests = [];
  115.  
  116.  
  117. // theme for the jQuery dialog
  118. if (typeof(GM_getResourceText) !== 'undefined' && typeof(GM_addStyle) !== 'undefined') {
  119.  
  120.  
  121. // https://stackoverflow.com/a/11532646/ , i.e. https://stackoverflow.com/a/11532646/3231411 (By Brock Adams)
  122. // Themes files URLs: https://cdnjs.com/libraries/jqueryui
  123. let iconSet1 = GM_getResourceURL ('IconSet1');
  124. let iconSet2 = GM_getResourceURL ('IconSet2');
  125. let iconSet3 = GM_getResourceURL ('IconSet3');
  126. let iconSet4 = GM_getResourceURL ('IconSet4');
  127. let iconSet5 = GM_getResourceURL ('IconSet5');
  128. let iconSet6 = GM_getResourceURL ('IconSet6');
  129. let iconSet7 = GM_getResourceURL ('IconSet7');
  130. let jqUI_CssSrc = GM_getResourceText ('jqUI_CSS');
  131. // jqUI_CssSrc = jqUI_CssSrc.replace (/url\(images\/ui\-bg_.*00\.png\)/g, '');
  132. jqUI_CssSrc = jqUI_CssSrc.replace (/images\/ui-bg_glass_75_d0e5f5_1x400\.png/g, iconSet1);
  133. jqUI_CssSrc = jqUI_CssSrc.replace (/images\/ui-bg_glass_85_dfeffc_1x400\.png/g, iconSet2);
  134. jqUI_CssSrc = jqUI_CssSrc.replace (/images\/ui-bg_gloss-wave_55_5c9ccc_500x100\.png/g, iconSet3);
  135. jqUI_CssSrc = jqUI_CssSrc.replace (/images\/ui-bg_inset-hard_100_fcfdfd_1x100\.png/g, iconSet4);
  136. jqUI_CssSrc = jqUI_CssSrc.replace (/images\/ui-icons_217bc0_256x240\.png/g, iconSet5);
  137. jqUI_CssSrc = jqUI_CssSrc.replace (/images\/ui-icons_469bdd_256x240\.png/g, iconSet6);
  138. jqUI_CssSrc = jqUI_CssSrc.replace (/images\/ui-icons_6da8d5_256x240\.png/g, iconSet7);
  139.  
  140. GM_addStyle (jqUI_CssSrc);
  141.  
  142.  
  143. } else { // e.g. Greasemonkey: https://github.com/greasemonkey/greasemonkey/issues/2548
  144. // load jquery-ui css dynamically to bypass Content-Security-Policy restrictions
  145. let loadCss = $.get('https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/themes/redmond/jquery-ui.min.css', function(css) {
  146. $('head').append('<style>' + css + '</style>');
  147. });
  148. requests.push(loadCss); // prevent a possible race condition where the dialog is opened before the css is loaded
  149. }
  150.  
  151.  
  152.  
  153.  
  154.  
  155.  
  156. // theme for the jQuery dialog
  157. // $('head').append(
  158. // '<link ' +
  159. // 'href="//ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/themes/redmond/jquery-ui.min.css" ' +
  160. // // 'href="//ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/themes/smoothness/jquery-ui.min.css" ' + // uncomment this line (and comment line 89) in order to change theme
  161. // 'rel="stylesheet" type="text/css">'
  162. // );
  163. // var newCSS = GM_getResourceText ('customCSS');
  164. // GM_addStyle (newCSS);
  165. $.ajax({
  166. url:'http://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/themes/redmond/jquery-ui.min.css',
  167. success:function(data){
  168. $('<style></style>').appendTo('head').html(data);
  169. }
  170. });
  171.  
  172. var regex = /^https:\/\/bugzilla\.mozilla\.org\/show_bug\.cgi\?id=(.*)$/;
  173. var base_url = 'https://bugzilla.mozilla.org/rest/bug?include_fields=id,summary,status,resolution,product,component,op_sys,platform,whiteboard,last_change_time&id=';
  174. var bugIds = [];
  175. var bugsComplete = [];
  176.  
  177. var table = document.getElementsByTagName('table')[0];
  178. var links = table.getElementsByTagName('a');
  179. var len = links.length;
  180. for (let i = 0; i < len; i++) {
  181. let n = links[i].href.match(regex);
  182. if (n !== null && n.length > 0) {
  183. let id = parseInt(n[1]);
  184. if (bugIds.indexOf(id) === -1) {
  185. bugIds.push(id);
  186. }
  187. }
  188. }
  189.  
  190. var numBugs = bugIds.length;
  191. var counter = 0;
  192.  
  193. var rest_url = base_url + bugIds.join();
  194.  
  195.  
  196. String.prototype.escapeHTML = function() {
  197. var tagsToReplace = {
  198. '&': '&amp;',
  199. '<': '&lt;',
  200. '>': '&gt;'
  201. };
  202. return this.replace(/[&<>]/g, function(tag) {
  203. return tagsToReplace[tag] || tag;
  204. });
  205. };
  206.  
  207.  
  208. time('MozillaMercurial-REST');
  209.  
  210.  
  211.  
  212. GM_xmlhttpRequest({
  213. method: 'GET',
  214. url: rest_url,
  215. onload: function(response) {
  216.  
  217. var data = JSON.parse(response.responseText);
  218.  
  219. timeEnd('MozillaMercurial-REST');
  220. $.each(data.bugs, function(index) {
  221. let bug = data.bugs[index];
  222. // process bug (let "shorthands" just to simplify things during refactoring)
  223. let status = bug.status;
  224. if (bug.resolution !== '') {status += ' ' + bug.resolution;}
  225. let product = bug.product;
  226. let component = bug.component;
  227. let platform = bug.platform;
  228. if (platform === 'Unspecified') {
  229. platform = 'Uns';
  230. }
  231. if (bug.op_sys !== '' && bug.op_sys !== 'Unspecified') {
  232. platform += '/' + bug.op_sys;
  233. }
  234. let whiteboard = bug.whiteboard === '' ? '[]' : bug.whiteboard;
  235. // todo: message???
  236.  
  237.  
  238.  
  239.  
  240.  
  241. // 2015-11-09T14:40:41Z
  242. function toRelativeTime(time, zone) {
  243. var format2 = ('YYYY-MM-DD HH:mm:ss Z');
  244. return moment(time, format2).tz(zone).fromNow();
  245. }
  246.  
  247.  
  248. function getLocalTimezone(){
  249. var tz = jstz.determine(); // Determines the time zone of the browser client
  250. return tz.name(); // Returns the name of the time zone eg "Europe/Berlin"
  251. }
  252.  
  253.  
  254.  
  255.  
  256. var changetime;
  257. var localTimezone = getLocalTimezone();
  258.  
  259. if (bug.last_change_time !== '') {
  260. var temp = toRelativeTime(bug.last_change_time, localTimezone);
  261. if (temp.match(/(an?) .*/)) {
  262. changetime = temp.replace(/an?/, 1);
  263. } else {
  264. changetime = temp;
  265. }
  266. // changetime
  267. } else {
  268. changetime = '';
  269. }
  270.  
  271.  
  272.  
  273.  
  274.  
  275.  
  276.  
  277.  
  278. log('----------------------------------------------------------------------------------------------------------------------------------');
  279. log((index + 1) + '/' + numBugs); // Progression counter
  280. log('BugNo: ' + bug.id + '\nTitle: ' + bug.summary + '\nStatus: ' + status + '\nProduct: ' + product + '\nComponent: ' + component + '\nPlatform: ' + platform + '\nWhiteboard: ' + whiteboard);
  281.  
  282. if (isRelevant(bug)) {
  283. // add html code for this bug
  284. bugsComplete.push('<tr><td><a href="'
  285. // + 'https://bugzilla.mozilla.org/show_bug.cgi?id='+ bug.id + '">'
  286. + 'https://bugzilla.mozilla.org/show_bug.cgi?id='+ bug.id + '"' + ' title="' + bug.id + ' - ' + bug.summary + '">#'
  287. + bug.id
  288. + '</a></td>'
  289. + '<td nowrap>(' + product + ': ' + component + ') </td>'
  290. + '<td>'+bug.summary.escapeHTML() + ' [' + platform + ']' + whiteboard.escapeHTML() + '</td>'
  291. + '<td>' + changetime + '</td></tr>'); // previously had a <br> at the end;
  292. }
  293. counter++; // increase counter
  294. // remove processed bug from bugIds
  295. let i = bugIds.indexOf(bug.id);
  296. if (i !== -1) {bugIds[i] = null;}
  297. });
  298. log('==============\nReceived ' + counter + ' of ' + numBugs + ' bugs.');
  299.  
  300.  
  301.  
  302.  
  303. // process remaining bugs one-by-one
  304. time('MozillaMercurial-missing');
  305. $.each(bugIds, function(index) {
  306. let id = bugIds[index];
  307. if (id !== null) {
  308. time('Requesting missing bug ' + id);
  309. let promise = $.getJSON('https://bugzilla.mozilla.org/rest/bug/' + id,
  310. function(json) {
  311. // I've not end up here yet, so cry if we do
  312. console.error('Request for bug ' + id + ' succeeded unexpectedly!');
  313. timeEnd('Requesting missing bug ' + id);
  314. console.error(json);
  315. });
  316. // Actually, we usually get an '401 Authorization Required' error
  317. promise.fail(function(req, status, error) {
  318. timeEnd('Requesting missing bug ' + id);
  319. if (error === 'Authorization Required') {
  320. // log("Bug " + id + " requires authorization!");
  321. log('https://bugzilla.mozilla.org/show_bug.cgi?id=' + id + ' requires authorization!');
  322. let text = ' requires authorization!<br>';
  323.  
  324. bugsComplete.push('<a href="'
  325. + 'https://bugzilla.mozilla.org/show_bug.cgi?id='+ id + '">#'
  326. + id + '</a>' + text);
  327. } else {
  328. console.error('Unexpected error encountered (Bug' + id + '): ' + status + ' ' + error);
  329. }
  330. });
  331. requests.push(promise);
  332. }
  333. });
  334. // wait for all requests to be settled, then join them together
  335. // Source: https://stackoverflow.com/questions/19177087/deferred-how-to-detect-when-every-promise-has-been-executed
  336. $.when.apply($, $.map(requests, function(p) {
  337. return p.then(null, function() {
  338. return $.Deferred().resolveWith(this, arguments);
  339. });
  340. })).always(function() {
  341. timeEnd('MozillaMercurial-missing');
  342. // Variable that will contain all values of the bugsComplete array, and will be displayed in the 'dialog' below
  343. var docu = '';
  344. docu = bugsComplete.join('');
  345. docu = ' <table id="tbl" style="width:100%">' +
  346. '<thead>' +
  347. '<tr><th>BugNo</th>' +
  348. '<th>Product/Component</th>' + // '<th>Product/Component_________</th>' +
  349. '<th>Summary</th>' +
  350. '<th>Modified</th></tr>' + // '<th>Modified__</th></tr>' +
  351. '</thead>' +
  352. '<tbody>' + docu + '</tbody></table>';
  353.  
  354.  
  355.  
  356.  
  357. var div = document.createElement('div');
  358. $('div.page_footer').append(div);
  359. div.id = 'dialog';
  360. docu = '<div id="dialog_content">' + docu + '</div>';
  361. div.innerHTML = docu;
  362. $('#dialog').hide();
  363.  
  364. $(function() {
  365. $('#dialog').dialog({
  366. title: 'List of fixed bugs of Firefox for desktop (' + bugsComplete.length + ')',
  367. width: '1350px'
  368. });
  369. });
  370.  
  371.  
  372.  
  373.  
  374.  
  375.  
  376. // THE CUSTOM PARSER MUST BE PUT BEFORE '$('#tbl').tablesorter ( {'' or else it wont work !!!!
  377. // add parser through the tablesorter addParser method (for the "Last modified" column)
  378. $.tablesorter.addParser({
  379. // set a unique id
  380. id: 'dates',
  381. is: function(s) {
  382. return false; // return false so this parser is not auto detected
  383. },
  384. format: function(s) {
  385. // format your data for normalization
  386. if (s !== ''){
  387. var number1, number2;
  388.  
  389. // format your data for normalization
  390. number1 = Number((/(.{1,2}) .*/).exec(s)[1]);
  391.  
  392.  
  393. if (s.match(/A few seconds ago/)) { number2 = 0;}
  394. else if (s.match(/(.*)seconds?.*/)) { number2 = 1;}
  395. else if (s.match(/(.*)minutes?.*/)) {number2 = 60;}
  396. else if (s.match(/(.*)hours?.*/)) { number2 = 3600;}
  397. else if (s.match(/(.*)days?.*/)) { number2 = 86400;}
  398. else if (s.match(/(.*)months?.*/)) { number2 = 30 * 86400;}
  399. else if (s.match(/(.*)years?.*/)) {number2 = 365 * 30 * 86400;}
  400. return number1 * number2;
  401.  
  402. }
  403. },
  404. // set type, either numeric or text
  405. type: 'numeric'
  406. });
  407.  
  408.  
  409.  
  410. // make table sortable
  411. $('#tbl').tablesorter({
  412. cssAsc: 'up',
  413. cssDesc: 'down',
  414. sortList: [[3, 0],[1, 0],[2, 0]], // in order the table to be sorted by default by column 3 'Modified', then by column 1 'Product/Component' and then by column 2 'Summary'
  415. headers: {3: {sorter: 'dates'}},
  416. initialized: function() {
  417. var mytable = document.getElementById('tbl');
  418. for (let i = 2, j = mytable.rows.length + 1; i < j; i++) {
  419. if (mytable.rows[i].cells[3].innerHTML !== mytable.rows[i - 1].cells[3].innerHTML) {
  420. for (let k = 0; k < 4; k++) {
  421. mytable.rows[i - 1].cells[k].style.borderBottom = '1px black dotted';
  422. }
  423. }
  424. }
  425. }
  426. });
  427.  
  428.  
  429.  
  430.  
  431.  
  432.  
  433. log('ALL IS DONE');
  434. timeEnd('MozillaMercurial');
  435.  
  436.  
  437.  
  438.  
  439.  
  440. });
  441.  
  442. }
  443. });
  444.  
  445.  
  446. var flag = 1;
  447.  
  448. // bind keypress of ` so that when pressed, the separators between groups of the same timestamps to be removed, in order to sort manually
  449. var listener = new window.keypress.Listener();
  450. listener.simple_combo('`', function() {
  451. // console.log('You pressed `');
  452. if (flag === 1) {
  453. flag = 0;
  454. // remove seperators
  455. var mytable = document.getElementById('tbl');
  456. for (let i = 2, j = mytable.rows.length + 1; i < j; i++) {
  457. for (let k = 0; k < 4; k++) {
  458. mytable.rows[i - 1].cells[k].style.borderBottom = 'none';
  459. }
  460. }
  461. var sorting = [[1, 0], [2, 0]]; // sort by column 1 'Product/Component' and then by column 2 'Summary'
  462. $('#tbl').trigger('sorton', [sorting]);
  463. } else {
  464. if (flag === 0) {
  465. flag = 1;
  466. // console.log('You pressed ~');
  467. sorting = [[3, 0], [1, 0], [2, 0]]; // sort by column 3 'Modified Date, then by '1 'Product/Component' and then by column 2 'Summary'
  468. $('#tbl').trigger('sorton', [sorting]);
  469. mytable = document.getElementById('tbl');
  470. for (let i = 2, j = mytable.rows.length + 1; i < j; i++) {
  471. if (mytable.rows[i].cells[3].innerHTML !== mytable.rows[i - 1].cells[3].innerHTML) {
  472. for (let k = 0; k < 4; k++) {
  473. mytable.rows[i - 1].cells[k].style.borderBottom = '1px black dotted';
  474. }
  475. }
  476. }
  477. }
  478. }
  479. });
  480.  
  481.  
  482.  
  483.  
  484.  
  485.  
  486. function isRelevant(bug) {
  487. if (!bug.id) {return false;}
  488. if (bug.status && bug.status !== 'RESOLVED' && bug.status !== 'VERIFIED') {
  489. log(' IRRELEVANT because of it\'s Status --> ' + bug.status);
  490. return false;
  491. }
  492. if (bug.component && bug.product && bug.component === 'Build Config' && (bug.product === 'Toolkit' || bug.product === 'Firefox')) {
  493. log(' IRRELEVANT because of it\'s Product --> ' + bug.product + 'having component --> ' + bug.component);
  494. return false;
  495. }
  496. if (bug.product &&
  497. bug.product !== 'Add-on SDK' &&
  498. bug.product !== 'Cloud Services' &&
  499. bug.product !== 'Core' &&
  500. bug.product !== 'Firefox' &&
  501. bug.product !== 'Hello (Loop)' &&
  502. bug.product !== 'Toolkit') {
  503. log(' IRRELEVANT because of it\'s Product --> ' + bug.product);
  504. return false;
  505. }
  506. if (bug.component &&
  507. bug.component === 'AutoConfig' ||
  508. bug.component === 'Build Config' ||
  509. bug.component === 'DMD' ||
  510. bug.component === 'Embedding: GRE Core' ||
  511. bug.component === 'Embedding: Mac' ||
  512. bug.component === 'Embedding: MFC Embed' ||
  513. bug.component === 'Embedding: Packaging' ||
  514. bug.component === 'Hardware Abstraction Layer' ||
  515. bug.component === 'mach' ||
  516. bug.component === 'Nanojit' ||
  517. bug.component === 'QuickLaunch' ||
  518. bug.component === 'Widget: Gonk') {
  519. log(' IRRELEVANT because of it\'s Component --> ' + bug.component);
  520. return false;
  521. }
  522.  
  523. log(' OK ' + 'https://bugzilla.mozilla.org/show_bug.cgi?id=' + bug.id);
  524. return true;
  525. }
  526.  
  527.  
  528.  
  529.  
  530. function log(str) {
  531. if (!silent) {
  532. console.log(str);
  533. }
  534. }
  535.  
  536. function time(str) {
  537. if (debug) {
  538. console.time(str);
  539. }
  540. }
  541.  
  542. function timeEnd(str) {
  543. if (debug) {
  544. console.timeEnd(str);
  545. }
  546. }
  547.  
  548.  
  549. $('#dialog').dialog({
  550. modal: false,
  551. title: 'Draggable, sizeable dialog',
  552. position: {
  553. my: 'top',
  554. at: 'top',
  555. of: document,
  556. collision: 'none'
  557. },
  558. // width: 1500, // not working
  559. zIndex: 3666
  560. }).dialog('widget').draggable('option', 'containment', 'none');
  561.  
  562. //-- Fix crazy bug in FF! ...
  563. $('#dialog').parent().css({
  564. position: 'fixed',
  565. top: 0,
  566. left: '4em',
  567. width: '75ex'
  568. });