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 --- .../en/default/bug_modal/common_header.html.tmpl | 84 ++ .../default/bug_modal/common_new_comment.html.tmpl | 55 + extensions/BugModal/web/common_bug_modal.css | 983 +++++++++++++ extensions/BugModal/web/common_bug_modal.js | 1504 ++++++++++++++++++++ template/en/default/bug/new_bug.html.tmpl | 29 +- 5 files changed, 2646 insertions(+), 9 deletions(-) create mode 100644 extensions/BugModal/template/en/default/bug_modal/common_header.html.tmpl create mode 100644 extensions/BugModal/template/en/default/bug_modal/common_new_comment.html.tmpl create mode 100644 extensions/BugModal/web/common_bug_modal.css create mode 100644 extensions/BugModal/web/common_bug_modal.js diff --git a/extensions/BugModal/template/en/default/bug_modal/common_header.html.tmpl b/extensions/BugModal/template/en/default/bug_modal/common_header.html.tmpl new file mode 100644 index 000000000..3d71f1625 --- /dev/null +++ b/extensions/BugModal/template/en/default/bug_modal/common_header.html.tmpl @@ -0,0 +1,84 @@ +[%# 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. + #%] + +[% + PROCESS global/variables.none.tmpl; + + title = title + + generate_api_token = 1; + + # these aren't always defined + UNLESS bodyclasses.defined; + bodyclasses = []; + END; + UNLESS javascript_urls.defined; + javascript_urls = []; + END; + UNLESS style_urls.defined; + style_urls = []; + END; + UNLESS jquery.defined; + jquery = []; + END; + + # right now we need yui for tracking flags and needinfo extensions + no_yui = 0; + + # add body classes for sec-groups, etc + bodyclasses.push("bug_modal"); + + # assets + javascript_urls.push( + "extensions/ProdCompSearch/web/js/prod_comp_search.js", + "extensions/BugModal/web/common_bug_modal.js", + "extensions/BugModal/web/comments.js", + "extensions/BugModal/web/dropdown.js", + "js/bugzilla-readable-status-min.js", + "js/field.js", + "js/comments.js", + "js/util.js" + ); + jquery.push( + "datetimepicker", + "contextMenu", + "visibility" + ); + style_urls.push( + "extensions/BugModal/web/common_bug_modal.css", + "extensions/BugModal/web/dropdown.css", + "skins/custom/bug_groups.css", + "js/jquery/plugins/datetimepicker/datetimepicker.css", + "js/jquery/plugins/contextMenu/contextMenu.css" + ); +%] + +[% javascript = BLOCK %] + + [%# expose useful data to js %] + + BUGZILLA.user = { + id: [% user.id FILTER none %], + login: '[% user.login FILTER js %]', + is_insider: [% user.is_insider ? "true" : "false" %], + is_timetracker: [% user.is_timetracker ? "true" : "false" %], + can_tag: [% user.can_tag_comments ? "true" : "false" %], + settings: { + quote_replies: '[% user.settings.quote_replies.value FILTER js %]', + zoom_textareas: [% user.settings.zoom_textareas.value == "on" ? "true" : "false" %], + remember_collapsed: [% user.settings.ui_remember_collapsed.value == "on" ? "true" : "false" %] + } + }; + + [% IF user.is_timetracker %] + BUGZILLA.remaining_time = [% bug.remaining_time FILTER js %]; // holds the original value + [% END %] + BUGZILLA.bug_secure = [% bug.groups_in.size ? 'true' : 'false' %]; +[% END %] + +[% Hook.process("end") %] \ No newline at end of file diff --git a/extensions/BugModal/template/en/default/bug_modal/common_new_comment.html.tmpl b/extensions/BugModal/template/en/default/bug_modal/common_new_comment.html.tmpl new file mode 100644 index 000000000..2ba3948d1 --- /dev/null +++ b/extensions/BugModal/template/en/default/bug_modal/common_new_comment.html.tmpl @@ -0,0 +1,55 @@ +[%# 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. + #%] + +[%# + # comment: comment object + # bug: bug object + #%] + +
+ + [% IF bug && !bug.check_can_change_field('longdesc', 0, 1) %] +
+ You are not allowed to make an additional comment on this [% terms.bug %]. +
+ [% RETURN %] + [% END %] + + [% IF bug && user.is_insider %] +
+ + +
+ [% END %] + + + +
+ +
+ + + +
diff --git a/extensions/BugModal/web/common_bug_modal.css b/extensions/BugModal/web/common_bug_modal.css new file mode 100644 index 000000000..4af850b99 --- /dev/null +++ b/extensions/BugModal/web/common_bug_modal.css @@ -0,0 +1,983 @@ +/* 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. */ + +/* generic */ + +.container { + display: table-cell; + width: 100%; +} + +.layout-table { + border-spacing: 0; +} + +.layout-table td { + padding: 0; +} + +.inline { + display: table-cell !important; + width: auto !important; +} + +.gravatar { + vertical-align: middle; + margin-right: 5px; +} + +.flag .vcard { + display: inline; +} + +.group-padlock { + vertical-align: middle; + margin-right: 5px; +} + +button.major { + font-size: 11px; + padding: 4px 8px; +} + +button.minor { + background-color: #eee; + background-image: linear-gradient(#fcfcfc, #eee); + color: #000; + font-size: 11px; + font-weight: 500; + padding: 4px 8px; + margin-bottom: 1px; + text-shadow: none; + -web-kit-box-shadow: 0 1px 0 0 rgba(0,0,0,0.1), inset 0 -1px 0 0 rgba(0,0,0,0.1); + -moz-box-shadow: 0 1px 0 0 rgba(0,0,0,0.1), inset 0 -1px 0 0 rgba(0,0,0,0.1); + box-shadow: 0 1px 0 0 rgba(0,0,0,0.1), inset 0 -1px 0 0 rgba(0,0,0,0.1), inset 0 0 1px 0 rgba(0,0,0,0.1); +} + +button.minor:hover { + -webkit-box-shadow: 0 1px 0 0 rgba(0,0,0,0.2), inset 0 -1px 0 0 rgba(0,0,0,0.3), inset 0 12px 24px 2px #ddd; + -moz-box-shadow: 0 1px 0 0 rgba(0,0,0,0.2), inset 0 -1px 0 0 rgba(0,0,0,0.3), inset 0 12px 24px 2px #ddd; + box-shadow: 0 1px 0 0 rgba(0,0,0,0.1), inset 0 -1px 0 0 rgba(0,0,0,0.1), inset 0 12px 24px 2px #ddd; +} + +select[multiple], .text_input, .yui-ac-input, input { + font-size: 12px !important; +} + +.spin-toggle { + cursor: pointer; + display: inline; +} + +.spin-toggle:hover { + text-decoration: underline; +} + +.spin-latch { + color: #999; + padding-right: 5px; +} + +.attention { + -webkit-box-shadow: 0 0 2px 2px #f88; + -moz-box-shadow: 0 0 2px 2px #f88; + box-shadow: 0 0 2px 2px #f88; +} + +a.activity-ref { + color: #000; +} + +/* modules */ + +.module { + color: #000; + border-radius: 2px; + margin-top: 5px; + font-size: 13px; +} + +.module.module-collapsed .module-content { + border: 1px solid red; +} + +.module-header { + background: #eee; + -webkit-box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.1); + -moz-box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.1); + box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.1); +} + +.module-header:hover { + outline: 1px solid #ccc; +} + +.module-latch { + padding: 2px 10px; + cursor: pointer; +} + +.module-spinner { + color: #999; + display: table-cell; + width: 10px; +} + +.module-spinner::before { + content: "\25BE"; +} + +.module-spinner[aria-expanded="false"]::before { + content: "\25B8"; +} + +.module-title { + font-weight: bold; +} + +.module-title, .module-subtitle { + font-size: 13px; + display: table-cell; + padding-left: 5px; +} + +.module-subtitle { + font-weight: normal; + padding-right: 5px; + color: #666; +} + +.module .fields-lhs { + min-width: 450px; + display: table-cell; + vertical-align: top; +} + +.module .fields-rhs { + min-width: 450px; + display: table-cell; + vertical-align: top; + width: 100%; +} + +.module-content { + padding: 2px 5px; + background: #fff; + -webkit-box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.1); + -moz-box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.1); + box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.1); +} + +.module .field { + margin-top: 4px; + vertical-align: top; + display: table; + width: 100%; +} + +.module .field.right { +} + +.module .field .name { + display: table-cell; + width: 100px; + min-width: 100px; + text-align: right; + vertical-align: top; + padding-right: 10px; + color: #666; +} + +.module .field .name .help { + color: #666; + cursor: help; +} + +.module .field.inline .name { + min-width: 0px; + width: auto; + padding-left: 10px; +} + +.module .indent { + padding-left: 10px; +} + +.module .field .value { + display: table-cell; +} + +.module .field .value.wide { + display: block; +} + +.module .field .value.edit { + width: 100%; +} + +.module .field .value input { + width: 100%; +} + +.module .field .value input[type="checkbox"] { + width: auto; +} + +.module .field .value.short input { + width: 170px; +} + +.field-button { + float: right; + margin-left: 8px; +} + +.field-edit-container { + overflow-y: hidden; +} + +/* field types */ + +input[type="number"] { + text-align: right; + width: 5em !important; +} + +.cf_date-img, .cf_datetime-img { + vertical-align: middle; +} + +/* specific fields */ + +#field-value-bug_id { + font-weight: bold; +} + +#field-value-short_desc { + margin: 0; + font-weight: bold; + font-size: 120%; +} + +#field-status_summary { + border-top: 1px dotted silver; + padding-top: 4px; +} + +#status-assignee, #status-assignee .vcard, #status-needinfo, #status-needinfo .vcard { + display: inline; +} + +#status-assignee, #status-needinfo { + margin-left: 8px; +} + +#duplicate-container, #duplicate-actions, #assigned-container, +#bottom-duplicate-container, #bottom-duplicate-actions { + display: table-cell; + vertical-align: top; + padding-left: 8px; +} + +#dup_id { + margin-left: 4px; +} + +#resolve-as, #bottom-status { + display: inline; +} + +#after-comment-commit-button { + margin-left: -8px; + margin-bottom: 4px; +} + +#needinfo_from_autocomplete { + width: auto; +} + +#needinfo_role_identity { + margin-left: 5px; +} + +#user-story { + margin: 0; + white-space: pre-wrap; + min-height: 2em; +} + +#user-story-actions { + float: right; +} + +#new-comment-notice { + padding: 20px 8px; + margin-bottom: 50px; +} + +#product-info, #component-info { + color: #484; + white-space: normal; +} + +#product-latch, #component-latch { + padding-right: 0; + cursor: pointer; +} + +#cc-latch { + color: #999; +} + +#cc-latch { + cursor: pointer; +} + +#cc-list { + max-height: 150px; + overflow-y: auto; + clear: both; + white-space: nowrap; +} + +#cc-list .vcard { + display: inline-block; +} + +#cc-list button { + padding: 2px 4px; +} + +.cc-remove { + font-size: 120%; +} + +.cc-removed { + text-decoration: line-through; +} + +#add-cc-btn { + margin-left: 8px; +} + +#add-cc { + width: 100%; +} + +.cc-loadable { + cursor: pointer; +} + +.cc-loadable:hover { + text-decoration: underline; +} + +/* actions */ + +#top-actions { + margin: 4px 0; +} + +#top-actions .save-btn { + float: right; +} + +#bottom-actions { + margin-top: 8px; + margin-bottom: 50px; + max-width: 1024px; +} + +#bottom-right-actions { + float: right; +} + +.edit-textarea-set-btn { + float: right; +} + +/* attachments */ + +#module-attachments .module-content { + padding: 0; +} + +#attachments { + width: 100%; +} + +#attachments td { + padding: 4px 8px; + vertical-align: top; + font-size: 13px; + border-bottom: 1px dotted silver; +} + +#attachments .attach-desc-td { + width: 100%; +} + +#attachments .attach-desc { + font-weight: bold; +} + +#attachments .attach-info { + font-size: 11px; +} + +#attachments .attach-time { + font-size: 11px; +} + +#attachments .attach-actions { + white-space: nowrap; +} + +#attachments .attach-flag { + white-space: nowrap; +} + +#attachments .flag-name-status { + font-weight: bold; +} + +#attachments .attach-obsolete .attach-desc { + text-decoration: line-through; +} + +#attachments .attach-patch .attach-desc-td { + background: #ffc; + background-image: linear-gradient(to right, #ffc, #fff); +} + +#attachments .bz_private { + background: #fff; +} + +#attachments .bz_private .attach-desc-td { + border-left: 4px solid darkred; +} + +#attachments .vcard { + display: inline; +} + +#attachments-actions button { + margin: 2px; +} + +#attachments .attach-flag .vcard { + white-space: nowrap; +} + +/* flags */ + +.flags td { + font-size: 13px !important; +} + +.flag-name { + text-align: right; +} + +td.flag-name, td.flag-requestee { + padding-left: 5px; +} + +td.flag-value select { + margin-left: 5px; +} + +td.flag-requestee { + width: 100%; +} + +.flags .vcard { + white-space: nowrap; +} + +.tracking-flags td, .tracking-flags th { + padding: 0 5px; +} + +.tracking-flags th { + font-weight: normal; + text-align: left; + color: #666; +} + +.tracking-flag-name, .tracking-flag-tracking { + text-align: right; + white-space: nowrap; +} + +/* groups */ + +.group-disabled { + color: #888; +} + +/* comments and activity */ + +#comment-actions { + margin-top: 4px; + text-align: right; +} + +.change-set { + clear: both; + -webkit-box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.1); + -moz-box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.1); + box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.1); + border-bottom: 1px solid rgba(0, 0, 0, 0.2); + margin-top: 8px; + border: 1px solid #ddd; +} + +.change-set:target { + outline: 2px solid #0095dd; +} + +.change-set .comment, .change-set .change { + padding-bottom: 1px; +} + +.change-head { + width: 100%; + background: #eee; +} + +.change-gravatar { + padding-left: 8px !important; +} + +.change-gravatar .vcard { + width: 36px; + text-align: center; +} + +.change-author { + width: 100%; + vertical-align: top; + padding: 5px 0 !important; +} + +.change-author .vcard { + display: inline; + font-weight: bold; +} + +.change-author .user-role { + margin-left: 1em; + color: #448844; +} + + +.change-name, .change-time, .comment-private { + display: inline; +} + +h3.change-name { + font-size: small; + font-weight: normal; +} + +.comment-actions { + white-space: nowrap; + vertical-align: top; + padding: 2px 2px 0 0 !important; +} + +.change-spinner { + width: 29px; +} + +.comment-text { + white-space: pre-wrap; + line-height: 1.2; + font-size: 13px; + font-family: "Droid Sans Mono",Menlo,Monaco,"Courier New",monospace; + background: #fff; + color: #222; + margin: 1px 0 0 0; + overflow: auto; + padding: 8px; + border-top: 1px solid #ddd; +} + +body.platform-Win32 .comment-text, body.platform-Win64 .comment-text { + font-family: "Fira Mono", monospace; +} + +.comment-text span.quote, .comment-text span.quote_wrapped { + background: #eee !important; + color: #444 !important; + display: block !important; + padding: 5px !important; + display: inline-block !important; + width: 99% !important; +} + +.comment-tags { + padding: 0 8px 2px 8px !important; +} + +.comment-tag { + border: 1px solid #eee; + padding: 2px 6px 2px 4px; + margin-right: 2px; + border-radius: 4px; + background-color: #fff; + color: #000; +} + +.comment-tag a { + padding-right: 4px; + cursor: pointer; +} + +#ctag { + margin-bottom: 4px; +} + +#ctag button { + margin-top: 2px; +} + +#ctag a { + margin-left: 8px; +} + +#ctag-error { + padding-left: 5px; + background-color: #faa; + color: #444; + border-radius: 2px; + margin-top: 2px; +} + +.comment-collapse-reason { + padding: 5px 7px !important; + width: 100%; +} + +.default-collapsed { + background: inherit; + color: #888; +} + +.default-collapsed .comment-actions { + padding: 2px; +} + +.private-comment { + color: #8b0000; + background: #f3eeee; +} + +.activity { + padding: 5px 8px; + background: #eee; + border-top: 1px solid #ddd; +} + +.activity-deleted { + text-decoration: line-through; +} + +/* add comment */ + +#add-comment { + margin-top: 20px; +} + +#add-comment-private, +#bugzilla-etiquette { + float: right; +} + +#comment { + border: 1px solid #ccc; +} + +#comment, #comment-preview { + clear: both; + width: 100%; + box-sizing: border-box !important; + margin: 0 0 1em; + max-width: 1024px; +} + +#comment-preview { + border: 1px solid #fff; + -moz-box-shadow: none; + -webkit-box-shadow: none; + box-shadow: none; + padding: 4px 3px 5px; +} + +#preview-throbber { + margin-left: 8px; +} + +#comment-tabs { + margin: 0px; + padding: 0px; + list-style: none; +} + +#comment-tabs li { + display: inline-block; + padding: 4px 8px; + cursor: pointer; + border: 1px solid silver; + border-top-left-radius: 2px; + border-top-right-radius: 2px; +} + +#comment-tabs li[aria-selected="true"] { + background: #fff; + border-bottom: 1px solid #fff; +} + +.preview-error { + color: #666; + font-style: italic; +} + +/* controls */ + +#summary-container { + display: table-cell; + width: 100%; + vertical-align: top; +} + +#xhr-error { + background: #fff; + color: #000; + border-radius: 2px; + border: 1px solid maroon; + margin: 5px 0px; + padding: 5px; +} + +#floating-message { + position: absolute; + left: 50%; + margin: 4px; +} + +#floating-message-text { + position: relative; + left: -50%; + cursor: default; + background: #fff9db; + color: #666458; + box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.5), inset 0 0 1px #000; + border-radius: 4px; + padding: 4px 8px; +} + +#mode-container { + display: table-cell; + white-space: nowrap; + text-align: right; + padding: 5px; + margin: 5px; +} + +#mode-btn, #commit-btn { + margin: 0 0 5px 0; +} + +#mode-btn-loading, #mode-btn-editing { + display: none; +} + +#edit-throbber { + margin-right: 5px; +} + +#product-throbber { + margin-left: 8px; +} + +#commit { + margin: 5px; +} + +#mode-container .button-row { + margin-top: 1px; + border-left: 5px solid white; +} + +/* theme */ + +#bugzilla-body { + max-width: 1024px; + min-width: 800px; + width: 100%; +} + +.vcard { + white-space: normal; +} + +.vcard a.disabled { + color: #888; +} + +input[type=text][disabled], input:not([type])[disabled] { + color: #888 !important; +} + +.xdsoft_datetimepicker button, .xdsoft_datetimepicker button:hover { + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} + +div.ui-widget-content { + background: #fff; +} + +div.ui-tooltip { + padding: 4px; + font-size: 13px; + font-family: inherit; + max-width: 500px; +} + +.yui-ac { + width: 100%; +} + +/* lightbox */ + +.lightbox img { + margin-right: 4px; + vertical-align: sub; +} + +#lb_img { + background-color: #fff; + border: 1px solid #666; + -webkit-box-shadow: 0 0 10px #555; + -moz-box-shadow: 0 0 10px #555; + box-shadow: 0 0 10px #555; + padding: 10px; + max-width: 90%; + margin: 20px auto; + cursor: pointer; +} + +#lb_overlay { + position: fixed; + background: none repeat scroll 0 0 rgba(0, 0, 0, 0.5); + width: 100%; + height: 100%; + top: 0px; + left: 0px; + text-align: center; + z-index: 2; +} + +#lb_overlay2 { + position: absolute; + left: 0px; + width: 100%; + text-align: center; + z-index: 2; +} + +#lb_text { + color: #fff; + font-weight: bold; + text-shadow: 1px 1px 1px #000; + position: fixed; + top: 4px; + left: 8px; + z-index: 3; + cursor: default; +} + +#lb_close_btn { + position: fixed; + top: 8px; + right: 8px; +} + +/* product search */ + +#field-product { + white-space: nowrap; +} + +#product-search-container { + white-space: nowrap; +} + +#product-search, #product-search-cancel { + margin-left: 8px; +} + +#product-search-error { + margin-left: 8px; + vertical-align: middle; +} + +.pcs-form { + display: inline; +} + +.pcs-header { + display: none; +} + +#pcs { + width: 235px; +} + +/* search navigation */ + +#search-nav { + background: rgba(255, 255, 255, 0.3); + padding: 4px 8px; +} + +#search-nav-label { + font-weight: bold; +} + +.search-nav-link, .search-nav-disabled { + margin-left: 4px; +} + +#search-nav-reget { + margin-left: 8px; +} + +.search-nav-disabled { + color: #777; +} + +/* clipboard shenanigans */ + +#clip-container { + position: fixed; + top: 0px; + left: 0px; + width: 0px; + height: 0px; + z-index: 100; + opacity: 0; +} + +#user-guide { + padding-top: 5px; +} 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($('