~launchpad-pqm/launchpad/devel

13444.5.2 by Gavin Panella
Copy the widgets from lp.registry.distroseries.initseries into .widgets. Will remove them from .initseries next.
1
/**
2
 * Copyright 2011 Canonical Ltd. This software is licensed under the
3
 * GNU Affero General Public License version 3 (see the file LICENSE).
4
 *
13444.6.4 by Gavin Panella
Correct some titles and comments.
5
 * DistroSeries Widgets.
13444.5.2 by Gavin Panella
Copy the widgets from lp.registry.distroseries.initseries into .widgets. Will remove them from .initseries next.
6
 *
13444.6.4 by Gavin Panella
Correct some titles and comments.
7
 * @module lp.registry.distroseries
8
 * @submodule widgets
13444.5.2 by Gavin Panella
Copy the widgets from lp.registry.distroseries.initseries into .widgets. Will remove them from .initseries next.
9
 */
10
11
YUI.add('lp.registry.distroseries.widgets', function(Y) {
12
13
Y.log('loading lp.registry.distroseries.widgets');
14
15
var namespace = Y.namespace('lp.registry.distroseries.widgets');
16
13444.6.2 by Gavin Panella
Remove the non-DistroSeries-specific widgets from the distroseries.widgets module.
17
var formwidgets = Y.lp.app.formwidgets;
18
13444.5.2 by Gavin Panella
Copy the widgets from lp.registry.distroseries.initseries into .widgets. Will remove them from .initseries next.
19
20
/**
21
 * A table to display, order, delete the selected parent series. Each parent
22
 * can also be made an overlay, and a component and a pocket selected.
23
 *
24
 */
25
var ParentSeriesListWidget;
26
27
ParentSeriesListWidget = function() {
28
    ParentSeriesListWidget
29
        .superclass.constructor.apply(this, arguments);
30
};
31
32
Y.mix(ParentSeriesListWidget, {
33
34
    NAME: 'parentSeriesListWidget',
35
36
    ATTRS: {
37
38
        /**
39
         * The DistroSeries the choices in this field should
40
         * reflect. Takes the form of a list of ids,
41
         * e.g. ["4", "55"].
42
         *
43
         * @property parents
44
         */
45
        parents: {
46
            getter: function() {
47
                var series = [];
48
                this.fieldNode.all("tbody > tr.parent").each(
49
                    function(node) {
50
                        series.push(
51
                            node.get('id').replace('parent-',''));
52
                    }
53
                );
54
                return series;
55
            }
56
        },
57
        overlays: {
58
            getter: function() {
59
                var overlays = [];
60
                this.fieldNode.all("tbody > tr.parent").each(
61
                    function(node) {
62
                        overlays.push(
63
                            node.one('input.overlay').get('checked'));
64
                    }
65
                );
66
                return overlays;
67
            }
68
        },
69
        overlay_pockets: {
70
            getter: function() {
71
                var overlay_pockets = [];
72
                this.fieldNode.all("tbody > tr.parent").each(
73
                    function(node) {
74
                        var select = node.one('td.pocket').one('select');
75
                        if (select !== null) {
76
                            overlay_pockets.push(select.get('value'));
77
                        }
78
                        else {
79
                            overlay_pockets.push(null);
80
                        }
81
                    }
82
                );
83
                return overlay_pockets;
84
            }
85
        },
86
        overlay_components: {
87
            getter: function() {
88
                var overlay_components = [];
89
                this.fieldNode.all("tbody > tr.parent").each(
90
                    function(node) {
91
                        var select = node.one('td.component').one('select');
92
                        if (select !== null) {
93
                            overlay_components.push(select.get('value'));
94
                        }
95
                        else {
96
                            overlay_components.push(null);
97
                        }
98
                    }
99
                );
100
                return overlay_components;
101
            }
102
        }
103
    }
104
});
105
13444.6.2 by Gavin Panella
Remove the non-DistroSeries-specific widgets from the distroseries.widgets module.
106
Y.extend(ParentSeriesListWidget, formwidgets.FormRowWidget, {
13444.5.2 by Gavin Panella
Copy the widgets from lp.registry.distroseries.initseries into .widgets. Will remove them from .initseries next.
107
108
    initializer: function() {
109
        ParentSeriesListWidget.superclass.initializer();
110
        this.client = new Y.lp.client.Launchpad();
111
        this.clean_display();
112
    },
113
114
    /**
115
     * Display a simple message when no parent series are selected.
116
     *
117
     * @method clean_display
118
     */
119
    clean_display: function() {
120
        this.fieldNode.empty();
121
        this.fieldNode.append(Y.Node.create("<div />")
122
            .set('text', '[No parent for this series yet!]'));
123
    },
124
125
    /**
126
     * Display the table header.
127
     *
128
     * @method init_display
129
     */
130
    init_display: function() {
131
        this.fieldNode.empty();
132
        this.fieldNode = Y.Node.create("<table />");
133
        var table_header = Y.Node.create(
134
            ["<thead><tr>",
135
             "<th>Order</th>",
136
             "<th>Parent name</th>",
137
             "<th>Overlay?</th>",
138
             "<th>Component</th>",
139
             "<th>Pocket</th>",
140
             "<th>Delete</th>",
141
             "</tr></thead>",
142
             "<tbody>",
143
             "</tbody>"
144
            ].join(""));
145
        this.fieldNode.append(table_header);
146
    },
147
148
    /**
149
     * Helper method to create a select widget from a list and add it
150
     * to a node.
151
     *
152
     * @method build_selector
153
     */
154
    build_selector: function(node, res_list, class_name) {
155
        var select = Y.Node.create('<select disabled="disabled"/>');
156
        res_list.forEach(
157
            function(choice) {
158
                select.appendChild('<option />')
159
                    .set('text', choice)
160
                    .set('value', choice);
161
            });
162
        node.one('td.'+class_name).append(select);
163
        node.one('input').on('click', function(e) {
164
            var select = node.one('td.'+class_name).one('select');
165
            if (select.hasAttribute('disabled')) {
166
                select.removeAttribute('disabled');
167
            }
168
            else {
169
                select.setAttribute('disabled', 'disabled');
170
            }
171
        });
172
    },
173
174
    /**
175
     * Build a select widget from a list retrieved from the api.
176
     *
177
     * @method build_select
178
     */
179
    build_select: function(node, class_name, path) {
180
        var self = this;
181
        var on = {
182
            success: function(res_list) {
183
                self.build_selector(node, res_list, class_name);
184
            },
185
            failure: function() {
186
                var failed_node = Y.Node.create('<span />')
187
                    .set('text', 'Failed to retrieve content.');
188
                node.one('td.'+class_name).append(failed_node);
189
                self.disable_overlay(node);
190
            }
191
        };
192
        this.client.get(path, {on: on});
193
     },
194
195
196
    /**
197
     * Move down a parent's line in the table.
198
     *
199
     * @method move_down
200
     */
201
    move_down: function(parent_id) {
202
        var node = this.fieldNode.one("tr#parent-" + parent_id);
203
        var other = node.next('tr.parent');
204
        if (other !== null) { node.swap(other);}
205
        Y.lazr.anim.green_flash({node: node}).run();
206
    },
207
208
    /**
209
     * Move up a parent's line in the table.
210
     *
211
     * @method move_up
212
     */
213
    move_up: function(parent_id) {
214
        var node = this.fieldNode.one("tr#parent-" + parent_id);
215
        var other = node.previous('tr.parent');
216
        if (other !== null) { node.swap(other);}
217
        Y.lazr.anim.green_flash({node: node}).run();
218
    },
219
220
    /**
221
     * Remove a parent series.
222
     *
223
     * @method remove_parent
224
     */
225
    remove_parent: function(parent_id) {
226
        if (this.get('parents').length === 1) {
227
            this.clean_display();
228
            this.renderUI();
229
        }
230
        else {
231
            this.fieldNode.one('tr#parent-' + parent_id).remove();
232
        }
233
        Y.fire("parent_removed", parent_id);
234
    },
235
236
    /**
237
     * Disable the overlay (used when something goes wrong fetching possible
238
     * components or pockets for the overlay).
239
     *
240
     * @method disable_overlay
241
     */
242
    disable_overlay: function(parent_node) {
243
        var overlay = parent_node.one('input.overlay');
244
        if (overlay.get('disabled') !== 'disabled') {
245
            Y.lazr.anim.red_flash({node: parent_node}).run();
246
            parent_node.one('input.overlay').set('disabled','disabled');
247
        }
248
    },
249
250
    /**
251
     * Add a parent series.
252
     *
253
     * @method add_parent
254
     */
255
    add_parent: function(parent) {
256
        if (this.get('parents').length === 0) {
257
            this.init_display();
258
            this.renderUI();
259
        }
260
        var item = this.fieldNode.one('tr#parent-' + parent.value);
261
        if (item !== null) {
262
            Y.lazr.anim.red_flash({node: item}).run();
263
            return false;
264
        }
265
        item =  Y.Node.create("<tr />")
266
            .addClass('parent')
267
            .set('id', 'parent-' + parent.value)
268
            .append(Y.Node.create("<td />")
269
                .append(Y.Node.create('<a href="" title="Move parent up"/>')
270
                    .addClass('move-up')
271
                .set('innerHTML', '&uarr;'))
272
                .append(Y.Node.create('<a href="" title="Move parent down"/>')
273
                    .addClass('move-down')
274
                .set('innerHTML', '&darr;')))
275
            .append(Y.Node.create("<td />")
276
                .addClass('series')
277
                .set('text', parent.title))
278
            .append(Y.Node.create("<td />")
279
                .set('align', 'center')
280
                .append(Y.Node.create('<input type="checkbox" />')
281
                    .addClass('overlay')))
282
            .append(Y.Node.create("<td />")
283
                .addClass('component'))
284
            .append(Y.Node.create("<td />")
285
                .addClass('pocket'))
286
            .append(Y.Node.create("<td />")
287
                .set('align', 'center')
288
                .append(Y.Node.create('<span />')
289
                    .addClass('sprite')
290
                    .addClass('remove')));
291
        this.fieldNode.one('tbody').append(item);
292
        this.build_select(item, 'component',
293
            parent.api_uri + '/component_names');
294
        this.build_select(item, 'pocket',
295
            parent.api_uri + '/suite_names');
296
        item.one('.move-up').on('click', function(e) {
297
            this.move_up(parent.value);
298
            e.preventDefault();
299
            return false;
300
        }, this);
301
        item.one('.move-down').on('click', function(e) {
302
            this.move_down(parent.value);
303
            e.preventDefault();
304
            return false;
305
        }, this);
306
        item.one('.remove').on('click', function(e) {
307
            var parent_id = item.get('id').replace('parent-','');
308
            this.remove_parent(parent_id);
309
            e.preventDefault();
310
            return false;
311
        }, this);
312
313
        Y.lazr.anim.green_flash({node: item}).run();
314
        return true;
315
    }
316
});
317
318
namespace.ParentSeriesListWidget = ParentSeriesListWidget;
319
320
321
/**
322
 * A special form of ChoiceListWidget for choosing architecture tags.
323
 *
324
 * @class ArchitecturesChoiceListWidget
325
 */
326
var ArchitecturesChoiceListWidget;
327
328
ArchitecturesChoiceListWidget = function() {
329
    ArchitecturesChoiceListWidget
330
        .superclass.constructor.apply(this, arguments);
331
};
332
333
Y.mix(ArchitecturesChoiceListWidget, {
334
335
    NAME: 'architecturesChoiceListWidget',
336
337
    ATTRS: {
338
    }
339
340
});
341
13444.6.2 by Gavin Panella
Remove the non-DistroSeries-specific widgets from the distroseries.widgets module.
342
Y.extend(ArchitecturesChoiceListWidget, formwidgets.ChoiceListWidget, {
13444.5.2 by Gavin Panella
Copy the widgets from lp.registry.distroseries.initseries into .widgets. Will remove them from .initseries next.
343
344
    initializer: function(config) {
345
        this.client = new Y.lp.client.Launchpad();
346
        this.error_handler = new Y.lp.client.ErrorHandler();
347
        this.error_handler.clearProgressUI = Y.bind(this.hideSpinner, this);
348
        this.error_handler.showError = Y.bind(this.showError, this);
349
        this._distroseries = {};
350
        this.clean_display();
351
    },
352
353
    /**
354
     * Display a simple message when no parent series are selected.
355
     *
356
     * @method clean_display
357
     */
358
    clean_display: function() {
359
        this.fieldNode.empty();
360
        this.fieldNode.append(Y.Node.create("<div />")
361
            .set('text', '[No architectures to select from yet!]'));
362
        this.renderUI();
363
    },
364
365
    /**
366
     * Add a parent distroseries, add the architectures for this new
367
     * distroseries to the possible choices.
368
     *
369
     * @method add_distroseries
13444.6.17 by Gavin Panella
Fix docstring.
370
     * @param {Object} distroseries The distroseries to add
371
     *     ({value:distroseries_id, api_uri:distroseries_uri}).
13444.5.2 by Gavin Panella
Copy the widgets from lp.registry.distroseries.initseries into .widgets. Will remove them from .initseries next.
372
     */
373
    add_distroseries: function(distroseries) {
374
        var path = distroseries.api_uri + "/architectures";
375
        var distroseries_id = distroseries.value;
376
        var self = this;
377
        var on = {
378
            success: function (results) {
379
                self.add_distroarchseries(distroseries_id, results);
380
            },
381
            failure: this.error_handler.getFailureHandler()
382
        };
383
        this.client.get(path, {on: on});
384
     },
385
386
    /**
387
     * Remove a parent distroseries, remove the architectures only
388
     * present in this parent series from the possible choices.
389
     *
390
     * @method remove_distroseries
391
     */
392
    remove_distroseries: function(distroseries_id) {
393
        // Compute which das is only in the distroseries to be removed.
394
        var arch_to_remove = [];
395
        var das = this._distroseries[distroseries_id];
396
        var i, ds, j;
397
        for (i=0; i<das.entries.length; i++) {
398
            var remove_das = true;
399
            var arch = das.entries[i].get('architecture_tag');
400
            for (ds in this._distroseries) {
401
                if (this._distroseries.hasOwnProperty(ds) &&
402
                    ds !== distroseries_id) {
403
                   var other_das = this._distroseries[ds];
404
                   for (j=0; j<other_das.entries.length; j++) {
405
                       var other_arch = other_das.entries[j].get(
406
                           'architecture_tag');
407
                       if (other_arch === arch) {
408
                           remove_das = false;
409
                       }
410
                   }
411
                }
412
            }
413
            if (remove_das) {
414
                arch_to_remove.push(arch);
415
            }
416
        }
417
        delete this._distroseries[distroseries_id];
418
        this.remove_choices(arch_to_remove);
419
        if (this.fieldNode.all('input').isEmpty()) {
420
            this.clean_display();
421
        }
422
    },
423
424
    /**
425
     * Add a list of distroarchseries.
426
     *
427
     * @method add_distroarchseries
428
     * @param {String} distroseries_id The distroarchseries id.
429
     * @param {Object} distroarchseries The distroarchseries object.
430
     */
431
    add_distroarchseries: function(distroseries_id, distroarchseries) {
432
        this._distroseries[distroseries_id] = distroarchseries;
433
        var choices = distroarchseries.entries.map(
434
            function(das) {
435
                return das.get("architecture_tag");
436
            }
437
        );
438
        this.add_choices(choices);
439
     }
440
441
});
442
443
namespace.ArchitecturesChoiceListWidget = ArchitecturesChoiceListWidget;
444
445
446
/**
447
 * A special form of SelectWidget for choosing packagesets.
448
 *
449
 * @class PackagesetPickerWidget
450
 */
451
var PackagesetPickerWidget;
452
453
PackagesetPickerWidget = function() {
454
    PackagesetPickerWidget
455
        .superclass.constructor.apply(this, arguments);
456
};
457
458
Y.mix(PackagesetPickerWidget, {
459
460
    NAME: 'packagesetPickerWidget',
461
462
    ATTRS: {
463
464
        /**
465
         * The DistroSeries the choices in this field should
466
         * reflect. Takes the form of a string, e.g. "ubuntu/hoary".
467
         *
468
         * @property distroSeries
13444.6.30 by Gavin Panella
Update the XXXs with bug numbers.
469
         * @deprecated Use add_distroseries() and
470
         *     remove_distroseries() instead.
13444.5.2 by Gavin Panella
Copy the widgets from lp.registry.distroseries.initseries into .widgets. Will remove them from .initseries next.
471
         */
472
        distroSeries: {
13444.6.30 by Gavin Panella
Update the XXXs with bug numbers.
473
            /* XXX: GavinPanella 2011-07-20 bug=814509: This widget
13444.6.16 by Gavin Panella
Lots of XXXs.
474
               handles multiple distroseries so this attribute is
475
               defunct. */
13444.5.2 by Gavin Panella
Copy the widgets from lp.registry.distroseries.initseries into .widgets. Will remove them from .initseries next.
476
            setter: function(value, name) {
477
                var distro_series_uri = Y.lp.client.get_absolute_uri(value);
478
                var on = {
479
                    start: Y.bind(this.showSpinner, this),
480
                    success: Y.bind(this.set, this, "packageSets"),
481
                    failure: this.error_handler.getFailureHandler(),
482
                    end: Y.bind(this.hideSpinner, this)
483
                };
484
                var config = {
485
                    on: on,
486
                    parameters: {
487
                        distroseries: distro_series_uri
488
                    }
489
                };
490
                this.client.named_get("package-sets", "getBySeries", config);
491
            }
492
        }
493
    }
494
});
495
496
13444.6.2 by Gavin Panella
Remove the non-DistroSeries-specific widgets from the distroseries.widgets module.
497
Y.extend(PackagesetPickerWidget, formwidgets.SelectWidget, {
13444.5.2 by Gavin Panella
Copy the widgets from lp.registry.distroseries.initseries into .widgets. Will remove them from .initseries next.
498
499
    /**
500
     * Add a distroseries: add its packagesets to the packageset picker.
501
     *
502
     * @method add_distroseries
13444.6.8 by Gavin Panella
First kind-of-working painful prototype.
503
     * @param {Object} distroseries An object describing a
504
     *     distroseries, containing three properties: api_uri, title
505
     *     and value. The first two are what you might expect. The
506
     *     last is simply a unique term for referencing the
507
     *     distroseries which is required when calling
13444.6.30 by Gavin Panella
Update the XXXs with bug numbers.
508
     *     remove_distroseries. TODO: Accept an lp.client.Entry as an
509
     *     alternative?
13444.5.2 by Gavin Panella
Copy the widgets from lp.registry.distroseries.initseries into .widgets. Will remove them from .initseries next.
510
     */
511
    add_distroseries: function(distroseries) {
512
        var distro_series_uri = Y.lp.client.get_absolute_uri(
513
            distroseries.api_uri);
514
        var self = this;
515
        var on = {
516
            success: function (results) {
517
                self.add_packagesets(results, distroseries);
518
            },
519
            failure: this.error_handler.getFailureHandler()
520
        };
521
        var config = {
522
            on: on,
523
            parameters: {
524
                distroseries: distro_series_uri
525
            }
526
        };
527
        this.client.named_get("package-sets", "getBySeries", config);
528
    },
529
530
    /**
531
     * Display a simple message when no parent series are selected.
532
     *
533
     * @method clean_display
534
     */
535
    clean_display: function() {
13444.6.30 by Gavin Panella
Update the XXXs with bug numbers.
536
        /* XXX: GavinPanella 2011-07-20 bug=814531: The list of
537
           choices should be set via this.set("choices", ...) and the
538
           setter should ensure that the selection is maintained.
539
           Doing it via the back door means that the choicesChange
540
           event is not fired. Rather than fire it explicity we should
541
           DTRT. */
542
        /* XXX: GavinPanella 2011-07-18 bug=814535: This does not
543
           update this._distroseries and can thus leave the widget in
544
           an invalid state. */
13444.5.2 by Gavin Panella
Copy the widgets from lp.registry.distroseries.initseries into .widgets. Will remove them from .initseries next.
545
        this.fieldNode.empty();
546
        this.fieldNode.append(Y.Node.create("<div />")
547
            .set('text', '[No package sets to select from yet!]'));
548
        this.renderUI();
549
    },
550
551
    /**
552
     * Initialize the picker's select node.
553
     *
554
     * @method init_select
555
     */
556
    init_select: function() {
13444.6.30 by Gavin Panella
Update the XXXs with bug numbers.
557
        /* XXX: GavinPanella 2011-07-20 bug=814531: This method will
558
           not be necessary once this.set("choices", ...) is used
13444.6.16 by Gavin Panella
Lots of XXXs.
559
           consistently. */
13444.5.2 by Gavin Panella
Copy the widgets from lp.registry.distroseries.initseries into .widgets. Will remove them from .initseries next.
560
        var select = this.fieldNode.one('select');
561
        if (select === null) {
562
            select = Y.Node.create("<select />");
563
            select.set("name", this.get("name"))
564
                    .set("size", this.get("size"));
565
            if (this.get("multiple")) {
566
                select.set("multiple", "multiple");
567
            }
568
            this.fieldNode.empty().append(select);
569
        }
570
        return select;
571
    },
572
573
    /**
574
     * Add a choice to the picker.
575
     *
576
     * @method add_choice
577
     * @param {Object} choice The choice object to be added
578
     *     ({text: choice_text, value: choice_value, data: choice_data}).
579
     */
580
    add_choice: function(choice) {
13444.6.30 by Gavin Panella
Update the XXXs with bug numbers.
581
        /* XXX: GavinPanella 2011-07-20 bug=814531: The list of
582
           choices should be set via this.set("choices", ...) and the
583
           setter should ensure that the selection is maintained.
584
           Doing it via the back door means that the choicesChange
585
           event is not fired. Rather than fire it explicity we should
586
           DTRT. */
13444.5.2 by Gavin Panella
Copy the widgets from lp.registry.distroseries.initseries into .widgets. Will remove them from .initseries next.
587
        var select = this.init_select();
588
        var option = Y.Node.create("<option />");
589
        option.set("value", choice.value)
590
            .set("text", choice.text)
591
            .setData("data", choice.data);
592
        var options = select.all('option');
593
        if (options.isEmpty()) {
594
            select.append(option);
595
        }
596
        else {
597
            var pos = this._sorted_position(choice.text);
598
            if (pos === 0) {
599
                select.prepend(option);
600
            }
601
            else {
602
                select.insertBefore(option, options.item(pos));
603
            }
604
        }
605
    },
606
607
    /**
608
     * Add choices (a set of packagesets) to the picker.
609
     *
610
     * @method add_packagesets
611
     * @param {Y.lp.client.Collection} packagesets The collection of
612
     *     packagesets to add.
613
     * @param {Object} distroseries The distroseries object
614
     *     ({value:distroseries_id), api_uri:distroseries_uri}).
615
     */
616
    add_packagesets: function(packagesets, distroseries) {
617
        this._packagesets[distroseries.value] = packagesets.entries;
618
        packagesets.entries.forEach(
619
            function(packageset) {
620
                var value = packageset.get("id");
621
                this.add_choice({
622
                    data: packageset,
623
                    value: value,
624
                    text: (
625
                        packageset.get("name") + ": " +
626
                        packageset.get("description") +
627
                        " (" + distroseries.title + ") ")
628
                });
629
            }, this);
630
        this.autoSize(10);
631
        Y.lazr.anim.green_flash({node: this.fieldNode}).run();
632
     },
633
634
    /**
635
     * Remove a distroseries: remove its packagesets from the picker.
636
     *
637
     * @method remove_distroseries
638
     * @param {String} distroseries_id The id of the distroseries to be
639
     *     removed.
640
     */
641
    remove_distroseries: function(distroseries_id) {
642
        this._packagesets[distroseries_id].forEach(
643
            function(packageset) {
13444.6.30 by Gavin Panella
Update the XXXs with bug numbers.
644
                /* XXX: GavinPanella 2011-07-20 bug=814531: The list
645
                   of choices should be set via this.set("choices",
646
                   ...)  and the setter should ensure that the
647
                   selection is maintained. Doing it via the back door
648
                   means that the choicesChange event is not
649
                   fired. Rather than fire it explicity we should
650
                   DTRT. */
13444.5.2 by Gavin Panella
Copy the widgets from lp.registry.distroseries.initseries into .widgets. Will remove them from .initseries next.
651
                this.fieldNode.one(
652
                    'option[value="' + packageset.get("id") + '"]').remove();
653
            }, this);
654
       Y.lazr.anim.green_flash({node: this.fieldNode}).run();
655
        if (this.fieldNode.all('option').isEmpty()) {
656
            this.clean_display();
657
        }
658
    },
659
660
    initializer: function(config) {
661
        this.client = new Y.lp.client.Launchpad();
662
        this.error_handler = new Y.lp.client.ErrorHandler();
663
        this.error_handler.clearProgressUI = Y.bind(this.hideSpinner, this);
664
        this.error_handler.showError = Y.bind(this.showError, this);
665
        this.clean_display();
666
        // _packagesets maps each distroseries' id to a collection of ids
667
        // of its packagesets.
668
        // It's populated each time a new distroseries is added as a parent
669
        // and used when a distroseries is removed to get all the
670
        // corresponding packagesets to be removed from the widget.
671
        this._packagesets = {};
672
    }
673
674
});
675
676
namespace.PackagesetPickerWidget = PackagesetPickerWidget;
677
678
13444.6.2 by Gavin Panella
Remove the non-DistroSeries-specific widgets from the distroseries.widgets module.
679
}, "0.1", {"requires": [
680
               "node", "dom", "io", "widget", "lp.client",
681
               "lp.app.formwidgets", "lazr.anim", "array-extras",
682
               "transition"]});