diff options
author | David Lawrence <dlawrence@mozilla.com> | 2013-07-25 08:55:15 +0200 |
---|---|---|
committer | Byron Jones <bjones@mozilla.com> | 2013-07-25 08:55:15 +0200 |
commit | 701e41d96429e68d8989b2063d02cd0d20a229ea (patch) | |
tree | c462cc86f7ade39cd11a5b27a279dbb4b4dba48e /extensions/TrackingFlags/web | |
parent | b84db39863cd55166bff584265d255a0f882e64a (diff) | |
download | bugzilla-701e41d96429e68d8989b2063d02cd0d20a229ea.tar.gz bugzilla-701e41d96429e68d8989b2063d02cd0d20a229ea.tar.xz |
Bug 750742: Create new BMO extension called TrackingFlags to move current tracking flags away from custom fields
Diffstat (limited to 'extensions/TrackingFlags/web')
-rw-r--r-- | extensions/TrackingFlags/web/js/admin.js | 384 | ||||
-rw-r--r-- | extensions/TrackingFlags/web/js/tracking_flags.js | 56 | ||||
-rw-r--r-- | extensions/TrackingFlags/web/styles/admin.css | 107 | ||||
-rw-r--r-- | extensions/TrackingFlags/web/styles/edit_bug.css | 18 |
4 files changed, 565 insertions, 0 deletions
diff --git a/extensions/TrackingFlags/web/js/admin.js b/extensions/TrackingFlags/web/js/admin.js new file mode 100644 index 000000000..461ef7c63 --- /dev/null +++ b/extensions/TrackingFlags/web/js/admin.js @@ -0,0 +1,384 @@ +/* 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. */ + +// init + +var Dom = YAHOO.util.Dom; +var Event = YAHOO.util.Event; + +Event.onDOMReady(function() { + try { + if (!JSON) + JSON = YAHOO.lang.JSON; + + Event.addListener('flag_name', 'change', change_flag_name, Dom.get('flag_name')); + Event.addListener('flag_desc', 'change', change_string_value, Dom.get('flag_desc')); + Event.addListener('flag_type', 'change', change_select_value, Dom.get('flag_type')); + Event.addListener('flag_sort', 'change', change_int_value, Dom.get('flag_sort')); + + Event.addListener('product', 'change', function() { + if (Dom.get('product').value == '') + Dom.get('component').options.length = 0; + }); + + update_flag_values(); + update_flag_visibility(); + tag_missing_values(); + } catch(e) { + console.error(e); + } +}); + +// field + +function change_flag_name(e, o) { + change_string_value(e, o); + if (o.value == '') + return; + o.value = o.value.replace(/[^a-z0-9_]/g, '_'); + if (!o.value.match(/^cf_/)) + o.value = 'cf_' + o.value; + if (Dom.get('flag_desc').value == '') { + var desc = o.value; + desc = desc.replace(/^cf_/, ''); + desc = desc.replace(/_/g, '-'); + Dom.get('flag_desc').value = desc; + tag_missing_value(Dom.get('flag_desc')); + } +} + +function inc_field(id, amount) { + var el = Dom.get(id); + el.value = el.value.match(/-?\d+/) * 1 + amount; + change_int_value(null, el); +} + +// values + +function update_flag_values() { + // update the values table from the flag_values global + + var tbl = Dom.get('flag_values'); + if (!tbl) + return; + + // remove current entries + while (tbl.rows.length > 3) { + tbl.deleteRow(2); + } + + // add all entries + + for (var i = 0, l = flag_values.length; i < l; i++) { + var value = flag_values[i]; + + var row = tbl.insertRow(2 + i); + var cell; + + // value + cell = row.insertCell(0); + if (value.value == '---') { + cell.innerHTML = '---'; + } else { + var inputEl = document.createElement('input'); + inputEl.id = 'value_' + i; + inputEl.type = 'text'; + inputEl.className = 'option_value'; + inputEl.value = value.value; + Event.addListener(inputEl, 'change', change_string_value, inputEl); + Event.addListener(inputEl, 'change', function(e, o) { + flag_values[o.id.match(/\d+$/)].value = o.value; + tag_invalid_values(); + }, inputEl); + Event.addListener(inputEl, 'keyup', function(e, o) { + if ((e.key || e.keyCode) == 27 && o.value == '') + remove_value(o.id.match(/\d+$/)); + }, inputEl); + cell.appendChild(inputEl); + } + + // setter + cell = row.insertCell(1); + var selectEl = document.createElement('select'); + selectEl.id = 'setter_' + i; + Event.addListener(selectEl, 'change', change_select_value, selectEl); + var optionEl = document.createElement('option'); + optionEl.value = ''; + selectEl.appendChild(optionEl); + for (var j = 0, m = groups.length; j < m; j++) { + var group = groups[j]; + optionEl = document.createElement('option'); + optionEl.value = group.id; + optionEl.innerHTML = YAHOO.lang.escapeHTML(group.name); + optionEl.selected = group.id == value.setter_group_id; + selectEl.appendChild(optionEl); + } + Event.addListener(selectEl, 'change', function(e, o) { + flag_values[o.id.match(/\d+$/)].setter_group_id = o.value; + tag_invalid_values(); + }, selectEl); + cell.appendChild(selectEl); + + // active + cell = row.insertCell(2); + if (value.value == '---') { + cell.innerHTML = 'Yes'; + } else { + var inputEl = document.createElement('input'); + inputEl.type = 'checkbox'; + inputEl.id = 'is_active_' + i; + inputEl.checked = value.is_active; + Event.addListener(inputEl, 'change', function(e, o) { + flag_values[o.id.match(/\d+$/)].is_active = o.checked; + }, inputEl); + cell.appendChild(inputEl); + } + + // actions + cell = row.insertCell(3); + var html = + '[' + + (i == 0 + ? '<span class="txt_icon"> - </span>' + : '<a class="txt_icon" href="#" onclick="value_move_up(' + i + ');return false"> Δ </a>' + ) + + '|' + + ( i == l - 1 + ? '<span class="txt_icon"> - </span>' + : '<a class="txt_icon" href="#" onclick="value_move_down(' + i + ');return false"> ∇ </a>' + ); + if (value.value != '---') + html += '| <a href="#" onclick="remove_value(' + i + ');return false">Remove</a>'; + html += ']'; + cell.innerHTML = html; + } + + tag_invalid_values(); +} + +function tag_invalid_values() { + // reset + for (var i = 0, l = flag_values.length; i < l; i++) { + Dom.removeClass('value_' + i, 'admin_error'); + } + + for (var i = 0, l = flag_values.length; i < l; i++) { + // missing + if (flag_values[i].value == '') + Dom.addClass('value_' + i, 'admin_error'); + if (!flag_values[i].setter_group_id) + Dom.addClass('setter_' + i, 'admin_error'); + + // duplicate values + for (var j = i; j < l; j++) { + if (i != j && flag_values[i].value == flag_values[j].value) { + Dom.addClass('value_' + i, 'admin_error'); + Dom.addClass('value_' + j, 'admin_error'); + } + } + } +} + +function value_move_up(idx) { + if (idx == 0) + return; + var tmp = flag_values[idx]; + flag_values[idx] = flag_values[idx - 1]; + flag_values[idx - 1] = tmp; + update_flag_values(); +} + +function value_move_down(idx) { + if (idx == flag_values.length - 1) + return; + var tmp = flag_values[idx]; + flag_values[idx] = flag_values[idx + 1]; + flag_values[idx + 1] = tmp; + update_flag_values(); +} + +function add_value() { + var value = new Object(); + value.id = 0; + value.value = ''; + value.setter_group_id = ''; + value.is_active = true; + var idx = flag_values.length; + flag_values[idx] = value; + update_flag_values(); + Dom.get('value_' + idx).focus(); +} + +function remove_value(idx) { + flag_values.splice(idx, 1); + update_flag_values(); +} + +function update_value(e, o) { + var i = o.value.match(/\d+/); + flag_values[i].value = o.value; +} + +// visibility + +function update_flag_visibility() { + // update the visibility table from the flag_visibility global + + var tbl = Dom.get('flag_visibility'); + if (!tbl) + return; + + // remove current entries + while (tbl.rows.length > 3) { + tbl.deleteRow(2); + } + + // show something if there aren't any components + + if (!flag_visibility.length) { + var row = tbl.insertRow(2); + var cell = row.insertCell(0); + cell.innerHTML = '<i class="admin_error_text">missing</i>'; + } + + // add all entries + + for (var i = 0, l = flag_visibility.length; i < l; i++) { + var visibility = flag_visibility[i]; + + var row = tbl.insertRow(2 + i); + var cell; + + // product + cell = row.insertCell(0); + cell.innerHTML = visibility.product; + + // component + cell = row.insertCell(1); + cell.innerHTML = visibility.component + ? visibility.component + : '<i>-- Any --</i>'; + + // actions + cell = row.insertCell(2); + cell.innerHTML = '[ <a href="#" onclick="remove_visibility(' + i + ');return false">Remove</a> ]'; + } +} + +function add_visibility() { + // validation + var product = Dom.get('product').value; + var component = Dom.get('component').value; + if (!product) { + alert('Please select a product.'); + return; + } + + // don't allow duplicates + for (var i = 0, l = flag_visibility.length; i < l; i++) { + if (flag_visibility[i].product == product && flag_visibility[i].component == component) { + Dom.get('product').value = ''; + Dom.get('component').options.length = 0; + return; + } + } + + if (component == '') { + // if we're adding an "any" component, remove non-any components + for (var i = 0; i < flag_visibility.length; i++) { + var visibility = flag_visibility[i]; + if (visibility.product == product) { + flag_visibility.splice(i, 1); + i--; + } + } + } else { + // don't add non-any components if an "any" component exists + for (var i = 0, l = flag_visibility.length; i < l; i++) { + var visibility = flag_visibility[i]; + if (visibility.product == product && !visibility.component) { + return; + } + } + } + + // add to model + var visibility = new Object(); + visibility.id = 0; + visibility.product = product; + visibility.component = component; + flag_visibility[flag_visibility.length] = visibility; + + // update ui + update_flag_visibility(); + Dom.get('product').value = ''; + Dom.get('component').options.length = 0; +} + +function remove_visibility(idx) { + flag_visibility.splice(idx, 1); + update_flag_visibility(); +} + +// validation and submission + +function tag_missing_values() { + var els = document.getElementsByTagName('input'); + for (var i = 0, l = els.length; i < l; i++) { + var el = els[i]; + if (el.id.match(/^(flag|value)_/)) + tag_missing_value(el); + } + tag_missing_value(Dom.get('flag_type')); +} + +function tag_missing_value(el) { + el.value == '' + ? Dom.addClass(el, 'admin_error') + : Dom.removeClass(el, 'admin_error'); +} + +function delete_confirm(flag) { + if (confirm('Are you sure you want to delete the flag ' + flag + ' ?')) { + Dom.get('delete').value = 1; + return true; + } else { + return false; + } +} + +function on_submit() { + if (Dom.get('delete') && Dom.get('delete').value) + return; + // let perl manage most validation errors, because they are clearly marked + // the exception is an empty visibility list, so catch that here as well + if (!flag_visibility.length) { + alert('You must provide at least one product for visibility.'); + return false; + } + + Dom.get('values').value = JSON.stringify(flag_values); + Dom.get('visibility').value = JSON.stringify(flag_visibility); + return true; +} + +// utils + +function change_string_value(e, o) { + o.value = YAHOO.lang.trim(o.value); + tag_missing_value(o); +} + +function change_int_value(e, o) { + o.value = o.value.match(/-?\d+/); + tag_missing_value(o); +} + +function change_select_value(e, o) { + tag_missing_value(o); +} + diff --git a/extensions/TrackingFlags/web/js/tracking_flags.js b/extensions/TrackingFlags/web/js/tracking_flags.js new file mode 100644 index 000000000..135b93dba --- /dev/null +++ b/extensions/TrackingFlags/web/js/tracking_flags.js @@ -0,0 +1,56 @@ +/* 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; + +var TrackingFlags = { + flags: {}, + types: [] +}; + +function hide_tracking_flags() { + for (var i = 0, l = TrackingFlags.types.length; i < l; i++) { + var flag_type = TrackingFlags.types[i]; + for (var field in TrackingFlags.flags[flag_type]) { + var el = Dom.get(field); + var value = el ? el.value : TrackingFlags.flags[flag_type][field]; + if (el && (value != TrackingFlags.flags[flag_type][field])) { + show_tracking_flags(flag_type); + return; + } + if (value == '---') { + Dom.addClass('row_' + field, 'bz_default_hidden'); + } else { + Dom.addClass(field, 'bz_default_hidden'); + Dom.removeClass('ro_' + field, 'bz_default_hidden'); + } + } + } +} + +function show_tracking_flags(flag_type) { + Dom.addClass('edit_' + flag_type + '_flags_action', 'bz_default_hidden'); + for (var field in TrackingFlags.flags[flag_type]) { + if (Dom.get(field).value == '---') { + Dom.removeClass('row_' + field, 'bz_default_hidden'); + } else { + Dom.removeClass(field, 'bz_default_hidden'); + Dom.addClass('ro_' + field, 'bz_default_hidden'); + } + } +} + +YAHOO.util.Event.onDOMReady(function() { + var edit_tracking_links = Dom.getElementsByClassName('edit_tracking_flags_link'); + for (var i = 0, l = edit_tracking_links.length; i < l; i++) { + YAHOO.util.Event.addListener(edit_tracking_links[i], 'click', function(e) { + e.preventDefault(); + show_tracking_flags(this.name); + }); + } +}); diff --git a/extensions/TrackingFlags/web/styles/admin.css b/extensions/TrackingFlags/web/styles/admin.css new file mode 100644 index 000000000..374409ce6 --- /dev/null +++ b/extensions/TrackingFlags/web/styles/admin.css @@ -0,0 +1,107 @@ +/* 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. */ + +/* list */ + +.list { + border: 1px solid #888888; +} + +.list td, .list th { + padding: 3px 10px 3px 3px; + border: 1px solid #888888; +} + +.list .odd_row { + background-color: #ffffff; + color: #000000; +} + +.list .even_row { + background-color: #eeeeee; + color: #000000; +} + +.list tr:hover { + background-color: #ccddee; +} + + +.list th { + text-align: left; + background: #dddddd; +} + +.list .disabled { + color: #888888; + text-decoration: line-through; +} + +#new_flag { + margin: 1em 0em; +} + +/* edit */ + +.edit { + margin-bottom: 2em; +} + +.edit .header { + background: #dddddd; +} + +.edit .help { + font-style: italic; +} + +.edit td, .edit th { + padding: 1px 5px; +} + +.edit th { + text-align: left; +} + +#edit_mode { + margin: 1em 0em; +} + +#flag_name { + width: 20em; +} + +#flag_desc { + width: 20em; +} + +#flag_sort { + width: 10em; +} + +.option_value { + width: 10em; +} + +.hidden { + display: none; +} + +.txt_icon { + font-family: monospace; +} + +.admin_error { + border: 1px solid red; + box-shadow: 0px 0px 4px #ff0000; + -webkit-box-shadow: 0px 0px 4px #ff0000; + -moz-box-shadow: 0px 0px 4px #ff0000; +} + +.admin_error_text { + color: #cc0000; +} diff --git a/extensions/TrackingFlags/web/styles/edit_bug.css b/extensions/TrackingFlags/web/styles/edit_bug.css new file mode 100644 index 000000000..132a6a1ca --- /dev/null +++ b/extensions/TrackingFlags/web/styles/edit_bug.css @@ -0,0 +1,18 @@ +/* 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. */ + +.tracking_flags { + width: auto !important; +} + +.tracking_flags .field_label { + font-weight: normal !important; +} + +#Create .tracking_flags th { + text-align: left; +} |