Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
YUI.add('widget-position-align', function (Y, NAME) {
2
 
3
/**
4
Provides extended/advanced XY positioning support for Widgets, through an
5
extension.
6
 
7
It builds on top of the `widget-position` module, to provide alignment and
8
centering support. Future releases aim to add constrained and fixed positioning
9
support.
10
 
11
@module widget-position-align
12
**/
13
var Lang = Y.Lang,
14
 
15
    ALIGN        = 'align',
16
    ALIGN_ON     = 'alignOn',
17
 
18
    VISIBLE      = 'visible',
19
    BOUNDING_BOX = 'boundingBox',
20
 
21
    OFFSET_WIDTH    = 'offsetWidth',
22
    OFFSET_HEIGHT   = 'offsetHeight',
23
    REGION          = 'region',
24
    VIEWPORT_REGION = 'viewportRegion';
25
 
26
/**
27
Widget extension, which can be used to add extended XY positioning support to
28
the base Widget class, through the `Base.create` method.
29
 
30
**Note:** This extension requires that the `WidgetPosition` extension be added
31
to the Widget (before `WidgetPositionAlign`, if part of the same extension list
32
passed to `Base.build`).
33
 
34
@class WidgetPositionAlign
35
@param {Object} config User configuration object.
36
@constructor
37
**/
38
function PositionAlign (config) {}
39
 
40
PositionAlign.ATTRS = {
41
 
42
    /**
43
    The alignment configuration for this widget.
44
 
45
    The `align` attribute is used to align a reference point on the widget, with
46
    the reference point on another `Node`, or the viewport. The object which
47
    `align` expects has the following properties:
48
 
49
      * __`node`__: The `Node` to which the widget is to be aligned. If set to
50
        `null`, or not provided, the widget is aligned to the viewport.
51
 
52
      * __`points`__: A two element Array, defining the two points on the widget
53
        and `Node`/viewport which are to be aligned. The first element is the
54
        point on the widget, and the second element is the point on the
55
        `Node`/viewport. Supported alignment points are defined as static
56
        properties on `WidgetPositionAlign`.
57
 
58
    @example Aligns the top-right corner of the widget with the top-left corner
59
    of the viewport:
60
 
61
        myWidget.set('align', {
62
            points: [Y.WidgetPositionAlign.TR, Y.WidgetPositionAlign.TL]
63
        });
64
 
65
    @attribute align
66
    @type Object
67
    @default null
68
    **/
69
    align: {
70
        value: null
71
    },
72
 
73
    /**
74
    A convenience Attribute, which can be used as a shortcut for the `align`
75
    Attribute.
76
 
77
    If set to `true`, the widget is centered in the viewport. If set to a `Node`
78
    reference or valid selector String, the widget will be centered within the
79
    `Node`. If set to `false`, no center positioning is applied.
80
 
81
    @attribute centered
82
    @type Boolean|Node
83
    @default false
84
    **/
85
    centered: {
86
        setter : '_setAlignCenter',
87
        lazyAdd:false,
88
        value  :false
89
    },
90
 
91
    /**
92
    An Array of Objects corresponding to the `Node`s and events that will cause
93
    the alignment of this widget to be synced to the DOM.
94
 
95
    The `alignOn` Attribute is expected to be an Array of Objects with the
96
    following properties:
97
 
98
      * __`eventName`__: The String event name to listen for.
99
 
100
      * __`node`__: The optional `Node` that will fire the event, it can be a
101
        `Node` reference or a selector String. This will default to the widget's
102
        `boundingBox`.
103
 
104
    @example Sync this widget's alignment on window resize:
105
 
106
        myWidget.set('alignOn', [
107
            {
108
                node     : Y.one('win'),
109
                eventName: 'resize'
110
            }
111
        ]);
112
 
113
    @attribute alignOn
114
    @type Array
115
    @default []
116
    **/
117
    alignOn: {
118
        value    : [],
119
        validator: Y.Lang.isArray
120
    }
121
};
122
 
123
/**
124
Constant used to specify the top-left corner for alignment
125
 
126
@property TL
127
@type String
128
@value 'tl'
129
@static
130
**/
131
PositionAlign.TL = 'tl';
132
 
133
/**
134
Constant used to specify the top-right corner for alignment
135
 
136
@property TR
137
@type String
138
@value 'tr'
139
@static
140
**/
141
PositionAlign.TR = 'tr';
142
 
143
/**
144
Constant used to specify the bottom-left corner for alignment
145
 
146
@property BL
147
@type String
148
@value 'bl'
149
@static
150
**/
151
PositionAlign.BL = 'bl';
152
 
153
/**
154
Constant used to specify the bottom-right corner for alignment
155
 
156
@property BR
157
@type String
158
@value 'br'
159
@static
160
**/
161
PositionAlign.BR = 'br';
162
 
163
/**
164
Constant used to specify the top edge-center point for alignment
165
 
166
@property TC
167
@type String
168
@value 'tc'
169
@static
170
**/
171
PositionAlign.TC = 'tc';
172
 
173
/**
174
Constant used to specify the right edge, center point for alignment
175
 
176
@property RC
177
@type String
178
@value 'rc'
179
@static
180
**/
181
PositionAlign.RC = 'rc';
182
 
183
/**
184
Constant used to specify the bottom edge, center point for alignment
185
 
186
@property BC
187
@type String
188
@value 'bc'
189
@static
190
**/
191
PositionAlign.BC = 'bc';
192
 
193
/**
194
Constant used to specify the left edge, center point for alignment
195
 
196
@property LC
197
@type String
198
@value 'lc'
199
@static
200
**/
201
PositionAlign.LC = 'lc';
202
 
203
/**
204
Constant used to specify the center of widget/node/viewport for alignment
205
 
206
@property CC
207
@type String
208
@value 'cc'
209
@static
210
*/
211
PositionAlign.CC = 'cc';
212
 
213
PositionAlign.prototype = {
214
    // -- Protected Properties -------------------------------------------------
215
 
216
 
217
    initializer : function() {
218
        if (!this._posNode) {
219
            Y.error('WidgetPosition needs to be added to the Widget, ' +
220
                'before WidgetPositionAlign is added');
221
        }
222
 
223
        Y.after(this._bindUIPosAlign, this, 'bindUI');
224
        Y.after(this._syncUIPosAlign, this, 'syncUI');
225
    },
226
 
227
    /**
228
    Holds the alignment-syncing event handles.
229
 
230
    @property _posAlignUIHandles
231
    @type Array
232
    @default null
233
    @protected
234
    **/
235
    _posAlignUIHandles: null,
236
 
237
    // -- Lifecycle Methods ----------------------------------------------------
238
 
239
    destructor: function () {
240
        this._detachPosAlignUIHandles();
241
    },
242
 
243
    /**
244
    Bind event listeners responsible for updating the UI state in response to
245
    the widget's position-align related state changes.
246
 
247
    This method is invoked after `bindUI` has been invoked for the `Widget`
248
    class using the AOP infrastructure.
249
 
250
    @method _bindUIPosAlign
251
    @protected
252
    **/
253
    _bindUIPosAlign: function () {
254
        this.after('alignChange', this._afterAlignChange);
255
        this.after('alignOnChange', this._afterAlignOnChange);
256
        this.after('visibleChange', this._syncUIPosAlign);
257
    },
258
 
259
    /**
260
    Synchronizes the current `align` Attribute value to the DOM.
261
 
262
    This method is invoked after `syncUI` has been invoked for the `Widget`
263
    class using the AOP infrastructure.
264
 
265
    @method _syncUIPosAlign
266
    @protected
267
    **/
268
    _syncUIPosAlign: function () {
269
        var align = this.get(ALIGN);
270
 
271
        this._uiSetVisiblePosAlign(this.get(VISIBLE));
272
 
273
        if (align) {
274
            this._uiSetAlign(align.node, align.points);
275
        }
276
    },
277
 
278
    // -- Public Methods -------------------------------------------------------
279
 
280
    /**
281
    Aligns this widget to the provided `Node` (or viewport) using the provided
282
    points. This method can be invoked with no arguments which will cause the
283
    widget's current `align` Attribute value to be synced to the DOM.
284
 
285
    @example Aligning to the top-left corner of the `<body>`:
286
 
287
        myWidget.align('body',
288
            [Y.WidgetPositionAlign.TL, Y.WidgetPositionAlign.TR]);
289
 
290
    @method align
291
    @param {Node|String|null} [node] A reference (or selector String) for the
292
      `Node` which with the widget is to be aligned. If null is passed in, the
293
      widget will be aligned with the viewport.
294
    @param {Array[2]} [points] A two item array specifying the points on the
295
      widget and `Node`/viewport which will to be aligned. The first entry is
296
      the point on the widget, and the second entry is the point on the
297
      `Node`/viewport. Valid point references are defined as static constants on
298
      the `WidgetPositionAlign` extension.
299
    @chainable
300
    **/
301
    align: function (node, points) {
302
        if (arguments.length) {
303
            // Set the `align` Attribute.
304
            this.set(ALIGN, {
305
                node  : node,
306
                points: points
307
            });
308
        } else {
309
            // Sync the current `align` Attribute value to the DOM.
310
            this._syncUIPosAlign();
311
        }
312
 
313
        return this;
314
    },
315
 
316
    /**
317
    Centers the widget in the viewport, or if a `Node` is passed in, it will
318
    be centered to that `Node`.
319
 
320
    @method centered
321
    @param {Node|String} [node] A `Node` reference or selector String defining
322
      the `Node` which the widget should be centered. If a `Node` is not  passed
323
      in, then the widget will be centered to the viewport.
324
    @chainable
325
    **/
326
    centered: function (node) {
327
        return this.align(node, [PositionAlign.CC, PositionAlign.CC]);
328
    },
329
 
330
    // -- Protected Methods ----------------------------------------------------
331
 
332
    /**
333
    Default setter for `center` Attribute changes. Sets up the appropriate
334
    value, and passes it through the to the align attribute.
335
 
336
    @method _setAlignCenter
337
    @param {Boolean|Node} val The Attribute value being set.
338
    @return {Boolean|Node} the value passed in.
339
    @protected
340
    **/
341
    _setAlignCenter: function (val) {
342
        if (val) {
343
            this.set(ALIGN, {
344
                node  : val === true ? null : val,
345
                points: [PositionAlign.CC, PositionAlign.CC]
346
            });
347
        }
348
 
349
        return val;
350
    },
351
 
352
    /**
353
    Updates the UI to reflect the `align` value passed in.
354
 
355
    **Note:** See the `align` Attribute documentation, for the Object structure
356
    expected.
357
 
358
    @method _uiSetAlign
359
    @param {Node|String|null} [node] The node to align to, or null to indicate
360
      the viewport.
361
    @param {Array} points The alignment points.
362
    @protected
363
    **/
364
    _uiSetAlign: function (node, points) {
365
        if ( ! Lang.isArray(points) || points.length !== 2) {
366
            Y.error('align: Invalid Points Arguments');
367
            return;
368
        }
369
 
370
        var nodeRegion = this._getRegion(node),
371
            widgetPoint, nodePoint, xy;
372
 
373
        if ( ! nodeRegion) {
374
            // No-op, nothing to align to.
375
            return;
376
        }
377
 
378
        widgetPoint = points[0];
379
        nodePoint   = points[1];
380
 
381
        // TODO: Optimize KWeight - Would lookup table help?
382
        switch (nodePoint) {
383
        case PositionAlign.TL:
384
            xy = [nodeRegion.left, nodeRegion.top];
385
            break;
386
 
387
        case PositionAlign.TR:
388
            xy = [nodeRegion.right, nodeRegion.top];
389
            break;
390
 
391
        case PositionAlign.BL:
392
            xy = [nodeRegion.left, nodeRegion.bottom];
393
            break;
394
 
395
        case PositionAlign.BR:
396
            xy = [nodeRegion.right, nodeRegion.bottom];
397
            break;
398
 
399
        case PositionAlign.TC:
400
            xy = [
401
                nodeRegion.left + Math.floor(nodeRegion.width / 2),
402
                nodeRegion.top
403
            ];
404
            break;
405
 
406
        case PositionAlign.BC:
407
            xy = [
408
                nodeRegion.left + Math.floor(nodeRegion.width / 2),
409
                nodeRegion.bottom
410
            ];
411
            break;
412
 
413
        case PositionAlign.LC:
414
            xy = [
415
                nodeRegion.left,
416
                nodeRegion.top + Math.floor(nodeRegion.height / 2)
417
            ];
418
            break;
419
 
420
        case PositionAlign.RC:
421
            xy = [
422
                nodeRegion.right,
423
                nodeRegion.top + Math.floor(nodeRegion.height / 2)
424
            ];
425
            break;
426
 
427
        case PositionAlign.CC:
428
            xy = [
429
                nodeRegion.left + Math.floor(nodeRegion.width / 2),
430
                nodeRegion.top + Math.floor(nodeRegion.height / 2)
431
            ];
432
            break;
433
 
434
        default:
435
            Y.log('align: Invalid Points Arguments', 'info',
436
                'widget-position-align');
437
            break;
438
 
439
        }
440
 
441
        if (xy) {
442
            this._doAlign(widgetPoint, xy[0], xy[1]);
443
        }
444
    },
445
 
446
    /**
447
    Attaches or detaches alignment-syncing event handlers based on the widget's
448
    `visible` Attribute state.
449
 
450
    @method _uiSetVisiblePosAlign
451
    @param {Boolean} visible The current value of the widget's `visible`
452
      Attribute.
453
    @protected
454
    **/
455
    _uiSetVisiblePosAlign: function (visible) {
456
        if (visible) {
457
            this._attachPosAlignUIHandles();
458
        } else {
459
            this._detachPosAlignUIHandles();
460
        }
461
    },
462
 
463
    /**
464
    Attaches the alignment-syncing event handlers.
465
 
466
    @method _attachPosAlignUIHandles
467
    @protected
468
    **/
469
    _attachPosAlignUIHandles: function () {
470
        if (this._posAlignUIHandles) {
471
            // No-op if we have already setup the event handlers.
472
            return;
473
        }
474
 
475
        var bb        = this.get(BOUNDING_BOX),
476
            syncAlign = Y.bind(this._syncUIPosAlign, this),
477
            handles   = [];
478
 
479
        Y.Array.each(this.get(ALIGN_ON), function (o) {
480
            var event = o.eventName,
481
                node  = Y.one(o.node) || bb;
482
 
483
            if (event) {
484
                handles.push(node.on(event, syncAlign));
485
            }
486
        });
487
 
488
        this._posAlignUIHandles = handles;
489
    },
490
 
491
    /**
492
    Detaches the alignment-syncing event handlers.
493
 
494
    @method _detachPosAlignUIHandles
495
    @protected
496
    **/
497
    _detachPosAlignUIHandles: function () {
498
        var handles = this._posAlignUIHandles;
499
        if (handles) {
500
            new Y.EventHandle(handles).detach();
501
            this._posAlignUIHandles = null;
502
        }
503
    },
504
 
505
    // -- Private Methods ------------------------------------------------------
506
 
507
    /**
508
    Helper method, used to align the given point on the widget, with the XY page
509
    coordinates provided.
510
 
511
    @method _doAlign
512
    @param {String} widgetPoint Supported point constant
513
      (e.g. WidgetPositionAlign.TL)
514
    @param {Number} x X page coordinate to align to.
515
    @param {Number} y Y page coordinate to align to.
516
    @private
517
    **/
518
    _doAlign: function (widgetPoint, x, y) {
519
        var widgetNode = this._posNode,
520
            xy;
521
 
522
        switch (widgetPoint) {
523
        case PositionAlign.TL:
524
            xy = [x, y];
525
            break;
526
 
527
        case PositionAlign.TR:
528
            xy = [
529
                x - widgetNode.get(OFFSET_WIDTH),
530
                y
531
            ];
532
            break;
533
 
534
        case PositionAlign.BL:
535
            xy = [
536
                x,
537
                y - widgetNode.get(OFFSET_HEIGHT)
538
            ];
539
            break;
540
 
541
        case PositionAlign.BR:
542
            xy = [
543
                x - widgetNode.get(OFFSET_WIDTH),
544
                y - widgetNode.get(OFFSET_HEIGHT)
545
            ];
546
            break;
547
 
548
        case PositionAlign.TC:
549
            xy = [
550
                x - (widgetNode.get(OFFSET_WIDTH) / 2),
551
                y
552
            ];
553
            break;
554
 
555
        case PositionAlign.BC:
556
            xy = [
557
                x - (widgetNode.get(OFFSET_WIDTH) / 2),
558
                y - widgetNode.get(OFFSET_HEIGHT)
559
            ];
560
            break;
561
 
562
        case PositionAlign.LC:
563
            xy = [
564
                x,
565
                y - (widgetNode.get(OFFSET_HEIGHT) / 2)
566
            ];
567
            break;
568
 
569
        case PositionAlign.RC:
570
            xy = [
571
                x - widgetNode.get(OFFSET_WIDTH),
572
                y - (widgetNode.get(OFFSET_HEIGHT) / 2)
573
            ];
574
            break;
575
 
576
        case PositionAlign.CC:
577
            xy = [
578
                x - (widgetNode.get(OFFSET_WIDTH) / 2),
579
                y - (widgetNode.get(OFFSET_HEIGHT) / 2)
580
            ];
581
            break;
582
 
583
        default:
584
            Y.log('align: Invalid Points Argument', 'info',
585
                'widget-position-align');
586
            break;
587
 
588
        }
589
 
590
        if (xy) {
591
            this.move(xy);
592
        }
593
    },
594
 
595
    /**
596
    Returns the region of the passed-in `Node`, or the viewport region if
597
    calling with passing in a `Node`.
598
 
599
    @method _getRegion
600
    @param {Node} [node] The node to get the region of.
601
    @return {Object} The node's region.
602
    @private
603
    **/
604
    _getRegion: function (node) {
605
        var nodeRegion;
606
 
607
        if ( ! node) {
608
            nodeRegion = this._posNode.get(VIEWPORT_REGION);
609
        } else {
610
            node = Y.Node.one(node);
611
            if (node) {
612
                nodeRegion = node.get(REGION);
613
            }
614
        }
615
 
616
        return nodeRegion;
617
    },
618
 
619
    // -- Protected Event Handlers ---------------------------------------------
620
 
621
    /**
622
    Handles `alignChange` events by updating the UI in response to `align`
623
    Attribute changes.
624
 
625
    @method _afterAlignChange
626
    @param {EventFacade} e
627
    @protected
628
    **/
629
    _afterAlignChange: function (e) {
630
        var align = e.newVal;
631
        if (align) {
632
            this._uiSetAlign(align.node, align.points);
633
        }
634
    },
635
 
636
    /**
637
    Handles `alignOnChange` events by updating the alignment-syncing event
638
    handlers.
639
 
640
    @method _afterAlignOnChange
641
    @param {EventFacade} e
642
    @protected
643
    **/
644
    _afterAlignOnChange: function(e) {
645
        this._detachPosAlignUIHandles();
646
 
647
        if (this.get(VISIBLE)) {
648
            this._attachPosAlignUIHandles();
649
        }
650
    }
651
};
652
 
653
Y.WidgetPositionAlign = PositionAlign;
654
 
655
 
656
}, '3.18.1', {"requires": ["widget-position"]});