зеркало из https://github.com/mozilla/pjs.git
Bug 80169: JavaScript-enhanced keyword editing - Patch by Teemu Mannermaa <wicked@etlicon.fi> r=justdave a=LpSolit
This commit is contained in:
Родитель
fb345f0e3a
Коммит
26cb84ad73
|
@ -1127,6 +1127,7 @@ $vars->{'currenttime'} = time();
|
|||
# The following variables are used when the user is making changes to multiple bugs.
|
||||
if ($dotweak) {
|
||||
$vars->{'dotweak'} = 1;
|
||||
$vars->{'valid_keywords'} = [map($_->name, Bugzilla::Keyword->get_all)];
|
||||
$vars->{'use_keywords'} = 1 if Bugzilla::Keyword::keyword_count();
|
||||
|
||||
$vars->{'products'} = Bugzilla->user->get_enterable_products;
|
||||
|
|
|
@ -364,6 +364,7 @@ $vars->{'bug_severity'} = get_legal_field_values('bug_severity');
|
|||
$vars->{'rep_platform'} = get_legal_field_values('rep_platform');
|
||||
$vars->{'op_sys'} = get_legal_field_values('op_sys');
|
||||
|
||||
$vars->{'valid_keywords'} = [map($_->name, Bugzilla::Keyword->get_all)];
|
||||
$vars->{'use_keywords'} = 1 if Bugzilla::Keyword::keyword_count();
|
||||
|
||||
$vars->{'assigned_to'} = formvalue('assigned_to');
|
||||
|
|
|
@ -0,0 +1,162 @@
|
|||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Keyword Chooser.
|
||||
*
|
||||
* The Initial Developer of the Original Code is America Online, Inc.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2004
|
||||
* Mozilla Foundation. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Christopher A. Aillon <christopher@aillon.com> (Original Author)
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
function KeywordChooser(aParent, aChooser, aAvail, aChosen, aValidKeywords)
|
||||
{
|
||||
// Initialization
|
||||
this._parent = aParent;
|
||||
this._chooser = aChooser;
|
||||
this._available = aAvail;
|
||||
this._chosen = aChosen;
|
||||
this._validKeywords = aValidKeywords;
|
||||
|
||||
this.setInitialStyles();
|
||||
|
||||
// Register us, our properties, and our events
|
||||
this._parent.chooser = this;
|
||||
this._chooser.chooserElement = this._parent;
|
||||
}
|
||||
|
||||
KeywordChooser.prototype =
|
||||
{
|
||||
// chooses the selected items
|
||||
choose: function()
|
||||
{
|
||||
this._swapSelected(this._available, this._chosen);
|
||||
},
|
||||
|
||||
unchoose: function()
|
||||
{
|
||||
this._swapSelected(this._chosen, this._available);
|
||||
},
|
||||
|
||||
positionChooser: function()
|
||||
{
|
||||
if (this._positioned) {
|
||||
return;
|
||||
}
|
||||
|
||||
var elemY = bz_findPosY(this._parent);
|
||||
var elemX = bz_findPosX(this._parent);
|
||||
var elemH = this._parent.offsetHeight;
|
||||
|
||||
this._chooser.style.left = elemX + "px";
|
||||
this._chooser.style.top = elemY + elemH + 1 + "px";
|
||||
|
||||
this._positioned = true;
|
||||
},
|
||||
|
||||
setInitialStyles: function()
|
||||
{
|
||||
this._chooser.style.display = "none";
|
||||
this._chooser.style.position = "absolute";
|
||||
this._positioned = false;
|
||||
},
|
||||
|
||||
open: function()
|
||||
{
|
||||
this._chooser.style.display = "";
|
||||
this._available.style.display = "";
|
||||
this._chosen.style.display = "";
|
||||
this._parent.disabled = true;
|
||||
this.positionChooser();
|
||||
},
|
||||
|
||||
ok: function()
|
||||
{
|
||||
var len = this._chosen.options.length;
|
||||
|
||||
var text = "";
|
||||
for (var i = 0; i < len; i++) {
|
||||
text += this._chosen.options[i].text;
|
||||
if (i != len - 1) {
|
||||
text += ", ";
|
||||
}
|
||||
}
|
||||
|
||||
this._parent.value = text;
|
||||
this._parent.title = text;
|
||||
|
||||
this.close();
|
||||
},
|
||||
|
||||
cancel: function()
|
||||
{
|
||||
var chosentext = this._parent.value;
|
||||
var chosenArray = new Array();
|
||||
|
||||
if (chosentext != ""){
|
||||
chosenArray = chosentext.split(", ");
|
||||
}
|
||||
|
||||
var availArray = new Array();
|
||||
|
||||
for (var i = 0; i < this._validKeywords.length; i++) {
|
||||
if (!bz_isValueInArray(chosenArray, this._validKeywords[i])) {
|
||||
availArray[availArray.length] = this._validKeywords[i];
|
||||
}
|
||||
}
|
||||
|
||||
bz_populateSelectFromArray(this._available, availArray, false, true);
|
||||
bz_populateSelectFromArray(this._chosen, chosenArray, false, true);
|
||||
this.close();
|
||||
},
|
||||
|
||||
close: function()
|
||||
{
|
||||
this._chooser.style.display = "none";
|
||||
this._parent.disabled = false;
|
||||
},
|
||||
|
||||
_swapSelected: function(aSource, aTarget)
|
||||
{
|
||||
var kNothingSelected = -1;
|
||||
while (aSource.selectedIndex != kNothingSelected) {
|
||||
var option = aSource.options[aSource.selectedIndex];
|
||||
aTarget.appendChild(option);
|
||||
option.selected = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function InitializeKeywordChooser(aValidKeywords)
|
||||
{
|
||||
var keywords = document.getElementById("keywords");
|
||||
var chooser = document.getElementById("keyword-chooser");
|
||||
var avail = document.getElementById("keyword-list");
|
||||
var chosen = document.getElementById("bug-keyword-list");
|
||||
var chooserObj = new KeywordChooser(keywords, chooser, avail, chosen, aValidKeywords);
|
||||
}
|
|
@ -20,6 +20,7 @@
|
|||
*
|
||||
* Contributor(s):
|
||||
* Max Kanat-Alexander <mkanat@bugzilla.org>
|
||||
* Christopher A. Aillon <christopher@aillon.com>
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
|
@ -114,3 +115,100 @@ function bz_getFullWidth(fromObj)
|
|||
|
||||
return scrollX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create wanted options in a select form control.
|
||||
*
|
||||
* @param aSelect Select form control to manipulate.
|
||||
* @param aValue Value attribute of the new option element.
|
||||
* @param aTextValue Value of a text node appended to the new option
|
||||
* element.
|
||||
* @param aOwnerDocument Owner document of the new option element. If not
|
||||
* specified then "document" will be used.
|
||||
* @return Created option element.
|
||||
*/
|
||||
function bz_createOptionInSelect(aSelect, aValue, aTextValue, aOwnerDocument)
|
||||
{
|
||||
if (!aOwnerDocument) {
|
||||
aOwnerDocument = document;
|
||||
}
|
||||
|
||||
var myOption = aOwnerDocument.createElement("option");
|
||||
myOption.setAttribute("value", aValue);
|
||||
|
||||
var myTextNode = aOwnerDocument.createTextNode(aTextValue)
|
||||
myOption.appendChild(myTextNode);
|
||||
|
||||
aSelect.appendChild(myOption);
|
||||
|
||||
return myOption;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all options from a select form control.
|
||||
*
|
||||
* @param aElm Select form control of which options to clear.
|
||||
* @param aSkipFirst Boolean; true to skip (not clear) first option in the
|
||||
* select and false to remove all options.
|
||||
*/
|
||||
function bz_clearOptions(aElm, aSkipFirst)
|
||||
{
|
||||
var start = 0;
|
||||
|
||||
// Skip the first element? (for 'Choose One' type foo)
|
||||
if (aSkipFirst) {
|
||||
start = 1;
|
||||
}
|
||||
|
||||
var length = aElm.options.length;
|
||||
|
||||
for (var run = start; run < length; run++) {
|
||||
aElm.removeChild(aElm.options[start]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an array and moves all the values to an select.
|
||||
*
|
||||
* @param aSelect Select form control to populate. Will be cleared
|
||||
* before array values are created in it.
|
||||
* @param aArray Array with values to populate select with.
|
||||
* @param aSkipFirst Boolean; true to skip (not touch) first option in the
|
||||
* select and false to remove all options.
|
||||
* @param aUseNameAsValue Boolean; true if name is used as value and false if
|
||||
* not.
|
||||
*/
|
||||
function bz_populateSelectFromArray(aSelect, aArray, aSkipFirst, aUseNameAsValue)
|
||||
{
|
||||
// Clear the field
|
||||
bz_clearOptions(aSelect, aSkipFirst);
|
||||
|
||||
for (var run = 0; run < aArray.length; run++) {
|
||||
if (aUseNameAsValue) {
|
||||
bz_createOptionInSelect(aSelect, aArray[run], aArray[run]);
|
||||
} else {
|
||||
bz_createOptionInSelect(aSelect, aArray[run][0], aArray[run][0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a specified value is in the specified array.
|
||||
*
|
||||
* @param aArray Array to search for the value.
|
||||
* @param aValue Value to search from the array.
|
||||
* @return Boolean; true if value is found in the array and false if not.
|
||||
*/
|
||||
function bz_isValueInArray(aArray, aValue)
|
||||
{
|
||||
var run = 0;
|
||||
var len = aArray.length;
|
||||
|
||||
for ( ; run < len; run++) {
|
||||
if (aArray[run] == aValue) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -255,6 +255,7 @@ if ($cgi->cookie("BUGLIST")) {
|
|||
@bug_list = split(/:/, $cgi->cookie("BUGLIST"));
|
||||
}
|
||||
$vars->{'bug_list'} = \@bug_list;
|
||||
$vars->{'valid_keywords'} = [map($_->name, Bugzilla::Keyword->get_all)];
|
||||
$vars->{'use_keywords'} = 1 if Bugzilla::Keyword::keyword_count();
|
||||
|
||||
if ($token) {
|
||||
|
|
|
@ -66,6 +66,7 @@ my $cgi = Bugzilla->cgi;
|
|||
my $dbh = Bugzilla->dbh;
|
||||
my $template = Bugzilla->template;
|
||||
local our $vars = {};
|
||||
$vars->{'valid_keywords'} = [map($_->name, Bugzilla::Keyword->get_all)];
|
||||
$vars->{'use_keywords'} = 1 if Bugzilla::Keyword::keyword_count();
|
||||
|
||||
my @editable_bug_fields = editable_bug_fields();
|
||||
|
|
|
@ -97,6 +97,7 @@ eval {
|
|||
|
||||
$vars->{'bugs'} = \@bugs;
|
||||
$vars->{'marks'} = \%marks;
|
||||
$vars->{'valid_keywords'} = [map($_->name, Bugzilla::Keyword->get_all)];
|
||||
$vars->{'use_keywords'} = 1 if Bugzilla::Keyword::keyword_count();
|
||||
|
||||
my @bugids = map {$_->bug_id} @bugs;
|
||||
|
|
|
@ -346,3 +346,14 @@ div.user_match {
|
|||
.field_value {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
#keyword-chooser {
|
||||
padding: 10px;
|
||||
position: absolute;
|
||||
z-index: 25;
|
||||
top: 50px;
|
||||
left: 50px;
|
||||
border: 2px solid #404D6C;
|
||||
-moz-border-radius: 5px;
|
||||
background: white;
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
[% PROCESS global/header.html.tmpl
|
||||
title = title
|
||||
style_urls = [ 'skins/standard/create_attachment.css' ]
|
||||
javascript_urls = [ "js/attachment.js" ]
|
||||
javascript_urls = [ "js/attachment.js", "js/util.js", "js/keyword-chooser.js" ]
|
||||
%]
|
||||
|
||||
<script type="text/javascript">
|
||||
|
@ -486,7 +486,7 @@ function handleWantsAttachment(wants_attachment) {
|
|||
</strong>
|
||||
</td>
|
||||
<td colspan="3">
|
||||
<input name="keywords" size="60" value="[% keywords FILTER html %]"> (optional)
|
||||
<input id="keywords" name="keywords" size="60" value="[% keywords FILTER html %]" onfocus="this.chooser.open();"> (optional)
|
||||
</td>
|
||||
</tr>
|
||||
[% END %]
|
||||
|
@ -575,6 +575,12 @@ function handleWantsAttachment(wants_attachment) {
|
|||
<input type="hidden" name="form_name" value="enter_bug">
|
||||
</form>
|
||||
|
||||
[% IF use_keywords %]
|
||||
[% PROCESS "bug/keyword-chooser.html.tmpl"
|
||||
sel_keywords = keywords.split(', ')
|
||||
%]
|
||||
[% END %]
|
||||
|
||||
[%# Links or content with more information about the bug being created. %]
|
||||
[% Hook.process("end") %]
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
|
||||
[% PROCESS global/header.html.tmpl
|
||||
title = "$terms.Bug $id Submitted"
|
||||
javascript_urls = [ "js/util.js", "js/keyword-chooser.js" ]
|
||||
%]
|
||||
|
||||
[% header_done = 1 %]
|
||||
|
|
|
@ -204,7 +204,8 @@
|
|||
<b><a href="describekeywords.cgi"><u>K</u>eywords</a></b></label>:
|
||||
</td>
|
||||
[% PROCESS input inputname => "keywords" size => 60 colspan => 2
|
||||
value => bug.keywords.join(', ') %]
|
||||
value => bug.keywords.join(', ')
|
||||
onfocus => "this.chooser.open()" %]
|
||||
</tr>
|
||||
[% END %]
|
||||
|
||||
|
@ -542,6 +543,12 @@
|
|||
|
||||
</form>
|
||||
|
||||
[% IF use_keywords %]
|
||||
[% PROCESS "bug/keyword-chooser.html.tmpl"
|
||||
sel_keywords = bug.keywords.split(', ')
|
||||
%]
|
||||
[% END %]
|
||||
|
||||
[%############################################################################%]
|
||||
[%# Block for the first table in the "Details" section #%]
|
||||
[%############################################################################%]
|
||||
|
@ -826,7 +833,8 @@
|
|||
[% IF bug.check_can_change_field(inputname, 0, 1) %]
|
||||
<input id="[% inputname %]" name="[% inputname %]"
|
||||
value="[% val FILTER html %]"[% " size=\"$size\"" IF size %]
|
||||
[% " maxlength=\"$maxlength\"" IF maxlength %]>
|
||||
[% " maxlength=\"$maxlength\"" IF maxlength %]
|
||||
[% " onfocus=\"$onfocus\"" IF onfocus %]>
|
||||
[% ELSE %]
|
||||
<input type="hidden" name="[% inputname %]" id="[% inputname %]"
|
||||
value="[% val FILTER html %]">
|
||||
|
@ -843,6 +851,7 @@
|
|||
[% colspan = 0 %]
|
||||
[% size = 0 %]
|
||||
[% value = undef %]
|
||||
[% onfocus = undef %]
|
||||
[% END %]
|
||||
|
||||
[%############################################################################%]
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
<!-- 1.0@bugzilla.org -->
|
||||
[%# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is Keyword Picker.
|
||||
#
|
||||
# The Initial Developer of the Original Code is America Online, Inc.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2004
|
||||
# Mozilla Foundation. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Christopher A. Aillon <christopher@aillon.com> (Original Author)
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# use your version of this file under the terms of the MPL, indicate your
|
||||
# decision by deleting the provisions above and replace them with the notice
|
||||
# and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
# the provisions above, a recipient may use your version of this file under
|
||||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
#%]
|
||||
|
||||
[%############################################################################%]
|
||||
[%# Keyword Picker #%]
|
||||
[%############################################################################%]
|
||||
[%# #%]
|
||||
[%# If you edit this file, you might also need to edit js/keyword-chooser.js #%]
|
||||
[%# #%]
|
||||
[%############################################################################%]
|
||||
|
||||
<div id="keyword-chooser" style="display: none">
|
||||
<table>
|
||||
<tr>
|
||||
<td valign="top">
|
||||
Available Keywords:<br>
|
||||
<select id="keyword-list" size="5" multiple>
|
||||
[% FOREACH kwd = valid_keywords %]
|
||||
[% UNLESS sel_keywords && lsearch(sel_keywords, kwd) != -1 %]
|
||||
<option value="[% kwd FILTER html %]">[% kwd FILTER html %]</option>
|
||||
[% END %]
|
||||
[% END %]
|
||||
</select>
|
||||
</td>
|
||||
<td valign="middle">
|
||||
<button onclick="document.getElementById('keyword-chooser').chooserElement.chooser.choose(); return false;">-></button><br>
|
||||
<button onclick="document.getElementById('keyword-chooser').chooserElement.chooser.unchoose(); return false;"><-</button>
|
||||
</td>
|
||||
<td valign="top">
|
||||
Bug Keywords:<br>
|
||||
<select id="bug-keyword-list" size="5" multiple>
|
||||
[% FOREACH kwd = valid_keywords %]
|
||||
[% IF sel_keywords && lsearch(sel_keywords, kwd) != -1 %]
|
||||
<option value="[% kwd FILTER html %]">[% kwd FILTER html %]</option>
|
||||
[% END %]
|
||||
[% END %]
|
||||
</select>
|
||||
</td>
|
||||
<td valign="middle">
|
||||
<button type="button" onclick="document.getElementById('keyword-chooser').chooserElement.chooser.ok(); return false">OK</button>
|
||||
<br>
|
||||
<button type="button" onclick="document.getElementById('keyword-chooser').chooserElement.chooser.cancel(); return false" style="margin-top:3px;">Cancel</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
var validKeywords = new Array();
|
||||
|
||||
[% FOREACH kwd = valid_keywords %]
|
||||
validKeywords[validKeywords.length] = "[% kwd FILTER html %]";
|
||||
[% END %]
|
||||
|
||||
InitializeKeywordChooser(validKeywords);
|
||||
</script>
|
|
@ -42,4 +42,6 @@
|
|||
[% title = "Change Votes" %]
|
||||
[% END %]
|
||||
|
||||
[% PROCESS global/header.html.tmpl %]
|
||||
[% PROCESS global/header.html.tmpl
|
||||
javascript_urls = [ "js/util.js", "js/keyword-chooser.js" ]
|
||||
%]
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
"bz_component_$bug.component",
|
||||
"bz_bug_$bug.bug_id"
|
||||
]
|
||||
javascript_urls = [ "js/util.js", "js/keyword-chooser.js" ]
|
||||
%]
|
||||
[% END %]
|
||||
|
||||
|
|
|
@ -315,6 +315,7 @@
|
|||
'" colspan=\"$colspan\"" IF colspan',
|
||||
'" size=\"$size\"" IF size',
|
||||
'" maxlength=\"$maxlength\"" IF maxlength',
|
||||
'" onfocus=\"$onfocus\"" IF onfocus',
|
||||
'flag.status',
|
||||
],
|
||||
|
||||
|
|
|
@ -204,7 +204,8 @@
|
|||
</label>
|
||||
</th>
|
||||
<td colspan="3">
|
||||
<input id="keywords" name="keywords" size="32">
|
||||
<input id="keywords" name="keywords" size="32"
|
||||
onfocus = "this.chooser.open();">
|
||||
<select name="keywordaction">
|
||||
<option value="add">Add these keywords</option>
|
||||
<option value="delete">Delete these keywords</option>
|
||||
|
@ -238,6 +239,12 @@
|
|||
|
||||
</table>
|
||||
|
||||
[% IF use_keywords %]
|
||||
[% PROCESS "bug/keyword-chooser.html.tmpl"
|
||||
sel_keywords = keywords.split(', ')
|
||||
%]
|
||||
[% END %]
|
||||
|
||||
<b><label for="comment">Additional Comments:</label></b><br>
|
||||
[% INCLUDE global/textarea.html.tmpl
|
||||
name = 'comment'
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
title = title
|
||||
style = style
|
||||
atomlink = "buglist.cgi?$urlquerypart&title=$title&ctype=atom"
|
||||
javascript_urls = [ "js/util.js", "js/keyword-chooser.js" ]
|
||||
%]
|
||||
|
||||
<div class="bz_query_head" align="center">
|
||||
|
|
Загрузка…
Ссылка в новой задаче