launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #03175
[Merge] lp:~benji/launchpad/better-HTML-generation into lp:launchpad
Benji York has proposed merging lp:~benji/launchpad/better-HTML-generation into lp:launchpad.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~benji/launchpad/better-HTML-generation/+merge/55960
This branch fixes the way structural subscription JavaScript constructs HTML, moving away from string concatenation to Y.Node.create() and friends.
There's also a fair bit of lint fixing required to quite JSLint.
--
https://code.launchpad.net/~benji/launchpad/better-HTML-generation/+merge/55960
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~benji/launchpad/better-HTML-generation into lp:launchpad.
=== modified file 'lib/lp/registry/javascript/structural-subscription.js'
--- lib/lp/registry/javascript/structural-subscription.js 2011-03-30 15:23:42 +0000
+++ lib/lp/registry/javascript/structural-subscription.js 2011-04-01 16:08:35 +0000
@@ -11,9 +11,6 @@
var namespace = Y.namespace('lp.registry.structural_subscription');
-var INNER_HTML = 'innerHTML',
- VALUE = 'value';
-
var FILTER_COMMENTS = 'filter-comments',
FILTER_WRAPPER = 'filter-wrapper',
ACCORDION_WRAPPER = 'accordion-wrapper',
@@ -21,8 +18,7 @@
ADDED_OR_CHANGED = 'added-or-changed',
ADVANCED_FILTER = 'advanced-filter',
MATCH_ALL = 'match-all',
- MATCH_ANY = 'match-any',
- SS_COLLAPSIBLE = 'ss-collapsible'
+ MATCH_ANY = 'match-any'
;
var add_subscription_overlay;
@@ -61,7 +57,7 @@
function list_contains(list, target) {
// The list may be undefined in some cases.
return Y.Lang.isArray(list) && list.indexOf(target) !== -1;
-};
+}
// Expose to tests.
namespace._list_contains = list_contains;
@@ -209,8 +205,9 @@
function save_subscription(form_data) {
var who;
var has_errors = check_for_errors_in_overlay(add_subscription_overlay);
- if (has_errors)
+ if (has_errors) {
return false;
+ }
if (form_data.recipient[0] === 'user') {
who = LP.links.me;
} else {
@@ -223,15 +220,18 @@
function check_for_errors_in_overlay(overlay) {
var has_errors = false;
- var errors = new Array();
- for (var field in overlay.field_errors) {
- if (overlay.field_errors[field]) {
- has_errors = true;
- errors.push(field);
+ var errors = [];
+ var field;
+ for (field in overlay.field_errors) {
+ if (overlay.field_errors.hasOwnProperty(field)) {
+ if (overlay.field_errors[field]) {
+ has_errors = true;
+ errors.push(field);
+ }
}
- };
+ }
if (has_errors) {
- var error_text = errors.pop()
+ var error_text = errors.pop();
if (errors.length > 0) {
error_text = errors.join(', ') + ' and ' + error_text;
}
@@ -242,40 +242,47 @@
} else {
return false;
}
-};
+}
/**
* Handle the activation of the edit subscription link.
*/
function edit_subscription_handler(context, form_data) {
var has_errors = check_for_errors_in_overlay(add_subscription_overlay);
- if (has_errors)
+ var filter_id = '#filter-description-'+context.filter_id.toString();
+ if (has_errors) {
return false;
+ }
var on = {success: function (new_data) {
var filter = new_data.getAttrs();
- var description_node = Y.one(
- '#filter-description-'+context.filter_id.toString());
- description_node.set(
- INNER_HTML, render_filter_description(filter));
- var name_node = Y.one(
- '#filter-name-'+context.filter_id.toString());
- name_node.set(
- INNER_HTML, render_filter_name(context.filter_info, filter));
+ var description_node = Y.one(filter_id);
+ description_node
+ .empty()
+ .appendChild(create_filter_description(filter));
+ description_node.ancestor('.subscription-filter').one('.filter-name')
+ .empty()
+ .appendChild(render_filter_title(context.filter_info, filter));
add_subscription_overlay.hide();
}};
patch_bug_filter(context.filter_info.filter, form_data, on);
}
+/**
+ * Ititilize the overlay errors and set up field validators.
+ */
function setup_overlay_validators(overlay, overlay_id) {
+<<<<<<< TREE
overlay.field_validators = {
tags: get_error_for_tags_list
};
+=======
+>>>>>>> MERGE-SOURCE
overlay.field_errors = {};
- for (var field_name in overlay.field_validators) {
- add_input_validator(overlay, overlay_id, field_name,
- overlay.field_validators[field_name]);
- };
-};
+ add_input_validator(overlay, overlay_id,
+ 'name', get_error_for_subscription_name);
+ add_input_validator(overlay, overlay_id,
+ 'tags', get_error_for_tags_list);
+}
/**
* Populate the overlay element with the contents of the add/edit form.
@@ -295,8 +302,9 @@
form_submit_callback: function(formdata) {
// Do not clean up if saving was not successful.
var save_succeeded = submit_callback(formdata);
- if (save_succeeded !== false)
+ if (save_succeeded !== false) {
clean_up();
+ }
}
});
add_subscription_overlay.render(content_box_id);
@@ -373,10 +381,14 @@
* @param {String} name Name of the control.
*/
function make_cell(item, name) {
- return '<td style="padding-left:3px"><label><input type="checkbox" ' +
- 'name="' + name +'" ' +
- 'value="' + item + '" checked="checked">' +
- item + '</label><td>';
+ var cell = Y.Node.create('<td style="padding-left:3px"><td>');
+ cell.appendChild('<label></label>')
+ .append(Y.Node.create('<input type="checkbox" checked></input>')
+ .set('name', name)
+ .set('value', item))
+ .append(Y.Node.create('<span></span>')
+ .set('text', item));
+ return cell;
}
/**
* Make a table.
@@ -388,19 +400,15 @@
* @param {Int} num_cols The number of columns for the table to use.
*/
function make_table(list, name, num_cols) {
- var html = '<table>';
- var i;
+ var table = Y.Node.create('<table></table>');
+ var i, row;
for (i=0; i<list.length; i++) {
if (i % num_cols === 0) {
- if (i !== 0) {
- html += '</tr>';
- }
- html += '<tr>';
+ row = table.appendChild('<tr></tr>');
}
- html += make_cell(list[i], name);
+ row.appendChild(make_cell(list[i], name));
}
- html += '</tr></table>';
- return html;
+ return table;
}
/**
@@ -413,16 +421,18 @@
* @return {Object} Hash with 'all_name', 'none_name', and 'html' keys.
*/
function make_selector_controls(parent) {
+ var selectors_id = parent + '-selectors';
var rv = {};
- rv.all_name = parent + '-select-all';
- rv.none_name = parent + '-select-none';
- rv.html = '<div id="'+ parent + '-selectors" '+
- 'style="margin-left: 10px;margin-bottom: 10px">' +
- ' <a href="#" id="' + rv.all_name +
- '">Select all</a> ' +
- ' <a href="#" id="' + rv.none_name +
- '">Select none</a>' +
- '</div>';
+ rv.all_link = Y.Node.create(
+ '<a href="#" class="select-all">Select all</a>');
+ rv.none_link = Y.Node.create(
+ '<a href="#" class="select-none">Select none</a>');
+ rv.node = Y.Node.create(
+ '<div style="margin-left: 10px;margin-bottom: 10px"></div>')
+ .set('id', selectors_id)
+ .append(rv.all_link)
+ .append(' ')
+ .append(rv.none_link);
return rv;
}
@@ -474,27 +484,23 @@
id: "tags_ai",
contentHeight: {method: "auto"}
} );
-
tags_container = tags_ai;
-
- tags_ai.set("bodyContent",
- '<div>\n' +
- '<div>\n' +
- ' <input type="radio" name="tag_match" value="' +
- MATCH_ALL + '" checked> Match all tags\n' +
- ' <input type="radio" name="tag_match" value="' +
- MATCH_ANY + '"> Match any tags\n' +
- '</div>\n' +
- '<div style="padding-bottom:10px;">\n' +
- ' <input type="text" name="tags" size="60"/>\n' +
+ tags_ai.set("bodyContent", Y.Node.create('<div><div></div></div>')
+ .append(Y.Node.create('<input type="radio" name="tag_match" checked>')
+ .set('value', MATCH_ALL))
+ .append('Match all tags')
+ .append(Y.Node.create('<input type="radio" name="tag_match" checked>')
+ .set('value', MATCH_ANY))
+ .append('Match any tags')
+ .append(
+ '<div style="padding-bottom:10px;">' +
+ ' <input type="text" name="tags" size="60"/>' +
' <a target="help"'+
' href="/+help/structural-subscription-tags.html" ' +
' class="sprite maybe"> '+
'<span class="invisible-link">Structural subscription tags '+
- ' help</span></a>\n ' +
- '</div>\n' +
- '</div>\n');
-
+ ' help</span></a> ' +
+ '</div>'));
accordion.addItem(tags_ai);
// Build importances pane.
@@ -507,20 +513,17 @@
} );
var importances = LP.cache.importances;
var selectors = make_selector_controls('importances');
- var importances_html = '<div id="importances-wrapper">' +
- selectors.html +
- make_table(importances, 'importances', 4) +
- '</div>';
- importances_ai.set("bodyContent", importances_html);
+ importances_ai.set("bodyContent",
+ Y.Node.create('<div id="importances-wrapper"></div>')
+ .append(selectors.node)
+ .append(make_table(importances, 'importances', 4)));
accordion.addItem(importances_ai);
// Wire up the 'all' and 'none' selectors.
- var all_link = content_node.one('#' + selectors.all_name);
- var none_link = Y.one('#' + selectors.none_name);
var node = content_node.one('#importances-wrapper');
- var select_all_handler = make_select_handler(node, importances, true);
- var select_none_handler = make_select_handler(node, importances, false);
- all_link.on('click', select_all_handler);
- none_link.on('click', select_none_handler);
+ selectors.all_link.on('click',
+ make_select_handler(node, importances, true));
+ selectors.none_link.on('click',
+ make_select_handler(node, importances, false));
// Build statuses pane.
statuses_ai = new Y.AccordionItem( {
@@ -532,18 +535,17 @@
} );
var statuses = LP.cache.statuses;
selectors = make_selector_controls('statuses');
- var status_html = '<div id="statuses-wrapper">' +
- selectors.html + make_table(statuses, 'statuses', 3)+
- '</div>';
- statuses_ai.set("bodyContent", status_html);
+ statuses_ai.set("bodyContent",
+ Y.Node.create('<div id="statuses-wrapper"></div>')
+ .append(selectors.node)
+ .append(make_table(statuses, 'statuses', 3)));
accordion.addItem(statuses_ai);
- all_link = content_node.one('#' + selectors.all_name);
- none_link = Y.one('#' + selectors.none_name);
+ // Wire up the 'all' and 'none' selectors.
node = content_node.one('#statuses-wrapper');
- select_all_handler = make_select_handler(node, statuses, true);
- select_none_handler = make_select_handler(node, statuses, false);
- all_link.on('click', select_all_handler);
- none_link.on('click', select_none_handler);
+ selectors.all_link.on('click',
+ make_select_handler(node, statuses, true));
+ selectors.none_link.on('click',
+ make_select_handler(node, statuses, false));
return accordion;
}
@@ -624,105 +626,125 @@
}
/**
+ * Add a recipient picker to the overlay.
+ */
+function add_recipient_picker(content_box, hide) {
+ var no_recipient_picker = Y.Node.create(
+ '<input type="hidden" name="recipient" value="user">' +
+ '<span>Yourself</span>');
+ var recipient_picker = Y.Node.create(
+ '<label><input type="radio" name="recipient" value="user" checked>' +
+ ' Yourself</label><br>' +
+ '<label><input type="radio" name="recipient" value="team">' +
+ ' One of the teams you administer</label><br>' +
+ '<dl style="margin-left:25px;">' +
+ ' <dt></dt>' +
+ ' <dd>' +
+ ' <select name="team" id="structural-subscription-teams">' +
+ ' </select>' +
+ ' </dd>' +
+ '</dl>');
+ var teams = LP.cache.administratedTeams;
+ var node = content_box.one('#bug-mail-recipient');
+ node.empty();
+ // Populate the team drop down from LP.cache data, if appropriate.
+ if (!hide && teams.length > 0) {
+ var select = recipient_picker.one('#structural-subscription-teams');
+ var i;
+ for (i=0; i<teams.length; i++) {
+ select.append(Y.Node.create('<option></option>')
+ .set('text', teams[i].title)
+ .set('value', teams[i].link));
+ }
+ select.on(
+ 'focus',
+ function () {
+ Y.one('input[value="team"][name="recipient"]').set(
+ 'checked', true);
+ }
+ );
+ node.append(recipient_picker);
+ } else {
+ node.append(no_recipient_picker);
+ }
+}
+
+/**
* Construct the overlay and populate it with the add/edit form.
*/
function setup_overlay(content_box_id, hide_recipient_picker) {
var content_node = Y.one(content_box_id);
- var container = Y.Node.create('<div id="overlay-container"></div>');
- var accordion_overlay_id = 'accordion-overlay';
- var teams = LP.cache.administratedTeams;
- var no_recipient_picker =
- ' <input type="hidden" name="recipient" value="user">\n' +
- ' <span>Yourself</span>\n',
- recipient_picker =
- ' <input type="radio" name="recipient" value="user"\n'+
- ' id="structural-subscription-recipient-user" checked>\n'+
- ' <label for="structural-subscription-recipient-user">\n'+
- ' Yourself</label><br>\n' +
- ' <input type="radio" name="recipient"\n'+
- ' id="structural-subscription-recipient-team"\n'+
- ' value="team">\n'+
- ' <label for="structural-subscription-recipient-team">One of\n'+
- ' the teams you administer</label><br>\n' +
- ' <dl style="margin-left:25px;">\n' +
- ' <dt></dt>\n' +
- ' <dd>\n' +
- ' <select name="team" id="structural-subscription-teams">\n'+
- ' </select>\n' +
- ' </dd>\n' +
- ' </dl>\n',
- control_code =
- '<dl>\n' +
- ' <dt>Bug mail recipient</dt>\n' +
- ' <dd>\n' +
- ((!hide_recipient_picker && teams.length > 0) ?
- recipient_picker : no_recipient_picker) +
- ' </dd>\n' +
- ' <dt>Subscription name</dt>\n' +
- ' <dd>\n' +
- ' <input type="text" name="name">\n' +
- ' <a target="help" class="sprite maybe"\n' +
- ' href="/+help/structural-subscription-name.html"> \n' +
- ' <span class="invisible-link">Structural subscription\n'+
- ' description help</span></a>\n ' +
- ' </dd>\n' +
- ' <dt>Receive mail for bugs affecting\n'+
- ' <span id="structural-subscription-context-title">\n'+
- ' '+LP.cache.context.title+'</span> that</dt>\n' +
- ' <dd>\n' +
- ' <div id="events">\n' +
- ' <input type="radio" name="events"\n' +
- ' value="' + ADDED_OR_CLOSED + '"\n'+
- ' id="' + ADDED_OR_CLOSED + '" checked>\n'+
- ' <label for="'+ADDED_OR_CLOSED+'">are added or '+
- ' closed</label>\n'+
- ' <br>\n' +
- ' <input type="radio" name="events"\n'+
- ' value="' + ADDED_OR_CHANGED + '"\n' +
- ' id="' + ADDED_OR_CHANGED + '">\n'+
- ' <label for="'+ADDED_OR_CHANGED+'">are added or changed in\n'+
- ' any way\n'+
- ' <em id="'+ADDED_OR_CHANGED+'-more">(more options...)</em>\n'+
- ' </label>\n' +
- ' </div>\n' +
- ' <div id="' + FILTER_WRAPPER + '" class="ss-collapsible">\n' +
- ' <dl style="margin-left:25px;">\n' +
- ' <dt></dt>\n' +
- ' <dd>\n' +
- ' <input type="checkbox" name="filters"\n' +
- ' value="' + FILTER_COMMENTS + '"\n'+
- ' id="'+FILTER_COMMENTS+'">\n' +
- ' <label for="'+FILTER_COMMENTS+'">Don\'t send mail about\n'+
- ' comments</label><br>\n' +
- ' <input type="checkbox" name="filters"\n' +
- ' value="' + ADVANCED_FILTER + '"\n' +
- ' id="' + ADVANCED_FILTER + '">\n' +
- ' <label for="'+ADVANCED_FILTER+'">Bugs must match this\n'+
- ' filter <em id="'+ADVANCED_FILTER+'-more">(...)</em>\n'+
- ' </label><br>\n' +
- ' <div id="' + ACCORDION_WRAPPER + '" \n' +
- ' class="' + SS_COLLAPSIBLE + '">\n' +
- ' <dl>\n' +
- ' <dt></dt>\n' +
- ' <dd style="margin-left:25px;">\n' +
- ' <div id="' + accordion_overlay_id + '"\n' +
- ' style="position:relative; '+
- 'overflow:hidden;"></div>\n' +
- ' </dd>\n' +
- ' </dl>\n' +
- ' </div> \n' +
- ' </dd>\n' +
- ' </dl>\n' +
- ' </div> \n' +
- ' </dd>\n' +
- ' <dt></dt>\n' +
- '</dl>';
-
- content_node.appendChild(container);
- container.appendChild(Y.Node.create(control_code));
-
- var accordion = create_accordion(
- '#' + accordion_overlay_id, content_node);
+ var container = Y.Node.create(
+ '<div id="overlay-container"><dl>' +
+ ' <dt>Bug mail recipient</dt>' +
+ ' <dd id="bug-mail-recipient">' +
+ ' </dd>' +
+ ' <dt>Subscription name</dt>' +
+ ' <dd>' +
+ ' <input type="text" name="name">' +
+ ' <a target="help" class="sprite maybe"' +
+ ' href="/+help/structural-subscription-name.html"> ' +
+ ' <span class="invisible-link">Structural subscription' +
+ ' description help</span></a> ' +
+ ' </dd>' +
+ ' <dt>Receive mail for bugs affecting' +
+ ' <span id="structural-subscription-context-title"></span> that</dt>' +
+ ' <dd>' +
+ ' <div id="events">' +
+ ' <input type="radio" name="events"' +
+ ' value="added-or-closed"' +
+ ' id="added-or-closed" checked>' +
+ ' <label for="added-or-closed">are added or ' +
+ ' closed</label>' +
+ ' <br>' +
+ ' <input type="radio" name="events"' +
+ ' value="added-or-changed"' +
+ ' id="added-or-changed">' +
+ ' <label for="added-or-changed">are added or changed in' +
+ ' any way' +
+ ' <em id="added-or-changed-more">(more options...)</em>' +
+ ' </label>' +
+ ' </div>' +
+ ' <div id="filter-wrapper" class="ss-collapsible">' +
+ ' <dl style="margin-left:25px;">' +
+ ' <dt></dt>' +
+ ' <dd>' +
+ ' <input type="checkbox" name="filters"' +
+ ' value="filter-comments"' +
+ ' id="filter-comments">' +
+ ' <label for="filter-comments">Don\'t send mail about' +
+ ' comments</label><br>' +
+ ' <input type="checkbox" name="filters"' +
+ ' value="advanced-filter"' +
+ ' id="advanced-filter">' +
+ ' <label for="advanced-filter">Bugs must match this' +
+ ' filter <em id="advanced-filter-more">(...)</em>' +
+ ' </label><br>' +
+ ' <div id="accordion-wrapper" ' +
+ ' class="ss-collapsible">' +
+ ' <dl>' +
+ ' <dt></dt>' +
+ ' <dd style="margin-left:25px;">' +
+ ' <div id="accordion-overlay"' +
+ ' style="position:relative; overflow:hidden;"></div>' +
+ ' </dd>' +
+ ' </dl>' +
+ ' </div> ' +
+ ' </dd>' +
+ ' </dl>' +
+ ' </div> ' +
+ ' </dd>' +
+ ' <dt></dt>' +
+ '</dl></div>');
+
+ // Assemble some nodes and set the title.
+ content_node
+ .appendChild(container)
+ .one('#structural-subscription-context-title')
+ .set('text', LP.cache.context.title);
+
+ var accordion = create_accordion('#accordion-overlay', content_node);
+ add_recipient_picker(container, hide_recipient_picker);
// Set up click handlers for the events radio buttons.
var radio_group = Y.all('#events input');
@@ -735,26 +757,6 @@
advanced_filter.on(
'change',
function() {handle_change(ADVANCED_FILTER, ACCORDION_WRAPPER);});
- // Populate the team drop down from LP.cache data, if appropriate.
- if (!hide_recipient_picker && teams.length > 0) {
- var select = Y.one('#structural-subscription-teams');
- var i;
- var team;
- for (i=0; i<teams.length; i++) {
- team = teams[i];
- var option = Y.Node.create('<option></option>');
- option.set(INNER_HTML, team.title);
- option.set(VALUE, team.link);
- select.appendChild(option);
- }
- select.on(
- 'focus',
- function () {
- Y.one('input[value="team"][name="recipient"]').set(
- 'checked', true);
- }
- );
- }
return '#' + container._node.id;
} // setup_overlay
// Expose in the namespace for testing purposes.
@@ -807,7 +809,7 @@
var overlay_id = setup_overlay(config.content_box, true);
var submit_button = Y.Node.create(
'<button type="submit" name="field.actions.create" ' +
- 'value="Save Changes" class="lazr-pos lazr-btn" '+
+ 'value="Save Changes" class="lazr-pos lazr-btn" ' +
'>OK</button>');
// This is a bit of an odd approach, but it lets us retrofit code
// without a large refactoring. When edit_subscription_handler is
@@ -848,8 +850,9 @@
overlay.field_errors[field_name] = true;
// Accordion sets fixed height for the accordion item,
// so we have to resize the tags container.
- if (field_name == 'tags')
+ if (field_name === 'tags') {
tags_container.resize();
+ }
// Firefox prohibits focus from inside the 'focus lost' event
// handler (probably to stop loops), so we need to run
// it from a different context (which we do with setTimeout).
@@ -859,8 +862,23 @@
overlay.field_errors[field_name] = false;
}
});
-};
-
+}
+
+<<<<<<< TREE
+=======
+function get_error_for_subscription_name(value) {
+ if (value.search(/[<>"&]/) === -1) {
+ return null;
+ } else {
+ return ('Subscription name cannot contain any of the following ' +
+ 'characters: < > " &');
+ }
+}
+
+// Export for testing
+namespace._get_error_for_subscription_name = get_error_for_subscription_name;
+
+>>>>>>> MERGE-SOURCE
function get_error_for_tags_list(value) {
// See database/schema/trusted.sql valid_name() function
// which is used to validate a single tag.
@@ -874,7 +892,8 @@
'digits 0-9 and symbols "+", "-" or ".", and they ' +
'must start with a lowercase letter or a digit.');
}
-};
+}
+
// Export for testing
namespace._get_error_for_tags_list = get_error_for_tags_list;
@@ -956,12 +975,12 @@
var i;
for (i=0; i<teams.length; i++) {
if (teams[i].link === filter_info.subscriber_link){
- recipient_label.set(INNER_HTML, teams[i].title);
+ recipient_label.set('text', teams[i].title);
break;
}
}
} else {
- recipient_label.set(INNER_HTML, 'Yourself');
+ recipient_label.set('text', 'Yourself');
}
content_node.one('[name="name"]').set('value',filter.description);
if (is_lifecycle) {
@@ -1011,10 +1030,10 @@
context.filter_info = filter_info;
context.filter_id = filter_id;
var title = subscription.target_title;
- Y.one('#structural-subscription-context-title').set(
- INNER_HTML, title);
- Y.one('#subscription-overlay-title').set(
- INNER_HTML, 'Edit subscription for '+title+' bugs');
+ Y.one('#structural-subscription-context-title')
+ .set('text', title);
+ Y.one('#subscription-overlay-title')
+ .set('text', 'Edit subscription for '+title+' bugs');
add_subscription_overlay.show();
}
};
@@ -1035,14 +1054,14 @@
headers: {'X-HTTP-Method-Override': 'DELETE'},
on: {success: function(transactionid, response, args){
var subscriber = Y.one(
- '#subscription-'+subscriber_id.toString()),
- node = subscriber,
- filters = subscriber.all('.subscription-filter');
+ '#subscription-'+subscriber_id.toString());
+ var to_collapse = subscriber;
+ var filters = subscriber.all('.subscription-filter');
if (!filters.isEmpty()) {
- node = Y.one(
+ to_collapse = Y.one(
'#subscription-filter-'+filter_id.toString());
}
- collapse_node(node);
+ collapse_node(to_collapse);
},
failure: error_handler.getFailureHandler()
}
@@ -1066,11 +1085,12 @@
var filter_info = sub.filters[j];
if (!filter_info.subscriber_is_team ||
filter_info.user_is_team_admin) {
- var edit_link = Y.one('#edit-'+filter_id.toString());
+ var node = Y.one('#subscription-filter-'+filter_id.toString());
+ var edit_link = node.one('a.edit-subscription');
var edit_handler = make_edit_handler(
sub, filter_info, filter_id, config, context);
edit_link.on('click', edit_handler);
- var delete_link = Y.one('#unsubscribe-'+filter_id.toString());
+ var delete_link = node.one('a.delete-subscription');
var delete_handler = make_delete_handler(
filter_info.filter, filter_id, i);
delete_link.on('click', delete_handler);
@@ -1085,22 +1105,27 @@
*/
function fill_in_bug_subscriptions(config, context) {
validate_config(config);
+
var listing = Y.one('#subscription-listing');
var subscription_info = LP.cache.subscription_info;
- var html = '<div class="yui-g"><div id="structural-subscriptions">';
+ var top_node = Y.Node.create(
+ '<div class="yui-g"><div id="structural-subscriptions"></div></div>');
var filter_id = 0;
var i;
var j;
for (i=0; i<subscription_info.length; i++) {
var sub = subscription_info[i];
- html +=
+ var sub_node = top_node.appendChild(Y.Node.create(
'<div style="margin-top: 2em; padding: 0 1em 1em 1em; '+
- ' border: 1px solid #ddd;"'+
- ' id="subscription-'+i.toString()+'">'+
- ' <span style="float: left; margin-top: -0.6em; padding: 0 1ex;'+
- ' background-color: #fff;">Subscriptions to'+
- ' <a href="'+sub.target_url+'">'+sub.target_title+'</a>'+
- ' </span>';
+ ' border: 1px solid #ddd;"></div>')
+ .set('id', 'subscription-'+i.toString()));
+ sub_node.appendChild(Y.Node.create(
+ ' <span style="float: left; margin-top: -0.6em; '+
+ ' padding: 0 1ex; background-color: #fff;"></a>'))
+ .appendChild('<span>Subscriptions to </span>')
+ .appendChild(Y.Node.create('<a></a>')
+ .set('href', sub.target_url)
+ .set('text', sub.target_title));
for (j=0; j<sub.filters.length; j++) {
var filter = sub.filters[j].filter;
@@ -1110,52 +1135,53 @@
// and see the information you expect to see.
LP.cache['structural-subscription-filter-'+filter_id.toString()] =
filter;
- html +=
+ var filter_node = sub_node.appendChild(Y.Node.create(
'<div style="margin: 1em 0em 0em 1em"'+
- ' id="subscription-filter-'+filter_id.toString()+'"'+
- ' class="subscription-filter">'+
- ' <div style="margin-top: 1em">'+
- ' <strong id="filter-name-'+
- filter_id.toString()+'">'+
- render_filter_name(sub.filters[j], filter)+'</strong>';
+ ' class="subscription-filter"></div>')
+ .set('id', 'subscription-filter-'+filter_id.toString()))
+ .appendChild(Y.Node.create(
+ '<div style="margin-top: 1em"></div>'));
+ filter_node.appendChild(Y.Node.create(
+ '<strong class="filter-name"></strong>'))
+ .appendChild(render_filter_title(sub.filters[j], filter));
+
if (!sub.filters[j].subscriber_is_team ||
sub.filters[j].user_is_team_admin) {
// User can edit the subscription.
- html +=
+ filter_node.appendChild(Y.Node.create(
'<span style="float: right">'+
- '<a href="#" class="sprite modify edit js-action"'+
- ' id="edit-'+filter_id.toString()+'">'+
+ '<a href="#" class="sprite modify edit js-action '+
+ ' edit-subscription">'+
' Edit this subscription</a> or '+
- '<a href="#" class="sprite modify remove js-action"'+
- ' id="unsubscribe-'+filter_id.toString()+'">'+
- ' Unsubscribe</a></span>';
+ '<a href="#" class="sprite modify remove js-action '+
+ ' delete-subscription">'+
+ ' Unsubscribe</a></span>'));
} else {
// User cannot edit the subscription, because this is a
// team and the user does not have admin privileges.
- html +=
+ filter_node.appendChild(Y.Node.create(
'<span style="float: right"><em>'+
'You do not have privileges to change this subscription'+
- '</em></span>';
+ '</em></span>'));
}
- html += '</div>';
- html +=
- '<div style="padding-left: 1em"'+
- ' id="filter-description-'+filter_id.toString()+'">'+
- render_filter_description(filter)+'</div>';
-
- html += '</div>';
+
+ filter_node.appendChild(Y.Node.create(
+ '<div style="padding-left: 1em"></div>')
+ .set('id', 'filter-description-'+filter_id.toString()))
+ .appendChild(create_filter_description(filter));
+
filter_id += 1;
}
// We can remove this once we enforce at least one filter per
// subscription.
if (subscription_info[i].filters.length === 0) {
- html += '<strong>All messages</strong>';
+ sub_node.appendChild(
+ '<div style="clear: both; padding: 1em 0 0 1em"></div>')
+ .appendChild('<strong>All messages</strong>');
}
- html += '</div>';
}
- html += '</div></div>';
- listing.appendChild(Y.Node.create(html));
+ listing.appendChild(top_node);
wire_up_edit_links(config, context);
}
@@ -1163,7 +1189,8 @@
/**
* Construct a one-line textual description of a filter's name.
*/
-function render_filter_name(filter_info, filter) {
+function render_filter_title(filter_info, filter) {
+ var title = Y.Node.create('<span></span>');
var description;
if (filter.description) {
description = '"'+filter.description+'"';
@@ -1171,62 +1198,79 @@
description = '(unnamed)';
}
if (filter_info.subscriber_is_team) {
- return '<a href="'+filter_info.subscriber_url+'">'+
- filter_info.subscriber_title+"</a> subscription: "+description;
+ title.appendChild(Y.Node.create('<a></a>'))
+ .set('href', filter_info.subscriber_url)
+ .set('text', filter_info.subscriber_title);
+ title.appendChild(Y.Node.create('<span></span>'))
+ .set('text', ' subscription: '+description);
} else {
- return 'Your subscription: '+description;
+ title.set('text', 'Your subscription: '+description);
}
+ return title;
}
/**
* Construct a textual description of all of filter's properties.
*/
-function render_filter_description(filter) {
- var html = '';
- var filter_items = '';
+function create_filter_description(filter) {
+ var description = Y.Node.create('<div></div>');
+
+ var filter_items = [];
// Format status conditions.
if (filter.statuses.length !== 0) {
- filter_items += '<li> have status ' +
- filter.statuses.join(', ');
+ filter_items.push(Y.Node.create('<li></li>')
+ .set('text', 'have status '+filter.statuses.join(', ')));
}
// Format importance conditions.
if (filter.importances.length !== 0) {
- filter_items += '<li> are of importance ' +
- filter.importances.join(', ');
+ filter_items.push(Y.Node.create('<li></li>')
+ .set('text', 'are of importance '+filter.importances.join(', ')));
}
// Format tag conditions.
if (filter.tags.length !== 0) {
- filter_items += '<li> are tagged with ';
+ var tag_desc = Y.Node.create('<li>are tagged with </li>')
+ .append(Y.Node.create('<strong></strong>'))
+ .append(Y.Node.create('<span> of these tags: </span>'))
+ .append(Y.Node.create('<span></span>')
+ .set('text', filter.tags.join(', ')));
+
if (filter.find_all_tags) {
- filter_items += '<strong>all</strong>';
+ tag_desc.one('strong').set('text', 'all');
} else {
- filter_items += '<strong>any</strong>';
+ tag_desc.one('strong').set('text', 'any');
}
- filter_items += ' of these tags: ' +
- filter.tags.join(', ');
+ filter_items.push(tag_desc);
}
// If there were any conditions to list, stich them in with an
// intro.
- if (filter_items !== '') {
- html += 'You are subscribed to bugs that'+
- '<ul class="bulleted">'+filter_items+'</ul>';
+ if (filter_items.length > 0) {
+ var ul = Y.Node.create('<ul class="bulleted"></ul>');
+ Y.each(filter_items, function (li) {ul.appendChild(li);});
+ description.appendChild(
+ Y.Node.create('<span>You are subscribed to bugs that</span>'));
+ description.appendChild(ul);
}
// Format event details.
+ var events; // When will email be sent?
if (filter.bug_notification_level === 'Discussion') {
- html += 'You will recieve an email when any change '+
+ events = 'You will recieve an email when any change '+
'is made or a comment is added.';
} else if (filter.bug_notification_level === 'Details') {
- html += 'You will recieve an email when any changes '+
+ events = 'You will recieve an email when any changes '+
'are made to the bug. Bug comments will not be sent.';
} else if (filter.bug_notification_level === 'Lifecycle') {
- html += 'You will recieve an email when bugs are '+
+ events = 'You will recieve an email when bugs are '+
'opened or closed.';
+ } else {
+ throw new Error('Unrecognized events.');
}
- return html;
+ description.appendChild(Y.Node.create(events));
+
+ return description;
}
/**
=== modified file 'lib/lp/registry/javascript/tests/test_structural_subscription.js'
--- lib/lp/registry/javascript/tests/test_structural_subscription.js 2011-03-30 15:23:42 +0000
+++ lib/lp/registry/javascript/tests/test_structural_subscription.js 2011-04-01 16:08:35 +0000
@@ -147,11 +147,12 @@
test_make_selector_controls: function() {
// Verify the creation of select all/none controls.
var selectors = module.make_selector_controls('sharona');
- Assert.areEqual('sharona-select-all', selectors['all_name']);
- Assert.areEqual('sharona-select-none', selectors['none_name']);
- Assert.areEqual(
- '<div id="sharona-selectors"',
- selectors['html'].slice(0, 27));
+ Assert.areEqual(
+ 'Select all', selectors.all_link.get('text'));
+ Assert.areEqual(
+ 'Select none', selectors.none_link.get('text'));
+ Assert.areEqual(
+ 'sharona-selectors', selectors.node.get('id'));
}
});
suite.add(test_case);
@@ -419,8 +420,8 @@
// accordion pane.
module.setup(this.configuration);
var checkboxes = Y.all('input[name="importances"]');
- var select_all = Y.one('#importances-select-all');
- var select_none = Y.one('#importances-select-none');
+ var select_all = Y.one('#importances-selectors > a.select-all');
+ var select_none = Y.one('#importances-selectors > a.select-none');
Assert.isTrue(test_checked(checkboxes, true));
// Simulate a click on the select_none control.
Y.Event.simulate(Y.Node.getDOMNode(select_none), 'click');
@@ -435,8 +436,8 @@
// accordion pane.
module.setup(this.configuration);
var checkboxes = Y.all('input[name="statuses"]');
- var select_all = Y.one('#statuses-select-all');
- var select_none = Y.one('#statuses-select-none');
+ var select_all = Y.one('#statuses-selectors > a.select-all');
+ var select_none = Y.one('#statuses-selectors > a.select-none');
Assert.isTrue(test_checked(checkboxes, true));
// Simulate a click on the select_none control.
Y.Event.simulate(Y.Node.getDOMNode(select_none), 'click');
=== modified file 'lib/lp/registry/windmill/tests/test_plusnew_step2.py'
--- lib/lp/registry/windmill/tests/test_plusnew_step2.py 2011-02-10 04:00:00 +0000
+++ lib/lp/registry/windmill/tests/test_plusnew_step2.py 2011-04-01 16:08:35 +0000
@@ -37,7 +37,9 @@
lpuser.SAMPLE_PERSON.ensure_login(self.client)
- self.client.waits.forElement(id='field.displayname', timeout=u'20000')
+ self.client.open(url=u'%s/projects/+new'
+ % RegistryWindmillLayer.base_url)
+ self.client.waits.forElement(id='field.displayname', timeout=u'200000')
self.client.type(text=u'Badgers', id='field.displayname')
self.client.type(text=u'badgers', id='field.name')
self.client.type(text=u"There's the Badger", id='field.title')