~launchpad-pqm/launchpad/devel

« back to all changes in this revision

Viewing changes to lib/lp/registry/javascript/distroseries.js

  • Committer: Launchpad Patch Queue Manager
  • Date: 2011-05-23 18:43:31 UTC
  • mfrom: (13084.2.6 page-match-rewrite-url)
  • Revision ID: launchpad@pqm.canonical.com-20110523184331-dhd2c7cgfuu49epw
[r=sinzui][bug=784273] Adds facility to the PageMatch to handle bad
        URIs

Show diffs side-by-side

added added

removed removed

Lines of Context:
161
161
 
162
162
namespace.FormRowWidget = FormRowWidget;
163
163
 
164
 
/**
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.
167
 
 *
168
 
 */
169
 
var ParentSeriesListWidget = function() {
170
 
    ParentSeriesListWidget
171
 
        .superclass.constructor.apply(this, arguments);
172
 
};
173
 
 
174
 
Y.mix(ParentSeriesListWidget, {
175
 
 
176
 
    NAME: 'parentSeriesListWidget',
177
 
 
178
 
    ATTRS: {
179
 
 
180
 
        /**
181
 
         * The DistroSeries the choices in this field should
182
 
         * reflect. Takes the form of a list of ids,
183
 
         * e.g. ["4", "55"].
184
 
         *
185
 
         * @property parents
186
 
         */
187
 
        parents: {
188
 
            getter: function() {
189
 
                var series = [];
190
 
                this.fieldNode.all("tbody > tr.parent").each(
191
 
                    function(node) {
192
 
                        series.push(
193
 
                            node.get('id').replace('parent-',''));
194
 
                    }
195
 
                );
196
 
                return series;
197
 
            }
198
 
        },
199
 
        overlays: {
200
 
            getter: function() {
201
 
                var overlays = [];
202
 
                this.fieldNode.all("tbody > tr.parent").each(
203
 
                    function(node) {
204
 
                        overlays.push(
205
 
                            node.one('input.overlay').get('checked'));
206
 
                    }
207
 
                );
208
 
                return overlays;
209
 
            }
210
 
        },
211
 
        overlay_pockets: {
212
 
            getter: function() {
213
 
                var overlay_pockets = [];
214
 
                this.fieldNode.all("tbody > tr.parent").each(
215
 
                    function(node) {
216
 
                        var select = node.one('td.pocket').one('select');
217
 
                        if (select !== null) {
218
 
                            overlay_pockets.push(select.get('value'));
219
 
                        }
220
 
                        else {
221
 
                            overlay_pockets.push(null);
222
 
                        }
223
 
                    }
224
 
                );
225
 
                return overlay_pockets;
226
 
            }
227
 
        },
228
 
        overlay_components: {
229
 
            getter: function() {
230
 
                var overlay_components = [];
231
 
                this.fieldNode.all("tbody > tr.parent").each(
232
 
                    function(node) {
233
 
                        var select = node.one('td.component').one('select');
234
 
                        if (select !== null) {
235
 
                            overlay_components.push(select.get('value'));
236
 
                        }
237
 
                        else {
238
 
                            overlay_components.push(null);
239
 
                        }
240
 
                    }
241
 
                );
242
 
                return overlay_components;
243
 
            }
244
 
        }
245
 
    }
246
 
});
247
 
 
248
 
Y.extend(ParentSeriesListWidget, FormRowWidget, {
249
 
 
250
 
    initializer: function() {
251
 
        ParentSeriesListWidget.superclass.initializer();
252
 
        this.client = new Y.lp.client.Launchpad();
253
 
        this.clean_display();
254
 
    },
255
 
 
256
 
    /**
257
 
     * Display a simple message when no parent series are selected.
258
 
     *
259
 
     * @method clean_display
260
 
     */
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!]'));
265
 
    },
266
 
 
267
 
    /**
268
 
     * Display the table header.
269
 
     *
270
 
     * @method init_display
271
 
     */
