MDL-32843 import YUI 3.5.1
[moodle.git] / lib / yui / 3.5.1 / build / view / view.js
bloba08865a4cf2e76f92c6bd645fc7a0f5e5307e93f
1 /*
2 YUI 3.5.1 (build 22)
3 Copyright 2012 Yahoo! Inc. All rights reserved.
4 Licensed under the BSD License.
5 http://yuilibrary.com/license/
6 */
7 YUI.add('view', function(Y) {
9 /**
10 Represents a logical piece of an application's user interface, and provides a
11 lightweight, overridable API for rendering content and handling delegated DOM
12 events on a container element.
14 @module app
15 @submodule view
16 @since 3.4.0
17 **/
19 /**
20 Represents a logical piece of an application's user interface, and provides a
21 lightweight, overridable API for rendering content and handling delegated DOM
22 events on a container element.
24 The View class imposes little structure and provides only minimal functionality
25 of its own: it's basically just an overridable API interface that helps you
26 implement custom views.
28 As of YUI 3.5.0, View allows ad-hoc attributes to be specified at instantiation
29 time, so you don't need to subclass `Y.View` to add custom attributes. Just pass
30 them to the constructor:
32     var view = new Y.View({foo: 'bar'});
33     view.get('foo'); // => "bar"
35 @class View
36 @constructor
37 @extends Base
38 @since 3.4.0
39 **/
41 function View() {
42     View.superclass.constructor.apply(this, arguments);
45 Y.View = Y.extend(View, Y.Base, {
46     // -- Public Properties ----------------------------------------------------
48     /**
49     Template for this view's container.
51     @property containerTemplate
52     @type String
53     @default "<div/>"
54     @since 3.5.0
55     **/
56     containerTemplate: '<div/>',
58     /**
59     Hash of CSS selectors mapped to events to delegate to elements matching
60     those selectors.
62     CSS selectors are relative to the `container` element. Events are attached
63     to the container, and delegation is used so that subscribers are only
64     notified of events that occur on elements inside the container that match
65     the specified selectors. This allows the container's contents to be re-
66     rendered as needed without losing event subscriptions.
68     Event handlers can be specified either as functions or as strings that map
69     to function names on this view instance or its prototype.
71     The `this` object in event handlers will refer to this view instance. If
72     you'd prefer `this` to be something else, use `Y.bind()` to bind a custom
73     `this` object.
75     @example
77         var view = new Y.View({
78             events: {
79                 // Call `this.toggle()` whenever the element with the id
80                 // "toggle-button" is clicked.
81                 '#toggle-button': {click: 'toggle'},
83                 // Call `this.hoverOn()` when the mouse moves over any element
84                 // with the "hoverable" class, and `this.hoverOff()` when the
85                 // mouse moves out of any element with the "hoverable" class.
86                 '.hoverable': {
87                     mouseover: 'hoverOn',
88                     mouseout : 'hoverOff'
89                 }
90             }
91         });
93     @property events
94     @type Object
95     @default {}
96     **/
97     events: {},
99     /**
100     Template for this view's contents.
102     This is a convenience property that has no default behavior of its own.
103     It's only provided as a convention to allow you to store whatever you
104     consider to be a template, whether that's an HTML string, a `Y.Node`
105     instance, a Mustache template, or anything else your little heart
106     desires.
108     How this template gets used is entirely up to you and your custom
109     `render()` method.
111     @property template
112     @type mixed
113     @default ''
114     **/
115     template: '',
117     // -- Protected Properties -------------------------------------------------
119     /**
120     This tells `Y.Base` that it should create ad-hoc attributes for config
121     properties passed to View's constructor. This makes it possible to
122     instantiate a view and set a bunch of attributes without having to subclass
123     `Y.View` and declare all those attributes first.
125     @property _allowAdHocAttrs
126     @type Boolean
127     @default true
128     @protected
129     @since 3.5.0
130     **/
131     _allowAdHocAttrs: true,
133     // -- Lifecycle Methods ----------------------------------------------------
134     initializer: function (config) {
135         config || (config = {});
137         // Set instance properties specified in the config.
138         config.containerTemplate &&
139             (this.containerTemplate = config.containerTemplate);
141         config.template && (this.template = config.template);
143         // Merge events from the config into events in `this.events`.
144         this.events = config.events ? Y.merge(this.events, config.events) :
145             this.events;
147         // When the container node changes (or when it's set for the first
148         // time), we'll attach events to it, but not until then. This allows the
149         // container to be created lazily the first time it's accessed rather
150         // than always on init.
151         this.after('containerChange', this._afterContainerChange);
152     },
154     /**
155     Destroys this View, detaching any DOM events and optionally also destroying
156     its container node.
158     By default, the container node will not be destroyed. Pass an _options_
159     object with a truthy `remove` property to destroy the container as well.
161     @method destroy
162     @param {Object} [options] Options.
163         @param {Boolean} [options.remove=false] If `true`, this View's container
164             will be removed from the DOM and destroyed as well.
165     @chainable
166     */
167     destroy: function (options) {
168         // We also accept `delete` as a synonym for `remove`.
169         if (options && (options.remove || options['delete'])) {
170             // Attaching an event handler here because the `destroy` event is
171             // preventable. If we destroyed the container before calling the
172             // superclass's `destroy()` method and the event was prevented, the
173             // class would end up in a broken state.
174             this.onceAfter('destroy', function () {
175                 this._destroyContainer();
176             });
177         }
179         return View.superclass.destroy.call(this);
180     },
182     destructor: function () {
183         this.detachEvents();
184         delete this._container;
185     },
187     // -- Public Methods -------------------------------------------------------
189     /**
190     Attaches delegated event handlers to this view's container element. This
191     method is called internally to subscribe to events configured in the
192     `events` attribute when the view is initialized.
194     You may override this method to customize the event attaching logic.
196     @method attachEvents
197     @param {Object} [events] Hash of events to attach. See the docs for the
198         `events` attribute for details on the format. If not specified, this
199         view's `events` property will be used.
200     @chainable
201     @see detachEvents
202     **/
203     attachEvents: function (events) {
204         var container = this.get('container'),
205             owns      = Y.Object.owns,
206             handler, handlers, name, selector;
208         this.detachEvents();
210         events || (events = this.events);
212         for (selector in events) {
213             if (!owns(events, selector)) { continue; }
215             handlers = events[selector];
217             for (name in handlers) {
218                 if (!owns(handlers, name)) { continue; }
220                 handler = handlers[name];
222                 if (typeof handler === 'string') {
223                     handler = this[handler];
224                 }
226                 this._attachedViewEvents.push(
227                     container.delegate(name, handler, selector, this));
228             }
229         }
231         return this;
232     },
234     /**
235     Creates and returns a container node for this view.
237     By default, the container is created from the HTML template specified in the
238     `containerTemplate` property, and is _not_ added to the DOM automatically.
240     You may override this method to customize how the container node is created
241     (such as by rendering it from a custom template format). Your method must
242     return a `Y.Node` instance.
244     @method create
245     @param {HTMLElement|Node|String} [container] Selector string, `Y.Node`
246         instance, or DOM element to use at the container node.
247     @return {Node} Node instance of the created container node.
248     **/
249     create: function (container) {
250         return container ? Y.one(container) :
251                 Y.Node.create(this.containerTemplate);
252     },
254     /**
255     Detaches DOM events that have previously been attached to the container by
256     `attachEvents()`.
258     @method detachEvents
259     @chainable
260     @see attachEvents
261     **/
262     detachEvents: function () {
263         Y.Array.each(this._attachedViewEvents, function (handle) {
264             handle.detach();
265         });
267         this._attachedViewEvents = [];
268         return this;
269     },
271     /**
272     Removes this view's container element from the DOM (if it's in the DOM),
273     but doesn't destroy it or any event listeners attached to it.
275     @method remove
276     @chainable
277     **/
278     remove: function () {
279         var container = this.get('container');
280         container && container.remove();
281         return this;
282     },
284     /**
285     Renders this view.
287     This method is a noop by default. Override it to provide a custom
288     implementation that renders this view's content and appends it to the
289     container element. Ideally your `render` method should also return `this` as
290     the end to allow chaining, but that's up to you.
292     Since there's no default renderer, you're free to render your view however
293     you see fit, whether that means manipulating the DOM directly, dumping
294     strings into `innerHTML`, or using a template language of some kind.
296     For basic templating needs, `Y.Node.create()` and `Y.Lang.sub()` may
297     suffice, but there are no restrictions on what tools or techniques you can
298     use to render your view. All you need to do is append something to the
299     container element at some point, and optionally append the container
300     to the DOM if it's not there already.
302     @method render
303     @chainable
304     **/
305     render: function () {
306         return this;
307     },
309     // -- Protected Methods ----------------------------------------------------
311     /**
312     Removes the `container` from the DOM and purges all its event listeners.
314     @method _destroyContainer
315     @protected
316     **/
317     _destroyContainer: function () {
318         var container = this.get('container');
319         container && container.remove(true);
320     },
322     /**
323     Getter for the `container` attribute.
325     @method _getContainer
326     @param {Node|null} value Current attribute value.
327     @return {Node} Container node.
328     @protected
329     @since 3.5.0
330     **/
331     _getContainer: function (value) {
332         // This wackiness is necessary to enable fully lazy creation of the
333         // container node both when no container is specified and when one is
334         // specified via a valueFn.
336         if (!this._container) {
337             if (value) {
338                 // Attach events to the container when it's specified via a
339                 // valueFn, which won't fire the containerChange event.
340                 this._container = value;
341                 this.attachEvents();
342             } else {
343                 // Create a default container and set that as the new attribute
344                 // value. The `this._container` property prevents infinite
345                 // recursion.
346                 value = this._container = this.create();
347                 this._set('container', value);
348             }
349         }
351         return value;
352     },
354     // -- Protected Event Handlers ---------------------------------------------
356     /**
357     Handles `containerChange` events. Detaches event handlers from the old
358     container (if any) and attaches them to the new container.
360     Right now the `container` attr is initOnly so this event should only ever
361     fire the first time the container is created, but in the future (once Y.App
362     can handle it) we may allow runtime container changes.
364     @method _afterContainerChange
365     @protected
366     @since 3.5.0
367     **/
368     _afterContainerChange: function () {
369         this.attachEvents(this.events);
370     }
371 }, {
372     NAME: 'view',
374     ATTRS: {
375         /**
376         Container node into which this view's content will be rendered.
378         The container node serves as the host for all DOM events attached by the
379         view. Delegation is used to handle events on children of the container,
380         allowing the container's contents to be re-rendered at any time without
381         losing event subscriptions.
383         The default container is a `<div>` Node, but you can override this in
384         a subclass, or by passing in a custom `container` config value at
385         instantiation time. If you override the default container in a subclass
386         using `ATTRS`, you must use the `valueFn` property. The view's constructor 
387         will ignore any assignments using `value`.
389         When `container` is overridden by a subclass or passed as a config
390         option at instantiation time, you can provide it as a selector string, a
391         DOM element, a `Y.Node` instance, or (if you are subclassing and modifying
392         the attribute), a `valueFn` function that returns a `Y.Node` instance. 
393         The value will be converted into a `Y.Node` instance if it isn't one 
394         already.
396         The container is not added to the page automatically. This allows you to
397         have full control over how and when your view is actually rendered to
398         the page.
400         @attribute container
401         @type HTMLElement|Node|String
402         @default Y.Node.create(this.containerTemplate)
403         @writeOnce
404         **/
405         container: {
406             getter   : '_getContainer',
407             setter   : Y.one,
408             writeOnce: true
409         }
410     },
412     /**
413     Properties that shouldn't be turned into ad-hoc attributes when passed to
414     View's constructor.
416     @property _NON_ATTRS_CFG
417     @type Array
418     @static
419     @protected
420     @since 3.5.0
421     **/
422     _NON_ATTRS_CFG: [
423         'containerTemplate',
424         'events',
425         'template'
426     ]
431 }, '3.5.1' ,{requires:['base-build', 'node-event-delegate']});