From 9243cbb3b6f014563d15c8b7ababdd55a722627a Mon Sep 17 00:00:00 2001 From: Sebastin Santy Date: Sat, 10 Jun 2017 08:48:53 +0530 Subject: Bug 1365342 - Extract the preview comment functionality and make it a reusable template * Putting variables.none at the top * Put the message at the top * Remove header_done * Add an #xhr-error to show bugzilla_ajax() errors --- extensions/BugModal/web/common_bug_modal.js | 1504 +++++++++++++++++++++++++++ 1 file changed, 1504 insertions(+) create mode 100644 extensions/BugModal/web/common_bug_modal.js (limited to 'extensions/BugModal/web/common_bug_modal.js') diff --git a/extensions/BugModal/web/common_bug_modal.js b/extensions/BugModal/web/common_bug_modal.js new file mode 100644 index 000000000..3a5a149fb --- /dev/null +++ b/extensions/BugModal/web/common_bug_modal.js @@ -0,0 +1,1504 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This Source Code Form is "Incompatible With Secondary Licenses", as + * defined by the Mozilla Public License, v. 2.0. */ + +// expand/collapse module +function slide_module(module, action, fast) { + if (!module.attr('id')) + return; + var latch = module.find('.module-latch'); + var spinner = module.find('.module-spinner'); + var content = $(module.children('.module-content')[0]); + var duration = fast ? 0 : 200; + + function slide_done() { + var is_visible = content.is(':visible'); + spinner.attr({ + 'aria-expanded': is_visible, + 'aria-label': is_visible ? latch.data('label-expanded') : latch.data('label-collapsed'), + }); + if (BUGZILLA.user.settings.remember_collapsed) + localStorage.setItem(module.attr('id') + '.visibility', is_visible ? 'show' : 'hide'); + } + + if (action == 'show') { + content.slideDown(duration, 'swing', slide_done); + } + else if (action == 'hide') { + content.slideUp(duration, 'swing', slide_done); + } + else { + content.slideToggle(duration, 'swing', slide_done); + } +} + +function init_module_visibility() { + if (!BUGZILLA.user.settings.remember_collapsed) + return; + $('.module').each(function() { + var that = $(this); + var id = that.attr('id'); + if (!id) return; + if (that.data('non-stick')) return; + var stored = localStorage.getItem(id + '.visibility'); + if (stored) { + slide_module(that, stored, true); + } + }); +} + +$(function() { + 'use strict'; + + // update relative dates + var relative_timer_duration = 60000; + var relative_timer_id = window.setInterval(relativeTimer, relative_timer_duration); + $(document).on('show.visibility', function() { + relative_timer_id = window.setInterval(relativeTimer, relative_timer_duration); + }); + $(document).on('hide.visibility', function() { + window.clearInterval(relative_timer_id); + }); + + function relativeTimer() { + var now = Math.floor(new Date().getTime() / 1000); + $('.rel-time').each(function() { + $(this).text(timeAgo(now - $(this).data('time'))); + }); + $('.rel-time-title').each(function() { + $(this).attr('title', timeAgo(now - $(this).data('time'))); + }); + } + + // all keywords for autocompletion (lazy-loaded on edit) + var keywords = []; + + // products with descriptions (also lazy-loaded) + var products = []; + + // restore edit mode after navigating back + function restoreEditMode() { + if (!$('#editing').val()) + return; + $('.module') + .each(function() { + slide_module($(this), 'hide', true); + }); + $($('#editing').val().split(' ')) + .each(function() { + slide_module($('#' + this), 'show', true); + }); + $('#mode-btn').click(); + $('.save-btn').prop('disabled', false); + $('#editing').val(''); + } + + // expand/colapse module + $('.module-latch') + .click(function(event) { + event.preventDefault(); + slide_module($(this).parents('.module')); + }) + .keydown(function(event) { + // expand/colapse module with the enter or space key + if (event.keyCode === 13 || event.keyCode === 32) { + event.preventDefault(); + slide_module($(this).parents('.module')); + } + }); + + // toggle obsolete attachments + $('#attachments-obsolete-btn') + .click(function(event) { + event.preventDefault(); + $(event.target).text(($('#attachments tr:hidden').length ? 'Hide' : 'Show') + ' Obsolete Attachments'); + $('#attachments tr.attach-obsolete').toggle(); + }); + + // url --> unsafe warning + $('.bug-url') + .click(function(event) { + var that = $(this); + event.stopPropagation(); + if (!that.data('safe')) { + event.preventDefault(); + if (confirm('This is considered an unsafe URL and could possibly be harmful. ' + + 'The full URL is:\n\n' + that.attr('href') + '\n\nContinue?')) + { + try { + window.open(that.attr('href')); + } catch(ex) { + alert('Malformed URL'); + } + } + } + }); + + // top btn + $('#top-btn') + .click(function(event) { + event.preventDefault(); + $.scrollTo($('body')); + }); + + // bottom btn + $('#bottom-btn') + .click(function(event) { + event.preventDefault(); + $.scrollTo($('#bottom-actions')); + }); + + // hide floating message when clicked + $('#floating-message') + .click(function(event) { + event.preventDefault(); + $(this).hide(); + }); + + // use non-native tooltips for relative/absolute times and bug summaries + $('.rel-time, .rel-time-title, .abs-time-title, .bz_bug_link, .tt').tooltip({ + position: { my: "left top+8", at: "left bottom", collision: "flipfit" }, + show: { effect: 'none' }, + hide: { effect: 'none' } + }); + + // tooltips create a new ui-helper-hidden-accessible div each time a + // tooltip is shown. this is never removed leading to memory leak and + // bloated dom. http://bugs.jqueryui.com/ticket/10689 + $('.ui-helper-hidden-accessible').remove(); + + // product/component info + $('.spin-toggle, #product-latch, #component-latch') + .click(function(event) { + spin_toggle(event); + }).keydown(function(event) { + // allow space or enter to toggle visibility + if (event.keyCode == 13 || event.keyCode == 32) { + spin_toggle(event); + } + }); + + function spin_toggle(event) { + event.preventDefault(); + var type = $(event.target).data('for'); + var latch = $('#' + type + '-latch'); + var name = $('#' + type + '-name'); + var info = $('#' + type + '-info'); + var label = latch.attr('aria-label'); + + if (latch.data('expanded')) { + label = label.replace(/^hide/, 'show'); + latch.data('expanded', false).html('▸'); + latch.attr('aria-expanded', false); + info.hide(); + } + else { + label = label.replace(/^show/, 'hide'); + latch.data('expanded', true).html('▾'); + latch.attr('aria-expanded', true); + info.show(); + } + latch.attr('aria-label', label); + name.attr('title', label); + } + + // cc list + + function ccListLoading() { + $('#cc-list').html( + ' Loading...' + ); + } + + function ccListUpdate() { + bugzilla_ajax( + { + url: 'rest/bug_modal/cc/' + BUGZILLA.bug_id + }, + function(data) { + $('#cc-list').html(data.html); + $('#cc-latch').data('fetched', true); + $('#cc-list .cc-user').hover( + function() { + $('#ccr-' + $(this).data('n')).css('visibility', 'visible'); + }, + function() { + $('#ccr-' + $(this).data('n')).css('visibility', 'hidden'); + } + ); + $('#cc-list .cc-remove') + .click(function(event) { + event.preventDefault(); + $('#top-save-btn').show(); + var n = $(this).data('n'); + var ccu = $('#ccu-' + n); + if (ccu.hasClass('cc-removed')) { + ccu.removeClass('cc-removed'); + $('#cc-' + n).remove(); + } + else { + $('#removecc').val('on'); + ccu.addClass('cc-removed'); + $('').attr({ + type: 'hidden', + id: 'cc-' + n, + value: $('#ccr-' + n).data('login'), + name: 'cc' + }).appendTo('#changeform'); + } + }); + } + ); + } + + if (BUGZILLA.user.id) { + $('#cc-summary').addClass('cc-loadable'); + $('#cc-latch, #cc-summary') + .click(function(event) { + cc_toggle(event); + }).keydown(function(event) { + // allow space or enter to toggle visibility + if (event.keyCode == 13 || event.keyCode == 32) { + cc_toggle(event); + } + }); + } + + function cc_toggle(event) { + event.preventDefault(); + var latch = $('#cc-latch'); + var label = latch.attr('aria-label'); + if (latch.data('expanded')) { + label = label.replace(/^hide/, 'show'); + latch.data('expanded', false).html('▸'); + $('#cc-list').hide(); + } + else { + latch.data('expanded', true).html('▾'); + label = label.replace(/^show/, 'hide'); + $('#cc-list').show(); + if (!latch.data('fetched')) { + ccListLoading(); + ccListUpdate(); + } + } + latch.attr('aria-label', label); + $('#cc-summary').attr('aria-label', label); + } + + // copy summary to clipboard + + function clipboardSummary() { + return 'Bug ' + BUGZILLA.bug_id + ' - ' + $('#field-value-short_desc').text(); + } + + if ($('#copy-summary').length) { + var hasExecCopy = false; + try { + hasExecCopy = document.queryCommandSupported("copy"); + } catch(ex) { + // ignore + } + + if (hasExecCopy) { + $('#copy-summary') + .click(function() { + // execCommand("copy") only works on selected text + $('#clip-container').show(); + $('#clip').val(clipboardSummary()).select(); + document.execCommand("copy"); + $('#clip-container').hide(); + }); + } + else { + // we don't know if flash is enabled without waiting for load to timeout + // remember the flash enabled state between pages + var hasFlash = true; + if (localStorage.getItem('hasFlash') === null) { + $('#copy-summary').hide(); + } + else { + hasFlash = localStorage.getItem('hasFlash'); + } + if (hasFlash) { + var s = document.createElement("script"); + s.onload = function () { + ZeroClipboard.config({ flashLoadTimeout: 5000 }); + var zero = new ZeroClipboard($('#copy-summary')); + zero.on({ + 'ready': function(event) { + $('#copy-summary').show(); + localStorage.setItem('hasFlash', true); + }, + 'error': function(event) { + console.log(event.message); + zero.destroy(); + $('#global-zeroclipboard-html-bridge').remove(); + $('#copy-summary').hide(); + localStorage.removeItem('hasFlash'); + }, + 'copy': function(event) { + var clipboard = event.clipboardData; + clipboard.setData('text/plain', clipboardSummary()); + } + }); + }; + s.src = "extensions/BugModal/web/ZeroClipboard/ZeroClipboard.min.js"; + document.getElementsByTagName('head')[0].appendChild(s); + } + } + } + + // lightboxes + $('.lightbox, .comment-text .lightbox + span:first-of-type a:first-of-type') + .click(function(event) { + if (event.metaKey || event.ctrlKey || event.altKey || event.shiftKey) + return; + event.preventDefault(); + lb_show(this); + }); + + // when copying the bug id and summary, reformat to remove \n and alias + $(document).on( + 'copy', function(event) { + var selection = document.getSelection().toString().trim(); + var match = selection.match(/^(Bug \d+)\s*\n(.+)$/) || + selection.match(/^(Bug \d+)\s+\([^\)]+\)\s*\n(.+)$/); + if (match) { + var content = match[1] + ' - ' + match[2].trim(); + if (event.originalEvent.clipboardData) { + event.originalEvent.clipboardData.setData('text/plain', content); + } + else if (window.clipboardData) { + window.clipboardData.setData('Text', content); + } + else { + return; + } + event.preventDefault(); + } + }); + + // action button actions + + // reset + $('#action-reset') + .click(function(event) { + event.preventDefault(); + var visible = $(this).data('modules'); + $('.module-content').each(function() { + var content = $(this); + var moduleID = content.parent('.module').attr('id'); + var isDefault = $.inArray(moduleID, visible) !== -1; + if (content.is(':visible') && !isDefault) { + slide_module($('#' + moduleID), 'hide'); + } + else if (content.is(':hidden') && isDefault) { + slide_module($('#' + moduleID), 'show'); + } + }); + }) + .data('modules', $('.module-content:visible').map(function() { + return $(this).parent('.module').attr('id'); + })); + + // expand all modules + $('#action-expand-all') + .click(function(event) { + event.preventDefault(); + $('.module-content:hidden').each(function() { + slide_module($(this).parent('.module')); + }); + }); + + // collapse all modules + $('#action-collapse-all') + .click(function(event) { + event.preventDefault(); + $('.module-content:visible').each(function() { + slide_module($(this).parent('.module')); + }); + }); + + // add comment menuitem, scroll the textarea into view + $('#action-add-comment, #add-comment-btn') + .click(function(event) { + event.preventDefault(); + // focus first to grow the textarea, so we scroll to the correct location + $('#comment').focus(); + $.scrollTo($('#bottom-save-btn')); + }); + + // last comment menuitem + $('#action-last-comment') + .click(function(event) { + event.preventDefault(); + var id = $('.comment:last')[0].parentNode.id; + $.scrollTo(id); + }); + + // show bug history + $('#action-history') + .click(function(event) { + event.preventDefault(); + document.location.href = 'show_activity.cgi?id=' + BUGZILLA.bug_id; + }); + + // use scrollTo for in-page activity links + $('.activity-ref') + .click(function(event) { + event.preventDefault(); + $.scrollTo($(this).attr('href').substr(1)); + }); + + // Update readable bug status + var rbs = $("#readable-bug-status"); + var rbs_text = bugzillaReadableStatus.readable(rbs.data('readable-bug-status')); + rbs.text(rbs_text); + + if (BUGZILLA.user.id === 0) return; + + // + // anything after this point is only executed for logged in users + // + + // dirty field tracking + $('#changeform select').each(function() { + var that = $(this); + var dirty = $('#' + that.attr('id') + '-dirty'); + if (!dirty) return; + var isMultiple = that.attr('multiple'); + + // store the option that had the selected attribute when we + // initially loaded + var value = that.find('option[selected]').map(function() { return this.value; }).toArray(); + if (value.length === 0 && !that.attr('multiple')) + value = that.find('option:first').map(function() { return this.value; }).toArray(); + that.data('preselected', value); + + // if the user hasn't touched a field, override the browser's choice + // with bugzilla's + if (!dirty.val()) + that.val(value); + }); + + // edit/save mode button + $('#mode-btn') + .click(function(event) { + event.preventDefault(); + + // hide buttons, old error messages + $('#mode-btn-readonly').hide(); + + // toggle visibility + $('.edit-hide').hide(); + $('.edit-show').show(); + + // expand specific modules during the initial edit + if (!$('#editing').val()) + slide_module($('#module-details'), 'show'); + + // if there's no current user-story, it's a better experience if it's editable by default + if ($('#cf_user_story').val() === '') { + $('#user-story-edit-btn').click(); + } + + // "loading.." ui + $('#mode-btn-loading').show(); + $('#cancel-btn').prop('disabled', true); + $('#mode-btn').prop('disabled', true); + + // load the missing select data + bugzilla_ajax( + { + url: 'rest/bug_modal/edit/' + BUGZILLA.bug_id + }, + function(data) { + $('#mode-btn').hide(); + + // populate select menus + $.each(data.options, function(key, value) { + var el = $('#' + key); + if (!el) return; + var selected = el.val(); + el.empty(); + $(value).each(function(i, v) { + el.append($('