272
 
    init_display: function() {
273
 
        this.fieldNode.empty();
274
 
        this.fieldNode = Y.Node.create("<table />");
275
 
        var table_header = Y.Node.create(
276
 
            ["<thead><tr>",
277
 
             "<th>Order</th>",
278
 
             "<th>Parent name</th>",
279
 
             "<th>Overlay?</th>",
280
 
             "<th>Component</th>",
281
 
             "<th>Pocket</th>",
282
 
             "<th>Delete</th>",
283
 
             "</tr></thead>",
284
 
             "<tbody>",
285
 
             "</tbody>"
286
 
            ].join(""));
287
 
        this.fieldNode.append(table_header);
288
 
    },
289
 
 
290
 
    /**
291
 
     * Helper method to create a select widget from a list and add it
292
 
     * to a node.
293
 
     *
294
 
     * @method build_selector
295
 
     */
296
 
    build_selector: function(node, res_list, class_name) {
297
 
        var select = Y.Node.create('<select disabled="disabled"/>');
298
 
        res_list.forEach(
299
 
            function(choice) {
300
 
                select.appendChild('<option />')
301
 
                    .set('text', choice)
302
 
                    .set('value', choice);
303
 
            });
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');
309
 
            }
310
 
            else {
311
 
                select.setAttribute('disabled', 'disabled');
312
 
            }
313
 
        });
314
 
    },
315
 
 
316
 
    /**
317
 
     * Build a select widget from a list retrieved from the api.
318
 
     *
319
 
     * @method build_select
320
 
     */
321
 
    build_select: function(node, class_name, path) {
322
 
        var self = this;
323
 
        var on = {
324
 
            success: function(res_list) {
325
 
                self.build_selector(node, res_list, class_name);
326
 
            },
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);
332
 
            }
333
 
        };
334
 
        this.client.get(path, {on: on});
335
 
     },
336
 
 
337
 
 
338
 
    /**
339
 
     * Move down a parent's line in the table.
340
 
     *
341
 
     * @method move_down
342
 
     */
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();
348
 
    },
349
 
 
350
 
    /**
351
 
     * Move up a parent's line in the table.
352
 
     *
353
 
     * @method move_up
354
 
     */
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();
360
 
    },
361
 
 
362
 
    /**
363
 
     * Remove a parent series.
364
 
     *
365
 
     * @method add_parent
366
 
     */
367
 
    remove_parent: function(parent_id) {
368
 
        if (this.get('parents').length === 1) {
369
 
            this.clean_display();
370
 
            this.renderUI();
371
 
        }
372
 
        else {
373
 
            this.fieldNode.one('tr#parent-' + parent_id).remove();
374
 
        }
375
 
        Y.fire("parent_removed", parent_id);
376
 
    },
377
 
 
378
 
    /**
379
 
     * Disable the overlay (used when something goes wrong fetching possible
380
 
     * components or pockets for the overlay).
381
 
     *
382
 
     * @method disable_overlay
383
 
     */
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');
389
 
        }
390
 
    },
391
 
 
392
 
    /**
393
 
     * Add a parent series.
394
 
     *
395
 
     * @method add_parent
396
 
     */
397
 
    add_parent: function(parent) {
398
 
        if (this.get('parents').length === 0) {
399
 
            this.init_display();
400
 
            this.renderUI();
401
 
        }
402
 
        var item = this.fieldNode.one('tr#parent-' + parent.value);
403
 
        if (item !== null) {
404
 
            Y.lazr.anim.red_flash({node: item}).run();
405
 
            return false;
406
 
        }
407
 
        item =  Y.Node.create("<tr />")
408
 
            .addClass('parent')
409
 
            .set('id', 'parent-' + parent.value)
410
 
            .append(Y.Node.create("<td />")
411
 
                .append(Y.Node.create('<a href="" title="Move parent up"/>')
412
 
                    .addClass('move-up')
413
 
                .set('innerHTML', '&uarr;'))
414
 
                .append(Y.Node.create('<a href="" title="Move parent down"/>')
415
 
                    .addClass('move-down')
416
 
                .set('innerHTML', '&darr;')))
417
 
            .append(Y.Node.create("<td />")
418
 
                .addClass('series')
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 />")
427
 
                .addClass('pocket'))
428
 
            .append(Y.Node.create("<td />")
429
 
                .set('align', 'center')
430
 
                .append(Y.Node.create('<span />')
431
 
                    .addClass('sprite')
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);
440
 
            e.preventDefault();
441
 
            return false;
442
 
        }, this);
