162
162
namespace.FormRowWidget = FormRowWidget;
165
* A table to display, order, delete the selected parent series. Each parent
166
* can also be made an overlay, and a component and a pocket selected.
169
var ParentSeriesListWidget = function() {
170
ParentSeriesListWidget
171
.superclass.constructor.apply(this, arguments);
174
Y.mix(ParentSeriesListWidget, {
176
NAME: 'parentSeriesListWidget',
181
* The DistroSeries the choices in this field should
182
* reflect. Takes the form of a list of ids,
190
this.fieldNode.all("tbody > tr.parent").each(
193
node.get('id').replace('parent-',''));
202
this.fieldNode.all("tbody > tr.parent").each(
205
node.one('input.overlay').get('checked'));
213
var overlay_pockets = [];
214
this.fieldNode.all("tbody > tr.parent").each(
216
var select = node.one('td.pocket').one('select');
217
if (select !== null) {
218
overlay_pockets.push(select.get('value'));
221
overlay_pockets.push(null);
225
return overlay_pockets;
228
overlay_components: {
230
var overlay_components = [];
231
this.fieldNode.all("tbody > tr.parent").each(
233
var select = node.one('td.component').one('select');
234
if (select !== null) {
235
overlay_components.push(select.get('value'));
238
overlay_components.push(null);
242
return overlay_components;
248
Y.extend(ParentSeriesListWidget, FormRowWidget, {
250
initializer: function() {
251
ParentSeriesListWidget.superclass.initializer();
252
this.client = new Y.lp.client.Launchpad();
253
this.clean_display();
257
* Display a simple message when no parent series are selected.
259
* @method clean_display
261
clean_display: function() {
262
this.fieldNode.empty();
263
this.fieldNode.append(Y.Node.create("<div />")
264
.set('text', '[No parent for this series yet!]'));
268
* Display the table header.
270
* @method init_display
272
init_display: function() {
273
this.fieldNode.empty();
274
this.fieldNode = Y.Node.create("<table />");
275
var table_header = Y.Node.create(
278
"<th>Parent name</th>",
280
"<th>Component</th>",
287
this.fieldNode.append(table_header);
291
* Helper method to create a select widget from a list and add it
294
* @method build_selector
296
build_selector: function(node, res_list, class_name) {
297
var select = Y.Node.create('<select disabled="disabled"/>');
300
select.appendChild('<option />')
302
.set('value', choice);
304
node.one('td.'+class_name).append(select);
305
node.one('input').on('click', function(e) {
306
var select = node.one('td.'+class_name).one('select');
307
if (select.hasAttribute('disabled')) {
308
select.removeAttribute('disabled');
311
select.setAttribute('disabled', 'disabled');
317
* Build a select widget from a list retrieved from the api.
319
* @method build_select
321
build_select: function(node, class_name, path) {
324
success: function(res_list) {
325
self.build_selector(node, res_list, class_name);
327
failure: function() {
328
var failed_node = Y.Node.create('<span />')
329
.set('text', 'Failed to retrieve content.');
330
node.one('td.'+class_name).append(failed_node);
331
self.disable_overlay(node);
334
this.client.get(path, {on: on});
339
* Move down a parent's line in the table.
343
move_down: function(parent_id) {
344
var node = this.fieldNode.one("tr#parent-" + parent_id);
345
var other = node.next('tr.parent');
346
if (other !== null) { node.swap(other);}
347
Y.lazr.anim.green_flash({node: node}).run();
351
* Move up a parent's line in the table.
355
move_up: function(parent_id) {
356
var node = this.fieldNode.one("tr#parent-" + parent_id);
357
var other = node.previous('tr.parent');
358
if (other !== null) { node.swap(other);}
359
Y.lazr.anim.green_flash({node: node}).run();
363
* Remove a parent series.
367
remove_parent: function(parent_id) {
368
if (this.get('parents').length === 1) {
369
this.clean_display();
373
this.fieldNode.one('tr#parent-' + parent_id).remove();
375
Y.fire("parent_removed", parent_id);
379
* Disable the overlay (used when something goes wrong fetching possible
380
* components or pockets for the overlay).
382
* @method disable_overlay
384
disable_overlay: function(parent_node) {
385
var overlay = parent_node.one('input.overlay');
386
if (overlay.get('disabled') !== 'disabled') {
387
Y.lazr.anim.red_flash({node: parent_node}).run();
388
parent_node.one('input.overlay').set('disabled','disabled');
393
* Add a parent series.
397
add_parent: function(parent) {
398
if (this.get('parents').length === 0) {
402
var item = this.fieldNode.one('tr#parent-' + parent.value);
404
Y.lazr.anim.red_flash({node: item}).run();
407
item = Y.Node.create("<tr />")
409
.set('id', 'parent-' + parent.value)
410
.append(Y.Node.create("<td />")
411
.append(Y.Node.create('<a href="" title="Move parent up"/>')
413
.set('innerHTML', '↑'))
414
.append(Y.Node.create('<a href="" title="Move parent down"/>')
415
.addClass('move-down')
416
.set('innerHTML', '↓')))
417
.append(Y.Node.create("<td />")
419
.set('text', parent.title))
420
.append(Y.Node.create("<td />")
421
.set('align', 'center')
422
.append(Y.Node.create('<input type="checkbox" />')
423
.addClass('overlay')))
424
.append(Y.Node.create("<td />")
425
.addClass('component'))
426
.append(Y.Node.create("<td />")
428
.append(Y.Node.create("<td />")
429
.set('align', 'center')
430
.append(Y.Node.create('<span />')
432
.addClass('remove')));
433
this.fieldNode.one('tbody').append(item);
434
this.build_select(item, 'component',
435
parent.api_uri + '/component_names');
436
this.build_select(item, 'pocket',
437
parent.api_uri + '/suite_names');
438
item.one('.move-up').on('click', function(e) {
439
this.move_up(parent.value);
443
item.one('.move-down').on('click', function(e) {
444
this.move_down(parent.value);
448
item.one('.remove').on('click', function(e) {
449
var parent_id = item.get('id').replace('parent-','');
450
this.remove_parent(parent_id);
455
Y.lazr.anim.green_flash({node: item}).run();
460
namespace.ParentSeriesListWidget = ParentSeriesListWidget;
464
166
* A form row matching that which LaunchpadForm presents, containing a
560
Y.extend(ChoiceListWidget, FormRowWidget, {
563
* Helper method to create an entry for the select widget.
565
* @method _createChoice
567
_createChoice: function(choice) {
568
var field_name = this.get("name");
569
var field_type = this.get("type");
570
var item = Y.Node.create(
571
"<li><input /> <label /></li>");
573
.set("type", field_type)
574
.set("name", field_name)
575
.set("value", choice);
578
"for", item.one("input").generateID())
579
.setStyle("font-weight", "normal")
580
.set("text", choice);
585
* Remove a list of choices from the possible widget's choices.
587
* @method remove_choices
589
remove_choices: function(choices) {
592
this.fieldNode.all("select > option").each(
593
function(option) { options.push(option); });
595
"li input[value=" + choice + "]").each(
597
li_input.get('parentNode').remove();
603
Y.lazr.anim.green_flash({node: this.fieldNode}).run();
606
_sorted_position: function(choice) {
608
this.fieldNode.all("input").each(
610
options.push(node.get('value'));
613
options.push(choice);
614
return options.sort().indexOf(choice);
618
* Add new choices (if they are not already present).
620
* @method add_choices
622
add_choices: function(new_choices) {
625
if (this.fieldNode.all(
626
"li > input[value=" + choice + "]").isEmpty()) {
627
var list = this.fieldNode.one('ul');
629
list = Y.Node.create("<ul />");
630
this.fieldNode.empty().append(list);
632
var option = this._createChoice(choice);
633
var options = list.all('input');
634
if (options.isEmpty()) {
638
var pos = this._sorted_position(choice);
640
list.prepend(option);
643
list.insertBefore(option, options.item(pos));
649
Y.lazr.anim.green_flash({node: this.fieldNode}).run();
273
Y.extend(ChoiceListWidget, FormRowWidget);
655
275
namespace.ChoiceListWidget = ChoiceListWidget;
670
290
NAME: 'architecturesChoiceListWidget',
295
* The DistroSeries the choices in this field should
296
* reflect. Takes the form of a string, e.g. "ubuntu/hoary".
298
* @property distroSeries
301
setter: function(value, name) {
302
var path = value + "/architectures";
304
start: Y.bind(this.showSpinner, this),
305
success: Y.bind(this.set, this, "distroArchSerieses"),
306
failure: this.error_handler.getFailureHandler(),
307
end: Y.bind(this.hideSpinner, this)
309
this.client.get(path, {on: on});
314
* The DistroArchSerieses the choices in this field should
315
* reflect. Takes the form of a Y.lp.client.Collection.
317
* @property distroArchSerieses
319
distroArchSerieses: {
320
setter: function(value, name) {
322
"choices", value.entries.map(
324
return das.get("architecture_tag");
328
if (value.entries.length === 0) {
329
this.fieldNode.append(
330
Y.Node.create('<p />').set(
332
"The chosen series has no architectures!"));
334
Y.lazr.anim.green_flash({node: this.fieldNode}).run();
681
345
this.error_handler = new Y.lp.client.ErrorHandler();
682
346
this.error_handler.clearProgressUI = Y.bind(this.hideSpinner, this);
683
347
this.error_handler.showError = Y.bind(this.showError, this);
684
this._distroseries = {};
685
this.clean_display();
689
* Display a simple message when no parent series are selected.
691
* @method clean_display
693
clean_display: function() {
694
this.fieldNode.empty();
695
this.fieldNode.append(Y.Node.create("<div />")
696
.set('text', '[No architectures to select from yet!]'));
701
* Add a parent distroseries, add the architectures for this new
702
* distroseries to the possible choices.
704
* @method add_distroseries
705
* @param {Object} The distroseries to add ({value:distroseries_id),
706
* api_uri:distroseries_uri}).
708
add_distroseries: function(distroseries) {
709
var path = distroseries.api_uri + "/architectures";
710
var distroseries_id = distroseries.value;
713
success: function (results) {
714
self.add_distroarchseries(distroseries_id, results);
716
failure: this.error_handler.getFailureHandler()
718
this.client.get(path, {on: on});
722
* Remove a parent distroseries, remove the architectures only
723
* present in this parent series from the possible choices.
725
* @method remove_distroseries
727
remove_distroseries: function(distroseries_id) {
728
// Compute which das is only in the distroseries to be removed.
730
var das = this._distroseries[distroseries_id];
732
for (i=0; i<das.entries.length; i++) {
734
arch = das.entries[i].get('architecture_tag');
735
for (ds in this._distroseries) {
736
if (ds !== distroseries_id) {
737
var other_das = this._distroseries[ds];
738
for (j=0; j<other_das.entries.length; j++) {
739
var other_arch = other_das.entries[j].get(
741
if (other_arch === arch) {
748
arch_to_remove.push(arch);
751
delete this._distroseries[distroseries_id];
752
this.remove_choices(arch_to_remove);
753
if (this.fieldNode.all('input').isEmpty()) {
754
this.clean_display();
759
* Add a list of distroarchseries.
761
* @method add_distroarchseries
762
* @param {String} distroseries_id The distroarchseries id.
763
* @param {Object} distroarchseries The distroarchseries object.
765
add_distroarchseries: function(distroseries_id, distroarchseries) {
766
this._distroseries[distroseries_id] = distroarchseries;
767
var choices = distroarchseries.entries.map(
769
return das.get("architecture_tag");
772
this.add_choices(choices);
984
548
this.client.named_get("package-sets", "getBySeries", config);
553
* The Packagesets the choices in this field should
554
* reflect. Takes the form of a Y.lp.client.Collection.
556
* @property packageSets
559
setter: function(value, name) {
561
"choices", value.entries.map(
562
function(packageset) {
565
value: packageset.get("name"),
567
packageset.get("name") + ": " +
568
packageset.get("description"))
573
if (value.entries.length === 0) {
574
this.fieldNode.append(
575
Y.Node.create('<p />').set(
577
"The chosen series has no package sets!"));
582
Y.lazr.anim.green_flash({node: this.fieldNode}).run();
991
590
Y.extend(PackagesetPickerWidget, SelectWidget, {
994
* Add a distroseries: add its packagesets to the packageset picker.
996
* @method add_distroseries
997
* @param {Object} distroseries The distroseries object.
999
add_distroseries: function(distroseries) {
1000
var distro_series_uri = Y.lp.client.get_absolute_uri(
1001
distroseries.api_uri);
1004
success: function (results) {
1005
self.add_packagesets(results, distroseries);
1007
failure: this.error_handler.getFailureHandler()
1012
distroseries: distro_series_uri
1015
this.client.named_get("package-sets", "getBySeries", config);
1019
* Display a simple message when no parent series are selected.
1021
* @method clean_display
1023
clean_display: function() {
1024
this.fieldNode.empty();
1025
this.fieldNode.append(Y.Node.create("<div />")
1026
.set('text', '[No package sets to select from yet!]'));
1031
* Initialise the picker's select node.
1033
* @method init_select
1035
init_select: function() {
1036
var select = this.fieldNode.one('select');
1037
if (select === null) {
1038
select = Y.Node.create("<select />");
1039
select.set("name", this.get("name"))
1040
.set("size", this.get("size"));
1041
if (this.get("multiple")) {
1042
select.set("multiple", "multiple");
1044
this.fieldNode.empty().append(select);
1050
* Add a choice to the picker.
1052
* @method add_choice
1053
* @param {Object} choice The choice object to be added
1054
* ({text: choice_text, value: choice_value, data: choice_data}).
1056
add_choice: function(choice) {
1057
var select = this.init_select();
1058
var option = Y.Node.create("<option />");
1059
option.set("value", choice.value)
1060
.set("text", choice.text)
1061
.setData("data", choice.data);
1062
var options = select.all('option');
1063
if (options.isEmpty()) {
1064
select.append(option);
1067
var pos = this._sorted_position(choice.text);
1069
select.prepend(option);
1072
select.insertBefore(option, options.item(pos));
1078
* Add choices (a set of packagesets) to the picker.
1080
* @method add_packagesets
1081
* @param {Y.lp.client.Collection} packagesets The collection of
1082
* packagesets to add.
1083
* @param {Object} distroseries The distroseries object
1084
* ({value:distroseries_id), api_uri:distroseries_uri}).
1086
add_packagesets: function(packagesets, distroseries) {
1087
this._packagesets[distroseries.value] = packagesets.entries;
1088
packagesets.entries.forEach(
1089
function(packageset) {
1090
var value = packageset.get("id");
1095
packageset.get("name") + ": " +
1096
packageset.get("description") +
1097
" (" + distroseries.title + ") ")
1101
Y.lazr.anim.green_flash({node: this.fieldNode}).run();
1105
* Remove a distroseries: remove its packagesets from the picker.
1107
* @method remove_distroseries
1108
* @param {String} distroseries_id The id of the distroseries to be
1111
remove_distroseries: function(distroseries_id) {
1112
this._packagesets[distroseries_id].forEach(
1113
function(packageset) {
1115
'option[value="' + packageset.get("id") + '"]').remove();
1117
Y.lazr.anim.green_flash({node: this.fieldNode}).run();
1118
if (this.fieldNode.all('option').isEmpty()) {
1119
this.clean_display();
1123
592
initializer: function(config) {
1124
593
this.client = new Y.lp.client.Launchpad();
1125
594
this.error_handler = new Y.lp.client.ErrorHandler();
1126
595
this.error_handler.clearProgressUI = Y.bind(this.hideSpinner, this);
1127
596
this.error_handler.showError = Y.bind(this.showError, this);
1128
this.clean_display();
1129
// _packagesets maps each distroseries' id to a collection of ids
1130
// of its packagesets.
1131
// It's populated each time a new distroseries is added as a parent
1132
// and used when a distroseries is removed to get all the
1133
// corresponding packagesets to be removed from the widget.
1134
this._packagesets = {};
1282
744
name: this.context.name,
1283
745
distribution: this.context.distribution_link,
1284
parents: this.deriveFromChoices.get("parents"),
1285
746
architectures: this.architectureChoice.get("choice"),
1286
747
packagesets: this.packagesetChoice.get("choice"),
1287
rebuild: this.packageCopyOptions.get("choice") === (
1288
"Copy Source and Rebuild"),
1289
overlays: this.deriveFromChoices.get("overlays"),
1290
overlay_pockets: this.deriveFromChoices.get(
1292
overlay_components: this.deriveFromChoices.get(
1293
"overlay_components")
748
rebuild: this.packageCopyOptions.get("choice") == (
749
"Copy Source and Rebuild")
752
var parent = this.deriveFromChoice.get("value");
1296
753
this.client.named_post(
1297
this.context.self_link, "initDerivedDistroSeries", config);
754
parent, "deriveDistroSeries", config);
1302
759
namespace.DeriveDistroSeriesActionsWidget = DeriveDistroSeriesActionsWidget;
1305
* Show the "Add parent series" overlay.
1307
var show_add_parent_series_form = function(e) {
1311
header: 'Add a parent series',
1312
step_title: 'Search'
1315
config.save = function(result) {
1316
add_parent_series(result);
1319
parent_picker = Y.lp.app.picker.create('DistroSeriesDerivation', config);
1320
parent_picker.show();
1323
namespace.show_add_parent_series_form = show_add_parent_series_form;
1326
* Add a parent series.
1328
var add_parent_series = function(parent) {
1329
Y.fire("add_parent", parent);
1332
namespace.add_parent_series = add_parent_series;
1336
763
* Setup the widgets on the +initseries page.
1340
767
namespace.setup = function() {
1341
768
var form_container = Y.one("#initseries-form-container");
1342
var form_table = form_container.one("table.form");
1343
var form_table_body = form_table.append(Y.Node.create('<tbody />'));
769
var form_table_body = form_container.one("table.form > tbody");
771
// Pre-existing form fields.
772
var derive_from_choice =
773
form_table_body.one("[name=field.derived_from_series]");
1346
var add_parent_link = Y.Node.create('<a href="+add-parent-series">')
1349
.set("id", "add-parent-series")
1350
.set("text", "Add parent series");
1351
add_parent_link.appendTo(form_table_body);
1352
var parents_selection =
1353
new ParentSeriesListWidget()
1354
.set("name", "field.parent")
1355
.set("label", "Parent Series:")
1356
.set("description", (
1357
"Choose and configure the parent series."))
1358
.render(form_table_body);
1359
776
var architecture_choice =
1360
777
new ArchitecturesChoiceListWidget()
1361
778
.set("name", "field.architectures")
1362
779
.set("label", "Architectures:")
1363
780
.set("description", (
1364
781
"Choose the architectures you want to " +
1365
"use from the parent series (or select none " +
1366
"if you want to use all the available " +
782
"use from the parent series."))
1368
783
.render(form_table_body);
1369
784
var packageset_choice =
1370
785
new PackagesetPickerWidget()
1387
800
.set("label", "Copy options:")
1388
801
.set("description", (
1389
802
"Choose whether to rebuild all the sources you copy " +
1390
"from the parent(s), or to copy their binaries too."))
803
"from the parent, or to copy their binaries too."))
1391
804
.set("choices", ["Copy Source and Rebuild",
1392
805
"Copy Source and Binaries"])
1393
806
.set("choice", "Copy Source and Binaries")
1394
807
.render(form_table_body);
1395
// Disable rebuilding if LP.cache['rebuilding_allowed'] is false.
1396
if (!LP.cache['rebuilding_allowed']) {
1397
package_copy_options.fieldNode
1398
.one('input[value="Copy Source and Rebuild"]')
1399
.set('disabled', 'disabled');
1400
package_copy_options.set("description", (
1401
"Note that you cannot rebuild sources because the " +
1402
"distribution already has an initialized series."));
1404
808
var form_actions =
1405
809
new DeriveDistroSeriesActionsWidget({
1406
810
context: LP.cache.context,
1407
811
srcNode: form_container.one("div.actions"),
1408
deriveFromChoices: parents_selection,
812
deriveFromChoice: derive_from_choice,
1409
813
architectureChoice: architecture_choice,
1410
814
packagesetChoice: packageset_choice,
1411
815
packageCopyOptions: package_copy_options
1414
// Wire up the add parent series link.
1415
var link = Y.one('#add-parent-series');
1416
if (Y.Lang.isValue(link)) {
1417
link.addClass('js-action');
1418
link.on('click', show_add_parent_series_form);
1421
Y.on('add_parent', function(parent) {
1422
var added = parents_selection.add_parent(parent);
1424
Y.fire("parent_added", parent);
1428
Y.on('parent_added', function(parent) {
1429
architecture_choice.add_distroseries(parent);
1430
packageset_choice.add_distroseries(parent);
1433
Y.on('parent_removed', function(parent_id) {
1434
architecture_choice.remove_distroseries(parent_id);
1435
packageset_choice.remove_distroseries(parent_id);
818
// Wire up the distroseries select to the architectures widget.
819
function update_architecture_choice() {
821
.set("distroSeries", derive_from_choice.get("value"));
823
derive_from_choice.on("change", update_architecture_choice);
824
// Update the architectures widget for the selected distroseries.
825
update_architecture_choice();
827
// Wire up the distroseries select to the packagesets widget.
828
function update_packageset_choice() {
830
.set("distroSeries", derive_from_choice.get("value"));
832
derive_from_choice.on("change", update_packageset_choice);
833
// Update the packagesets widget for the selected distroseries.
834
update_packageset_choice();
1438
836
// Wire up the form submission.
1439
837
form_container.one("form").on(