~launchpad-pqm/launchpad/devel

« back to all changes in this revision

Viewing changes to lib/lp/app/javascript/lazr/activator/activator.js

[r=deryck][bug=803954] Bring lazr-js source into lp tree and package
        yui as a dependency

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (c) 2009, Canonical Ltd. All rights reserved. */
 
2
 
 
3
YUI.add('lazr.activator', function(Y) {
 
4
 
 
5
var ACTIVATOR     = 'activator',
 
6
 
 
7
    // Local aliases
 
8
    getCN = Y.ClassNameManager.getClassName,
 
9
 
 
10
    // Templates.
 
11
    MESSAGE_HEADER_TEMPLATE = '<div></div>',
 
12
    MESSAGE_BODY_TEMPLATE = '<div></div>',
 
13
    MESSAGE_CLOSE_BUTTON_TEMPLATE = '<button>Close</button>',
 
14
 
 
15
    // Events.
 
16
    ACT           = 'act',
 
17
 
 
18
    // Class for hiding elements.
 
19
    C_HIDDEN      = getCN(ACTIVATOR, 'hidden'),
 
20
 
 
21
    // Classes identifying elements.
 
22
    C_ACT         = getCN(ACTIVATOR, 'act'),
 
23
    C_DATA_BOX    = getCN(ACTIVATOR, 'data-box'),
 
24
    C_MESSAGE_BOX = getCN(ACTIVATOR, 'message-box'),
 
25
 
 
26
    // Classes for internally created elements.
 
27
    C_MESSAGE_CLOSE  = getCN(ACTIVATOR, 'message-close'),
 
28
    C_MESSAGE_HEADER = getCN(ACTIVATOR, 'message-header'),
 
29
    C_MESSAGE_BODY   = getCN(ACTIVATOR, 'message-body'),
 
30
 
 
31
    // Classes indicating status.
 
32
    C_PROCESSING  = getCN(ACTIVATOR, 'processing'),
 
33
    C_CANCEL      = getCN(ACTIVATOR, 'cancellation'),
 
34
    C_SUCCESS     = getCN(ACTIVATOR, 'success'),
 
35
    C_FAILURE     = getCN(ACTIVATOR, 'failure'),
 
36
 
 
37
    ALL_STATUSES  = [C_SUCCESS, C_FAILURE, C_CANCEL, C_PROCESSING];
 
38
 
 
39
/**
 
40
 * The Activator widget will hook up an element to trigger an action,
 
41
 * and it will change the display to indicate whether the action
 
42
 * is processing, ended successfully, ended in error, or was cancelled.
 
43
 *
 
44
 * A CSS class is applied in each different state. Success and
 
45
 * failure also trigger a green or red flash animation.
 
46
 *
 
47
 * @class Activator
 
48
 * @constructor
 
49
 * @extends Widget
 
50
 */
 
51
var Activator = function() {
 
52
    Activator.superclass.constructor.apply(this, arguments);
 
53
};
 
54
 
 
55
Activator.NAME = ACTIVATOR;
 
56
 
 
57
Activator.ATTRS = {};
 
58
 
 
59
Y.extend(Activator, Y.Widget, {
 
60
 
 
61
    /**
 
62
     * Destination of status messages.
 
63
     *
 
64
     * @property message_box
 
65
     * @type Node
 
66
     */
 
67
    message_box: null,
 
68
 
 
69
    /**
 
70
     * Destination of new data. Useful when activating an editor.
 
71
     *
 
72
     * @property data_box
 
73
     * @type Node
 
74
     */
 
75
    data_box: null,
 
76
 
 
77
    /**
 
78
     * Element that triggers the event.
 
79
     *
 
80
     * @property action_element
 
81
     * @type Node
 
82
     */
 
83
    action_element: null,
 
84
 
 
85
    /**
 
86
     * Set the CSS class on the context box to indicate that the status is
 
87
     * either error, success, cancellation, or processing.
 
88
     *
 
89
     * @method _setStatusClass
 
90
     * @protected.
 
91
     */
 
92
    _setStatusClass: function(css_class) {
 
93
        Y.Array.each(ALL_STATUSES, function (old_class, i) {
 
94
            this.get('contentBox').removeClass(old_class);
 
95
        }, this);
 
96
        this.get('contentBox').addClass(css_class);
 
97
    },
 
98
 
 
99
    /**
 
100
     * Display message for either error, success, cancellation, or
 
101
     * processing.
 
102
     *
 
103
     * @method _renderMessage
 
104
     * @protected.
 
105
     */
 
106
    _renderMessage: function(title, message_node) {
 
107
        this.message_box.set('innerHTML', '');
 
108
        if (message_node === undefined) {
 
109
            this.message_box.addClass(C_HIDDEN);
 
110
        } else {
 
111
            this.message_box.removeClass(C_HIDDEN);
 
112
 
 
113
            // Close button
 
114
            var message_close_button = Y.Node.create(
 
115
                MESSAGE_CLOSE_BUTTON_TEMPLATE);
 
116
            message_close_button.addClass(C_MESSAGE_CLOSE);
 
117
            message_close_button.addClass('lazr-btn');
 
118
 
 
119
            message_close_button.on('click', function (e) {
 
120
                this.message_box.addClass(C_HIDDEN);
 
121
            }, this);
 
122
 
 
123
            // Header
 
124
            var message_header = Y.Node.create(MESSAGE_HEADER_TEMPLATE);
 
125
            message_header.appendChild(message_close_button);
 
126
            message_header.appendChild(Y.Node.create(title));
 
127
            message_header.addClass(C_MESSAGE_HEADER);
 
128
 
 
129
            // Body
 
130
            var message_body = Y.Node.create(
 
131
                MESSAGE_BODY_TEMPLATE);
 
132
            message_body.appendChild(message_node);
 
133
            message_body.addClass(C_MESSAGE_BODY);
 
134
 
 
135
            this.message_box.appendChild(message_header);
 
136
            this.message_box.appendChild(message_body);
 
137
        }
 
138
    },
 
139
 
 
140
    /**
 
141
     * Animate that the action occurred successfully, and overwrite the
 
142
     * contents of the element which has the C_DATA_BOX class.
 
143
     *
 
144
     * @method renderSuccess
 
145
     * @param {Node} data_node Optional parameter to update data. Normally
 
146
     *                         this would indicate editing a field.
 
147
     * @param {Node} message_node Optional parameter to display a message.
 
148
     * @protected
 
149
     */
 
150
    renderSuccess: function(data_node, message_node) {
 
151
        if (data_node !== undefined) {
 
152
            this.data_box.set('innerHTML', '');
 
153
            this.data_box.appendChild(data_node);
 
154
        }
 
155
        this._setStatusClass(C_SUCCESS);
 
156
        this._renderMessage('Message', message_node);
 
157
        var anim = Y.lazr.anim.green_flash({node: this.animation_node});
 
158
        anim.run();
 
159
    },
 
160
 
 
161
    /**
 
162
     * Animate failure.
 
163
     *
 
164
     * @method renderFailure
 
165
     * @param {Node} Optional parameter to display a message.
 
166
     * @protected.
 
167
     */
 
168
    renderFailure: function(message_node) {
 
169
        this._renderMessage('Error', message_node);
 
170
        this._setStatusClass(C_FAILURE);
 
171
        var anim = Y.lazr.anim.red_flash({node: this.animation_node});
 
172
        anim.run();
 
173
    },
 
174
 
 
175
    /**
 
176
     * Animate cancellation.
 
177
     *
 
178
     * @method renderCancellation
 
179
     * @param {Node} Optional parameter to display a message.
 
180
     * @protected.
 
181
     */
 
182
    renderCancellation: function(message_node) {
 
183
        this._renderMessage('Message', message_node);
 
184
        this._setStatusClass(C_CANCEL);
 
185
        var anim = Y.lazr.anim.red_flash({node: this.animation_node});
 
186
        anim.run();
 
187
    },
 
188
 
 
189
    /**
 
190
     * Indicate that the action is processing. This is normally done
 
191
     * by configuring the C_PROCESSING class to display a spinning
 
192
     * animated GIF.
 
193
     *
 
194
     * @method renderProcessing
 
195
     * @param {Node} Optional parameter to display a message.
 
196
     * @protected.
 
197
     */
 
198
    renderProcessing: function(message_node) {
 
199
        this._renderMessage('Message', message_node);
 
200
        this._setStatusClass(C_PROCESSING);
 
201
    },
 
202
 
 
203
    /**
 
204
     * Initialize the widget.
 
205
     *
 
206
     * @method initializer
 
207
     * @protected
 
208
     */
 
209
    initializer: function(cfg) {
 
210
        this.publish(ACT);
 
211
        if (cfg === undefined || cfg.contentBox === undefined) {
 
212
            // We need the contentBox to be passed in the cfg,
 
213
            // although the init method is the one that actually copies
 
214
            // that cfg to the contentBox ATTR.
 
215
            throw new Error("Missing contentBox argument for Activator.");
 
216
        }
 
217
        this.message_box = this.get('contentBox').one('.' + C_MESSAGE_BOX);
 
218
        if (this.message_box === null) {
 
219
            throw new Error("Can't find element with CSS class " +
 
220
                C_MESSAGE_BOX + ".");
 
221
        }
 
222
        this.data_box = this.get('contentBox').one('.' + C_DATA_BOX);
 
223
        if (this.data_box === null) {
 
224
            throw new Error("Can't find element with CSS class " +
 
225
                C_DATA_BOX + ".");
 
226
        }
 
227
        this.action_element = this.get('contentBox').one('.' + C_ACT);
 
228
        if (this.action_element === null) {
 
229
            throw new Error("Can't find element with CSS class " +
 
230
                C_ACT + ".");
 
231
        }
 
232
        this.animation_node = cfg.animationNode;
 
233
        if (this.animation_node === undefined) {
 
234
            this.animation_node = this.get('contentBox');
 
235
        }
 
236
    },
 
237
 
 
238
    /**
 
239
     * Update the DOM structure and edit CSS classes.
 
240
     *
 
241
     * @method renderUI
 
242
     * @protected
 
243
     */
 
244
    renderUI: function() {
 
245
        // Just in case the user didn't assign the correct classes.
 
246
        this.action_element.removeClass(C_HIDDEN);
 
247
        // Use &thinsp; character to prevent IE7 from hiding the
 
248
        // yui3-activator-act button, when it just has a background-image
 
249
        // and no content in it or in the data_box.
 
250
        this.get('contentBox').prepend('&thinsp;');
 
251
    },
 
252
 
 
253
    /**
 
254
     * Set the event handler for the actor element.
 
255
     *
 
256
     * @method bindUI
 
257
     * @protected
 
258
     */
 
259
    bindUI: function() {
 
260
        var activator = this;
 
261
        Y.on('click', function(e) {
 
262
            activator.fire(ACT);
 
263
            e.preventDefault();
 
264
        }, this.action_element);
 
265
    },
 
266
 
 
267
    /**
 
268
     * UI syncing should all be handled by the status events.
 
269
     *
 
270
     * @method syncUI
 
271
     * @protected
 
272
     */
 
273
    syncUI: function() {
 
274
    }
 
275
});
 
276
 
 
277
Y.lazr.ui.disableTabIndex(Activator);
 
278
 
 
279
Y.namespace('lazr.activator');
 
280
Y.lazr.activator.Activator = Activator;
 
281
 
 
282
 
 
283
}, "0.1", {"skinnable": true,
 
284
           "requires": ["oop", "event", "node", "widget",
 
285
                        "lazr.anim", "lazr.base"]});