443
 
        item.one('.move-down').on('click', function(e) {
444
 
            this.move_down(parent.value);
445
 
            e.preventDefault();
446
 
            return false;
447
 
        }, this);
448
 
        item.one('.remove').on('click', function(e) {
449
 
            var parent_id = item.get('id').replace('parent-','');
450
 
            this.remove_parent(parent_id);
451
 
            e.preventDefault();
452
 
            return false;
453
 
        }, this);
454
 
 
455
 
        Y.lazr.anim.green_flash({node: item}).run();
456
 
        return true;
457
 
    }
458
 
});
459
 
 
460
 
namespace.ParentSeriesListWidget = ParentSeriesListWidget;
461
 
 
462
164
 
463
165
/**
464
166
 * A form row matching that which LaunchpadForm presents, containing a
487
189
            },
488
190
            setter: function(value, name) {
489
191
                var choices = Y.Array.unique(value).sort();
 
192
                var field_name = this.get("name");
 
193
                var field_type = this.get("type");
490
194
                var list = Y.Node.create("<ul />");
491
 
                var self = this;
492
195
                choices.forEach(
493
196
                    function(choice) {
494
 
                       var item = self._createChoice(choice);
495
 
                       list.append(item);
 
197
                        var item = Y.Node.create(
 
198
                            "<li><input /> <label /></li>");
 
199
                        item.one("input")
 
200
                            .set("type", field_type)
 
201
                            .set("name", field_name)
 
202
                            .set("value", choice);
 
203
                        item.one("label")
 
204
                            .setAttribute(
 
205
                                "for", item.one("input").generateID())
 
206
                            .setStyle("font-weight", "normal")
 
207
                            .set("text", choice);
 
208
                        list.append(item);
496
209
                    }
497
210
                );
498
211
                this.fieldNode.empty().append(list);
526
239
                        }
527
240
                    }
528
241
                );
529
 
                if (this.get("type") === "radio") {
 
242
                if (this.get("type") == "radio") {
530
243
                    if (choice.length === 0) {
531
244
                        choice = null;
532
245
                    }
533
 
                    else if (choice.length === 1) {
 
246
                    else if (choice.length == 1) {
534
247
                        choice = choice[0];
535
248
                    }
536
249
                    else {
557
270
 
558
271
});
559
272
 
560
 
Y.extend(ChoiceListWidget, FormRowWidget, {
561
 
 
562
 
    /**
563
 
     * Helper method to create an entry for the select widget.
564
 
     *
565
 
     * @method _createChoice
566
 
     */
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>");
572
 
        item.one("input")
573
 
            .set("type", field_type)
574
 
            .set("name", field_name)
575
 
            .set("value", choice);
576
 
        item.one("label")
577
 
            .setAttribute(
578
 
                "for", item.one("input").generateID())
579
 
            .setStyle("font-weight", "normal")
580
 
            .set("text", choice);
581
 
        return item;
582
 
    },
583
 
 
584
 
   /**
585
 
     * Remove a list of choices from the possible widget's choices.
586
 
     *
587
 
     * @method remove_choices
588
 
     */
589
 
    remove_choices: function(choices) {
590
 
        choices.forEach(
591
 
            function(choice) {
592
 
                this.fieldNode.all("select > option").each(
593
 
                    function(option) { options.push(option); });
594
 
                this.fieldNode.all(
595
 
                    "li input[value=" + choice + "]").each(
596
 
                        function(li_input) {
597
 
                            li_input.get('parentNode').remove();
598
 
                        }
599
 
                );
600
 
            },
601
 
            this
602
 
        );
603
 
        Y.lazr.anim.green_flash({node: this.fieldNode}).run();
604
 
    },
605
 
 
606
 
    _sorted_position: function(choice) {
607
 
        var options = [];
608
 
        this.fieldNode.all("input").each(
609
 
            function(node) {
610
 
                options.push(node.get('value'));
611
 
            }
612
 
        );
613
 
        options.push(choice);
614
 
        return options.sort().indexOf(choice);
615
 
    },
616
 
 
617
 
   /**
618
 
     * Add new choices (if they are not already present).
619
 
     *
620
 
     * @method add_choices
621
 
     */
