~launchpad-pqm/launchpad/devel

« back to all changes in this revision

Viewing changes to lib/lp/app/javascript/multicheckbox.js

[r=abentley][bug=735899] Use the new inline multicheckbox selection
        widget to edit the source package recipe distroseries attribute.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
YUI.add('lp.app.multicheckbox', function(Y) {
 
2
 
 
3
var namespace = Y.namespace('lp.app.multicheckbox');
 
4
 
 
5
/* Add a multicheckbox widget which will PATCH a given attribute on
 
6
 * a given resource.
 
7
 *
 
8
 * @method addMultiCheckboxPatcher
 
9
 * @param {Array} items The items which to display as checkboxes.
 
10
 * @param {String} help_text The text to display beneath the checkboxes.
 
11
 * @param {String} resource_uri The object being modified.
 
12
 * @param {String} attribute_name The attribute on the resource being
 
13
 *                                modified.
 
14
 * @param {String} attribute_type The attribute type.
 
15
 *     "reference": the items are object references
 
16
 *     Other values are currently ignored.
 
17
 * @param {String} content_box_id
 
18
 * @param {Object} config Object literal of config name/value pairs.
 
19
 *     config.header: a line of text at the top of the widget.
 
20
 *     config.step_title: overrides the subtitle.
 
21
 */
 
22
namespace.addMultiCheckboxPatcher = function (
 
23
    items, help_text, resource_uri, attribute_name, attribute_type,
 
24
    content_box_id, config, client) {
 
25
 
 
26
 
 
27
    if (Y.UA.ie) {
 
28
        return;
 
29
    }
 
30
 
 
31
    // We may have been passed a mock client for testing but if not, create
 
32
    // a proper one.
 
33
    if (client === undefined)
 
34
        client = new Y.lp.client.Launchpad();
 
35
 
 
36
    var content_box = Y.one('#'+content_box_id);
 
37
    var result_node = Y.one('#'+content_box_id+'-items');
 
38
    var widget_node = Y.one('#'+attribute_name);
 
39
    var activator = new Y.lazr.activator.Activator(
 
40
        {contentBox: content_box, animationNode: widget_node});
 
41
 
 
42
    var failure_handler = function (id, response, args) {
 
43
        activator.renderFailure(
 
44
            Y.Node.create(
 
45
                '<div>' + response.statusText +
 
46
                    '<pre>' + response.responseText + '</pre>' +
 
47
                '</div>'));
 
48
    };
 
49
 
 
50
    // The function called to save the selected items.
 
51
    function save(editform, item_value_mapping) {
 
52
        var choice_nodes = Y.one('[id="'+attribute_name+'.items"]');
 
53
        var result = namespace.getSelectedItems(
 
54
            choice_nodes, item_value_mapping, attribute_type);
 
55
        activator.renderProcessing();
 
56
        var success_handler = function (entry) {
 
57
            var xhtml = entry.getHTML(attribute_name);
 
58
            result_node.set('innerHTML', xhtml);
 
59
            activator.renderSuccess(result_node);
 
60
        };
 
61
 
 
62
        var patch_payload = {};
 
63
        patch_payload[attribute_name] = result;
 
64
        client.patch(editform._resource_uri, patch_payload, {
 
65
            accept: 'application/json;include=lp_html',
 
66
            on: {
 
67
                success: success_handler,
 
68
                failure: failure_handler
 
69
            }
 
70
        });
 
71
    }
 
72
 
 
73
    config.save = save;
 
74
    config.content_box_id = content_box_id;
 
75
    var editform = namespace.create(attribute_name, items, help_text, config);
 
76
    editform._resource_uri = resource_uri;
 
77
    var extra_buttons = Y.Node.create(
 
78
        '<div style="text-align: center; height: 3em; ' +
 
79
        'white-space: nowrap"/>');
 
80
    activator.subscribe('act', function (e) {
 
81
        editform.show();
 
82
    });
 
83
    activator.render();
 
84
    return editform;
 
85
};
 
86
 
 
87
 
 
88
/**
 
89
  * Creates a multicheckbox widget that has already been rendered and hidden.
 
90
  *
 
91
  * @requires dom, lazr.activator, lazr.overlay
 
92
  * @method create
 
93
  * @param {String} attribute_name The attribute on the resource being
 
94
  *                                modified.
 
95
  * @param {Array} items Items for which to create checkbox elements.
 
96
  * @param {String} help_text text display below the checkboxes.
 
97
  * @param {Object} config Optional Object literal of config name/value pairs.
 
98
  *                        config.header is a line of text at the top of
 
99
  *                        the widget.
 
100
  *                        config.save is a Function (optional) which takes
 
101
  *                        a single string argument.
 
102
  */
 
103
namespace.create = function (attribute_name, items, help_text, config) {
 
104
    if (Y.UA.ie) {
 
105
        return;
 
106
    }
 
107
 
 
108
    if (config !== undefined) {
 
109
        var header = 'Choose an item.';
 
110
        if (config.header !== undefined) {
 
111
            header = config.header;
 
112
        }
 
113
    }
 
114
 
 
115
    var new_config = Y.merge(config, {
 
116
        align: {
 
117
            points: [Y.WidgetPositionAlign.CC,
 
118
                     Y.WidgetPositionAlign.CC]
 
119
        },
 
120
        progressbar: true,
 
121
        progress: 100,
 
122
        headerContent: "<h2>" + header + "</h2>",
 
123
        centered: true,
 
124
        zIndex: 1000,
 
125
        visible: false
 
126
        });
 
127
 
 
128
    // We use a pretty overlay to display the checkboxes.
 
129
    var editform = new Y.lazr.PrettyOverlay(new_config);
 
130
 
 
131
    // The html for each checkbox.
 
132
    var CHECKBOX_TEMPLATE =
 
133
        ['<label style="{item_style}" for="{field_name}.{field_index}">',
 
134
        '<input id="{field_name}.{field_index}" ',
 
135
        'name="{field_name}.{field_index}" ',
 
136
        'class="checkboxType" type="checkbox" value="{field_value}" ',
 
137
        '{item_checked}>&nbsp;{field_text}</label>'].join("");
 
138
 
 
139
    var content = Y.Node.create("<div/>");
 
140
    var header_node = Y.Node.create(
 
141
        "<div class='yui3-lazr-formoverlay-form-header'/>");
 
142
    content.appendChild(header_node);
 
143
    var body = Y.Node.create("<div class='yui3-widget-bd'/>");
 
144
 
 
145
    // Set up the nodes for each checkbox.
 
146
    var choices_nodes = Y.Node.create('<ul id="'+attribute_name+'.items"/>');
 
147
    // A mapping from checkbox value attributes (data token) -> data values
 
148
    var item_value_mapping = {};
 
149
    Y.Array.each(items, function(data, i) {
 
150
        var checked_html = '';
 
151
        if (data.checked)
 
152
            checked_html = 'checked="checked"';
 
153
        var checkbox_html = Y.Lang.substitute(
 
154
            CHECKBOX_TEMPLATE,
 
155
            {field_name: "field."+attribute_name, field_index:i,
 
156
            field_value: data.token, field_text: data.name,
 
157
            item_style: data.style, item_checked: checked_html});
 
158
 
 
159
        var choice_item = Y.Node.create("<li/>");
 
160
        choice_item.set("innerHTML", checkbox_html);
 
161
        choices_nodes.appendChild(choice_item);
 
162
        item_value_mapping[data.token] = data.value;
 
163
    }, this);
 
164
    body.appendChild(choices_nodes);
 
165
    content.appendChild(body);
 
166
    var help_node = Y.Node.create("<p class='formHelp'>"+help_text+"</p>");
 
167
    content.appendChild(help_node);
 
168
    editform.set('bodyContent', content);
 
169
 
 
170
    // We replace the default Close button (x) with our own save/cancel ones.
 
171
    var close_node = editform.get('boundingBox').one('div.close');
 
172
    var orig_close_button = close_node.one('a');
 
173
    orig_close_button.setAttribute('style', 'display: none');
 
174
    var save_button = Y.Node.create(
 
175
        '<button id="'+config.content_box_id+'-save" ' +
 
176
        'class="overlay-close-button lazr-pos lazr-btn">Ok</button>');
 
177
    var close_button = Y.Node.create(
 
178
        '<button class="overlay-close-button lazr-neg lazr-btn">Cancel' +
 
179
        '</button>');
 
180
    save_button.on('click', function(e) {
 
181
        e.halt();
 
182
        editform.hide();
 
183
        config.save(editform, item_value_mapping);
 
184
    });
 
185
    close_button.on('click', function(e) {
 
186
        e.halt();
 
187
        editform.fire('cancel');
 
188
    });
 
189
    close_node.appendChild(close_button);
 
190
    close_node.appendChild(save_button);
 
191
    editform.render();
 
192
    editform.hide();
 
193
    return editform;
 
194
};
 
195
 
 
196
 
 
197
/*
 
198
 * Return a list of the selected checkbox values.
 
199
 * Exposed via the namespace so it is accessible to tests.
 
200
 */
 
201
namespace.getSelectedItems = function(choice_nodes, item_value_mapping,
 
202
                                      attribute_type) {
 
203
    var result = [];
 
204
    choice_nodes.all('.checkboxType').each(function(item) {
 
205
        if (item.get("checked")) {
 
206
            var item_token = item.getAttribute("value");
 
207
            var item_value = item_value_mapping[item_token];
 
208
            var marshalled_value = marshall(item_value, attribute_type);
 
209
            result.push(marshalled_value);
 
210
        }
 
211
    });
 
212
    return result;
 
213
};
 
214
 
 
215
 
 
216
/*
 
217
 * Transform the selected value according to the attribute type we are editing
 
218
 */
 
219
function marshall(value, attribute_type) {
 
220
    switch (attribute_type) {
 
221
        case "reference":
 
222
            var item_value = Y.lp.client.normalize_uri(value);
 
223
            return Y.lp.client.get_absolute_uri(item_value);
 
224
        break;
 
225
        default:
 
226
            return value;
 
227
    }
 
228
}
 
229
 
 
230
}, "0.1", {"requires": [
 
231
    "dom", "lazr.overlay", "lazr.activator", "lp.client"
 
232
    ]});