/* 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(''); } function saveBugComment(text) { if (text.length < 1) return clearSavedBugComment(); if (text.length > 65535) return; let key = `bug-modal-saved-comment-${BUGZILLA.bug_id}`; let value = { text: text, savedAt: Date.now() }; localStorage.setItem(key, JSON.stringify(value)); } function clearSavedBugComment() { let key = `bug-modal-saved-comment-${BUGZILLA.bug_id}`; localStorage.removeItem(key); } function restoreSavedBugComment() { expireSavedComments(); let key = `bug-modal-saved-comment-${BUGZILLA.bug_id}`; let value = JSON.parse(localStorage.getItem(key)); if (value){ let commentBox = document.querySelector("textarea#comment"); commentBox.value = value['text']; if (BUGZILLA.user.settings.autosize_comments) { autosize.update(commentBox); } } } function expireSavedComments() { const AGE_THRESHOLD = 7 * 24 * 60 * 60 * 1000; // 7 days in milliseconds. let expiredKeys = []; for (let i = 0; i < localStorage.length; i++) { let key = localStorage.key(i); if (key.match(/^bug-modal-saved-comment-/)) { let value = JSON.parse(localStorage.getItem(key)); let savedAt = value['savedAt'] || 0; let age = Date.now() - savedAt; if (age < 0 || age > AGE_THRESHOLD) { expiredKeys.push(key); } } } expiredKeys.forEach((key) => { localStorage.removeItem(key); }); } // 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($('#main-inner')); }); // 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 if ($('#copy-summary').length) { var hasExecCopy = false; try { hasExecCopy = document.queryCommandSupported("copy"); } catch(ex) { // ignore } if (hasExecCopy) { const url = BUGZILLA.bug_url; const text = `Bug ${BUGZILLA.bug_id} - ${BUGZILLA.bug_summary}`; const html = `${text}`; document.addEventListener('copy', event => { if (event.target.nodeType === 1 && event.target.matches('#clip')) { event.clipboardData.setData('text/uri-list', url); event.clipboardData.setData('text/plain', text); event.clipboardData.setData('text/html', html); event.preventDefault(); } }); $('#copy-summary') .click(function() { // execCommand("copy") only works on selected text $('#clip-container').show(); $('#clip').val(text).select(); $('#floating-message-text') .text(document.execCommand("copy") ? 'Bug summary copied!' : 'Couldn’t copy bug summary'); $('#floating-message').fadeIn(250).delay(2500).fadeOut(); $('#clip-container').hide(); }); } else { $('#copy-summary').hide(); } } // 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); }); // 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(); window.open(`show_activity.cgi?id=${BUGZILLA.bug_id}`, '_blank'); }); // 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($('