622
 
    add_choices: function(new_choices) {
623
 
        new_choices.forEach(
624
 
            function(choice) {
625
 
                if (this.fieldNode.all(
626
 
                    "li > input[value=" + choice + "]").isEmpty()) {
627
 
                    var list = this.fieldNode.one('ul');
628
 
                    if (list === null) {
629
 
                        list = Y.Node.create("<ul />");
630
 
                        this.fieldNode.empty().append(list);
631
 
                    }
632
 
                    var option = this._createChoice(choice);
633
 
                    var options = list.all('input');
634
 
                    if (options.isEmpty()) {
635
 
                        list.append(option);
636
 
                    }
637
 
                    else {
638
 
                        var pos = this._sorted_position(choice);
639
 
                        if (pos === 0) {
640
 
                            list.prepend(option);
641
 
                        }
642
 
                        else {
643
 
                            list.insertBefore(option, options.item(pos));
644
 
                        }
645
 
                    }
646
 
                }
647
 
            }, this
648
 
        );
649
 
        Y.lazr.anim.green_flash({node: this.fieldNode}).run();
650
 
    }
651
 
 
652
 
});
653
 
 
 
273
Y.extend(ChoiceListWidget, FormRowWidget);
654
274
 
655
275
namespace.ChoiceListWidget = ChoiceListWidget;
656
276
 
670
290
    NAME: 'architecturesChoiceListWidget',
671
291
 
672
292
    ATTRS: {
 
293
 
 
294
        /**
 
295
         * The DistroSeries the choices in this field should
 
296
         * reflect. Takes the form of a string, e.g. "ubuntu/hoary".
 
297
         *
 
298
         * @property distroSeries
 
299
         */
 
300
        distroSeries: {
 
301
            setter: function(value, name) {
 
302
                var path = value + "/architectures";
 
303
                var on = {
 
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)
 
308
                };
 
309
                this.client.get(path, {on: on});
 
310
            }
 
311
        },
 
312
 
 
313
        /**
 
314
         * The DistroArchSerieses the choices in this field should
 
315
         * reflect. Takes the form of a Y.lp.client.Collection.
 
316
         *
 
317
         * @property distroArchSerieses
 
318
         */
 
319
        distroArchSerieses: {
 
320
            setter: function(value, name) {
 
321
                this.set(
 
322
                    "choices", value.entries.map(
 
323
                        function(das) {
 
324
                            return das.get("architecture_tag");
 
325
                        }
 
326
                    )
 
327
                );
 
328
                if (value.entries.length === 0) {
 
329
                    this.fieldNode.append(
 
330
                        Y.Node.create('<p />').set(
 
331
                            "text",
 
332
                            "The chosen series has no architectures!"));
 
333
                }
 
334
                Y.lazr.anim.green_flash({node: this.fieldNode}).run();
 
335
            }
 
336
        }
673
337
    }
674
338
 
675
339
});
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();
686
 
    },
687
 
 
688
 
    /**
689
 
     * Display a simple message when no parent series are selected.
690
 
     *
691
 
     * @method clean_display
692
 
     */
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!]'));
697
 
        this.renderUI();
698
 
    },
699
 
 
700
 
    /**
701
 
     * Add a parent distroseries, add the architectures for this new
702
 
     * distroseries to the possible choices.
703
 
     *
704
 
     * @method add_distroseries
705
 
     * @param {Object} The distroseries to add ({value:distroseries_id),
706
 
     *     api_uri:distroseries_uri}).
707
 
     */
708
 
    add_distroseries: function(distroseries) {
709
 
        var path = distroseries.api_uri + "/architectures";
710
 
        var distroseries_id = distroseries.value;
711
 
        var self = this;
712
 
        var on = {
713
 
            success: function (results) {
714
 
                self.add_distroarchseries(distroseries_id, results);
715
 
            },
716
 
            failure: this.error_handler.getFailureHandler()
717
 
        };
718
 
        this.client.get(path, {on: on});
719
 
     },
720
 
 
721
 
    /**
722
 
     * Remove a parent distroseries, remove the architectures only
723
 
     * present in this parent series from the possible choices.
724
 
     *
725
 
     * @method remove_distroseries
726
 
     */
