summaryrefslogtreecommitdiffstats
path: root/extensions/GuidedBugEntry/web
diff options
context:
space:
mode:
authorDavid Lawrence <dlawrence@mozilla.com>2011-08-11 00:26:03 +0200
committerDavid Lawrence <dlawrence@mozilla.com>2011-08-11 00:26:03 +0200
commit1f30fac936a3b0905e736dd86e559e33caf036ac (patch)
treeff2e0e3ab03e96dd177ef939304ec8a5b04865e5 /extensions/GuidedBugEntry/web
parent36e4d1fddb336e0ebfb61e0b7870e2b60c666ef5 (diff)
downloadbugzilla-1f30fac936a3b0905e736dd86e559e33caf036ac.tar.gz
bugzilla-1f30fac936a3b0905e736dd86e559e33caf036ac.tar.xz
Initial checkin of bmo/4.0 extensions. Still todo: port changes to core Bugzilla code
Diffstat (limited to 'extensions/GuidedBugEntry/web')
-rw-r--r--extensions/GuidedBugEntry/web/images/advanced.pngbin0 -> 720 bytes
-rw-r--r--extensions/GuidedBugEntry/web/images/help.pngbin0 -> 786 bytes
-rw-r--r--extensions/GuidedBugEntry/web/images/idea.pngbin0 -> 2799 bytes
-rw-r--r--extensions/GuidedBugEntry/web/images/input.pngbin0 -> 5545 bytes
-rw-r--r--extensions/GuidedBugEntry/web/images/message.pngbin0 -> 1497 bytes
-rw-r--r--extensions/GuidedBugEntry/web/images/products/camino.pngbin0 -> 6060 bytes
-rw-r--r--extensions/GuidedBugEntry/web/images/products/core.pngbin0 -> 7497 bytes
-rw-r--r--extensions/GuidedBugEntry/web/images/products/dino.pngbin0 -> 3375 bytes
-rw-r--r--extensions/GuidedBugEntry/web/images/products/fennec.pngbin0 -> 9023 bytes
-rw-r--r--extensions/GuidedBugEntry/web/images/products/firefox.pngbin0 -> 9395 bytes
-rw-r--r--extensions/GuidedBugEntry/web/images/products/labs.pngbin0 -> 4085 bytes
-rw-r--r--extensions/GuidedBugEntry/web/images/products/mozilla.pngbin0 -> 10808 bytes
-rw-r--r--extensions/GuidedBugEntry/web/images/products/other.pngbin0 -> 6654 bytes
-rw-r--r--extensions/GuidedBugEntry/web/images/products/seamonkey.pngbin0 -> 5255 bytes
-rw-r--r--extensions/GuidedBugEntry/web/images/products/sunbird.pngbin0 -> 10462 bytes
-rw-r--r--extensions/GuidedBugEntry/web/images/products/thunderbird.pngbin0 -> 9939 bytes
-rw-r--r--extensions/GuidedBugEntry/web/images/sumo.pngbin0 -> 3517 bytes
-rw-r--r--extensions/GuidedBugEntry/web/images/support.pngbin0 -> 2409 bytes
-rw-r--r--extensions/GuidedBugEntry/web/images/throbber.gifbin0 -> 723 bytes
-rw-r--r--extensions/GuidedBugEntry/web/images/warning.pngbin0 -> 1428 bytes
-rw-r--r--extensions/GuidedBugEntry/web/js/guided.js824
-rw-r--r--extensions/GuidedBugEntry/web/js/products.js117
-rw-r--r--extensions/GuidedBugEntry/web/style/guided.css231
-rw-r--r--extensions/GuidedBugEntry/web/yui-history-iframe.txt0
24 files changed, 1172 insertions, 0 deletions
diff --git a/extensions/GuidedBugEntry/web/images/advanced.png b/extensions/GuidedBugEntry/web/images/advanced.png
new file mode 100644
index 000000000..71a3fcb78
--- /dev/null
+++ b/extensions/GuidedBugEntry/web/images/advanced.png
Binary files differ
diff --git a/extensions/GuidedBugEntry/web/images/help.png b/extensions/GuidedBugEntry/web/images/help.png
new file mode 100644
index 000000000..5c870176d
--- /dev/null
+++ b/extensions/GuidedBugEntry/web/images/help.png
Binary files differ
diff --git a/extensions/GuidedBugEntry/web/images/idea.png b/extensions/GuidedBugEntry/web/images/idea.png
new file mode 100644
index 000000000..0a0ce6c79
--- /dev/null
+++ b/extensions/GuidedBugEntry/web/images/idea.png
Binary files differ
diff --git a/extensions/GuidedBugEntry/web/images/input.png b/extensions/GuidedBugEntry/web/images/input.png
new file mode 100644
index 000000000..34c10e989
--- /dev/null
+++ b/extensions/GuidedBugEntry/web/images/input.png
Binary files differ
diff --git a/extensions/GuidedBugEntry/web/images/message.png b/extensions/GuidedBugEntry/web/images/message.png
new file mode 100644
index 000000000..55b6add19
--- /dev/null
+++ b/extensions/GuidedBugEntry/web/images/message.png
Binary files differ
diff --git a/extensions/GuidedBugEntry/web/images/products/camino.png b/extensions/GuidedBugEntry/web/images/products/camino.png
new file mode 100644
index 000000000..c833b4d04
--- /dev/null
+++ b/extensions/GuidedBugEntry/web/images/products/camino.png
Binary files differ
diff --git a/extensions/GuidedBugEntry/web/images/products/core.png b/extensions/GuidedBugEntry/web/images/products/core.png
new file mode 100644
index 000000000..b9c5053f6
--- /dev/null
+++ b/extensions/GuidedBugEntry/web/images/products/core.png
Binary files differ
diff --git a/extensions/GuidedBugEntry/web/images/products/dino.png b/extensions/GuidedBugEntry/web/images/products/dino.png
new file mode 100644
index 000000000..9e0470a07
--- /dev/null
+++ b/extensions/GuidedBugEntry/web/images/products/dino.png
Binary files differ
diff --git a/extensions/GuidedBugEntry/web/images/products/fennec.png b/extensions/GuidedBugEntry/web/images/products/fennec.png
new file mode 100644
index 000000000..ebad7e358
--- /dev/null
+++ b/extensions/GuidedBugEntry/web/images/products/fennec.png
Binary files differ
diff --git a/extensions/GuidedBugEntry/web/images/products/firefox.png b/extensions/GuidedBugEntry/web/images/products/firefox.png
new file mode 100644
index 000000000..582a6952a
--- /dev/null
+++ b/extensions/GuidedBugEntry/web/images/products/firefox.png
Binary files differ
diff --git a/extensions/GuidedBugEntry/web/images/products/labs.png b/extensions/GuidedBugEntry/web/images/products/labs.png
new file mode 100644
index 000000000..346e0ef06
--- /dev/null
+++ b/extensions/GuidedBugEntry/web/images/products/labs.png
Binary files differ
diff --git a/extensions/GuidedBugEntry/web/images/products/mozilla.png b/extensions/GuidedBugEntry/web/images/products/mozilla.png
new file mode 100644
index 000000000..e506328bc
--- /dev/null
+++ b/extensions/GuidedBugEntry/web/images/products/mozilla.png
Binary files differ
diff --git a/extensions/GuidedBugEntry/web/images/products/other.png b/extensions/GuidedBugEntry/web/images/products/other.png
new file mode 100644
index 000000000..e436c22ae
--- /dev/null
+++ b/extensions/GuidedBugEntry/web/images/products/other.png
Binary files differ
diff --git a/extensions/GuidedBugEntry/web/images/products/seamonkey.png b/extensions/GuidedBugEntry/web/images/products/seamonkey.png
new file mode 100644
index 000000000..fcb261ae1
--- /dev/null
+++ b/extensions/GuidedBugEntry/web/images/products/seamonkey.png
Binary files differ
diff --git a/extensions/GuidedBugEntry/web/images/products/sunbird.png b/extensions/GuidedBugEntry/web/images/products/sunbird.png
new file mode 100644
index 000000000..6b15c257d
--- /dev/null
+++ b/extensions/GuidedBugEntry/web/images/products/sunbird.png
Binary files differ
diff --git a/extensions/GuidedBugEntry/web/images/products/thunderbird.png b/extensions/GuidedBugEntry/web/images/products/thunderbird.png
new file mode 100644
index 000000000..f3523183a
--- /dev/null
+++ b/extensions/GuidedBugEntry/web/images/products/thunderbird.png
Binary files differ
diff --git a/extensions/GuidedBugEntry/web/images/sumo.png b/extensions/GuidedBugEntry/web/images/sumo.png
new file mode 100644
index 000000000..d5773647c
--- /dev/null
+++ b/extensions/GuidedBugEntry/web/images/sumo.png
Binary files differ
diff --git a/extensions/GuidedBugEntry/web/images/support.png b/extensions/GuidedBugEntry/web/images/support.png
new file mode 100644
index 000000000..2320ea74a
--- /dev/null
+++ b/extensions/GuidedBugEntry/web/images/support.png
Binary files differ
diff --git a/extensions/GuidedBugEntry/web/images/throbber.gif b/extensions/GuidedBugEntry/web/images/throbber.gif
new file mode 100644
index 000000000..bc4fa6561
--- /dev/null
+++ b/extensions/GuidedBugEntry/web/images/throbber.gif
Binary files differ
diff --git a/extensions/GuidedBugEntry/web/images/warning.png b/extensions/GuidedBugEntry/web/images/warning.png
new file mode 100644
index 000000000..86bed170d
--- /dev/null
+++ b/extensions/GuidedBugEntry/web/images/warning.png
Binary files differ
diff --git a/extensions/GuidedBugEntry/web/js/guided.js b/extensions/GuidedBugEntry/web/js/guided.js
new file mode 100644
index 000000000..f600dbb21
--- /dev/null
+++ b/extensions/GuidedBugEntry/web/js/guided.js
@@ -0,0 +1,824 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.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 the GuidedBugEntry Bugzilla Extension.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Byron Jones <glob@mozilla.com>
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// global
+
+var Dom = YAHOO.util.Dom;
+var Event = YAHOO.util.Event;
+var History = YAHOO.util.History;
+
+var guided = {
+ _currentStep: '',
+ detectedPlatform: '',
+ detectedOpSys: '',
+ currentUser: '',
+ openStates: [],
+
+ setStep: function(newStep, noSetHistory) {
+ // initialise new step
+ eval(newStep + '.onShow()');
+
+ // change visibility of _step div
+ if (this._currentStep)
+ Dom.addClass(this._currentStep + '_step', 'hidden');
+ this._currentStep = newStep;
+ Dom.removeClass(this._currentStep + '_step', 'hidden');
+
+ // scroll to top of page to mimic real navigation
+ scroll(0,0);
+
+ // update history
+ if (History && !noSetHistory) {
+ History.navigate('h', newStep + "|" + product.getName());
+ }
+ },
+
+ init: function() {
+ // init history manager
+ try {
+ History.register('h', History.getBookmarkedState('h') || 'product',
+ this._onStateChange);
+ History.initialize("yui-history-field", "yui-history-iframe");
+ History.onReady(function () {
+ guided._onStateChange(History.getCurrentState('h'), true);
+ });
+ } catch(err) {
+ History = false;
+ }
+
+ // init steps
+ product.onInit();
+ dupes.onInit();
+ bugForm.onInit();
+ },
+
+ _onStateChange: function(state, noSetHistory) {
+ state = state.split("|");
+ product.setName(state[1] || '');
+ guided.setStep(state[0], noSetHistory);
+ },
+
+ setAdvancedLink: function() {
+ href = 'enter_bug.cgi?format=__default__' +
+ '&product=' + escape(product.getName()) +
+ '&short_desc=' + escape(dupes.getSummary());
+ Dom.get('advanced_img').href = href;
+ Dom.get('advanced_link').href = href;
+ }
+};
+
+// product step
+
+var product = {
+ details: false,
+ _counter: 0,
+ _loaded: '',
+
+ onInit: function() { },
+
+ onShow: function() {
+ Dom.removeClass('advanced', 'hidden');
+ },
+
+ select: function(productName) {
+ // called when a product is selected
+ this.setName(productName);
+ dupes.reset();
+ guided.setStep('dupes');
+ },
+
+ getName: function() {
+ return Dom.get('product').value;
+ },
+
+ _getNameAndRelated: function() {
+ var result = [];
+
+ var name = this.getName();
+ result.push(name);
+
+ if (products[name] && products[name].related) {
+ for (var i = 0, n = products[name].related.length; i < n; i++) {
+ result.push(products[name].related[i]);
+ }
+ }
+
+ return result;
+ },
+
+ setName: function(productName) {
+ if (productName == this.getName() && this.details)
+ return;
+
+ // display the product name
+ Dom.get('product').value = productName;
+ Dom.get('product_label').innerHTML = YAHOO.lang.escapeHTML(productName);
+ Dom.get('dupes_product_name').innerHTML = YAHOO.lang.escapeHTML(productName);
+ Dom.get('list_comp').href = 'describecomponents.cgi?product=' + escape(productName);
+ guided.setAdvancedLink();
+
+ if (productName == '') {
+ Dom.addClass("product_support", "hidden");
+ return;
+ }
+
+ // use the correct security group
+ if (products[productName] && products[productName].secgroup) {
+ Dom.get('groups').value = products[productName].secgroup;
+ } else {
+ Dom.get('groups').value = products['_default'].secgroup;
+ }
+
+ // use the correct platform & op_sys
+ if (products[productName] && products[productName].detectPlatform) {
+ Dom.get('rep_platform').value = guided.detectedPlatform;
+ Dom.get('op_sys').value = guided.detectedOpSys;
+ } else {
+ Dom.get('rep_platform').value = 'All';
+ Dom.get('op_sys').value = 'All';
+ }
+
+ // show support message
+ if (products[productName] && products[productName].support) {
+ Dom.get("product_support_message").innerHTML =
+ YAHOO.lang.escapeHTML(products[productName].support);
+ Dom.removeClass("product_support", "hidden");
+ } else {
+ Dom.addClass("product_support", "hidden");
+ }
+
+ if (this._loaded == productName)
+ return;
+
+ // grab the product information
+ this.details = false;
+ this._loaded = productName;
+ YAHOO.util.Connect.setDefaultPostHeader('text/plain; charset=UTF-8');
+ YAHOO.util.Connect.asyncRequest(
+ 'POST',
+ 'jsonrpc.cgi',
+ {
+ success: function(res) {
+ try {
+ data = YAHOO.lang.JSON.parse(res.responseText);
+ if (data.error)
+ throw(data.error.message);
+ product.details = data.result.products[0];
+ bugForm.onProductUpdated();
+ } catch (err) {
+ product.details = false;
+ bugForm.onProductUpdated();
+ if (err) {
+ alert('Failed to retreive components for product "' +
+ productName + '":' + "\n\n" + err);
+ if (console)
+ console.error(err);
+ }
+ }
+ },
+ failure: function(res) {
+ this._loaded = '';
+ product.details = false;
+ bugForm.onProductUpdated();
+ if (res.responseText) {
+ alert('Failed to retreive components for product "' +
+ productName + '":' + "\n\n" + res.responseText);
+ if (console)
+ console.error(res);
+ }
+ }
+ },
+ YAHOO.lang.JSON.stringify({
+ version: "1.1",
+ method: "Product.get",
+ id: ++this._counter,
+ params: {
+ names: [productName],
+ exclude_fields: ['internals', 'milestones']
+ }
+ }
+ )
+ );
+ }
+};
+
+// other products step
+
+var otherProducts = {
+ onInit: function() { },
+
+ onShow: function() {
+ Dom.removeClass('advanced', 'hidden');
+ }
+};
+
+// duplicates step
+
+var dupes = {
+ _counter: 0,
+ _dataTable: null,
+ _dataTableColumns: null,
+ _elSummary: null,
+ _elSearch: null,
+ _elList: null,
+ _currentSearchQuery: '',
+
+ onInit: function() {
+ this._elSummary = Dom.get('dupes_summary');
+ this._elSearch = Dom.get('dupes_search');
+ this._elList = Dom.get('dupes_list');
+
+ Event.onBlur(this._elSummary, this._onSummaryBlur);
+ Event.addListener(this._elSummary, 'keydown', this._onSummaryKeyDown);
+ Event.addListener(this._elSummary, 'keyup', this._onSummaryKeyUp);
+ Event.addListener(this._elSearch, 'click', this._doSearch);
+ },
+
+ setLabels: function(labels) {
+ this._dataTableColumns = [
+ { key: "id", label: labels.id, formatter: this._formatId },
+ { key: "summary", label: labels.summary, formatter: "text" },
+ { key: "status", label: labels.status, formatter: this._formatStatus },
+ { key: "update_token", label: '', formatter: this._formatCc }
+ ];
+ },
+
+ _initDataTable: function() {
+ var dataSource = new YAHOO.util.XHRDataSource("jsonrpc.cgi");
+ dataSource.connTimeout = 15000;
+ dataSource.connMethodPost = true;
+ dataSource.connXhrMode = "cancelStaleRequests";
+ dataSource.maxCacheEntries = 3;
+ dataSource.responseSchema = {
+ resultsList : "result.bugs",
+ metaFields : { error: "error", jsonRpcId: "id" }
+ };
+ // DataSource can't understand a JSON-RPC error response, so
+ // we have to modify the result data if we get one.
+ dataSource.doBeforeParseData =
+ function(oRequest, oFullResponse, oCallback) {
+ if (oFullResponse.error) {
+ oFullResponse.result = {};
+ oFullResponse.result.bugs = [];
+ if (console)
+ console.error("JSON-RPC error:", oFullResponse.error);
+ }
+ return oFullResponse;
+ };
+ dataSource.subscribe('dataErrorEvent',
+ function() {
+ dupes._currentSearchQuery = '';
+ }
+ );
+
+ this._dataTable = new YAHOO.widget.DataTable(
+ 'dupes_list',
+ this._dataTableColumns,
+ dataSource,
+ {
+ initialLoad: false,
+ MSG_EMPTY: 'No similar issues found.',
+ MSG_ERROR: 'An error occurred while searching for similar issues,' +
+ ' please try again.'
+ }
+ );
+ },
+
+ _formatId: function(el, oRecord, oColumn, oData) {
+ el.innerHTML = '<a href="show_bug.cgi?id=' + oData +
+ '" target="_blank">' + oData + '</a>';
+ },
+
+ _formatStatus: function(el, oRecord, oColumn, oData) {
+ var resolution = oRecord.getData('resolution');
+ var bug_status = display_value('bug_status', oData);
+ if (resolution) {
+ el.innerHTML = bug_status + ' ' +
+ display_value('resolution', resolution);
+ } else {
+ el.innerHTML = bug_status;
+ }
+ },
+
+ _formatCc: function(el, oRecord, oColumn, oData) {
+ var cc = oRecord.getData('cc');
+ var isCCed = false;
+ for (var i = 0, n = cc.length; i < n; i++) {
+ if (cc[i] == guided.currentUser) {
+ isCCed = true;
+ break;
+ }
+ }
+ dupes._buildCcHTML(el, oRecord.getData('id'), oRecord.getData('status'),
+ isCCed);
+ },
+
+ _buildCcHTML: function(el, id, bugStatus, isCCed) {
+ while (el.childNodes.length > 0)
+ el.removeChild(el.firstChild);
+
+ var isOpen = false;
+ for (var i = 0, n = guided.openStates.length; i < n; i++) {
+ if (guided.openStates[i] == bugStatus) {
+ isOpen = true;
+ break;
+ }
+ }
+
+ if (!isOpen && !isCCed) {
+ // you can't cc yourself to a closed bug here
+ return;
+ }
+
+ var button = document.createElement('button');
+ button.setAttribute('type', 'button');
+ if (isCCed) {
+ button.innerHTML = 'Stop&nbsp;following';
+ button.onclick = function() {
+ dupes.updateFollowing(el, id, bugStatus, button, false); return false;
+ };
+ } else {
+ button.innerHTML = 'Follow&nbsp;bug';
+ button.onclick = function() {
+ dupes.updateFollowing(el, id, bugStatus, button, true); return false;
+ };
+ }
+ el.appendChild(button);
+ },
+
+ updateFollowing: function(el, bugID, bugStatus, button, follow) {
+ button.disabled = true;
+ button.innerHTML = 'Updating...';
+
+ var ccObject;
+ if (follow) {
+ ccObject = { add: [ guided.currentUser ] };
+ } else {
+ ccObject = { remove: [ guided.currentUser ] };
+ }
+
+ YAHOO.util.Connect.setDefaultPostHeader('text/plain; charset=UTF-8');
+ YAHOO.util.Connect.asyncRequest(
+ 'POST',
+ 'jsonrpc.cgi',
+ {
+ success: function(res) {
+ data = YAHOO.lang.JSON.parse(res.responseText);
+ if (data.error)
+ throw(data.error.message);
+ dupes._buildCcHTML(el, bugID, bugStatus, follow);
+ },
+ failure: function(res) {
+ dupes._buildCcHTML(el, bugID, bugStatus, !follow);
+ if (res.responseText)
+ alert("Update failed:\n\n" + res.responseText);
+ }
+ },
+ YAHOO.lang.JSON.stringify({
+ version: "1.1",
+ method: "Bug.update",
+ id: ++this._counter,
+ params: {
+ ids: [ bugID ],
+ cc : ccObject
+ }
+ })
+ );
+ },
+
+ reset: function() {
+ this._elSummary.value = '';
+ Dom.addClass(this._elList, 'hidden');
+ Dom.addClass('dupes_continue', 'hidden');
+ this._elList.innerHTML = '';
+ this._showProductSupport();
+ this._currentSearchQuery = '';
+ },
+
+ _showProductSupport: function() {
+ var elSupport = Dom.get('product_support_' +
+ product.getName().replace(' ', '_').toLowerCase());
+ var supportElements = Dom.getElementsByClassName('product_support');
+ for (var i = 0, n = supportElements.length; i < n; i++) {
+ if (supportElements[i] == elSupport) {
+ Dom.removeClass(elSupport, 'hidden');
+ } else {
+ Dom.addClass(supportElements[i], 'hidden');
+ }
+ }
+ },
+
+ onShow: function() {
+ this._showProductSupport();
+ this._onSummaryBlur();
+
+ // hide the advanced form and top continue button entry until
+ // a search has happened
+ Dom.addClass('advanced', 'hidden');
+ Dom.addClass('dupes_continue_button_top', 'hidden');
+
+ if (!this._elSearch.disabled && this.getSummary().length >= 4) {
+ // do an immediate search after a page refresh if there's a query
+ this._doSearch();
+
+ } else {
+ // prepare for a search
+ this.reset();
+ }
+ },
+
+ _onSummaryBlur: function() {
+ dupes._elSearch.disabled = dupes._elSummary.value == '';
+ guided.setAdvancedLink();
+ },
+
+ _onSummaryKeyDown: function(e) {
+ // map <enter> to doSearch()
+ if (e && (e.keyCode == 13)) {
+ dupes._doSearch();
+ Event.stopPropagation(e);
+ }
+ },
+
+ _onSummaryKeyUp: function(e) {
+ // disable search button until there's a query
+ dupes._elSearch.disabled = YAHOO.lang.trim(dupes._elSummary.value) == '';
+ },
+
+ _doSearch: function() {
+ if (dupes.getSummary().length < 4) {
+ alert('The summary must be at least 4 characters long.');
+ return;
+ }
+ dupes._elSummary.blur();
+
+ // don't query if we already have the results (or they are pending)
+ if (dupes._currentSearchQuery == dupes.getSummary())
+ return;
+ dupes._currentSearchQuery = dupes.getSummary();
+
+ // initialise the datatable as late as possible
+ dupes._initDataTable();
+
+ try {
+ // run the search
+ Dom.removeClass(dupes._elList, 'hidden');
+
+ dupes._dataTable.showTableMessage(
+ 'Searching for similar issues...&nbsp;&nbsp;&nbsp;' +
+ '<img src="extensions/GuidedBugEntry/web/images/throbber.gif"' +
+ ' width="16" height="11">',
+ YAHOO.widget.DataTable.CLASS_LOADING
+ );
+ var json_object = {
+ version: "1.1",
+ method: "Bug.possible_duplicates",
+ id: ++dupes._counter,
+ params: {
+ product: product._getNameAndRelated(),
+ summary: dupes.getSummary(),
+ limit: 12,
+ include_fields: [ "id", "summary", "status", "resolution",
+ "update_token", "cc" ]
+ }
+ };
+
+ dupes._dataTable.getDataSource().sendRequest(
+ YAHOO.lang.JSON.stringify(json_object),
+ {
+ success: dupes._onDupeResults,
+ failure: dupes._onDupeResults,
+ scope: dupes._dataTable,
+ argument: dupes._dataTable.getState()
+ }
+ );
+
+ Dom.get('dupes_continue_button_top').disabled = true;
+ Dom.get('dupes_continue_button_bottom').disabled = true;
+ Dom.removeClass('dupes_continue', 'hidden');
+ } catch(err) {
+ if (console)
+ console.error(err.message);
+ }
+ },
+
+ _onDupeResults: function(sRequest, oResponse, oPayload) {
+ Dom.removeClass('advanced', 'hidden');
+ Dom.removeClass('dupes_continue_button_top', 'hidden');
+ Dom.get('dupes_continue_button_top').disabled = false;
+ Dom.get('dupes_continue_button_bottom').disabled = false;
+ dupes._dataTable.onDataReturnInitializeTable(sRequest, oResponse,
+ oPayload);
+ },
+
+ getSummary: function() {
+ var summary = YAHOO.lang.trim(this._elSummary.value);
+ // work around chrome bug
+ if (summary == dupes._elSummary.getAttribute('placeholder')) {
+ return '';
+ } else {
+ return summary;
+ }
+ }
+};
+
+// bug form step
+
+var bugForm = {
+ _visibleHelpPanel: null,
+ _mandatoryFields: [ 'short_desc', 'version_select' ],
+
+ onInit: function() {
+ Dom.get('user_agent').value = navigator.userAgent;
+ if (navigator.buildID && navigator.buildID != navigator.userAgent) {
+ Dom.get('build_id').value = navigator.buildID;
+ }
+ Event.addListener(Dom.get('short_desc'), 'blur', function() {
+ Dom.get('dupes_summary').value = Dom.get('short_desc').value;
+ guided.setAdvancedLink();
+ });
+ },
+
+ onShow: function() {
+ Dom.removeClass('advanced', 'hidden');
+ // default the summary to the dupes query
+ Dom.get('short_desc').value = dupes.getSummary();
+ this.resetSubmitButton();
+ if (Dom.get('component_select').length == 0)
+ this.onProductUpdated();
+ this.onFileChange();
+ for (var i = 0, n = this._mandatoryFields.length; i < n; i++) {
+ Dom.removeClass(this._mandatoryFields[i], 'missing');
+ }
+ },
+
+ resetSubmitButton: function() {
+ Dom.get('submit').disabled = false;
+ Dom.get('submit').value = 'Submit Bug';
+ },
+
+ onProductUpdated: function() {
+ var productName = product.getName();
+
+ // init
+ var elComponents = Dom.get('component_select');
+ Dom.addClass('component_description', 'hidden');
+ elComponents.options.length = 0;
+
+ var elVersions = Dom.get('version_select');
+ elVersions.length = 0;
+
+ // product not loaded yet, bail out
+ if (!product.details) {
+ Dom.addClass('versionTH', 'hidden');
+ Dom.addClass('versionTD', 'hidden');
+ Dom.get('productTD').colSpan = 2;
+ Dom.get('submit').disabled = true;
+ return;
+ }
+ Dom.get('submit').disabled = false;
+
+ // build components
+ // if there's a general component in it, make it selected by default
+ var defaultComponent = false;
+ for (var i = 0, n = product.details.components.length; i < n; i++) {
+ var component = product.details.components[i];
+ if (component.is_active == '1') {
+ elComponents.options[elComponents.options.length] =
+ new Option(component.name, component.name);
+ if (!defaultComponent && component.name.match(/general/i)) {
+ defaultComponent = component.name;
+ }
+ }
+ }
+
+ var elComponent = Dom.get('component');
+ if (elComponent.value == '' && defaultComponent)
+ elComponent.value = defaultComponent;
+ if (elComponent.value != '') {
+ elComponents.value = elComponent.value;
+ this.onComponentChange(elComponent.value);
+ }
+
+ // build versions
+ var defaultVersion = '';
+ var currentVersion = Dom.get('version').value;
+ for (var i = 0, n = product.details.versions.length; i < n; i++) {
+ var version = product.details.versions[i];
+ if (version.is_active == '1') {
+ elVersions.options[elVersions.options.length] =
+ new Option(version.name, version.name);
+ if (currentVersion == version.name)
+ defaultVersion = version.name;
+ }
+ }
+
+ if (!defaultVersion) {
+ // try to detect version on a per-product basis
+ if (products[productName] && products[productName].version) {
+ var detectedVersion = products[productName].version();
+ var options = elVersions.options;
+ for (var i = 0, n = options.length; i < n; i++) {
+ if (options[i].value == detectedVersion) {
+ defaultVersion = detectedVersion;
+ break;
+ }
+ }
+ }
+ }
+ if (!defaultVersion) {
+ // load last selected version
+ defaultVersion = YAHOO.util.Cookie.get('VERSION-' + productName);
+ }
+
+ if (elVersions.length > 1) {
+ // more than one version, show select
+ Dom.get('productTD').colSpan = 1;
+ Dom.removeClass('versionTH', 'hidden');
+ Dom.removeClass('versionTD', 'hidden');
+
+ } else {
+ // if there's only one version, we don't need to ask the user
+ Dom.addClass('versionTH', 'hidden');
+ Dom.addClass('versionTD', 'hidden');
+ Dom.get('productTD').colSpan = 2;
+ defaultVersion = elVersions.options[0].value;
+ }
+
+ if (defaultVersion) {
+ elVersions.value = defaultVersion;
+
+ } else {
+ // no default version, select an empty value to force a decision
+ var opt = new Option('', '');
+ try {
+ // standards
+ elVersions.add(opt, elVersions.options[0]);
+ } catch(ex) {
+ // ie only
+ elVersions.add(opt, 0);
+ }
+ elVersions.value = '';
+ }
+ bugForm.onVersionChange(elVersions.value);
+ },
+
+ onComponentChange: function(componentName) {
+ // show the component description
+ Dom.get('component').value = componentName;
+ var elComponentDesc = Dom.get('component_description');
+ elComponentDesc.innerHTML = '';
+ for (var i = 0, n = product.details.components.length; i < n; i++) {
+ var component = product.details.components[i];
+ if (component.name == componentName) {
+ elComponentDesc.innerHTML = component.description;
+ break;
+ }
+ }
+ Dom.removeClass(elComponentDesc, 'hidden');
+ },
+
+ onVersionChange: function(version) {
+ Dom.get('version').value = version;
+ },
+
+ onFileChange: function() {
+ // toggle ui enabled when a file is uploaded or cleared
+ var elFile = Dom.get('data');
+ var elReset = Dom.get('reset_data');
+ var elDescription = Dom.get('data_description');
+ var filename = bugForm._getFilename();
+ if (filename) {
+ elReset.disabled = false;
+ elDescription.value = filename;
+ elDescription.disabled = false;
+ } else {
+ elReset.disabled = true;
+ elDescription.value = '';
+ elDescription.disabled = true;
+ }
+ },
+
+ onFileClear: function() {
+ Dom.get('data').value = '';
+ this.onFileChange();
+ return false;
+ },
+
+ _getFilename: function() {
+ var filename = Dom.get('data').value;
+ if (!filename)
+ return '';
+ filename = filename.replace(/^.+[\\\/]/, '');
+ return filename;
+ },
+
+ _mandatoryCheck: function() {
+ result = true;
+ for (var i = 0, n = this._mandatoryFields.length; i < n; i++ ) {
+ id = this._mandatoryFields[i];
+ el = Dom.get(id);
+
+ if (el.type.toString() == "checkbox") {
+ value = el.checked;
+ } else {
+ value = el.value.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
+ el.value = value;
+ }
+
+ if (value == '') {
+ Dom.addClass(id, 'missing');
+ result = false;
+ } else {
+ Dom.removeClass(id, 'missing');
+ }
+ }
+ return result;
+ },
+
+ validate: function() {
+
+ // check mandatory fields
+
+ if (!bugForm._mandatoryCheck()) {
+ if (Dom.hasClass('short_desc', 'missing')
+ && Dom.hasClass('version_select', 'missing')) {
+ alert('Please enter the summary, and select the relevant version.');
+ } else if (Dom.hasClass('short_desc', 'missing')) {
+ alert('Please enter the summary.');
+ } else {
+ alert('Please select the relevant version.\n\n' +
+ 'If you are unsure select "unspecified".');
+ }
+
+ return false;
+ }
+
+ if (Dom.get('data').value && !Dom.get('data_description').value)
+ Dom.get('data_description').value = bugForm._getFilename();
+
+ Dom.get('submit').disabled = true;
+ Dom.get('submit').value = 'Submitting Bug...';
+
+ return true;
+ },
+
+ _initHelp: function(el) {
+ var help_id = el.getAttribute('helpid');
+ if (!el.panel) {
+ if (!el.id)
+ el.id = help_id + '_parent';
+ el.panel = new YAHOO.widget.Panel(
+ help_id,
+ {
+ width: "320px",
+ visible: false,
+ close: false,
+ context: [el.id, 'tl', 'tr', null, [5, 0]]
+ }
+ );
+ el.panel.render();
+ Dom.removeClass(help_id, 'hidden');
+ }
+ },
+
+ showHelp: function(el) {
+ this._initHelp(el);
+ if (this._visibleHelpPanel)
+ this._visibleHelpPanel.hide();
+ el.panel.show();
+ this._visibleHelpPanel = el.panel;
+ },
+
+ hideHelp: function(el) {
+ if (!el.panel)
+ return;
+ if (this._visibleHelpPanel)
+ this._visibleHelpPanel.hide();
+ el.panel.hide();
+ this._visibleHelpPanel = null;
+ }
+}
+
diff --git a/extensions/GuidedBugEntry/web/js/products.js b/extensions/GuidedBugEntry/web/js/products.js
new file mode 100644
index 000000000..cdab695fc
--- /dev/null
+++ b/extensions/GuidedBugEntry/web/js/products.js
@@ -0,0 +1,117 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.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 the GuidedBugEntry Bugzilla Extension.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Byron Jones <glob@mozilla.com>
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/* Product-specifc configuration for guided bug entry
+ *
+ * related: array of product names which will also be searched for duplicates
+ * version: function which returns a version (eg. detected from UserAgent)
+ * support: string which is displayed at the top of the duplicates page
+ * secgroup: the group to place confidential bugs into
+ * detectPlatform: when true the platform and op_sys will be set from the
+ * browser's user agent. when false, these will be set to All
+ */
+
+var products = {
+
+ "Firefox": {
+ related: [ "Core", "Toolkit" ],
+ version: function() {
+ var re = /Firefox\/(\d+)\.(\d+)/i;
+ var match = re.exec(navigator.userAgent);
+ if (match) {
+ var maj = match[1];
+ var min = match[2];
+ if (maj * 1 >= 5) {
+ return maj + " Branch";
+ } else {
+ return maj + "." + min + " Branch";
+ }
+ } else {
+ return false;
+ }
+ },
+ detectPlatform: true,
+ support:
+ 'If you are new to Firefox or Bugzilla, please consider checking ' +
+ '<a href="http://support.mozilla.com/">' +
+ '<img src="extensions/GuidedBugEntry/web/images/sumo.png" width="16" height="16" align="absmiddle">' +
+ ' <b>Firefox Help</b></a> instead of creating a bug.'
+ },
+
+ "Fennec": {
+ related: [ "Core", "Toolkit" ],
+ detectPlatform: true,
+ support:
+ 'If you are new to Firefox or Bugzilla, please consider checking ' +
+ '<a href="http://support.mozilla.com/">' +
+ '<img src="extensions/GuidedBugEntry/web/images/sumo.png" width="16" height="16" align="absmiddle">' +
+ ' <b>Firefox Help</b></a> instead of creating a bug.'
+ },
+
+ "SeaMonkey": {
+ related: [ "Core", "Toolkit" ],
+ detectPlatform: true,
+ version: function() {
+ var re = /SeaMonkey\/(\d+)\.(\d+)/i;
+ var match = re.exec(navigator.userAgent);
+ if (match) {
+ var maj = match[1];
+ var min = match[2];
+ return "SeaMonkey " + maj + "." + min + " Branch";
+ } else {
+ return false;
+ }
+ }
+ },
+
+ "Camino": {
+ related: [ "Core", "Toolkit" ],
+ detectPlatform: true
+ },
+
+ "Core": {
+ detectPlatform: true
+ },
+
+ "Thunderbird": {
+ related: [ "Core", "Toolkit", "MailNews Core" ],
+ detectPlatform: true
+ },
+
+ "Penelope": {
+ related: [ "Core", "Toolkit", "MailNews Core" ]
+ },
+
+ "Bugzilla": {
+ support:
+ 'Please use <a href="http://landfill.bugzilla.org/">Bugzilla Landfill</a> to file "test bugs".'
+ },
+
+ "bugzilla.mozilla.org": {
+ related: [ "Bugzilla" ],
+ support:
+ 'Please use <a href="http://landfill.bugzilla.org/">Bugzilla Landfill</a> to file "test bugs".'
+ }
+}
diff --git a/extensions/GuidedBugEntry/web/style/guided.css b/extensions/GuidedBugEntry/web/style/guided.css
new file mode 100644
index 000000000..f4d8ec90d
--- /dev/null
+++ b/extensions/GuidedBugEntry/web/style/guided.css
@@ -0,0 +1,231 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.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 the GuidedBugEntry Bugzilla Extension.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Byron Jones <glob@mozilla.com>
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/* global */
+
+#page_title {
+}
+
+#page_title h2 {
+ margin-bottom: 0px;
+}
+
+#page_title h3 {
+ margin-top: 0px;
+}
+
+.hidden {
+ display: none;
+}
+
+#yui-history-iframe {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 1px;
+ height: 1px;
+ visibility: hidden;
+}
+
+.step {
+ margin-left: 20px;
+ margin-bottom: 25px;
+}
+
+#steps a img {
+ border: none;
+}
+
+#advanced {
+ margin-top: 50px;
+}
+
+#advanced img {
+ vertical-align: middle;
+}
+
+#advanced a {
+ cursor: pointer;
+}
+
+/* remove the shaded background from data_table header
+ it looks out of place */
+.yui-skin-sam .yui-dt th {
+ background: #f0f0f0;
+}
+
+/* products and other_products step */
+
+.exits {
+ width: 600px;
+ margin-bottom: 10px;
+}
+
+.exits td {
+ padding: 5px;
+}
+
+.exits h2 {
+ margin: 0px;
+ font-size: 90%;
+}
+
+.exit_img {
+ width: 64px;
+ text-align: right;
+}
+
+#products {
+ width: 600px;
+}
+
+#products td {
+ padding: 5px;
+ padding-bottom: 10px;
+}
+
+#products h2 {
+ margin-bottom: 0px;
+}
+
+#products p {
+ margin-top: 0px;
+}
+
+.product_img {
+ width: 64px;
+}
+
+#other_products .classification {
+ font-weight: bold;
+}
+
+#other_products .classification th {
+ font-size: large;
+}
+
+/* duplicates step */
+
+#dupes_summary {
+ width: 500px;
+}
+
+#dupes_list {
+ margin-top: 1em;
+ margin-bottom: 1em;
+}
+
+#product_support {
+ border: 1px solid #dddddd;
+}
+
+/* bug form step */
+
+#bugForm {
+ width: 600px;
+ border: 4px solid #e0e0e0;
+ -moz-border-radius: 5px;
+ border-radius: 5px;
+}
+
+#bugForm th, #bugForm td {
+ padding: 5px;
+}
+
+#bugForm .even th, #bugForm .even td {
+ background: #e0e0e0;
+}
+
+#bugForm .label {
+ text-align: left;
+ font-weight: bold;
+ white-space: nowrap
+}
+
+#bugzilla-body #bugForm th {
+ vertical-align: middle;
+}
+
+#bugForm .textInput {
+ width: 450px;
+}
+
+#bugForm textarea {
+ font-family: Verdana, sans-serif;
+ font-size: small;
+ width: 590px;
+}
+
+#bugForm .mandatory_mark {
+ color: red;
+ font-size: 80%;
+}
+
+#bugForm .mandatory {
+}
+
+#bugForm .textInput[disabled] {
+ background: transparent;
+ border: 1px solid #dddddd;
+}
+
+#versionTD {
+ text-align: right;
+ white-space: nowrap
+}
+
+#component_select {
+ width: 450px;
+}
+
+#component_description {
+ padding: 5px;
+}
+
+#bugForm .missing {
+ border: 1px solid red;
+ box-shadow: 0px 0px 4px #ff0000;
+ -webkit-box-shadow: 0px 0px 4px #ff0000;
+ -moz-box-shadow: 0px 0px 4px #ff0000;
+}
+
+#submitTD {
+ text-align: right;
+}
+
+.help {
+ position: absolute;
+ background: #ffffff;
+ padding: 2px;
+ cursor: default;
+}
+
+.help-bad {
+ color: #990000;
+}
+
+.help-good {
+ color: #009900;
+}
diff --git a/extensions/GuidedBugEntry/web/yui-history-iframe.txt b/extensions/GuidedBugEntry/web/yui-history-iframe.txt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/extensions/GuidedBugEntry/web/yui-history-iframe.txt