/* 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. */ var Dom = YAHOO.util.Dom; YAHOO.bugzilla.commentTagging = { ctag_div : false, ctag_add : false, counter : 0, min_len : 3, max_len : 24, tags_by_no: {}, nos_by_tag: {}, current_id: 0, current_no: -1, can_edit : false, pending : {}, label : '', min_len_error: '', max_len_error: '', init : function(can_edit) { this.can_edit = can_edit; this.ctag_div = Dom.get('bz_ctag_div'); this.ctag_add = Dom.get('bz_ctag_add'); YAHOO.util.Event.on(this.ctag_add, 'keypress', this.onKeyPress); YAHOO.util.Event.onDOMReady(function() { YAHOO.bugzilla.commentTagging.updateCollapseControls(); }); if (!can_edit) return; $('#bz_ctag_add').devbridgeAutocomplete({ serviceUrl: function(query) { return 'rest/bug/comment/tags/' + encodeURIComponent(query); }, params: { Bugzilla_api_token: BUGZILLA.api_token }, deferRequestBy: 250, minChars: 1, tabDisabled: true, transformResult: function(response) { response = $.parseJSON(response); return { suggestions: $.map(response, function(dataItem) { return { value: dataItem, data : null }; }) }; } }); }, toggle : function(comment_id, comment_no) { if (!this.ctag_div) return; var tags_container = Dom.get('ct_' + comment_no); if (this.current_id == comment_id) { // hide this.current_id = 0; this.current_no = -1; Dom.addClass(this.ctag_div, 'bz_default_hidden'); this.hideError(); window.focus(); } else { // show or move this.rpcRefresh(comment_id, comment_no); this.current_id = comment_id; this.current_no = comment_no; this.ctag_add.value = ''; tags_container.parentNode.insertBefore(this.ctag_div, tags_container); Dom.removeClass(this.ctag_div, 'bz_default_hidden'); Dom.removeClass(tags_container.parentNode, 'bz_default_hidden'); var comment = Dom.get('comment_text_' + comment_no); if (Dom.hasClass(comment, 'collapsed')) { var link = Dom.get('comment_link_' + comment_no); expand_comment(link, comment, comment_no); } window.setTimeout(function() { YAHOO.bugzilla.commentTagging.ctag_add.focus(); }, 50); } }, hideInput : function() { if (this.current_id != 0) { var comment_no = this.current_no; this.toggle(this.current_id, this.current_no); this.hideEmpty(comment_no); } this.hideError(); }, hideEmpty : function(comment_no) { if (Dom.get('ct_' + comment_no).children.length == 0) { Dom.addClass('comment_tag_' + comment_no, 'bz_default_hidden'); } }, showError : function(comment_id, comment_no, error) { var bz_ctag_error = Dom.get('bz_ctag_error'); var tags_container = Dom.get('ct_' + comment_no); tags_container.parentNode.appendChild(bz_ctag_error, tags_container); Dom.get('bz_ctag_error_msg').innerHTML = YAHOO.lang.escapeHTML(error); Dom.removeClass(bz_ctag_error, 'bz_default_hidden'); }, hideError : function() { Dom.addClass('bz_ctag_error', 'bz_default_hidden'); }, onKeyPress : function(evt) { evt = evt || window.event; var charCode = evt.charCode || evt.keyCode; if (evt.keyCode == 27) { // escape YAHOO.bugzilla.commentTagging.hideInput(); YAHOO.util.Event.stopEvent(evt); } else if (evt.keyCode == 13) { // return YAHOO.util.Event.stopEvent(evt); var tags = YAHOO.bugzilla.commentTagging.ctag_add.value.split(/[ ,]/); var comment_id = YAHOO.bugzilla.commentTagging.current_id; var comment_no = YAHOO.bugzilla.commentTagging.current_no; try { YAHOO.bugzilla.commentTagging.add(comment_id, comment_no, tags); YAHOO.bugzilla.commentTagging.hideInput(); } catch(e) { YAHOO.bugzilla.commentTagging.showError(comment_id, comment_no, e.message); } } }, showTags : function(comment_id, comment_no, tags) { // remove existing tags var tags_container = Dom.get('ct_' + comment_no); while (tags_container.hasChildNodes()) { tags_container.removeChild(tags_container.lastChild); } // add tags if (tags != '') { if (typeof(tags) == 'string') { tags = tags.split(','); } for (var i = 0, l = tags.length; i < l; i++) { tags_container.appendChild(this.buildTagHtml(comment_id, comment_no, tags[i])); } } // update tracking array this.tags_by_no['c' + comment_no] = tags; this.updateCollapseControls(); }, updateCollapseControls : function() { var container = Dom.get('comment_tags_collapse_expand_container'); if (!container) return; // build list of tags this.nos_by_tag = {}; for (var id in this.tags_by_no) { if (this.tags_by_no.hasOwnProperty(id)) { for (var i = 0, l = this.tags_by_no[id].length; i < l; i++) { var tag = this.tags_by_no[id][i].toLowerCase(); if (!this.nos_by_tag.hasOwnProperty(tag)) { this.nos_by_tag[tag] = []; } this.nos_by_tag[tag].push(id); } } } var tags = []; for (var tag in this.nos_by_tag) { if (this.nos_by_tag.hasOwnProperty(tag)) { tags.push(tag); } } tags.sort(); if (tags.length) { var div = document.createElement('div'); div.appendChild(document.createTextNode(this.label)); var ul = document.createElement('ul'); ul.id = 'comment_tags_collapse_expand'; div.appendChild(ul); for (var i = 0, l = tags.length; i < l; i++) { var tag = tags[i]; var li = document.createElement('li'); ul.appendChild(li); var a = document.createElement('a'); li.appendChild(a); Dom.setAttribute(a, 'href', '#'); YAHOO.util.Event.addListener(a, 'click', function(evt, tag) { YAHOO.bugzilla.commentTagging.toggleCollapse(tag); YAHOO.util.Event.stopEvent(evt); }, tag); li.appendChild(document.createTextNode(' (' + this.nos_by_tag[tag].length + ')')); a.innerHTML = tag; } while (container.hasChildNodes()) { container.removeChild(container.lastChild); } container.appendChild(div); } else { while (container.hasChildNodes()) { container.removeChild(container.lastChild); } } }, toggleCollapse : function(tag) { var nos = this.nos_by_tag[tag]; if (!nos) return; toggle_all_comments('collapse'); for (var i = 0, l = nos.length; i < l; i++) { var comment_no = nos[i].match(/\d+$/)[0]; var comment = Dom.get('comment_text_' + comment_no); var link = Dom.get('comment_link_' + comment_no); expand_comment(link, comment, comment_no); } }, buildTagHtml : function(comment_id, comment_no, tag) { var el = document.createElement('span'); Dom.setAttribute(el, 'id', 'ct_' + comment_no + '_' + tag); Dom.addClass(el, 'bz_comment_tag'); if (this.can_edit) { var a = document.createElement('a'); Dom.setAttribute(a, 'href', '#'); YAHOO.util.Event.addListener(a, 'click', function(evt, args) { YAHOO.bugzilla.commentTagging.remove(args[0], args[1], args[2]) YAHOO.util.Event.stopEvent(evt); }, [comment_id, comment_no, tag]); a.appendChild(document.createTextNode('x')); el.appendChild(a); el.appendChild(document.createTextNode("\u00a0")); } el.appendChild(document.createTextNode(tag)); return el; }, add : function(comment_id, comment_no, add_tags) { // build list of current tags from html var tags = new Array(); var spans = Dom.getElementsByClassName('bz_comment_tag', undefined, 'ct_' + comment_no); for (var i = 0, l = spans.length; i < l; i++) { tags.push(spans[i].textContent.substr(2)); } // add new tags var new_tags = new Array(); for (var i = 0, l = add_tags.length; i < l; i++) { var tag = YAHOO.lang.trim(add_tags[i]); // validation if (tag == '') continue; if (tag.length < YAHOO.bugzilla.commentTagging.min_len) throw new Error(this.min_len_error) if (tag.length > YAHOO.bugzilla.commentTagging.max_len) throw new Error(this.max_len_error) // append new tag if (bz_isValueInArrayIgnoreCase(tags, tag)) continue; new_tags.push(tag); tags.push(tag); } tags.sort(); // update this.showTags(comment_id, comment_no, tags); this.rpcUpdate(comment_id, comment_no, new_tags, undefined); }, remove : function(comment_id, comment_no, tag) { var el = Dom.get('ct_' + comment_no + '_' + tag); if (el) { el.parentNode.removeChild(el); this.rpcUpdate(comment_id, comment_no, undefined, [ tag ]); this.hideEmpty(comment_no); } }, // If multiple updates are triggered quickly, overlapping refresh events // are generated. We ignore all events except the last one. incPending : function(comment_id) { if (this.pending['c' + comment_id] == undefined) { this.pending['c' + comment_id] = 1; } else { this.pending['c' + comment_id]++; } }, decPending : function(comment_id) { if (this.pending['c' + comment_id] != undefined) this.pending['c' + comment_id]--; }, hasPending : function(comment_id) { return this.pending['c' + comment_id] != undefined && this.pending['c' + comment_id] > 0; }, rpcRefresh : function(comment_id, comment_no, noRefreshOnError) { this.incPending(comment_id); YAHOO.util.Connect.setDefaultPostHeader('application/json', true); YAHOO.util.Connect.asyncRequest('POST', 'jsonrpc.cgi', { success: function(res) { YAHOO.bugzilla.commentTagging.decPending(comment_id); data = YAHOO.lang.JSON.parse(res.responseText); if (data.error) { YAHOO.bugzilla.commentTagging.handleRpcError( comment_id, comment_no, data.error.message, noRefreshOnError); return; } if (!YAHOO.bugzilla.commentTagging.hasPending(comment_id)) YAHOO.bugzilla.commentTagging.showTags( comment_id, comment_no, data.result.comments[comment_id].tags); }, failure: function(res) { YAHOO.bugzilla.commentTagging.decPending(comment_id); YAHOO.bugzilla.commentTagging.handleRpcError( comment_id, comment_no, res.responseText, noRefreshOnError); } }, YAHOO.lang.JSON.stringify({ version: "1.1", method: 'Bug.comments', params: { Bugzilla_api_token: BUGZILLA.api_token, comment_ids: [ comment_id ], include_fields: [ 'tags' ] } }) ); }, rpcUpdate : function(comment_id, comment_no, add, remove) { this.incPending(comment_id); YAHOO.util.Connect.setDefaultPostHeader('application/json', true); YAHOO.util.Connect.asyncRequest('POST', 'jsonrpc.cgi', { success: function(res) { YAHOO.bugzilla.commentTagging.decPending(comment_id); data = YAHOO.lang.JSON.parse(res.responseText); if (data.error) { YAHOO.bugzilla.commentTagging.handleRpcError(comment_id, comment_no, data.error.message); return; } if (!YAHOO.bugzilla.commentTagging.hasPending(comment_id)) YAHOO.bugzilla.commentTagging.showTags(comment_id, comment_no, data.result); }, failure: function(res) { YAHOO.bugzilla.commentTagging.decPending(comment_id); YAHOO.bugzilla.commentTagging.handleRpcError(comment_id, comment_no, res.responseText); } }, YAHOO.lang.JSON.stringify({ version: "1.1", method: 'Bug.update_comment_tags', params: { Bugzilla_api_token: BUGZILLA.api_token, comment_id: comment_id, add: add, remove: remove } }) ); }, handleRpcError : function(comment_id, comment_no, message, noRefreshOnError) { YAHOO.bugzilla.commentTagging.showError(comment_id, comment_no, message); if (!noRefreshOnError) { YAHOO.bugzilla.commentTagging.rpcRefresh(comment_id, comment_no, true); } } }