727
 
    remove_distroseries: function(distroseries_id) {
728
 
        // Compute which das is only in the distroseries to be removed.
729
 
        arch_to_remove = [];
730
 
        var das = this._distroseries[distroseries_id];
731
 
        var i, ds, j;
732
 
        for (i=0; i<das.entries.length; i++) {
733
 
            remove_das = true;
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(
740
 
                           'architecture_tag');
741
 
                       if (other_arch === arch) {
742
 
                           remove_das = false;
743
 
                       }
744
 
                   }
745
 
                }
746
 
            }
747
 
            if (remove_das) {
748
 
                arch_to_remove.push(arch);
749
 
            }
750
 
        }
751
 
        delete this._distroseries[distroseries_id];
752
 
        this.remove_choices(arch_to_remove);
753
 
        if (this.fieldNode.all('input').isEmpty()) {
754
 
            this.clean_display();
755
 
        }
756
 
    },
757
 
 
758
 
    /**
759
 
     * Add a list of distroarchseries.
760
 
     *
761
 
     * @method add_distroarchseries
762
 
     * @param {String} distroseries_id The distroarchseries id.
763
 
     * @param {Object} distroarchseries The distroarchseries object.
764
 
     */
765
 
    add_distroarchseries: function(distroseries_id, distroarchseries) {
766
 
        this._distroseries[distroseries_id] = distroarchseries;
767
 
        var choices = distroarchseries.entries.map(
768
 
            function(das) {
769
 
                return das.get("architecture_tag");
770
 
            }
771
 
        );
772
 
        this.add_choices(choices);
773
 
     }
 
348
    }
774
349
 
775
350
});
776
351
 
905
480
 
906
481
Y.extend(SelectWidget, FormRowWidget, {
907
482
 
908
 
    _sorted_position: function(choice) {
909
 
        var options = [];
910
 
        this.fieldNode.all("option").each(
911
 
            function(node) {
912
 
                options.push(node.get('text'));
913
 
            }
914
 
        );
915
 
        options.push(choice);
916
 
        return options.sort().indexOf(choice);
917
 
    },
918
 
 
919
483
    /**
920
484
     * Choose a size for the select control based on the number of
921
485
     * choices, up to an optional maximum size.
983
547
                };
984
548
                this.client.named_get("package-sets", "getBySeries", config);
985
549
            }
 
550
        },
 
551
 
 
552
        /**
 
553
         * The Packagesets the choices in this field should
 
554
         * reflect. Takes the form of a Y.lp.client.Collection.
 
555
         *
 
556
         * @property packageSets
 
557
         */
 
558
        packageSets: {
 
559
            setter: function(value, name) {
 
560
                this.set(
 
561
                    "choices", value.entries.map(
 
562
                        function(packageset) {
 
563
                            return {
 
564
                                data: packageset,
 
565
                                value: packageset.get("name"),
 
566
                                text: (
 
567
                                    packageset.get("name") + ": " +
 
568
                                    packageset.get("description"))
 
569
                            };
 
570
                        }
 
571
                    )
 
572
                );
 
573
                if (value.entries.length === 0) {
 
574
                    this.fieldNode.append(
 
575
                        Y.Node.create('<p />').set(
 
576
                            "text",
 
577
                            "The chosen series has no package sets!"));
 
578
                }
 
579
                else {
 
580
                    this.autoSize(10);
 
581
                }
 
582
                Y.lazr.anim.green_flash({node: this.fieldNode}).run();
 
583
            }
986
584
        }
 
585
 
987
586
    }
 
587
 
988
588
});
989
589
 
990
 
 
991
590
Y.extend(PackagesetPickerWidget, SelectWidget, {
992
591
 
993
 
    /**
994
 
     * Add a distroseries: add its packagesets to the packageset picker.
995
 
     *
996
 
     * @method add_distroseries
997
 
     * @param {Object} distroseries The distroseries object.
998
 
     */
999
 
    add_distroseries: function(distroseries) {
1000
 
        var distro_series_uri = Y.lp.client.get_absolute_uri(
1001
 
            distroseries.api_uri);
1002
 
        var self = this;
1003
 
        var on = {
1004
 
            success: function (results) {
1005
 
                self.add_packagesets(results, distroseries);
1006
 
            },
1007
 
            failure: this.error_handler.getFailureHandler()
1008
 
        };
1009
 
        var config = {
1010
 
            on: on,
1011
 
            parameters: {
1012
 
                distroseries: distro_series_uri
1013
 
            }
1014
 
        };
1015
 
        this.client.named_get("package-sets", "getBySeries", config);
1016
 
    },
1017
 
 
1018
 
    /**
1019
 
     * Display a simple message when no parent series are selected.
1020
 
     *
1021
 
     * @method clean_display
1022
 
     */
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!]'));
1027
 
        this.renderUI();
1028
 
    },
1029
 
 
1030
 
    /**
1031
 
     * Initialise the picker's select node.
1032
 
     *
1033
 
     * @method init_select
1034
 
     */
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");
1043
 
            }
1044
 
            this.fieldNode.empty().append(select);
1045
 
        }
1046
 
        return select;
1047
 
    },
1048
 
 
1049
 
    /**
1050
 
     * Add a choice to the picker.
1051
 
     *
1052
 
     * @method add_choice
1053
 
     * @param {Object} choice The choice object to be added
1054
 
     *     ({text: choice_text, value: choice_value, data: choice_data}).
1055
 
     */
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);
1065
 
        }
1066
 
        else {
1067
 
            var pos = this._sorted_position(choice.text);
1068
 
            if (pos === 0) {
1069
 
                select.prepend(option);
1070
 
            }
1071
 
            else {
1072
 
                select.insertBefore(option, options.item(pos));
1073
 
            }
1074
 
        }
1075
 
    },
1076
 
 
1077
 
    /**
1078
 
     * Add choices (a set of packagesets) to the picker.
1079
 
     *
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}).
1085
 
     */
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");
1091
 
                this.add_choice({
1092
 
                    data: packageset,
1093
 
                    value: value,
1094
 
                    text: (
1095
 
                        packageset.get("name") + ": " +
1096
 
                        packageset.get("description") +
1097
 
                        " (" + distroseries.title + ") ")
1098
 
                });
1099
 
            }, this);
1100
 
        this.autoSize(10);
1101
 
        Y.lazr.anim.green_flash({node: this.fieldNode}).run();
1102
 
     },
1103
 
 
1104
 
    /**
1105
 
     * Remove a distroseries: remove its packagesets from the picker.
1106
 
     *
1107
 
     * @method remove_distroseries
1108
 
     * @param {String} distroseries_id The id of the distroseries to be
1109
 
     *     removed.
1110
 
     */
1111
 
    remove_distroseries: function(distroseries_id) {
1112
 
        this._packagesets[distroseries_id].forEach(
1113
 
            function(packageset) {
1114
 
                this.fieldNode.one(
1115
 
                    'option[value="' + packageset.get("id") + '"]').remove();
1116
 
            }, this);
1117
 
       Y.lazr.anim.green_flash({node: this.fieldNode}).run();
1118
 
        if (this.fieldNode.all('option').isEmpty()) {
1119
 
            this.clean_display();
1120
 
        }
1121
 
    },
1122
 
 
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 = {};
1135
597
    }
1136
598
 
1137
599
});
1234
696
 
1235
697
    initializer: function(config) {
1236
698
        this.context = config.context;
1237
 
        this.deriveFromChoices = config.deriveFromChoices;
 
699
        this.deriveFromChoice = config.deriveFromChoice;
1238
700
        this.architectureChoice = config.architectureChoice;
1239
701
        this.packagesetChoice = config.packagesetChoice;
1240
702
        this.packageCopyOptions = config.packageCopyOptions;
1281
743
            parameters: {
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(
1291
 
                    "overlay_pockets"),
1292
 
                overlay_components: this.deriveFromChoices.get(
1293
 
                    "overlay_components")
 
748
                rebuild: this.packageCopyOptions.get("choice") == (
 
749
                    "Copy Source and Rebuild")
1294
750
            }
1295
751
        };
 
752
        var parent = this.deriveFromChoice.get("value");
1296
753
        this.client.named_post(
1297
 
            this.context.self_link, "initDerivedDistroSeries", config);
 
754
            parent, "deriveDistroSeries", config);
1298
755
    }
1299
756
 
1300
757
});
1301
758
 
1302
759
namespace.DeriveDistroSeriesActionsWidget = DeriveDistroSeriesActionsWidget;
1303
760
 
1304
 
/*
1305
 
 * Show the "Add parent series" overlay.
1306
 
 */
1307
 
var show_add_parent_series_form = function(e) {
1308
 
 
1309
 
    e.preventDefault();
1310
 
    var config = {
1311
 
        header: 'Add a parent series',
1312
 
        step_title: 'Search'
1313
 
    };
1314
 
 
1315
 
    config.save = function(result) {
1316
 
        add_parent_series(result);
1317
 
    };
1318
 
 
1319
 
    parent_picker = Y.lp.app.picker.create('DistroSeriesDerivation', config);
1320
 
    parent_picker.show();
1321
 
};
1322
 
 
1323
 
namespace.show_add_parent_series_form = show_add_parent_series_form;
1324
 
 
1325
 
/*
1326
 
 * Add a parent series.
1327
 
 */
1328
 
var add_parent_series = function(parent) {
1329
 
    Y.fire("add_parent", parent);
1330
 
};
1331
 
 
1332
 
namespace.add_parent_series = add_parent_series;
1333
 
 
1334
761
 
1335
762
/**
1336
763
 * Setup the widgets on the +initseries page.
1339
766
 */
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");
 
770
 
 
771
    // Pre-existing form fields.
 
772
    var derive_from_choice =
 
773
        form_table_body.one("[name=field.derived_from_series]");
1344
774
 
1345
775
    // Widgets.
1346
 
    var add_parent_link = Y.Node.create('<a href="+add-parent-series">')
1347
 
            .addClass("sprite")
1348
 
            .addClass("add")
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 " +
1367
 
                     "architectures)."))
 
782
                     "use from the parent series."))
1368
783
            .render(form_table_body);
1369
784
    var packageset_choice =
1370
785
        new PackagesetPickerWidget()
1376
791
            .set("label", "Package sets to copy from parent:")
1377
792
            .set("description", (
1378
793
                     "The package sets that will be imported " +
1379
 
                     "into the derived distroseries (select none " +
1380
 
                     "if you want to import all the available " +
1381
 
                     "package sets)."))
 
794
                     "into the derived distroseries."))
1382
795
            .render(form_table_body);
1383
796
    var package_copy_options =
1384
797
        new ChoiceListWidget()
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."));
1403
 
    }
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
1412
816
        });
1413
817
 
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);
1419
 
    }
1420
 
 
1421
 
    Y.on('add_parent', function(parent) {
1422
 
        var added = parents_selection.add_parent(parent);
1423
 
        if (added) {
1424
 
            Y.fire("parent_added", parent);
1425
 
        }
1426
 
    });
1427
 
 
1428
 
    Y.on('parent_added', function(parent) {
1429
 
        architecture_choice.add_distroseries(parent);
1430
 
        packageset_choice.add_distroseries(parent);
1431
 
    });
1432
 
 
1433
 
    Y.on('parent_removed', function(parent_id) {
1434
 
        architecture_choice.remove_distroseries(parent_id);
1435
 
        packageset_choice.remove_distroseries(parent_id);
1436
 
    });
 
818
    // Wire up the distroseries select to the architectures widget.
 
819
    function update_architecture_choice() {
 
820
        architecture_choice
 
821
            .set("distroSeries", derive_from_choice.get("value"));
 
822
    }
 
823
    derive_from_choice.on("change", update_architecture_choice);
 
824
    // Update the architectures widget for the selected distroseries.
 
825
    update_architecture_choice();
 
826
 
 
827
    // Wire up the distroseries select to the packagesets widget.
 
828
    function update_packageset_choice() {
 
829
        packageset_choice
 
830
            .set("distroSeries", derive_from_choice.get("value"));
 
831
    }
 
832
    derive_from_choice.on("change", update_packageset_choice);
 
833
    // Update the packagesets widget for the selected distroseries.
 
834
    update_packageset_choice();
1437
835
 
1438
836
    // Wire up the form submission.
1439
837
    form_container.one("form").on(
1446
844
 
1447
845
 
1448
846
}, "0.1", {"requires": ["node", "dom", "io", "widget", "lp.client",
1449
 
                        "lazr.anim", "array-extras", "transition",
1450
 
                        "lp.app.picker"]});
 
847
                        "lazr.anim", "array-extras", "transition"]});